xref: /openbmc/linux/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c (revision 34d6f206a88c2651d216bd3487ac956a40b2ba8e)
1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo  * Copyright (c) 2010 Broadcom Corporation
405491d2cSKalle Valo  */
505491d2cSKalle Valo 
605491d2cSKalle Valo /* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
705491d2cSKalle Valo 
805491d2cSKalle Valo #include <linux/kernel.h>
905491d2cSKalle Valo #include <linux/etherdevice.h>
1005491d2cSKalle Valo #include <linux/module.h>
1105491d2cSKalle Valo #include <linux/vmalloc.h>
1205491d2cSKalle Valo #include <net/cfg80211.h>
1305491d2cSKalle Valo #include <net/netlink.h>
1420f2c5faSRafał Miłecki #include <uapi/linux/if_arp.h>
1505491d2cSKalle Valo 
1605491d2cSKalle Valo #include <brcmu_utils.h>
1705491d2cSKalle Valo #include <defs.h>
1805491d2cSKalle Valo #include <brcmu_wifi.h>
19a21bf90eSHans de Goede #include <brcm_hw_ids.h>
2005491d2cSKalle Valo #include "core.h"
2105491d2cSKalle Valo #include "debug.h"
2205491d2cSKalle Valo #include "tracepoint.h"
2305491d2cSKalle Valo #include "fwil_types.h"
2405491d2cSKalle Valo #include "p2p.h"
2505491d2cSKalle Valo #include "btcoex.h"
26ac55136fSArend Van Spriel #include "pno.h"
2792072e5fSSaravanan Shanmugham #include "fwsignal.h"
2805491d2cSKalle Valo #include "cfg80211.h"
2905491d2cSKalle Valo #include "feature.h"
3005491d2cSKalle Valo #include "fwil.h"
3105491d2cSKalle Valo #include "proto.h"
3205491d2cSKalle Valo #include "vendor.h"
3305491d2cSKalle Valo #include "bus.h"
3405491d2cSKalle Valo #include "common.h"
358d59a64cSHector Martin #include "fwvid.h"
3605491d2cSKalle Valo 
3705491d2cSKalle Valo #define BRCMF_SCAN_IE_LEN_MAX		2048
3805491d2cSKalle Valo 
3905491d2cSKalle Valo #define WPA_OUI				"\x00\x50\xF2"	/* WPA OUI */
4005491d2cSKalle Valo #define WPA_OUI_TYPE			1
4105491d2cSKalle Valo #define RSN_OUI				"\x00\x0F\xAC"	/* RSN OUI */
4205491d2cSKalle Valo #define	WME_OUI_TYPE			2
4305491d2cSKalle Valo #define WPS_OUI_TYPE			4
4405491d2cSKalle Valo 
4505491d2cSKalle Valo #define VS_IE_FIXED_HDR_LEN		6
4605491d2cSKalle Valo #define WPA_IE_VERSION_LEN		2
4705491d2cSKalle Valo #define WPA_IE_MIN_OUI_LEN		4
4805491d2cSKalle Valo #define WPA_IE_SUITE_COUNT_LEN		2
4905491d2cSKalle Valo 
5005491d2cSKalle Valo #define WPA_CIPHER_NONE			0	/* None */
5105491d2cSKalle Valo #define WPA_CIPHER_WEP_40		1	/* WEP (40-bit) */
5205491d2cSKalle Valo #define WPA_CIPHER_TKIP			2	/* TKIP: default for WPA */
5305491d2cSKalle Valo #define WPA_CIPHER_AES_CCM		4	/* AES (CCM) */
5405491d2cSKalle Valo #define WPA_CIPHER_WEP_104		5	/* WEP (104-bit) */
5505491d2cSKalle Valo 
5605491d2cSKalle Valo #define RSN_AKM_NONE			0	/* None (IBSS) */
5705491d2cSKalle Valo #define RSN_AKM_UNSPECIFIED		1	/* Over 802.1x */
5805491d2cSKalle Valo #define RSN_AKM_PSK			2	/* Pre-shared Key */
59240d61a9SHante Meuleman #define RSN_AKM_SHA256_1X		5	/* SHA256, 802.1X */
60240d61a9SHante Meuleman #define RSN_AKM_SHA256_PSK		6	/* SHA256, Pre-shared Key */
61d5f59c96SChung-Hsien Hsu #define RSN_AKM_SAE			8	/* SAE */
6205491d2cSKalle Valo #define RSN_CAP_LEN			2	/* Length of RSN capabilities */
63240d61a9SHante Meuleman #define RSN_CAP_PTK_REPLAY_CNTR_MASK	(BIT(2) | BIT(3))
64240d61a9SHante Meuleman #define RSN_CAP_MFPR_MASK		BIT(6)
65240d61a9SHante Meuleman #define RSN_CAP_MFPC_MASK		BIT(7)
66240d61a9SHante Meuleman #define RSN_PMKID_COUNT_LEN		2
6705491d2cSKalle Valo 
6805491d2cSKalle Valo #define VNDR_IE_CMD_LEN			4	/* length of the set command
6905491d2cSKalle Valo 						 * string :"add", "del" (+ NUL)
7005491d2cSKalle Valo 						 */
7105491d2cSKalle Valo #define VNDR_IE_COUNT_OFFSET		4
7205491d2cSKalle Valo #define VNDR_IE_PKTFLAG_OFFSET		8
7305491d2cSKalle Valo #define VNDR_IE_VSIE_OFFSET		12
7405491d2cSKalle Valo #define VNDR_IE_HDR_SIZE		12
7505491d2cSKalle Valo #define VNDR_IE_PARSE_LIMIT		5
7605491d2cSKalle Valo 
7705491d2cSKalle Valo #define	DOT11_MGMT_HDR_LEN		24	/* d11 management header len */
7805491d2cSKalle Valo #define	DOT11_BCN_PRB_FIXED_LEN		12	/* beacon/probe fixed length */
7905491d2cSKalle Valo 
8005491d2cSKalle Valo #define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS	320
8105491d2cSKalle Valo #define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS	400
8205491d2cSKalle Valo #define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS	20
8305491d2cSKalle Valo 
841678ba8eSHante Meuleman #define BRCMF_SCAN_CHANNEL_TIME		40
851678ba8eSHante Meuleman #define BRCMF_SCAN_UNASSOC_TIME		40
861678ba8eSHante Meuleman #define BRCMF_SCAN_PASSIVE_TIME		120
871678ba8eSHante Meuleman 
883021ad9aSHante Meuleman #define BRCMF_ND_INFO_TIMEOUT		msecs_to_jiffies(2000)
893021ad9aSHante Meuleman 
903dc05ffbSNicolas Saenz Julienne #define BRCMF_PS_MAX_TIMEOUT_MS		2000
913dc05ffbSNicolas Saenz Julienne 
926c04deaeSWright Feng /* Dump obss definitions */
9325076fe2SDouble Lo #define ACS_MSRMNT_DELAY		80
946c04deaeSWright Feng #define CHAN_NOISE_DUMMY		(-80)
956c04deaeSWright Feng #define OBSS_TOKEN_IDX			15
966c04deaeSWright Feng #define IBSS_TOKEN_IDX			15
976c04deaeSWright Feng #define TX_TOKEN_IDX			14
986c04deaeSWright Feng #define CTG_TOKEN_IDX			13
996c04deaeSWright Feng #define PKT_TOKEN_IDX			15
1006c04deaeSWright Feng #define IDLE_TOKEN_IDX			12
1016c04deaeSWright Feng 
10205491d2cSKalle Valo #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
10305491d2cSKalle Valo 	(sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
10405491d2cSKalle Valo 
1054920ab13SMinsuk Kang #define BRCMF_MAX_CHANSPEC_LIST \
1064920ab13SMinsuk Kang 	(BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)
1074920ab13SMinsuk Kang 
1086c04deaeSWright Feng struct brcmf_dump_survey {
1096c04deaeSWright Feng 	u32 obss;
1106c04deaeSWright Feng 	u32 ibss;
1116c04deaeSWright Feng 	u32 no_ctg;
1126c04deaeSWright Feng 	u32 no_pckt;
1136c04deaeSWright Feng 	u32 tx;
1146c04deaeSWright Feng 	u32 idle;
1156c04deaeSWright Feng };
1166c04deaeSWright Feng 
1176c04deaeSWright Feng struct cca_stats_n_flags {
1186c04deaeSWright Feng 	u32 msrmnt_time; /* Time for Measurement (msec) */
1196c04deaeSWright Feng 	u32 msrmnt_done; /* flag set when measurement complete */
1206c04deaeSWright Feng 	char buf[1];
1216c04deaeSWright Feng };
1226c04deaeSWright Feng 
1236c04deaeSWright Feng struct cca_msrmnt_query {
1246c04deaeSWright Feng 	u32 msrmnt_query;
1256c04deaeSWright Feng 	u32 time_req;
1266c04deaeSWright Feng };
1276c04deaeSWright Feng 
check_vif_up(struct brcmf_cfg80211_vif * vif)12805491d2cSKalle Valo static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
12905491d2cSKalle Valo {
13005491d2cSKalle Valo 	if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
13105491d2cSKalle Valo 		brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
13205491d2cSKalle Valo 			  vif->sme_state);
13305491d2cSKalle Valo 		return false;
13405491d2cSKalle Valo 	}
13505491d2cSKalle Valo 	return true;
13605491d2cSKalle Valo }
13705491d2cSKalle Valo 
13805491d2cSKalle Valo #define RATE_TO_BASE100KBPS(rate)   (((rate) * 10) / 2)
13905491d2cSKalle Valo #define RATETAB_ENT(_rateid, _flags) \
14005491d2cSKalle Valo 	{                                                               \
14105491d2cSKalle Valo 		.bitrate        = RATE_TO_BASE100KBPS(_rateid),     \
14205491d2cSKalle Valo 		.hw_value       = (_rateid),                            \
14305491d2cSKalle Valo 		.flags          = (_flags),                             \
14405491d2cSKalle Valo 	}
14505491d2cSKalle Valo 
14605491d2cSKalle Valo static struct ieee80211_rate __wl_rates[] = {
14705491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_1M, 0),
14805491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
14905491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
15005491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
15105491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_6M, 0),
15205491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_9M, 0),
15305491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_12M, 0),
15405491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_18M, 0),
15505491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_24M, 0),
15605491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_36M, 0),
15705491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_48M, 0),
15805491d2cSKalle Valo 	RATETAB_ENT(BRCM_RATE_54M, 0),
15905491d2cSKalle Valo };
16005491d2cSKalle Valo 
16105491d2cSKalle Valo #define wl_g_rates		(__wl_rates + 0)
16205491d2cSKalle Valo #define wl_g_rates_size		ARRAY_SIZE(__wl_rates)
16305491d2cSKalle Valo #define wl_a_rates		(__wl_rates + 4)
16405491d2cSKalle Valo #define wl_a_rates_size		(wl_g_rates_size - 4)
16505491d2cSKalle Valo 
16605491d2cSKalle Valo #define CHAN2G(_channel, _freq) {				\
16757fbcce3SJohannes Berg 	.band			= NL80211_BAND_2GHZ,		\
16805491d2cSKalle Valo 	.center_freq		= (_freq),			\
16905491d2cSKalle Valo 	.hw_value		= (_channel),			\
17005491d2cSKalle Valo 	.max_antenna_gain	= 0,				\
17105491d2cSKalle Valo 	.max_power		= 30,				\
17205491d2cSKalle Valo }
17305491d2cSKalle Valo 
17405491d2cSKalle Valo #define CHAN5G(_channel) {					\
17557fbcce3SJohannes Berg 	.band			= NL80211_BAND_5GHZ,		\
17605491d2cSKalle Valo 	.center_freq		= 5000 + (5 * (_channel)),	\
17705491d2cSKalle Valo 	.hw_value		= (_channel),			\
17805491d2cSKalle Valo 	.max_antenna_gain	= 0,				\
17905491d2cSKalle Valo 	.max_power		= 30,				\
18005491d2cSKalle Valo }
18105491d2cSKalle Valo 
18205491d2cSKalle Valo static struct ieee80211_channel __wl_2ghz_channels[] = {
18305491d2cSKalle Valo 	CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
18405491d2cSKalle Valo 	CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
18505491d2cSKalle Valo 	CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
18605491d2cSKalle Valo 	CHAN2G(13, 2472), CHAN2G(14, 2484)
18705491d2cSKalle Valo };
18805491d2cSKalle Valo 
18905491d2cSKalle Valo static struct ieee80211_channel __wl_5ghz_channels[] = {
19005491d2cSKalle Valo 	CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
19105491d2cSKalle Valo 	CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
19205491d2cSKalle Valo 	CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
19305491d2cSKalle Valo 	CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
19405491d2cSKalle Valo 	CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
19505491d2cSKalle Valo 	CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
19605491d2cSKalle Valo };
19705491d2cSKalle Valo 
19805491d2cSKalle Valo /* Band templates duplicated per wiphy. The channel info
19905491d2cSKalle Valo  * above is added to the band during setup.
20005491d2cSKalle Valo  */
20105491d2cSKalle Valo static const struct ieee80211_supported_band __wl_band_2ghz = {
20257fbcce3SJohannes Berg 	.band = NL80211_BAND_2GHZ,
20305491d2cSKalle Valo 	.bitrates = wl_g_rates,
20405491d2cSKalle Valo 	.n_bitrates = wl_g_rates_size,
20505491d2cSKalle Valo };
20605491d2cSKalle Valo 
20705491d2cSKalle Valo static const struct ieee80211_supported_band __wl_band_5ghz = {
20857fbcce3SJohannes Berg 	.band = NL80211_BAND_5GHZ,
20905491d2cSKalle Valo 	.bitrates = wl_a_rates,
21005491d2cSKalle Valo 	.n_bitrates = wl_a_rates_size,
21105491d2cSKalle Valo };
21205491d2cSKalle Valo 
21305491d2cSKalle Valo /* This is to override regulatory domains defined in cfg80211 module (reg.c)
21405491d2cSKalle Valo  * By default world regulatory domain defined in reg.c puts the flags
21505491d2cSKalle Valo  * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
21605491d2cSKalle Valo  * With respect to these flags, wpa_supplicant doesn't * start p2p
21705491d2cSKalle Valo  * operations on 5GHz channels. All the changes in world regulatory
21805491d2cSKalle Valo  * domain are to be done here.
21905491d2cSKalle Valo  */
22005491d2cSKalle Valo static const struct ieee80211_regdomain brcmf_regdom = {
22105491d2cSKalle Valo 	.n_reg_rules = 4,
22205491d2cSKalle Valo 	.alpha2 =  "99",
22305491d2cSKalle Valo 	.reg_rules = {
22405491d2cSKalle Valo 		/* IEEE 802.11b/g, channels 1..11 */
22505491d2cSKalle Valo 		REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
22605491d2cSKalle Valo 		/* If any */
22705491d2cSKalle Valo 		/* IEEE 802.11 channel 14 - Only JP enables
22805491d2cSKalle Valo 		 * this and for 802.11b only
22905491d2cSKalle Valo 		 */
23005491d2cSKalle Valo 		REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
23105491d2cSKalle Valo 		/* IEEE 802.11a, channel 36..64 */
232fa905092SArend van Spriel 		REG_RULE(5150-10, 5350+10, 160, 6, 20, 0),
23305491d2cSKalle Valo 		/* IEEE 802.11a, channel 100..165 */
234fa905092SArend van Spriel 		REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), }
23505491d2cSKalle Valo };
23605491d2cSKalle Valo 
237240d61a9SHante Meuleman /* Note: brcmf_cipher_suites is an array of int defining which cipher suites
238240d61a9SHante Meuleman  * are supported. A pointer to this array and the number of entries is passed
239240d61a9SHante Meuleman  * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
240240d61a9SHante Meuleman  * So the cipher suite AES_CMAC has to be the last one in the array, and when
241240d61a9SHante Meuleman  * device does not support MFP then the number of suites will be decreased by 1
242240d61a9SHante Meuleman  */
243240d61a9SHante Meuleman static const u32 brcmf_cipher_suites[] = {
24405491d2cSKalle Valo 	WLAN_CIPHER_SUITE_WEP40,
24505491d2cSKalle Valo 	WLAN_CIPHER_SUITE_WEP104,
24605491d2cSKalle Valo 	WLAN_CIPHER_SUITE_TKIP,
24705491d2cSKalle Valo 	WLAN_CIPHER_SUITE_CCMP,
248240d61a9SHante Meuleman 	/* Keep as last entry: */
249240d61a9SHante Meuleman 	WLAN_CIPHER_SUITE_AES_CMAC
25005491d2cSKalle Valo };
25105491d2cSKalle Valo 
25205491d2cSKalle Valo /* Vendor specific ie. id = 221, oui and type defines exact ie */
25305491d2cSKalle Valo struct brcmf_vs_tlv {
25405491d2cSKalle Valo 	u8 id;
25505491d2cSKalle Valo 	u8 len;
25605491d2cSKalle Valo 	u8 oui[3];
25705491d2cSKalle Valo 	u8 oui_type;
25805491d2cSKalle Valo };
25905491d2cSKalle Valo 
26005491d2cSKalle Valo struct parsed_vndr_ie_info {
26105491d2cSKalle Valo 	u8 *ie_ptr;
26205491d2cSKalle Valo 	u32 ie_len;	/* total length including id & length field */
26305491d2cSKalle Valo 	struct brcmf_vs_tlv vndrie;
26405491d2cSKalle Valo };
26505491d2cSKalle Valo 
26605491d2cSKalle Valo struct parsed_vndr_ies {
26705491d2cSKalle Valo 	u32 count;
26805491d2cSKalle Valo 	struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
26905491d2cSKalle Valo };
27005491d2cSKalle Valo 
2714388827bSWright Feng #define WL_INTERFACE_CREATE_VER_1		1
2724388827bSWright Feng #define WL_INTERFACE_CREATE_VER_2		2
2734388827bSWright Feng #define WL_INTERFACE_CREATE_VER_3		3
2744388827bSWright Feng #define WL_INTERFACE_CREATE_VER_MAX		WL_INTERFACE_CREATE_VER_3
2754388827bSWright Feng 
2762b5fb30fSWright Feng #define WL_INTERFACE_MAC_DONT_USE	0x0
2772b5fb30fSWright Feng #define WL_INTERFACE_MAC_USE		0x2
2782b5fb30fSWright Feng 
2792b5fb30fSWright Feng #define WL_INTERFACE_CREATE_STA		0x0
2802b5fb30fSWright Feng #define WL_INTERFACE_CREATE_AP		0x1
2812b5fb30fSWright Feng 
2824388827bSWright Feng struct wl_interface_create_v1 {
2834388827bSWright Feng 	u16	ver;			/* structure version */
2844388827bSWright Feng 	u32	flags;			/* flags for operation */
2854388827bSWright Feng 	u8	mac_addr[ETH_ALEN];	/* MAC address */
2864388827bSWright Feng 	u32	wlc_index;		/* optional for wlc index */
2874388827bSWright Feng };
2884388827bSWright Feng 
2894388827bSWright Feng struct wl_interface_create_v2 {
2904388827bSWright Feng 	u16	ver;			/* structure version */
2914388827bSWright Feng 	u8	pad1[2];
2924388827bSWright Feng 	u32	flags;			/* flags for operation */
2934388827bSWright Feng 	u8	mac_addr[ETH_ALEN];	/* MAC address */
2944388827bSWright Feng 	u8	iftype;			/* type of interface created */
2954388827bSWright Feng 	u8	pad2;
2964388827bSWright Feng 	u32	wlc_index;		/* optional for wlc index */
2974388827bSWright Feng };
2984388827bSWright Feng 
2994388827bSWright Feng struct wl_interface_create_v3 {
3004388827bSWright Feng 	u16 ver;			/* structure version */
3014388827bSWright Feng 	u16 len;			/* length of structure + data */
3024388827bSWright Feng 	u16 fixed_len;			/* length of structure */
3034388827bSWright Feng 	u8 iftype;			/* type of interface created */
3044388827bSWright Feng 	u8 wlc_index;			/* optional for wlc index */
3054388827bSWright Feng 	u32 flags;			/* flags for operation */
3064388827bSWright Feng 	u8 mac_addr[ETH_ALEN];		/* MAC address */
3074388827bSWright Feng 	u8 bssid[ETH_ALEN];		/* optional for BSSID */
3084388827bSWright Feng 	u8 if_index;			/* interface index request */
3094388827bSWright Feng 	u8 pad[3];
3104388827bSWright Feng 	u8 data[];			/* Optional for specific data */
3112b5fb30fSWright Feng };
3122b5fb30fSWright Feng 
nl80211_band_to_fwil(enum nl80211_band band)3137705ba6fSArend van Spriel static u8 nl80211_band_to_fwil(enum nl80211_band band)
3147705ba6fSArend van Spriel {
3157705ba6fSArend van Spriel 	switch (band) {
3167705ba6fSArend van Spriel 	case NL80211_BAND_2GHZ:
3177705ba6fSArend van Spriel 		return WLC_BAND_2G;
3187705ba6fSArend van Spriel 	case NL80211_BAND_5GHZ:
3197705ba6fSArend van Spriel 		return WLC_BAND_5G;
3207705ba6fSArend van Spriel 	default:
3217705ba6fSArend van Spriel 		WARN_ON(1);
3227705ba6fSArend van Spriel 		break;
3237705ba6fSArend van Spriel 	}
3247705ba6fSArend van Spriel 	return 0;
3257705ba6fSArend van Spriel }
3267705ba6fSArend van Spriel 
chandef_to_chanspec(struct brcmu_d11inf * d11inf,struct cfg80211_chan_def * ch)32705491d2cSKalle Valo static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
32805491d2cSKalle Valo 			       struct cfg80211_chan_def *ch)
32905491d2cSKalle Valo {
33005491d2cSKalle Valo 	struct brcmu_chan ch_inf;
33105491d2cSKalle Valo 	s32 primary_offset;
33205491d2cSKalle Valo 
33305491d2cSKalle Valo 	brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
33405491d2cSKalle Valo 		  ch->chan->center_freq, ch->center_freq1, ch->width);
33505491d2cSKalle Valo 	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
33636e8072eSRafał Miłecki 	primary_offset = ch->chan->center_freq - ch->center_freq1;
33705491d2cSKalle Valo 	switch (ch->width) {
33805491d2cSKalle Valo 	case NL80211_CHAN_WIDTH_20:
33905491d2cSKalle Valo 	case NL80211_CHAN_WIDTH_20_NOHT:
34005491d2cSKalle Valo 		ch_inf.bw = BRCMU_CHAN_BW_20;
34105491d2cSKalle Valo 		WARN_ON(primary_offset != 0);
34205491d2cSKalle Valo 		break;
34305491d2cSKalle Valo 	case NL80211_CHAN_WIDTH_40:
34405491d2cSKalle Valo 		ch_inf.bw = BRCMU_CHAN_BW_40;
34536e8072eSRafał Miłecki 		if (primary_offset > 0)
34605491d2cSKalle Valo 			ch_inf.sb = BRCMU_CHAN_SB_U;
34705491d2cSKalle Valo 		else
34805491d2cSKalle Valo 			ch_inf.sb = BRCMU_CHAN_SB_L;
34905491d2cSKalle Valo 		break;
35005491d2cSKalle Valo 	case NL80211_CHAN_WIDTH_80:
35105491d2cSKalle Valo 		ch_inf.bw = BRCMU_CHAN_BW_80;
35236e8072eSRafał Miłecki 		if (primary_offset == -30)
35305491d2cSKalle Valo 			ch_inf.sb = BRCMU_CHAN_SB_LL;
35436e8072eSRafał Miłecki 		else if (primary_offset == -10)
35505491d2cSKalle Valo 			ch_inf.sb = BRCMU_CHAN_SB_LU;
35636e8072eSRafał Miłecki 		else if (primary_offset == 10)
35736e8072eSRafał Miłecki 			ch_inf.sb = BRCMU_CHAN_SB_UL;
35836e8072eSRafał Miłecki 		else
35936e8072eSRafał Miłecki 			ch_inf.sb = BRCMU_CHAN_SB_UU;
36005491d2cSKalle Valo 		break;
36105491d2cSKalle Valo 	case NL80211_CHAN_WIDTH_160:
362f491645fSArend van Spriel 		ch_inf.bw = BRCMU_CHAN_BW_160;
363f491645fSArend van Spriel 		if (primary_offset == -70)
364f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_LLL;
365f491645fSArend van Spriel 		else if (primary_offset == -50)
366f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_LLU;
367f491645fSArend van Spriel 		else if (primary_offset == -30)
368f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_LUL;
369f491645fSArend van Spriel 		else if (primary_offset == -10)
370f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_LUU;
371f491645fSArend van Spriel 		else if (primary_offset == 10)
372f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_ULL;
373f491645fSArend van Spriel 		else if (primary_offset == 30)
374f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_ULU;
375f491645fSArend van Spriel 		else if (primary_offset == 50)
376f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_UUL;
377f491645fSArend van Spriel 		else
378f491645fSArend van Spriel 			ch_inf.sb = BRCMU_CHAN_SB_UUU;
379f491645fSArend van Spriel 		break;
380f491645fSArend van Spriel 	case NL80211_CHAN_WIDTH_80P80:
38105491d2cSKalle Valo 	case NL80211_CHAN_WIDTH_5:
38205491d2cSKalle Valo 	case NL80211_CHAN_WIDTH_10:
38305491d2cSKalle Valo 	default:
38405491d2cSKalle Valo 		WARN_ON_ONCE(1);
38505491d2cSKalle Valo 	}
38605491d2cSKalle Valo 	switch (ch->chan->band) {
38757fbcce3SJohannes Berg 	case NL80211_BAND_2GHZ:
38805491d2cSKalle Valo 		ch_inf.band = BRCMU_CHAN_BAND_2G;
38905491d2cSKalle Valo 		break;
39057fbcce3SJohannes Berg 	case NL80211_BAND_5GHZ:
39105491d2cSKalle Valo 		ch_inf.band = BRCMU_CHAN_BAND_5G;
39205491d2cSKalle Valo 		break;
39357fbcce3SJohannes Berg 	case NL80211_BAND_60GHZ:
39405491d2cSKalle Valo 	default:
39505491d2cSKalle Valo 		WARN_ON_ONCE(1);
39605491d2cSKalle Valo 	}
39705491d2cSKalle Valo 	d11inf->encchspec(&ch_inf);
39805491d2cSKalle Valo 
399f491645fSArend van Spriel 	brcmf_dbg(TRACE, "chanspec: 0x%x\n", ch_inf.chspec);
40005491d2cSKalle Valo 	return ch_inf.chspec;
40105491d2cSKalle Valo }
40205491d2cSKalle Valo 
channel_to_chanspec(struct brcmu_d11inf * d11inf,struct ieee80211_channel * ch)40305491d2cSKalle Valo u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
40405491d2cSKalle Valo 			struct ieee80211_channel *ch)
40505491d2cSKalle Valo {
40605491d2cSKalle Valo 	struct brcmu_chan ch_inf;
40705491d2cSKalle Valo 
40805491d2cSKalle Valo 	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
40905491d2cSKalle Valo 	ch_inf.bw = BRCMU_CHAN_BW_20;
41005491d2cSKalle Valo 	d11inf->encchspec(&ch_inf);
41105491d2cSKalle Valo 
41205491d2cSKalle Valo 	return ch_inf.chspec;
41305491d2cSKalle Valo }
41405491d2cSKalle Valo 
41505491d2cSKalle Valo /* Traverse a string of 1-byte tag/1-byte length/variable-length value
41605491d2cSKalle Valo  * triples, returning a pointer to the substring whose first element
41705491d2cSKalle Valo  * matches tag
41805491d2cSKalle Valo  */
419c8d87079SRafał Miłecki static const struct brcmf_tlv *
brcmf_parse_tlvs(const void * buf,int buflen,uint key)42005491d2cSKalle Valo brcmf_parse_tlvs(const void *buf, int buflen, uint key)
42105491d2cSKalle Valo {
42205491d2cSKalle Valo 	const struct brcmf_tlv *elt = buf;
42305491d2cSKalle Valo 	int totlen = buflen;
42405491d2cSKalle Valo 
42505491d2cSKalle Valo 	/* find tagged parameter */
42605491d2cSKalle Valo 	while (totlen >= TLV_HDR_LEN) {
42705491d2cSKalle Valo 		int len = elt->len;
42805491d2cSKalle Valo 
42905491d2cSKalle Valo 		/* validate remaining totlen */
43005491d2cSKalle Valo 		if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
43105491d2cSKalle Valo 			return elt;
43205491d2cSKalle Valo 
43305491d2cSKalle Valo 		elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
43405491d2cSKalle Valo 		totlen -= (len + TLV_HDR_LEN);
43505491d2cSKalle Valo 	}
43605491d2cSKalle Valo 
43705491d2cSKalle Valo 	return NULL;
43805491d2cSKalle Valo }
43905491d2cSKalle Valo 
44005491d2cSKalle Valo /* Is any of the tlvs the expected entry? If
44105491d2cSKalle Valo  * not update the tlvs buffer pointer/length.
44205491d2cSKalle Valo  */
44305491d2cSKalle Valo static bool
brcmf_tlv_has_ie(const u8 * ie,const u8 ** tlvs,u32 * tlvs_len,const u8 * oui,u32 oui_len,u8 type)44405491d2cSKalle Valo brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
44505491d2cSKalle Valo 		 const u8 *oui, u32 oui_len, u8 type)
44605491d2cSKalle Valo {
44705491d2cSKalle Valo 	/* If the contents match the OUI and the type */
44805491d2cSKalle Valo 	if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
44905491d2cSKalle Valo 	    !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
45005491d2cSKalle Valo 	    type == ie[TLV_BODY_OFF + oui_len]) {
45105491d2cSKalle Valo 		return true;
45205491d2cSKalle Valo 	}
45305491d2cSKalle Valo 
45405491d2cSKalle Valo 	if (tlvs == NULL)
45505491d2cSKalle Valo 		return false;
45605491d2cSKalle Valo 	/* point to the next ie */
45705491d2cSKalle Valo 	ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
45805491d2cSKalle Valo 	/* calculate the length of the rest of the buffer */
45905491d2cSKalle Valo 	*tlvs_len -= (int)(ie - *tlvs);
46005491d2cSKalle Valo 	/* update the pointer to the start of the buffer */
46105491d2cSKalle Valo 	*tlvs = ie;
46205491d2cSKalle Valo 
46305491d2cSKalle Valo 	return false;
46405491d2cSKalle Valo }
46505491d2cSKalle Valo 
46605491d2cSKalle Valo static struct brcmf_vs_tlv *
brcmf_find_wpaie(const u8 * parse,u32 len)46705491d2cSKalle Valo brcmf_find_wpaie(const u8 *parse, u32 len)
46805491d2cSKalle Valo {
46905491d2cSKalle Valo 	const struct brcmf_tlv *ie;
47005491d2cSKalle Valo 
47105491d2cSKalle Valo 	while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
47205491d2cSKalle Valo 		if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
47305491d2cSKalle Valo 				     WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
47405491d2cSKalle Valo 			return (struct brcmf_vs_tlv *)ie;
47505491d2cSKalle Valo 	}
47605491d2cSKalle Valo 	return NULL;
47705491d2cSKalle Valo }
47805491d2cSKalle Valo 
47905491d2cSKalle Valo static struct brcmf_vs_tlv *
brcmf_find_wpsie(const u8 * parse,u32 len)48005491d2cSKalle Valo brcmf_find_wpsie(const u8 *parse, u32 len)
48105491d2cSKalle Valo {
48205491d2cSKalle Valo 	const struct brcmf_tlv *ie;
48305491d2cSKalle Valo 
48405491d2cSKalle Valo 	while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
48505491d2cSKalle Valo 		if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
48605491d2cSKalle Valo 				     WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
48705491d2cSKalle Valo 			return (struct brcmf_vs_tlv *)ie;
48805491d2cSKalle Valo 	}
48905491d2cSKalle Valo 	return NULL;
49005491d2cSKalle Valo }
49105491d2cSKalle Valo 
brcmf_vif_change_validate(struct brcmf_cfg80211_info * cfg,struct brcmf_cfg80211_vif * vif,enum nl80211_iftype new_type)49205491d2cSKalle Valo static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
49305491d2cSKalle Valo 				     struct brcmf_cfg80211_vif *vif,
49405491d2cSKalle Valo 				     enum nl80211_iftype new_type)
49505491d2cSKalle Valo {
49605491d2cSKalle Valo 	struct brcmf_cfg80211_vif *pos;
497353c46acSArend van Spriel 	bool check_combos = false;
498353c46acSArend van Spriel 	int ret = 0;
499e227300cSPurushottam Kushwaha 	struct iface_combination_params params = {
500e227300cSPurushottam Kushwaha 		.num_different_channels = 1,
501e227300cSPurushottam Kushwaha 	};
50205491d2cSKalle Valo 
50305491d2cSKalle Valo 	list_for_each_entry(pos, &cfg->vif_list, list)
504353c46acSArend van Spriel 		if (pos == vif) {
505e227300cSPurushottam Kushwaha 			params.iftype_num[new_type]++;
506353c46acSArend van Spriel 		} else {
507353c46acSArend van Spriel 			/* concurrent interfaces so need check combinations */
508353c46acSArend van Spriel 			check_combos = true;
509e227300cSPurushottam Kushwaha 			params.iftype_num[pos->wdev.iftype]++;
510353c46acSArend van Spriel 		}
51105491d2cSKalle Valo 
512353c46acSArend van Spriel 	if (check_combos)
513e227300cSPurushottam Kushwaha 		ret = cfg80211_check_combinations(cfg->wiphy, &params);
514353c46acSArend van Spriel 
515353c46acSArend van Spriel 	return ret;
51605491d2cSKalle Valo }
51705491d2cSKalle Valo 
brcmf_vif_add_validate(struct brcmf_cfg80211_info * cfg,enum nl80211_iftype new_type)51805491d2cSKalle Valo static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
51905491d2cSKalle Valo 				  enum nl80211_iftype new_type)
52005491d2cSKalle Valo {
52105491d2cSKalle Valo 	struct brcmf_cfg80211_vif *pos;
522e227300cSPurushottam Kushwaha 	struct iface_combination_params params = {
523e227300cSPurushottam Kushwaha 		.num_different_channels = 1,
524e227300cSPurushottam Kushwaha 	};
52505491d2cSKalle Valo 
52605491d2cSKalle Valo 	list_for_each_entry(pos, &cfg->vif_list, list)
527e227300cSPurushottam Kushwaha 		params.iftype_num[pos->wdev.iftype]++;
52805491d2cSKalle Valo 
529e227300cSPurushottam Kushwaha 	params.iftype_num[new_type]++;
530e227300cSPurushottam Kushwaha 	return cfg80211_check_combinations(cfg->wiphy, &params);
53105491d2cSKalle Valo }
53205491d2cSKalle Valo 
convert_key_from_CPU(struct brcmf_wsec_key * key,struct brcmf_wsec_key_le * key_le)53305491d2cSKalle Valo static void convert_key_from_CPU(struct brcmf_wsec_key *key,
53405491d2cSKalle Valo 				 struct brcmf_wsec_key_le *key_le)
53505491d2cSKalle Valo {
53605491d2cSKalle Valo 	key_le->index = cpu_to_le32(key->index);
53705491d2cSKalle Valo 	key_le->len = cpu_to_le32(key->len);
53805491d2cSKalle Valo 	key_le->algo = cpu_to_le32(key->algo);
53905491d2cSKalle Valo 	key_le->flags = cpu_to_le32(key->flags);
54005491d2cSKalle Valo 	key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
54105491d2cSKalle Valo 	key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
54205491d2cSKalle Valo 	key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
54305491d2cSKalle Valo 	memcpy(key_le->data, key->data, sizeof(key->data));
54405491d2cSKalle Valo 	memcpy(key_le->ea, key->ea, sizeof(key->ea));
54505491d2cSKalle Valo }
54605491d2cSKalle Valo 
54705491d2cSKalle Valo static int
send_key_to_dongle(struct brcmf_if * ifp,struct brcmf_wsec_key * key)54805491d2cSKalle Valo send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
54905491d2cSKalle Valo {
55016e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
55105491d2cSKalle Valo 	int err;
55205491d2cSKalle Valo 	struct brcmf_wsec_key_le key_le;
55305491d2cSKalle Valo 
55405491d2cSKalle Valo 	convert_key_from_CPU(key, &key_le);
55505491d2cSKalle Valo 
55605491d2cSKalle Valo 	brcmf_netdev_wait_pend8021x(ifp);
55705491d2cSKalle Valo 
55805491d2cSKalle Valo 	err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
55905491d2cSKalle Valo 					sizeof(key_le));
56005491d2cSKalle Valo 
56105491d2cSKalle Valo 	if (err)
56216e64676SRafał Miłecki 		bphy_err(drvr, "wsec_key error (%d)\n", err);
56305491d2cSKalle Valo 	return err;
56405491d2cSKalle Valo }
56505491d2cSKalle Valo 
56605491d2cSKalle Valo static void
brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev * wdev)56705491d2cSKalle Valo brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
56805491d2cSKalle Valo {
56905491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
57005491d2cSKalle Valo 	struct brcmf_if *ifp;
57105491d2cSKalle Valo 
57205491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
57305491d2cSKalle Valo 	ifp = vif->ifp;
57405491d2cSKalle Valo 
57505491d2cSKalle Valo 	if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
57605491d2cSKalle Valo 	    (wdev->iftype == NL80211_IFTYPE_AP) ||
57705491d2cSKalle Valo 	    (wdev->iftype == NL80211_IFTYPE_P2P_GO))
57805491d2cSKalle Valo 		brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
57905491d2cSKalle Valo 						ADDR_DIRECT);
58005491d2cSKalle Valo 	else
58105491d2cSKalle Valo 		brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
58205491d2cSKalle Valo 						ADDR_INDIRECT);
58305491d2cSKalle Valo }
58405491d2cSKalle Valo 
brcmf_get_first_free_bsscfgidx(struct brcmf_pub * drvr)585d02fb8f1SRafał Miłecki static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
586d02fb8f1SRafał Miłecki {
587d02fb8f1SRafał Miłecki 	int bsscfgidx;
588d02fb8f1SRafał Miłecki 
589d02fb8f1SRafał Miłecki 	for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {
590d02fb8f1SRafał Miłecki 		/* bsscfgidx 1 is reserved for legacy P2P */
591d02fb8f1SRafał Miłecki 		if (bsscfgidx == 1)
592d02fb8f1SRafał Miłecki 			continue;
593d02fb8f1SRafał Miłecki 		if (!drvr->iflist[bsscfgidx])
594d02fb8f1SRafał Miłecki 			return bsscfgidx;
595d02fb8f1SRafał Miłecki 	}
596d02fb8f1SRafał Miłecki 
597d02fb8f1SRafał Miłecki 	return -ENOMEM;
598d02fb8f1SRafał Miłecki }
599d02fb8f1SRafał Miłecki 
brcmf_set_vif_sta_macaddr(struct brcmf_if * ifp,u8 * mac_addr)6004388827bSWright Feng static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr)
6012b5fb30fSWright Feng {
6022b5fb30fSWright Feng 	u8 mac_idx = ifp->drvr->sta_mac_idx;
6032b5fb30fSWright Feng 
6042b5fb30fSWright Feng 	/* set difference MAC address with locally administered bit */
6054388827bSWright Feng 	memcpy(mac_addr, ifp->mac_addr, ETH_ALEN);
6064388827bSWright Feng 	mac_addr[0] |= 0x02;
6074388827bSWright Feng 	mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0;
6082b5fb30fSWright Feng 	mac_idx++;
6092b5fb30fSWright Feng 	mac_idx = mac_idx % 2;
6102b5fb30fSWright Feng 	ifp->drvr->sta_mac_idx = mac_idx;
6112b5fb30fSWright Feng }
6122b5fb30fSWright Feng 
brcmf_cfg80211_request_sta_if(struct brcmf_if * ifp,u8 * macaddr)6132b5fb30fSWright Feng static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr)
6142b5fb30fSWright Feng {
6154388827bSWright Feng 	struct wl_interface_create_v1 iface_v1;
6164388827bSWright Feng 	struct wl_interface_create_v2 iface_v2;
6174388827bSWright Feng 	struct wl_interface_create_v3 iface_v3;
6184388827bSWright Feng 	u32 iface_create_ver;
6192b5fb30fSWright Feng 	int err;
6202b5fb30fSWright Feng 
6214388827bSWright Feng 	/* interface_create version 1 */
6224388827bSWright Feng 	memset(&iface_v1, 0, sizeof(iface_v1));
6234388827bSWright Feng 	iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
6244388827bSWright Feng 	iface_v1.flags = WL_INTERFACE_CREATE_STA |
6254388827bSWright Feng 			 WL_INTERFACE_MAC_USE;
6264388827bSWright Feng 	if (!is_zero_ether_addr(macaddr))
6274388827bSWright Feng 		memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN);
6284388827bSWright Feng 	else
6294388827bSWright Feng 		brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
6302b5fb30fSWright Feng 
6314388827bSWright Feng 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
6324388827bSWright Feng 				       &iface_v1,
6334388827bSWright Feng 				       sizeof(iface_v1));
6344388827bSWright Feng 	if (err) {
6354388827bSWright Feng 		brcmf_info("failed to create interface(v1), err=%d\n",
6364388827bSWright Feng 			   err);
6372b5fb30fSWright Feng 	} else {
6384388827bSWright Feng 		brcmf_dbg(INFO, "interface created(v1)\n");
6394388827bSWright Feng 		return 0;
6402b5fb30fSWright Feng 	}
6412b5fb30fSWright Feng 
6424388827bSWright Feng 	/* interface_create version 2 */
6434388827bSWright Feng 	memset(&iface_v2, 0, sizeof(iface_v2));
6444388827bSWright Feng 	iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
6454388827bSWright Feng 	iface_v2.flags = WL_INTERFACE_MAC_USE;
6464388827bSWright Feng 	iface_v2.iftype = WL_INTERFACE_CREATE_STA;
6474388827bSWright Feng 	if (!is_zero_ether_addr(macaddr))
6484388827bSWright Feng 		memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN);
6494388827bSWright Feng 	else
6504388827bSWright Feng 		brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
6514388827bSWright Feng 
6524388827bSWright Feng 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
6534388827bSWright Feng 				       &iface_v2,
6544388827bSWright Feng 				       sizeof(iface_v2));
6554388827bSWright Feng 	if (err) {
6564388827bSWright Feng 		brcmf_info("failed to create interface(v2), err=%d\n",
6574388827bSWright Feng 			   err);
6584388827bSWright Feng 	} else {
6594388827bSWright Feng 		brcmf_dbg(INFO, "interface created(v2)\n");
6604388827bSWright Feng 		return 0;
6614388827bSWright Feng 	}
6624388827bSWright Feng 
6634388827bSWright Feng 	/* interface_create version 3+ */
6644388827bSWright Feng 	/* get supported version from firmware side */
6654388827bSWright Feng 	iface_create_ver = 0;
666*fb1862ceSArend van Spriel 	err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",
6674388827bSWright Feng 					 &iface_create_ver);
6684388827bSWright Feng 	if (err) {
6694388827bSWright Feng 		brcmf_err("fail to get supported version, err=%d\n", err);
6704388827bSWright Feng 		return -EOPNOTSUPP;
6714388827bSWright Feng 	}
6724388827bSWright Feng 
6734388827bSWright Feng 	switch (iface_create_ver) {
6744388827bSWright Feng 	case WL_INTERFACE_CREATE_VER_3:
6754388827bSWright Feng 		memset(&iface_v3, 0, sizeof(iface_v3));
6764388827bSWright Feng 		iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
6774388827bSWright Feng 		iface_v3.flags = WL_INTERFACE_MAC_USE;
6784388827bSWright Feng 		iface_v3.iftype = WL_INTERFACE_CREATE_STA;
6794388827bSWright Feng 		if (!is_zero_ether_addr(macaddr))
6804388827bSWright Feng 			memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN);
6814388827bSWright Feng 		else
6824388827bSWright Feng 			brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
6834388827bSWright Feng 
6844388827bSWright Feng 		err = brcmf_fil_iovar_data_get(ifp, "interface_create",
6854388827bSWright Feng 					       &iface_v3,
6864388827bSWright Feng 					       sizeof(iface_v3));
6874388827bSWright Feng 
6884388827bSWright Feng 		if (!err)
6894388827bSWright Feng 			brcmf_dbg(INFO, "interface created(v3)\n");
6904388827bSWright Feng 		break;
6914388827bSWright Feng 	default:
6924388827bSWright Feng 		brcmf_err("not support interface create(v%d)\n",
6934388827bSWright Feng 			  iface_create_ver);
6944388827bSWright Feng 		err = -EOPNOTSUPP;
6954388827bSWright Feng 		break;
6964388827bSWright Feng 	}
6974388827bSWright Feng 
6984388827bSWright Feng 	if (err) {
6994388827bSWright Feng 		brcmf_info("station interface creation failed (%d)\n",
7004388827bSWright Feng 			   err);
7014388827bSWright Feng 		return -EIO;
7024388827bSWright Feng 	}
7034388827bSWright Feng 
7044388827bSWright Feng 	return 0;
7052b5fb30fSWright Feng }
7062b5fb30fSWright Feng 
brcmf_cfg80211_request_ap_if(struct brcmf_if * ifp)70705491d2cSKalle Valo static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
70805491d2cSKalle Valo {
7091562bdefSPrasanna Kerekoppa 	struct wl_interface_create_v1 iface_v1;
7101562bdefSPrasanna Kerekoppa 	struct wl_interface_create_v2 iface_v2;
7111562bdefSPrasanna Kerekoppa 	struct wl_interface_create_v3 iface_v3;
7121562bdefSPrasanna Kerekoppa 	u32 iface_create_ver;
71316e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
71405491d2cSKalle Valo 	struct brcmf_mbss_ssid_le mbss_ssid_le;
71505491d2cSKalle Valo 	int bsscfgidx;
71605491d2cSKalle Valo 	int err;
71705491d2cSKalle Valo 
7181562bdefSPrasanna Kerekoppa 	/* interface_create version 1 */
7191562bdefSPrasanna Kerekoppa 	memset(&iface_v1, 0, sizeof(iface_v1));
7201562bdefSPrasanna Kerekoppa 	iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
7211562bdefSPrasanna Kerekoppa 	iface_v1.flags = WL_INTERFACE_CREATE_AP |
7221562bdefSPrasanna Kerekoppa 			 WL_INTERFACE_MAC_USE;
7231562bdefSPrasanna Kerekoppa 
7241562bdefSPrasanna Kerekoppa 	brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
7251562bdefSPrasanna Kerekoppa 
7261562bdefSPrasanna Kerekoppa 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
7271562bdefSPrasanna Kerekoppa 				       &iface_v1,
7281562bdefSPrasanna Kerekoppa 				       sizeof(iface_v1));
7291562bdefSPrasanna Kerekoppa 	if (err) {
7301562bdefSPrasanna Kerekoppa 		brcmf_info("failed to create interface(v1), err=%d\n",
7311562bdefSPrasanna Kerekoppa 			   err);
7321562bdefSPrasanna Kerekoppa 	} else {
7331562bdefSPrasanna Kerekoppa 		brcmf_dbg(INFO, "interface created(v1)\n");
7341562bdefSPrasanna Kerekoppa 		return 0;
7351562bdefSPrasanna Kerekoppa 	}
7361562bdefSPrasanna Kerekoppa 
7371562bdefSPrasanna Kerekoppa 	/* interface_create version 2 */
7381562bdefSPrasanna Kerekoppa 	memset(&iface_v2, 0, sizeof(iface_v2));
7391562bdefSPrasanna Kerekoppa 	iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
7401562bdefSPrasanna Kerekoppa 	iface_v2.flags = WL_INTERFACE_MAC_USE;
7411562bdefSPrasanna Kerekoppa 	iface_v2.iftype = WL_INTERFACE_CREATE_AP;
7421562bdefSPrasanna Kerekoppa 
7431562bdefSPrasanna Kerekoppa 	brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
7441562bdefSPrasanna Kerekoppa 
7451562bdefSPrasanna Kerekoppa 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
7461562bdefSPrasanna Kerekoppa 				       &iface_v2,
7471562bdefSPrasanna Kerekoppa 				       sizeof(iface_v2));
7481562bdefSPrasanna Kerekoppa 	if (err) {
7491562bdefSPrasanna Kerekoppa 		brcmf_info("failed to create interface(v2), err=%d\n",
7501562bdefSPrasanna Kerekoppa 			   err);
7511562bdefSPrasanna Kerekoppa 	} else {
7521562bdefSPrasanna Kerekoppa 		brcmf_dbg(INFO, "interface created(v2)\n");
7531562bdefSPrasanna Kerekoppa 		return 0;
7541562bdefSPrasanna Kerekoppa 	}
7551562bdefSPrasanna Kerekoppa 
7561562bdefSPrasanna Kerekoppa 	/* interface_create version 3+ */
7571562bdefSPrasanna Kerekoppa 	/* get supported version from firmware side */
7581562bdefSPrasanna Kerekoppa 	iface_create_ver = 0;
759*fb1862ceSArend van Spriel 	err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",
7601562bdefSPrasanna Kerekoppa 					 &iface_create_ver);
7611562bdefSPrasanna Kerekoppa 	if (err) {
7621562bdefSPrasanna Kerekoppa 		brcmf_err("fail to get supported version, err=%d\n", err);
7631562bdefSPrasanna Kerekoppa 		return -EOPNOTSUPP;
7641562bdefSPrasanna Kerekoppa 	}
7651562bdefSPrasanna Kerekoppa 
7661562bdefSPrasanna Kerekoppa 	switch (iface_create_ver) {
7671562bdefSPrasanna Kerekoppa 	case WL_INTERFACE_CREATE_VER_3:
7681562bdefSPrasanna Kerekoppa 		memset(&iface_v3, 0, sizeof(iface_v3));
7691562bdefSPrasanna Kerekoppa 		iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
7701562bdefSPrasanna Kerekoppa 		iface_v3.flags = WL_INTERFACE_MAC_USE;
7711562bdefSPrasanna Kerekoppa 		iface_v3.iftype = WL_INTERFACE_CREATE_AP;
7721562bdefSPrasanna Kerekoppa 		brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
7731562bdefSPrasanna Kerekoppa 
7741562bdefSPrasanna Kerekoppa 		err = brcmf_fil_iovar_data_get(ifp, "interface_create",
7751562bdefSPrasanna Kerekoppa 					       &iface_v3,
7761562bdefSPrasanna Kerekoppa 					       sizeof(iface_v3));
7771562bdefSPrasanna Kerekoppa 
7781562bdefSPrasanna Kerekoppa 		if (!err)
7791562bdefSPrasanna Kerekoppa 			brcmf_dbg(INFO, "interface created(v3)\n");
7801562bdefSPrasanna Kerekoppa 		break;
7811562bdefSPrasanna Kerekoppa 	default:
7821562bdefSPrasanna Kerekoppa 		brcmf_err("not support interface create(v%d)\n",
7831562bdefSPrasanna Kerekoppa 			  iface_create_ver);
7841562bdefSPrasanna Kerekoppa 		err = -EOPNOTSUPP;
7851562bdefSPrasanna Kerekoppa 		break;
7861562bdefSPrasanna Kerekoppa 	}
7871562bdefSPrasanna Kerekoppa 
7881562bdefSPrasanna Kerekoppa 	if (err) {
7891562bdefSPrasanna Kerekoppa 		brcmf_info("Does not support interface_create (%d)\n",
7901562bdefSPrasanna Kerekoppa 			   err);
79105491d2cSKalle Valo 		memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
792d02fb8f1SRafał Miłecki 		bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);
79305491d2cSKalle Valo 		if (bsscfgidx < 0)
79405491d2cSKalle Valo 			return bsscfgidx;
79505491d2cSKalle Valo 
79605491d2cSKalle Valo 		mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
79705491d2cSKalle Valo 		mbss_ssid_le.SSID_len = cpu_to_le32(5);
79805491d2cSKalle Valo 		sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx);
79905491d2cSKalle Valo 
80005491d2cSKalle Valo 		err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
80105491d2cSKalle Valo 						sizeof(mbss_ssid_le));
8021562bdefSPrasanna Kerekoppa 
80305491d2cSKalle Valo 		if (err < 0)
80416e64676SRafał Miłecki 			bphy_err(drvr, "setting ssid failed %d\n", err);
8051562bdefSPrasanna Kerekoppa 	}
80605491d2cSKalle Valo 
80705491d2cSKalle Valo 	return err;
80805491d2cSKalle Valo }
80905491d2cSKalle Valo 
81005491d2cSKalle Valo /**
8112b5fb30fSWright Feng  * brcmf_apsta_add_vif() - create a new AP or STA virtual interface
81205491d2cSKalle Valo  *
81305491d2cSKalle Valo  * @wiphy: wiphy device of new interface.
81405491d2cSKalle Valo  * @name: name of the new interface.
8152b5fb30fSWright Feng  * @params: contains mac address for AP or STA device.
8162b5fb30fSWright Feng  * @type: interface type.
81705491d2cSKalle Valo  */
81805491d2cSKalle Valo static
brcmf_apsta_add_vif(struct wiphy * wiphy,const char * name,struct vif_params * params,enum nl80211_iftype type)8192b5fb30fSWright Feng struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name,
8202b5fb30fSWright Feng 					 struct vif_params *params,
8212b5fb30fSWright Feng 					 enum nl80211_iftype type)
82205491d2cSKalle Valo {
82305491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
82405491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
82516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
82605491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
82705491d2cSKalle Valo 	int err;
82805491d2cSKalle Valo 
8292b5fb30fSWright Feng 	if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP)
8302b5fb30fSWright Feng 		return ERR_PTR(-EINVAL);
8312b5fb30fSWright Feng 
83205491d2cSKalle Valo 	if (brcmf_cfg80211_vif_event_armed(cfg))
83305491d2cSKalle Valo 		return ERR_PTR(-EBUSY);
83405491d2cSKalle Valo 
83505491d2cSKalle Valo 	brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
83605491d2cSKalle Valo 
8372b5fb30fSWright Feng 	vif = brcmf_alloc_vif(cfg, type);
83805491d2cSKalle Valo 	if (IS_ERR(vif))
83905491d2cSKalle Valo 		return (struct wireless_dev *)vif;
84005491d2cSKalle Valo 
84105491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, vif);
84205491d2cSKalle Valo 
8432b5fb30fSWright Feng 	if (type == NL80211_IFTYPE_STATION)
8442b5fb30fSWright Feng 		err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr);
8452b5fb30fSWright Feng 	else
84605491d2cSKalle Valo 		err = brcmf_cfg80211_request_ap_if(ifp);
84705491d2cSKalle Valo 	if (err) {
84805491d2cSKalle Valo 		brcmf_cfg80211_arm_vif_event(cfg, NULL);
84905491d2cSKalle Valo 		goto fail;
85005491d2cSKalle Valo 	}
85105491d2cSKalle Valo 
85205491d2cSKalle Valo 	/* wait for firmware event */
853a9eb0c4bSArend van Spriel 	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
85463ce3d5dSArend van Spriel 					    BRCMF_VIF_EVENT_TIMEOUT);
85505491d2cSKalle Valo 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
85605491d2cSKalle Valo 	if (!err) {
85716e64676SRafał Miłecki 		bphy_err(drvr, "timeout occurred\n");
85805491d2cSKalle Valo 		err = -EIO;
85905491d2cSKalle Valo 		goto fail;
86005491d2cSKalle Valo 	}
86105491d2cSKalle Valo 
86205491d2cSKalle Valo 	/* interface created in firmware */
86305491d2cSKalle Valo 	ifp = vif->ifp;
86405491d2cSKalle Valo 	if (!ifp) {
86516e64676SRafał Miłecki 		bphy_err(drvr, "no if pointer provided\n");
86605491d2cSKalle Valo 		err = -ENOENT;
86705491d2cSKalle Valo 		goto fail;
86805491d2cSKalle Valo 	}
86905491d2cSKalle Valo 
87005491d2cSKalle Valo 	strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
87105491d2cSKalle Valo 	err = brcmf_net_attach(ifp, true);
87205491d2cSKalle Valo 	if (err) {
87316e64676SRafał Miłecki 		bphy_err(drvr, "Registering netdevice failed\n");
874dca2307eSArend Van Spriel 		free_netdev(ifp->ndev);
87505491d2cSKalle Valo 		goto fail;
87605491d2cSKalle Valo 	}
87705491d2cSKalle Valo 
87805491d2cSKalle Valo 	return &ifp->vif->wdev;
87905491d2cSKalle Valo 
88005491d2cSKalle Valo fail:
88105491d2cSKalle Valo 	brcmf_free_vif(vif);
88205491d2cSKalle Valo 	return ERR_PTR(err);
88305491d2cSKalle Valo }
88405491d2cSKalle Valo 
brcmf_is_apmode(struct brcmf_cfg80211_vif * vif)88505491d2cSKalle Valo static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
88605491d2cSKalle Valo {
88705491d2cSKalle Valo 	enum nl80211_iftype iftype;
88805491d2cSKalle Valo 
88905491d2cSKalle Valo 	iftype = vif->wdev.iftype;
89005491d2cSKalle Valo 	return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
89105491d2cSKalle Valo }
89205491d2cSKalle Valo 
brcmf_is_ibssmode(struct brcmf_cfg80211_vif * vif)89305491d2cSKalle Valo static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
89405491d2cSKalle Valo {
89505491d2cSKalle Valo 	return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
89605491d2cSKalle Valo }
89705491d2cSKalle Valo 
89820f2c5faSRafał Miłecki /**
89920f2c5faSRafał Miłecki  * brcmf_mon_add_vif() - create monitor mode virtual interface
90020f2c5faSRafał Miłecki  *
90120f2c5faSRafał Miłecki  * @wiphy: wiphy device of new interface.
90220f2c5faSRafał Miłecki  * @name: name of the new interface.
90320f2c5faSRafał Miłecki  */
brcmf_mon_add_vif(struct wiphy * wiphy,const char * name)90420f2c5faSRafał Miłecki static struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy,
90520f2c5faSRafał Miłecki 					      const char *name)
90620f2c5faSRafał Miłecki {
90720f2c5faSRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
90820f2c5faSRafał Miłecki 	struct brcmf_cfg80211_vif *vif;
90920f2c5faSRafał Miłecki 	struct net_device *ndev;
91020f2c5faSRafał Miłecki 	struct brcmf_if *ifp;
91120f2c5faSRafał Miłecki 	int err;
91220f2c5faSRafał Miłecki 
91320f2c5faSRafał Miłecki 	if (cfg->pub->mon_if) {
91420f2c5faSRafał Miłecki 		err = -EEXIST;
91520f2c5faSRafał Miłecki 		goto err_out;
91620f2c5faSRafał Miłecki 	}
91720f2c5faSRafał Miłecki 
91820f2c5faSRafał Miłecki 	vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_MONITOR);
91920f2c5faSRafał Miłecki 	if (IS_ERR(vif)) {
92020f2c5faSRafał Miłecki 		err = PTR_ERR(vif);
92120f2c5faSRafał Miłecki 		goto err_out;
92220f2c5faSRafał Miłecki 	}
92320f2c5faSRafał Miłecki 
92420f2c5faSRafał Miłecki 	ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup);
92520f2c5faSRafał Miłecki 	if (!ndev) {
92620f2c5faSRafał Miłecki 		err = -ENOMEM;
92720f2c5faSRafał Miłecki 		goto err_free_vif;
92820f2c5faSRafał Miłecki 	}
92920f2c5faSRafał Miłecki 	ndev->type = ARPHRD_IEEE80211_RADIOTAP;
93020f2c5faSRafał Miłecki 	ndev->ieee80211_ptr = &vif->wdev;
93120f2c5faSRafał Miłecki 	ndev->needs_free_netdev = true;
93220f2c5faSRafał Miłecki 	ndev->priv_destructor = brcmf_cfg80211_free_netdev;
93320f2c5faSRafał Miłecki 	SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
93420f2c5faSRafał Miłecki 
93520f2c5faSRafał Miłecki 	ifp = netdev_priv(ndev);
93620f2c5faSRafał Miłecki 	ifp->vif = vif;
93720f2c5faSRafał Miłecki 	ifp->ndev = ndev;
93820f2c5faSRafał Miłecki 	ifp->drvr = cfg->pub;
93920f2c5faSRafał Miłecki 
94020f2c5faSRafał Miłecki 	vif->ifp = ifp;
94120f2c5faSRafał Miłecki 	vif->wdev.netdev = ndev;
94220f2c5faSRafał Miłecki 
94320f2c5faSRafał Miłecki 	err = brcmf_net_mon_attach(ifp);
94420f2c5faSRafał Miłecki 	if (err) {
94520f2c5faSRafał Miłecki 		brcmf_err("Failed to attach %s device\n", ndev->name);
94620f2c5faSRafał Miłecki 		free_netdev(ndev);
94720f2c5faSRafał Miłecki 		goto err_free_vif;
94820f2c5faSRafał Miłecki 	}
94920f2c5faSRafał Miłecki 
95020f2c5faSRafał Miłecki 	cfg->pub->mon_if = ifp;
95120f2c5faSRafał Miłecki 
95220f2c5faSRafał Miłecki 	return &vif->wdev;
95320f2c5faSRafał Miłecki 
95420f2c5faSRafał Miłecki err_free_vif:
95520f2c5faSRafał Miłecki 	brcmf_free_vif(vif);
95620f2c5faSRafał Miłecki err_out:
95720f2c5faSRafał Miłecki 	return ERR_PTR(err);
95820f2c5faSRafał Miłecki }
95920f2c5faSRafał Miłecki 
brcmf_mon_del_vif(struct wiphy * wiphy,struct wireless_dev * wdev)96020f2c5faSRafał Miłecki static int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
96120f2c5faSRafał Miłecki {
96220f2c5faSRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
96320f2c5faSRafał Miłecki 	struct net_device *ndev = wdev->netdev;
96420f2c5faSRafał Miłecki 
96520f2c5faSRafał Miłecki 	ndev->netdev_ops->ndo_stop(ndev);
96620f2c5faSRafał Miłecki 
96720f2c5faSRafał Miłecki 	brcmf_net_detach(ndev, true);
96820f2c5faSRafał Miłecki 
96920f2c5faSRafał Miłecki 	cfg->pub->mon_if = NULL;
97020f2c5faSRafał Miłecki 
97120f2c5faSRafał Miłecki 	return 0;
97220f2c5faSRafał Miłecki }
97320f2c5faSRafał Miłecki 
brcmf_cfg80211_add_iface(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,struct vif_params * params)97405491d2cSKalle Valo static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
97505491d2cSKalle Valo 						     const char *name,
97605491d2cSKalle Valo 						     unsigned char name_assign_type,
97705491d2cSKalle Valo 						     enum nl80211_iftype type,
97805491d2cSKalle Valo 						     struct vif_params *params)
97905491d2cSKalle Valo {
98016e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
98116e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
98205491d2cSKalle Valo 	struct wireless_dev *wdev;
98305491d2cSKalle Valo 	int err;
98405491d2cSKalle Valo 
98505491d2cSKalle Valo 	brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
98605491d2cSKalle Valo 	err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
98705491d2cSKalle Valo 	if (err) {
98816e64676SRafał Miłecki 		bphy_err(drvr, "iface validation failed: err=%d\n", err);
98905491d2cSKalle Valo 		return ERR_PTR(err);
99005491d2cSKalle Valo 	}
99105491d2cSKalle Valo 	switch (type) {
99205491d2cSKalle Valo 	case NL80211_IFTYPE_ADHOC:
99305491d2cSKalle Valo 	case NL80211_IFTYPE_AP_VLAN:
99405491d2cSKalle Valo 	case NL80211_IFTYPE_WDS:
99505491d2cSKalle Valo 	case NL80211_IFTYPE_MESH_POINT:
99605491d2cSKalle Valo 		return ERR_PTR(-EOPNOTSUPP);
99720f2c5faSRafał Miłecki 	case NL80211_IFTYPE_MONITOR:
99820f2c5faSRafał Miłecki 		return brcmf_mon_add_vif(wiphy, name);
9992b5fb30fSWright Feng 	case NL80211_IFTYPE_STATION:
100005491d2cSKalle Valo 	case NL80211_IFTYPE_AP:
10012b5fb30fSWright Feng 		wdev = brcmf_apsta_add_vif(wiphy, name, params, type);
10020cd33c20SRafał Miłecki 		break;
100305491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_CLIENT:
100405491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_GO:
100505491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_DEVICE:
1006818a986eSJohannes Berg 		wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params);
10070cd33c20SRafał Miłecki 		break;
100805491d2cSKalle Valo 	case NL80211_IFTYPE_UNSPECIFIED:
100905491d2cSKalle Valo 	default:
101005491d2cSKalle Valo 		return ERR_PTR(-EINVAL);
101105491d2cSKalle Valo 	}
10120cd33c20SRafał Miłecki 
10130cd33c20SRafał Miłecki 	if (IS_ERR(wdev))
101416e64676SRafał Miłecki 		bphy_err(drvr, "add iface %s type %d failed: err=%d\n", name,
10153ef005b8SRafał Miłecki 			 type, (int)PTR_ERR(wdev));
10160cd33c20SRafał Miłecki 	else
10170cd33c20SRafał Miłecki 		brcmf_cfg80211_update_proto_addr_mode(wdev);
10180cd33c20SRafał Miłecki 
10190cd33c20SRafał Miłecki 	return wdev;
102005491d2cSKalle Valo }
102105491d2cSKalle Valo 
brcmf_scan_config_mpc(struct brcmf_if * ifp,int mpc)102205491d2cSKalle Valo static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
102305491d2cSKalle Valo {
102405491d2cSKalle Valo 	if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
102505491d2cSKalle Valo 		brcmf_set_mpc(ifp, mpc);
102605491d2cSKalle Valo }
102705491d2cSKalle Valo 
brcmf_set_mpc(struct brcmf_if * ifp,int mpc)102805491d2cSKalle Valo void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
102905491d2cSKalle Valo {
103016e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
103105491d2cSKalle Valo 	s32 err = 0;
103205491d2cSKalle Valo 
103305491d2cSKalle Valo 	if (check_vif_up(ifp->vif)) {
103405491d2cSKalle Valo 		err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
103505491d2cSKalle Valo 		if (err) {
103616e64676SRafał Miłecki 			bphy_err(drvr, "fail to set mpc\n");
103705491d2cSKalle Valo 			return;
103805491d2cSKalle Valo 		}
103905491d2cSKalle Valo 		brcmf_dbg(INFO, "MPC : %d\n", mpc);
104005491d2cSKalle Valo 	}
104105491d2cSKalle Valo }
104205491d2cSKalle Valo 
brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le * params_v2_le,struct brcmf_scan_params_le * params_le)1043398ce273SHector Martin static void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le,
1044398ce273SHector Martin 				       struct brcmf_scan_params_le *params_le)
1045398ce273SHector Martin {
1046398ce273SHector Martin 	size_t params_size;
1047398ce273SHector Martin 	u32 ch;
1048398ce273SHector Martin 	int n_channels, n_ssids;
1049398ce273SHector Martin 
1050398ce273SHector Martin 	memcpy(&params_le->ssid_le, &params_v2_le->ssid_le,
1051398ce273SHector Martin 	       sizeof(params_le->ssid_le));
1052398ce273SHector Martin 	memcpy(&params_le->bssid, &params_v2_le->bssid,
1053398ce273SHector Martin 	       sizeof(params_le->bssid));
1054398ce273SHector Martin 
1055398ce273SHector Martin 	params_le->bss_type = params_v2_le->bss_type;
1056398ce273SHector Martin 	params_le->scan_type = le32_to_cpu(params_v2_le->scan_type);
1057398ce273SHector Martin 	params_le->nprobes = params_v2_le->nprobes;
1058398ce273SHector Martin 	params_le->active_time = params_v2_le->active_time;
1059398ce273SHector Martin 	params_le->passive_time = params_v2_le->passive_time;
1060398ce273SHector Martin 	params_le->home_time = params_v2_le->home_time;
1061398ce273SHector Martin 	params_le->channel_num = params_v2_le->channel_num;
1062398ce273SHector Martin 
1063398ce273SHector Martin 	ch = le32_to_cpu(params_v2_le->channel_num);
1064398ce273SHector Martin 	n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK;
1065398ce273SHector Martin 	n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT;
1066398ce273SHector Martin 
1067398ce273SHector Martin 	params_size = sizeof(u16) * n_channels;
1068398ce273SHector Martin 	if (n_ssids > 0) {
1069398ce273SHector Martin 		params_size = roundup(params_size, sizeof(u32));
1070398ce273SHector Martin 		params_size += sizeof(struct brcmf_ssid_le) * n_ssids;
1071398ce273SHector Martin 	}
1072398ce273SHector Martin 
1073398ce273SHector Martin 	memcpy(&params_le->channel_list[0],
1074398ce273SHector Martin 	       &params_v2_le->channel_list[0], params_size);
1075398ce273SHector Martin }
1076398ce273SHector Martin 
brcmf_escan_prep(struct brcmf_cfg80211_info * cfg,struct brcmf_scan_params_v2_le * params_le,struct cfg80211_scan_request * request)1077398ce273SHector Martin static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
1078398ce273SHector Martin 			     struct brcmf_scan_params_v2_le *params_le,
1079398ce273SHector Martin 			     struct cfg80211_scan_request *request)
1080398ce273SHector Martin {
1081398ce273SHector Martin 	u32 n_ssids;
1082398ce273SHector Martin 	u32 n_channels;
1083398ce273SHector Martin 	s32 i;
1084398ce273SHector Martin 	s32 offset;
1085398ce273SHector Martin 	u16 chanspec;
1086398ce273SHector Martin 	char *ptr;
1087398ce273SHector Martin 	int length;
1088398ce273SHector Martin 	struct brcmf_ssid_le ssid_le;
1089398ce273SHector Martin 
1090398ce273SHector Martin 	eth_broadcast_addr(params_le->bssid);
1091398ce273SHector Martin 
1092398ce273SHector Martin 	length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
1093398ce273SHector Martin 
1094398ce273SHector Martin 	params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);
1095398ce273SHector Martin 	params_le->bss_type = DOT11_BSSTYPE_ANY;
1096398ce273SHector Martin 	params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE);
1097398ce273SHector Martin 	params_le->channel_num = 0;
1098398ce273SHector Martin 	params_le->nprobes = cpu_to_le32(-1);
1099398ce273SHector Martin 	params_le->active_time = cpu_to_le32(-1);
1100398ce273SHector Martin 	params_le->passive_time = cpu_to_le32(-1);
1101398ce273SHector Martin 	params_le->home_time = cpu_to_le32(-1);
1102398ce273SHector Martin 	memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
1103398ce273SHector Martin 
1104398ce273SHector Martin 	/* Scan abort */
1105398ce273SHector Martin 	if (!request) {
1106398ce273SHector Martin 		length += sizeof(u16);
1107398ce273SHector Martin 		params_le->channel_num = cpu_to_le32(1);
1108398ce273SHector Martin 		params_le->channel_list[0] = cpu_to_le16(-1);
1109398ce273SHector Martin 		params_le->length = cpu_to_le16(length);
1110398ce273SHector Martin 		return;
1111398ce273SHector Martin 	}
1112398ce273SHector Martin 
1113398ce273SHector Martin 	n_ssids = request->n_ssids;
1114398ce273SHector Martin 	n_channels = request->n_channels;
1115398ce273SHector Martin 
1116398ce273SHector Martin 	/* Copy channel array if applicable */
1117398ce273SHector Martin 	brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
1118398ce273SHector Martin 		  n_channels);
1119398ce273SHector Martin 	if (n_channels > 0) {
1120398ce273SHector Martin 		length += roundup(sizeof(u16) * n_channels, sizeof(u32));
1121398ce273SHector Martin 		for (i = 0; i < n_channels; i++) {
1122398ce273SHector Martin 			chanspec = channel_to_chanspec(&cfg->d11inf,
1123398ce273SHector Martin 						       request->channels[i]);
1124398ce273SHector Martin 			brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
1125398ce273SHector Martin 				  request->channels[i]->hw_value, chanspec);
1126398ce273SHector Martin 			params_le->channel_list[i] = cpu_to_le16(chanspec);
1127398ce273SHector Martin 		}
1128398ce273SHector Martin 	} else {
1129398ce273SHector Martin 		brcmf_dbg(SCAN, "Scanning all channels\n");
1130398ce273SHector Martin 	}
1131398ce273SHector Martin 
1132398ce273SHector Martin 	/* Copy ssid array if applicable */
1133398ce273SHector Martin 	brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
1134398ce273SHector Martin 	if (n_ssids > 0) {
1135398ce273SHector Martin 		offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) +
1136398ce273SHector Martin 				n_channels * sizeof(u16);
1137398ce273SHector Martin 		offset = roundup(offset, sizeof(u32));
1138398ce273SHector Martin 		length += sizeof(ssid_le) * n_ssids,
1139398ce273SHector Martin 		ptr = (char *)params_le + offset;
1140398ce273SHector Martin 		for (i = 0; i < n_ssids; i++) {
1141398ce273SHector Martin 			memset(&ssid_le, 0, sizeof(ssid_le));
1142398ce273SHector Martin 			ssid_le.SSID_len =
1143398ce273SHector Martin 					cpu_to_le32(request->ssids[i].ssid_len);
1144398ce273SHector Martin 			memcpy(ssid_le.SSID, request->ssids[i].ssid,
1145398ce273SHector Martin 			       request->ssids[i].ssid_len);
1146398ce273SHector Martin 			if (!ssid_le.SSID_len)
1147398ce273SHector Martin 				brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
1148398ce273SHector Martin 			else
1149398ce273SHector Martin 				brcmf_dbg(SCAN, "%d: scan for  %.32s size=%d\n",
1150398ce273SHector Martin 					  i, ssid_le.SSID, ssid_le.SSID_len);
1151398ce273SHector Martin 			memcpy(ptr, &ssid_le, sizeof(ssid_le));
1152398ce273SHector Martin 			ptr += sizeof(ssid_le);
1153398ce273SHector Martin 		}
1154398ce273SHector Martin 	} else {
1155398ce273SHector Martin 		brcmf_dbg(SCAN, "Performing passive scan\n");
1156398ce273SHector Martin 		params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE);
1157398ce273SHector Martin 	}
1158398ce273SHector Martin 	params_le->length = cpu_to_le16(length);
1159398ce273SHector Martin 	/* Adding mask to channel numbers */
1160398ce273SHector Martin 	params_le->channel_num =
1161398ce273SHector Martin 		cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
1162398ce273SHector Martin 			(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
1163398ce273SHector Martin }
1164398ce273SHector Martin 
brcmf_notify_escan_complete(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,bool aborted,bool fw_abort)116505491d2cSKalle Valo s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
116605491d2cSKalle Valo 				struct brcmf_if *ifp, bool aborted,
116705491d2cSKalle Valo 				bool fw_abort)
116805491d2cSKalle Valo {
116916e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
1170398ce273SHector Martin 	struct brcmf_scan_params_v2_le params_v2_le;
117105491d2cSKalle Valo 	struct cfg80211_scan_request *scan_request;
1172efc2c1faSArend Van Spriel 	u64 reqid;
1173efc2c1faSArend Van Spriel 	u32 bucket;
117405491d2cSKalle Valo 	s32 err = 0;
117505491d2cSKalle Valo 
117605491d2cSKalle Valo 	brcmf_dbg(SCAN, "Enter\n");
117705491d2cSKalle Valo 
117805491d2cSKalle Valo 	/* clear scan request, because the FW abort can cause a second call */
117905491d2cSKalle Valo 	/* to this functon and might cause a double cfg80211_scan_done      */
118005491d2cSKalle Valo 	scan_request = cfg->scan_request;
118105491d2cSKalle Valo 	cfg->scan_request = NULL;
118205491d2cSKalle Valo 
118319079484SZheng Wang 	timer_delete_sync(&cfg->escan_timeout);
118405491d2cSKalle Valo 
118505491d2cSKalle Valo 	if (fw_abort) {
118605491d2cSKalle Valo 		/* Do a scan abort to stop the driver's scan engine */
118705491d2cSKalle Valo 		brcmf_dbg(SCAN, "ABORT scan in firmware\n");
1188398ce273SHector Martin 
1189398ce273SHector Martin 		brcmf_escan_prep(cfg, &params_v2_le, NULL);
1190398ce273SHector Martin 
119105491d2cSKalle Valo 		/* E-Scan (or anyother type) can be aborted by SCAN */
1192398ce273SHector Martin 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
119305491d2cSKalle Valo 			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
1194398ce273SHector Martin 						     &params_v2_le,
1195398ce273SHector Martin 						     sizeof(params_v2_le));
1196398ce273SHector Martin 		} else {
1197398ce273SHector Martin 			struct brcmf_scan_params_le params_le;
1198398ce273SHector Martin 
1199398ce273SHector Martin 			brcmf_scan_params_v2_to_v1(&params_v2_le, &params_le);
1200398ce273SHector Martin 			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
1201398ce273SHector Martin 						     &params_le,
1202398ce273SHector Martin 						     sizeof(params_le));
1203398ce273SHector Martin 		}
1204398ce273SHector Martin 
120505491d2cSKalle Valo 		if (err)
120616e64676SRafał Miłecki 			bphy_err(drvr, "Scan abort failed\n");
120705491d2cSKalle Valo 	}
120805491d2cSKalle Valo 
120905491d2cSKalle Valo 	brcmf_scan_config_mpc(ifp, 1);
121005491d2cSKalle Valo 
121105491d2cSKalle Valo 	/*
1212fa85b30aSArend Van Spriel 	 * e-scan can be initiated internally
121305491d2cSKalle Valo 	 * which takes precedence.
121405491d2cSKalle Valo 	 */
1215efc2c1faSArend Van Spriel 	if (cfg->int_escan_map) {
1216efc2c1faSArend Van Spriel 		brcmf_dbg(SCAN, "scheduled scan completed (%x)\n",
1217efc2c1faSArend Van Spriel 			  cfg->int_escan_map);
1218efc2c1faSArend Van Spriel 		while (cfg->int_escan_map) {
1219efc2c1faSArend Van Spriel 			bucket = __ffs(cfg->int_escan_map);
1220efc2c1faSArend Van Spriel 			cfg->int_escan_map &= ~BIT(bucket);
1221efc2c1faSArend Van Spriel 			reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno,
1222efc2c1faSArend Van Spriel 							       bucket);
1223efc2c1faSArend Van Spriel 			if (!aborted) {
1224efc2c1faSArend Van Spriel 				brcmf_dbg(SCAN, "report results: reqid=%llu\n",
1225efc2c1faSArend Van Spriel 					  reqid);
1226efc2c1faSArend Van Spriel 				cfg80211_sched_scan_results(cfg_to_wiphy(cfg),
1227efc2c1faSArend Van Spriel 							    reqid);
1228efc2c1faSArend Van Spriel 			}
1229efc2c1faSArend Van Spriel 		}
123005491d2cSKalle Valo 	} else if (scan_request) {
12311d76250bSAvraham Stern 		struct cfg80211_scan_info info = {
12321d76250bSAvraham Stern 			.aborted = aborted,
12331d76250bSAvraham Stern 		};
12341d76250bSAvraham Stern 
123505491d2cSKalle Valo 		brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
123605491d2cSKalle Valo 			  aborted ? "Aborted" : "Done");
12371d76250bSAvraham Stern 		cfg80211_scan_done(scan_request, &info);
123805491d2cSKalle Valo 	}
123905491d2cSKalle Valo 	if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
124005491d2cSKalle Valo 		brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
124105491d2cSKalle Valo 
124205491d2cSKalle Valo 	return err;
124305491d2cSKalle Valo }
124405491d2cSKalle Valo 
brcmf_cfg80211_del_apsta_iface(struct wiphy * wiphy,struct wireless_dev * wdev)12452b5fb30fSWright Feng static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy,
1246dba8fbc6SRafał Miłecki 					  struct wireless_dev *wdev)
1247dba8fbc6SRafał Miłecki {
1248856d5a01SArend Van Spriel 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1249dba8fbc6SRafał Miłecki 	struct net_device *ndev = wdev->netdev;
1250dba8fbc6SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
125116e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
1252dba8fbc6SRafał Miłecki 	int ret;
1253dba8fbc6SRafał Miłecki 	int err;
1254dba8fbc6SRafał Miłecki 
1255dba8fbc6SRafał Miłecki 	brcmf_cfg80211_arm_vif_event(cfg, ifp->vif);
1256dba8fbc6SRafał Miłecki 
1257dba8fbc6SRafał Miłecki 	err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0);
1258dba8fbc6SRafał Miłecki 	if (err) {
125916e64676SRafał Miłecki 		bphy_err(drvr, "interface_remove failed %d\n", err);
1260dba8fbc6SRafał Miłecki 		goto err_unarm;
1261dba8fbc6SRafał Miłecki 	}
1262dba8fbc6SRafał Miłecki 
1263dba8fbc6SRafał Miłecki 	/* wait for firmware event */
1264dba8fbc6SRafał Miłecki 	ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
1265dba8fbc6SRafał Miłecki 					    BRCMF_VIF_EVENT_TIMEOUT);
1266dba8fbc6SRafał Miłecki 	if (!ret) {
126716e64676SRafał Miłecki 		bphy_err(drvr, "timeout occurred\n");
1268dba8fbc6SRafał Miłecki 		err = -EIO;
1269dba8fbc6SRafał Miłecki 		goto err_unarm;
1270dba8fbc6SRafał Miłecki 	}
1271dba8fbc6SRafał Miłecki 
1272dba8fbc6SRafał Miłecki 	brcmf_remove_interface(ifp, true);
1273dba8fbc6SRafał Miłecki 
1274dba8fbc6SRafał Miłecki err_unarm:
1275dba8fbc6SRafał Miłecki 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
1276dba8fbc6SRafał Miłecki 	return err;
1277dba8fbc6SRafał Miłecki }
1278dba8fbc6SRafał Miłecki 
127905491d2cSKalle Valo static
brcmf_cfg80211_del_iface(struct wiphy * wiphy,struct wireless_dev * wdev)128005491d2cSKalle Valo int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
128105491d2cSKalle Valo {
1282856d5a01SArend Van Spriel 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
128305491d2cSKalle Valo 	struct net_device *ndev = wdev->netdev;
128405491d2cSKalle Valo 
1285dba8fbc6SRafał Miłecki 	if (ndev && ndev == cfg_to_ndev(cfg))
1286dba8fbc6SRafał Miłecki 		return -ENOTSUPP;
1287dba8fbc6SRafał Miłecki 
128805491d2cSKalle Valo 	/* vif event pending in firmware */
128905491d2cSKalle Valo 	if (brcmf_cfg80211_vif_event_armed(cfg))
129005491d2cSKalle Valo 		return -EBUSY;
129105491d2cSKalle Valo 
129205491d2cSKalle Valo 	if (ndev) {
129305491d2cSKalle Valo 		if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
129405491d2cSKalle Valo 		    cfg->escan_info.ifp == netdev_priv(ndev))
129505491d2cSKalle Valo 			brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
129605491d2cSKalle Valo 						    true, true);
129705491d2cSKalle Valo 
129805491d2cSKalle Valo 		brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
129905491d2cSKalle Valo 	}
130005491d2cSKalle Valo 
130105491d2cSKalle Valo 	switch (wdev->iftype) {
130205491d2cSKalle Valo 	case NL80211_IFTYPE_ADHOC:
130305491d2cSKalle Valo 	case NL80211_IFTYPE_AP_VLAN:
130405491d2cSKalle Valo 	case NL80211_IFTYPE_WDS:
130505491d2cSKalle Valo 	case NL80211_IFTYPE_MESH_POINT:
130605491d2cSKalle Valo 		return -EOPNOTSUPP;
130720f2c5faSRafał Miłecki 	case NL80211_IFTYPE_MONITOR:
130820f2c5faSRafał Miłecki 		return brcmf_mon_del_vif(wiphy, wdev);
13092b5fb30fSWright Feng 	case NL80211_IFTYPE_STATION:
1310dba8fbc6SRafał Miłecki 	case NL80211_IFTYPE_AP:
13112b5fb30fSWright Feng 		return brcmf_cfg80211_del_apsta_iface(wiphy, wdev);
131205491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_CLIENT:
131305491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_GO:
131405491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_DEVICE:
131505491d2cSKalle Valo 		return brcmf_p2p_del_vif(wiphy, wdev);
131605491d2cSKalle Valo 	case NL80211_IFTYPE_UNSPECIFIED:
131705491d2cSKalle Valo 	default:
131805491d2cSKalle Valo 		return -EINVAL;
131905491d2cSKalle Valo 	}
132005491d2cSKalle Valo 	return -EOPNOTSUPP;
132105491d2cSKalle Valo }
132205491d2cSKalle Valo 
132305491d2cSKalle Valo static s32
brcmf_cfg80211_change_iface(struct wiphy * wiphy,struct net_device * ndev,enum nl80211_iftype type,struct vif_params * params)132405491d2cSKalle Valo brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
1325818a986eSJohannes Berg 			 enum nl80211_iftype type,
132605491d2cSKalle Valo 			 struct vif_params *params)
132705491d2cSKalle Valo {
1328856d5a01SArend Van Spriel 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
132905491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
133005491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif = ifp->vif;
133116e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
133205491d2cSKalle Valo 	s32 infra = 0;
133305491d2cSKalle Valo 	s32 ap = 0;
133405491d2cSKalle Valo 	s32 err = 0;
133505491d2cSKalle Valo 
133637a869ecSHante Meuleman 	brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
133737a869ecSHante Meuleman 		  type);
133805491d2cSKalle Valo 
133905491d2cSKalle Valo 	/* WAR: There are a number of p2p interface related problems which
134005491d2cSKalle Valo 	 * need to be handled initially (before doing the validate).
134105491d2cSKalle Valo 	 * wpa_supplicant tends to do iface changes on p2p device/client/go
134205491d2cSKalle Valo 	 * which are not always possible/allowed. However we need to return
134305491d2cSKalle Valo 	 * OK otherwise the wpa_supplicant wont start. The situation differs
134405491d2cSKalle Valo 	 * on configuration and setup (p2pon=1 module param). The first check
134505491d2cSKalle Valo 	 * is to see if the request is a change to station for p2p iface.
134605491d2cSKalle Valo 	 */
134705491d2cSKalle Valo 	if ((type == NL80211_IFTYPE_STATION) &&
134805491d2cSKalle Valo 	    ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
134905491d2cSKalle Valo 	     (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
135005491d2cSKalle Valo 	     (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
135105491d2cSKalle Valo 		brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
135205491d2cSKalle Valo 		/* Now depending on whether module param p2pon=1 was used the
135305491d2cSKalle Valo 		 * response needs to be either 0 or EOPNOTSUPP. The reason is
135405491d2cSKalle Valo 		 * that if p2pon=1 is used, but a newer supplicant is used then
135505491d2cSKalle Valo 		 * we should return an error, as this combination wont work.
135605491d2cSKalle Valo 		 * In other situations 0 is returned and supplicant will start
135705491d2cSKalle Valo 		 * normally. It will give a trace in cfg80211, but it is the
135805491d2cSKalle Valo 		 * only way to get it working. Unfortunately this will result
135905491d2cSKalle Valo 		 * in situation where we wont support new supplicant in
136005491d2cSKalle Valo 		 * combination with module param p2pon=1, but that is the way
136105491d2cSKalle Valo 		 * it is. If the user tries this then unloading of driver might
136205491d2cSKalle Valo 		 * fail/lock.
136305491d2cSKalle Valo 		 */
136405491d2cSKalle Valo 		if (cfg->p2p.p2pdev_dynamically)
136505491d2cSKalle Valo 			return -EOPNOTSUPP;
136605491d2cSKalle Valo 		else
136705491d2cSKalle Valo 			return 0;
136805491d2cSKalle Valo 	}
136905491d2cSKalle Valo 	err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
137005491d2cSKalle Valo 	if (err) {
137116e64676SRafał Miłecki 		bphy_err(drvr, "iface validation failed: err=%d\n", err);
137205491d2cSKalle Valo 		return err;
137305491d2cSKalle Valo 	}
137405491d2cSKalle Valo 	switch (type) {
137505491d2cSKalle Valo 	case NL80211_IFTYPE_MONITOR:
137605491d2cSKalle Valo 	case NL80211_IFTYPE_WDS:
137716e64676SRafał Miłecki 		bphy_err(drvr, "type (%d) : currently we do not support this type\n",
137805491d2cSKalle Valo 			 type);
137905491d2cSKalle Valo 		return -EOPNOTSUPP;
138005491d2cSKalle Valo 	case NL80211_IFTYPE_ADHOC:
138105491d2cSKalle Valo 		infra = 0;
138205491d2cSKalle Valo 		break;
138305491d2cSKalle Valo 	case NL80211_IFTYPE_STATION:
138405491d2cSKalle Valo 		infra = 1;
138505491d2cSKalle Valo 		break;
138605491d2cSKalle Valo 	case NL80211_IFTYPE_AP:
138705491d2cSKalle Valo 	case NL80211_IFTYPE_P2P_GO:
138805491d2cSKalle Valo 		ap = 1;
138905491d2cSKalle Valo 		break;
139005491d2cSKalle Valo 	default:
139105491d2cSKalle Valo 		err = -EINVAL;
139205491d2cSKalle Valo 		goto done;
139305491d2cSKalle Valo 	}
139405491d2cSKalle Valo 
139505491d2cSKalle Valo 	if (ap) {
139605491d2cSKalle Valo 		if (type == NL80211_IFTYPE_P2P_GO) {
139705491d2cSKalle Valo 			brcmf_dbg(INFO, "IF Type = P2P GO\n");
139805491d2cSKalle Valo 			err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
139905491d2cSKalle Valo 		}
140005491d2cSKalle Valo 		if (!err) {
140105491d2cSKalle Valo 			brcmf_dbg(INFO, "IF Type = AP\n");
140205491d2cSKalle Valo 		}
140305491d2cSKalle Valo 	} else {
140405491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
140505491d2cSKalle Valo 		if (err) {
140616e64676SRafał Miłecki 			bphy_err(drvr, "WLC_SET_INFRA error (%d)\n", err);
140705491d2cSKalle Valo 			err = -EAGAIN;
140805491d2cSKalle Valo 			goto done;
140905491d2cSKalle Valo 		}
141005491d2cSKalle Valo 		brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
141105491d2cSKalle Valo 			  "Adhoc" : "Infra");
141205491d2cSKalle Valo 	}
141305491d2cSKalle Valo 	ndev->ieee80211_ptr->iftype = type;
141405491d2cSKalle Valo 
141505491d2cSKalle Valo 	brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
141605491d2cSKalle Valo 
141705491d2cSKalle Valo done:
141805491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
141905491d2cSKalle Valo 
142005491d2cSKalle Valo 	return err;
142105491d2cSKalle Valo }
142205491d2cSKalle Valo 
142305491d2cSKalle Valo static s32
brcmf_run_escan(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,struct cfg80211_scan_request * request)142405491d2cSKalle Valo brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
1425c4958106SHante Meuleman 		struct cfg80211_scan_request *request)
142605491d2cSKalle Valo {
142716e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
1428398ce273SHector Martin 	s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE +
1429398ce273SHector Martin 			  offsetof(struct brcmf_escan_params_le, params_v2_le);
143005491d2cSKalle Valo 	struct brcmf_escan_params_le *params;
143105491d2cSKalle Valo 	s32 err = 0;
143205491d2cSKalle Valo 
143305491d2cSKalle Valo 	brcmf_dbg(SCAN, "E-SCAN START\n");
143405491d2cSKalle Valo 
143505491d2cSKalle Valo 	if (request != NULL) {
143605491d2cSKalle Valo 		/* Allocate space for populating ssids in struct */
143705491d2cSKalle Valo 		params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
143805491d2cSKalle Valo 
143905491d2cSKalle Valo 		/* Allocate space for populating ssids in struct */
1440e9a6ca82SHante Meuleman 		params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
144105491d2cSKalle Valo 	}
144205491d2cSKalle Valo 
144305491d2cSKalle Valo 	params = kzalloc(params_size, GFP_KERNEL);
144405491d2cSKalle Valo 	if (!params) {
144505491d2cSKalle Valo 		err = -ENOMEM;
144605491d2cSKalle Valo 		goto exit;
144705491d2cSKalle Valo 	}
144805491d2cSKalle Valo 	BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
1449398ce273SHector Martin 	brcmf_escan_prep(cfg, &params->params_v2_le, request);
1450398ce273SHector Martin 
1451398ce273SHector Martin 	params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2);
1452398ce273SHector Martin 
1453398ce273SHector Martin 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
1454398ce273SHector Martin 		struct brcmf_escan_params_le *params_v1;
1455398ce273SHector Martin 
1456398ce273SHector Martin 		params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
1457398ce273SHector Martin 		params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE;
1458398ce273SHector Martin 		params_v1 = kzalloc(params_size, GFP_KERNEL);
145907d69832SPetr Tesarik 		if (!params_v1) {
146007d69832SPetr Tesarik 			err = -ENOMEM;
146107d69832SPetr Tesarik 			goto exit_params;
146207d69832SPetr Tesarik 		}
1463398ce273SHector Martin 		params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
1464398ce273SHector Martin 		brcmf_scan_params_v2_to_v1(&params->params_v2_le, &params_v1->params_le);
1465398ce273SHector Martin 		kfree(params);
1466398ce273SHector Martin 		params = params_v1;
1467398ce273SHector Martin 	}
1468398ce273SHector Martin 
1469c4958106SHante Meuleman 	params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
147005491d2cSKalle Valo 	params->sync_id = cpu_to_le16(0x1234);
147105491d2cSKalle Valo 
147205491d2cSKalle Valo 	err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
147305491d2cSKalle Valo 	if (err) {
147405491d2cSKalle Valo 		if (err == -EBUSY)
147505491d2cSKalle Valo 			brcmf_dbg(INFO, "system busy : escan canceled\n");
147605491d2cSKalle Valo 		else
147716e64676SRafał Miłecki 			bphy_err(drvr, "error (%d)\n", err);
147805491d2cSKalle Valo 	}
147905491d2cSKalle Valo 
148007d69832SPetr Tesarik exit_params:
148105491d2cSKalle Valo 	kfree(params);
148205491d2cSKalle Valo exit:
148305491d2cSKalle Valo 	return err;
148405491d2cSKalle Valo }
148505491d2cSKalle Valo 
148605491d2cSKalle Valo static s32
brcmf_do_escan(struct brcmf_if * ifp,struct cfg80211_scan_request * request)1487ab5981c8SArend Van Spriel brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request)
148805491d2cSKalle Valo {
1489ab5981c8SArend Van Spriel 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
149005491d2cSKalle Valo 	s32 err;
149105491d2cSKalle Valo 	struct brcmf_scan_results *results;
149205491d2cSKalle Valo 	struct escan_info *escan = &cfg->escan_info;
149305491d2cSKalle Valo 
149405491d2cSKalle Valo 	brcmf_dbg(SCAN, "Enter\n");
149505491d2cSKalle Valo 	escan->ifp = ifp;
1496ab5981c8SArend Van Spriel 	escan->wiphy = cfg->wiphy;
149705491d2cSKalle Valo 	escan->escan_state = WL_ESCAN_STATE_SCANNING;
1498bbf35414SArend Van Spriel 
149905491d2cSKalle Valo 	brcmf_scan_config_mpc(ifp, 0);
150005491d2cSKalle Valo 	results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
150105491d2cSKalle Valo 	results->version = 0;
150205491d2cSKalle Valo 	results->count = 0;
150305491d2cSKalle Valo 	results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
150405491d2cSKalle Valo 
1505c4958106SHante Meuleman 	err = escan->run(cfg, ifp, request);
150605491d2cSKalle Valo 	if (err)
150705491d2cSKalle Valo 		brcmf_scan_config_mpc(ifp, 1);
150805491d2cSKalle Valo 	return err;
150905491d2cSKalle Valo }
151005491d2cSKalle Valo 
151105491d2cSKalle Valo static s32
brcmf_cfg80211_scan(struct wiphy * wiphy,struct cfg80211_scan_request * request)1512588378f1SArend Van Spriel brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
151305491d2cSKalle Valo {
151405491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
151516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
1516588378f1SArend Van Spriel 	struct brcmf_cfg80211_vif *vif;
1517588378f1SArend Van Spriel 	s32 err = 0;
151805491d2cSKalle Valo 
1519588378f1SArend Van Spriel 	brcmf_dbg(TRACE, "Enter\n");
1520588378f1SArend Van Spriel 	vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1521588378f1SArend Van Spriel 	if (!check_vif_up(vif))
1522588378f1SArend Van Spriel 		return -EIO;
152305491d2cSKalle Valo 
152405491d2cSKalle Valo 	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
152516e64676SRafał Miłecki 		bphy_err(drvr, "Scanning already: status (%lu)\n",
15263ef005b8SRafał Miłecki 			 cfg->scan_status);
152705491d2cSKalle Valo 		return -EAGAIN;
152805491d2cSKalle Valo 	}
152905491d2cSKalle Valo 	if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
153016e64676SRafał Miłecki 		bphy_err(drvr, "Scanning being aborted: status (%lu)\n",
153105491d2cSKalle Valo 			 cfg->scan_status);
153205491d2cSKalle Valo 		return -EAGAIN;
153305491d2cSKalle Valo 	}
153405491d2cSKalle Valo 	if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
153516e64676SRafał Miłecki 		bphy_err(drvr, "Scanning suppressed: status (%lu)\n",
153605491d2cSKalle Valo 			 cfg->scan_status);
153705491d2cSKalle Valo 		return -EAGAIN;
153805491d2cSKalle Valo 	}
15398c6efda2SArend Van Spriel 	if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) {
154016e64676SRafał Miłecki 		bphy_err(drvr, "Connecting: status (%lu)\n", vif->sme_state);
154105491d2cSKalle Valo 		return -EAGAIN;
154205491d2cSKalle Valo 	}
154305491d2cSKalle Valo 
154405491d2cSKalle Valo 	/* If scan req comes for p2p0, send it over primary I/F */
154505491d2cSKalle Valo 	if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
154605491d2cSKalle Valo 		vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
154705491d2cSKalle Valo 
1548588378f1SArend Van Spriel 	brcmf_dbg(SCAN, "START ESCAN\n");
1549588378f1SArend Van Spriel 
155005491d2cSKalle Valo 	cfg->scan_request = request;
155105491d2cSKalle Valo 	set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
15528c6efda2SArend Van Spriel 
155305491d2cSKalle Valo 	cfg->escan_info.run = brcmf_run_escan;
155405491d2cSKalle Valo 	err = brcmf_p2p_scan_prep(wiphy, request, vif);
155505491d2cSKalle Valo 	if (err)
155605491d2cSKalle Valo 		goto scan_out;
155705491d2cSKalle Valo 
1558bd99a301SArend Van Spriel 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
1559bd99a301SArend Van Spriel 				    request->ie, request->ie_len);
1560bd99a301SArend Van Spriel 	if (err)
1561bd99a301SArend Van Spriel 		goto scan_out;
1562bd99a301SArend Van Spriel 
1563ab5981c8SArend Van Spriel 	err = brcmf_do_escan(vif->ifp, request);
156405491d2cSKalle Valo 	if (err)
156505491d2cSKalle Valo 		goto scan_out;
156605491d2cSKalle Valo 
156705491d2cSKalle Valo 	/* Arm scan timeout timer */
1568df2d8388SArend Van Spriel 	mod_timer(&cfg->escan_timeout,
1569df2d8388SArend Van Spriel 		  jiffies + msecs_to_jiffies(BRCMF_ESCAN_TIMER_INTERVAL_MS));
157005491d2cSKalle Valo 
157105491d2cSKalle Valo 	return 0;
157205491d2cSKalle Valo 
157305491d2cSKalle Valo scan_out:
157416e64676SRafał Miłecki 	bphy_err(drvr, "scan error (%d)\n", err);
157505491d2cSKalle Valo 	clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
157605491d2cSKalle Valo 	cfg->scan_request = NULL;
157705491d2cSKalle Valo 	return err;
157805491d2cSKalle Valo }
157905491d2cSKalle Valo 
brcmf_set_rts(struct net_device * ndev,u32 rts_threshold)158005491d2cSKalle Valo static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
158105491d2cSKalle Valo {
15823ef005b8SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
158316e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
158405491d2cSKalle Valo 	s32 err = 0;
158505491d2cSKalle Valo 
15863ef005b8SRafał Miłecki 	err = brcmf_fil_iovar_int_set(ifp, "rtsthresh", rts_threshold);
158705491d2cSKalle Valo 	if (err)
158816e64676SRafał Miłecki 		bphy_err(drvr, "Error (%d)\n", err);
158905491d2cSKalle Valo 
159005491d2cSKalle Valo 	return err;
159105491d2cSKalle Valo }
159205491d2cSKalle Valo 
brcmf_set_frag(struct net_device * ndev,u32 frag_threshold)159305491d2cSKalle Valo static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
159405491d2cSKalle Valo {
15953ef005b8SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
159616e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
159705491d2cSKalle Valo 	s32 err = 0;
159805491d2cSKalle Valo 
15993ef005b8SRafał Miłecki 	err = brcmf_fil_iovar_int_set(ifp, "fragthresh",
160005491d2cSKalle Valo 				      frag_threshold);
160105491d2cSKalle Valo 	if (err)
160216e64676SRafał Miłecki 		bphy_err(drvr, "Error (%d)\n", err);
160305491d2cSKalle Valo 
160405491d2cSKalle Valo 	return err;
160505491d2cSKalle Valo }
160605491d2cSKalle Valo 
brcmf_set_retry(struct net_device * ndev,u32 retry,bool l)160705491d2cSKalle Valo static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
160805491d2cSKalle Valo {
16093ef005b8SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
161016e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
161105491d2cSKalle Valo 	s32 err = 0;
161205491d2cSKalle Valo 	u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
161305491d2cSKalle Valo 
16143ef005b8SRafał Miłecki 	err = brcmf_fil_cmd_int_set(ifp, cmd, retry);
161505491d2cSKalle Valo 	if (err) {
161616e64676SRafał Miłecki 		bphy_err(drvr, "cmd (%d) , error (%d)\n", cmd, err);
161705491d2cSKalle Valo 		return err;
161805491d2cSKalle Valo 	}
161905491d2cSKalle Valo 	return err;
162005491d2cSKalle Valo }
162105491d2cSKalle Valo 
brcmf_cfg80211_set_wiphy_params(struct wiphy * wiphy,u32 changed)162205491d2cSKalle Valo static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
162305491d2cSKalle Valo {
162405491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
162505491d2cSKalle Valo 	struct net_device *ndev = cfg_to_ndev(cfg);
162605491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
162705491d2cSKalle Valo 	s32 err = 0;
162805491d2cSKalle Valo 
162905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
163005491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
163105491d2cSKalle Valo 		return -EIO;
163205491d2cSKalle Valo 
163305491d2cSKalle Valo 	if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
163405491d2cSKalle Valo 	    (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
163505491d2cSKalle Valo 		cfg->conf->rts_threshold = wiphy->rts_threshold;
163605491d2cSKalle Valo 		err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
163705491d2cSKalle Valo 		if (!err)
163805491d2cSKalle Valo 			goto done;
163905491d2cSKalle Valo 	}
164005491d2cSKalle Valo 	if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
164105491d2cSKalle Valo 	    (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
164205491d2cSKalle Valo 		cfg->conf->frag_threshold = wiphy->frag_threshold;
164305491d2cSKalle Valo 		err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
164405491d2cSKalle Valo 		if (!err)
164505491d2cSKalle Valo 			goto done;
164605491d2cSKalle Valo 	}
164705491d2cSKalle Valo 	if (changed & WIPHY_PARAM_RETRY_LONG
164805491d2cSKalle Valo 	    && (cfg->conf->retry_long != wiphy->retry_long)) {
164905491d2cSKalle Valo 		cfg->conf->retry_long = wiphy->retry_long;
165005491d2cSKalle Valo 		err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
165105491d2cSKalle Valo 		if (!err)
165205491d2cSKalle Valo 			goto done;
165305491d2cSKalle Valo 	}
165405491d2cSKalle Valo 	if (changed & WIPHY_PARAM_RETRY_SHORT
165505491d2cSKalle Valo 	    && (cfg->conf->retry_short != wiphy->retry_short)) {
165605491d2cSKalle Valo 		cfg->conf->retry_short = wiphy->retry_short;
165705491d2cSKalle Valo 		err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
165805491d2cSKalle Valo 		if (!err)
165905491d2cSKalle Valo 			goto done;
166005491d2cSKalle Valo 	}
166105491d2cSKalle Valo 
166205491d2cSKalle Valo done:
166305491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
166405491d2cSKalle Valo 	return err;
166505491d2cSKalle Valo }
166605491d2cSKalle Valo 
brcmf_init_prof(struct brcmf_cfg80211_profile * prof)166705491d2cSKalle Valo static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
166805491d2cSKalle Valo {
166905491d2cSKalle Valo 	memset(prof, 0, sizeof(*prof));
167005491d2cSKalle Valo }
167105491d2cSKalle Valo 
brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg * e)167205491d2cSKalle Valo static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
167305491d2cSKalle Valo {
167405491d2cSKalle Valo 	u16 reason;
167505491d2cSKalle Valo 
167605491d2cSKalle Valo 	switch (e->event_code) {
167705491d2cSKalle Valo 	case BRCMF_E_DEAUTH:
167805491d2cSKalle Valo 	case BRCMF_E_DEAUTH_IND:
167905491d2cSKalle Valo 	case BRCMF_E_DISASSOC_IND:
168005491d2cSKalle Valo 		reason = e->reason;
168105491d2cSKalle Valo 		break;
168205491d2cSKalle Valo 	case BRCMF_E_LINK:
168305491d2cSKalle Valo 	default:
168405491d2cSKalle Valo 		reason = 0;
168505491d2cSKalle Valo 		break;
168605491d2cSKalle Valo 	}
168705491d2cSKalle Valo 	return reason;
168805491d2cSKalle Valo }
168905491d2cSKalle Valo 
brcmf_set_wsec(struct brcmf_if * ifp,const u8 * key,u16 key_len,u16 flags)16908d59a64cSHector Martin int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags)
1691b8a64f0eSArend van Spriel {
169216e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
1693b8a64f0eSArend van Spriel 	struct brcmf_wsec_pmk_le pmk;
169489b89e52SHector Martin 	int err;
1695b8a64f0eSArend van Spriel 
16968d59a64cSHector Martin 	if (key_len > sizeof(pmk.key)) {
16978d59a64cSHector Martin 		bphy_err(drvr, "key must be less than %zu bytes\n",
16988d59a64cSHector Martin 			 sizeof(pmk.key));
16998d59a64cSHector Martin 		return -EINVAL;
17008d59a64cSHector Martin 	}
17018d59a64cSHector Martin 
170289b89e52SHector Martin 	memset(&pmk, 0, sizeof(pmk));
170389b89e52SHector Martin 
17048d59a64cSHector Martin 	/* pass key material directly */
17058d59a64cSHector Martin 	pmk.key_len = cpu_to_le16(key_len);
17068d59a64cSHector Martin 	pmk.flags = cpu_to_le16(flags);
17078d59a64cSHector Martin 	memcpy(pmk.key, key, key_len);
1708b8a64f0eSArend van Spriel 
17098d59a64cSHector Martin 	/* store key material in firmware */
1710b8a64f0eSArend van Spriel 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
1711b8a64f0eSArend van Spriel 				     &pmk, sizeof(pmk));
1712b8a64f0eSArend van Spriel 	if (err < 0)
171316e64676SRafał Miłecki 		bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",
17148d59a64cSHector Martin 			 key_len);
1715b8a64f0eSArend van Spriel 
1716b8a64f0eSArend van Spriel 	return err;
1717b8a64f0eSArend van Spriel }
17188d59a64cSHector Martin BRCMF_EXPORT_SYMBOL_GPL(brcmf_set_wsec);
1719b8a64f0eSArend van Spriel 
brcmf_set_pmk(struct brcmf_if * ifp,const u8 * pmk_data,u16 pmk_len)17208d59a64cSHector Martin static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
17213b1e0a7bSChung-Hsien Hsu {
17228d59a64cSHector Martin 	return brcmf_set_wsec(ifp, pmk_data, pmk_len, 0);
17233b1e0a7bSChung-Hsien Hsu }
17243b1e0a7bSChung-Hsien Hsu 
brcmf_link_down(struct brcmf_cfg80211_vif * vif,u16 reason,bool locally_generated)17251b050d97SSoontak Lee static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,
17261b050d97SSoontak Lee 			    bool locally_generated)
172705491d2cSKalle Valo {
172805491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
172916e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
17301ac11ae9SArend van Spriel 	bool bus_up = drvr->bus_if->state == BRCMF_BUS_UP;
173105491d2cSKalle Valo 	s32 err = 0;
173205491d2cSKalle Valo 
173305491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
173405491d2cSKalle Valo 
1735b0a79088SHante Meuleman 	if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
17361ac11ae9SArend van Spriel 		if (bus_up) {
173705491d2cSKalle Valo 			brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n");
173805491d2cSKalle Valo 			err = brcmf_fil_cmd_data_set(vif->ifp,
173905491d2cSKalle Valo 						     BRCMF_C_DISASSOC, NULL, 0);
17401ac11ae9SArend van Spriel 			if (err)
17411ac11ae9SArend van Spriel 				bphy_err(drvr, "WLC_DISASSOC failed (%d)\n",
17421ac11ae9SArend van Spriel 					 err);
174305491d2cSKalle Valo 		}
17441ac11ae9SArend van Spriel 
1745b0a79088SHante Meuleman 		if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1746b0a79088SHante Meuleman 		    (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
174705491d2cSKalle Valo 			cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
17481b050d97SSoontak Lee 					      locally_generated, GFP_KERNEL);
174905491d2cSKalle Valo 	}
175005491d2cSKalle Valo 	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
175152617beeSWataru Gohda 	clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
175252617beeSWataru Gohda 	clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
175305491d2cSKalle Valo 	clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
175405491d2cSKalle Valo 	brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
1755b8a64f0eSArend van Spriel 	if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
17561ac11ae9SArend van Spriel 		if (bus_up)
1757b8a64f0eSArend van Spriel 			brcmf_set_pmk(vif->ifp, NULL, 0);
1758b8a64f0eSArend van Spriel 		vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
1759b8a64f0eSArend van Spriel 	}
176005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
176105491d2cSKalle Valo }
176205491d2cSKalle Valo 
176305491d2cSKalle Valo static s32
brcmf_cfg80211_join_ibss(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ibss_params * params)176405491d2cSKalle Valo brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
176505491d2cSKalle Valo 		      struct cfg80211_ibss_params *params)
176605491d2cSKalle Valo {
176705491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
176805491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
176905491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
177016e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
177105491d2cSKalle Valo 	struct brcmf_join_params join_params;
177205491d2cSKalle Valo 	size_t join_params_size = 0;
177305491d2cSKalle Valo 	s32 err = 0;
177405491d2cSKalle Valo 	s32 wsec = 0;
177505491d2cSKalle Valo 	s32 bcnprd;
177605491d2cSKalle Valo 	u16 chanspec;
1777e9a6ca82SHante Meuleman 	u32 ssid_len;
177805491d2cSKalle Valo 
177905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
178005491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
178105491d2cSKalle Valo 		return -EIO;
178205491d2cSKalle Valo 
178305491d2cSKalle Valo 	if (params->ssid)
178405491d2cSKalle Valo 		brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
178505491d2cSKalle Valo 	else {
178605491d2cSKalle Valo 		brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
178705491d2cSKalle Valo 		return -EOPNOTSUPP;
178805491d2cSKalle Valo 	}
178905491d2cSKalle Valo 
179005491d2cSKalle Valo 	set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
179105491d2cSKalle Valo 
179205491d2cSKalle Valo 	if (params->bssid)
179305491d2cSKalle Valo 		brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
179405491d2cSKalle Valo 	else
179505491d2cSKalle Valo 		brcmf_dbg(CONN, "No BSSID specified\n");
179605491d2cSKalle Valo 
179705491d2cSKalle Valo 	if (params->chandef.chan)
179805491d2cSKalle Valo 		brcmf_dbg(CONN, "channel: %d\n",
179905491d2cSKalle Valo 			  params->chandef.chan->center_freq);
180005491d2cSKalle Valo 	else
180105491d2cSKalle Valo 		brcmf_dbg(CONN, "no channel specified\n");
180205491d2cSKalle Valo 
180305491d2cSKalle Valo 	if (params->channel_fixed)
180405491d2cSKalle Valo 		brcmf_dbg(CONN, "fixed channel required\n");
180505491d2cSKalle Valo 	else
180605491d2cSKalle Valo 		brcmf_dbg(CONN, "no fixed channel required\n");
180705491d2cSKalle Valo 
180805491d2cSKalle Valo 	if (params->ie && params->ie_len)
180905491d2cSKalle Valo 		brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
181005491d2cSKalle Valo 	else
181105491d2cSKalle Valo 		brcmf_dbg(CONN, "no ie specified\n");
181205491d2cSKalle Valo 
181305491d2cSKalle Valo 	if (params->beacon_interval)
181405491d2cSKalle Valo 		brcmf_dbg(CONN, "beacon interval: %d\n",
181505491d2cSKalle Valo 			  params->beacon_interval);
181605491d2cSKalle Valo 	else
181705491d2cSKalle Valo 		brcmf_dbg(CONN, "no beacon interval specified\n");
181805491d2cSKalle Valo 
181905491d2cSKalle Valo 	if (params->basic_rates)
182005491d2cSKalle Valo 		brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
182105491d2cSKalle Valo 	else
182205491d2cSKalle Valo 		brcmf_dbg(CONN, "no basic rates specified\n");
182305491d2cSKalle Valo 
182405491d2cSKalle Valo 	if (params->privacy)
182505491d2cSKalle Valo 		brcmf_dbg(CONN, "privacy required\n");
182605491d2cSKalle Valo 	else
182705491d2cSKalle Valo 		brcmf_dbg(CONN, "no privacy required\n");
182805491d2cSKalle Valo 
182905491d2cSKalle Valo 	/* Configure Privacy for starter */
183005491d2cSKalle Valo 	if (params->privacy)
183105491d2cSKalle Valo 		wsec |= WEP_ENABLED;
183205491d2cSKalle Valo 
183305491d2cSKalle Valo 	err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
183405491d2cSKalle Valo 	if (err) {
183516e64676SRafał Miłecki 		bphy_err(drvr, "wsec failed (%d)\n", err);
183605491d2cSKalle Valo 		goto done;
183705491d2cSKalle Valo 	}
183805491d2cSKalle Valo 
183905491d2cSKalle Valo 	/* Configure Beacon Interval for starter */
184005491d2cSKalle Valo 	if (params->beacon_interval)
184105491d2cSKalle Valo 		bcnprd = params->beacon_interval;
184205491d2cSKalle Valo 	else
184305491d2cSKalle Valo 		bcnprd = 100;
184405491d2cSKalle Valo 
184505491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
184605491d2cSKalle Valo 	if (err) {
184716e64676SRafał Miłecki 		bphy_err(drvr, "WLC_SET_BCNPRD failed (%d)\n", err);
184805491d2cSKalle Valo 		goto done;
184905491d2cSKalle Valo 	}
185005491d2cSKalle Valo 
185105491d2cSKalle Valo 	/* Configure required join parameter */
185205491d2cSKalle Valo 	memset(&join_params, 0, sizeof(struct brcmf_join_params));
185305491d2cSKalle Valo 
185405491d2cSKalle Valo 	/* SSID */
1855e9a6ca82SHante Meuleman 	ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1856e9a6ca82SHante Meuleman 	memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1857e9a6ca82SHante Meuleman 	join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
185805491d2cSKalle Valo 	join_params_size = sizeof(join_params.ssid_le);
185905491d2cSKalle Valo 
186005491d2cSKalle Valo 	/* BSSID */
186105491d2cSKalle Valo 	if (params->bssid) {
186205491d2cSKalle Valo 		memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1863e9a6ca82SHante Meuleman 		join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
186405491d2cSKalle Valo 		memcpy(profile->bssid, params->bssid, ETH_ALEN);
186505491d2cSKalle Valo 	} else {
186605491d2cSKalle Valo 		eth_broadcast_addr(join_params.params_le.bssid);
186705491d2cSKalle Valo 		eth_zero_addr(profile->bssid);
186805491d2cSKalle Valo 	}
186905491d2cSKalle Valo 
187005491d2cSKalle Valo 	/* Channel */
187105491d2cSKalle Valo 	if (params->chandef.chan) {
187205491d2cSKalle Valo 		u32 target_channel;
187305491d2cSKalle Valo 
187405491d2cSKalle Valo 		cfg->channel =
187505491d2cSKalle Valo 			ieee80211_frequency_to_channel(
187605491d2cSKalle Valo 				params->chandef.chan->center_freq);
187705491d2cSKalle Valo 		if (params->channel_fixed) {
187805491d2cSKalle Valo 			/* adding chanspec */
187905491d2cSKalle Valo 			chanspec = chandef_to_chanspec(&cfg->d11inf,
188005491d2cSKalle Valo 						       &params->chandef);
188105491d2cSKalle Valo 			join_params.params_le.chanspec_list[0] =
188205491d2cSKalle Valo 				cpu_to_le16(chanspec);
188305491d2cSKalle Valo 			join_params.params_le.chanspec_num = cpu_to_le32(1);
188405491d2cSKalle Valo 			join_params_size += sizeof(join_params.params_le);
188505491d2cSKalle Valo 		}
188605491d2cSKalle Valo 
188705491d2cSKalle Valo 		/* set channel for starter */
188805491d2cSKalle Valo 		target_channel = cfg->channel;
188905491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
189005491d2cSKalle Valo 					    target_channel);
189105491d2cSKalle Valo 		if (err) {
189216e64676SRafał Miłecki 			bphy_err(drvr, "WLC_SET_CHANNEL failed (%d)\n", err);
189305491d2cSKalle Valo 			goto done;
189405491d2cSKalle Valo 		}
189505491d2cSKalle Valo 	} else
189605491d2cSKalle Valo 		cfg->channel = 0;
189705491d2cSKalle Valo 
189805491d2cSKalle Valo 	cfg->ibss_starter = false;
189905491d2cSKalle Valo 
190005491d2cSKalle Valo 
190105491d2cSKalle Valo 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
190205491d2cSKalle Valo 				     &join_params, join_params_size);
190305491d2cSKalle Valo 	if (err) {
190416e64676SRafał Miłecki 		bphy_err(drvr, "WLC_SET_SSID failed (%d)\n", err);
190505491d2cSKalle Valo 		goto done;
190605491d2cSKalle Valo 	}
190705491d2cSKalle Valo 
190805491d2cSKalle Valo done:
190905491d2cSKalle Valo 	if (err)
191005491d2cSKalle Valo 		clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
191105491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
191205491d2cSKalle Valo 	return err;
191305491d2cSKalle Valo }
191405491d2cSKalle Valo 
191505491d2cSKalle Valo static s32
brcmf_cfg80211_leave_ibss(struct wiphy * wiphy,struct net_device * ndev)191605491d2cSKalle Valo brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
191705491d2cSKalle Valo {
191805491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
191905491d2cSKalle Valo 
192005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
19216a98d64aSHante Meuleman 	if (!check_vif_up(ifp->vif)) {
19226a98d64aSHante Meuleman 		/* When driver is being unloaded, it can end up here. If an
19236a98d64aSHante Meuleman 		 * error is returned then later on a debug trace in the wireless
19246a98d64aSHante Meuleman 		 * core module will be printed. To avoid this 0 is returned.
19256a98d64aSHante Meuleman 		 */
19266a98d64aSHante Meuleman 		return 0;
19276a98d64aSHante Meuleman 	}
192805491d2cSKalle Valo 
19291b050d97SSoontak Lee 	brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING, true);
193042e0ed0dSHante Meuleman 	brcmf_net_setcarrier(ifp, false);
193105491d2cSKalle Valo 
193205491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
193305491d2cSKalle Valo 
193405491d2cSKalle Valo 	return 0;
193505491d2cSKalle Valo }
193605491d2cSKalle Valo 
brcmf_set_wpa_version(struct net_device * ndev,struct cfg80211_connect_params * sme)193705491d2cSKalle Valo static s32 brcmf_set_wpa_version(struct net_device *ndev,
193805491d2cSKalle Valo 				 struct cfg80211_connect_params *sme)
193905491d2cSKalle Valo {
19403ef005b8SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
194105491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
194216e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
194305491d2cSKalle Valo 	struct brcmf_cfg80211_security *sec;
194405491d2cSKalle Valo 	s32 val = 0;
194505491d2cSKalle Valo 	s32 err = 0;
194605491d2cSKalle Valo 
194705491d2cSKalle Valo 	if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
194805491d2cSKalle Valo 		val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
194905491d2cSKalle Valo 	else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
195005491d2cSKalle Valo 		val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
19513b1e0a7bSChung-Hsien Hsu 	else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
19523b1e0a7bSChung-Hsien Hsu 		val = WPA3_AUTH_SAE_PSK;
195305491d2cSKalle Valo 	else
195405491d2cSKalle Valo 		val = WPA_AUTH_DISABLED;
195505491d2cSKalle Valo 	brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
19563ef005b8SRafał Miłecki 	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val);
195705491d2cSKalle Valo 	if (err) {
195816e64676SRafał Miłecki 		bphy_err(drvr, "set wpa_auth failed (%d)\n", err);
195905491d2cSKalle Valo 		return err;
196005491d2cSKalle Valo 	}
196105491d2cSKalle Valo 	sec = &profile->sec;
196205491d2cSKalle Valo 	sec->wpa_versions = sme->crypto.wpa_versions;
196305491d2cSKalle Valo 	return err;
196405491d2cSKalle Valo }
196505491d2cSKalle Valo 
brcmf_set_auth_type(struct net_device * ndev,struct cfg80211_connect_params * sme)196605491d2cSKalle Valo static s32 brcmf_set_auth_type(struct net_device *ndev,
196705491d2cSKalle Valo 			       struct cfg80211_connect_params *sme)
196805491d2cSKalle Valo {
19693ef005b8SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
197005491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
197116e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
197205491d2cSKalle Valo 	struct brcmf_cfg80211_security *sec;
197305491d2cSKalle Valo 	s32 val = 0;
197405491d2cSKalle Valo 	s32 err = 0;
197505491d2cSKalle Valo 
197605491d2cSKalle Valo 	switch (sme->auth_type) {
197705491d2cSKalle Valo 	case NL80211_AUTHTYPE_OPEN_SYSTEM:
197805491d2cSKalle Valo 		val = 0;
197905491d2cSKalle Valo 		brcmf_dbg(CONN, "open system\n");
198005491d2cSKalle Valo 		break;
198105491d2cSKalle Valo 	case NL80211_AUTHTYPE_SHARED_KEY:
198205491d2cSKalle Valo 		val = 1;
198305491d2cSKalle Valo 		brcmf_dbg(CONN, "shared key\n");
198405491d2cSKalle Valo 		break;
19853b1e0a7bSChung-Hsien Hsu 	case NL80211_AUTHTYPE_SAE:
19863b1e0a7bSChung-Hsien Hsu 		val = 3;
19873b1e0a7bSChung-Hsien Hsu 		brcmf_dbg(CONN, "SAE authentication\n");
19883b1e0a7bSChung-Hsien Hsu 		break;
198905491d2cSKalle Valo 	default:
199005491d2cSKalle Valo 		val = 2;
199192c31360SHante Meuleman 		brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
199205491d2cSKalle Valo 		break;
199305491d2cSKalle Valo 	}
199405491d2cSKalle Valo 
19953ef005b8SRafał Miłecki 	err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
199605491d2cSKalle Valo 	if (err) {
199716e64676SRafał Miłecki 		bphy_err(drvr, "set auth failed (%d)\n", err);
199805491d2cSKalle Valo 		return err;
199905491d2cSKalle Valo 	}
200005491d2cSKalle Valo 	sec = &profile->sec;
200105491d2cSKalle Valo 	sec->auth_type = sme->auth_type;
200205491d2cSKalle Valo 	return err;
200305491d2cSKalle Valo }
200405491d2cSKalle Valo 
200505491d2cSKalle Valo static s32
brcmf_set_wsec_mode(struct net_device * ndev,struct cfg80211_connect_params * sme)200605491d2cSKalle Valo brcmf_set_wsec_mode(struct net_device *ndev,
2007240d61a9SHante Meuleman 		    struct cfg80211_connect_params *sme)
200805491d2cSKalle Valo {
20093ef005b8SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
201005491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
201116e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
201205491d2cSKalle Valo 	struct brcmf_cfg80211_security *sec;
201305491d2cSKalle Valo 	s32 pval = 0;
201405491d2cSKalle Valo 	s32 gval = 0;
201505491d2cSKalle Valo 	s32 wsec;
201605491d2cSKalle Valo 	s32 err = 0;
201705491d2cSKalle Valo 
201805491d2cSKalle Valo 	if (sme->crypto.n_ciphers_pairwise) {
201905491d2cSKalle Valo 		switch (sme->crypto.ciphers_pairwise[0]) {
202005491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_WEP40:
202105491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_WEP104:
202205491d2cSKalle Valo 			pval = WEP_ENABLED;
202305491d2cSKalle Valo 			break;
202405491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_TKIP:
202505491d2cSKalle Valo 			pval = TKIP_ENABLED;
202605491d2cSKalle Valo 			break;
202705491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_CCMP:
202805491d2cSKalle Valo 			pval = AES_ENABLED;
202905491d2cSKalle Valo 			break;
203005491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_AES_CMAC:
203105491d2cSKalle Valo 			pval = AES_ENABLED;
203205491d2cSKalle Valo 			break;
203305491d2cSKalle Valo 		default:
203416e64676SRafał Miłecki 			bphy_err(drvr, "invalid cipher pairwise (%d)\n",
203505491d2cSKalle Valo 				 sme->crypto.ciphers_pairwise[0]);
203605491d2cSKalle Valo 			return -EINVAL;
203705491d2cSKalle Valo 		}
203805491d2cSKalle Valo 	}
203905491d2cSKalle Valo 	if (sme->crypto.cipher_group) {
204005491d2cSKalle Valo 		switch (sme->crypto.cipher_group) {
204105491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_WEP40:
204205491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_WEP104:
204305491d2cSKalle Valo 			gval = WEP_ENABLED;
204405491d2cSKalle Valo 			break;
204505491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_TKIP:
204605491d2cSKalle Valo 			gval = TKIP_ENABLED;
204705491d2cSKalle Valo 			break;
204805491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_CCMP:
204905491d2cSKalle Valo 			gval = AES_ENABLED;
205005491d2cSKalle Valo 			break;
205105491d2cSKalle Valo 		case WLAN_CIPHER_SUITE_AES_CMAC:
205205491d2cSKalle Valo 			gval = AES_ENABLED;
205305491d2cSKalle Valo 			break;
205405491d2cSKalle Valo 		default:
205516e64676SRafał Miłecki 			bphy_err(drvr, "invalid cipher group (%d)\n",
205605491d2cSKalle Valo 				 sme->crypto.cipher_group);
205705491d2cSKalle Valo 			return -EINVAL;
205805491d2cSKalle Valo 		}
205905491d2cSKalle Valo 	}
206005491d2cSKalle Valo 
206105491d2cSKalle Valo 	brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
206205491d2cSKalle Valo 	/* In case of privacy, but no security and WPS then simulate */
206305491d2cSKalle Valo 	/* setting AES. WPS-2.0 allows no security                   */
206405491d2cSKalle Valo 	if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
206505491d2cSKalle Valo 	    sme->privacy)
206605491d2cSKalle Valo 		pval = AES_ENABLED;
206705491d2cSKalle Valo 
206805491d2cSKalle Valo 	wsec = pval | gval;
20693ef005b8SRafał Miłecki 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
207005491d2cSKalle Valo 	if (err) {
207116e64676SRafał Miłecki 		bphy_err(drvr, "error (%d)\n", err);
207205491d2cSKalle Valo 		return err;
207305491d2cSKalle Valo 	}
207405491d2cSKalle Valo 
207505491d2cSKalle Valo 	sec = &profile->sec;
207605491d2cSKalle Valo 	sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
207705491d2cSKalle Valo 	sec->cipher_group = sme->crypto.cipher_group;
207805491d2cSKalle Valo 
207905491d2cSKalle Valo 	return err;
208005491d2cSKalle Valo }
208105491d2cSKalle Valo 
208205491d2cSKalle Valo static s32
brcmf_set_key_mgmt(struct net_device * ndev,struct cfg80211_connect_params * sme)208305491d2cSKalle Valo brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
208405491d2cSKalle Valo {
2085240d61a9SHante Meuleman 	struct brcmf_if *ifp = netdev_priv(ndev);
20862526ff21SArend van Spriel 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
208716e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
2088240d61a9SHante Meuleman 	s32 val;
2089240d61a9SHante Meuleman 	s32 err;
2090240d61a9SHante Meuleman 	const struct brcmf_tlv *rsn_ie;
2091240d61a9SHante Meuleman 	const u8 *ie;
2092240d61a9SHante Meuleman 	u32 ie_len;
2093240d61a9SHante Meuleman 	u32 offset;
2094240d61a9SHante Meuleman 	u16 rsn_cap;
2095240d61a9SHante Meuleman 	u32 mfp;
2096240d61a9SHante Meuleman 	u16 count;
209705491d2cSKalle Valo 
20982526ff21SArend van Spriel 	profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
2099be898fedSChung-Hsien Hsu 	profile->is_ft = false;
21002526ff21SArend van Spriel 
2101240d61a9SHante Meuleman 	if (!sme->crypto.n_akm_suites)
2102240d61a9SHante Meuleman 		return 0;
2103240d61a9SHante Meuleman 
2104*fb1862ceSArend van Spriel 	err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
2105*fb1862ceSArend van Spriel 				       "wpa_auth", &val);
210605491d2cSKalle Valo 	if (err) {
210716e64676SRafał Miłecki 		bphy_err(drvr, "could not get wpa_auth (%d)\n", err);
210805491d2cSKalle Valo 		return err;
210905491d2cSKalle Valo 	}
211005491d2cSKalle Valo 	if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
211105491d2cSKalle Valo 		switch (sme->crypto.akm_suites[0]) {
211205491d2cSKalle Valo 		case WLAN_AKM_SUITE_8021X:
211305491d2cSKalle Valo 			val = WPA_AUTH_UNSPECIFIED;
21142526ff21SArend van Spriel 			if (sme->want_1x)
21152526ff21SArend van Spriel 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
211605491d2cSKalle Valo 			break;
211705491d2cSKalle Valo 		case WLAN_AKM_SUITE_PSK:
211805491d2cSKalle Valo 			val = WPA_AUTH_PSK;
211905491d2cSKalle Valo 			break;
212005491d2cSKalle Valo 		default:
2121beca6bd9SJames Prestwood 			bphy_err(drvr, "invalid akm suite (%d)\n",
2122beca6bd9SJames Prestwood 				 sme->crypto.akm_suites[0]);
212305491d2cSKalle Valo 			return -EINVAL;
212405491d2cSKalle Valo 		}
212505491d2cSKalle Valo 	} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
212605491d2cSKalle Valo 		switch (sme->crypto.akm_suites[0]) {
212705491d2cSKalle Valo 		case WLAN_AKM_SUITE_8021X:
212805491d2cSKalle Valo 			val = WPA2_AUTH_UNSPECIFIED;
21292526ff21SArend van Spriel 			if (sme->want_1x)
21302526ff21SArend van Spriel 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
213105491d2cSKalle Valo 			break;
2132240d61a9SHante Meuleman 		case WLAN_AKM_SUITE_8021X_SHA256:
2133240d61a9SHante Meuleman 			val = WPA2_AUTH_1X_SHA256;
21342526ff21SArend van Spriel 			if (sme->want_1x)
21352526ff21SArend van Spriel 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
2136240d61a9SHante Meuleman 			break;
2137240d61a9SHante Meuleman 		case WLAN_AKM_SUITE_PSK_SHA256:
2138240d61a9SHante Meuleman 			val = WPA2_AUTH_PSK_SHA256;
2139240d61a9SHante Meuleman 			break;
214005491d2cSKalle Valo 		case WLAN_AKM_SUITE_PSK:
214105491d2cSKalle Valo 			val = WPA2_AUTH_PSK;
214205491d2cSKalle Valo 			break;
21434ad298daSChung-Hsien Hsu 		case WLAN_AKM_SUITE_FT_8021X:
21444ad298daSChung-Hsien Hsu 			val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;
2145be898fedSChung-Hsien Hsu 			profile->is_ft = true;
2146a858376cSChung-Hsien Hsu 			if (sme->want_1x)
2147a858376cSChung-Hsien Hsu 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
21484ad298daSChung-Hsien Hsu 			break;
21494ad298daSChung-Hsien Hsu 		case WLAN_AKM_SUITE_FT_PSK:
21504ad298daSChung-Hsien Hsu 			val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
2151be898fedSChung-Hsien Hsu 			profile->is_ft = true;
21524ad298daSChung-Hsien Hsu 			break;
215305491d2cSKalle Valo 		default:
2154beca6bd9SJames Prestwood 			bphy_err(drvr, "invalid akm suite (%d)\n",
2155beca6bd9SJames Prestwood 				 sme->crypto.akm_suites[0]);
215605491d2cSKalle Valo 			return -EINVAL;
215705491d2cSKalle Valo 		}
21583b1e0a7bSChung-Hsien Hsu 	} else if (val & WPA3_AUTH_SAE_PSK) {
21593b1e0a7bSChung-Hsien Hsu 		switch (sme->crypto.akm_suites[0]) {
21603b1e0a7bSChung-Hsien Hsu 		case WLAN_AKM_SUITE_SAE:
21613b1e0a7bSChung-Hsien Hsu 			val = WPA3_AUTH_SAE_PSK;
2162b2fe11f0SChung-Hsien Hsu 			if (sme->crypto.sae_pwd) {
2163b2fe11f0SChung-Hsien Hsu 				brcmf_dbg(INFO, "using SAE offload\n");
2164b2fe11f0SChung-Hsien Hsu 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
2165b2fe11f0SChung-Hsien Hsu 			}
21663b1e0a7bSChung-Hsien Hsu 			break;
21674b51de06SPaweł Drewniak 		case WLAN_AKM_SUITE_FT_OVER_SAE:
21684b51de06SPaweł Drewniak 			val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT;
21694b51de06SPaweł Drewniak 			profile->is_ft = true;
21704b51de06SPaweł Drewniak 			if (sme->crypto.sae_pwd) {
21714b51de06SPaweł Drewniak 				brcmf_dbg(INFO, "using SAE offload\n");
21724b51de06SPaweł Drewniak 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
21734b51de06SPaweł Drewniak 			}
21744b51de06SPaweł Drewniak 			break;
21753b1e0a7bSChung-Hsien Hsu 		default:
2176beca6bd9SJames Prestwood 			bphy_err(drvr, "invalid akm suite (%d)\n",
2177beca6bd9SJames Prestwood 				 sme->crypto.akm_suites[0]);
21783b1e0a7bSChung-Hsien Hsu 			return -EINVAL;
21793b1e0a7bSChung-Hsien Hsu 		}
218005491d2cSKalle Valo 	}
218105491d2cSKalle Valo 
21822526ff21SArend van Spriel 	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
21832526ff21SArend van Spriel 		brcmf_dbg(INFO, "using 1X offload\n");
21842526ff21SArend van Spriel 
2185240d61a9SHante Meuleman 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
2186240d61a9SHante Meuleman 		goto skip_mfp_config;
2187240d61a9SHante Meuleman 	/* The MFP mode (1 or 2) needs to be determined, parse IEs. The
2188240d61a9SHante Meuleman 	 * IE will not be verified, just a quick search for MFP config
2189240d61a9SHante Meuleman 	 */
2190240d61a9SHante Meuleman 	rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
2191240d61a9SHante Meuleman 				  WLAN_EID_RSN);
2192240d61a9SHante Meuleman 	if (!rsn_ie)
2193240d61a9SHante Meuleman 		goto skip_mfp_config;
2194240d61a9SHante Meuleman 	ie = (const u8 *)rsn_ie;
2195240d61a9SHante Meuleman 	ie_len = rsn_ie->len + TLV_HDR_LEN;
2196240d61a9SHante Meuleman 	/* Skip unicast suite */
2197240d61a9SHante Meuleman 	offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
2198240d61a9SHante Meuleman 	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
2199240d61a9SHante Meuleman 		goto skip_mfp_config;
2200240d61a9SHante Meuleman 	/* Skip multicast suite */
2201240d61a9SHante Meuleman 	count = ie[offset] + (ie[offset + 1] << 8);
2202240d61a9SHante Meuleman 	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
2203240d61a9SHante Meuleman 	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
2204240d61a9SHante Meuleman 		goto skip_mfp_config;
2205240d61a9SHante Meuleman 	/* Skip auth key management suite(s) */
2206240d61a9SHante Meuleman 	count = ie[offset] + (ie[offset + 1] << 8);
2207240d61a9SHante Meuleman 	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
2208240d61a9SHante Meuleman 	if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
2209240d61a9SHante Meuleman 		goto skip_mfp_config;
2210240d61a9SHante Meuleman 	/* Ready to read capabilities */
2211240d61a9SHante Meuleman 	mfp = BRCMF_MFP_NONE;
2212240d61a9SHante Meuleman 	rsn_cap = ie[offset] + (ie[offset + 1] << 8);
2213240d61a9SHante Meuleman 	if (rsn_cap & RSN_CAP_MFPR_MASK)
2214240d61a9SHante Meuleman 		mfp = BRCMF_MFP_REQUIRED;
2215240d61a9SHante Meuleman 	else if (rsn_cap & RSN_CAP_MFPC_MASK)
2216240d61a9SHante Meuleman 		mfp = BRCMF_MFP_CAPABLE;
2217240d61a9SHante Meuleman 	brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
2218240d61a9SHante Meuleman 
2219240d61a9SHante Meuleman skip_mfp_config:
222005491d2cSKalle Valo 	brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
2221240d61a9SHante Meuleman 	err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
222205491d2cSKalle Valo 	if (err) {
222316e64676SRafał Miłecki 		bphy_err(drvr, "could not set wpa_auth (%d)\n", err);
222405491d2cSKalle Valo 		return err;
222505491d2cSKalle Valo 	}
222605491d2cSKalle Valo 
222705491d2cSKalle Valo 	return err;
222805491d2cSKalle Valo }
222905491d2cSKalle Valo 
223005491d2cSKalle Valo static s32
brcmf_set_sharedkey(struct net_device * ndev,struct cfg80211_connect_params * sme)223105491d2cSKalle Valo brcmf_set_sharedkey(struct net_device *ndev,
223205491d2cSKalle Valo 		    struct cfg80211_connect_params *sme)
223305491d2cSKalle Valo {
22343ef005b8SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
223516e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
223605491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
223705491d2cSKalle Valo 	struct brcmf_cfg80211_security *sec;
223805491d2cSKalle Valo 	struct brcmf_wsec_key key;
223905491d2cSKalle Valo 	s32 val;
224005491d2cSKalle Valo 	s32 err = 0;
224105491d2cSKalle Valo 
224205491d2cSKalle Valo 	brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
224305491d2cSKalle Valo 
224405491d2cSKalle Valo 	if (sme->key_len == 0)
224505491d2cSKalle Valo 		return 0;
224605491d2cSKalle Valo 
224705491d2cSKalle Valo 	sec = &profile->sec;
224805491d2cSKalle Valo 	brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
224905491d2cSKalle Valo 		  sec->wpa_versions, sec->cipher_pairwise);
225005491d2cSKalle Valo 
22513b1e0a7bSChung-Hsien Hsu 	if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |
22523b1e0a7bSChung-Hsien Hsu 				 NL80211_WPA_VERSION_3))
225305491d2cSKalle Valo 		return 0;
225405491d2cSKalle Valo 
225505491d2cSKalle Valo 	if (!(sec->cipher_pairwise &
225605491d2cSKalle Valo 	    (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
225705491d2cSKalle Valo 		return 0;
225805491d2cSKalle Valo 
225905491d2cSKalle Valo 	memset(&key, 0, sizeof(key));
226005491d2cSKalle Valo 	key.len = (u32) sme->key_len;
226105491d2cSKalle Valo 	key.index = (u32) sme->key_idx;
226205491d2cSKalle Valo 	if (key.len > sizeof(key.data)) {
226316e64676SRafał Miłecki 		bphy_err(drvr, "Too long key length (%u)\n", key.len);
226405491d2cSKalle Valo 		return -EINVAL;
226505491d2cSKalle Valo 	}
226605491d2cSKalle Valo 	memcpy(key.data, sme->key, key.len);
226705491d2cSKalle Valo 	key.flags = BRCMF_PRIMARY_KEY;
226805491d2cSKalle Valo 	switch (sec->cipher_pairwise) {
226905491d2cSKalle Valo 	case WLAN_CIPHER_SUITE_WEP40:
227005491d2cSKalle Valo 		key.algo = CRYPTO_ALGO_WEP1;
227105491d2cSKalle Valo 		break;
227205491d2cSKalle Valo 	case WLAN_CIPHER_SUITE_WEP104:
227305491d2cSKalle Valo 		key.algo = CRYPTO_ALGO_WEP128;
227405491d2cSKalle Valo 		break;
227505491d2cSKalle Valo 	default:
227616e64676SRafał Miłecki 		bphy_err(drvr, "Invalid algorithm (%d)\n",
227705491d2cSKalle Valo 			 sme->crypto.ciphers_pairwise[0]);
227805491d2cSKalle Valo 		return -EINVAL;
227905491d2cSKalle Valo 	}
228005491d2cSKalle Valo 	/* Set the new key/index */
228105491d2cSKalle Valo 	brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
228205491d2cSKalle Valo 		  key.len, key.index, key.algo);
228305491d2cSKalle Valo 	brcmf_dbg(CONN, "key \"%s\"\n", key.data);
22843ef005b8SRafał Miłecki 	err = send_key_to_dongle(ifp, &key);
228505491d2cSKalle Valo 	if (err)
228605491d2cSKalle Valo 		return err;
228705491d2cSKalle Valo 
228805491d2cSKalle Valo 	if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
228905491d2cSKalle Valo 		brcmf_dbg(CONN, "set auth_type to shared key\n");
229005491d2cSKalle Valo 		val = WL_AUTH_SHARED_KEY;	/* shared key */
22913ef005b8SRafał Miłecki 		err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
229205491d2cSKalle Valo 		if (err)
229316e64676SRafał Miłecki 			bphy_err(drvr, "set auth failed (%d)\n", err);
229405491d2cSKalle Valo 	}
229505491d2cSKalle Valo 	return err;
229605491d2cSKalle Valo }
229705491d2cSKalle Valo 
229805491d2cSKalle Valo static
brcmf_war_auth_type(struct brcmf_if * ifp,enum nl80211_auth_type type)229905491d2cSKalle Valo enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
230005491d2cSKalle Valo 					   enum nl80211_auth_type type)
230105491d2cSKalle Valo {
230205491d2cSKalle Valo 	if (type == NL80211_AUTHTYPE_AUTOMATIC &&
230305491d2cSKalle Valo 	    brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
230405491d2cSKalle Valo 		brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
230505491d2cSKalle Valo 		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
230605491d2cSKalle Valo 	}
230705491d2cSKalle Valo 	return type;
230805491d2cSKalle Valo }
230905491d2cSKalle Valo 
brcmf_set_join_pref(struct brcmf_if * ifp,struct cfg80211_bss_selection * bss_select)23107705ba6fSArend van Spriel static void brcmf_set_join_pref(struct brcmf_if *ifp,
23117705ba6fSArend van Spriel 				struct cfg80211_bss_selection *bss_select)
23127705ba6fSArend van Spriel {
231316e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
23147705ba6fSArend van Spriel 	struct brcmf_join_pref_params join_pref_params[2];
23157705ba6fSArend van Spriel 	enum nl80211_band band;
23167705ba6fSArend van Spriel 	int err, i = 0;
23177705ba6fSArend van Spriel 
23187705ba6fSArend van Spriel 	join_pref_params[i].len = 2;
23197705ba6fSArend van Spriel 	join_pref_params[i].rssi_gain = 0;
23207705ba6fSArend van Spriel 
23217705ba6fSArend van Spriel 	if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)
23227705ba6fSArend van Spriel 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);
23237705ba6fSArend van Spriel 
23247705ba6fSArend van Spriel 	switch (bss_select->behaviour) {
23257705ba6fSArend van Spriel 	case __NL80211_BSS_SELECT_ATTR_INVALID:
23267705ba6fSArend van Spriel 		brcmf_c_set_joinpref_default(ifp);
23277705ba6fSArend van Spriel 		return;
23287705ba6fSArend van Spriel 	case NL80211_BSS_SELECT_ATTR_BAND_PREF:
23297705ba6fSArend van Spriel 		join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;
23307705ba6fSArend van Spriel 		band = bss_select->param.band_pref;
23317705ba6fSArend van Spriel 		join_pref_params[i].band = nl80211_band_to_fwil(band);
23327705ba6fSArend van Spriel 		i++;
23337705ba6fSArend van Spriel 		break;
23347705ba6fSArend van Spriel 	case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:
23357705ba6fSArend van Spriel 		join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;
23367705ba6fSArend van Spriel 		band = bss_select->param.adjust.band;
23377705ba6fSArend van Spriel 		join_pref_params[i].band = nl80211_band_to_fwil(band);
23387705ba6fSArend van Spriel 		join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;
23397705ba6fSArend van Spriel 		i++;
23407705ba6fSArend van Spriel 		break;
23417705ba6fSArend van Spriel 	case NL80211_BSS_SELECT_ATTR_RSSI:
23427705ba6fSArend van Spriel 	default:
23437705ba6fSArend van Spriel 		break;
23447705ba6fSArend van Spriel 	}
23457705ba6fSArend van Spriel 	join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;
23467705ba6fSArend van Spriel 	join_pref_params[i].len = 2;
23477705ba6fSArend van Spriel 	join_pref_params[i].rssi_gain = 0;
23487705ba6fSArend van Spriel 	join_pref_params[i].band = 0;
23497705ba6fSArend van Spriel 	err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
23507705ba6fSArend van Spriel 				       sizeof(join_pref_params));
23517705ba6fSArend van Spriel 	if (err)
235216e64676SRafał Miłecki 		bphy_err(drvr, "Set join_pref error (%d)\n", err);
23537705ba6fSArend van Spriel }
23547705ba6fSArend van Spriel 
235505491d2cSKalle Valo static s32
brcmf_cfg80211_connect(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme)235605491d2cSKalle Valo brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
235705491d2cSKalle Valo 		       struct cfg80211_connect_params *sme)
235805491d2cSKalle Valo {
235905491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
236005491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
23612526ff21SArend van Spriel 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
236205491d2cSKalle Valo 	struct ieee80211_channel *chan = sme->channel;
236316e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
236405491d2cSKalle Valo 	struct brcmf_join_params join_params;
236505491d2cSKalle Valo 	size_t join_params_size;
236605491d2cSKalle Valo 	const struct brcmf_tlv *rsn_ie;
236705491d2cSKalle Valo 	const struct brcmf_vs_tlv *wpa_ie;
236805491d2cSKalle Valo 	const void *ie;
236905491d2cSKalle Valo 	u32 ie_len;
237005491d2cSKalle Valo 	struct brcmf_ext_join_params_le *ext_join_params;
237105491d2cSKalle Valo 	u16 chanspec;
237205491d2cSKalle Valo 	s32 err = 0;
2373e9a6ca82SHante Meuleman 	u32 ssid_len;
237405491d2cSKalle Valo 
237505491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
237605491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
237705491d2cSKalle Valo 		return -EIO;
237805491d2cSKalle Valo 
237905491d2cSKalle Valo 	if (!sme->ssid) {
238016e64676SRafał Miłecki 		bphy_err(drvr, "Invalid ssid\n");
238105491d2cSKalle Valo 		return -EOPNOTSUPP;
238205491d2cSKalle Valo 	}
238305491d2cSKalle Valo 
2384c81c1fd4SPrasanna Kerekoppa 	if (sme->channel_hint)
2385c81c1fd4SPrasanna Kerekoppa 		chan = sme->channel_hint;
2386c81c1fd4SPrasanna Kerekoppa 
2387c81c1fd4SPrasanna Kerekoppa 	if (sme->bssid_hint)
2388c81c1fd4SPrasanna Kerekoppa 		sme->bssid = sme->bssid_hint;
2389c81c1fd4SPrasanna Kerekoppa 
239005491d2cSKalle Valo 	if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
239105491d2cSKalle Valo 		/* A normal (non P2P) connection request setup. */
239205491d2cSKalle Valo 		ie = NULL;
239305491d2cSKalle Valo 		ie_len = 0;
239405491d2cSKalle Valo 		/* find the WPA_IE */
239505491d2cSKalle Valo 		wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
239605491d2cSKalle Valo 		if (wpa_ie) {
239705491d2cSKalle Valo 			ie = wpa_ie;
239805491d2cSKalle Valo 			ie_len = wpa_ie->len + TLV_HDR_LEN;
239905491d2cSKalle Valo 		} else {
240005491d2cSKalle Valo 			/* find the RSN_IE */
240105491d2cSKalle Valo 			rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
240205491d2cSKalle Valo 						  sme->ie_len,
240305491d2cSKalle Valo 						  WLAN_EID_RSN);
240405491d2cSKalle Valo 			if (rsn_ie) {
240505491d2cSKalle Valo 				ie = rsn_ie;
240605491d2cSKalle Valo 				ie_len = rsn_ie->len + TLV_HDR_LEN;
240705491d2cSKalle Valo 			}
240805491d2cSKalle Valo 		}
240905491d2cSKalle Valo 		brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
241005491d2cSKalle Valo 	}
241105491d2cSKalle Valo 
241205491d2cSKalle Valo 	err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
241305491d2cSKalle Valo 				    sme->ie, sme->ie_len);
241405491d2cSKalle Valo 	if (err)
241516e64676SRafał Miłecki 		bphy_err(drvr, "Set Assoc REQ IE Failed\n");
241605491d2cSKalle Valo 	else
241705491d2cSKalle Valo 		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
241805491d2cSKalle Valo 
241905491d2cSKalle Valo 	set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
242005491d2cSKalle Valo 
242105491d2cSKalle Valo 	if (chan) {
242205491d2cSKalle Valo 		cfg->channel =
242305491d2cSKalle Valo 			ieee80211_frequency_to_channel(chan->center_freq);
242405491d2cSKalle Valo 		chanspec = channel_to_chanspec(&cfg->d11inf, chan);
242505491d2cSKalle Valo 		brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
242605491d2cSKalle Valo 			  cfg->channel, chan->center_freq, chanspec);
242705491d2cSKalle Valo 	} else {
242805491d2cSKalle Valo 		cfg->channel = 0;
242905491d2cSKalle Valo 		chanspec = 0;
243005491d2cSKalle Valo 	}
243105491d2cSKalle Valo 
243205491d2cSKalle Valo 	brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
243305491d2cSKalle Valo 
243405491d2cSKalle Valo 	err = brcmf_set_wpa_version(ndev, sme);
243505491d2cSKalle Valo 	if (err) {
243616e64676SRafał Miłecki 		bphy_err(drvr, "wl_set_wpa_version failed (%d)\n", err);
243705491d2cSKalle Valo 		goto done;
243805491d2cSKalle Valo 	}
243905491d2cSKalle Valo 
244005491d2cSKalle Valo 	sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
244105491d2cSKalle Valo 	err = brcmf_set_auth_type(ndev, sme);
244205491d2cSKalle Valo 	if (err) {
244316e64676SRafał Miłecki 		bphy_err(drvr, "wl_set_auth_type failed (%d)\n", err);
244405491d2cSKalle Valo 		goto done;
244505491d2cSKalle Valo 	}
244605491d2cSKalle Valo 
2447240d61a9SHante Meuleman 	err = brcmf_set_wsec_mode(ndev, sme);
244805491d2cSKalle Valo 	if (err) {
244916e64676SRafał Miłecki 		bphy_err(drvr, "wl_set_set_cipher failed (%d)\n", err);
245005491d2cSKalle Valo 		goto done;
245105491d2cSKalle Valo 	}
245205491d2cSKalle Valo 
245305491d2cSKalle Valo 	err = brcmf_set_key_mgmt(ndev, sme);
245405491d2cSKalle Valo 	if (err) {
245516e64676SRafał Miłecki 		bphy_err(drvr, "wl_set_key_mgmt failed (%d)\n", err);
245605491d2cSKalle Valo 		goto done;
245705491d2cSKalle Valo 	}
245805491d2cSKalle Valo 
245905491d2cSKalle Valo 	err = brcmf_set_sharedkey(ndev, sme);
246005491d2cSKalle Valo 	if (err) {
246116e64676SRafał Miłecki 		bphy_err(drvr, "brcmf_set_sharedkey failed (%d)\n", err);
246205491d2cSKalle Valo 		goto done;
246305491d2cSKalle Valo 	}
246405491d2cSKalle Valo 
24653b1e0a7bSChung-Hsien Hsu 	if (sme->crypto.psk &&
24663b1e0a7bSChung-Hsien Hsu 	    profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
24672526ff21SArend van Spriel 		if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
24682526ff21SArend van Spriel 			err = -EINVAL;
24692526ff21SArend van Spriel 			goto done;
24702526ff21SArend van Spriel 		}
2471b8a64f0eSArend van Spriel 		brcmf_dbg(INFO, "using PSK offload\n");
24722526ff21SArend van Spriel 		profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
24732526ff21SArend van Spriel 	}
2474b8a64f0eSArend van Spriel 
24752526ff21SArend van Spriel 	if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
2476b8a64f0eSArend van Spriel 		/* enable firmware supplicant for this interface */
2477b8a64f0eSArend van Spriel 		err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1);
2478b8a64f0eSArend van Spriel 		if (err < 0) {
247916e64676SRafał Miłecki 			bphy_err(drvr, "failed to enable fw supplicant\n");
2480b8a64f0eSArend van Spriel 			goto done;
2481b8a64f0eSArend van Spriel 		}
24822526ff21SArend van Spriel 	}
2483b8a64f0eSArend van Spriel 
24843b1e0a7bSChung-Hsien Hsu 	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)
2485b8a64f0eSArend van Spriel 		err = brcmf_set_pmk(ifp, sme->crypto.psk,
2486b8a64f0eSArend van Spriel 				    BRCMF_WSEC_MAX_PSK_LEN);
24873b1e0a7bSChung-Hsien Hsu 	else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {
24883b1e0a7bSChung-Hsien Hsu 		/* clean up user-space RSNE */
248937ff144dSZhang Changzhong 		err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0);
249037ff144dSZhang Changzhong 		if (err) {
24913b1e0a7bSChung-Hsien Hsu 			bphy_err(drvr, "failed to clean up user-space RSNE\n");
2492b8a64f0eSArend van Spriel 			goto done;
2493b8a64f0eSArend van Spriel 		}
24948d59a64cSHector Martin 		err = brcmf_fwvid_set_sae_password(ifp, &sme->crypto);
24953b1e0a7bSChung-Hsien Hsu 		if (!err && sme->crypto.psk)
24963b1e0a7bSChung-Hsien Hsu 			err = brcmf_set_pmk(ifp, sme->crypto.psk,
24973b1e0a7bSChung-Hsien Hsu 					    BRCMF_WSEC_MAX_PSK_LEN);
24983b1e0a7bSChung-Hsien Hsu 	}
24993b1e0a7bSChung-Hsien Hsu 	if (err)
25003b1e0a7bSChung-Hsien Hsu 		goto done;
2501b8a64f0eSArend van Spriel 
250205491d2cSKalle Valo 	/* Join with specific BSSID and cached SSID
250305491d2cSKalle Valo 	 * If SSID is zero join based on BSSID only
250405491d2cSKalle Valo 	 */
250505491d2cSKalle Valo 	join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
250605491d2cSKalle Valo 		offsetof(struct brcmf_assoc_params_le, chanspec_list);
250705491d2cSKalle Valo 	if (cfg->channel)
250805491d2cSKalle Valo 		join_params_size += sizeof(u16);
250984f23fb1SJakub Kicinski 	ext_join_params = kzalloc(sizeof(*ext_join_params), GFP_KERNEL);
251005491d2cSKalle Valo 	if (ext_join_params == NULL) {
251105491d2cSKalle Valo 		err = -ENOMEM;
251205491d2cSKalle Valo 		goto done;
251305491d2cSKalle Valo 	}
2514e9a6ca82SHante Meuleman 	ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
2515e9a6ca82SHante Meuleman 	ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
2516e9a6ca82SHante Meuleman 	memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
2517e9a6ca82SHante Meuleman 	if (ssid_len < IEEE80211_MAX_SSID_LEN)
2518e9a6ca82SHante Meuleman 		brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
2519e9a6ca82SHante Meuleman 			  ext_join_params->ssid_le.SSID, ssid_len);
252005491d2cSKalle Valo 
252105491d2cSKalle Valo 	/* Set up join scan parameters */
252205491d2cSKalle Valo 	ext_join_params->scan_le.scan_type = -1;
252305491d2cSKalle Valo 	ext_join_params->scan_le.home_time = cpu_to_le32(-1);
252405491d2cSKalle Valo 
252505491d2cSKalle Valo 	if (sme->bssid)
252605491d2cSKalle Valo 		memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
252705491d2cSKalle Valo 	else
252805491d2cSKalle Valo 		eth_broadcast_addr(ext_join_params->assoc_le.bssid);
252905491d2cSKalle Valo 
253005491d2cSKalle Valo 	if (cfg->channel) {
253105491d2cSKalle Valo 		ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
253205491d2cSKalle Valo 
253305491d2cSKalle Valo 		ext_join_params->assoc_le.chanspec_list[0] =
253405491d2cSKalle Valo 			cpu_to_le16(chanspec);
253505491d2cSKalle Valo 		/* Increase dwell time to receive probe response or detect
253605491d2cSKalle Valo 		 * beacon from target AP at a noisy air only during connect
253705491d2cSKalle Valo 		 * command.
253805491d2cSKalle Valo 		 */
253905491d2cSKalle Valo 		ext_join_params->scan_le.active_time =
254005491d2cSKalle Valo 			cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
254105491d2cSKalle Valo 		ext_join_params->scan_le.passive_time =
254205491d2cSKalle Valo 			cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
254305491d2cSKalle Valo 		/* To sync with presence period of VSDB GO send probe request
254405491d2cSKalle Valo 		 * more frequently. Probe request will be stopped when it gets
254505491d2cSKalle Valo 		 * probe response from target AP/GO.
254605491d2cSKalle Valo 		 */
254705491d2cSKalle Valo 		ext_join_params->scan_le.nprobes =
254805491d2cSKalle Valo 			cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
254905491d2cSKalle Valo 				    BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
255005491d2cSKalle Valo 	} else {
255105491d2cSKalle Valo 		ext_join_params->scan_le.active_time = cpu_to_le32(-1);
255205491d2cSKalle Valo 		ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
255305491d2cSKalle Valo 		ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
255405491d2cSKalle Valo 	}
255505491d2cSKalle Valo 
25567705ba6fSArend van Spriel 	brcmf_set_join_pref(ifp, &sme->bss_select);
25577705ba6fSArend van Spriel 
255805491d2cSKalle Valo 	err  = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
255905491d2cSKalle Valo 					 join_params_size);
256005491d2cSKalle Valo 	kfree(ext_join_params);
256105491d2cSKalle Valo 	if (!err)
256205491d2cSKalle Valo 		/* This is it. join command worked, we are done */
256305491d2cSKalle Valo 		goto done;
256405491d2cSKalle Valo 
256505491d2cSKalle Valo 	/* join command failed, fallback to set ssid */
256605491d2cSKalle Valo 	memset(&join_params, 0, sizeof(join_params));
256705491d2cSKalle Valo 	join_params_size = sizeof(join_params.ssid_le);
256805491d2cSKalle Valo 
2569e9a6ca82SHante Meuleman 	memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
2570e9a6ca82SHante Meuleman 	join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
257105491d2cSKalle Valo 
257205491d2cSKalle Valo 	if (sme->bssid)
257305491d2cSKalle Valo 		memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
257405491d2cSKalle Valo 	else
257505491d2cSKalle Valo 		eth_broadcast_addr(join_params.params_le.bssid);
257605491d2cSKalle Valo 
257705491d2cSKalle Valo 	if (cfg->channel) {
257805491d2cSKalle Valo 		join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
257905491d2cSKalle Valo 		join_params.params_le.chanspec_num = cpu_to_le32(1);
258005491d2cSKalle Valo 		join_params_size += sizeof(join_params.params_le);
258105491d2cSKalle Valo 	}
258205491d2cSKalle Valo 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
258305491d2cSKalle Valo 				     &join_params, join_params_size);
258405491d2cSKalle Valo 	if (err)
258516e64676SRafał Miłecki 		bphy_err(drvr, "BRCMF_C_SET_SSID failed (%d)\n", err);
258605491d2cSKalle Valo 
258705491d2cSKalle Valo done:
258805491d2cSKalle Valo 	if (err)
258905491d2cSKalle Valo 		clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
259005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
259105491d2cSKalle Valo 	return err;
259205491d2cSKalle Valo }
259305491d2cSKalle Valo 
259405491d2cSKalle Valo static s32
brcmf_cfg80211_disconnect(struct wiphy * wiphy,struct net_device * ndev,u16 reason_code)259505491d2cSKalle Valo brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
259605491d2cSKalle Valo 		       u16 reason_code)
259705491d2cSKalle Valo {
259816e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
259905491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
260005491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
260116e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
260205491d2cSKalle Valo 	struct brcmf_scb_val_le scbval;
260305491d2cSKalle Valo 	s32 err = 0;
260405491d2cSKalle Valo 
260505491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
260605491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
260705491d2cSKalle Valo 		return -EIO;
260805491d2cSKalle Valo 
260905491d2cSKalle Valo 	clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
261005491d2cSKalle Valo 	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
261152617beeSWataru Gohda 	clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state);
261252617beeSWataru Gohda 	clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state);
261305491d2cSKalle Valo 	cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
261405491d2cSKalle Valo 
261505491d2cSKalle Valo 	memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
261605491d2cSKalle Valo 	scbval.val = cpu_to_le32(reason_code);
261705491d2cSKalle Valo 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
261805491d2cSKalle Valo 				     &scbval, sizeof(scbval));
261905491d2cSKalle Valo 	if (err)
262016e64676SRafał Miłecki 		bphy_err(drvr, "error (%d)\n", err);
262105491d2cSKalle Valo 
262205491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
262305491d2cSKalle Valo 	return err;
262405491d2cSKalle Valo }
262505491d2cSKalle Valo 
262605491d2cSKalle Valo static s32
brcmf_cfg80211_set_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,enum nl80211_tx_power_setting type,s32 mbm)262705491d2cSKalle Valo brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
262805491d2cSKalle Valo 			    enum nl80211_tx_power_setting type, s32 mbm)
262905491d2cSKalle Valo {
263005491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
263105491d2cSKalle Valo 	struct net_device *ndev = cfg_to_ndev(cfg);
263205491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
263316e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
263405491d2cSKalle Valo 	s32 err;
263505491d2cSKalle Valo 	s32 disable;
263605491d2cSKalle Valo 	u32 qdbm = 127;
263705491d2cSKalle Valo 
263805491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
263905491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
264005491d2cSKalle Valo 		return -EIO;
264105491d2cSKalle Valo 
264205491d2cSKalle Valo 	switch (type) {
264305491d2cSKalle Valo 	case NL80211_TX_POWER_AUTOMATIC:
264405491d2cSKalle Valo 		break;
264505491d2cSKalle Valo 	case NL80211_TX_POWER_LIMITED:
264605491d2cSKalle Valo 	case NL80211_TX_POWER_FIXED:
264705491d2cSKalle Valo 		if (mbm < 0) {
264816e64676SRafał Miłecki 			bphy_err(drvr, "TX_POWER_FIXED - dbm is negative\n");
264905491d2cSKalle Valo 			err = -EINVAL;
265005491d2cSKalle Valo 			goto done;
265105491d2cSKalle Valo 		}
265205491d2cSKalle Valo 		qdbm =  MBM_TO_DBM(4 * mbm);
265305491d2cSKalle Valo 		if (qdbm > 127)
265405491d2cSKalle Valo 			qdbm = 127;
265505491d2cSKalle Valo 		qdbm |= WL_TXPWR_OVERRIDE;
265605491d2cSKalle Valo 		break;
265705491d2cSKalle Valo 	default:
265816e64676SRafał Miłecki 		bphy_err(drvr, "Unsupported type %d\n", type);
265905491d2cSKalle Valo 		err = -EINVAL;
266005491d2cSKalle Valo 		goto done;
266105491d2cSKalle Valo 	}
266205491d2cSKalle Valo 	/* Make sure radio is off or on as far as software is concerned */
266305491d2cSKalle Valo 	disable = WL_RADIO_SW_DISABLE << 16;
266405491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
266505491d2cSKalle Valo 	if (err)
266616e64676SRafał Miłecki 		bphy_err(drvr, "WLC_SET_RADIO error (%d)\n", err);
266705491d2cSKalle Valo 
266805491d2cSKalle Valo 	err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
266905491d2cSKalle Valo 	if (err)
267016e64676SRafał Miłecki 		bphy_err(drvr, "qtxpower error (%d)\n", err);
267105491d2cSKalle Valo 
267205491d2cSKalle Valo done:
267305491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
267405491d2cSKalle Valo 	return err;
267505491d2cSKalle Valo }
267605491d2cSKalle Valo 
267705491d2cSKalle Valo static s32
brcmf_cfg80211_get_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,s32 * dbm)267805491d2cSKalle Valo brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
267905491d2cSKalle Valo 			    s32 *dbm)
268005491d2cSKalle Valo {
268116e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2682856d5a01SArend Van Spriel 	struct brcmf_cfg80211_vif *vif = wdev_to_vif(wdev);
268316e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
2684*fb1862ceSArend van Spriel 	s32 qdbm;
268505491d2cSKalle Valo 	s32 err;
268605491d2cSKalle Valo 
268705491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
2688856d5a01SArend Van Spriel 	if (!check_vif_up(vif))
268905491d2cSKalle Valo 		return -EIO;
269005491d2cSKalle Valo 
2691856d5a01SArend Van Spriel 	err = brcmf_fil_iovar_int_get(vif->ifp, "qtxpower", &qdbm);
269205491d2cSKalle Valo 	if (err) {
269316e64676SRafał Miłecki 		bphy_err(drvr, "error (%d)\n", err);
269405491d2cSKalle Valo 		goto done;
269505491d2cSKalle Valo 	}
269605491d2cSKalle Valo 	*dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
269705491d2cSKalle Valo 
269805491d2cSKalle Valo done:
269905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
270005491d2cSKalle Valo 	return err;
270105491d2cSKalle Valo }
270205491d2cSKalle Valo 
270305491d2cSKalle Valo static s32
brcmf_cfg80211_config_default_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool unicast,bool multicast)270405491d2cSKalle Valo brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
2705e7a7b84eSVeerendranath Jakkam 				  int link_id, u8 key_idx, bool unicast,
2706e7a7b84eSVeerendranath Jakkam 				  bool multicast)
270705491d2cSKalle Valo {
270805491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
270916e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
271005491d2cSKalle Valo 	u32 index;
271105491d2cSKalle Valo 	u32 wsec;
271205491d2cSKalle Valo 	s32 err = 0;
271305491d2cSKalle Valo 
271405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
271505491d2cSKalle Valo 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
271605491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
271705491d2cSKalle Valo 		return -EIO;
271805491d2cSKalle Valo 
271905491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
272005491d2cSKalle Valo 	if (err) {
272116e64676SRafał Miłecki 		bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
272205491d2cSKalle Valo 		goto done;
272305491d2cSKalle Valo 	}
272405491d2cSKalle Valo 
272505491d2cSKalle Valo 	if (wsec & WEP_ENABLED) {
272605491d2cSKalle Valo 		/* Just select a new current key */
272705491d2cSKalle Valo 		index = key_idx;
272805491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp,
272905491d2cSKalle Valo 					    BRCMF_C_SET_KEY_PRIMARY, index);
273005491d2cSKalle Valo 		if (err)
273116e64676SRafał Miłecki 			bphy_err(drvr, "error (%d)\n", err);
273205491d2cSKalle Valo 	}
273305491d2cSKalle Valo done:
273405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
273505491d2cSKalle Valo 	return err;
273605491d2cSKalle Valo }
273705491d2cSKalle Valo 
273805491d2cSKalle Valo static s32
brcmf_cfg80211_del_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr)2739219e0f74SHante Meuleman brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2740e7a7b84eSVeerendranath Jakkam 		       int link_id, u8 key_idx, bool pairwise,
2741e7a7b84eSVeerendranath Jakkam 		       const u8 *mac_addr)
274205491d2cSKalle Valo {
274305491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
2744240d61a9SHante Meuleman 	struct brcmf_wsec_key *key;
2745240d61a9SHante Meuleman 	s32 err;
2746219e0f74SHante Meuleman 
2747219e0f74SHante Meuleman 	brcmf_dbg(TRACE, "Enter\n");
2748240d61a9SHante Meuleman 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
2749240d61a9SHante Meuleman 
2750219e0f74SHante Meuleman 	if (!check_vif_up(ifp->vif))
2751219e0f74SHante Meuleman 		return -EIO;
2752219e0f74SHante Meuleman 
2753219e0f74SHante Meuleman 	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2754219e0f74SHante Meuleman 		/* we ignore this key index in this case */
2755219e0f74SHante Meuleman 		return -EINVAL;
2756219e0f74SHante Meuleman 	}
275705491d2cSKalle Valo 
2758240d61a9SHante Meuleman 	key = &ifp->vif->profile.key[key_idx];
2759219e0f74SHante Meuleman 
2760240d61a9SHante Meuleman 	if (key->algo == CRYPTO_ALGO_OFF) {
2761240d61a9SHante Meuleman 		brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
2762240d61a9SHante Meuleman 		return -EINVAL;
2763240d61a9SHante Meuleman 	}
2764219e0f74SHante Meuleman 
2765240d61a9SHante Meuleman 	memset(key, 0, sizeof(*key));
2766240d61a9SHante Meuleman 	key->index = (u32)key_idx;
2767240d61a9SHante Meuleman 	key->flags = BRCMF_PRIMARY_KEY;
2768219e0f74SHante Meuleman 
2769240d61a9SHante Meuleman 	/* Clear the key/index */
2770240d61a9SHante Meuleman 	err = send_key_to_dongle(ifp, key);
277105491d2cSKalle Valo 
2772219e0f74SHante Meuleman 	brcmf_dbg(TRACE, "Exit\n");
277305491d2cSKalle Valo 	return err;
277405491d2cSKalle Valo }
277505491d2cSKalle Valo 
277605491d2cSKalle Valo static s32
brcmf_cfg80211_add_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,struct key_params * params)277705491d2cSKalle Valo brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2778e7a7b84eSVeerendranath Jakkam 		       int link_id, u8 key_idx, bool pairwise,
2779e7a7b84eSVeerendranath Jakkam 		       const u8 *mac_addr, struct key_params *params)
278005491d2cSKalle Valo {
278116e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
278205491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
278316e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
278405491d2cSKalle Valo 	struct brcmf_wsec_key *key;
278505491d2cSKalle Valo 	s32 val;
278605491d2cSKalle Valo 	s32 wsec;
2787219e0f74SHante Meuleman 	s32 err;
278805491d2cSKalle Valo 	u8 keybuf[8];
2789219e0f74SHante Meuleman 	bool ext_key;
279005491d2cSKalle Valo 
279105491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
279205491d2cSKalle Valo 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
279305491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
279405491d2cSKalle Valo 		return -EIO;
279505491d2cSKalle Valo 
279605491d2cSKalle Valo 	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
279705491d2cSKalle Valo 		/* we ignore this key index in this case */
279816e64676SRafał Miłecki 		bphy_err(drvr, "invalid key index (%d)\n", key_idx);
279905491d2cSKalle Valo 		return -EINVAL;
280005491d2cSKalle Valo 	}
280105491d2cSKalle Valo 
2802219e0f74SHante Meuleman 	if (params->key_len == 0)
2803e7a7b84eSVeerendranath Jakkam 		return brcmf_cfg80211_del_key(wiphy, ndev, -1, key_idx,
2804e7a7b84eSVeerendranath Jakkam 					      pairwise, mac_addr);
2805219e0f74SHante Meuleman 
2806219e0f74SHante Meuleman 	if (params->key_len > sizeof(key->data)) {
280716e64676SRafał Miłecki 		bphy_err(drvr, "Too long key length (%u)\n", params->key_len);
2808219e0f74SHante Meuleman 		return -EINVAL;
2809219e0f74SHante Meuleman 	}
2810219e0f74SHante Meuleman 
2811219e0f74SHante Meuleman 	ext_key = false;
2812219e0f74SHante Meuleman 	if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
281305491d2cSKalle Valo 	    (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
2814219e0f74SHante Meuleman 		brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
2815219e0f74SHante Meuleman 		ext_key = true;
281605491d2cSKalle Valo 	}
281705491d2cSKalle Valo 
281805491d2cSKalle Valo 	key = &ifp->vif->profile.key[key_idx];
281905491d2cSKalle Valo 	memset(key, 0, sizeof(*key));
2820219e0f74SHante Meuleman 	if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
2821219e0f74SHante Meuleman 		memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
282205491d2cSKalle Valo 	key->len = params->key_len;
282305491d2cSKalle Valo 	key->index = key_idx;
282405491d2cSKalle Valo 	memcpy(key->data, params->key, key->len);
2825219e0f74SHante Meuleman 	if (!ext_key)
282605491d2cSKalle Valo 		key->flags = BRCMF_PRIMARY_KEY;
2827219e0f74SHante Meuleman 
282878db077dSSoontak Lee 	if (params->seq && params->seq_len == 6) {
282978db077dSSoontak Lee 		/* rx iv */
283078db077dSSoontak Lee 		u8 *ivptr;
283178db077dSSoontak Lee 
283278db077dSSoontak Lee 		ivptr = (u8 *)params->seq;
283378db077dSSoontak Lee 		key->rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
283478db077dSSoontak Lee 			(ivptr[3] << 8) | ivptr[2];
283578db077dSSoontak Lee 		key->rxiv.lo = (ivptr[1] << 8) | ivptr[0];
283678db077dSSoontak Lee 		key->iv_initialized = true;
283778db077dSSoontak Lee 	}
283878db077dSSoontak Lee 
283905491d2cSKalle Valo 	switch (params->cipher) {
284005491d2cSKalle Valo 	case WLAN_CIPHER_SUITE_WEP40:
284105491d2cSKalle Valo 		key->algo = CRYPTO_ALGO_WEP1;
284205491d2cSKalle Valo 		val = WEP_ENABLED;
284305491d2cSKalle Valo 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
284405491d2cSKalle Valo 		break;
284505491d2cSKalle Valo 	case WLAN_CIPHER_SUITE_WEP104:
284605491d2cSKalle Valo 		key->algo = CRYPTO_ALGO_WEP128;
284705491d2cSKalle Valo 		val = WEP_ENABLED;
284805491d2cSKalle Valo 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
284905491d2cSKalle Valo 		break;
285005491d2cSKalle Valo 	case WLAN_CIPHER_SUITE_TKIP:
285105491d2cSKalle Valo 		if (!brcmf_is_apmode(ifp->vif)) {
285205491d2cSKalle Valo 			brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
285305491d2cSKalle Valo 			memcpy(keybuf, &key->data[24], sizeof(keybuf));
285405491d2cSKalle Valo 			memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
285505491d2cSKalle Valo 			memcpy(&key->data[16], keybuf, sizeof(keybuf));
285605491d2cSKalle Valo 		}
285705491d2cSKalle Valo 		key->algo = CRYPTO_ALGO_TKIP;
285805491d2cSKalle Valo 		val = TKIP_ENABLED;
285905491d2cSKalle Valo 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
286005491d2cSKalle Valo 		break;
286105491d2cSKalle Valo 	case WLAN_CIPHER_SUITE_AES_CMAC:
286205491d2cSKalle Valo 		key->algo = CRYPTO_ALGO_AES_CCM;
286305491d2cSKalle Valo 		val = AES_ENABLED;
286405491d2cSKalle Valo 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
286505491d2cSKalle Valo 		break;
286605491d2cSKalle Valo 	case WLAN_CIPHER_SUITE_CCMP:
286705491d2cSKalle Valo 		key->algo = CRYPTO_ALGO_AES_CCM;
286805491d2cSKalle Valo 		val = AES_ENABLED;
286905491d2cSKalle Valo 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
287005491d2cSKalle Valo 		break;
287105491d2cSKalle Valo 	default:
287216e64676SRafał Miłecki 		bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher);
287305491d2cSKalle Valo 		err = -EINVAL;
287405491d2cSKalle Valo 		goto done;
287505491d2cSKalle Valo 	}
287605491d2cSKalle Valo 
287705491d2cSKalle Valo 	err = send_key_to_dongle(ifp, key);
2878219e0f74SHante Meuleman 	if (ext_key || err)
287905491d2cSKalle Valo 		goto done;
288005491d2cSKalle Valo 
288105491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
288205491d2cSKalle Valo 	if (err) {
288316e64676SRafał Miłecki 		bphy_err(drvr, "get wsec error (%d)\n", err);
288405491d2cSKalle Valo 		goto done;
288505491d2cSKalle Valo 	}
288605491d2cSKalle Valo 	wsec |= val;
288705491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
288805491d2cSKalle Valo 	if (err) {
288916e64676SRafał Miłecki 		bphy_err(drvr, "set wsec error (%d)\n", err);
289005491d2cSKalle Valo 		goto done;
289105491d2cSKalle Valo 	}
289205491d2cSKalle Valo 
289305491d2cSKalle Valo done:
289405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
289505491d2cSKalle Valo 	return err;
289605491d2cSKalle Valo }
289705491d2cSKalle Valo 
289805491d2cSKalle Valo static s32
brcmf_cfg80211_get_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,void * cookie,void (* callback)(void * cookie,struct key_params * params))2899e7a7b84eSVeerendranath Jakkam brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2900e7a7b84eSVeerendranath Jakkam 		       int link_id, u8 key_idx, bool pairwise,
2901e7a7b84eSVeerendranath Jakkam 		       const u8 *mac_addr, void *cookie,
2902240d61a9SHante Meuleman 		       void (*callback)(void *cookie,
2903240d61a9SHante Meuleman 					struct key_params *params))
290405491d2cSKalle Valo {
290516e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
290605491d2cSKalle Valo 	struct key_params params;
290705491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
290805491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
290916e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
291005491d2cSKalle Valo 	struct brcmf_cfg80211_security *sec;
291105491d2cSKalle Valo 	s32 wsec;
291205491d2cSKalle Valo 	s32 err = 0;
291305491d2cSKalle Valo 
291405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
291505491d2cSKalle Valo 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
291605491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
291705491d2cSKalle Valo 		return -EIO;
291805491d2cSKalle Valo 
291905491d2cSKalle Valo 	memset(&params, 0, sizeof(params));
292005491d2cSKalle Valo 
292105491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
292205491d2cSKalle Valo 	if (err) {
292316e64676SRafał Miłecki 		bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
292405491d2cSKalle Valo 		/* Ignore this error, may happen during DISASSOC */
292505491d2cSKalle Valo 		err = -EAGAIN;
292605491d2cSKalle Valo 		goto done;
292705491d2cSKalle Valo 	}
292805491d2cSKalle Valo 	if (wsec & WEP_ENABLED) {
292905491d2cSKalle Valo 		sec = &profile->sec;
293005491d2cSKalle Valo 		if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
293105491d2cSKalle Valo 			params.cipher = WLAN_CIPHER_SUITE_WEP40;
293205491d2cSKalle Valo 			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
293305491d2cSKalle Valo 		} else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
293405491d2cSKalle Valo 			params.cipher = WLAN_CIPHER_SUITE_WEP104;
293505491d2cSKalle Valo 			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
293605491d2cSKalle Valo 		}
293705491d2cSKalle Valo 	} else if (wsec & TKIP_ENABLED) {
293805491d2cSKalle Valo 		params.cipher = WLAN_CIPHER_SUITE_TKIP;
293905491d2cSKalle Valo 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
294005491d2cSKalle Valo 	} else if (wsec & AES_ENABLED) {
294105491d2cSKalle Valo 		params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
294205491d2cSKalle Valo 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
294305491d2cSKalle Valo 	} else  {
294416e64676SRafał Miłecki 		bphy_err(drvr, "Invalid algo (0x%x)\n", wsec);
294505491d2cSKalle Valo 		err = -EINVAL;
294605491d2cSKalle Valo 		goto done;
294705491d2cSKalle Valo 	}
294805491d2cSKalle Valo 	callback(cookie, &params);
294905491d2cSKalle Valo 
295005491d2cSKalle Valo done:
295105491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
295205491d2cSKalle Valo 	return err;
295305491d2cSKalle Valo }
295405491d2cSKalle Valo 
295505491d2cSKalle Valo static s32
brcmf_cfg80211_config_default_mgmt_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx)295605491d2cSKalle Valo brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2957e7a7b84eSVeerendranath Jakkam 				       struct net_device *ndev, int link_id,
2958e7a7b84eSVeerendranath Jakkam 				       u8 key_idx)
295905491d2cSKalle Valo {
2960240d61a9SHante Meuleman 	struct brcmf_if *ifp = netdev_priv(ndev);
2961240d61a9SHante Meuleman 
2962240d61a9SHante Meuleman 	brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
2963240d61a9SHante Meuleman 
2964240d61a9SHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
2965240d61a9SHante Meuleman 		return 0;
2966240d61a9SHante Meuleman 
296705491d2cSKalle Valo 	brcmf_dbg(INFO, "Not supported\n");
296805491d2cSKalle Valo 
296905491d2cSKalle Valo 	return -EOPNOTSUPP;
297005491d2cSKalle Valo }
297105491d2cSKalle Valo 
297205491d2cSKalle Valo static void
brcmf_cfg80211_reconfigure_wep(struct brcmf_if * ifp)297305491d2cSKalle Valo brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
297405491d2cSKalle Valo {
297516e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
297605491d2cSKalle Valo 	s32 err;
297705491d2cSKalle Valo 	u8 key_idx;
297805491d2cSKalle Valo 	struct brcmf_wsec_key *key;
297905491d2cSKalle Valo 	s32 wsec;
298005491d2cSKalle Valo 
298105491d2cSKalle Valo 	for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
298205491d2cSKalle Valo 		key = &ifp->vif->profile.key[key_idx];
298305491d2cSKalle Valo 		if ((key->algo == CRYPTO_ALGO_WEP1) ||
298405491d2cSKalle Valo 		    (key->algo == CRYPTO_ALGO_WEP128))
298505491d2cSKalle Valo 			break;
298605491d2cSKalle Valo 	}
298705491d2cSKalle Valo 	if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
298805491d2cSKalle Valo 		return;
298905491d2cSKalle Valo 
299005491d2cSKalle Valo 	err = send_key_to_dongle(ifp, key);
299105491d2cSKalle Valo 	if (err) {
299216e64676SRafał Miłecki 		bphy_err(drvr, "Setting WEP key failed (%d)\n", err);
299305491d2cSKalle Valo 		return;
299405491d2cSKalle Valo 	}
299505491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
299605491d2cSKalle Valo 	if (err) {
299716e64676SRafał Miłecki 		bphy_err(drvr, "get wsec error (%d)\n", err);
299805491d2cSKalle Valo 		return;
299905491d2cSKalle Valo 	}
300005491d2cSKalle Valo 	wsec |= WEP_ENABLED;
300105491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
300205491d2cSKalle Valo 	if (err)
300316e64676SRafał Miłecki 		bphy_err(drvr, "set wsec error (%d)\n", err);
300405491d2cSKalle Valo }
300505491d2cSKalle Valo 
brcmf_convert_sta_flags(u32 fw_sta_flags,struct station_info * si)300605491d2cSKalle Valo static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
300705491d2cSKalle Valo {
300805491d2cSKalle Valo 	struct nl80211_sta_flag_update *sfu;
300905491d2cSKalle Valo 
301005491d2cSKalle Valo 	brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
301122d0d2faSOmer Efrat 	si->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);
301205491d2cSKalle Valo 	sfu = &si->sta_flags;
301305491d2cSKalle Valo 	sfu->mask = BIT(NL80211_STA_FLAG_WME) |
301405491d2cSKalle Valo 		    BIT(NL80211_STA_FLAG_AUTHENTICATED) |
301505491d2cSKalle Valo 		    BIT(NL80211_STA_FLAG_ASSOCIATED) |
301605491d2cSKalle Valo 		    BIT(NL80211_STA_FLAG_AUTHORIZED);
301705491d2cSKalle Valo 	if (fw_sta_flags & BRCMF_STA_WME)
301805491d2cSKalle Valo 		sfu->set |= BIT(NL80211_STA_FLAG_WME);
301905491d2cSKalle Valo 	if (fw_sta_flags & BRCMF_STA_AUTHE)
302005491d2cSKalle Valo 		sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
302105491d2cSKalle Valo 	if (fw_sta_flags & BRCMF_STA_ASSOC)
302205491d2cSKalle Valo 		sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
302305491d2cSKalle Valo 	if (fw_sta_flags & BRCMF_STA_AUTHO)
302405491d2cSKalle Valo 		sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
302505491d2cSKalle Valo }
302605491d2cSKalle Valo 
brcmf_fill_bss_param(struct brcmf_if * ifp,struct station_info * si)302705491d2cSKalle Valo static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
302805491d2cSKalle Valo {
302916e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
303005491d2cSKalle Valo 	struct {
303105491d2cSKalle Valo 		__le32 len;
303205491d2cSKalle Valo 		struct brcmf_bss_info_le bss_le;
303305491d2cSKalle Valo 	} *buf;
303405491d2cSKalle Valo 	u16 capability;
303505491d2cSKalle Valo 	int err;
303605491d2cSKalle Valo 
303705491d2cSKalle Valo 	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
303805491d2cSKalle Valo 	if (!buf)
303905491d2cSKalle Valo 		return;
304005491d2cSKalle Valo 
304105491d2cSKalle Valo 	buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
304205491d2cSKalle Valo 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
304305491d2cSKalle Valo 				     WL_BSS_INFO_MAX);
304405491d2cSKalle Valo 	if (err) {
304516e64676SRafał Miłecki 		bphy_err(drvr, "Failed to get bss info (%d)\n", err);
304623e9c128SRafał Miłecki 		goto out_kfree;
304705491d2cSKalle Valo 	}
304822d0d2faSOmer Efrat 	si->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);
304905491d2cSKalle Valo 	si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
305005491d2cSKalle Valo 	si->bss_param.dtim_period = buf->bss_le.dtim_period;
305105491d2cSKalle Valo 	capability = le16_to_cpu(buf->bss_le.capability);
305205491d2cSKalle Valo 	if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
305305491d2cSKalle Valo 		si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
305405491d2cSKalle Valo 	if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
305505491d2cSKalle Valo 		si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
305605491d2cSKalle Valo 	if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
305705491d2cSKalle Valo 		si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
305823e9c128SRafał Miłecki 
305923e9c128SRafał Miłecki out_kfree:
306023e9c128SRafał Miłecki 	kfree(buf);
306105491d2cSKalle Valo }
306205491d2cSKalle Valo 
306305491d2cSKalle Valo static s32
brcmf_cfg80211_get_station_ibss(struct brcmf_if * ifp,struct station_info * sinfo)30643f5893d1SHante Meuleman brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
30653f5893d1SHante Meuleman 				struct station_info *sinfo)
30663f5893d1SHante Meuleman {
306716e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
30683f5893d1SHante Meuleman 	struct brcmf_scb_val_le scbval;
30693f5893d1SHante Meuleman 	struct brcmf_pktcnt_le pktcnt;
30703f5893d1SHante Meuleman 	s32 err;
3071*fb1862ceSArend van Spriel 	u32 rate;
30723f5893d1SHante Meuleman 	u32 rssi;
30733f5893d1SHante Meuleman 
30743f5893d1SHante Meuleman 	/* Get the current tx rate */
30753f5893d1SHante Meuleman 	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
30763f5893d1SHante Meuleman 	if (err < 0) {
307716e64676SRafał Miłecki 		bphy_err(drvr, "BRCMF_C_GET_RATE error (%d)\n", err);
30783f5893d1SHante Meuleman 		return err;
30793f5893d1SHante Meuleman 	}
308022d0d2faSOmer Efrat 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
30813f5893d1SHante Meuleman 	sinfo->txrate.legacy = rate * 5;
30823f5893d1SHante Meuleman 
30833f5893d1SHante Meuleman 	memset(&scbval, 0, sizeof(scbval));
30843f5893d1SHante Meuleman 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
30853f5893d1SHante Meuleman 				     sizeof(scbval));
30863f5893d1SHante Meuleman 	if (err) {
308716e64676SRafał Miłecki 		bphy_err(drvr, "BRCMF_C_GET_RSSI error (%d)\n", err);
30883f5893d1SHante Meuleman 		return err;
30893f5893d1SHante Meuleman 	}
30903f5893d1SHante Meuleman 	rssi = le32_to_cpu(scbval.val);
309122d0d2faSOmer Efrat 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
30923f5893d1SHante Meuleman 	sinfo->signal = rssi;
30933f5893d1SHante Meuleman 
30943f5893d1SHante Meuleman 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
30953f5893d1SHante Meuleman 				     sizeof(pktcnt));
30963f5893d1SHante Meuleman 	if (err) {
309716e64676SRafał Miłecki 		bphy_err(drvr, "BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
30983f5893d1SHante Meuleman 		return err;
30993f5893d1SHante Meuleman 	}
310022d0d2faSOmer Efrat 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
310122d0d2faSOmer Efrat 			 BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
310222d0d2faSOmer Efrat 			 BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
310322d0d2faSOmer Efrat 			 BIT_ULL(NL80211_STA_INFO_TX_FAILED);
31043f5893d1SHante Meuleman 	sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
31053f5893d1SHante Meuleman 	sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
31063f5893d1SHante Meuleman 	sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
31073f5893d1SHante Meuleman 	sinfo->tx_failed  = le32_to_cpu(pktcnt.tx_bad_pkt);
31083f5893d1SHante Meuleman 
31093f5893d1SHante Meuleman 	return 0;
31103f5893d1SHante Meuleman }
31113f5893d1SHante Meuleman 
31123f5893d1SHante Meuleman static s32
brcmf_cfg80211_get_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_info * sinfo)311305491d2cSKalle Valo brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
311405491d2cSKalle Valo 			   const u8 *mac, struct station_info *sinfo)
311505491d2cSKalle Valo {
311616e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
311705491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
311816e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
311994abd778SJaap Jan Meijer 	struct brcmf_scb_val_le scb_val;
312005491d2cSKalle Valo 	s32 err = 0;
312105491d2cSKalle Valo 	struct brcmf_sta_info_le sta_info_le;
312205491d2cSKalle Valo 	u32 sta_flags;
312305491d2cSKalle Valo 	u32 is_tdls_peer;
31249a159093SAlvin Šipraga 	s32 total_rssi_avg = 0;
31259a159093SAlvin Šipraga 	s32 total_rssi = 0;
31269a159093SAlvin Šipraga 	s32 count_rssi = 0;
312794abd778SJaap Jan Meijer 	int rssi;
312805491d2cSKalle Valo 	u32 i;
312905491d2cSKalle Valo 
313005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
313105491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
313205491d2cSKalle Valo 		return -EIO;
313305491d2cSKalle Valo 
31343f5893d1SHante Meuleman 	if (brcmf_is_ibssmode(ifp->vif))
31353f5893d1SHante Meuleman 		return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
31363f5893d1SHante Meuleman 
313705491d2cSKalle Valo 	memset(&sta_info_le, 0, sizeof(sta_info_le));
313805491d2cSKalle Valo 	memcpy(&sta_info_le, mac, ETH_ALEN);
313905491d2cSKalle Valo 	err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
314005491d2cSKalle Valo 				       &sta_info_le,
314105491d2cSKalle Valo 				       sizeof(sta_info_le));
314205491d2cSKalle Valo 	is_tdls_peer = !err;
314305491d2cSKalle Valo 	if (err) {
314405491d2cSKalle Valo 		err = brcmf_fil_iovar_data_get(ifp, "sta_info",
314505491d2cSKalle Valo 					       &sta_info_le,
314605491d2cSKalle Valo 					       sizeof(sta_info_le));
314705491d2cSKalle Valo 		if (err < 0) {
314816e64676SRafał Miłecki 			bphy_err(drvr, "GET STA INFO failed, %d\n", err);
314905491d2cSKalle Valo 			goto done;
315005491d2cSKalle Valo 		}
315105491d2cSKalle Valo 	}
315205491d2cSKalle Valo 	brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
315322d0d2faSOmer Efrat 	sinfo->filled = BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
315405491d2cSKalle Valo 	sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
315505491d2cSKalle Valo 	sta_flags = le32_to_cpu(sta_info_le.flags);
315605491d2cSKalle Valo 	brcmf_convert_sta_flags(sta_flags, sinfo);
315705491d2cSKalle Valo 	sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
315805491d2cSKalle Valo 	if (is_tdls_peer)
315905491d2cSKalle Valo 		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
316005491d2cSKalle Valo 	else
316105491d2cSKalle Valo 		sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
316205491d2cSKalle Valo 	if (sta_flags & BRCMF_STA_ASSOC) {
316322d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);
316405491d2cSKalle Valo 		sinfo->connected_time = le32_to_cpu(sta_info_le.in);
316505491d2cSKalle Valo 		brcmf_fill_bss_param(ifp, sinfo);
316605491d2cSKalle Valo 	}
316705491d2cSKalle Valo 	if (sta_flags & BRCMF_STA_SCBSTATS) {
316822d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
316905491d2cSKalle Valo 		sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
317022d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
317105491d2cSKalle Valo 		sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
317205491d2cSKalle Valo 		sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
317322d0d2faSOmer Efrat 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
317405491d2cSKalle Valo 		sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
317505491d2cSKalle Valo 		sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
317605491d2cSKalle Valo 		if (sinfo->tx_packets) {
317722d0d2faSOmer Efrat 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
317805491d2cSKalle Valo 			sinfo->txrate.legacy =
317905491d2cSKalle Valo 				le32_to_cpu(sta_info_le.tx_rate) / 100;
318005491d2cSKalle Valo 		}
318105491d2cSKalle Valo 		if (sinfo->rx_packets) {
318222d0d2faSOmer Efrat 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
318305491d2cSKalle Valo 			sinfo->rxrate.legacy =
318405491d2cSKalle Valo 				le32_to_cpu(sta_info_le.rx_rate) / 100;
318505491d2cSKalle Valo 		}
318605491d2cSKalle Valo 		if (le16_to_cpu(sta_info_le.ver) >= 4) {
318722d0d2faSOmer Efrat 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
318805491d2cSKalle Valo 			sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
318922d0d2faSOmer Efrat 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
319005491d2cSKalle Valo 			sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
319105491d2cSKalle Valo 		}
319205491d2cSKalle Valo 		for (i = 0; i < BRCMF_ANT_MAX; i++) {
31939a159093SAlvin Šipraga 			if (sta_info_le.rssi[i] == 0 ||
31949a159093SAlvin Šipraga 			    sta_info_le.rx_lastpkt_rssi[i] == 0)
31959a159093SAlvin Šipraga 				continue;
3196feb45643SAlvin Šipraga 			sinfo->chains |= BIT(count_rssi);
31979a159093SAlvin Šipraga 			sinfo->chain_signal[count_rssi] =
31989a159093SAlvin Šipraga 				sta_info_le.rx_lastpkt_rssi[i];
319905491d2cSKalle Valo 			sinfo->chain_signal_avg[count_rssi] =
320005491d2cSKalle Valo 				sta_info_le.rssi[i];
32019a159093SAlvin Šipraga 			total_rssi += sta_info_le.rx_lastpkt_rssi[i];
32029a159093SAlvin Šipraga 			total_rssi_avg += sta_info_le.rssi[i];
320305491d2cSKalle Valo 			count_rssi++;
320405491d2cSKalle Valo 		}
320505491d2cSKalle Valo 		if (count_rssi) {
320622d0d2faSOmer Efrat 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
32079a159093SAlvin Šipraga 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
32089a159093SAlvin Šipraga 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
32099a159093SAlvin Šipraga 			sinfo->filled |=
32109a159093SAlvin Šipraga 				BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
32119a159093SAlvin Šipraga 			sinfo->signal = total_rssi / count_rssi;
32129a159093SAlvin Šipraga 			sinfo->signal_avg = total_rssi_avg / count_rssi;
321394abd778SJaap Jan Meijer 		} else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
321494abd778SJaap Jan Meijer 			&ifp->vif->sme_state)) {
321594abd778SJaap Jan Meijer 			memset(&scb_val, 0, sizeof(scb_val));
321694abd778SJaap Jan Meijer 			err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
321794abd778SJaap Jan Meijer 						     &scb_val, sizeof(scb_val));
321894abd778SJaap Jan Meijer 			if (err) {
321916e64676SRafał Miłecki 				bphy_err(drvr, "Could not get rssi (%d)\n",
32203ef005b8SRafał Miłecki 					 err);
322194abd778SJaap Jan Meijer 				goto done;
322294abd778SJaap Jan Meijer 			} else {
322394abd778SJaap Jan Meijer 				rssi = le32_to_cpu(scb_val.val);
322422d0d2faSOmer Efrat 				sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
322594abd778SJaap Jan Meijer 				sinfo->signal = rssi;
322694abd778SJaap Jan Meijer 				brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
322794abd778SJaap Jan Meijer 			}
322805491d2cSKalle Valo 		}
322905491d2cSKalle Valo 	}
323005491d2cSKalle Valo done:
323105491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
323205491d2cSKalle Valo 	return err;
323305491d2cSKalle Valo }
323405491d2cSKalle Valo 
323505491d2cSKalle Valo static int
brcmf_cfg80211_dump_station(struct wiphy * wiphy,struct net_device * ndev,int idx,u8 * mac,struct station_info * sinfo)323605491d2cSKalle Valo brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
323705491d2cSKalle Valo 			    int idx, u8 *mac, struct station_info *sinfo)
323805491d2cSKalle Valo {
323905491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
324005491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
324116e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
324205491d2cSKalle Valo 	s32 err;
324305491d2cSKalle Valo 
324405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
324505491d2cSKalle Valo 
324605491d2cSKalle Valo 	if (idx == 0) {
324705491d2cSKalle Valo 		cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
324805491d2cSKalle Valo 		err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
324905491d2cSKalle Valo 					     &cfg->assoclist,
325005491d2cSKalle Valo 					     sizeof(cfg->assoclist));
325105491d2cSKalle Valo 		if (err) {
325278f0a64fSDmitry Osipenko 			/* GET_ASSOCLIST unsupported by firmware of older chips */
325378f0a64fSDmitry Osipenko 			if (err == -EBADE)
325478f0a64fSDmitry Osipenko 				bphy_info_once(drvr, "BRCMF_C_GET_ASSOCLIST unsupported\n");
325578f0a64fSDmitry Osipenko 			else
325678f0a64fSDmitry Osipenko 				bphy_err(drvr, "BRCMF_C_GET_ASSOCLIST failed, err=%d\n",
325705491d2cSKalle Valo 					 err);
325878f0a64fSDmitry Osipenko 
325905491d2cSKalle Valo 			cfg->assoclist.count = 0;
326005491d2cSKalle Valo 			return -EOPNOTSUPP;
326105491d2cSKalle Valo 		}
326205491d2cSKalle Valo 	}
326305491d2cSKalle Valo 	if (idx < le32_to_cpu(cfg->assoclist.count)) {
326405491d2cSKalle Valo 		memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
326505491d2cSKalle Valo 		return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
326605491d2cSKalle Valo 	}
326705491d2cSKalle Valo 	return -ENOENT;
326805491d2cSKalle Valo }
326905491d2cSKalle Valo 
327005491d2cSKalle Valo static s32
brcmf_cfg80211_set_power_mgmt(struct wiphy * wiphy,struct net_device * ndev,bool enabled,s32 timeout)327105491d2cSKalle Valo brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
327205491d2cSKalle Valo 			   bool enabled, s32 timeout)
327305491d2cSKalle Valo {
327405491d2cSKalle Valo 	s32 pm;
327505491d2cSKalle Valo 	s32 err = 0;
327605491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
327705491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
327816e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
327905491d2cSKalle Valo 
328005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
328105491d2cSKalle Valo 
328205491d2cSKalle Valo 	/*
328305491d2cSKalle Valo 	 * Powersave enable/disable request is coming from the
328405491d2cSKalle Valo 	 * cfg80211 even before the interface is up. In that
328505491d2cSKalle Valo 	 * scenario, driver will be storing the power save
328605491d2cSKalle Valo 	 * preference in cfg struct to apply this to
328705491d2cSKalle Valo 	 * FW later while initializing the dongle
328805491d2cSKalle Valo 	 */
328905491d2cSKalle Valo 	cfg->pwr_save = enabled;
329005491d2cSKalle Valo 	if (!check_vif_up(ifp->vif)) {
329105491d2cSKalle Valo 
329205491d2cSKalle Valo 		brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
329305491d2cSKalle Valo 		goto done;
329405491d2cSKalle Valo 	}
329505491d2cSKalle Valo 
329605491d2cSKalle Valo 	pm = enabled ? PM_FAST : PM_OFF;
329705491d2cSKalle Valo 	/* Do not enable the power save after assoc if it is a p2p interface */
329805491d2cSKalle Valo 	if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
329905491d2cSKalle Valo 		brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
330005491d2cSKalle Valo 		pm = PM_OFF;
330105491d2cSKalle Valo 	}
330205491d2cSKalle Valo 	brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
330305491d2cSKalle Valo 
330405491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
330505491d2cSKalle Valo 	if (err) {
330605491d2cSKalle Valo 		if (err == -ENODEV)
330716e64676SRafał Miłecki 			bphy_err(drvr, "net_device is not ready yet\n");
330805491d2cSKalle Valo 		else
330916e64676SRafał Miłecki 			bphy_err(drvr, "error (%d)\n", err);
331005491d2cSKalle Valo 	}
33113dc05ffbSNicolas Saenz Julienne 
33123dc05ffbSNicolas Saenz Julienne 	err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret",
33133dc05ffbSNicolas Saenz Julienne 				min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS));
33143dc05ffbSNicolas Saenz Julienne 	if (err)
33153dc05ffbSNicolas Saenz Julienne 		bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err);
33163dc05ffbSNicolas Saenz Julienne 
331705491d2cSKalle Valo done:
331805491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
331905491d2cSKalle Valo 	return err;
332005491d2cSKalle Valo }
332105491d2cSKalle Valo 
brcmf_inform_single_bss(struct brcmf_cfg80211_info * cfg,struct brcmf_bss_info_le * bi)332205491d2cSKalle Valo static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
332305491d2cSKalle Valo 				   struct brcmf_bss_info_le *bi)
332405491d2cSKalle Valo {
332505491d2cSKalle Valo 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
332616e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
332705491d2cSKalle Valo 	struct cfg80211_bss *bss;
3328aed14219SFranky Lin 	enum nl80211_band band;
332905491d2cSKalle Valo 	struct brcmu_chan ch;
333005491d2cSKalle Valo 	u16 channel;
333105491d2cSKalle Valo 	u32 freq;
333205491d2cSKalle Valo 	u16 notify_capability;
333305491d2cSKalle Valo 	u16 notify_interval;
333405491d2cSKalle Valo 	u8 *notify_ie;
333505491d2cSKalle Valo 	size_t notify_ielen;
3336763ece85SFranky Lin 	struct cfg80211_inform_bss bss_data = {};
333705491d2cSKalle Valo 
333805491d2cSKalle Valo 	if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
333916e64676SRafał Miłecki 		bphy_err(drvr, "Bss info is larger than buffer. Discarding\n");
3340bd994491SRaveendran Somu 		return -EINVAL;
334105491d2cSKalle Valo 	}
334205491d2cSKalle Valo 
334305491d2cSKalle Valo 	if (!bi->ctl_ch) {
334405491d2cSKalle Valo 		ch.chspec = le16_to_cpu(bi->chanspec);
334505491d2cSKalle Valo 		cfg->d11inf.decchspec(&ch);
33464712d88aSRafał Miłecki 		bi->ctl_ch = ch.control_ch_num;
334705491d2cSKalle Valo 	}
334805491d2cSKalle Valo 	channel = bi->ctl_ch;
334905491d2cSKalle Valo 
335005491d2cSKalle Valo 	if (channel <= CH_MAX_2G_CHANNEL)
3351aed14219SFranky Lin 		band = NL80211_BAND_2GHZ;
335205491d2cSKalle Valo 	else
3353aed14219SFranky Lin 		band = NL80211_BAND_5GHZ;
335405491d2cSKalle Valo 
3355aed14219SFranky Lin 	freq = ieee80211_channel_to_frequency(channel, band);
33567742fce4SFranky Lin 	bss_data.chan = ieee80211_get_channel(wiphy, freq);
33577742fce4SFranky Lin 	bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20;
33587742fce4SFranky Lin 	bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
335905491d2cSKalle Valo 
336005491d2cSKalle Valo 	notify_capability = le16_to_cpu(bi->capability);
336105491d2cSKalle Valo 	notify_interval = le16_to_cpu(bi->beacon_period);
336205491d2cSKalle Valo 	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
336305491d2cSKalle Valo 	notify_ielen = le32_to_cpu(bi->ie_length);
33647742fce4SFranky Lin 	bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100;
336505491d2cSKalle Valo 
336605491d2cSKalle Valo 	brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
336705491d2cSKalle Valo 	brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
336805491d2cSKalle Valo 	brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
336905491d2cSKalle Valo 	brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
33707742fce4SFranky Lin 	brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal);
337105491d2cSKalle Valo 
33727742fce4SFranky Lin 	bss = cfg80211_inform_bss_data(wiphy, &bss_data,
337305491d2cSKalle Valo 				       CFG80211_BSS_FTYPE_UNKNOWN,
337405491d2cSKalle Valo 				       (const u8 *)bi->BSSID,
337505491d2cSKalle Valo 				       0, notify_capability,
337605491d2cSKalle Valo 				       notify_interval, notify_ie,
33777742fce4SFranky Lin 				       notify_ielen, GFP_KERNEL);
337805491d2cSKalle Valo 
337905491d2cSKalle Valo 	if (!bss)
338005491d2cSKalle Valo 		return -ENOMEM;
338105491d2cSKalle Valo 
338205491d2cSKalle Valo 	cfg80211_put_bss(wiphy, bss);
338305491d2cSKalle Valo 
338405491d2cSKalle Valo 	return 0;
338505491d2cSKalle Valo }
338605491d2cSKalle Valo 
338705491d2cSKalle Valo static struct brcmf_bss_info_le *
next_bss_le(struct brcmf_scan_results * list,struct brcmf_bss_info_le * bss)338805491d2cSKalle Valo next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
338905491d2cSKalle Valo {
339005491d2cSKalle Valo 	if (bss == NULL)
339105491d2cSKalle Valo 		return list->bss_info_le;
339205491d2cSKalle Valo 	return (struct brcmf_bss_info_le *)((unsigned long)bss +
339305491d2cSKalle Valo 					    le32_to_cpu(bss->length));
339405491d2cSKalle Valo }
339505491d2cSKalle Valo 
brcmf_inform_bss(struct brcmf_cfg80211_info * cfg)339605491d2cSKalle Valo static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
339705491d2cSKalle Valo {
339816e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
339905491d2cSKalle Valo 	struct brcmf_scan_results *bss_list;
340005491d2cSKalle Valo 	struct brcmf_bss_info_le *bi = NULL;	/* must be initialized */
340105491d2cSKalle Valo 	s32 err = 0;
340205491d2cSKalle Valo 	int i;
340305491d2cSKalle Valo 
340405491d2cSKalle Valo 	bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
340505491d2cSKalle Valo 	if (bss_list->count != 0 &&
340605491d2cSKalle Valo 	    bss_list->version != BRCMF_BSS_INFO_VERSION) {
340716e64676SRafał Miłecki 		bphy_err(drvr, "Version %d != WL_BSS_INFO_VERSION\n",
340805491d2cSKalle Valo 			 bss_list->version);
340905491d2cSKalle Valo 		return -EOPNOTSUPP;
341005491d2cSKalle Valo 	}
341105491d2cSKalle Valo 	brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
341205491d2cSKalle Valo 	for (i = 0; i < bss_list->count; i++) {
341305491d2cSKalle Valo 		bi = next_bss_le(bss_list, bi);
341405491d2cSKalle Valo 		err = brcmf_inform_single_bss(cfg, bi);
341505491d2cSKalle Valo 		if (err)
341605491d2cSKalle Valo 			break;
341705491d2cSKalle Valo 	}
341805491d2cSKalle Valo 	return err;
341905491d2cSKalle Valo }
342005491d2cSKalle Valo 
brcmf_inform_ibss(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const u8 * bssid)3421b0a79088SHante Meuleman static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
342205491d2cSKalle Valo 			     struct net_device *ndev, const u8 *bssid)
342305491d2cSKalle Valo {
342405491d2cSKalle Valo 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
342516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
342605491d2cSKalle Valo 	struct ieee80211_channel *notify_channel;
342705491d2cSKalle Valo 	struct brcmf_bss_info_le *bi = NULL;
342805491d2cSKalle Valo 	struct ieee80211_supported_band *band;
342905491d2cSKalle Valo 	struct cfg80211_bss *bss;
343005491d2cSKalle Valo 	struct brcmu_chan ch;
343105491d2cSKalle Valo 	u8 *buf = NULL;
343205491d2cSKalle Valo 	s32 err = 0;
343305491d2cSKalle Valo 	u32 freq;
343405491d2cSKalle Valo 	u16 notify_capability;
343505491d2cSKalle Valo 	u16 notify_interval;
343605491d2cSKalle Valo 	u8 *notify_ie;
343705491d2cSKalle Valo 	size_t notify_ielen;
343805491d2cSKalle Valo 	s32 notify_signal;
343905491d2cSKalle Valo 
344005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
344105491d2cSKalle Valo 
344205491d2cSKalle Valo 	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
344305491d2cSKalle Valo 	if (buf == NULL) {
344405491d2cSKalle Valo 		err = -ENOMEM;
344505491d2cSKalle Valo 		goto CleanUp;
344605491d2cSKalle Valo 	}
344705491d2cSKalle Valo 
344805491d2cSKalle Valo 	*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
344905491d2cSKalle Valo 
345005491d2cSKalle Valo 	err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
345105491d2cSKalle Valo 				     buf, WL_BSS_INFO_MAX);
345205491d2cSKalle Valo 	if (err) {
345316e64676SRafał Miłecki 		bphy_err(drvr, "WLC_GET_BSS_INFO failed: %d\n", err);
345405491d2cSKalle Valo 		goto CleanUp;
345505491d2cSKalle Valo 	}
345605491d2cSKalle Valo 
345705491d2cSKalle Valo 	bi = (struct brcmf_bss_info_le *)(buf + 4);
345805491d2cSKalle Valo 
345905491d2cSKalle Valo 	ch.chspec = le16_to_cpu(bi->chanspec);
346005491d2cSKalle Valo 	cfg->d11inf.decchspec(&ch);
346105491d2cSKalle Valo 
346205491d2cSKalle Valo 	if (ch.band == BRCMU_CHAN_BAND_2G)
346357fbcce3SJohannes Berg 		band = wiphy->bands[NL80211_BAND_2GHZ];
346405491d2cSKalle Valo 	else
346557fbcce3SJohannes Berg 		band = wiphy->bands[NL80211_BAND_5GHZ];
346605491d2cSKalle Valo 
34674712d88aSRafał Miłecki 	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
3468b0a79088SHante Meuleman 	cfg->channel = freq;
346905491d2cSKalle Valo 	notify_channel = ieee80211_get_channel(wiphy, freq);
347005491d2cSKalle Valo 
347105491d2cSKalle Valo 	notify_capability = le16_to_cpu(bi->capability);
347205491d2cSKalle Valo 	notify_interval = le16_to_cpu(bi->beacon_period);
347305491d2cSKalle Valo 	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
347405491d2cSKalle Valo 	notify_ielen = le32_to_cpu(bi->ie_length);
347505491d2cSKalle Valo 	notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
347605491d2cSKalle Valo 
34774712d88aSRafał Miłecki 	brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq);
347805491d2cSKalle Valo 	brcmf_dbg(CONN, "capability: %X\n", notify_capability);
347905491d2cSKalle Valo 	brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
348005491d2cSKalle Valo 	brcmf_dbg(CONN, "signal: %d\n", notify_signal);
348105491d2cSKalle Valo 
348205491d2cSKalle Valo 	bss = cfg80211_inform_bss(wiphy, notify_channel,
348305491d2cSKalle Valo 				  CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
348405491d2cSKalle Valo 				  notify_capability, notify_interval,
348505491d2cSKalle Valo 				  notify_ie, notify_ielen, notify_signal,
348605491d2cSKalle Valo 				  GFP_KERNEL);
348705491d2cSKalle Valo 
348805491d2cSKalle Valo 	if (!bss) {
348905491d2cSKalle Valo 		err = -ENOMEM;
349005491d2cSKalle Valo 		goto CleanUp;
349105491d2cSKalle Valo 	}
349205491d2cSKalle Valo 
349305491d2cSKalle Valo 	cfg80211_put_bss(wiphy, bss);
349405491d2cSKalle Valo 
349505491d2cSKalle Valo CleanUp:
349605491d2cSKalle Valo 
349705491d2cSKalle Valo 	kfree(buf);
349805491d2cSKalle Valo 
349905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
350005491d2cSKalle Valo 
350105491d2cSKalle Valo 	return err;
350205491d2cSKalle Valo }
350305491d2cSKalle Valo 
brcmf_update_bss_info(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)350405491d2cSKalle Valo static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
350505491d2cSKalle Valo 				 struct brcmf_if *ifp)
350605491d2cSKalle Valo {
350716e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
3508a373f38cSRamesh Rangavittal 	struct brcmf_bss_info_le *bi = NULL;
350905491d2cSKalle Valo 	s32 err = 0;
351005491d2cSKalle Valo 
351105491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
351205491d2cSKalle Valo 	if (brcmf_is_ibssmode(ifp->vif))
351305491d2cSKalle Valo 		return err;
351405491d2cSKalle Valo 
351505491d2cSKalle Valo 	*(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
351605491d2cSKalle Valo 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
351705491d2cSKalle Valo 				     cfg->extra_buf, WL_EXTRA_BUF_MAX);
351805491d2cSKalle Valo 	if (err) {
351916e64676SRafał Miłecki 		bphy_err(drvr, "Could not get bss info %d\n", err);
352005491d2cSKalle Valo 		goto update_bss_info_out;
352105491d2cSKalle Valo 	}
352205491d2cSKalle Valo 	bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
352305491d2cSKalle Valo 	err = brcmf_inform_single_bss(cfg, bi);
352405491d2cSKalle Valo 
352505491d2cSKalle Valo update_bss_info_out:
352605491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit");
352705491d2cSKalle Valo 	return err;
352805491d2cSKalle Valo }
352905491d2cSKalle Valo 
brcmf_abort_scanning(struct brcmf_cfg80211_info * cfg)353005491d2cSKalle Valo void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
353105491d2cSKalle Valo {
353205491d2cSKalle Valo 	struct escan_info *escan = &cfg->escan_info;
353305491d2cSKalle Valo 
353405491d2cSKalle Valo 	set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
3535efc2c1faSArend Van Spriel 	if (cfg->int_escan_map || cfg->scan_request) {
353605491d2cSKalle Valo 		escan->escan_state = WL_ESCAN_STATE_IDLE;
353705491d2cSKalle Valo 		brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
353805491d2cSKalle Valo 	}
353905491d2cSKalle Valo 	clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
354005491d2cSKalle Valo 	clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
354105491d2cSKalle Valo }
354205491d2cSKalle Valo 
brcmf_cfg80211_escan_timeout_worker(struct work_struct * work)354305491d2cSKalle Valo static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
354405491d2cSKalle Valo {
354505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg =
354605491d2cSKalle Valo 			container_of(work, struct brcmf_cfg80211_info,
354705491d2cSKalle Valo 				     escan_timeout_work);
354805491d2cSKalle Valo 
354905491d2cSKalle Valo 	brcmf_inform_bss(cfg);
355005491d2cSKalle Valo 	brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
355105491d2cSKalle Valo }
355205491d2cSKalle Valo 
brcmf_escan_timeout(struct timer_list * t)3553e99e88a9SKees Cook static void brcmf_escan_timeout(struct timer_list *t)
355405491d2cSKalle Valo {
355505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg =
3556e99e88a9SKees Cook 			from_timer(cfg, t, escan_timeout);
355716e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
355805491d2cSKalle Valo 
3559efc2c1faSArend Van Spriel 	if (cfg->int_escan_map || cfg->scan_request) {
356016e64676SRafał Miłecki 		bphy_err(drvr, "timer expired\n");
356105491d2cSKalle Valo 		schedule_work(&cfg->escan_timeout_work);
356205491d2cSKalle Valo 	}
356305491d2cSKalle Valo }
356405491d2cSKalle Valo 
356505491d2cSKalle Valo static s32
brcmf_compare_update_same_bss(struct brcmf_cfg80211_info * cfg,struct brcmf_bss_info_le * bss,struct brcmf_bss_info_le * bss_info_le)356605491d2cSKalle Valo brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
356705491d2cSKalle Valo 			      struct brcmf_bss_info_le *bss,
356805491d2cSKalle Valo 			      struct brcmf_bss_info_le *bss_info_le)
356905491d2cSKalle Valo {
357005491d2cSKalle Valo 	struct brcmu_chan ch_bss, ch_bss_info_le;
357105491d2cSKalle Valo 
357205491d2cSKalle Valo 	ch_bss.chspec = le16_to_cpu(bss->chanspec);
357305491d2cSKalle Valo 	cfg->d11inf.decchspec(&ch_bss);
357405491d2cSKalle Valo 	ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
357505491d2cSKalle Valo 	cfg->d11inf.decchspec(&ch_bss_info_le);
357605491d2cSKalle Valo 
357705491d2cSKalle Valo 	if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
357805491d2cSKalle Valo 		ch_bss.band == ch_bss_info_le.band &&
357905491d2cSKalle Valo 		bss_info_le->SSID_len == bss->SSID_len &&
358005491d2cSKalle Valo 		!memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
358105491d2cSKalle Valo 		if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
358205491d2cSKalle Valo 			(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
358305491d2cSKalle Valo 			s16 bss_rssi = le16_to_cpu(bss->RSSI);
358405491d2cSKalle Valo 			s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
358505491d2cSKalle Valo 
358605491d2cSKalle Valo 			/* preserve max RSSI if the measurements are
358705491d2cSKalle Valo 			* both on-channel or both off-channel
358805491d2cSKalle Valo 			*/
358905491d2cSKalle Valo 			if (bss_info_rssi > bss_rssi)
359005491d2cSKalle Valo 				bss->RSSI = bss_info_le->RSSI;
359105491d2cSKalle Valo 		} else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
359205491d2cSKalle Valo 			(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
359305491d2cSKalle Valo 			/* preserve the on-channel rssi measurement
359405491d2cSKalle Valo 			* if the new measurement is off channel
359505491d2cSKalle Valo 			*/
359605491d2cSKalle Valo 			bss->RSSI = bss_info_le->RSSI;
359705491d2cSKalle Valo 			bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
359805491d2cSKalle Valo 		}
359905491d2cSKalle Valo 		return 1;
360005491d2cSKalle Valo 	}
360105491d2cSKalle Valo 	return 0;
360205491d2cSKalle Valo }
360305491d2cSKalle Valo 
360405491d2cSKalle Valo static s32
brcmf_cfg80211_escan_handler(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)360505491d2cSKalle Valo brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
360605491d2cSKalle Valo 			     const struct brcmf_event_msg *e, void *data)
360705491d2cSKalle Valo {
360816e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
360916e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = drvr->config;
361005491d2cSKalle Valo 	s32 status;
361105491d2cSKalle Valo 	struct brcmf_escan_result_le *escan_result_le;
361217df6453SArend Van Spriel 	u32 escan_buflen;
361305491d2cSKalle Valo 	struct brcmf_bss_info_le *bss_info_le;
361405491d2cSKalle Valo 	struct brcmf_bss_info_le *bss = NULL;
361505491d2cSKalle Valo 	u32 bi_length;
361605491d2cSKalle Valo 	struct brcmf_scan_results *list;
361705491d2cSKalle Valo 	u32 i;
361805491d2cSKalle Valo 	bool aborted;
361905491d2cSKalle Valo 
362005491d2cSKalle Valo 	status = e->status;
362105491d2cSKalle Valo 
3622b9472a2eSHans de Goede 	if (status == BRCMF_E_STATUS_ABORT)
3623b9472a2eSHans de Goede 		goto exit;
3624b9472a2eSHans de Goede 
362505491d2cSKalle Valo 	if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
362616e64676SRafał Miłecki 		bphy_err(drvr, "scan not ready, bsscfgidx=%d\n",
36273ef005b8SRafał Miłecki 			 ifp->bsscfgidx);
362805491d2cSKalle Valo 		return -EPERM;
362905491d2cSKalle Valo 	}
363005491d2cSKalle Valo 
363105491d2cSKalle Valo 	if (status == BRCMF_E_STATUS_PARTIAL) {
363205491d2cSKalle Valo 		brcmf_dbg(SCAN, "ESCAN Partial result\n");
363317df6453SArend Van Spriel 		if (e->datalen < sizeof(*escan_result_le)) {
363416e64676SRafał Miłecki 			bphy_err(drvr, "invalid event data length\n");
363517df6453SArend Van Spriel 			goto exit;
363617df6453SArend Van Spriel 		}
363705491d2cSKalle Valo 		escan_result_le = (struct brcmf_escan_result_le *) data;
363805491d2cSKalle Valo 		if (!escan_result_le) {
363916e64676SRafał Miłecki 			bphy_err(drvr, "Invalid escan result (NULL pointer)\n");
364005491d2cSKalle Valo 			goto exit;
364105491d2cSKalle Valo 		}
364217df6453SArend Van Spriel 		escan_buflen = le32_to_cpu(escan_result_le->buflen);
364317df6453SArend Van Spriel 		if (escan_buflen > BRCMF_ESCAN_BUF_SIZE ||
364417df6453SArend Van Spriel 		    escan_buflen > e->datalen ||
364517df6453SArend Van Spriel 		    escan_buflen < sizeof(*escan_result_le)) {
364616e64676SRafał Miłecki 			bphy_err(drvr, "Invalid escan buffer length: %d\n",
364717df6453SArend Van Spriel 				 escan_buflen);
364817df6453SArend Van Spriel 			goto exit;
364917df6453SArend Van Spriel 		}
365005491d2cSKalle Valo 		if (le16_to_cpu(escan_result_le->bss_count) != 1) {
365116e64676SRafał Miłecki 			bphy_err(drvr, "Invalid bss_count %d: ignoring\n",
365205491d2cSKalle Valo 				 escan_result_le->bss_count);
365305491d2cSKalle Valo 			goto exit;
365405491d2cSKalle Valo 		}
365505491d2cSKalle Valo 		bss_info_le = &escan_result_le->bss_info_le;
365605491d2cSKalle Valo 
365705491d2cSKalle Valo 		if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
365805491d2cSKalle Valo 			goto exit;
365905491d2cSKalle Valo 
3660efc2c1faSArend Van Spriel 		if (!cfg->int_escan_map && !cfg->scan_request) {
366105491d2cSKalle Valo 			brcmf_dbg(SCAN, "result without cfg80211 request\n");
366205491d2cSKalle Valo 			goto exit;
366305491d2cSKalle Valo 		}
366405491d2cSKalle Valo 
366505491d2cSKalle Valo 		bi_length = le32_to_cpu(bss_info_le->length);
366617df6453SArend Van Spriel 		if (bi_length != escan_buflen -	WL_ESCAN_RESULTS_FIXED_SIZE) {
366716e64676SRafał Miłecki 			bphy_err(drvr, "Ignoring invalid bss_info length: %d\n",
366805491d2cSKalle Valo 				 bi_length);
366905491d2cSKalle Valo 			goto exit;
367005491d2cSKalle Valo 		}
367105491d2cSKalle Valo 
367205491d2cSKalle Valo 		if (!(cfg_to_wiphy(cfg)->interface_modes &
367305491d2cSKalle Valo 					BIT(NL80211_IFTYPE_ADHOC))) {
367405491d2cSKalle Valo 			if (le16_to_cpu(bss_info_le->capability) &
367505491d2cSKalle Valo 						WLAN_CAPABILITY_IBSS) {
367616e64676SRafał Miłecki 				bphy_err(drvr, "Ignoring IBSS result\n");
367705491d2cSKalle Valo 				goto exit;
367805491d2cSKalle Valo 			}
367905491d2cSKalle Valo 		}
368005491d2cSKalle Valo 
368105491d2cSKalle Valo 		list = (struct brcmf_scan_results *)
368205491d2cSKalle Valo 				cfg->escan_info.escan_buf;
3683d5367334SHante Meuleman 		if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
368416e64676SRafał Miłecki 			bphy_err(drvr, "Buffer is too small: ignoring\n");
368505491d2cSKalle Valo 			goto exit;
368605491d2cSKalle Valo 		}
368705491d2cSKalle Valo 
368805491d2cSKalle Valo 		for (i = 0; i < list->count; i++) {
368905491d2cSKalle Valo 			bss = bss ? (struct brcmf_bss_info_le *)
369005491d2cSKalle Valo 				((unsigned char *)bss +
369105491d2cSKalle Valo 				le32_to_cpu(bss->length)) : list->bss_info_le;
369205491d2cSKalle Valo 			if (brcmf_compare_update_same_bss(cfg, bss,
369305491d2cSKalle Valo 							  bss_info_le))
369405491d2cSKalle Valo 				goto exit;
369505491d2cSKalle Valo 		}
3696d5367334SHante Meuleman 		memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
3697d5367334SHante Meuleman 		       bi_length);
369805491d2cSKalle Valo 		list->version = le32_to_cpu(bss_info_le->version);
369905491d2cSKalle Valo 		list->buflen += bi_length;
370005491d2cSKalle Valo 		list->count++;
370105491d2cSKalle Valo 	} else {
370205491d2cSKalle Valo 		cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
370305491d2cSKalle Valo 		if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
370405491d2cSKalle Valo 			goto exit;
3705efc2c1faSArend Van Spriel 		if (cfg->int_escan_map || cfg->scan_request) {
370605491d2cSKalle Valo 			brcmf_inform_bss(cfg);
370705491d2cSKalle Valo 			aborted = status != BRCMF_E_STATUS_SUCCESS;
370805491d2cSKalle Valo 			brcmf_notify_escan_complete(cfg, ifp, aborted, false);
370905491d2cSKalle Valo 		} else
371005491d2cSKalle Valo 			brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
371105491d2cSKalle Valo 				  status);
371205491d2cSKalle Valo 	}
371305491d2cSKalle Valo exit:
371405491d2cSKalle Valo 	return 0;
371505491d2cSKalle Valo }
371605491d2cSKalle Valo 
brcmf_init_escan(struct brcmf_cfg80211_info * cfg)371705491d2cSKalle Valo static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
371805491d2cSKalle Valo {
371905491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
372005491d2cSKalle Valo 			    brcmf_cfg80211_escan_handler);
372105491d2cSKalle Valo 	cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
372205491d2cSKalle Valo 	/* Init scan_timeout timer */
3723e99e88a9SKees Cook 	timer_setup(&cfg->escan_timeout, brcmf_escan_timeout, 0);
372405491d2cSKalle Valo 	INIT_WORK(&cfg->escan_timeout_work,
372505491d2cSKalle Valo 		  brcmf_cfg80211_escan_timeout_worker);
372605491d2cSKalle Valo }
372705491d2cSKalle Valo 
3728fa85b30aSArend Van Spriel static struct cfg80211_scan_request *
brcmf_alloc_internal_escan_request(struct wiphy * wiphy,u32 n_netinfo)3729fa85b30aSArend Van Spriel brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {
3730fa85b30aSArend Van Spriel 	struct cfg80211_scan_request *req;
3731fa85b30aSArend Van Spriel 	size_t req_size;
3732fa85b30aSArend Van Spriel 
3733fa85b30aSArend Van Spriel 	req_size = sizeof(*req) +
3734fa85b30aSArend Van Spriel 		   n_netinfo * sizeof(req->channels[0]) +
3735fa85b30aSArend Van Spriel 		   n_netinfo * sizeof(*req->ssids);
3736fa85b30aSArend Van Spriel 
3737fa85b30aSArend Van Spriel 	req = kzalloc(req_size, GFP_KERNEL);
3738fa85b30aSArend Van Spriel 	if (req) {
3739fa85b30aSArend Van Spriel 		req->wiphy = wiphy;
3740fa85b30aSArend Van Spriel 		req->ssids = (void *)(&req->channels[0]) +
3741fa85b30aSArend Van Spriel 			     n_netinfo * sizeof(req->channels[0]);
3742fa85b30aSArend Van Spriel 	}
3743fa85b30aSArend Van Spriel 	return req;
3744fa85b30aSArend Van Spriel }
3745fa85b30aSArend Van Spriel 
brcmf_internal_escan_add_info(struct cfg80211_scan_request * req,u8 * ssid,u8 ssid_len,u8 channel)3746fa85b30aSArend Van Spriel static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
3747fa85b30aSArend Van Spriel 					 u8 *ssid, u8 ssid_len, u8 channel)
3748fa85b30aSArend Van Spriel {
3749fa85b30aSArend Van Spriel 	struct ieee80211_channel *chan;
3750fa85b30aSArend Van Spriel 	enum nl80211_band band;
37516ea51fc7SArend Van Spriel 	int freq, i;
3752fa85b30aSArend Van Spriel 
3753fa85b30aSArend Van Spriel 	if (channel <= CH_MAX_2G_CHANNEL)
3754fa85b30aSArend Van Spriel 		band = NL80211_BAND_2GHZ;
3755fa85b30aSArend Van Spriel 	else
3756fa85b30aSArend Van Spriel 		band = NL80211_BAND_5GHZ;
3757fa85b30aSArend Van Spriel 
3758fa85b30aSArend Van Spriel 	freq = ieee80211_channel_to_frequency(channel, band);
3759fa85b30aSArend Van Spriel 	if (!freq)
3760fa85b30aSArend Van Spriel 		return -EINVAL;
3761fa85b30aSArend Van Spriel 
3762fa85b30aSArend Van Spriel 	chan = ieee80211_get_channel(req->wiphy, freq);
3763fa85b30aSArend Van Spriel 	if (!chan)
3764fa85b30aSArend Van Spriel 		return -EINVAL;
3765fa85b30aSArend Van Spriel 
37666ea51fc7SArend Van Spriel 	for (i = 0; i < req->n_channels; i++) {
37676ea51fc7SArend Van Spriel 		if (req->channels[i] == chan)
37686ea51fc7SArend Van Spriel 			break;
37696ea51fc7SArend Van Spriel 	}
37704571767dSKees Cook 	if (i == req->n_channels) {
37714571767dSKees Cook 		req->n_channels++;
37724571767dSKees Cook 		req->channels[i] = chan;
37734571767dSKees Cook 	}
37746ea51fc7SArend Van Spriel 
37756ea51fc7SArend Van Spriel 	for (i = 0; i < req->n_ssids; i++) {
37766ea51fc7SArend Van Spriel 		if (req->ssids[i].ssid_len == ssid_len &&
37776ea51fc7SArend Van Spriel 		    !memcmp(req->ssids[i].ssid, ssid, ssid_len))
37786ea51fc7SArend Van Spriel 			break;
37796ea51fc7SArend Van Spriel 	}
37806ea51fc7SArend Van Spriel 	if (i == req->n_ssids) {
3781fa85b30aSArend Van Spriel 		memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
3782fa85b30aSArend Van Spriel 		req->ssids[req->n_ssids++].ssid_len = ssid_len;
37836ea51fc7SArend Van Spriel 	}
3784fa85b30aSArend Van Spriel 	return 0;
3785fa85b30aSArend Van Spriel }
3786fa85b30aSArend Van Spriel 
brcmf_start_internal_escan(struct brcmf_if * ifp,u32 fwmap,struct cfg80211_scan_request * request)3787efc2c1faSArend Van Spriel static int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap,
3788fa85b30aSArend Van Spriel 				      struct cfg80211_scan_request *request)
3789fa85b30aSArend Van Spriel {
3790fa85b30aSArend Van Spriel 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3791fa85b30aSArend Van Spriel 	int err;
3792fa85b30aSArend Van Spriel 
3793fa85b30aSArend Van Spriel 	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3794efc2c1faSArend Van Spriel 		if (cfg->int_escan_map)
3795efc2c1faSArend Van Spriel 			brcmf_dbg(SCAN, "aborting internal scan: map=%u\n",
3796efc2c1faSArend Van Spriel 				  cfg->int_escan_map);
3797fa85b30aSArend Van Spriel 		/* Abort any on-going scan */
3798fa85b30aSArend Van Spriel 		brcmf_abort_scanning(cfg);
3799fa85b30aSArend Van Spriel 	}
3800fa85b30aSArend Van Spriel 
3801efc2c1faSArend Van Spriel 	brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap);
3802fa85b30aSArend Van Spriel 	set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3803fa85b30aSArend Van Spriel 	cfg->escan_info.run = brcmf_run_escan;
3804fa85b30aSArend Van Spriel 	err = brcmf_do_escan(ifp, request);
3805fa85b30aSArend Van Spriel 	if (err) {
3806fa85b30aSArend Van Spriel 		clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3807fa85b30aSArend Van Spriel 		return err;
3808fa85b30aSArend Van Spriel 	}
3809efc2c1faSArend Van Spriel 	cfg->int_escan_map = fwmap;
3810fa85b30aSArend Van Spriel 	return 0;
3811fa85b30aSArend Van Spriel }
3812fa85b30aSArend Van Spriel 
381353e3a80dSArend Van Spriel static struct brcmf_pno_net_info_le *
brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le * pfn_v1)381453e3a80dSArend Van Spriel brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)
381553e3a80dSArend Van Spriel {
381653e3a80dSArend Van Spriel 	struct brcmf_pno_scanresults_v2_le *pfn_v2;
381753e3a80dSArend Van Spriel 	struct brcmf_pno_net_info_le *netinfo;
381853e3a80dSArend Van Spriel 
381953e3a80dSArend Van Spriel 	switch (pfn_v1->version) {
382053e3a80dSArend Van Spriel 	default:
382153e3a80dSArend Van Spriel 		WARN_ON(1);
38220bde10dcSGustavo A. R. Silva 		fallthrough;
382353e3a80dSArend Van Spriel 	case cpu_to_le32(1):
382453e3a80dSArend Van Spriel 		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);
382553e3a80dSArend Van Spriel 		break;
382653e3a80dSArend Van Spriel 	case cpu_to_le32(2):
382753e3a80dSArend Van Spriel 		pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;
382853e3a80dSArend Van Spriel 		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);
382953e3a80dSArend Van Spriel 		break;
383053e3a80dSArend Van Spriel 	}
383153e3a80dSArend Van Spriel 
383253e3a80dSArend Van Spriel 	return netinfo;
383353e3a80dSArend Van Spriel }
383453e3a80dSArend Van Spriel 
38355419f7f1SHante Meuleman /* PFN result doesn't have all the info which are required by the supplicant
38365419f7f1SHante Meuleman  * (For e.g IEs) Do a target Escan so that sched scan results are reported
38375419f7f1SHante Meuleman  * via wl_inform_single_bss in the required format. Escan does require the
38385419f7f1SHante Meuleman  * scan request in the form of cfg80211_scan_request. For timebeing, create
38395419f7f1SHante Meuleman  * cfg80211_scan_request one out of the received PNO event.
38405419f7f1SHante Meuleman  */
38415419f7f1SHante Meuleman static s32
brcmf_notify_sched_scan_results(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)38425419f7f1SHante Meuleman brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
38435419f7f1SHante Meuleman 				const struct brcmf_event_msg *e, void *data)
38445419f7f1SHante Meuleman {
384516e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
384616e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = drvr->config;
38475419f7f1SHante Meuleman 	struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
38485419f7f1SHante Meuleman 	struct cfg80211_scan_request *request = NULL;
38495419f7f1SHante Meuleman 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
3850fa85b30aSArend Van Spriel 	int i, err = 0;
38515419f7f1SHante Meuleman 	struct brcmf_pno_scanresults_le *pfn_result;
3852efc2c1faSArend Van Spriel 	u32 bucket_map;
38535419f7f1SHante Meuleman 	u32 result_count;
38545419f7f1SHante Meuleman 	u32 status;
38554835f37eSArend Van Spriel 	u32 datalen;
38565419f7f1SHante Meuleman 
38575419f7f1SHante Meuleman 	brcmf_dbg(SCAN, "Enter\n");
38585419f7f1SHante Meuleman 
38590aedbcafSHante Meuleman 	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
38600aedbcafSHante Meuleman 		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
38610aedbcafSHante Meuleman 		return 0;
38620aedbcafSHante Meuleman 	}
38630aedbcafSHante Meuleman 
38645419f7f1SHante Meuleman 	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
38655419f7f1SHante Meuleman 		brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
38665419f7f1SHante Meuleman 		return 0;
38675419f7f1SHante Meuleman 	}
38685419f7f1SHante Meuleman 
38695419f7f1SHante Meuleman 	pfn_result = (struct brcmf_pno_scanresults_le *)data;
38705419f7f1SHante Meuleman 	result_count = le32_to_cpu(pfn_result->count);
38715419f7f1SHante Meuleman 	status = le32_to_cpu(pfn_result->status);
38725419f7f1SHante Meuleman 
38735419f7f1SHante Meuleman 	/* PFN event is limited to fit 512 bytes so we may get
38745419f7f1SHante Meuleman 	 * multiple NET_FOUND events. For now place a warning here.
38755419f7f1SHante Meuleman 	 */
38765419f7f1SHante Meuleman 	WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
38775419f7f1SHante Meuleman 	brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
3878fa85b30aSArend Van Spriel 	if (!result_count) {
387916e64676SRafał Miłecki 		bphy_err(drvr, "FALSE PNO Event. (pfn_count == 0)\n");
3880fa85b30aSArend Van Spriel 		goto out_err;
3881fa85b30aSArend Van Spriel 	}
38824835f37eSArend Van Spriel 
38834835f37eSArend Van Spriel 	netinfo_start = brcmf_get_netinfo_array(pfn_result);
38844835f37eSArend Van Spriel 	datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
38854835f37eSArend Van Spriel 	if (datalen < result_count * sizeof(*netinfo)) {
388616e64676SRafał Miłecki 		bphy_err(drvr, "insufficient event data\n");
38874835f37eSArend Van Spriel 		goto out_err;
38884835f37eSArend Van Spriel 	}
38894835f37eSArend Van Spriel 
3890fa85b30aSArend Van Spriel 	request = brcmf_alloc_internal_escan_request(wiphy,
3891fa85b30aSArend Van Spriel 						     result_count);
3892fa85b30aSArend Van Spriel 	if (!request) {
38935419f7f1SHante Meuleman 		err = -ENOMEM;
38945419f7f1SHante Meuleman 		goto out_err;
38955419f7f1SHante Meuleman 	}
38965419f7f1SHante Meuleman 
3897efc2c1faSArend Van Spriel 	bucket_map = 0;
38985419f7f1SHante Meuleman 	for (i = 0; i < result_count; i++) {
38995419f7f1SHante Meuleman 		netinfo = &netinfo_start[i];
39005419f7f1SHante Meuleman 
39014835f37eSArend Van Spriel 		if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
39024835f37eSArend Van Spriel 			netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
3903fa85b30aSArend Van Spriel 		brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
39045419f7f1SHante Meuleman 			  netinfo->SSID, netinfo->channel);
3905efc2c1faSArend Van Spriel 		bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo);
3906fa85b30aSArend Van Spriel 		err = brcmf_internal_escan_add_info(request,
3907fa85b30aSArend Van Spriel 						    netinfo->SSID,
3908fa85b30aSArend Van Spriel 						    netinfo->SSID_len,
3909fa85b30aSArend Van Spriel 						    netinfo->channel);
3910fa85b30aSArend Van Spriel 		if (err)
39115419f7f1SHante Meuleman 			goto out_err;
39125419f7f1SHante Meuleman 	}
39135419f7f1SHante Meuleman 
3914efc2c1faSArend Van Spriel 	if (!bucket_map)
3915efc2c1faSArend Van Spriel 		goto free_req;
3916efc2c1faSArend Van Spriel 
3917efc2c1faSArend Van Spriel 	err = brcmf_start_internal_escan(ifp, bucket_map, request);
3918fa85b30aSArend Van Spriel 	if (!err)
3919fa85b30aSArend Van Spriel 		goto free_req;
39205419f7f1SHante Meuleman 
39215419f7f1SHante Meuleman out_err:
3922b34939b9SArend Van Spriel 	cfg80211_sched_scan_stopped(wiphy, 0);
3923fa85b30aSArend Van Spriel free_req:
3924fa85b30aSArend Van Spriel 	kfree(request);
39255419f7f1SHante Meuleman 	return err;
39265419f7f1SHante Meuleman }
39275419f7f1SHante Meuleman 
39285419f7f1SHante Meuleman static int
brcmf_cfg80211_sched_scan_start(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_sched_scan_request * req)39295419f7f1SHante Meuleman brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
39305419f7f1SHante Meuleman 				struct net_device *ndev,
39313e2e86abSArend Van Spriel 				struct cfg80211_sched_scan_request *req)
39325419f7f1SHante Meuleman {
3933856d5a01SArend Van Spriel 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
393416e64676SRafał Miłecki 	struct brcmf_if *ifp = netdev_priv(ndev);
393516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
39365419f7f1SHante Meuleman 
3937efc2c1faSArend Van Spriel 	brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n",
39383e2e86abSArend Van Spriel 		  req->n_match_sets, req->n_ssids);
3939dfe5b0d5SArend Van Spriel 
39405419f7f1SHante Meuleman 	if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
394116e64676SRafał Miłecki 		bphy_err(drvr, "Scanning suppressed: status=%lu\n",
39425419f7f1SHante Meuleman 			 cfg->scan_status);
39435419f7f1SHante Meuleman 		return -EAGAIN;
39445419f7f1SHante Meuleman 	}
39455419f7f1SHante Meuleman 
39463e2e86abSArend Van Spriel 	if (req->n_match_sets <= 0) {
39473e2e86abSArend Van Spriel 		brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n",
39483e2e86abSArend Van Spriel 			  req->n_match_sets);
39495419f7f1SHante Meuleman 		return -EINVAL;
39505419f7f1SHante Meuleman 	}
39515419f7f1SHante Meuleman 
39523e48611dSArend Van Spriel 	return brcmf_pno_start_sched_scan(ifp, req);
39535419f7f1SHante Meuleman }
39545419f7f1SHante Meuleman 
brcmf_cfg80211_sched_scan_stop(struct wiphy * wiphy,struct net_device * ndev,u64 reqid)39555419f7f1SHante Meuleman static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
39563a3ecf1dSArend Van Spriel 					  struct net_device *ndev, u64 reqid)
39575419f7f1SHante Meuleman {
39585419f7f1SHante Meuleman 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3959ac55136fSArend Van Spriel 	struct brcmf_if *ifp = netdev_priv(ndev);
39605419f7f1SHante Meuleman 
39615419f7f1SHante Meuleman 	brcmf_dbg(SCAN, "enter\n");
3962efc2c1faSArend Van Spriel 	brcmf_pno_stop_sched_scan(ifp, reqid);
3963efc2c1faSArend Van Spriel 	if (cfg->int_escan_map)
3964ac55136fSArend Van Spriel 		brcmf_notify_escan_complete(cfg, ifp, true, true);
39655419f7f1SHante Meuleman 	return 0;
39665419f7f1SHante Meuleman }
39675419f7f1SHante Meuleman 
brcmf_delay(u32 ms)396805491d2cSKalle Valo static __always_inline void brcmf_delay(u32 ms)
396905491d2cSKalle Valo {
397005491d2cSKalle Valo 	if (ms < 1000 / HZ) {
397105491d2cSKalle Valo 		cond_resched();
397205491d2cSKalle Valo 		mdelay(ms);
397305491d2cSKalle Valo 	} else {
397405491d2cSKalle Valo 		msleep(ms);
397505491d2cSKalle Valo 	}
397605491d2cSKalle Valo }
397705491d2cSKalle Valo 
brcmf_config_wowl_pattern(struct brcmf_if * ifp,u8 cmd[4],u8 * pattern,u32 patternsize,u8 * mask,u32 packet_offset)397805491d2cSKalle Valo static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
397905491d2cSKalle Valo 				     u8 *pattern, u32 patternsize, u8 *mask,
398005491d2cSKalle Valo 				     u32 packet_offset)
398105491d2cSKalle Valo {
398205491d2cSKalle Valo 	struct brcmf_fil_wowl_pattern_le *filter;
398305491d2cSKalle Valo 	u32 masksize;
398405491d2cSKalle Valo 	u32 patternoffset;
398505491d2cSKalle Valo 	u8 *buf;
398605491d2cSKalle Valo 	u32 bufsize;
398705491d2cSKalle Valo 	s32 ret;
398805491d2cSKalle Valo 
398905491d2cSKalle Valo 	masksize = (patternsize + 7) / 8;
399005491d2cSKalle Valo 	patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
399105491d2cSKalle Valo 
399205491d2cSKalle Valo 	bufsize = sizeof(*filter) + patternsize + masksize;
399305491d2cSKalle Valo 	buf = kzalloc(bufsize, GFP_KERNEL);
399405491d2cSKalle Valo 	if (!buf)
399505491d2cSKalle Valo 		return -ENOMEM;
399605491d2cSKalle Valo 	filter = (struct brcmf_fil_wowl_pattern_le *)buf;
399705491d2cSKalle Valo 
399805491d2cSKalle Valo 	memcpy(filter->cmd, cmd, 4);
399905491d2cSKalle Valo 	filter->masksize = cpu_to_le32(masksize);
400005491d2cSKalle Valo 	filter->offset = cpu_to_le32(packet_offset);
400105491d2cSKalle Valo 	filter->patternoffset = cpu_to_le32(patternoffset);
400205491d2cSKalle Valo 	filter->patternsize = cpu_to_le32(patternsize);
400305491d2cSKalle Valo 	filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
400405491d2cSKalle Valo 
400505491d2cSKalle Valo 	if ((mask) && (masksize))
400605491d2cSKalle Valo 		memcpy(buf + sizeof(*filter), mask, masksize);
400705491d2cSKalle Valo 	if ((pattern) && (patternsize))
400805491d2cSKalle Valo 		memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
400905491d2cSKalle Valo 
401005491d2cSKalle Valo 	ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
401105491d2cSKalle Valo 
401205491d2cSKalle Valo 	kfree(buf);
401305491d2cSKalle Valo 	return ret;
401405491d2cSKalle Valo }
401505491d2cSKalle Valo 
40163021ad9aSHante Meuleman static s32
brcmf_wowl_nd_results(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)40173021ad9aSHante Meuleman brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
40183021ad9aSHante Meuleman 		      void *data)
40193021ad9aSHante Meuleman {
402016e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
402116e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = drvr->config;
40223021ad9aSHante Meuleman 	struct brcmf_pno_scanresults_le *pfn_result;
40233021ad9aSHante Meuleman 	struct brcmf_pno_net_info_le *netinfo;
40243021ad9aSHante Meuleman 
40253021ad9aSHante Meuleman 	brcmf_dbg(SCAN, "Enter\n");
40263021ad9aSHante Meuleman 
40270aedbcafSHante Meuleman 	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
40280aedbcafSHante Meuleman 		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
40290aedbcafSHante Meuleman 		return 0;
40300aedbcafSHante Meuleman 	}
40310aedbcafSHante Meuleman 
40323021ad9aSHante Meuleman 	pfn_result = (struct brcmf_pno_scanresults_le *)data;
40333021ad9aSHante Meuleman 
40343021ad9aSHante Meuleman 	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
40353021ad9aSHante Meuleman 		brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
40363021ad9aSHante Meuleman 		return 0;
40373021ad9aSHante Meuleman 	}
40383021ad9aSHante Meuleman 
40393021ad9aSHante Meuleman 	if (le32_to_cpu(pfn_result->count) < 1) {
404016e64676SRafał Miłecki 		bphy_err(drvr, "Invalid result count, expected 1 (%d)\n",
40413021ad9aSHante Meuleman 			 le32_to_cpu(pfn_result->count));
40423021ad9aSHante Meuleman 		return -EINVAL;
40433021ad9aSHante Meuleman 	}
40443021ad9aSHante Meuleman 
4045d29afe91SArend Van Spriel 	netinfo = brcmf_get_netinfo_array(pfn_result);
40461b5e2423SArend van Spriel 	if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
40471b5e2423SArend van Spriel 		netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
40483021ad9aSHante Meuleman 	memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
40493021ad9aSHante Meuleman 	cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
40503021ad9aSHante Meuleman 	cfg->wowl.nd->n_channels = 1;
40513021ad9aSHante Meuleman 	cfg->wowl.nd->channels[0] =
40523021ad9aSHante Meuleman 		ieee80211_channel_to_frequency(netinfo->channel,
40533021ad9aSHante Meuleman 			netinfo->channel <= CH_MAX_2G_CHANNEL ?
40543021ad9aSHante Meuleman 					NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
40553021ad9aSHante Meuleman 	cfg->wowl.nd_info->n_matches = 1;
40563021ad9aSHante Meuleman 	cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
40573021ad9aSHante Meuleman 
40583021ad9aSHante Meuleman 	/* Inform (the resume task) that the net detect information was recvd */
40593021ad9aSHante Meuleman 	cfg->wowl.nd_data_completed = true;
40603021ad9aSHante Meuleman 	wake_up(&cfg->wowl.nd_data_wait);
40613021ad9aSHante Meuleman 
40623021ad9aSHante Meuleman 	return 0;
40633021ad9aSHante Meuleman }
40643021ad9aSHante Meuleman 
4065aeb64225SHante Meuleman #ifdef CONFIG_PM
4066aeb64225SHante Meuleman 
brcmf_report_wowl_wakeind(struct wiphy * wiphy,struct brcmf_if * ifp)4067aeb64225SHante Meuleman static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
4068aeb64225SHante Meuleman {
40693021ad9aSHante Meuleman 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
407016e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
4071aeb64225SHante Meuleman 	struct brcmf_wowl_wakeind_le wake_ind_le;
4072aeb64225SHante Meuleman 	struct cfg80211_wowlan_wakeup wakeup_data;
4073aeb64225SHante Meuleman 	struct cfg80211_wowlan_wakeup *wakeup;
4074aeb64225SHante Meuleman 	u32 wakeind;
4075aeb64225SHante Meuleman 	s32 err;
40763021ad9aSHante Meuleman 	int timeout;
4077aeb64225SHante Meuleman 
4078aeb64225SHante Meuleman 	err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
4079aeb64225SHante Meuleman 				       sizeof(wake_ind_le));
40803021ad9aSHante Meuleman 	if (err) {
408116e64676SRafał Miłecki 		bphy_err(drvr, "Get wowl_wakeind failed, err = %d\n", err);
4082aeb64225SHante Meuleman 		return;
4083aeb64225SHante Meuleman 	}
4084aeb64225SHante Meuleman 
4085aeb64225SHante Meuleman 	wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
4086aeb64225SHante Meuleman 	if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
40873021ad9aSHante Meuleman 		       BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
40883021ad9aSHante Meuleman 		       BRCMF_WOWL_PFN_FOUND)) {
4089aeb64225SHante Meuleman 		wakeup = &wakeup_data;
4090aeb64225SHante Meuleman 		memset(&wakeup_data, 0, sizeof(wakeup_data));
4091aeb64225SHante Meuleman 		wakeup_data.pattern_idx = -1;
4092aeb64225SHante Meuleman 
4093aeb64225SHante Meuleman 		if (wakeind & BRCMF_WOWL_MAGIC) {
4094aeb64225SHante Meuleman 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
4095aeb64225SHante Meuleman 			wakeup_data.magic_pkt = true;
4096aeb64225SHante Meuleman 		}
4097aeb64225SHante Meuleman 		if (wakeind & BRCMF_WOWL_DIS) {
4098aeb64225SHante Meuleman 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
4099aeb64225SHante Meuleman 			wakeup_data.disconnect = true;
4100aeb64225SHante Meuleman 		}
4101aeb64225SHante Meuleman 		if (wakeind & BRCMF_WOWL_BCN) {
4102aeb64225SHante Meuleman 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
4103aeb64225SHante Meuleman 			wakeup_data.disconnect = true;
4104aeb64225SHante Meuleman 		}
4105aeb64225SHante Meuleman 		if (wakeind & BRCMF_WOWL_RETR) {
4106aeb64225SHante Meuleman 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
4107aeb64225SHante Meuleman 			wakeup_data.disconnect = true;
4108aeb64225SHante Meuleman 		}
4109aeb64225SHante Meuleman 		if (wakeind & BRCMF_WOWL_NET) {
4110aeb64225SHante Meuleman 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
4111aeb64225SHante Meuleman 			/* For now always map to pattern 0, no API to get
4112aeb64225SHante Meuleman 			 * correct information available at the moment.
4113aeb64225SHante Meuleman 			 */
4114aeb64225SHante Meuleman 			wakeup_data.pattern_idx = 0;
4115aeb64225SHante Meuleman 		}
41163021ad9aSHante Meuleman 		if (wakeind & BRCMF_WOWL_PFN_FOUND) {
41173021ad9aSHante Meuleman 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
41183021ad9aSHante Meuleman 			timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
41193021ad9aSHante Meuleman 				cfg->wowl.nd_data_completed,
41203021ad9aSHante Meuleman 				BRCMF_ND_INFO_TIMEOUT);
41213021ad9aSHante Meuleman 			if (!timeout)
412216e64676SRafał Miłecki 				bphy_err(drvr, "No result for wowl net detect\n");
41233021ad9aSHante Meuleman 			else
41243021ad9aSHante Meuleman 				wakeup_data.net_detect = cfg->wowl.nd_info;
41253021ad9aSHante Meuleman 		}
41265c22fb85SHante Meuleman 		if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
41275c22fb85SHante Meuleman 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
41285c22fb85SHante Meuleman 			wakeup_data.gtk_rekey_failure = true;
41295c22fb85SHante Meuleman 		}
4130aeb64225SHante Meuleman 	} else {
4131aeb64225SHante Meuleman 		wakeup = NULL;
4132aeb64225SHante Meuleman 	}
4133aeb64225SHante Meuleman 	cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
4134aeb64225SHante Meuleman }
4135aeb64225SHante Meuleman 
4136aeb64225SHante Meuleman #else
4137aeb64225SHante Meuleman 
brcmf_report_wowl_wakeind(struct wiphy * wiphy,struct brcmf_if * ifp)4138aeb64225SHante Meuleman static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
4139aeb64225SHante Meuleman {
4140aeb64225SHante Meuleman }
4141aeb64225SHante Meuleman 
4142aeb64225SHante Meuleman #endif /* CONFIG_PM */
4143aeb64225SHante Meuleman 
brcmf_cfg80211_resume(struct wiphy * wiphy)414405491d2cSKalle Valo static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
414505491d2cSKalle Valo {
414605491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
414705491d2cSKalle Valo 	struct net_device *ndev = cfg_to_ndev(cfg);
414805491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
414905491d2cSKalle Valo 
415005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
415105491d2cSKalle Valo 
41523021ad9aSHante Meuleman 	if (cfg->wowl.active) {
4153aeb64225SHante Meuleman 		brcmf_report_wowl_wakeind(wiphy, ifp);
4154aeb64225SHante Meuleman 		brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
4155aeb64225SHante Meuleman 		brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
415673ef9e64SHante Meuleman 		if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
415752f22fb2SFranky Lin 			brcmf_configure_arp_nd_offload(ifp, true);
415805491d2cSKalle Valo 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
41593021ad9aSHante Meuleman 				      cfg->wowl.pre_pmmode);
41603021ad9aSHante Meuleman 		cfg->wowl.active = false;
41613021ad9aSHante Meuleman 		if (cfg->wowl.nd_enabled) {
41623a3ecf1dSArend Van Spriel 			brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0);
41633021ad9aSHante Meuleman 			brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
41643021ad9aSHante Meuleman 			brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
41653021ad9aSHante Meuleman 					    brcmf_notify_sched_scan_results);
41663021ad9aSHante Meuleman 			cfg->wowl.nd_enabled = false;
41673021ad9aSHante Meuleman 		}
416805491d2cSKalle Valo 	}
416905491d2cSKalle Valo 	return 0;
417005491d2cSKalle Valo }
417105491d2cSKalle Valo 
brcmf_configure_wowl(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,struct cfg80211_wowlan * wowl)417205491d2cSKalle Valo static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
417305491d2cSKalle Valo 				 struct brcmf_if *ifp,
417405491d2cSKalle Valo 				 struct cfg80211_wowlan *wowl)
417505491d2cSKalle Valo {
417605491d2cSKalle Valo 	u32 wowl_config;
4177a7ed7828SHante Meuleman 	struct brcmf_wowl_wakeind_le wowl_wakeind;
417805491d2cSKalle Valo 	u32 i;
417905491d2cSKalle Valo 
418005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Suspend, wowl config.\n");
418105491d2cSKalle Valo 
418273ef9e64SHante Meuleman 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
418352f22fb2SFranky Lin 		brcmf_configure_arp_nd_offload(ifp, false);
41843021ad9aSHante Meuleman 	brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
418505491d2cSKalle Valo 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
418605491d2cSKalle Valo 
418705491d2cSKalle Valo 	wowl_config = 0;
418805491d2cSKalle Valo 	if (wowl->disconnect)
418905491d2cSKalle Valo 		wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
419005491d2cSKalle Valo 	if (wowl->magic_pkt)
419105491d2cSKalle Valo 		wowl_config |= BRCMF_WOWL_MAGIC;
419205491d2cSKalle Valo 	if ((wowl->patterns) && (wowl->n_patterns)) {
419305491d2cSKalle Valo 		wowl_config |= BRCMF_WOWL_NET;
419405491d2cSKalle Valo 		for (i = 0; i < wowl->n_patterns; i++) {
419505491d2cSKalle Valo 			brcmf_config_wowl_pattern(ifp, "add",
419605491d2cSKalle Valo 				(u8 *)wowl->patterns[i].pattern,
419705491d2cSKalle Valo 				wowl->patterns[i].pattern_len,
419805491d2cSKalle Valo 				(u8 *)wowl->patterns[i].mask,
419905491d2cSKalle Valo 				wowl->patterns[i].pkt_offset);
420005491d2cSKalle Valo 		}
420105491d2cSKalle Valo 	}
42023021ad9aSHante Meuleman 	if (wowl->nd_config) {
42033021ad9aSHante Meuleman 		brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
42043021ad9aSHante Meuleman 						wowl->nd_config);
42053021ad9aSHante Meuleman 		wowl_config |= BRCMF_WOWL_PFN_FOUND;
42063021ad9aSHante Meuleman 
42073021ad9aSHante Meuleman 		cfg->wowl.nd_data_completed = false;
42083021ad9aSHante Meuleman 		cfg->wowl.nd_enabled = true;
42093021ad9aSHante Meuleman 		/* Now reroute the event for PFN to the wowl function. */
42103021ad9aSHante Meuleman 		brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
42113021ad9aSHante Meuleman 		brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
42123021ad9aSHante Meuleman 				    brcmf_wowl_nd_results);
42133021ad9aSHante Meuleman 	}
42145c22fb85SHante Meuleman 	if (wowl->gtk_rekey_failure)
42155c22fb85SHante Meuleman 		wowl_config |= BRCMF_WOWL_GTK_FAILURE;
42163021ad9aSHante Meuleman 	if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
42173021ad9aSHante Meuleman 		wowl_config |= BRCMF_WOWL_UNASSOC;
42183021ad9aSHante Meuleman 
4219a7ed7828SHante Meuleman 	memcpy(&wowl_wakeind, "clear", 6);
4220a7ed7828SHante Meuleman 	brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind,
4221a7ed7828SHante Meuleman 				 sizeof(wowl_wakeind));
422205491d2cSKalle Valo 	brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
422305491d2cSKalle Valo 	brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
422405491d2cSKalle Valo 	brcmf_bus_wowl_config(cfg->pub->bus_if, true);
42253021ad9aSHante Meuleman 	cfg->wowl.active = true;
422605491d2cSKalle Valo }
422705491d2cSKalle Valo 
brcmf_keepalive_start(struct brcmf_if * ifp,unsigned int interval)42287a6cfe28SLoic Poulain static int brcmf_keepalive_start(struct brcmf_if *ifp, unsigned int interval)
42297a6cfe28SLoic Poulain {
42307a6cfe28SLoic Poulain 	struct brcmf_mkeep_alive_pkt_le kalive = {0};
42317a6cfe28SLoic Poulain 	int ret = 0;
42327a6cfe28SLoic Poulain 
42337a6cfe28SLoic Poulain 	/* Configure Null function/data keepalive */
42347a6cfe28SLoic Poulain 	kalive.version = cpu_to_le16(1);
42353db30b79SLoic Poulain 	kalive.period_msec = cpu_to_le32(interval * MSEC_PER_SEC);
42367a6cfe28SLoic Poulain 	kalive.len_bytes = cpu_to_le16(0);
42373db30b79SLoic Poulain 	kalive.keep_alive_id = 0;
42387a6cfe28SLoic Poulain 
42397a6cfe28SLoic Poulain 	ret = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &kalive, sizeof(kalive));
42407a6cfe28SLoic Poulain 	if (ret)
42417a6cfe28SLoic Poulain 		brcmf_err("keep-alive packet config failed, ret=%d\n", ret);
42427a6cfe28SLoic Poulain 
42437a6cfe28SLoic Poulain 	return ret;
42447a6cfe28SLoic Poulain }
42457a6cfe28SLoic Poulain 
brcmf_cfg80211_suspend(struct wiphy * wiphy,struct cfg80211_wowlan * wowl)424605491d2cSKalle Valo static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
424705491d2cSKalle Valo 				  struct cfg80211_wowlan *wowl)
424805491d2cSKalle Valo {
424905491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
425005491d2cSKalle Valo 	struct net_device *ndev = cfg_to_ndev(cfg);
425105491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
425205491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
425305491d2cSKalle Valo 
425405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
425505491d2cSKalle Valo 
425605491d2cSKalle Valo 	/* if the primary net_device is not READY there is nothing
425705491d2cSKalle Valo 	 * we can do but pray resume goes smoothly.
425805491d2cSKalle Valo 	 */
425905491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
426005491d2cSKalle Valo 		goto exit;
426105491d2cSKalle Valo 
42623021ad9aSHante Meuleman 	/* Stop scheduled scan */
42633021ad9aSHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
42643a3ecf1dSArend Van Spriel 		brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0);
42653021ad9aSHante Meuleman 
426605491d2cSKalle Valo 	/* end any scanning */
426705491d2cSKalle Valo 	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
426805491d2cSKalle Valo 		brcmf_abort_scanning(cfg);
426905491d2cSKalle Valo 
427005491d2cSKalle Valo 	if (wowl == NULL) {
427105491d2cSKalle Valo 		brcmf_bus_wowl_config(cfg->pub->bus_if, false);
427205491d2cSKalle Valo 		list_for_each_entry(vif, &cfg->vif_list, list) {
427305491d2cSKalle Valo 			if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
427405491d2cSKalle Valo 				continue;
427505491d2cSKalle Valo 			/* While going to suspend if associated with AP
427605491d2cSKalle Valo 			 * disassociate from AP to save power while system is
427705491d2cSKalle Valo 			 * in suspended state
427805491d2cSKalle Valo 			 */
42791b050d97SSoontak Lee 			brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED, true);
428005491d2cSKalle Valo 			/* Make sure WPA_Supplicant receives all the event
428105491d2cSKalle Valo 			 * generated due to DISASSOC call to the fw to keep
428205491d2cSKalle Valo 			 * the state fw and WPA_Supplicant state consistent
428305491d2cSKalle Valo 			 */
428405491d2cSKalle Valo 			brcmf_delay(500);
428505491d2cSKalle Valo 		}
428605491d2cSKalle Valo 		/* Configure MPC */
428705491d2cSKalle Valo 		brcmf_set_mpc(ifp, 1);
428805491d2cSKalle Valo 
428905491d2cSKalle Valo 	} else {
429005491d2cSKalle Valo 		/* Configure WOWL paramaters */
429105491d2cSKalle Valo 		brcmf_configure_wowl(cfg, ifp, wowl);
42927a6cfe28SLoic Poulain 
42937a6cfe28SLoic Poulain 		/* Prevent disassociation due to inactivity with keep-alive */
42947a6cfe28SLoic Poulain 		brcmf_keepalive_start(ifp, 30);
429505491d2cSKalle Valo 	}
429605491d2cSKalle Valo 
429705491d2cSKalle Valo exit:
429805491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
429905491d2cSKalle Valo 	/* clear any scanning activity */
430005491d2cSKalle Valo 	cfg->scan_status = 0;
430105491d2cSKalle Valo 	return 0;
430205491d2cSKalle Valo }
430305491d2cSKalle Valo 
4304a96202acSHector Martin static s32
brcmf_pmksa_v3_op(struct brcmf_if * ifp,struct cfg80211_pmksa * pmksa,bool alive)4305a96202acSHector Martin brcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa,
4306a96202acSHector Martin 		  bool alive)
4307a96202acSHector Martin {
4308a96202acSHector Martin 	struct brcmf_pmk_op_v3_le *pmk_op;
4309a96202acSHector Martin 	int length = offsetof(struct brcmf_pmk_op_v3_le, pmk);
4310a96202acSHector Martin 	int ret;
4311a96202acSHector Martin 
4312a96202acSHector Martin 	pmk_op = kzalloc(sizeof(*pmk_op), GFP_KERNEL);
4313df62e22cSDuoming Zhou 	if (!pmk_op)
4314df62e22cSDuoming Zhou 		return -ENOMEM;
4315df62e22cSDuoming Zhou 
4316a96202acSHector Martin 	pmk_op->version = cpu_to_le16(BRCMF_PMKSA_VER_3);
4317a96202acSHector Martin 
4318a96202acSHector Martin 	if (!pmksa) {
4319a96202acSHector Martin 		/* Flush operation, operate on entire list */
4320a96202acSHector Martin 		pmk_op->count = cpu_to_le16(0);
4321a96202acSHector Martin 	} else {
4322a96202acSHector Martin 		/* Single PMK operation */
4323a96202acSHector Martin 		pmk_op->count = cpu_to_le16(1);
4324a96202acSHector Martin 		length += sizeof(struct brcmf_pmksa_v3);
43254291f94fSJanne Grunau 		if (pmksa->bssid)
4326a96202acSHector Martin 			memcpy(pmk_op->pmk[0].bssid, pmksa->bssid, ETH_ALEN);
43274291f94fSJanne Grunau 		if (pmksa->pmkid) {
4328a96202acSHector Martin 			memcpy(pmk_op->pmk[0].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
4329a96202acSHector Martin 			pmk_op->pmk[0].pmkid_len = WLAN_PMKID_LEN;
43304291f94fSJanne Grunau 		}
43314291f94fSJanne Grunau 		if (pmksa->ssid && pmksa->ssid_len) {
43324291f94fSJanne Grunau 			memcpy(pmk_op->pmk[0].ssid.SSID, pmksa->ssid, pmksa->ssid_len);
43334291f94fSJanne Grunau 			pmk_op->pmk[0].ssid.SSID_len = pmksa->ssid_len;
43344291f94fSJanne Grunau 		}
4335a96202acSHector Martin 		pmk_op->pmk[0].time_left = cpu_to_le32(alive ? BRCMF_PMKSA_NO_EXPIRY : 0);
4336a96202acSHector Martin 	}
4337a96202acSHector Martin 
4338a96202acSHector Martin 	pmk_op->length = cpu_to_le16(length);
4339a96202acSHector Martin 
4340a96202acSHector Martin 	ret = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_op, sizeof(*pmk_op));
4341a96202acSHector Martin 	kfree(pmk_op);
4342a96202acSHector Martin 	return ret;
4343a96202acSHector Martin }
4344a96202acSHector Martin 
434505491d2cSKalle Valo static __used s32
brcmf_update_pmklist(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)43466c404f34SHante Meuleman brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
434705491d2cSKalle Valo {
43486c404f34SHante Meuleman 	struct brcmf_pmk_list_le *pmk_list;
43496c404f34SHante Meuleman 	int i;
43506c404f34SHante Meuleman 	u32 npmk;
435105491d2cSKalle Valo 
43526c404f34SHante Meuleman 	pmk_list = &cfg->pmk_list;
43536c404f34SHante Meuleman 	npmk = le32_to_cpu(pmk_list->npmk);
435405491d2cSKalle Valo 
43556c404f34SHante Meuleman 	brcmf_dbg(CONN, "No of elements %d\n", npmk);
43566c404f34SHante Meuleman 	for (i = 0; i < npmk; i++)
43576c404f34SHante Meuleman 		brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
435805491d2cSKalle Valo 
4359e56a7708SJinpeng Cui 	return brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
43606c404f34SHante Meuleman 			sizeof(*pmk_list));
436105491d2cSKalle Valo }
436205491d2cSKalle Valo 
436305491d2cSKalle Valo static s32
brcmf_cfg80211_set_pmksa(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_pmksa * pmksa)436405491d2cSKalle Valo brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
436505491d2cSKalle Valo 			 struct cfg80211_pmksa *pmksa)
436605491d2cSKalle Valo {
436705491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
436805491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
43696c404f34SHante Meuleman 	struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
437016e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
43716c404f34SHante Meuleman 	s32 err;
43726c404f34SHante Meuleman 	u32 npmk, i;
437305491d2cSKalle Valo 
437405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
437505491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
437605491d2cSKalle Valo 		return -EIO;
437705491d2cSKalle Valo 
4378a96202acSHector Martin 	brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmksa->bssid);
4379a96202acSHector Martin 	brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmksa->pmkid);
4380a96202acSHector Martin 
4381a96202acSHector Martin 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4382a96202acSHector Martin 		return brcmf_pmksa_v3_op(ifp, pmksa, true);
4383a96202acSHector Martin 
4384a96202acSHector Martin 	/* TODO: implement PMKID_V2 */
4385a96202acSHector Martin 
43866c404f34SHante Meuleman 	npmk = le32_to_cpu(cfg->pmk_list.npmk);
43876c404f34SHante Meuleman 	for (i = 0; i < npmk; i++)
43886c404f34SHante Meuleman 		if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
438905491d2cSKalle Valo 			break;
43906c404f34SHante Meuleman 	if (i < BRCMF_MAXPMKID) {
43916c404f34SHante Meuleman 		memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
43926c404f34SHante Meuleman 		memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
43936c404f34SHante Meuleman 		if (i == npmk) {
43946c404f34SHante Meuleman 			npmk++;
43956c404f34SHante Meuleman 			cfg->pmk_list.npmk = cpu_to_le32(npmk);
439605491d2cSKalle Valo 		}
43976c404f34SHante Meuleman 	} else {
439816e64676SRafał Miłecki 		bphy_err(drvr, "Too many PMKSA entries cached %d\n", npmk);
43996c404f34SHante Meuleman 		return -EINVAL;
44006c404f34SHante Meuleman 	}
440105491d2cSKalle Valo 
44026c404f34SHante Meuleman 	err = brcmf_update_pmklist(cfg, ifp);
440305491d2cSKalle Valo 
440405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
440505491d2cSKalle Valo 	return err;
440605491d2cSKalle Valo }
440705491d2cSKalle Valo 
440805491d2cSKalle Valo static s32
brcmf_cfg80211_del_pmksa(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_pmksa * pmksa)440905491d2cSKalle Valo brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
441005491d2cSKalle Valo 			 struct cfg80211_pmksa *pmksa)
441105491d2cSKalle Valo {
441205491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
441305491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
44146c404f34SHante Meuleman 	struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
441516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
44166c404f34SHante Meuleman 	s32 err;
44176c404f34SHante Meuleman 	u32 npmk, i;
441805491d2cSKalle Valo 
441905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
442005491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
442105491d2cSKalle Valo 		return -EIO;
442205491d2cSKalle Valo 
44237703773eSNicolas Iooss 	brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid);
442405491d2cSKalle Valo 
4425a96202acSHector Martin 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4426a96202acSHector Martin 		return brcmf_pmksa_v3_op(ifp, pmksa, false);
4427a96202acSHector Martin 
4428a96202acSHector Martin 	/* TODO: implement PMKID_V2 */
4429a96202acSHector Martin 
44306c404f34SHante Meuleman 	npmk = le32_to_cpu(cfg->pmk_list.npmk);
44316c404f34SHante Meuleman 	for (i = 0; i < npmk; i++)
44327703773eSNicolas Iooss 		if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
443305491d2cSKalle Valo 			break;
443405491d2cSKalle Valo 
44356c404f34SHante Meuleman 	if ((npmk > 0) && (i < npmk)) {
44366c404f34SHante Meuleman 		for (; i < (npmk - 1); i++) {
44376c404f34SHante Meuleman 			memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
44386c404f34SHante Meuleman 			memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
443905491d2cSKalle Valo 			       WLAN_PMKID_LEN);
444005491d2cSKalle Valo 		}
44416c404f34SHante Meuleman 		memset(&pmk[i], 0, sizeof(*pmk));
44426c404f34SHante Meuleman 		cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
44436c404f34SHante Meuleman 	} else {
444416e64676SRafał Miłecki 		bphy_err(drvr, "Cache entry not found\n");
44456c404f34SHante Meuleman 		return -EINVAL;
44466c404f34SHante Meuleman 	}
444705491d2cSKalle Valo 
44486c404f34SHante Meuleman 	err = brcmf_update_pmklist(cfg, ifp);
444905491d2cSKalle Valo 
445005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
445105491d2cSKalle Valo 	return err;
445205491d2cSKalle Valo 
445305491d2cSKalle Valo }
445405491d2cSKalle Valo 
445505491d2cSKalle Valo static s32
brcmf_cfg80211_flush_pmksa(struct wiphy * wiphy,struct net_device * ndev)445605491d2cSKalle Valo brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
445705491d2cSKalle Valo {
445805491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
445905491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
44606c404f34SHante Meuleman 	s32 err;
446105491d2cSKalle Valo 
446205491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
446305491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
446405491d2cSKalle Valo 		return -EIO;
446505491d2cSKalle Valo 
4466a96202acSHector Martin 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4467a96202acSHector Martin 		return brcmf_pmksa_v3_op(ifp, NULL, false);
4468a96202acSHector Martin 
4469a96202acSHector Martin 	/* TODO: implement PMKID_V2 */
4470a96202acSHector Martin 
44716c404f34SHante Meuleman 	memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
44726c404f34SHante Meuleman 	err = brcmf_update_pmklist(cfg, ifp);
447305491d2cSKalle Valo 
447405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
447505491d2cSKalle Valo 	return err;
447605491d2cSKalle Valo 
447705491d2cSKalle Valo }
447805491d2cSKalle Valo 
brcmf_configure_opensecurity(struct brcmf_if * ifp)447905491d2cSKalle Valo static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
448005491d2cSKalle Valo {
448116e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
448205491d2cSKalle Valo 	s32 err;
4483fdfb0f94SWright Feng 	s32 wpa_val;
448405491d2cSKalle Valo 
448505491d2cSKalle Valo 	/* set auth */
448605491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
448705491d2cSKalle Valo 	if (err < 0) {
448816e64676SRafał Miłecki 		bphy_err(drvr, "auth error %d\n", err);
448905491d2cSKalle Valo 		return err;
449005491d2cSKalle Valo 	}
449105491d2cSKalle Valo 	/* set wsec */
449205491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
449305491d2cSKalle Valo 	if (err < 0) {
449416e64676SRafał Miłecki 		bphy_err(drvr, "wsec error %d\n", err);
449505491d2cSKalle Valo 		return err;
449605491d2cSKalle Valo 	}
449705491d2cSKalle Valo 	/* set upper-layer auth */
4498fdfb0f94SWright Feng 	if (brcmf_is_ibssmode(ifp->vif))
4499fdfb0f94SWright Feng 		wpa_val = WPA_AUTH_NONE;
4500fdfb0f94SWright Feng 	else
4501fdfb0f94SWright Feng 		wpa_val = WPA_AUTH_DISABLED;
4502fdfb0f94SWright Feng 	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val);
450305491d2cSKalle Valo 	if (err < 0) {
450416e64676SRafał Miłecki 		bphy_err(drvr, "wpa_auth error %d\n", err);
450505491d2cSKalle Valo 		return err;
450605491d2cSKalle Valo 	}
450705491d2cSKalle Valo 
450805491d2cSKalle Valo 	return 0;
450905491d2cSKalle Valo }
451005491d2cSKalle Valo 
brcmf_valid_wpa_oui(u8 * oui,bool is_rsn_ie)451105491d2cSKalle Valo static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
451205491d2cSKalle Valo {
451305491d2cSKalle Valo 	if (is_rsn_ie)
451405491d2cSKalle Valo 		return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
451505491d2cSKalle Valo 
451605491d2cSKalle Valo 	return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
451705491d2cSKalle Valo }
451805491d2cSKalle Valo 
451905491d2cSKalle Valo static s32
brcmf_configure_wpaie(struct brcmf_if * ifp,const struct brcmf_vs_tlv * wpa_ie,bool is_rsn_ie)452005491d2cSKalle Valo brcmf_configure_wpaie(struct brcmf_if *ifp,
452105491d2cSKalle Valo 		      const struct brcmf_vs_tlv *wpa_ie,
452205491d2cSKalle Valo 		      bool is_rsn_ie)
452305491d2cSKalle Valo {
452416e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
452505491d2cSKalle Valo 	u32 auth = 0; /* d11 open authentication */
452605491d2cSKalle Valo 	u16 count;
452705491d2cSKalle Valo 	s32 err = 0;
4528240d61a9SHante Meuleman 	s32 len;
452905491d2cSKalle Valo 	u32 i;
453005491d2cSKalle Valo 	u32 wsec;
453105491d2cSKalle Valo 	u32 pval = 0;
453205491d2cSKalle Valo 	u32 gval = 0;
453305491d2cSKalle Valo 	u32 wpa_auth = 0;
453405491d2cSKalle Valo 	u32 offset;
453505491d2cSKalle Valo 	u8 *data;
453605491d2cSKalle Valo 	u16 rsn_cap;
453705491d2cSKalle Valo 	u32 wme_bss_disable;
4538240d61a9SHante Meuleman 	u32 mfp;
453905491d2cSKalle Valo 
454005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
454105491d2cSKalle Valo 	if (wpa_ie == NULL)
454205491d2cSKalle Valo 		goto exit;
454305491d2cSKalle Valo 
454405491d2cSKalle Valo 	len = wpa_ie->len + TLV_HDR_LEN;
454505491d2cSKalle Valo 	data = (u8 *)wpa_ie;
454605491d2cSKalle Valo 	offset = TLV_HDR_LEN;
454705491d2cSKalle Valo 	if (!is_rsn_ie)
454805491d2cSKalle Valo 		offset += VS_IE_FIXED_HDR_LEN;
454905491d2cSKalle Valo 	else
455005491d2cSKalle Valo 		offset += WPA_IE_VERSION_LEN;
455105491d2cSKalle Valo 
455205491d2cSKalle Valo 	/* check for multicast cipher suite */
455305491d2cSKalle Valo 	if (offset + WPA_IE_MIN_OUI_LEN > len) {
455405491d2cSKalle Valo 		err = -EINVAL;
455516e64676SRafał Miłecki 		bphy_err(drvr, "no multicast cipher suite\n");
455605491d2cSKalle Valo 		goto exit;
455705491d2cSKalle Valo 	}
455805491d2cSKalle Valo 
455905491d2cSKalle Valo 	if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
456005491d2cSKalle Valo 		err = -EINVAL;
456116e64676SRafał Miłecki 		bphy_err(drvr, "ivalid OUI\n");
456205491d2cSKalle Valo 		goto exit;
456305491d2cSKalle Valo 	}
456405491d2cSKalle Valo 	offset += TLV_OUI_LEN;
456505491d2cSKalle Valo 
456605491d2cSKalle Valo 	/* pick up multicast cipher */
456705491d2cSKalle Valo 	switch (data[offset]) {
456805491d2cSKalle Valo 	case WPA_CIPHER_NONE:
456905491d2cSKalle Valo 		gval = 0;
457005491d2cSKalle Valo 		break;
457105491d2cSKalle Valo 	case WPA_CIPHER_WEP_40:
457205491d2cSKalle Valo 	case WPA_CIPHER_WEP_104:
457305491d2cSKalle Valo 		gval = WEP_ENABLED;
457405491d2cSKalle Valo 		break;
457505491d2cSKalle Valo 	case WPA_CIPHER_TKIP:
457605491d2cSKalle Valo 		gval = TKIP_ENABLED;
457705491d2cSKalle Valo 		break;
457805491d2cSKalle Valo 	case WPA_CIPHER_AES_CCM:
457905491d2cSKalle Valo 		gval = AES_ENABLED;
458005491d2cSKalle Valo 		break;
458105491d2cSKalle Valo 	default:
458205491d2cSKalle Valo 		err = -EINVAL;
458316e64676SRafał Miłecki 		bphy_err(drvr, "Invalid multi cast cipher info\n");
458405491d2cSKalle Valo 		goto exit;
458505491d2cSKalle Valo 	}
458605491d2cSKalle Valo 
458705491d2cSKalle Valo 	offset++;
458805491d2cSKalle Valo 	/* walk thru unicast cipher list and pick up what we recognize */
458905491d2cSKalle Valo 	count = data[offset] + (data[offset + 1] << 8);
459005491d2cSKalle Valo 	offset += WPA_IE_SUITE_COUNT_LEN;
459105491d2cSKalle Valo 	/* Check for unicast suite(s) */
459205491d2cSKalle Valo 	if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
459305491d2cSKalle Valo 		err = -EINVAL;
459416e64676SRafał Miłecki 		bphy_err(drvr, "no unicast cipher suite\n");
459505491d2cSKalle Valo 		goto exit;
459605491d2cSKalle Valo 	}
459705491d2cSKalle Valo 	for (i = 0; i < count; i++) {
459805491d2cSKalle Valo 		if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
459905491d2cSKalle Valo 			err = -EINVAL;
460016e64676SRafał Miłecki 			bphy_err(drvr, "ivalid OUI\n");
460105491d2cSKalle Valo 			goto exit;
460205491d2cSKalle Valo 		}
460305491d2cSKalle Valo 		offset += TLV_OUI_LEN;
460405491d2cSKalle Valo 		switch (data[offset]) {
460505491d2cSKalle Valo 		case WPA_CIPHER_NONE:
460605491d2cSKalle Valo 			break;
460705491d2cSKalle Valo 		case WPA_CIPHER_WEP_40:
460805491d2cSKalle Valo 		case WPA_CIPHER_WEP_104:
460905491d2cSKalle Valo 			pval |= WEP_ENABLED;
461005491d2cSKalle Valo 			break;
461105491d2cSKalle Valo 		case WPA_CIPHER_TKIP:
461205491d2cSKalle Valo 			pval |= TKIP_ENABLED;
461305491d2cSKalle Valo 			break;
461405491d2cSKalle Valo 		case WPA_CIPHER_AES_CCM:
461505491d2cSKalle Valo 			pval |= AES_ENABLED;
461605491d2cSKalle Valo 			break;
461705491d2cSKalle Valo 		default:
461816e64676SRafał Miłecki 			bphy_err(drvr, "Invalid unicast security info\n");
461905491d2cSKalle Valo 		}
462005491d2cSKalle Valo 		offset++;
462105491d2cSKalle Valo 	}
462205491d2cSKalle Valo 	/* walk thru auth management suite list and pick up what we recognize */
462305491d2cSKalle Valo 	count = data[offset] + (data[offset + 1] << 8);
462405491d2cSKalle Valo 	offset += WPA_IE_SUITE_COUNT_LEN;
462505491d2cSKalle Valo 	/* Check for auth key management suite(s) */
462605491d2cSKalle Valo 	if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
462705491d2cSKalle Valo 		err = -EINVAL;
462816e64676SRafał Miłecki 		bphy_err(drvr, "no auth key mgmt suite\n");
462905491d2cSKalle Valo 		goto exit;
463005491d2cSKalle Valo 	}
463105491d2cSKalle Valo 	for (i = 0; i < count; i++) {
463205491d2cSKalle Valo 		if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
463305491d2cSKalle Valo 			err = -EINVAL;
463416e64676SRafał Miłecki 			bphy_err(drvr, "ivalid OUI\n");
463505491d2cSKalle Valo 			goto exit;
463605491d2cSKalle Valo 		}
463705491d2cSKalle Valo 		offset += TLV_OUI_LEN;
463805491d2cSKalle Valo 		switch (data[offset]) {
463905491d2cSKalle Valo 		case RSN_AKM_NONE:
464005491d2cSKalle Valo 			brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
464105491d2cSKalle Valo 			wpa_auth |= WPA_AUTH_NONE;
464205491d2cSKalle Valo 			break;
464305491d2cSKalle Valo 		case RSN_AKM_UNSPECIFIED:
464405491d2cSKalle Valo 			brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
464505491d2cSKalle Valo 			is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
464605491d2cSKalle Valo 				    (wpa_auth |= WPA_AUTH_UNSPECIFIED);
464705491d2cSKalle Valo 			break;
464805491d2cSKalle Valo 		case RSN_AKM_PSK:
464905491d2cSKalle Valo 			brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
465005491d2cSKalle Valo 			is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
465105491d2cSKalle Valo 				    (wpa_auth |= WPA_AUTH_PSK);
465205491d2cSKalle Valo 			break;
4653240d61a9SHante Meuleman 		case RSN_AKM_SHA256_PSK:
4654240d61a9SHante Meuleman 			brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
4655240d61a9SHante Meuleman 			wpa_auth |= WPA2_AUTH_PSK_SHA256;
4656240d61a9SHante Meuleman 			break;
4657240d61a9SHante Meuleman 		case RSN_AKM_SHA256_1X:
4658240d61a9SHante Meuleman 			brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
4659240d61a9SHante Meuleman 			wpa_auth |= WPA2_AUTH_1X_SHA256;
4660240d61a9SHante Meuleman 			break;
4661d5f59c96SChung-Hsien Hsu 		case RSN_AKM_SAE:
4662d5f59c96SChung-Hsien Hsu 			brcmf_dbg(TRACE, "RSN_AKM_SAE\n");
4663d5f59c96SChung-Hsien Hsu 			wpa_auth |= WPA3_AUTH_SAE_PSK;
4664d5f59c96SChung-Hsien Hsu 			break;
466505491d2cSKalle Valo 		default:
466616e64676SRafał Miłecki 			bphy_err(drvr, "Invalid key mgmt info\n");
466705491d2cSKalle Valo 		}
466805491d2cSKalle Valo 		offset++;
466905491d2cSKalle Valo 	}
467005491d2cSKalle Valo 
4671240d61a9SHante Meuleman 	mfp = BRCMF_MFP_NONE;
467205491d2cSKalle Valo 	if (is_rsn_ie) {
467305491d2cSKalle Valo 		wme_bss_disable = 1;
467405491d2cSKalle Valo 		if ((offset + RSN_CAP_LEN) <= len) {
467505491d2cSKalle Valo 			rsn_cap = data[offset] + (data[offset + 1] << 8);
467605491d2cSKalle Valo 			if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
467705491d2cSKalle Valo 				wme_bss_disable = 0;
4678240d61a9SHante Meuleman 			if (rsn_cap & RSN_CAP_MFPR_MASK) {
4679240d61a9SHante Meuleman 				brcmf_dbg(TRACE, "MFP Required\n");
4680240d61a9SHante Meuleman 				mfp = BRCMF_MFP_REQUIRED;
4681240d61a9SHante Meuleman 				/* Firmware only supports mfp required in
4682d5f59c96SChung-Hsien Hsu 				 * combination with WPA2_AUTH_PSK_SHA256,
4683d5f59c96SChung-Hsien Hsu 				 * WPA2_AUTH_1X_SHA256, or WPA3_AUTH_SAE_PSK.
4684240d61a9SHante Meuleman 				 */
4685240d61a9SHante Meuleman 				if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
4686d5f59c96SChung-Hsien Hsu 						  WPA2_AUTH_1X_SHA256 |
4687d5f59c96SChung-Hsien Hsu 						  WPA3_AUTH_SAE_PSK))) {
4688240d61a9SHante Meuleman 					err = -EINVAL;
4689240d61a9SHante Meuleman 					goto exit;
469005491d2cSKalle Valo 				}
4691240d61a9SHante Meuleman 				/* Firmware has requirement that WPA2_AUTH_PSK/
4692240d61a9SHante Meuleman 				 * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
4693240d61a9SHante Meuleman 				 * is to be included in the rsn ie.
4694240d61a9SHante Meuleman 				 */
4695240d61a9SHante Meuleman 				if (wpa_auth & WPA2_AUTH_PSK_SHA256)
4696240d61a9SHante Meuleman 					wpa_auth |= WPA2_AUTH_PSK;
4697240d61a9SHante Meuleman 				else if (wpa_auth & WPA2_AUTH_1X_SHA256)
4698240d61a9SHante Meuleman 					wpa_auth |= WPA2_AUTH_UNSPECIFIED;
4699240d61a9SHante Meuleman 			} else if (rsn_cap & RSN_CAP_MFPC_MASK) {
4700240d61a9SHante Meuleman 				brcmf_dbg(TRACE, "MFP Capable\n");
4701240d61a9SHante Meuleman 				mfp = BRCMF_MFP_CAPABLE;
4702240d61a9SHante Meuleman 			}
4703240d61a9SHante Meuleman 		}
4704240d61a9SHante Meuleman 		offset += RSN_CAP_LEN;
470505491d2cSKalle Valo 		/* set wme_bss_disable to sync RSN Capabilities */
470605491d2cSKalle Valo 		err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
470705491d2cSKalle Valo 					       wme_bss_disable);
470805491d2cSKalle Valo 		if (err < 0) {
470916e64676SRafał Miłecki 			bphy_err(drvr, "wme_bss_disable error %d\n", err);
471005491d2cSKalle Valo 			goto exit;
471105491d2cSKalle Valo 		}
4712240d61a9SHante Meuleman 
4713240d61a9SHante Meuleman 		/* Skip PMKID cnt as it is know to be 0 for AP. */
4714240d61a9SHante Meuleman 		offset += RSN_PMKID_COUNT_LEN;
4715240d61a9SHante Meuleman 
4716240d61a9SHante Meuleman 		/* See if there is BIP wpa suite left for MFP */
4717240d61a9SHante Meuleman 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
4718240d61a9SHante Meuleman 		    ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
4719240d61a9SHante Meuleman 			err = brcmf_fil_bsscfg_data_set(ifp, "bip",
4720240d61a9SHante Meuleman 							&data[offset],
4721240d61a9SHante Meuleman 							WPA_IE_MIN_OUI_LEN);
4722240d61a9SHante Meuleman 			if (err < 0) {
472316e64676SRafał Miłecki 				bphy_err(drvr, "bip error %d\n", err);
4724240d61a9SHante Meuleman 				goto exit;
4725240d61a9SHante Meuleman 			}
4726240d61a9SHante Meuleman 		}
472705491d2cSKalle Valo 	}
472805491d2cSKalle Valo 	/* FOR WPS , set SES_OW_ENABLED */
472905491d2cSKalle Valo 	wsec = (pval | gval | SES_OW_ENABLED);
473005491d2cSKalle Valo 
473105491d2cSKalle Valo 	/* set auth */
473205491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
473305491d2cSKalle Valo 	if (err < 0) {
473416e64676SRafał Miłecki 		bphy_err(drvr, "auth error %d\n", err);
473505491d2cSKalle Valo 		goto exit;
473605491d2cSKalle Valo 	}
473705491d2cSKalle Valo 	/* set wsec */
473805491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
473905491d2cSKalle Valo 	if (err < 0) {
474016e64676SRafał Miłecki 		bphy_err(drvr, "wsec error %d\n", err);
474105491d2cSKalle Valo 		goto exit;
474205491d2cSKalle Valo 	}
4743240d61a9SHante Meuleman 	/* Configure MFP, this needs to go after wsec otherwise the wsec command
4744240d61a9SHante Meuleman 	 * will overwrite the values set by MFP
4745240d61a9SHante Meuleman 	 */
4746240d61a9SHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
4747240d61a9SHante Meuleman 		err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
4748240d61a9SHante Meuleman 		if (err < 0) {
474916e64676SRafał Miłecki 			bphy_err(drvr, "mfp error %d\n", err);
4750240d61a9SHante Meuleman 			goto exit;
4751240d61a9SHante Meuleman 		}
4752240d61a9SHante Meuleman 	}
475305491d2cSKalle Valo 	/* set upper-layer auth */
475405491d2cSKalle Valo 	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
475505491d2cSKalle Valo 	if (err < 0) {
475616e64676SRafał Miłecki 		bphy_err(drvr, "wpa_auth error %d\n", err);
475705491d2cSKalle Valo 		goto exit;
475805491d2cSKalle Valo 	}
475905491d2cSKalle Valo 
476005491d2cSKalle Valo exit:
476105491d2cSKalle Valo 	return err;
476205491d2cSKalle Valo }
476305491d2cSKalle Valo 
476405491d2cSKalle Valo static s32
brcmf_parse_vndr_ies(const u8 * vndr_ie_buf,u32 vndr_ie_len,struct parsed_vndr_ies * vndr_ies)476505491d2cSKalle Valo brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
476605491d2cSKalle Valo 		     struct parsed_vndr_ies *vndr_ies)
476705491d2cSKalle Valo {
476805491d2cSKalle Valo 	struct brcmf_vs_tlv *vndrie;
476905491d2cSKalle Valo 	struct brcmf_tlv *ie;
477005491d2cSKalle Valo 	struct parsed_vndr_ie_info *parsed_info;
477105491d2cSKalle Valo 	s32 remaining_len;
477205491d2cSKalle Valo 
477305491d2cSKalle Valo 	remaining_len = (s32)vndr_ie_len;
477405491d2cSKalle Valo 	memset(vndr_ies, 0, sizeof(*vndr_ies));
477505491d2cSKalle Valo 
477605491d2cSKalle Valo 	ie = (struct brcmf_tlv *)vndr_ie_buf;
477705491d2cSKalle Valo 	while (ie) {
477805491d2cSKalle Valo 		if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
477905491d2cSKalle Valo 			goto next;
478005491d2cSKalle Valo 		vndrie = (struct brcmf_vs_tlv *)ie;
478105491d2cSKalle Valo 		/* len should be bigger than OUI length + one */
478205491d2cSKalle Valo 		if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
478305491d2cSKalle Valo 			brcmf_err("invalid vndr ie. length is too small %d\n",
478405491d2cSKalle Valo 				  vndrie->len);
478505491d2cSKalle Valo 			goto next;
478605491d2cSKalle Valo 		}
478705491d2cSKalle Valo 		/* if wpa or wme ie, do not add ie */
478805491d2cSKalle Valo 		if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
478905491d2cSKalle Valo 		    ((vndrie->oui_type == WPA_OUI_TYPE) ||
479005491d2cSKalle Valo 		    (vndrie->oui_type == WME_OUI_TYPE))) {
479105491d2cSKalle Valo 			brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
479205491d2cSKalle Valo 			goto next;
479305491d2cSKalle Valo 		}
479405491d2cSKalle Valo 
479505491d2cSKalle Valo 		parsed_info = &vndr_ies->ie_info[vndr_ies->count];
479605491d2cSKalle Valo 
479705491d2cSKalle Valo 		/* save vndr ie information */
479805491d2cSKalle Valo 		parsed_info->ie_ptr = (char *)vndrie;
479905491d2cSKalle Valo 		parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
480005491d2cSKalle Valo 		memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
480105491d2cSKalle Valo 
480205491d2cSKalle Valo 		vndr_ies->count++;
480305491d2cSKalle Valo 
48040e48b86dSAndy Shevchenko 		brcmf_dbg(TRACE, "** OUI %3ph, type 0x%02x\n",
48050e48b86dSAndy Shevchenko 			  parsed_info->vndrie.oui,
480605491d2cSKalle Valo 			  parsed_info->vndrie.oui_type);
480705491d2cSKalle Valo 
480805491d2cSKalle Valo 		if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
480905491d2cSKalle Valo 			break;
481005491d2cSKalle Valo next:
481105491d2cSKalle Valo 		remaining_len -= (ie->len + TLV_HDR_LEN);
481205491d2cSKalle Valo 		if (remaining_len <= TLV_HDR_LEN)
481305491d2cSKalle Valo 			ie = NULL;
481405491d2cSKalle Valo 		else
481505491d2cSKalle Valo 			ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
481605491d2cSKalle Valo 				TLV_HDR_LEN);
481705491d2cSKalle Valo 	}
481805491d2cSKalle Valo 	return 0;
481905491d2cSKalle Valo }
482005491d2cSKalle Valo 
482105491d2cSKalle Valo static u32
brcmf_vndr_ie(u8 * iebuf,s32 pktflag,u8 * ie_ptr,u32 ie_len,s8 * add_del_cmd)482205491d2cSKalle Valo brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
482305491d2cSKalle Valo {
48245f42b382SXulin Sun 	strscpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN);
482505491d2cSKalle Valo 
482605491d2cSKalle Valo 	put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
482705491d2cSKalle Valo 
482805491d2cSKalle Valo 	put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
482905491d2cSKalle Valo 
483005491d2cSKalle Valo 	memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
483105491d2cSKalle Valo 
483205491d2cSKalle Valo 	return ie_len + VNDR_IE_HDR_SIZE;
483305491d2cSKalle Valo }
483405491d2cSKalle Valo 
brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif * vif,s32 pktflag,const u8 * vndr_ie_buf,u32 vndr_ie_len)483505491d2cSKalle Valo s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
483605491d2cSKalle Valo 			  const u8 *vndr_ie_buf, u32 vndr_ie_len)
483705491d2cSKalle Valo {
483816e64676SRafał Miłecki 	struct brcmf_pub *drvr;
483905491d2cSKalle Valo 	struct brcmf_if *ifp;
484005491d2cSKalle Valo 	struct vif_saved_ie *saved_ie;
484105491d2cSKalle Valo 	s32 err = 0;
484205491d2cSKalle Valo 	u8  *iovar_ie_buf;
484305491d2cSKalle Valo 	u8  *curr_ie_buf;
484405491d2cSKalle Valo 	u8  *mgmt_ie_buf = NULL;
484505491d2cSKalle Valo 	int mgmt_ie_buf_len;
484605491d2cSKalle Valo 	u32 *mgmt_ie_len;
484705491d2cSKalle Valo 	u32 del_add_ie_buf_len = 0;
484805491d2cSKalle Valo 	u32 total_ie_buf_len = 0;
484905491d2cSKalle Valo 	u32 parsed_ie_buf_len = 0;
485005491d2cSKalle Valo 	struct parsed_vndr_ies old_vndr_ies;
485105491d2cSKalle Valo 	struct parsed_vndr_ies new_vndr_ies;
485205491d2cSKalle Valo 	struct parsed_vndr_ie_info *vndrie_info;
485305491d2cSKalle Valo 	s32 i;
485405491d2cSKalle Valo 	u8 *ptr;
485505491d2cSKalle Valo 	int remained_buf_len;
485605491d2cSKalle Valo 
485705491d2cSKalle Valo 	if (!vif)
485805491d2cSKalle Valo 		return -ENODEV;
485905491d2cSKalle Valo 	ifp = vif->ifp;
486016e64676SRafał Miłecki 	drvr = ifp->drvr;
486105491d2cSKalle Valo 	saved_ie = &vif->saved_ie;
486205491d2cSKalle Valo 
486337a869ecSHante Meuleman 	brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
486437a869ecSHante Meuleman 		  pktflag);
486505491d2cSKalle Valo 	iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
486605491d2cSKalle Valo 	if (!iovar_ie_buf)
486705491d2cSKalle Valo 		return -ENOMEM;
486805491d2cSKalle Valo 	curr_ie_buf = iovar_ie_buf;
486905491d2cSKalle Valo 	switch (pktflag) {
487005491d2cSKalle Valo 	case BRCMF_VNDR_IE_PRBREQ_FLAG:
487105491d2cSKalle Valo 		mgmt_ie_buf = saved_ie->probe_req_ie;
487205491d2cSKalle Valo 		mgmt_ie_len = &saved_ie->probe_req_ie_len;
487305491d2cSKalle Valo 		mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
487405491d2cSKalle Valo 		break;
487505491d2cSKalle Valo 	case BRCMF_VNDR_IE_PRBRSP_FLAG:
487605491d2cSKalle Valo 		mgmt_ie_buf = saved_ie->probe_res_ie;
487705491d2cSKalle Valo 		mgmt_ie_len = &saved_ie->probe_res_ie_len;
487805491d2cSKalle Valo 		mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
487905491d2cSKalle Valo 		break;
488005491d2cSKalle Valo 	case BRCMF_VNDR_IE_BEACON_FLAG:
488105491d2cSKalle Valo 		mgmt_ie_buf = saved_ie->beacon_ie;
488205491d2cSKalle Valo 		mgmt_ie_len = &saved_ie->beacon_ie_len;
488305491d2cSKalle Valo 		mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
488405491d2cSKalle Valo 		break;
488505491d2cSKalle Valo 	case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
488605491d2cSKalle Valo 		mgmt_ie_buf = saved_ie->assoc_req_ie;
488705491d2cSKalle Valo 		mgmt_ie_len = &saved_ie->assoc_req_ie_len;
488805491d2cSKalle Valo 		mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
488905491d2cSKalle Valo 		break;
48902719afcaSRyohei Kondo 	case BRCMF_VNDR_IE_ASSOCRSP_FLAG:
48912719afcaSRyohei Kondo 		mgmt_ie_buf = saved_ie->assoc_res_ie;
48922719afcaSRyohei Kondo 		mgmt_ie_len = &saved_ie->assoc_res_ie_len;
48932719afcaSRyohei Kondo 		mgmt_ie_buf_len = sizeof(saved_ie->assoc_res_ie);
48942719afcaSRyohei Kondo 		break;
489505491d2cSKalle Valo 	default:
489605491d2cSKalle Valo 		err = -EPERM;
489716e64676SRafał Miłecki 		bphy_err(drvr, "not suitable type\n");
489805491d2cSKalle Valo 		goto exit;
489905491d2cSKalle Valo 	}
490005491d2cSKalle Valo 
490105491d2cSKalle Valo 	if (vndr_ie_len > mgmt_ie_buf_len) {
490205491d2cSKalle Valo 		err = -ENOMEM;
490316e64676SRafał Miłecki 		bphy_err(drvr, "extra IE size too big\n");
490405491d2cSKalle Valo 		goto exit;
490505491d2cSKalle Valo 	}
490605491d2cSKalle Valo 
490705491d2cSKalle Valo 	/* parse and save new vndr_ie in curr_ie_buff before comparing it */
490805491d2cSKalle Valo 	if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
490905491d2cSKalle Valo 		ptr = curr_ie_buf;
491005491d2cSKalle Valo 		brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
491105491d2cSKalle Valo 		for (i = 0; i < new_vndr_ies.count; i++) {
491205491d2cSKalle Valo 			vndrie_info = &new_vndr_ies.ie_info[i];
491305491d2cSKalle Valo 			memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
491405491d2cSKalle Valo 			       vndrie_info->ie_len);
491505491d2cSKalle Valo 			parsed_ie_buf_len += vndrie_info->ie_len;
491605491d2cSKalle Valo 		}
491705491d2cSKalle Valo 	}
491805491d2cSKalle Valo 
491905491d2cSKalle Valo 	if (mgmt_ie_buf && *mgmt_ie_len) {
492005491d2cSKalle Valo 		if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
492105491d2cSKalle Valo 		    (memcmp(mgmt_ie_buf, curr_ie_buf,
492205491d2cSKalle Valo 			    parsed_ie_buf_len) == 0)) {
492305491d2cSKalle Valo 			brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
492405491d2cSKalle Valo 			goto exit;
492505491d2cSKalle Valo 		}
492605491d2cSKalle Valo 
492705491d2cSKalle Valo 		/* parse old vndr_ie */
492805491d2cSKalle Valo 		brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
492905491d2cSKalle Valo 
493005491d2cSKalle Valo 		/* make a command to delete old ie */
493105491d2cSKalle Valo 		for (i = 0; i < old_vndr_ies.count; i++) {
493205491d2cSKalle Valo 			vndrie_info = &old_vndr_ies.ie_info[i];
493305491d2cSKalle Valo 
49340e48b86dSAndy Shevchenko 			brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%3ph\n",
493505491d2cSKalle Valo 				  vndrie_info->vndrie.id,
493605491d2cSKalle Valo 				  vndrie_info->vndrie.len,
49370e48b86dSAndy Shevchenko 				  vndrie_info->vndrie.oui);
493805491d2cSKalle Valo 
493905491d2cSKalle Valo 			del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
494005491d2cSKalle Valo 							   vndrie_info->ie_ptr,
494105491d2cSKalle Valo 							   vndrie_info->ie_len,
494205491d2cSKalle Valo 							   "del");
494305491d2cSKalle Valo 			curr_ie_buf += del_add_ie_buf_len;
494405491d2cSKalle Valo 			total_ie_buf_len += del_add_ie_buf_len;
494505491d2cSKalle Valo 		}
494605491d2cSKalle Valo 	}
494705491d2cSKalle Valo 
494805491d2cSKalle Valo 	*mgmt_ie_len = 0;
494905491d2cSKalle Valo 	/* Add if there is any extra IE */
495005491d2cSKalle Valo 	if (mgmt_ie_buf && parsed_ie_buf_len) {
495105491d2cSKalle Valo 		ptr = mgmt_ie_buf;
495205491d2cSKalle Valo 
495305491d2cSKalle Valo 		remained_buf_len = mgmt_ie_buf_len;
495405491d2cSKalle Valo 
495505491d2cSKalle Valo 		/* make a command to add new ie */
495605491d2cSKalle Valo 		for (i = 0; i < new_vndr_ies.count; i++) {
495705491d2cSKalle Valo 			vndrie_info = &new_vndr_ies.ie_info[i];
495805491d2cSKalle Valo 
495905491d2cSKalle Valo 			/* verify remained buf size before copy data */
496005491d2cSKalle Valo 			if (remained_buf_len < (vndrie_info->vndrie.len +
496105491d2cSKalle Valo 							VNDR_IE_VSIE_OFFSET)) {
496216e64676SRafał Miłecki 				bphy_err(drvr, "no space in mgmt_ie_buf: len left %d",
496305491d2cSKalle Valo 					 remained_buf_len);
496405491d2cSKalle Valo 				break;
496505491d2cSKalle Valo 			}
496605491d2cSKalle Valo 			remained_buf_len -= (vndrie_info->ie_len +
496705491d2cSKalle Valo 					     VNDR_IE_VSIE_OFFSET);
496805491d2cSKalle Valo 
49690e48b86dSAndy Shevchenko 			brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%3ph\n",
497005491d2cSKalle Valo 				  vndrie_info->vndrie.id,
497105491d2cSKalle Valo 				  vndrie_info->vndrie.len,
49720e48b86dSAndy Shevchenko 				  vndrie_info->vndrie.oui);
497305491d2cSKalle Valo 
497405491d2cSKalle Valo 			del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
497505491d2cSKalle Valo 							   vndrie_info->ie_ptr,
497605491d2cSKalle Valo 							   vndrie_info->ie_len,
497705491d2cSKalle Valo 							   "add");
497805491d2cSKalle Valo 
497905491d2cSKalle Valo 			/* save the parsed IE in wl struct */
498005491d2cSKalle Valo 			memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
498105491d2cSKalle Valo 			       vndrie_info->ie_len);
498205491d2cSKalle Valo 			*mgmt_ie_len += vndrie_info->ie_len;
498305491d2cSKalle Valo 
498405491d2cSKalle Valo 			curr_ie_buf += del_add_ie_buf_len;
498505491d2cSKalle Valo 			total_ie_buf_len += del_add_ie_buf_len;
498605491d2cSKalle Valo 		}
498705491d2cSKalle Valo 	}
498805491d2cSKalle Valo 	if (total_ie_buf_len) {
498905491d2cSKalle Valo 		err  = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
499005491d2cSKalle Valo 						 total_ie_buf_len);
499105491d2cSKalle Valo 		if (err)
499216e64676SRafał Miłecki 			bphy_err(drvr, "vndr ie set error : %d\n", err);
499305491d2cSKalle Valo 	}
499405491d2cSKalle Valo 
499505491d2cSKalle Valo exit:
499605491d2cSKalle Valo 	kfree(iovar_ie_buf);
499705491d2cSKalle Valo 	return err;
499805491d2cSKalle Valo }
499905491d2cSKalle Valo 
brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif * vif)500005491d2cSKalle Valo s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
500105491d2cSKalle Valo {
50022386f64cSColin Ian King 	static const s32 pktflags[] = {
500305491d2cSKalle Valo 		BRCMF_VNDR_IE_PRBREQ_FLAG,
500405491d2cSKalle Valo 		BRCMF_VNDR_IE_PRBRSP_FLAG,
500505491d2cSKalle Valo 		BRCMF_VNDR_IE_BEACON_FLAG
500605491d2cSKalle Valo 	};
500705491d2cSKalle Valo 	int i;
500805491d2cSKalle Valo 
500905491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(pktflags); i++)
501005491d2cSKalle Valo 		brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
501105491d2cSKalle Valo 
501205491d2cSKalle Valo 	memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
501305491d2cSKalle Valo 	return 0;
501405491d2cSKalle Valo }
501505491d2cSKalle Valo 
501605491d2cSKalle Valo static s32
brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif * vif,struct cfg80211_beacon_data * beacon)501705491d2cSKalle Valo brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
501805491d2cSKalle Valo 			struct cfg80211_beacon_data *beacon)
501905491d2cSKalle Valo {
502016e64676SRafał Miłecki 	struct brcmf_pub *drvr = vif->ifp->drvr;
502105491d2cSKalle Valo 	s32 err;
502205491d2cSKalle Valo 
502305491d2cSKalle Valo 	/* Set Beacon IEs to FW */
502405491d2cSKalle Valo 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
502505491d2cSKalle Valo 				    beacon->tail, beacon->tail_len);
502605491d2cSKalle Valo 	if (err) {
502716e64676SRafał Miłecki 		bphy_err(drvr, "Set Beacon IE Failed\n");
502805491d2cSKalle Valo 		return err;
502905491d2cSKalle Valo 	}
503005491d2cSKalle Valo 	brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
503105491d2cSKalle Valo 
503205491d2cSKalle Valo 	/* Set Probe Response IEs to FW */
503305491d2cSKalle Valo 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
503405491d2cSKalle Valo 				    beacon->proberesp_ies,
503505491d2cSKalle Valo 				    beacon->proberesp_ies_len);
503605491d2cSKalle Valo 	if (err)
503716e64676SRafał Miłecki 		bphy_err(drvr, "Set Probe Resp IE Failed\n");
503805491d2cSKalle Valo 	else
503905491d2cSKalle Valo 		brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
504005491d2cSKalle Valo 
50412719afcaSRyohei Kondo 	/* Set Assoc Response IEs to FW */
50422719afcaSRyohei Kondo 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG,
50432719afcaSRyohei Kondo 				    beacon->assocresp_ies,
50442719afcaSRyohei Kondo 				    beacon->assocresp_ies_len);
50452719afcaSRyohei Kondo 	if (err)
50462719afcaSRyohei Kondo 		brcmf_err("Set Assoc Resp IE Failed\n");
50472719afcaSRyohei Kondo 	else
50482719afcaSRyohei Kondo 		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc Resp\n");
50492719afcaSRyohei Kondo 
505005491d2cSKalle Valo 	return err;
505105491d2cSKalle Valo }
505205491d2cSKalle Valo 
505305491d2cSKalle Valo static s32
brcmf_parse_configure_security(struct brcmf_if * ifp,struct cfg80211_ap_settings * settings,enum nl80211_iftype dev_role)5054b46f1546SJia-Shyr Chuang brcmf_parse_configure_security(struct brcmf_if *ifp,
5055b46f1546SJia-Shyr Chuang 			       struct cfg80211_ap_settings *settings,
5056b46f1546SJia-Shyr Chuang 			       enum nl80211_iftype dev_role)
5057b46f1546SJia-Shyr Chuang {
5058b46f1546SJia-Shyr Chuang 	const struct brcmf_tlv *rsn_ie;
5059b46f1546SJia-Shyr Chuang 	const struct brcmf_vs_tlv *wpa_ie;
5060b46f1546SJia-Shyr Chuang 	s32 err = 0;
5061b46f1546SJia-Shyr Chuang 
5062b46f1546SJia-Shyr Chuang 	/* find the RSN_IE */
5063b46f1546SJia-Shyr Chuang 	rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
5064b46f1546SJia-Shyr Chuang 				  settings->beacon.tail_len, WLAN_EID_RSN);
5065b46f1546SJia-Shyr Chuang 
5066b46f1546SJia-Shyr Chuang 	/* find the WPA_IE */
5067b46f1546SJia-Shyr Chuang 	wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
5068b46f1546SJia-Shyr Chuang 				  settings->beacon.tail_len);
5069b46f1546SJia-Shyr Chuang 
5070b46f1546SJia-Shyr Chuang 	if (wpa_ie || rsn_ie) {
5071b46f1546SJia-Shyr Chuang 		brcmf_dbg(TRACE, "WPA(2) IE is found\n");
5072b46f1546SJia-Shyr Chuang 		if (wpa_ie) {
5073b46f1546SJia-Shyr Chuang 			/* WPA IE */
5074b46f1546SJia-Shyr Chuang 			err = brcmf_configure_wpaie(ifp, wpa_ie, false);
5075b46f1546SJia-Shyr Chuang 			if (err < 0)
5076b46f1546SJia-Shyr Chuang 				return err;
5077b46f1546SJia-Shyr Chuang 		} else {
5078b46f1546SJia-Shyr Chuang 			struct brcmf_vs_tlv *tmp_ie;
5079b46f1546SJia-Shyr Chuang 
5080b46f1546SJia-Shyr Chuang 			tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
5081b46f1546SJia-Shyr Chuang 
5082b46f1546SJia-Shyr Chuang 			/* RSN IE */
5083b46f1546SJia-Shyr Chuang 			err = brcmf_configure_wpaie(ifp, tmp_ie, true);
5084b46f1546SJia-Shyr Chuang 			if (err < 0)
5085b46f1546SJia-Shyr Chuang 				return err;
5086b46f1546SJia-Shyr Chuang 		}
5087b46f1546SJia-Shyr Chuang 	} else {
5088b46f1546SJia-Shyr Chuang 		brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
5089b46f1546SJia-Shyr Chuang 		brcmf_configure_opensecurity(ifp);
5090b46f1546SJia-Shyr Chuang 	}
5091b46f1546SJia-Shyr Chuang 
5092b46f1546SJia-Shyr Chuang 	return err;
5093b46f1546SJia-Shyr Chuang }
5094b46f1546SJia-Shyr Chuang 
5095b46f1546SJia-Shyr Chuang static s32
brcmf_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ap_settings * settings)509605491d2cSKalle Valo brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
509705491d2cSKalle Valo 			struct cfg80211_ap_settings *settings)
509805491d2cSKalle Valo {
509905491d2cSKalle Valo 	s32 ie_offset;
510005491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
510105491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
510216e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
5103787fb926SChung-Hsien Hsu 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
5104787fb926SChung-Hsien Hsu 	struct cfg80211_crypto_settings *crypto = &settings->crypto;
510505491d2cSKalle Valo 	const struct brcmf_tlv *ssid_ie;
510605491d2cSKalle Valo 	const struct brcmf_tlv *country_ie;
510705491d2cSKalle Valo 	struct brcmf_ssid_le ssid_le;
510805491d2cSKalle Valo 	s32 err = -EPERM;
510905491d2cSKalle Valo 	struct brcmf_join_params join_params;
511005491d2cSKalle Valo 	enum nl80211_iftype dev_role;
511105491d2cSKalle Valo 	struct brcmf_fil_bss_enable_le bss_enable;
51128707e08dSRafał Miłecki 	u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
511305491d2cSKalle Valo 	bool mbss;
511405491d2cSKalle Valo 	int is_11d;
5115b3589dfeSHante Meuleman 	bool supports_11d;
511605491d2cSKalle Valo 
511705491d2cSKalle Valo 	brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
511805491d2cSKalle Valo 		  settings->chandef.chan->hw_value,
511905491d2cSKalle Valo 		  settings->chandef.center_freq1, settings->chandef.width,
512005491d2cSKalle Valo 		  settings->beacon_interval, settings->dtim_period);
512105491d2cSKalle Valo 	brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
512205491d2cSKalle Valo 		  settings->ssid, settings->ssid_len, settings->auth_type,
512305491d2cSKalle Valo 		  settings->inactivity_timeout);
512405491d2cSKalle Valo 	dev_role = ifp->vif->wdev.iftype;
512505491d2cSKalle Valo 	mbss = ifp->vif->mbss;
512605491d2cSKalle Valo 
512705491d2cSKalle Valo 	/* store current 11d setting */
5128b3589dfeSHante Meuleman 	if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY,
5129b3589dfeSHante Meuleman 				  &ifp->vif->is_11d)) {
5130d3532ea6SArnd Bergmann 		is_11d = supports_11d = false;
5131b3589dfeSHante Meuleman 	} else {
513205491d2cSKalle Valo 		country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
513305491d2cSKalle Valo 					      settings->beacon.tail_len,
513405491d2cSKalle Valo 					      WLAN_EID_COUNTRY);
513505491d2cSKalle Valo 		is_11d = country_ie ? 1 : 0;
5136b3589dfeSHante Meuleman 		supports_11d = true;
5137b3589dfeSHante Meuleman 	}
513805491d2cSKalle Valo 
513905491d2cSKalle Valo 	memset(&ssid_le, 0, sizeof(ssid_le));
514005491d2cSKalle Valo 	if (settings->ssid == NULL || settings->ssid_len == 0) {
514105491d2cSKalle Valo 		ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
514205491d2cSKalle Valo 		ssid_ie = brcmf_parse_tlvs(
514305491d2cSKalle Valo 				(u8 *)&settings->beacon.head[ie_offset],
514405491d2cSKalle Valo 				settings->beacon.head_len - ie_offset,
514505491d2cSKalle Valo 				WLAN_EID_SSID);
5146ded89912SArend Van Spriel 		if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
514705491d2cSKalle Valo 			return -EINVAL;
514805491d2cSKalle Valo 
514905491d2cSKalle Valo 		memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
515005491d2cSKalle Valo 		ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
515105491d2cSKalle Valo 		brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
515205491d2cSKalle Valo 	} else {
515305491d2cSKalle Valo 		memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
515405491d2cSKalle Valo 		ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
515505491d2cSKalle Valo 	}
515605491d2cSKalle Valo 
515705491d2cSKalle Valo 	if (!mbss) {
515805491d2cSKalle Valo 		brcmf_set_mpc(ifp, 0);
515952f22fb2SFranky Lin 		brcmf_configure_arp_nd_offload(ifp, false);
516005491d2cSKalle Valo 	}
516105491d2cSKalle Valo 
51628707e08dSRafał Miłecki 	/* Parameters shared by all radio interfaces */
516305491d2cSKalle Valo 	if (!mbss) {
5164b3589dfeSHante Meuleman 		if ((supports_11d) && (is_11d != ifp->vif->is_11d)) {
516505491d2cSKalle Valo 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
516605491d2cSKalle Valo 						    is_11d);
516705491d2cSKalle Valo 			if (err < 0) {
516816e64676SRafał Miłecki 				bphy_err(drvr, "Regulatory Set Error, %d\n",
51693ef005b8SRafał Miłecki 					 err);
517005491d2cSKalle Valo 				goto exit;
517105491d2cSKalle Valo 			}
517205491d2cSKalle Valo 		}
517305491d2cSKalle Valo 		if (settings->beacon_interval) {
517405491d2cSKalle Valo 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
517505491d2cSKalle Valo 						    settings->beacon_interval);
517605491d2cSKalle Valo 			if (err < 0) {
517716e64676SRafał Miłecki 				bphy_err(drvr, "Beacon Interval Set Error, %d\n",
517805491d2cSKalle Valo 					 err);
517905491d2cSKalle Valo 				goto exit;
518005491d2cSKalle Valo 			}
518105491d2cSKalle Valo 		}
518205491d2cSKalle Valo 		if (settings->dtim_period) {
518305491d2cSKalle Valo 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
518405491d2cSKalle Valo 						    settings->dtim_period);
518505491d2cSKalle Valo 			if (err < 0) {
518616e64676SRafał Miłecki 				bphy_err(drvr, "DTIM Interval Set Error, %d\n",
51873ef005b8SRafał Miłecki 					 err);
518805491d2cSKalle Valo 				goto exit;
518905491d2cSKalle Valo 			}
519005491d2cSKalle Valo 		}
519105491d2cSKalle Valo 
51928abffd81SHante Meuleman 		if ((dev_role == NL80211_IFTYPE_AP) &&
51938abffd81SHante Meuleman 		    ((ifp->ifidx == 0) ||
5194774965f2SWright Feng 		     (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB) &&
5195774965f2SWright Feng 		      !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)))) {
519605491d2cSKalle Valo 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
519705491d2cSKalle Valo 			if (err < 0) {
519816e64676SRafał Miłecki 				bphy_err(drvr, "BRCMF_C_DOWN error %d\n",
51993ef005b8SRafał Miłecki 					 err);
520005491d2cSKalle Valo 				goto exit;
520105491d2cSKalle Valo 			}
520205491d2cSKalle Valo 			brcmf_fil_iovar_int_set(ifp, "apsta", 0);
520305491d2cSKalle Valo 		}
520405491d2cSKalle Valo 
520505491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
520605491d2cSKalle Valo 		if (err < 0) {
520716e64676SRafał Miłecki 			bphy_err(drvr, "SET INFRA error %d\n", err);
520805491d2cSKalle Valo 			goto exit;
520905491d2cSKalle Valo 		}
5210b3589dfeSHante Meuleman 	} else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) {
521105491d2cSKalle Valo 		/* Multiple-BSS should use same 11d configuration */
521205491d2cSKalle Valo 		err = -EINVAL;
521305491d2cSKalle Valo 		goto exit;
521405491d2cSKalle Valo 	}
52158707e08dSRafał Miłecki 
52168707e08dSRafał Miłecki 	/* Interface specific setup */
521705491d2cSKalle Valo 	if (dev_role == NL80211_IFTYPE_AP) {
521805491d2cSKalle Valo 		if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
521905491d2cSKalle Valo 			brcmf_fil_iovar_int_set(ifp, "mbss", 1);
522005491d2cSKalle Valo 
522105491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
522205491d2cSKalle Valo 		if (err < 0) {
522316e64676SRafał Miłecki 			bphy_err(drvr, "setting AP mode failed %d\n",
52243ef005b8SRafał Miłecki 				 err);
522505491d2cSKalle Valo 			goto exit;
522605491d2cSKalle Valo 		}
52278707e08dSRafał Miłecki 		if (!mbss) {
52288707e08dSRafał Miłecki 			/* Firmware 10.x requires setting channel after enabling
52298707e08dSRafał Miłecki 			 * AP and before bringing interface up.
52308707e08dSRafał Miłecki 			 */
52318707e08dSRafał Miłecki 			err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
52328707e08dSRafał Miłecki 			if (err < 0) {
523316e64676SRafał Miłecki 				bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
52348707e08dSRafał Miłecki 					 chanspec, err);
52358707e08dSRafał Miłecki 				goto exit;
52368707e08dSRafał Miłecki 			}
52378707e08dSRafał Miłecki 		}
523805491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
523905491d2cSKalle Valo 		if (err < 0) {
524016e64676SRafał Miłecki 			bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err);
524105491d2cSKalle Valo 			goto exit;
524205491d2cSKalle Valo 		}
5243b46f1546SJia-Shyr Chuang 
5244787fb926SChung-Hsien Hsu 		if (crypto->psk) {
5245787fb926SChung-Hsien Hsu 			brcmf_dbg(INFO, "using PSK offload\n");
5246787fb926SChung-Hsien Hsu 			profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_PSK);
5247787fb926SChung-Hsien Hsu 			err = brcmf_set_pmk(ifp, crypto->psk,
5248787fb926SChung-Hsien Hsu 					    BRCMF_WSEC_MAX_PSK_LEN);
5249787fb926SChung-Hsien Hsu 			if (err < 0)
5250787fb926SChung-Hsien Hsu 				goto exit;
5251787fb926SChung-Hsien Hsu 		}
5252d5f59c96SChung-Hsien Hsu 		if (crypto->sae_pwd) {
5253d5f59c96SChung-Hsien Hsu 			brcmf_dbg(INFO, "using SAE offload\n");
5254d5f59c96SChung-Hsien Hsu 			profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE);
52558d59a64cSHector Martin 			err = brcmf_fwvid_set_sae_password(ifp, crypto);
5256d5f59c96SChung-Hsien Hsu 			if (err < 0)
5257d5f59c96SChung-Hsien Hsu 				goto exit;
5258d5f59c96SChung-Hsien Hsu 		}
5259787fb926SChung-Hsien Hsu 		if (profile->use_fwauth == 0)
5260787fb926SChung-Hsien Hsu 			profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
5261787fb926SChung-Hsien Hsu 
5262b46f1546SJia-Shyr Chuang 		err = brcmf_parse_configure_security(ifp, settings,
5263b46f1546SJia-Shyr Chuang 						     NL80211_IFTYPE_AP);
5264b46f1546SJia-Shyr Chuang 		if (err < 0) {
5265b46f1546SJia-Shyr Chuang 			bphy_err(drvr, "brcmf_parse_configure_security error\n");
5266b46f1546SJia-Shyr Chuang 			goto exit;
5267b46f1546SJia-Shyr Chuang 		}
5268b46f1546SJia-Shyr Chuang 
526905491d2cSKalle Valo 		/* On DOWN the firmware removes the WEP keys, reconfigure
527005491d2cSKalle Valo 		 * them if they were set.
527105491d2cSKalle Valo 		 */
527205491d2cSKalle Valo 		brcmf_cfg80211_reconfigure_wep(ifp);
527305491d2cSKalle Valo 
527405491d2cSKalle Valo 		memset(&join_params, 0, sizeof(join_params));
527505491d2cSKalle Valo 		/* join parameters starts with ssid */
527605491d2cSKalle Valo 		memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
527705491d2cSKalle Valo 		/* create softap */
527805491d2cSKalle Valo 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
527905491d2cSKalle Valo 					     &join_params, sizeof(join_params));
528005491d2cSKalle Valo 		if (err < 0) {
528116e64676SRafał Miłecki 			bphy_err(drvr, "SET SSID error (%d)\n", err);
528205491d2cSKalle Valo 			goto exit;
528305491d2cSKalle Valo 		}
5284c940de10SRafał Miłecki 
5285d9429d03SSoontak Lee 		err = brcmf_fil_iovar_int_set(ifp, "closednet",
5286d9429d03SSoontak Lee 					      settings->hidden_ssid);
5287c940de10SRafał Miłecki 		if (err) {
5288d9429d03SSoontak Lee 			bphy_err(drvr, "%s closednet error (%d)\n",
5289d9429d03SSoontak Lee 				 settings->hidden_ssid ?
5290d9429d03SSoontak Lee 				 "enabled" : "disabled",
5291d9429d03SSoontak Lee 				 err);
5292c940de10SRafał Miłecki 			goto exit;
5293c940de10SRafał Miłecki 		}
5294c940de10SRafał Miłecki 
529505491d2cSKalle Valo 		brcmf_dbg(TRACE, "AP mode configuration complete\n");
52968707e08dSRafał Miłecki 	} else if (dev_role == NL80211_IFTYPE_P2P_GO) {
52978707e08dSRafał Miłecki 		err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
52988707e08dSRafał Miłecki 		if (err < 0) {
529916e64676SRafał Miłecki 			bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
53008707e08dSRafał Miłecki 				 chanspec, err);
53018707e08dSRafał Miłecki 			goto exit;
53028707e08dSRafał Miłecki 		}
5303b46f1546SJia-Shyr Chuang 
5304b46f1546SJia-Shyr Chuang 		err = brcmf_parse_configure_security(ifp, settings,
5305b46f1546SJia-Shyr Chuang 						     NL80211_IFTYPE_P2P_GO);
5306b46f1546SJia-Shyr Chuang 		if (err < 0) {
5307b46f1546SJia-Shyr Chuang 			brcmf_err("brcmf_parse_configure_security error\n");
5308b46f1546SJia-Shyr Chuang 			goto exit;
5309b46f1546SJia-Shyr Chuang 		}
5310b46f1546SJia-Shyr Chuang 
531105491d2cSKalle Valo 		err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
531205491d2cSKalle Valo 						sizeof(ssid_le));
531305491d2cSKalle Valo 		if (err < 0) {
531416e64676SRafał Miłecki 			bphy_err(drvr, "setting ssid failed %d\n", err);
531505491d2cSKalle Valo 			goto exit;
531605491d2cSKalle Valo 		}
531737a869ecSHante Meuleman 		bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
531805491d2cSKalle Valo 		bss_enable.enable = cpu_to_le32(1);
531905491d2cSKalle Valo 		err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
532005491d2cSKalle Valo 					       sizeof(bss_enable));
532105491d2cSKalle Valo 		if (err < 0) {
532216e64676SRafał Miłecki 			bphy_err(drvr, "bss_enable config failed %d\n", err);
532305491d2cSKalle Valo 			goto exit;
532405491d2cSKalle Valo 		}
532505491d2cSKalle Valo 
532605491d2cSKalle Valo 		brcmf_dbg(TRACE, "GO mode configuration complete\n");
53278707e08dSRafał Miłecki 	} else {
53288707e08dSRafał Miłecki 		WARN_ON(1);
532905491d2cSKalle Valo 	}
53308707e08dSRafał Miłecki 
5331f25ba69cSWright Feng 	brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
533205491d2cSKalle Valo 	set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
533305491d2cSKalle Valo 	brcmf_net_setcarrier(ifp, true);
533405491d2cSKalle Valo 
533505491d2cSKalle Valo exit:
533605491d2cSKalle Valo 	if ((err) && (!mbss)) {
533705491d2cSKalle Valo 		brcmf_set_mpc(ifp, 1);
533852f22fb2SFranky Lin 		brcmf_configure_arp_nd_offload(ifp, true);
533905491d2cSKalle Valo 	}
534005491d2cSKalle Valo 	return err;
534105491d2cSKalle Valo }
534205491d2cSKalle Valo 
brcmf_cfg80211_stop_ap(struct wiphy * wiphy,struct net_device * ndev,unsigned int link_id)53437b0a0e3cSJohannes Berg static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev,
53447b0a0e3cSJohannes Berg 				  unsigned int link_id)
534505491d2cSKalle Valo {
534616e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
534705491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
534816e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
5349787fb926SChung-Hsien Hsu 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
535005491d2cSKalle Valo 	s32 err;
535105491d2cSKalle Valo 	struct brcmf_fil_bss_enable_le bss_enable;
535205491d2cSKalle Valo 	struct brcmf_join_params join_params;
535305491d2cSKalle Valo 
535405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
535505491d2cSKalle Valo 
535605491d2cSKalle Valo 	if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
535705491d2cSKalle Valo 		/* Due to most likely deauths outstanding we sleep */
535805491d2cSKalle Valo 		/* first to make sure they get processed by fw. */
535905491d2cSKalle Valo 		msleep(400);
536005491d2cSKalle Valo 
5361787fb926SChung-Hsien Hsu 		if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) {
53628d59a64cSHector Martin 			struct cfg80211_crypto_settings crypto = {};
53638d59a64cSHector Martin 
5364787fb926SChung-Hsien Hsu 			if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK))
5365787fb926SChung-Hsien Hsu 				brcmf_set_pmk(ifp, NULL, 0);
5366d5f59c96SChung-Hsien Hsu 			if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE))
53678d59a64cSHector Martin 				brcmf_fwvid_set_sae_password(ifp, &crypto);
5368787fb926SChung-Hsien Hsu 			profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
5369787fb926SChung-Hsien Hsu 		}
5370787fb926SChung-Hsien Hsu 
537105491d2cSKalle Valo 		if (ifp->vif->mbss) {
537205491d2cSKalle Valo 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
537305491d2cSKalle Valo 			return err;
537405491d2cSKalle Valo 		}
537505491d2cSKalle Valo 
5376c940de10SRafał Miłecki 		/* First BSS doesn't get a full reset */
5377c940de10SRafał Miłecki 		if (ifp->bsscfgidx == 0)
5378c940de10SRafał Miłecki 			brcmf_fil_iovar_int_set(ifp, "closednet", 0);
5379c940de10SRafał Miłecki 
538005491d2cSKalle Valo 		memset(&join_params, 0, sizeof(join_params));
538105491d2cSKalle Valo 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
538205491d2cSKalle Valo 					     &join_params, sizeof(join_params));
538305491d2cSKalle Valo 		if (err < 0)
538416e64676SRafał Miłecki 			bphy_err(drvr, "SET SSID error (%d)\n", err);
538505491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
538605491d2cSKalle Valo 		if (err < 0)
538716e64676SRafał Miłecki 			bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err);
538805491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
538905491d2cSKalle Valo 		if (err < 0)
539016e64676SRafał Miłecki 			bphy_err(drvr, "setting AP mode failed %d\n", err);
539105491d2cSKalle Valo 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
539205491d2cSKalle Valo 			brcmf_fil_iovar_int_set(ifp, "mbss", 0);
5393b3589dfeSHante Meuleman 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
539405491d2cSKalle Valo 				      ifp->vif->is_11d);
539505491d2cSKalle Valo 		/* Bring device back up so it can be used again */
539605491d2cSKalle Valo 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
539705491d2cSKalle Valo 		if (err < 0)
539816e64676SRafał Miłecki 			bphy_err(drvr, "BRCMF_C_UP error %d\n", err);
5399f25ba69cSWright Feng 
5400f25ba69cSWright Feng 		brcmf_vif_clear_mgmt_ies(ifp->vif);
540105491d2cSKalle Valo 	} else {
540237a869ecSHante Meuleman 		bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
540305491d2cSKalle Valo 		bss_enable.enable = cpu_to_le32(0);
540405491d2cSKalle Valo 		err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
540505491d2cSKalle Valo 					       sizeof(bss_enable));
540605491d2cSKalle Valo 		if (err < 0)
540716e64676SRafał Miłecki 			bphy_err(drvr, "bss_enable config failed %d\n", err);
540805491d2cSKalle Valo 	}
540905491d2cSKalle Valo 	brcmf_set_mpc(ifp, 1);
541052f22fb2SFranky Lin 	brcmf_configure_arp_nd_offload(ifp, true);
541105491d2cSKalle Valo 	clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
541205491d2cSKalle Valo 	brcmf_net_setcarrier(ifp, false);
541305491d2cSKalle Valo 
541405491d2cSKalle Valo 	return err;
541505491d2cSKalle Valo }
541605491d2cSKalle Valo 
541705491d2cSKalle Valo static s32
brcmf_cfg80211_change_beacon(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_beacon_data * info)541805491d2cSKalle Valo brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
541905491d2cSKalle Valo 			     struct cfg80211_beacon_data *info)
542005491d2cSKalle Valo {
542105491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
542205491d2cSKalle Valo 
542305491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
542405491d2cSKalle Valo 
5425e56a7708SJinpeng Cui 	return brcmf_config_ap_mgmt_ie(ifp->vif, info);
542605491d2cSKalle Valo }
542705491d2cSKalle Valo 
542805491d2cSKalle Valo static int
brcmf_cfg80211_del_station(struct wiphy * wiphy,struct net_device * ndev,struct station_del_parameters * params)542905491d2cSKalle Valo brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
543005491d2cSKalle Valo 			   struct station_del_parameters *params)
543105491d2cSKalle Valo {
543205491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
543316e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
543405491d2cSKalle Valo 	struct brcmf_scb_val_le scbval;
543505491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
543605491d2cSKalle Valo 	s32 err;
543705491d2cSKalle Valo 
543805491d2cSKalle Valo 	if (!params->mac)
543905491d2cSKalle Valo 		return -EFAULT;
544005491d2cSKalle Valo 
544105491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
544205491d2cSKalle Valo 
544305491d2cSKalle Valo 	if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
544405491d2cSKalle Valo 		ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
544505491d2cSKalle Valo 	if (!check_vif_up(ifp->vif))
544605491d2cSKalle Valo 		return -EIO;
544705491d2cSKalle Valo 
544805491d2cSKalle Valo 	memcpy(&scbval.ea, params->mac, ETH_ALEN);
544905491d2cSKalle Valo 	scbval.val = cpu_to_le32(params->reason_code);
545005491d2cSKalle Valo 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
545105491d2cSKalle Valo 				     &scbval, sizeof(scbval));
545205491d2cSKalle Valo 	if (err)
545316e64676SRafał Miłecki 		bphy_err(drvr, "SCB_DEAUTHENTICATE_FOR_REASON failed %d\n",
54543ef005b8SRafał Miłecki 			 err);
545505491d2cSKalle Valo 
545605491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
545705491d2cSKalle Valo 	return err;
545805491d2cSKalle Valo }
545905491d2cSKalle Valo 
546005491d2cSKalle Valo static int
brcmf_cfg80211_change_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_parameters * params)546105491d2cSKalle Valo brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
546205491d2cSKalle Valo 			      const u8 *mac, struct station_parameters *params)
546305491d2cSKalle Valo {
546416e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
546516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
546605491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
546705491d2cSKalle Valo 	s32 err;
546805491d2cSKalle Valo 
546905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
547005491d2cSKalle Valo 		  params->sta_flags_mask, params->sta_flags_set);
547105491d2cSKalle Valo 
547205491d2cSKalle Valo 	/* Ignore all 00 MAC */
547305491d2cSKalle Valo 	if (is_zero_ether_addr(mac))
547405491d2cSKalle Valo 		return 0;
547505491d2cSKalle Valo 
547605491d2cSKalle Valo 	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
547705491d2cSKalle Valo 		return 0;
547805491d2cSKalle Valo 
547905491d2cSKalle Valo 	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
548005491d2cSKalle Valo 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
548105491d2cSKalle Valo 					     (void *)mac, ETH_ALEN);
548205491d2cSKalle Valo 	else
548305491d2cSKalle Valo 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
548405491d2cSKalle Valo 					     (void *)mac, ETH_ALEN);
548505491d2cSKalle Valo 	if (err < 0)
548616e64676SRafał Miłecki 		bphy_err(drvr, "Setting SCB (de-)authorize failed, %d\n", err);
548705491d2cSKalle Valo 
548805491d2cSKalle Valo 	return err;
548905491d2cSKalle Valo }
549005491d2cSKalle Valo 
549105491d2cSKalle Valo static void
brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy * wiphy,struct wireless_dev * wdev,struct mgmt_frame_regs * upd)54926cd536feSJohannes Berg brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
549305491d2cSKalle Valo 					       struct wireless_dev *wdev,
54946cd536feSJohannes Berg 					       struct mgmt_frame_regs *upd)
549505491d2cSKalle Valo {
549605491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
549705491d2cSKalle Valo 
549805491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
54996cd536feSJohannes Berg 
55006cd536feSJohannes Berg 	vif->mgmt_rx_reg = upd->interface_stypes;
550105491d2cSKalle Valo }
550205491d2cSKalle Valo 
550305491d2cSKalle Valo 
550405491d2cSKalle Valo static int
brcmf_cfg80211_mgmt_tx(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_mgmt_tx_params * params,u64 * cookie)550505491d2cSKalle Valo brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
550605491d2cSKalle Valo 		       struct cfg80211_mgmt_tx_params *params, u64 *cookie)
550705491d2cSKalle Valo {
550805491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
550905491d2cSKalle Valo 	struct ieee80211_channel *chan = params->chan;
551016e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
551105491d2cSKalle Valo 	const u8 *buf = params->buf;
551205491d2cSKalle Valo 	size_t len = params->len;
551305491d2cSKalle Valo 	const struct ieee80211_mgmt *mgmt;
551405491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
551505491d2cSKalle Valo 	s32 err = 0;
551605491d2cSKalle Valo 	s32 ie_offset;
551705491d2cSKalle Valo 	s32 ie_len;
551805491d2cSKalle Valo 	struct brcmf_fil_action_frame_le *action_frame;
551905491d2cSKalle Valo 	struct brcmf_fil_af_params_le *af_params;
552005491d2cSKalle Valo 	bool ack;
552105491d2cSKalle Valo 	s32 chan_nr;
552205491d2cSKalle Valo 	u32 freq;
552305491d2cSKalle Valo 
552405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
552505491d2cSKalle Valo 
552605491d2cSKalle Valo 	*cookie = 0;
552705491d2cSKalle Valo 
552805491d2cSKalle Valo 	mgmt = (const struct ieee80211_mgmt *)buf;
552905491d2cSKalle Valo 
553005491d2cSKalle Valo 	if (!ieee80211_is_mgmt(mgmt->frame_control)) {
553116e64676SRafał Miłecki 		bphy_err(drvr, "Driver only allows MGMT packet type\n");
553205491d2cSKalle Valo 		return -EPERM;
553305491d2cSKalle Valo 	}
553405491d2cSKalle Valo 
553505491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
553605491d2cSKalle Valo 
553705491d2cSKalle Valo 	if (ieee80211_is_probe_resp(mgmt->frame_control)) {
553805491d2cSKalle Valo 		/* Right now the only reason to get a probe response */
553905491d2cSKalle Valo 		/* is for p2p listen response or for p2p GO from     */
554005491d2cSKalle Valo 		/* wpa_supplicant. Unfortunately the probe is send   */
554105491d2cSKalle Valo 		/* on primary ndev, while dongle wants it on the p2p */
554205491d2cSKalle Valo 		/* vif. Since this is only reason for a probe        */
554305491d2cSKalle Valo 		/* response to be sent, the vif is taken from cfg.   */
554405491d2cSKalle Valo 		/* If ever desired to send proberesp for non p2p     */
554505491d2cSKalle Valo 		/* response then data should be checked for          */
554605491d2cSKalle Valo 		/* "DIRECT-". Note in future supplicant will take    */
554705491d2cSKalle Valo 		/* dedicated p2p wdev to do this and then this 'hack'*/
554805491d2cSKalle Valo 		/* is not needed anymore.                            */
554905491d2cSKalle Valo 		ie_offset =  DOT11_MGMT_HDR_LEN +
555005491d2cSKalle Valo 			     DOT11_BCN_PRB_FIXED_LEN;
555105491d2cSKalle Valo 		ie_len = len - ie_offset;
555205491d2cSKalle Valo 		if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
555305491d2cSKalle Valo 			vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
555405491d2cSKalle Valo 		err = brcmf_vif_set_mgmt_ie(vif,
555505491d2cSKalle Valo 					    BRCMF_VNDR_IE_PRBRSP_FLAG,
555605491d2cSKalle Valo 					    &buf[ie_offset],
555705491d2cSKalle Valo 					    ie_len);
555805491d2cSKalle Valo 		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
555905491d2cSKalle Valo 					GFP_KERNEL);
556005491d2cSKalle Valo 	} else if (ieee80211_is_action(mgmt->frame_control)) {
55618f44c9a4SArend van Spriel 		if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) {
556216e64676SRafał Miłecki 			bphy_err(drvr, "invalid action frame length\n");
55638f44c9a4SArend van Spriel 			err = -EINVAL;
55648f44c9a4SArend van Spriel 			goto exit;
55658f44c9a4SArend van Spriel 		}
556605491d2cSKalle Valo 		af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
556705491d2cSKalle Valo 		if (af_params == NULL) {
556816e64676SRafał Miłecki 			bphy_err(drvr, "unable to allocate frame\n");
556905491d2cSKalle Valo 			err = -ENOMEM;
557005491d2cSKalle Valo 			goto exit;
557105491d2cSKalle Valo 		}
557205491d2cSKalle Valo 		action_frame = &af_params->action_frame;
557305491d2cSKalle Valo 		/* Add the packet Id */
557405491d2cSKalle Valo 		action_frame->packet_id = cpu_to_le32(*cookie);
557505491d2cSKalle Valo 		/* Add BSSID */
557605491d2cSKalle Valo 		memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
557705491d2cSKalle Valo 		memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
557805491d2cSKalle Valo 		/* Add the length exepted for 802.11 header  */
557905491d2cSKalle Valo 		action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
558005491d2cSKalle Valo 		/* Add the channel. Use the one specified as parameter if any or
558105491d2cSKalle Valo 		 * the current one (got from the firmware) otherwise
558205491d2cSKalle Valo 		 */
558305491d2cSKalle Valo 		if (chan)
558405491d2cSKalle Valo 			freq = chan->center_freq;
558505491d2cSKalle Valo 		else
558605491d2cSKalle Valo 			brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
558705491d2cSKalle Valo 					      &freq);
558805491d2cSKalle Valo 		chan_nr = ieee80211_frequency_to_channel(freq);
558905491d2cSKalle Valo 		af_params->channel = cpu_to_le32(chan_nr);
5590ad96bc27SJoseph Chuang 		af_params->dwell_time = cpu_to_le32(params->wait);
559105491d2cSKalle Valo 		memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
559205491d2cSKalle Valo 		       le16_to_cpu(action_frame->len));
559305491d2cSKalle Valo 
559405491d2cSKalle Valo 		brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
559505491d2cSKalle Valo 			  *cookie, le16_to_cpu(action_frame->len), freq);
559605491d2cSKalle Valo 
559705491d2cSKalle Valo 		ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
559805491d2cSKalle Valo 						  af_params);
559905491d2cSKalle Valo 
560005491d2cSKalle Valo 		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
560105491d2cSKalle Valo 					GFP_KERNEL);
560205491d2cSKalle Valo 		kfree(af_params);
560305491d2cSKalle Valo 	} else {
560405491d2cSKalle Valo 		brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
56055b5e0928SAlexey Dobriyan 		brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len);
560605491d2cSKalle Valo 	}
560705491d2cSKalle Valo 
560805491d2cSKalle Valo exit:
560905491d2cSKalle Valo 	return err;
561005491d2cSKalle Valo }
561105491d2cSKalle Valo 
brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy * wiphy,struct net_device * ndev,s32 rssi_low,s32 rssi_high)56127dd56ea4SAlvin Šipraga static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
56137dd56ea4SAlvin Šipraga 						    struct net_device *ndev,
56147dd56ea4SAlvin Šipraga 						    s32 rssi_low, s32 rssi_high)
56157dd56ea4SAlvin Šipraga {
56167dd56ea4SAlvin Šipraga 	struct brcmf_cfg80211_vif *vif;
56177dd56ea4SAlvin Šipraga 	struct brcmf_if *ifp;
56187dd56ea4SAlvin Šipraga 	int err = 0;
56197dd56ea4SAlvin Šipraga 
56207dd56ea4SAlvin Šipraga 	brcmf_dbg(TRACE, "low=%d high=%d", rssi_low, rssi_high);
56217dd56ea4SAlvin Šipraga 
56227dd56ea4SAlvin Šipraga 	ifp = netdev_priv(ndev);
56237dd56ea4SAlvin Šipraga 	vif = ifp->vif;
56247dd56ea4SAlvin Šipraga 
56257dd56ea4SAlvin Šipraga 	if (rssi_low != vif->cqm_rssi_low || rssi_high != vif->cqm_rssi_high) {
56267dd56ea4SAlvin Šipraga 		/* The firmware will send an event when the RSSI is less than or
56277dd56ea4SAlvin Šipraga 		 * equal to a configured level and the previous RSSI event was
56287dd56ea4SAlvin Šipraga 		 * less than or equal to a different level. Set a third level
56297dd56ea4SAlvin Šipraga 		 * so that we also detect the transition from rssi <= rssi_high
56307dd56ea4SAlvin Šipraga 		 * to rssi > rssi_high.
56317dd56ea4SAlvin Šipraga 		 */
56327dd56ea4SAlvin Šipraga 		struct brcmf_rssi_event_le config = {
56337dd56ea4SAlvin Šipraga 			.rate_limit_msec = cpu_to_le32(0),
56347dd56ea4SAlvin Šipraga 			.rssi_level_num = 3,
56357dd56ea4SAlvin Šipraga 			.rssi_levels = {
56367dd56ea4SAlvin Šipraga 				clamp_val(rssi_low, S8_MIN, S8_MAX - 2),
56377dd56ea4SAlvin Šipraga 				clamp_val(rssi_high, S8_MIN + 1, S8_MAX - 1),
56387dd56ea4SAlvin Šipraga 				S8_MAX,
56397dd56ea4SAlvin Šipraga 			},
56407dd56ea4SAlvin Šipraga 		};
56417dd56ea4SAlvin Šipraga 
56427dd56ea4SAlvin Šipraga 		err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &config,
56437dd56ea4SAlvin Šipraga 					       sizeof(config));
56447dd56ea4SAlvin Šipraga 		if (err) {
56457dd56ea4SAlvin Šipraga 			err = -EINVAL;
56467dd56ea4SAlvin Šipraga 		} else {
56477dd56ea4SAlvin Šipraga 			vif->cqm_rssi_low = rssi_low;
56487dd56ea4SAlvin Šipraga 			vif->cqm_rssi_high = rssi_high;
56497dd56ea4SAlvin Šipraga 		}
56507dd56ea4SAlvin Šipraga 	}
56517dd56ea4SAlvin Šipraga 
56527dd56ea4SAlvin Šipraga 	return err;
56537dd56ea4SAlvin Šipraga }
565405491d2cSKalle Valo 
565505491d2cSKalle Valo static int
brcmf_cfg80211_cancel_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,u64 cookie)565605491d2cSKalle Valo brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
565705491d2cSKalle Valo 					struct wireless_dev *wdev,
565805491d2cSKalle Valo 					u64 cookie)
565905491d2cSKalle Valo {
566005491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
566116e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
566205491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
566305491d2cSKalle Valo 	int err = 0;
566405491d2cSKalle Valo 
566505491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
566605491d2cSKalle Valo 
566705491d2cSKalle Valo 	vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
566805491d2cSKalle Valo 	if (vif == NULL) {
566916e64676SRafał Miłecki 		bphy_err(drvr, "No p2p device available for probe response\n");
567005491d2cSKalle Valo 		err = -ENODEV;
567105491d2cSKalle Valo 		goto exit;
567205491d2cSKalle Valo 	}
567305491d2cSKalle Valo 	brcmf_p2p_cancel_remain_on_channel(vif->ifp);
567405491d2cSKalle Valo exit:
567505491d2cSKalle Valo 	return err;
567605491d2cSKalle Valo }
567705491d2cSKalle Valo 
brcmf_cfg80211_get_channel(struct wiphy * wiphy,struct wireless_dev * wdev,unsigned int link_id,struct cfg80211_chan_def * chandef)5678ee6e7aa3SRafał Miłecki static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
5679ee6e7aa3SRafał Miłecki 				      struct wireless_dev *wdev,
56807b0a0e3cSJohannes Berg 				      unsigned int link_id,
5681ee6e7aa3SRafał Miłecki 				      struct cfg80211_chan_def *chandef)
5682ee6e7aa3SRafał Miłecki {
5683ee6e7aa3SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5684ee6e7aa3SRafał Miłecki 	struct net_device *ndev = wdev->netdev;
568516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
5686ee6e7aa3SRafał Miłecki 	struct brcmu_chan ch;
5687ee6e7aa3SRafał Miłecki 	enum nl80211_band band = 0;
5688ee6e7aa3SRafał Miłecki 	enum nl80211_chan_width width = 0;
5689ee6e7aa3SRafał Miłecki 	u32 chanspec;
5690ee6e7aa3SRafał Miłecki 	int freq, err;
5691ee6e7aa3SRafał Miłecki 
56921ac11ae9SArend van Spriel 	if (!ndev || drvr->bus_if->state != BRCMF_BUS_UP)
5693ee6e7aa3SRafał Miłecki 		return -ENODEV;
5694ee6e7aa3SRafał Miłecki 
56951ac11ae9SArend van Spriel 	err = brcmf_fil_iovar_int_get(netdev_priv(ndev), "chanspec", &chanspec);
5696ee6e7aa3SRafał Miłecki 	if (err) {
569716e64676SRafał Miłecki 		bphy_err(drvr, "chanspec failed (%d)\n", err);
5698ee6e7aa3SRafał Miłecki 		return err;
5699ee6e7aa3SRafał Miłecki 	}
5700ee6e7aa3SRafał Miłecki 
5701ee6e7aa3SRafał Miłecki 	ch.chspec = chanspec;
5702ee6e7aa3SRafał Miłecki 	cfg->d11inf.decchspec(&ch);
5703ee6e7aa3SRafał Miłecki 
5704ee6e7aa3SRafał Miłecki 	switch (ch.band) {
5705ee6e7aa3SRafał Miłecki 	case BRCMU_CHAN_BAND_2G:
5706ee6e7aa3SRafał Miłecki 		band = NL80211_BAND_2GHZ;
5707ee6e7aa3SRafał Miłecki 		break;
5708ee6e7aa3SRafał Miłecki 	case BRCMU_CHAN_BAND_5G:
5709ee6e7aa3SRafał Miłecki 		band = NL80211_BAND_5GHZ;
5710ee6e7aa3SRafał Miłecki 		break;
5711ee6e7aa3SRafał Miłecki 	}
5712ee6e7aa3SRafał Miłecki 
5713ee6e7aa3SRafał Miłecki 	switch (ch.bw) {
5714ee6e7aa3SRafał Miłecki 	case BRCMU_CHAN_BW_80:
5715ee6e7aa3SRafał Miłecki 		width = NL80211_CHAN_WIDTH_80;
5716ee6e7aa3SRafał Miłecki 		break;
5717ee6e7aa3SRafał Miłecki 	case BRCMU_CHAN_BW_40:
5718ee6e7aa3SRafał Miłecki 		width = NL80211_CHAN_WIDTH_40;
5719ee6e7aa3SRafał Miłecki 		break;
5720ee6e7aa3SRafał Miłecki 	case BRCMU_CHAN_BW_20:
5721ee6e7aa3SRafał Miłecki 		width = NL80211_CHAN_WIDTH_20;
5722ee6e7aa3SRafał Miłecki 		break;
5723ee6e7aa3SRafał Miłecki 	case BRCMU_CHAN_BW_80P80:
5724ee6e7aa3SRafał Miłecki 		width = NL80211_CHAN_WIDTH_80P80;
5725ee6e7aa3SRafał Miłecki 		break;
5726ee6e7aa3SRafał Miłecki 	case BRCMU_CHAN_BW_160:
5727ee6e7aa3SRafał Miłecki 		width = NL80211_CHAN_WIDTH_160;
5728ee6e7aa3SRafał Miłecki 		break;
5729ee6e7aa3SRafał Miłecki 	}
5730ee6e7aa3SRafał Miłecki 
5731ee6e7aa3SRafał Miłecki 	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
5732ee6e7aa3SRafał Miłecki 	chandef->chan = ieee80211_get_channel(wiphy, freq);
5733ee6e7aa3SRafał Miłecki 	chandef->width = width;
5734ee6e7aa3SRafał Miłecki 	chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);
5735ee6e7aa3SRafał Miłecki 	chandef->center_freq2 = 0;
5736ee6e7aa3SRafał Miłecki 
5737ee6e7aa3SRafał Miłecki 	return 0;
5738ee6e7aa3SRafał Miłecki }
5739ee6e7aa3SRafał Miłecki 
brcmf_cfg80211_crit_proto_start(struct wiphy * wiphy,struct wireless_dev * wdev,enum nl80211_crit_proto_id proto,u16 duration)574005491d2cSKalle Valo static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
574105491d2cSKalle Valo 					   struct wireless_dev *wdev,
574205491d2cSKalle Valo 					   enum nl80211_crit_proto_id proto,
574305491d2cSKalle Valo 					   u16 duration)
574405491d2cSKalle Valo {
574505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
574605491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
574705491d2cSKalle Valo 
574805491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
574905491d2cSKalle Valo 
575005491d2cSKalle Valo 	/* only DHCP support for now */
575105491d2cSKalle Valo 	if (proto != NL80211_CRIT_PROTO_DHCP)
575205491d2cSKalle Valo 		return -EINVAL;
575305491d2cSKalle Valo 
575405491d2cSKalle Valo 	/* suppress and abort scanning */
575505491d2cSKalle Valo 	set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
575605491d2cSKalle Valo 	brcmf_abort_scanning(cfg);
575705491d2cSKalle Valo 
575805491d2cSKalle Valo 	return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
575905491d2cSKalle Valo }
576005491d2cSKalle Valo 
brcmf_cfg80211_crit_proto_stop(struct wiphy * wiphy,struct wireless_dev * wdev)576105491d2cSKalle Valo static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
576205491d2cSKalle Valo 					   struct wireless_dev *wdev)
576305491d2cSKalle Valo {
576405491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
576505491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
576605491d2cSKalle Valo 
576705491d2cSKalle Valo 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
576805491d2cSKalle Valo 
576905491d2cSKalle Valo 	brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
577005491d2cSKalle Valo 	clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
577105491d2cSKalle Valo }
577205491d2cSKalle Valo 
577305491d2cSKalle Valo static s32
brcmf_notify_tdls_peer_event(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)577405491d2cSKalle Valo brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
577505491d2cSKalle Valo 			     const struct brcmf_event_msg *e, void *data)
577605491d2cSKalle Valo {
577705491d2cSKalle Valo 	switch (e->reason) {
577805491d2cSKalle Valo 	case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
577905491d2cSKalle Valo 		brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
578005491d2cSKalle Valo 		break;
578105491d2cSKalle Valo 	case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
578205491d2cSKalle Valo 		brcmf_dbg(TRACE, "TDLS Peer Connected\n");
578305491d2cSKalle Valo 		brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
578405491d2cSKalle Valo 		break;
578505491d2cSKalle Valo 	case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
578605491d2cSKalle Valo 		brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
578705491d2cSKalle Valo 		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
578805491d2cSKalle Valo 		break;
578905491d2cSKalle Valo 	}
579005491d2cSKalle Valo 
579105491d2cSKalle Valo 	return 0;
579205491d2cSKalle Valo }
579305491d2cSKalle Valo 
brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)579405491d2cSKalle Valo static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
579505491d2cSKalle Valo {
579605491d2cSKalle Valo 	int ret;
579705491d2cSKalle Valo 
579805491d2cSKalle Valo 	switch (oper) {
579905491d2cSKalle Valo 	case NL80211_TDLS_DISCOVERY_REQ:
580005491d2cSKalle Valo 		ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
580105491d2cSKalle Valo 		break;
580205491d2cSKalle Valo 	case NL80211_TDLS_SETUP:
580305491d2cSKalle Valo 		ret = BRCMF_TDLS_MANUAL_EP_CREATE;
580405491d2cSKalle Valo 		break;
580505491d2cSKalle Valo 	case NL80211_TDLS_TEARDOWN:
580605491d2cSKalle Valo 		ret = BRCMF_TDLS_MANUAL_EP_DELETE;
580705491d2cSKalle Valo 		break;
580805491d2cSKalle Valo 	default:
580905491d2cSKalle Valo 		brcmf_err("unsupported operation: %d\n", oper);
581005491d2cSKalle Valo 		ret = -EOPNOTSUPP;
581105491d2cSKalle Valo 	}
581205491d2cSKalle Valo 	return ret;
581305491d2cSKalle Valo }
581405491d2cSKalle Valo 
brcmf_cfg80211_tdls_oper(struct wiphy * wiphy,struct net_device * ndev,const u8 * peer,enum nl80211_tdls_operation oper)581505491d2cSKalle Valo static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
581605491d2cSKalle Valo 				    struct net_device *ndev, const u8 *peer,
581705491d2cSKalle Valo 				    enum nl80211_tdls_operation oper)
581805491d2cSKalle Valo {
581916e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
582016e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
582105491d2cSKalle Valo 	struct brcmf_if *ifp;
582205491d2cSKalle Valo 	struct brcmf_tdls_iovar_le info;
582305491d2cSKalle Valo 	int ret = 0;
582405491d2cSKalle Valo 
582505491d2cSKalle Valo 	ret = brcmf_convert_nl80211_tdls_oper(oper);
582605491d2cSKalle Valo 	if (ret < 0)
582705491d2cSKalle Valo 		return ret;
582805491d2cSKalle Valo 
582905491d2cSKalle Valo 	ifp = netdev_priv(ndev);
583005491d2cSKalle Valo 	memset(&info, 0, sizeof(info));
583105491d2cSKalle Valo 	info.mode = (u8)ret;
583205491d2cSKalle Valo 	if (peer)
583305491d2cSKalle Valo 		memcpy(info.ea, peer, ETH_ALEN);
583405491d2cSKalle Valo 
583505491d2cSKalle Valo 	ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
583605491d2cSKalle Valo 				       &info, sizeof(info));
583705491d2cSKalle Valo 	if (ret < 0)
583816e64676SRafał Miłecki 		bphy_err(drvr, "tdls_endpoint iovar failed: ret=%d\n", ret);
583905491d2cSKalle Valo 
584005491d2cSKalle Valo 	return ret;
584105491d2cSKalle Valo }
584205491d2cSKalle Valo 
58432a2a5d18SArend Van Spriel static int
brcmf_cfg80211_update_conn_params(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme,u32 changed)58442a2a5d18SArend Van Spriel brcmf_cfg80211_update_conn_params(struct wiphy *wiphy,
58452a2a5d18SArend Van Spriel 				  struct net_device *ndev,
58462a2a5d18SArend Van Spriel 				  struct cfg80211_connect_params *sme,
58472a2a5d18SArend Van Spriel 				  u32 changed)
58482a2a5d18SArend Van Spriel {
584916e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
585016e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
58512a2a5d18SArend Van Spriel 	struct brcmf_if *ifp;
58522a2a5d18SArend Van Spriel 	int err;
58532a2a5d18SArend Van Spriel 
58542a2a5d18SArend Van Spriel 	if (!(changed & UPDATE_ASSOC_IES))
58552a2a5d18SArend Van Spriel 		return 0;
58562a2a5d18SArend Van Spriel 
58572a2a5d18SArend Van Spriel 	ifp = netdev_priv(ndev);
58582a2a5d18SArend Van Spriel 	err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
58592a2a5d18SArend Van Spriel 				    sme->ie, sme->ie_len);
58602a2a5d18SArend Van Spriel 	if (err)
586116e64676SRafał Miłecki 		bphy_err(drvr, "Set Assoc REQ IE Failed\n");
58622a2a5d18SArend Van Spriel 	else
58632a2a5d18SArend Van Spriel 		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
58642a2a5d18SArend Van Spriel 
58652a2a5d18SArend Van Spriel 	return err;
58662a2a5d18SArend Van Spriel }
58672a2a5d18SArend Van Spriel 
58685c22fb85SHante Meuleman #ifdef CONFIG_PM
58695c22fb85SHante Meuleman static int
brcmf_cfg80211_set_rekey_data(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_gtk_rekey_data * gtk)58705c22fb85SHante Meuleman brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
58715c22fb85SHante Meuleman 			      struct cfg80211_gtk_rekey_data *gtk)
58725c22fb85SHante Meuleman {
587316e64676SRafał Miłecki 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
587416e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
58755c22fb85SHante Meuleman 	struct brcmf_if *ifp = netdev_priv(ndev);
58765c22fb85SHante Meuleman 	struct brcmf_gtk_keyinfo_le gtk_le;
58775c22fb85SHante Meuleman 	int ret;
58785c22fb85SHante Meuleman 
58795c22fb85SHante Meuleman 	brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
58805c22fb85SHante Meuleman 
58815c22fb85SHante Meuleman 	memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
58825c22fb85SHante Meuleman 	memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
58835c22fb85SHante Meuleman 	memcpy(gtk_le.replay_counter, gtk->replay_ctr,
58845c22fb85SHante Meuleman 	       sizeof(gtk_le.replay_counter));
58855c22fb85SHante Meuleman 
58865c22fb85SHante Meuleman 	ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", &gtk_le,
58875c22fb85SHante Meuleman 				       sizeof(gtk_le));
58885c22fb85SHante Meuleman 	if (ret < 0)
588916e64676SRafał Miłecki 		bphy_err(drvr, "gtk_key_info iovar failed: ret=%d\n", ret);
58905c22fb85SHante Meuleman 
58915c22fb85SHante Meuleman 	return ret;
58925c22fb85SHante Meuleman }
58935c22fb85SHante Meuleman #endif
58945c22fb85SHante Meuleman 
brcmf_cfg80211_set_pmk(struct wiphy * wiphy,struct net_device * dev,const struct cfg80211_pmk_conf * conf)58952526ff21SArend van Spriel static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,
58962526ff21SArend van Spriel 				  const struct cfg80211_pmk_conf *conf)
58972526ff21SArend van Spriel {
58982526ff21SArend van Spriel 	struct brcmf_if *ifp;
58992526ff21SArend van Spriel 
59002526ff21SArend van Spriel 	brcmf_dbg(TRACE, "enter\n");
59012526ff21SArend van Spriel 
59022526ff21SArend van Spriel 	/* expect using firmware supplicant for 1X */
59032526ff21SArend van Spriel 	ifp = netdev_priv(dev);
59042526ff21SArend van Spriel 	if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
59052526ff21SArend van Spriel 		return -EINVAL;
59062526ff21SArend van Spriel 
590764d1519eSJohannes Berg 	if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN)
590864d1519eSJohannes Berg 		return -ERANGE;
590964d1519eSJohannes Berg 
59102526ff21SArend van Spriel 	return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);
59112526ff21SArend van Spriel }
59122526ff21SArend van Spriel 
brcmf_cfg80211_del_pmk(struct wiphy * wiphy,struct net_device * dev,const u8 * aa)59132526ff21SArend van Spriel static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,
59142526ff21SArend van Spriel 				  const u8 *aa)
59152526ff21SArend van Spriel {
59162526ff21SArend van Spriel 	struct brcmf_if *ifp;
59172526ff21SArend van Spriel 
59182526ff21SArend van Spriel 	brcmf_dbg(TRACE, "enter\n");
59192526ff21SArend van Spriel 	ifp = netdev_priv(dev);
59202526ff21SArend van Spriel 	if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
59212526ff21SArend van Spriel 		return -EINVAL;
59222526ff21SArend van Spriel 
59232526ff21SArend van Spriel 	return brcmf_set_pmk(ifp, NULL, 0);
59242526ff21SArend van Spriel }
59252526ff21SArend van Spriel 
59265c22fb85SHante Meuleman static struct cfg80211_ops brcmf_cfg80211_ops = {
592705491d2cSKalle Valo 	.add_virtual_intf = brcmf_cfg80211_add_iface,
592805491d2cSKalle Valo 	.del_virtual_intf = brcmf_cfg80211_del_iface,
592905491d2cSKalle Valo 	.change_virtual_intf = brcmf_cfg80211_change_iface,
593005491d2cSKalle Valo 	.scan = brcmf_cfg80211_scan,
593105491d2cSKalle Valo 	.set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
593205491d2cSKalle Valo 	.join_ibss = brcmf_cfg80211_join_ibss,
593305491d2cSKalle Valo 	.leave_ibss = brcmf_cfg80211_leave_ibss,
593405491d2cSKalle Valo 	.get_station = brcmf_cfg80211_get_station,
593505491d2cSKalle Valo 	.dump_station = brcmf_cfg80211_dump_station,
593605491d2cSKalle Valo 	.set_tx_power = brcmf_cfg80211_set_tx_power,
593705491d2cSKalle Valo 	.get_tx_power = brcmf_cfg80211_get_tx_power,
593805491d2cSKalle Valo 	.add_key = brcmf_cfg80211_add_key,
593905491d2cSKalle Valo 	.del_key = brcmf_cfg80211_del_key,
594005491d2cSKalle Valo 	.get_key = brcmf_cfg80211_get_key,
594105491d2cSKalle Valo 	.set_default_key = brcmf_cfg80211_config_default_key,
594205491d2cSKalle Valo 	.set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
594305491d2cSKalle Valo 	.set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
594405491d2cSKalle Valo 	.connect = brcmf_cfg80211_connect,
594505491d2cSKalle Valo 	.disconnect = brcmf_cfg80211_disconnect,
594605491d2cSKalle Valo 	.suspend = brcmf_cfg80211_suspend,
594705491d2cSKalle Valo 	.resume = brcmf_cfg80211_resume,
594805491d2cSKalle Valo 	.set_pmksa = brcmf_cfg80211_set_pmksa,
594905491d2cSKalle Valo 	.del_pmksa = brcmf_cfg80211_del_pmksa,
595005491d2cSKalle Valo 	.flush_pmksa = brcmf_cfg80211_flush_pmksa,
595105491d2cSKalle Valo 	.start_ap = brcmf_cfg80211_start_ap,
595205491d2cSKalle Valo 	.stop_ap = brcmf_cfg80211_stop_ap,
595305491d2cSKalle Valo 	.change_beacon = brcmf_cfg80211_change_beacon,
595405491d2cSKalle Valo 	.del_station = brcmf_cfg80211_del_station,
595505491d2cSKalle Valo 	.change_station = brcmf_cfg80211_change_station,
595605491d2cSKalle Valo 	.sched_scan_start = brcmf_cfg80211_sched_scan_start,
595705491d2cSKalle Valo 	.sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
59586cd536feSJohannes Berg 	.update_mgmt_frame_registrations =
59596cd536feSJohannes Berg 		brcmf_cfg80211_update_mgmt_frame_registrations,
596005491d2cSKalle Valo 	.mgmt_tx = brcmf_cfg80211_mgmt_tx,
59617dd56ea4SAlvin Šipraga 	.set_cqm_rssi_range_config = brcmf_cfg80211_set_cqm_rssi_range_config,
596205491d2cSKalle Valo 	.remain_on_channel = brcmf_p2p_remain_on_channel,
596305491d2cSKalle Valo 	.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
5964ee6e7aa3SRafał Miłecki 	.get_channel = brcmf_cfg80211_get_channel,
596505491d2cSKalle Valo 	.start_p2p_device = brcmf_p2p_start_device,
596605491d2cSKalle Valo 	.stop_p2p_device = brcmf_p2p_stop_device,
596705491d2cSKalle Valo 	.crit_proto_start = brcmf_cfg80211_crit_proto_start,
596805491d2cSKalle Valo 	.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
596905491d2cSKalle Valo 	.tdls_oper = brcmf_cfg80211_tdls_oper,
59702a2a5d18SArend Van Spriel 	.update_connect_params = brcmf_cfg80211_update_conn_params,
59712526ff21SArend van Spriel 	.set_pmk = brcmf_cfg80211_set_pmk,
59722526ff21SArend van Spriel 	.del_pmk = brcmf_cfg80211_del_pmk,
597305491d2cSKalle Valo };
597405491d2cSKalle Valo 
brcmf_cfg80211_get_ops(struct brcmf_mp_device * settings)59758c892df4SStijn Tintel struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings)
5976856d5a01SArend Van Spriel {
59778c892df4SStijn Tintel 	struct cfg80211_ops *ops;
59788c892df4SStijn Tintel 
59798c892df4SStijn Tintel 	ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),
5980856d5a01SArend Van Spriel 		       GFP_KERNEL);
59818c892df4SStijn Tintel 
59828c892df4SStijn Tintel 	if (ops && settings->roamoff)
59838c892df4SStijn Tintel 		ops->update_connect_params = NULL;
59848c892df4SStijn Tintel 
59858c892df4SStijn Tintel 	return ops;
5986856d5a01SArend Van Spriel }
5987856d5a01SArend Van Spriel 
brcmf_alloc_vif(struct brcmf_cfg80211_info * cfg,enum nl80211_iftype type)598805491d2cSKalle Valo struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
598926072330SRafał Miłecki 					   enum nl80211_iftype type)
599005491d2cSKalle Valo {
599105491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif_walk;
599205491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
599305491d2cSKalle Valo 	bool mbss;
59942635853cSWright Feng 	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
599505491d2cSKalle Valo 
599605491d2cSKalle Valo 	brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
599705491d2cSKalle Valo 		  sizeof(*vif));
599805491d2cSKalle Valo 	vif = kzalloc(sizeof(*vif), GFP_KERNEL);
599905491d2cSKalle Valo 	if (!vif)
600005491d2cSKalle Valo 		return ERR_PTR(-ENOMEM);
600105491d2cSKalle Valo 
600205491d2cSKalle Valo 	vif->wdev.wiphy = cfg->wiphy;
600305491d2cSKalle Valo 	vif->wdev.iftype = type;
600405491d2cSKalle Valo 
600505491d2cSKalle Valo 	brcmf_init_prof(&vif->profile);
600605491d2cSKalle Valo 
60072635853cSWright Feng 	if (type == NL80211_IFTYPE_AP &&
60082635853cSWright Feng 	    brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
600905491d2cSKalle Valo 		mbss = false;
601005491d2cSKalle Valo 		list_for_each_entry(vif_walk, &cfg->vif_list, list) {
601105491d2cSKalle Valo 			if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
601205491d2cSKalle Valo 				mbss = true;
601305491d2cSKalle Valo 				break;
601405491d2cSKalle Valo 			}
601505491d2cSKalle Valo 		}
601605491d2cSKalle Valo 		vif->mbss = mbss;
601705491d2cSKalle Valo 	}
601805491d2cSKalle Valo 
601905491d2cSKalle Valo 	list_add_tail(&vif->list, &cfg->vif_list);
602005491d2cSKalle Valo 	return vif;
602105491d2cSKalle Valo }
602205491d2cSKalle Valo 
brcmf_free_vif(struct brcmf_cfg80211_vif * vif)602305491d2cSKalle Valo void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
602405491d2cSKalle Valo {
602505491d2cSKalle Valo 	list_del(&vif->list);
602605491d2cSKalle Valo 	kfree(vif);
602705491d2cSKalle Valo }
602805491d2cSKalle Valo 
brcmf_cfg80211_free_netdev(struct net_device * ndev)602905491d2cSKalle Valo void brcmf_cfg80211_free_netdev(struct net_device *ndev)
603005491d2cSKalle Valo {
603105491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
603205491d2cSKalle Valo 	struct brcmf_if *ifp;
603305491d2cSKalle Valo 
603405491d2cSKalle Valo 	ifp = netdev_priv(ndev);
603505491d2cSKalle Valo 	vif = ifp->vif;
603605491d2cSKalle Valo 
603705491d2cSKalle Valo 	if (vif)
603805491d2cSKalle Valo 		brcmf_free_vif(vif);
603905491d2cSKalle Valo }
604005491d2cSKalle Valo 
brcmf_is_linkup(struct brcmf_cfg80211_vif * vif,const struct brcmf_event_msg * e)6041b8a64f0eSArend van Spriel static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
6042b8a64f0eSArend van Spriel 			    const struct brcmf_event_msg *e)
604305491d2cSKalle Valo {
604405491d2cSKalle Valo 	u32 event = e->event_code;
604505491d2cSKalle Valo 	u32 status = e->status;
604605491d2cSKalle Valo 
6047b2fe11f0SChung-Hsien Hsu 	if ((vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK ||
6048b2fe11f0SChung-Hsien Hsu 	     vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_SAE) &&
60492526ff21SArend van Spriel 	    event == BRCMF_E_PSK_SUP &&
6050b8a64f0eSArend van Spriel 	    status == BRCMF_E_STATUS_FWSUP_COMPLETED)
6051b8a64f0eSArend van Spriel 		set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
605205491d2cSKalle Valo 	if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
605305491d2cSKalle Valo 		brcmf_dbg(CONN, "Processing set ssid\n");
6054b8a64f0eSArend van Spriel 		memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
60553b1e0a7bSChung-Hsien Hsu 		if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&
60563b1e0a7bSChung-Hsien Hsu 		    vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)
605705491d2cSKalle Valo 			return true;
6058b8a64f0eSArend van Spriel 
6059b8a64f0eSArend van Spriel 		set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
606005491d2cSKalle Valo 	}
606105491d2cSKalle Valo 
6062b8a64f0eSArend van Spriel 	if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) &&
6063b8a64f0eSArend van Spriel 	    test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) {
6064b8a64f0eSArend van Spriel 		clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
6065b8a64f0eSArend van Spriel 		clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
6066b8a64f0eSArend van Spriel 		return true;
6067b8a64f0eSArend van Spriel 	}
606805491d2cSKalle Valo 	return false;
606905491d2cSKalle Valo }
607005491d2cSKalle Valo 
brcmf_is_linkdown(struct brcmf_cfg80211_vif * vif,const struct brcmf_event_msg * e)6071e862a3e4SLuca Pesce static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif,
6072e862a3e4SLuca Pesce 			    const struct brcmf_event_msg *e)
607305491d2cSKalle Valo {
607405491d2cSKalle Valo 	u32 event = e->event_code;
607505491d2cSKalle Valo 	u16 flags = e->flags;
607605491d2cSKalle Valo 
607705491d2cSKalle Valo 	if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
607805491d2cSKalle Valo 	    (event == BRCMF_E_DISASSOC_IND) ||
607905491d2cSKalle Valo 	    ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
608005491d2cSKalle Valo 		brcmf_dbg(CONN, "Processing link down\n");
6081e862a3e4SLuca Pesce 		clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
6082e862a3e4SLuca Pesce 		clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
608305491d2cSKalle Valo 		return true;
608405491d2cSKalle Valo 	}
608505491d2cSKalle Valo 	return false;
608605491d2cSKalle Valo }
608705491d2cSKalle Valo 
brcmf_is_nonetwork(struct brcmf_cfg80211_info * cfg,const struct brcmf_event_msg * e)608805491d2cSKalle Valo static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
608905491d2cSKalle Valo 			       const struct brcmf_event_msg *e)
609005491d2cSKalle Valo {
609105491d2cSKalle Valo 	u32 event = e->event_code;
609205491d2cSKalle Valo 	u32 status = e->status;
609305491d2cSKalle Valo 
609405491d2cSKalle Valo 	if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
609505491d2cSKalle Valo 		brcmf_dbg(CONN, "Processing Link %s & no network found\n",
609605491d2cSKalle Valo 			  e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
609705491d2cSKalle Valo 		return true;
609805491d2cSKalle Valo 	}
609905491d2cSKalle Valo 
610005491d2cSKalle Valo 	if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
610105491d2cSKalle Valo 		brcmf_dbg(CONN, "Processing connecting & no network found\n");
610205491d2cSKalle Valo 		return true;
610305491d2cSKalle Valo 	}
610405491d2cSKalle Valo 
6105b8a64f0eSArend van Spriel 	if (event == BRCMF_E_PSK_SUP &&
6106b8a64f0eSArend van Spriel 	    status != BRCMF_E_STATUS_FWSUP_COMPLETED) {
6107b8a64f0eSArend van Spriel 		brcmf_dbg(CONN, "Processing failed supplicant state: %u\n",
6108b8a64f0eSArend van Spriel 			  status);
6109b8a64f0eSArend van Spriel 		return true;
6110b8a64f0eSArend van Spriel 	}
6111b8a64f0eSArend van Spriel 
611205491d2cSKalle Valo 	return false;
611305491d2cSKalle Valo }
611405491d2cSKalle Valo 
brcmf_clear_assoc_ies(struct brcmf_cfg80211_info * cfg)611505491d2cSKalle Valo static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
611605491d2cSKalle Valo {
611705491d2cSKalle Valo 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
611805491d2cSKalle Valo 
611905491d2cSKalle Valo 	kfree(conn_info->req_ie);
612005491d2cSKalle Valo 	conn_info->req_ie = NULL;
612105491d2cSKalle Valo 	conn_info->req_ie_len = 0;
612205491d2cSKalle Valo 	kfree(conn_info->resp_ie);
612305491d2cSKalle Valo 	conn_info->resp_ie = NULL;
612405491d2cSKalle Valo 	conn_info->resp_ie_len = 0;
612505491d2cSKalle Valo }
612605491d2cSKalle Valo 
brcmf_map_prio_to_prec(void * config,u8 prio)612792072e5fSSaravanan Shanmugham u8 brcmf_map_prio_to_prec(void *config, u8 prio)
612892072e5fSSaravanan Shanmugham {
612992072e5fSSaravanan Shanmugham 	struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
613092072e5fSSaravanan Shanmugham 
613192072e5fSSaravanan Shanmugham 	if (!cfg)
613292072e5fSSaravanan Shanmugham 		return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
613392072e5fSSaravanan Shanmugham 		       (prio ^ 2) : prio;
613492072e5fSSaravanan Shanmugham 
613592072e5fSSaravanan Shanmugham 	/* For those AC(s) with ACM flag set to 1, convert its 4-level priority
613692072e5fSSaravanan Shanmugham 	 * to an 8-level precedence which is the same as BE's
613792072e5fSSaravanan Shanmugham 	 */
613892072e5fSSaravanan Shanmugham 	if (prio > PRIO_8021D_EE &&
613992072e5fSSaravanan Shanmugham 	    cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE])
614092072e5fSSaravanan Shanmugham 		return cfg->ac_priority[prio] * 2;
614192072e5fSSaravanan Shanmugham 
614292072e5fSSaravanan Shanmugham 	/* Conversion of 4-level priority to 8-level precedence */
614392072e5fSSaravanan Shanmugham 	if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK ||
614492072e5fSSaravanan Shanmugham 	    prio == PRIO_8021D_CL || prio == PRIO_8021D_VO)
614592072e5fSSaravanan Shanmugham 		return cfg->ac_priority[prio] * 2;
614692072e5fSSaravanan Shanmugham 	else
614792072e5fSSaravanan Shanmugham 		return cfg->ac_priority[prio] * 2 + 1;
614892072e5fSSaravanan Shanmugham }
614992072e5fSSaravanan Shanmugham 
brcmf_map_prio_to_aci(void * config,u8 prio)615092072e5fSSaravanan Shanmugham u8 brcmf_map_prio_to_aci(void *config, u8 prio)
615192072e5fSSaravanan Shanmugham {
615292072e5fSSaravanan Shanmugham 	/* Prio here refers to the 802.1d priority in range of 0 to 7.
615392072e5fSSaravanan Shanmugham 	 * ACI here refers to the WLAN AC Index in range of 0 to 3.
615492072e5fSSaravanan Shanmugham 	 * This function will return ACI corresponding to input prio.
615592072e5fSSaravanan Shanmugham 	 */
615692072e5fSSaravanan Shanmugham 	struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
615792072e5fSSaravanan Shanmugham 
615892072e5fSSaravanan Shanmugham 	if (cfg)
615992072e5fSSaravanan Shanmugham 		return cfg->ac_priority[prio];
616092072e5fSSaravanan Shanmugham 
616192072e5fSSaravanan Shanmugham 	return prio;
616292072e5fSSaravanan Shanmugham }
616392072e5fSSaravanan Shanmugham 
brcmf_init_wmm_prio(u8 * priority)616492072e5fSSaravanan Shanmugham static void brcmf_init_wmm_prio(u8 *priority)
616592072e5fSSaravanan Shanmugham {
616692072e5fSSaravanan Shanmugham 	/* Initialize AC priority array to default
616792072e5fSSaravanan Shanmugham 	 * 802.1d priority as per following table:
616892072e5fSSaravanan Shanmugham 	 * 802.1d prio 0,3 maps to BE
616992072e5fSSaravanan Shanmugham 	 * 802.1d prio 1,2 maps to BK
617092072e5fSSaravanan Shanmugham 	 * 802.1d prio 4,5 maps to VI
617192072e5fSSaravanan Shanmugham 	 * 802.1d prio 6,7 maps to VO
617292072e5fSSaravanan Shanmugham 	 */
617392072e5fSSaravanan Shanmugham 	priority[0] = BRCMF_FWS_FIFO_AC_BE;
617492072e5fSSaravanan Shanmugham 	priority[3] = BRCMF_FWS_FIFO_AC_BE;
617592072e5fSSaravanan Shanmugham 	priority[1] = BRCMF_FWS_FIFO_AC_BK;
617692072e5fSSaravanan Shanmugham 	priority[2] = BRCMF_FWS_FIFO_AC_BK;
617792072e5fSSaravanan Shanmugham 	priority[4] = BRCMF_FWS_FIFO_AC_VI;
617892072e5fSSaravanan Shanmugham 	priority[5] = BRCMF_FWS_FIFO_AC_VI;
617992072e5fSSaravanan Shanmugham 	priority[6] = BRCMF_FWS_FIFO_AC_VO;
618092072e5fSSaravanan Shanmugham 	priority[7] = BRCMF_FWS_FIFO_AC_VO;
618192072e5fSSaravanan Shanmugham }
618292072e5fSSaravanan Shanmugham 
brcmf_wifi_prioritize_acparams(const struct brcmf_cfg80211_edcf_acparam * acp,u8 * priority)618392072e5fSSaravanan Shanmugham static void brcmf_wifi_prioritize_acparams(const
618492072e5fSSaravanan Shanmugham 	struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)
618592072e5fSSaravanan Shanmugham {
618692072e5fSSaravanan Shanmugham 	u8 aci;
618792072e5fSSaravanan Shanmugham 	u8 aifsn;
618892072e5fSSaravanan Shanmugham 	u8 ecwmin;
618992072e5fSSaravanan Shanmugham 	u8 ecwmax;
619092072e5fSSaravanan Shanmugham 	u8 acm;
619192072e5fSSaravanan Shanmugham 	u8 ranking_basis[EDCF_AC_COUNT];
619292072e5fSSaravanan Shanmugham 	u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */
619392072e5fSSaravanan Shanmugham 	u8 index;
619492072e5fSSaravanan Shanmugham 
619592072e5fSSaravanan Shanmugham 	for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) {
619692072e5fSSaravanan Shanmugham 		aifsn  = acp->ACI & EDCF_AIFSN_MASK;
619792072e5fSSaravanan Shanmugham 		acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;
619892072e5fSSaravanan Shanmugham 		ecwmin = acp->ECW & EDCF_ECWMIN_MASK;
619992072e5fSSaravanan Shanmugham 		ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
620092072e5fSSaravanan Shanmugham 		brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n",
620192072e5fSSaravanan Shanmugham 			  aci, aifsn, acm, ecwmin, ecwmax);
620292072e5fSSaravanan Shanmugham 		/* Default AC_VO will be the lowest ranking value */
620392072e5fSSaravanan Shanmugham 		ranking_basis[aci] = aifsn + ecwmin + ecwmax;
620492072e5fSSaravanan Shanmugham 		/* Initialise priority starting at 0 (AC_BE) */
620592072e5fSSaravanan Shanmugham 		aci_prio[aci] = 0;
620692072e5fSSaravanan Shanmugham 
620792072e5fSSaravanan Shanmugham 		/* If ACM is set, STA can't use this AC as per 802.11.
620892072e5fSSaravanan Shanmugham 		 * Change the ranking to BE
620992072e5fSSaravanan Shanmugham 		 */
621092072e5fSSaravanan Shanmugham 		if (aci != AC_BE && aci != AC_BK && acm == 1)
621192072e5fSSaravanan Shanmugham 			ranking_basis[aci] = ranking_basis[AC_BE];
621292072e5fSSaravanan Shanmugham 	}
621392072e5fSSaravanan Shanmugham 
621492072e5fSSaravanan Shanmugham 	/* Ranking method which works for AC priority
621592072e5fSSaravanan Shanmugham 	 * swapping when values for cwmin, cwmax and aifsn are varied
621692072e5fSSaravanan Shanmugham 	 * Compare each aci_prio against each other aci_prio
621792072e5fSSaravanan Shanmugham 	 */
621892072e5fSSaravanan Shanmugham 	for (aci = 0; aci < EDCF_AC_COUNT; aci++) {
621992072e5fSSaravanan Shanmugham 		for (index = 0; index < EDCF_AC_COUNT; index++) {
622092072e5fSSaravanan Shanmugham 			if (index != aci) {
622192072e5fSSaravanan Shanmugham 				/* Smaller ranking value has higher priority,
622292072e5fSSaravanan Shanmugham 				 * so increment priority for each ACI which has
622392072e5fSSaravanan Shanmugham 				 * a higher ranking value
622492072e5fSSaravanan Shanmugham 				 */
622592072e5fSSaravanan Shanmugham 				if (ranking_basis[aci] < ranking_basis[index])
622692072e5fSSaravanan Shanmugham 					aci_prio[aci]++;
622792072e5fSSaravanan Shanmugham 			}
622892072e5fSSaravanan Shanmugham 		}
622992072e5fSSaravanan Shanmugham 	}
623092072e5fSSaravanan Shanmugham 
623192072e5fSSaravanan Shanmugham 	/* By now, aci_prio[] will be in range of 0 to 3.
623292072e5fSSaravanan Shanmugham 	 * Use ACI prio to get the new priority value for
623392072e5fSSaravanan Shanmugham 	 * each 802.1d traffic type, in this range.
623492072e5fSSaravanan Shanmugham 	 */
623592072e5fSSaravanan Shanmugham 	if (!(aci_prio[AC_BE] == aci_prio[AC_BK] &&
623692072e5fSSaravanan Shanmugham 	      aci_prio[AC_BK] == aci_prio[AC_VI] &&
623792072e5fSSaravanan Shanmugham 	      aci_prio[AC_VI] == aci_prio[AC_VO])) {
623892072e5fSSaravanan Shanmugham 		/* 802.1d 0,3 maps to BE */
623992072e5fSSaravanan Shanmugham 		priority[0] = aci_prio[AC_BE];
624092072e5fSSaravanan Shanmugham 		priority[3] = aci_prio[AC_BE];
624192072e5fSSaravanan Shanmugham 
624292072e5fSSaravanan Shanmugham 		/* 802.1d 1,2 maps to BK */
624392072e5fSSaravanan Shanmugham 		priority[1] = aci_prio[AC_BK];
624492072e5fSSaravanan Shanmugham 		priority[2] = aci_prio[AC_BK];
624592072e5fSSaravanan Shanmugham 
624692072e5fSSaravanan Shanmugham 		/* 802.1d 4,5 maps to VO */
624792072e5fSSaravanan Shanmugham 		priority[4] = aci_prio[AC_VI];
624892072e5fSSaravanan Shanmugham 		priority[5] = aci_prio[AC_VI];
624992072e5fSSaravanan Shanmugham 
625092072e5fSSaravanan Shanmugham 		/* 802.1d 6,7 maps to VO */
625192072e5fSSaravanan Shanmugham 		priority[6] = aci_prio[AC_VO];
625292072e5fSSaravanan Shanmugham 		priority[7] = aci_prio[AC_VO];
625392072e5fSSaravanan Shanmugham 	} else {
625492072e5fSSaravanan Shanmugham 		/* Initialize to default priority */
625592072e5fSSaravanan Shanmugham 		brcmf_init_wmm_prio(priority);
625692072e5fSSaravanan Shanmugham 	}
625792072e5fSSaravanan Shanmugham 
625892072e5fSSaravanan Shanmugham 	brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",
625992072e5fSSaravanan Shanmugham 		  priority[0], priority[1], priority[2], priority[3]);
626092072e5fSSaravanan Shanmugham 
626192072e5fSSaravanan Shanmugham 	brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n",
626292072e5fSSaravanan Shanmugham 		  priority[4], priority[5], priority[6], priority[7]);
626392072e5fSSaravanan Shanmugham }
626492072e5fSSaravanan Shanmugham 
brcmf_get_assoc_ies(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)626505491d2cSKalle Valo static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
626605491d2cSKalle Valo 			       struct brcmf_if *ifp)
626705491d2cSKalle Valo {
626816e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
626905491d2cSKalle Valo 	struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
627005491d2cSKalle Valo 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
627192072e5fSSaravanan Shanmugham 	struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT];
627205491d2cSKalle Valo 	u32 req_len;
627305491d2cSKalle Valo 	u32 resp_len;
627405491d2cSKalle Valo 	s32 err = 0;
627505491d2cSKalle Valo 
627605491d2cSKalle Valo 	brcmf_clear_assoc_ies(cfg);
627705491d2cSKalle Valo 
627805491d2cSKalle Valo 	err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
627905491d2cSKalle Valo 				       cfg->extra_buf, WL_ASSOC_INFO_MAX);
628005491d2cSKalle Valo 	if (err) {
628116e64676SRafał Miłecki 		bphy_err(drvr, "could not get assoc info (%d)\n", err);
628205491d2cSKalle Valo 		return err;
628305491d2cSKalle Valo 	}
628405491d2cSKalle Valo 	assoc_info =
628505491d2cSKalle Valo 		(struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
628605491d2cSKalle Valo 	req_len = le32_to_cpu(assoc_info->req_len);
628705491d2cSKalle Valo 	resp_len = le32_to_cpu(assoc_info->resp_len);
62880da40e01SJisoo Jang 	if (req_len > WL_EXTRA_BUF_MAX || resp_len > WL_EXTRA_BUF_MAX) {
62890da40e01SJisoo Jang 		bphy_err(drvr, "invalid lengths in assoc info: req %u resp %u\n",
62900da40e01SJisoo Jang 			 req_len, resp_len);
62910da40e01SJisoo Jang 		return -EINVAL;
62920da40e01SJisoo Jang 	}
629305491d2cSKalle Valo 	if (req_len) {
629405491d2cSKalle Valo 		err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
629505491d2cSKalle Valo 					       cfg->extra_buf,
629605491d2cSKalle Valo 					       WL_ASSOC_INFO_MAX);
629705491d2cSKalle Valo 		if (err) {
629816e64676SRafał Miłecki 			bphy_err(drvr, "could not get assoc req (%d)\n", err);
629905491d2cSKalle Valo 			return err;
630005491d2cSKalle Valo 		}
630105491d2cSKalle Valo 		conn_info->req_ie_len = req_len;
630205491d2cSKalle Valo 		conn_info->req_ie =
630305491d2cSKalle Valo 		    kmemdup(cfg->extra_buf, conn_info->req_ie_len,
630405491d2cSKalle Valo 			    GFP_KERNEL);
630546953f97SKangjie Lu 		if (!conn_info->req_ie)
630646953f97SKangjie Lu 			conn_info->req_ie_len = 0;
630705491d2cSKalle Valo 	} else {
630805491d2cSKalle Valo 		conn_info->req_ie_len = 0;
630905491d2cSKalle Valo 		conn_info->req_ie = NULL;
631005491d2cSKalle Valo 	}
631105491d2cSKalle Valo 	if (resp_len) {
631205491d2cSKalle Valo 		err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
631305491d2cSKalle Valo 					       cfg->extra_buf,
631405491d2cSKalle Valo 					       WL_ASSOC_INFO_MAX);
631505491d2cSKalle Valo 		if (err) {
631616e64676SRafał Miłecki 			bphy_err(drvr, "could not get assoc resp (%d)\n", err);
631705491d2cSKalle Valo 			return err;
631805491d2cSKalle Valo 		}
631905491d2cSKalle Valo 		conn_info->resp_ie_len = resp_len;
632005491d2cSKalle Valo 		conn_info->resp_ie =
632105491d2cSKalle Valo 		    kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
632205491d2cSKalle Valo 			    GFP_KERNEL);
632346953f97SKangjie Lu 		if (!conn_info->resp_ie)
632446953f97SKangjie Lu 			conn_info->resp_ie_len = 0;
632592072e5fSSaravanan Shanmugham 
632692072e5fSSaravanan Shanmugham 		err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta",
632792072e5fSSaravanan Shanmugham 					       edcf_acparam_info,
632892072e5fSSaravanan Shanmugham 					       sizeof(edcf_acparam_info));
632992072e5fSSaravanan Shanmugham 		if (err) {
633092072e5fSSaravanan Shanmugham 			brcmf_err("could not get wme_ac_sta (%d)\n", err);
633192072e5fSSaravanan Shanmugham 			return err;
633292072e5fSSaravanan Shanmugham 		}
633392072e5fSSaravanan Shanmugham 
633492072e5fSSaravanan Shanmugham 		brcmf_wifi_prioritize_acparams(edcf_acparam_info,
633592072e5fSSaravanan Shanmugham 					       cfg->ac_priority);
633605491d2cSKalle Valo 	} else {
633705491d2cSKalle Valo 		conn_info->resp_ie_len = 0;
633805491d2cSKalle Valo 		conn_info->resp_ie = NULL;
633905491d2cSKalle Valo 	}
634005491d2cSKalle Valo 	brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
634105491d2cSKalle Valo 		  conn_info->req_ie_len, conn_info->resp_ie_len);
634205491d2cSKalle Valo 
634305491d2cSKalle Valo 	return err;
634405491d2cSKalle Valo }
634505491d2cSKalle Valo 
634605491d2cSKalle Valo static s32
brcmf_bss_roaming_done(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e)634705491d2cSKalle Valo brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
634805491d2cSKalle Valo 		       struct net_device *ndev,
634905491d2cSKalle Valo 		       const struct brcmf_event_msg *e)
635005491d2cSKalle Valo {
635105491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
635205491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
635305491d2cSKalle Valo 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
635405491d2cSKalle Valo 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
635505491d2cSKalle Valo 	struct ieee80211_channel *notify_channel = NULL;
635605491d2cSKalle Valo 	struct ieee80211_supported_band *band;
635705491d2cSKalle Valo 	struct brcmf_bss_info_le *bi;
635805491d2cSKalle Valo 	struct brcmu_chan ch;
635929ce6ecbSAvraham Stern 	struct cfg80211_roam_info roam_info = {};
636005491d2cSKalle Valo 	u32 freq;
636105491d2cSKalle Valo 	s32 err = 0;
636205491d2cSKalle Valo 	u8 *buf;
636305491d2cSKalle Valo 
636405491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
636505491d2cSKalle Valo 
636605491d2cSKalle Valo 	brcmf_get_assoc_ies(cfg, ifp);
636705491d2cSKalle Valo 	memcpy(profile->bssid, e->addr, ETH_ALEN);
636805491d2cSKalle Valo 	brcmf_update_bss_info(cfg, ifp);
636905491d2cSKalle Valo 
637005491d2cSKalle Valo 	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
637105491d2cSKalle Valo 	if (buf == NULL) {
637205491d2cSKalle Valo 		err = -ENOMEM;
637305491d2cSKalle Valo 		goto done;
637405491d2cSKalle Valo 	}
637505491d2cSKalle Valo 
637605491d2cSKalle Valo 	/* data sent to dongle has to be little endian */
637705491d2cSKalle Valo 	*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
637805491d2cSKalle Valo 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
637905491d2cSKalle Valo 				     buf, WL_BSS_INFO_MAX);
638005491d2cSKalle Valo 
638105491d2cSKalle Valo 	if (err)
638205491d2cSKalle Valo 		goto done;
638305491d2cSKalle Valo 
638405491d2cSKalle Valo 	bi = (struct brcmf_bss_info_le *)(buf + 4);
638505491d2cSKalle Valo 	ch.chspec = le16_to_cpu(bi->chanspec);
638605491d2cSKalle Valo 	cfg->d11inf.decchspec(&ch);
638705491d2cSKalle Valo 
638805491d2cSKalle Valo 	if (ch.band == BRCMU_CHAN_BAND_2G)
638957fbcce3SJohannes Berg 		band = wiphy->bands[NL80211_BAND_2GHZ];
639005491d2cSKalle Valo 	else
639157fbcce3SJohannes Berg 		band = wiphy->bands[NL80211_BAND_5GHZ];
639205491d2cSKalle Valo 
63934712d88aSRafał Miłecki 	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
639405491d2cSKalle Valo 	notify_channel = ieee80211_get_channel(wiphy, freq);
639505491d2cSKalle Valo 
639605491d2cSKalle Valo done:
639705491d2cSKalle Valo 	kfree(buf);
639829ce6ecbSAvraham Stern 
6399efbabc11SVeerendranath Jakkam 	roam_info.links[0].channel = notify_channel;
6400efbabc11SVeerendranath Jakkam 	roam_info.links[0].bssid = profile->bssid;
640129ce6ecbSAvraham Stern 	roam_info.req_ie = conn_info->req_ie;
640229ce6ecbSAvraham Stern 	roam_info.req_ie_len = conn_info->req_ie_len;
640329ce6ecbSAvraham Stern 	roam_info.resp_ie = conn_info->resp_ie;
640429ce6ecbSAvraham Stern 	roam_info.resp_ie_len = conn_info->resp_ie_len;
640529ce6ecbSAvraham Stern 
640629ce6ecbSAvraham Stern 	cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
640705491d2cSKalle Valo 	brcmf_dbg(CONN, "Report roaming result\n");
640805491d2cSKalle Valo 
6409be898fedSChung-Hsien Hsu 	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) {
64100ff57171SVinayak Yadawad 		cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL);
6411be898fedSChung-Hsien Hsu 		brcmf_dbg(CONN, "Report port authorized\n");
6412be898fedSChung-Hsien Hsu 	}
6413be898fedSChung-Hsien Hsu 
641405491d2cSKalle Valo 	set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
641505491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
641605491d2cSKalle Valo 	return err;
641705491d2cSKalle Valo }
641805491d2cSKalle Valo 
641905491d2cSKalle Valo static s32
brcmf_bss_connect_done(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e,bool completed)642005491d2cSKalle Valo brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
642105491d2cSKalle Valo 		       struct net_device *ndev, const struct brcmf_event_msg *e,
642205491d2cSKalle Valo 		       bool completed)
642305491d2cSKalle Valo {
642405491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
642505491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
642605491d2cSKalle Valo 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
6427123fef3fSArend van Spriel 	struct cfg80211_connect_resp_params conn_params;
642805491d2cSKalle Valo 
642905491d2cSKalle Valo 	brcmf_dbg(TRACE, "Enter\n");
643005491d2cSKalle Valo 
643105491d2cSKalle Valo 	if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
643205491d2cSKalle Valo 			       &ifp->vif->sme_state)) {
6433123fef3fSArend van Spriel 		memset(&conn_params, 0, sizeof(conn_params));
643405491d2cSKalle Valo 		if (completed) {
643505491d2cSKalle Valo 			brcmf_get_assoc_ies(cfg, ifp);
643605491d2cSKalle Valo 			brcmf_update_bss_info(cfg, ifp);
643705491d2cSKalle Valo 			set_bit(BRCMF_VIF_STATUS_CONNECTED,
643805491d2cSKalle Valo 				&ifp->vif->sme_state);
6439123fef3fSArend van Spriel 			conn_params.status = WLAN_STATUS_SUCCESS;
6440123fef3fSArend van Spriel 		} else {
644152617beeSWataru Gohda 			clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS,
644252617beeSWataru Gohda 				  &ifp->vif->sme_state);
644352617beeSWataru Gohda 			clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS,
644452617beeSWataru Gohda 				  &ifp->vif->sme_state);
6445123fef3fSArend van Spriel 			conn_params.status = WLAN_STATUS_AUTH_TIMEOUT;
644605491d2cSKalle Valo 		}
6447efbabc11SVeerendranath Jakkam 		conn_params.links[0].bssid = profile->bssid;
6448123fef3fSArend van Spriel 		conn_params.req_ie = conn_info->req_ie;
6449123fef3fSArend van Spriel 		conn_params.req_ie_len = conn_info->req_ie_len;
6450123fef3fSArend van Spriel 		conn_params.resp_ie = conn_info->resp_ie;
6451123fef3fSArend van Spriel 		conn_params.resp_ie_len = conn_info->resp_ie_len;
6452123fef3fSArend van Spriel 		cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL);
645305491d2cSKalle Valo 		brcmf_dbg(CONN, "Report connect result - connection %s\n",
645405491d2cSKalle Valo 			  completed ? "succeeded" : "failed");
645505491d2cSKalle Valo 	}
645605491d2cSKalle Valo 	brcmf_dbg(TRACE, "Exit\n");
645705491d2cSKalle Valo 	return 0;
645805491d2cSKalle Valo }
645905491d2cSKalle Valo 
646005491d2cSKalle Valo static s32
brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e,void * data)646105491d2cSKalle Valo brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
646205491d2cSKalle Valo 			       struct net_device *ndev,
646305491d2cSKalle Valo 			       const struct brcmf_event_msg *e, void *data)
646405491d2cSKalle Valo {
646516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
646605491d2cSKalle Valo 	static int generation;
646705491d2cSKalle Valo 	u32 event = e->event_code;
646805491d2cSKalle Valo 	u32 reason = e->reason;
646941bd3d58SToke Høiland-Jørgensen 	struct station_info *sinfo;
647005491d2cSKalle Valo 
6471e1c122d5SRafał Miłecki 	brcmf_dbg(CONN, "event %s (%u), reason %d\n",
6472e1c122d5SRafał Miłecki 		  brcmf_fweh_event_name(event), event, reason);
647305491d2cSKalle Valo 	if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
647405491d2cSKalle Valo 	    ndev != cfg_to_ndev(cfg)) {
647505491d2cSKalle Valo 		brcmf_dbg(CONN, "AP mode link down\n");
647605491d2cSKalle Valo 		complete(&cfg->vif_disabled);
647705491d2cSKalle Valo 		return 0;
647805491d2cSKalle Valo 	}
647905491d2cSKalle Valo 
648005491d2cSKalle Valo 	if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
648105491d2cSKalle Valo 	    (reason == BRCMF_E_STATUS_SUCCESS)) {
648205491d2cSKalle Valo 		if (!data) {
648316e64676SRafał Miłecki 			bphy_err(drvr, "No IEs present in ASSOC/REASSOC_IND\n");
648405491d2cSKalle Valo 			return -EINVAL;
648505491d2cSKalle Valo 		}
648641bd3d58SToke Høiland-Jørgensen 
648741bd3d58SToke Høiland-Jørgensen 		sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
648841bd3d58SToke Høiland-Jørgensen 		if (!sinfo)
648941bd3d58SToke Høiland-Jørgensen 			return -ENOMEM;
649041bd3d58SToke Høiland-Jørgensen 
649141bd3d58SToke Høiland-Jørgensen 		sinfo->assoc_req_ies = data;
649241bd3d58SToke Høiland-Jørgensen 		sinfo->assoc_req_ies_len = e->datalen;
649305491d2cSKalle Valo 		generation++;
649441bd3d58SToke Høiland-Jørgensen 		sinfo->generation = generation;
649541bd3d58SToke Høiland-Jørgensen 		cfg80211_new_sta(ndev, e->addr, sinfo, GFP_KERNEL);
649641bd3d58SToke Høiland-Jørgensen 
649741bd3d58SToke Høiland-Jørgensen 		kfree(sinfo);
649805491d2cSKalle Valo 	} else if ((event == BRCMF_E_DISASSOC_IND) ||
649905491d2cSKalle Valo 		   (event == BRCMF_E_DEAUTH_IND) ||
650005491d2cSKalle Valo 		   (event == BRCMF_E_DEAUTH)) {
650105491d2cSKalle Valo 		cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
650205491d2cSKalle Valo 	}
650305491d2cSKalle Valo 	return 0;
650405491d2cSKalle Valo }
650505491d2cSKalle Valo 
650605491d2cSKalle Valo static s32
brcmf_notify_connect_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)650705491d2cSKalle Valo brcmf_notify_connect_status(struct brcmf_if *ifp,
650805491d2cSKalle Valo 			    const struct brcmf_event_msg *e, void *data)
650905491d2cSKalle Valo {
651005491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
651105491d2cSKalle Valo 	struct net_device *ndev = ifp->ndev;
651205491d2cSKalle Valo 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
651305491d2cSKalle Valo 	struct ieee80211_channel *chan;
651405491d2cSKalle Valo 	s32 err = 0;
651505491d2cSKalle Valo 
651605491d2cSKalle Valo 	if ((e->event_code == BRCMF_E_DEAUTH) ||
651705491d2cSKalle Valo 	    (e->event_code == BRCMF_E_DEAUTH_IND) ||
651805491d2cSKalle Valo 	    (e->event_code == BRCMF_E_DISASSOC_IND) ||
651905491d2cSKalle Valo 	    ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
652005491d2cSKalle Valo 		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
652105491d2cSKalle Valo 	}
652205491d2cSKalle Valo 
652305491d2cSKalle Valo 	if (brcmf_is_apmode(ifp->vif)) {
652405491d2cSKalle Valo 		err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
6525b8a64f0eSArend van Spriel 	} else if (brcmf_is_linkup(ifp->vif, e)) {
652605491d2cSKalle Valo 		brcmf_dbg(CONN, "Linkup\n");
652705491d2cSKalle Valo 		if (brcmf_is_ibssmode(ifp->vif)) {
6528b0a79088SHante Meuleman 			brcmf_inform_ibss(cfg, ndev, e->addr);
652905491d2cSKalle Valo 			chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
653005491d2cSKalle Valo 			memcpy(profile->bssid, e->addr, ETH_ALEN);
653105491d2cSKalle Valo 			cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
653205491d2cSKalle Valo 			clear_bit(BRCMF_VIF_STATUS_CONNECTING,
653305491d2cSKalle Valo 				  &ifp->vif->sme_state);
653405491d2cSKalle Valo 			set_bit(BRCMF_VIF_STATUS_CONNECTED,
653505491d2cSKalle Valo 				&ifp->vif->sme_state);
653605491d2cSKalle Valo 		} else
653705491d2cSKalle Valo 			brcmf_bss_connect_done(cfg, ndev, e, true);
653805491d2cSKalle Valo 		brcmf_net_setcarrier(ifp, true);
6539e862a3e4SLuca Pesce 	} else if (brcmf_is_linkdown(ifp->vif, e)) {
654005491d2cSKalle Valo 		brcmf_dbg(CONN, "Linkdown\n");
65410d9de08dSAble Liao 		if (!brcmf_is_ibssmode(ifp->vif) &&
654252617beeSWataru Gohda 		    (test_bit(BRCMF_VIF_STATUS_CONNECTED,
654352617beeSWataru Gohda 			      &ifp->vif->sme_state) ||
654452617beeSWataru Gohda 		     test_bit(BRCMF_VIF_STATUS_CONNECTING,
654552617beeSWataru Gohda 			      &ifp->vif->sme_state))) {
654652617beeSWataru Gohda 			if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
654752617beeSWataru Gohda 				     &ifp->vif->sme_state) &&
654852617beeSWataru Gohda 			    memcmp(profile->bssid, e->addr, ETH_ALEN))
65490d9de08dSAble Liao 				return err;
65500d9de08dSAble Liao 
655105491d2cSKalle Valo 			brcmf_bss_connect_done(cfg, ndev, e, false);
655242e0ed0dSHante Meuleman 			brcmf_link_down(ifp->vif,
65531b050d97SSoontak Lee 					brcmf_map_fw_linkdown_reason(e),
65541b050d97SSoontak Lee 					e->event_code &
65551b050d97SSoontak Lee 					(BRCMF_E_DEAUTH_IND |
65561b050d97SSoontak Lee 					BRCMF_E_DISASSOC_IND)
65571b050d97SSoontak Lee 					? false : true);
655805491d2cSKalle Valo 			brcmf_init_prof(ndev_to_prof(ndev));
655905491d2cSKalle Valo 			if (ndev != cfg_to_ndev(cfg))
656005491d2cSKalle Valo 				complete(&cfg->vif_disabled);
656105491d2cSKalle Valo 			brcmf_net_setcarrier(ifp, false);
656242e0ed0dSHante Meuleman 		}
656305491d2cSKalle Valo 	} else if (brcmf_is_nonetwork(cfg, e)) {
656405491d2cSKalle Valo 		if (brcmf_is_ibssmode(ifp->vif))
656505491d2cSKalle Valo 			clear_bit(BRCMF_VIF_STATUS_CONNECTING,
656605491d2cSKalle Valo 				  &ifp->vif->sme_state);
656705491d2cSKalle Valo 		else
656805491d2cSKalle Valo 			brcmf_bss_connect_done(cfg, ndev, e, false);
656905491d2cSKalle Valo 	}
657005491d2cSKalle Valo 
657105491d2cSKalle Valo 	return err;
657205491d2cSKalle Valo }
657305491d2cSKalle Valo 
657405491d2cSKalle Valo static s32
brcmf_notify_roaming_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)657505491d2cSKalle Valo brcmf_notify_roaming_status(struct brcmf_if *ifp,
657605491d2cSKalle Valo 			    const struct brcmf_event_msg *e, void *data)
657705491d2cSKalle Valo {
657805491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
657905491d2cSKalle Valo 	u32 event = e->event_code;
658005491d2cSKalle Valo 	u32 status = e->status;
658105491d2cSKalle Valo 
658205491d2cSKalle Valo 	if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
65838b943e36SChung-Hsien Hsu 		if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
65848b943e36SChung-Hsien Hsu 			     &ifp->vif->sme_state)) {
658505491d2cSKalle Valo 			brcmf_bss_roaming_done(cfg, ifp->ndev, e);
65868b943e36SChung-Hsien Hsu 		} else {
658705491d2cSKalle Valo 			brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
65888b943e36SChung-Hsien Hsu 			brcmf_net_setcarrier(ifp, true);
65898b943e36SChung-Hsien Hsu 		}
659005491d2cSKalle Valo 	}
659105491d2cSKalle Valo 
659205491d2cSKalle Valo 	return 0;
659305491d2cSKalle Valo }
659405491d2cSKalle Valo 
659505491d2cSKalle Valo static s32
brcmf_notify_mic_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)659605491d2cSKalle Valo brcmf_notify_mic_status(struct brcmf_if *ifp,
659705491d2cSKalle Valo 			const struct brcmf_event_msg *e, void *data)
659805491d2cSKalle Valo {
659905491d2cSKalle Valo 	u16 flags = e->flags;
660005491d2cSKalle Valo 	enum nl80211_key_type key_type;
660105491d2cSKalle Valo 
660205491d2cSKalle Valo 	if (flags & BRCMF_EVENT_MSG_GROUP)
660305491d2cSKalle Valo 		key_type = NL80211_KEYTYPE_GROUP;
660405491d2cSKalle Valo 	else
660505491d2cSKalle Valo 		key_type = NL80211_KEYTYPE_PAIRWISE;
660605491d2cSKalle Valo 
660705491d2cSKalle Valo 	cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
660805491d2cSKalle Valo 				     NULL, GFP_KERNEL);
660905491d2cSKalle Valo 
661005491d2cSKalle Valo 	return 0;
661105491d2cSKalle Valo }
661205491d2cSKalle Valo 
brcmf_notify_rssi(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)66137dd56ea4SAlvin Šipraga static s32 brcmf_notify_rssi(struct brcmf_if *ifp,
66147dd56ea4SAlvin Šipraga 			     const struct brcmf_event_msg *e, void *data)
66157dd56ea4SAlvin Šipraga {
66167dd56ea4SAlvin Šipraga 	struct brcmf_cfg80211_vif *vif = ifp->vif;
66177dd56ea4SAlvin Šipraga 	struct brcmf_rssi_be *info = data;
6618ec52d77dSJohn Keeping 	s32 rssi, snr = 0, noise = 0;
66197dd56ea4SAlvin Šipraga 	s32 low, high, last;
66207dd56ea4SAlvin Šipraga 
6621ec52d77dSJohn Keeping 	if (e->datalen >= sizeof(*info)) {
66227dd56ea4SAlvin Šipraga 		rssi = be32_to_cpu(info->rssi);
66237dd56ea4SAlvin Šipraga 		snr = be32_to_cpu(info->snr);
66247dd56ea4SAlvin Šipraga 		noise = be32_to_cpu(info->noise);
6625ec52d77dSJohn Keeping 	} else if (e->datalen >= sizeof(rssi)) {
6626ec52d77dSJohn Keeping 		rssi = be32_to_cpu(*(__be32 *)data);
6627ec52d77dSJohn Keeping 	} else {
6628ec52d77dSJohn Keeping 		brcmf_err("insufficient RSSI event data\n");
6629ec52d77dSJohn Keeping 		return 0;
6630ec52d77dSJohn Keeping 	}
66317dd56ea4SAlvin Šipraga 
66327dd56ea4SAlvin Šipraga 	low = vif->cqm_rssi_low;
66337dd56ea4SAlvin Šipraga 	high = vif->cqm_rssi_high;
66347dd56ea4SAlvin Šipraga 	last = vif->cqm_rssi_last;
66357dd56ea4SAlvin Šipraga 
66367dd56ea4SAlvin Šipraga 	brcmf_dbg(TRACE, "rssi=%d snr=%d noise=%d low=%d high=%d last=%d\n",
66377dd56ea4SAlvin Šipraga 		  rssi, snr, noise, low, high, last);
66387dd56ea4SAlvin Šipraga 
66397dd56ea4SAlvin Šipraga 	vif->cqm_rssi_last = rssi;
66407dd56ea4SAlvin Šipraga 
66417dd56ea4SAlvin Šipraga 	if (rssi <= low || rssi == 0) {
66427dd56ea4SAlvin Šipraga 		brcmf_dbg(INFO, "LOW rssi=%d\n", rssi);
66437dd56ea4SAlvin Šipraga 		cfg80211_cqm_rssi_notify(ifp->ndev,
66447dd56ea4SAlvin Šipraga 					 NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
66457dd56ea4SAlvin Šipraga 					 rssi, GFP_KERNEL);
66467dd56ea4SAlvin Šipraga 	} else if (rssi > high) {
66477dd56ea4SAlvin Šipraga 		brcmf_dbg(INFO, "HIGH rssi=%d\n", rssi);
66487dd56ea4SAlvin Šipraga 		cfg80211_cqm_rssi_notify(ifp->ndev,
66497dd56ea4SAlvin Šipraga 					 NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
66507dd56ea4SAlvin Šipraga 					 rssi, GFP_KERNEL);
66517dd56ea4SAlvin Šipraga 	}
66527dd56ea4SAlvin Šipraga 
66537dd56ea4SAlvin Šipraga 	return 0;
66547dd56ea4SAlvin Šipraga }
66557dd56ea4SAlvin Šipraga 
brcmf_notify_vif_event(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)665605491d2cSKalle Valo static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
665705491d2cSKalle Valo 				  const struct brcmf_event_msg *e, void *data)
665805491d2cSKalle Valo {
665905491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
666005491d2cSKalle Valo 	struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
666105491d2cSKalle Valo 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
666205491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
666305491d2cSKalle Valo 
666437a869ecSHante Meuleman 	brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
666505491d2cSKalle Valo 		  ifevent->action, ifevent->flags, ifevent->ifidx,
666637a869ecSHante Meuleman 		  ifevent->bsscfgidx);
666705491d2cSKalle Valo 
6668b64abcb7Smhiramat@kernel.org 	spin_lock(&event->vif_event_lock);
666905491d2cSKalle Valo 	event->action = ifevent->action;
667005491d2cSKalle Valo 	vif = event->vif;
667105491d2cSKalle Valo 
667205491d2cSKalle Valo 	switch (ifevent->action) {
667305491d2cSKalle Valo 	case BRCMF_E_IF_ADD:
667405491d2cSKalle Valo 		/* waiting process may have timed out */
667505491d2cSKalle Valo 		if (!cfg->vif_event.vif) {
6676b64abcb7Smhiramat@kernel.org 			spin_unlock(&event->vif_event_lock);
667705491d2cSKalle Valo 			return -EBADF;
667805491d2cSKalle Valo 		}
667905491d2cSKalle Valo 
668005491d2cSKalle Valo 		ifp->vif = vif;
668105491d2cSKalle Valo 		vif->ifp = ifp;
668205491d2cSKalle Valo 		if (ifp->ndev) {
668305491d2cSKalle Valo 			vif->wdev.netdev = ifp->ndev;
668405491d2cSKalle Valo 			ifp->ndev->ieee80211_ptr = &vif->wdev;
668505491d2cSKalle Valo 			SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
668605491d2cSKalle Valo 		}
6687b64abcb7Smhiramat@kernel.org 		spin_unlock(&event->vif_event_lock);
668805491d2cSKalle Valo 		wake_up(&event->vif_wq);
668905491d2cSKalle Valo 		return 0;
669005491d2cSKalle Valo 
669105491d2cSKalle Valo 	case BRCMF_E_IF_DEL:
6692b64abcb7Smhiramat@kernel.org 		spin_unlock(&event->vif_event_lock);
669305491d2cSKalle Valo 		/* event may not be upon user request */
669405491d2cSKalle Valo 		if (brcmf_cfg80211_vif_event_armed(cfg))
669505491d2cSKalle Valo 			wake_up(&event->vif_wq);
669605491d2cSKalle Valo 		return 0;
669705491d2cSKalle Valo 
669805491d2cSKalle Valo 	case BRCMF_E_IF_CHANGE:
6699b64abcb7Smhiramat@kernel.org 		spin_unlock(&event->vif_event_lock);
670005491d2cSKalle Valo 		wake_up(&event->vif_wq);
670105491d2cSKalle Valo 		return 0;
670205491d2cSKalle Valo 
670305491d2cSKalle Valo 	default:
6704b64abcb7Smhiramat@kernel.org 		spin_unlock(&event->vif_event_lock);
670505491d2cSKalle Valo 		break;
670605491d2cSKalle Valo 	}
670705491d2cSKalle Valo 	return -EINVAL;
670805491d2cSKalle Valo }
670905491d2cSKalle Valo 
brcmf_init_conf(struct brcmf_cfg80211_conf * conf)671005491d2cSKalle Valo static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
671105491d2cSKalle Valo {
671205491d2cSKalle Valo 	conf->frag_threshold = (u32)-1;
671305491d2cSKalle Valo 	conf->rts_threshold = (u32)-1;
671405491d2cSKalle Valo 	conf->retry_short = (u32)-1;
671505491d2cSKalle Valo 	conf->retry_long = (u32)-1;
671605491d2cSKalle Valo }
671705491d2cSKalle Valo 
brcmf_register_event_handlers(struct brcmf_cfg80211_info * cfg)671805491d2cSKalle Valo static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
671905491d2cSKalle Valo {
672005491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
672105491d2cSKalle Valo 			    brcmf_notify_connect_status);
672205491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
672305491d2cSKalle Valo 			    brcmf_notify_connect_status);
672405491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
672505491d2cSKalle Valo 			    brcmf_notify_connect_status);
672605491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
672705491d2cSKalle Valo 			    brcmf_notify_connect_status);
672805491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
672905491d2cSKalle Valo 			    brcmf_notify_connect_status);
673005491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
673105491d2cSKalle Valo 			    brcmf_notify_connect_status);
673205491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
673305491d2cSKalle Valo 			    brcmf_notify_roaming_status);
673405491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
673505491d2cSKalle Valo 			    brcmf_notify_mic_status);
673605491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
673705491d2cSKalle Valo 			    brcmf_notify_connect_status);
673805491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
673905491d2cSKalle Valo 			    brcmf_notify_sched_scan_results);
674005491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
674105491d2cSKalle Valo 			    brcmf_notify_vif_event);
674205491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
674305491d2cSKalle Valo 			    brcmf_p2p_notify_rx_mgmt_p2p_probereq);
674405491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
674505491d2cSKalle Valo 			    brcmf_p2p_notify_listen_complete);
674605491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
674705491d2cSKalle Valo 			    brcmf_p2p_notify_action_frame_rx);
674805491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
674905491d2cSKalle Valo 			    brcmf_p2p_notify_action_tx_complete);
675005491d2cSKalle Valo 	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
675105491d2cSKalle Valo 			    brcmf_p2p_notify_action_tx_complete);
6752b8a64f0eSArend van Spriel 	brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
6753b8a64f0eSArend van Spriel 			    brcmf_notify_connect_status);
67547dd56ea4SAlvin Šipraga 	brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi);
675505491d2cSKalle Valo }
675605491d2cSKalle Valo 
brcmf_deinit_priv_mem(struct brcmf_cfg80211_info * cfg)675705491d2cSKalle Valo static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
675805491d2cSKalle Valo {
675905491d2cSKalle Valo 	kfree(cfg->conf);
676005491d2cSKalle Valo 	cfg->conf = NULL;
676105491d2cSKalle Valo 	kfree(cfg->extra_buf);
676205491d2cSKalle Valo 	cfg->extra_buf = NULL;
67633021ad9aSHante Meuleman 	kfree(cfg->wowl.nd);
67643021ad9aSHante Meuleman 	cfg->wowl.nd = NULL;
67653021ad9aSHante Meuleman 	kfree(cfg->wowl.nd_info);
67663021ad9aSHante Meuleman 	cfg->wowl.nd_info = NULL;
6767d5367334SHante Meuleman 	kfree(cfg->escan_info.escan_buf);
6768d5367334SHante Meuleman 	cfg->escan_info.escan_buf = NULL;
676905491d2cSKalle Valo }
677005491d2cSKalle Valo 
brcmf_init_priv_mem(struct brcmf_cfg80211_info * cfg)677105491d2cSKalle Valo static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
677205491d2cSKalle Valo {
677305491d2cSKalle Valo 	cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
677405491d2cSKalle Valo 	if (!cfg->conf)
677505491d2cSKalle Valo 		goto init_priv_mem_out;
677605491d2cSKalle Valo 	cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
677705491d2cSKalle Valo 	if (!cfg->extra_buf)
677805491d2cSKalle Valo 		goto init_priv_mem_out;
67793021ad9aSHante Meuleman 	cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
67803021ad9aSHante Meuleman 	if (!cfg->wowl.nd)
67813021ad9aSHante Meuleman 		goto init_priv_mem_out;
67823021ad9aSHante Meuleman 	cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
67833021ad9aSHante Meuleman 				    sizeof(struct cfg80211_wowlan_nd_match *),
67843021ad9aSHante Meuleman 				    GFP_KERNEL);
67853021ad9aSHante Meuleman 	if (!cfg->wowl.nd_info)
67863021ad9aSHante Meuleman 		goto init_priv_mem_out;
6787d5367334SHante Meuleman 	cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
6788d5367334SHante Meuleman 	if (!cfg->escan_info.escan_buf)
6789d5367334SHante Meuleman 		goto init_priv_mem_out;
679005491d2cSKalle Valo 
679105491d2cSKalle Valo 	return 0;
679205491d2cSKalle Valo 
679305491d2cSKalle Valo init_priv_mem_out:
679405491d2cSKalle Valo 	brcmf_deinit_priv_mem(cfg);
679505491d2cSKalle Valo 
679605491d2cSKalle Valo 	return -ENOMEM;
679705491d2cSKalle Valo }
679805491d2cSKalle Valo 
wl_init_priv(struct brcmf_cfg80211_info * cfg)679905491d2cSKalle Valo static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
680005491d2cSKalle Valo {
680105491d2cSKalle Valo 	s32 err = 0;
680205491d2cSKalle Valo 
680305491d2cSKalle Valo 	cfg->scan_request = NULL;
680405491d2cSKalle Valo 	cfg->pwr_save = true;
680505491d2cSKalle Valo 	cfg->dongle_up = false;		/* dongle is not up yet */
680605491d2cSKalle Valo 	err = brcmf_init_priv_mem(cfg);
680705491d2cSKalle Valo 	if (err)
680805491d2cSKalle Valo 		return err;
680905491d2cSKalle Valo 	brcmf_register_event_handlers(cfg);
681005491d2cSKalle Valo 	mutex_init(&cfg->usr_sync);
681105491d2cSKalle Valo 	brcmf_init_escan(cfg);
681205491d2cSKalle Valo 	brcmf_init_conf(cfg->conf);
681392072e5fSSaravanan Shanmugham 	brcmf_init_wmm_prio(cfg->ac_priority);
681405491d2cSKalle Valo 	init_completion(&cfg->vif_disabled);
681505491d2cSKalle Valo 	return err;
681605491d2cSKalle Valo }
681705491d2cSKalle Valo 
wl_deinit_priv(struct brcmf_cfg80211_info * cfg)681805491d2cSKalle Valo static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
681905491d2cSKalle Valo {
682005491d2cSKalle Valo 	cfg->dongle_up = false;	/* dongle down */
682105491d2cSKalle Valo 	brcmf_abort_scanning(cfg);
682205491d2cSKalle Valo 	brcmf_deinit_priv_mem(cfg);
68235606aeaaSWataru Gohda 	brcmf_clear_assoc_ies(cfg);
682405491d2cSKalle Valo }
682505491d2cSKalle Valo 
init_vif_event(struct brcmf_cfg80211_vif_event * event)682605491d2cSKalle Valo static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
682705491d2cSKalle Valo {
682805491d2cSKalle Valo 	init_waitqueue_head(&event->vif_wq);
6829b64abcb7Smhiramat@kernel.org 	spin_lock_init(&event->vif_event_lock);
683005491d2cSKalle Valo }
683105491d2cSKalle Valo 
brcmf_dongle_roam(struct brcmf_if * ifp)68321119e23eSHante Meuleman static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
683305491d2cSKalle Valo {
683416e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
68351119e23eSHante Meuleman 	s32 err;
68361119e23eSHante Meuleman 	u32 bcn_timeout;
683705491d2cSKalle Valo 	__le32 roamtrigger[2];
683805491d2cSKalle Valo 	__le32 roam_delta[2];
683905491d2cSKalle Valo 
68401119e23eSHante Meuleman 	/* Configure beacon timeout value based upon roaming setting */
68417d34b056SHante Meuleman 	if (ifp->drvr->settings->roamoff)
68421119e23eSHante Meuleman 		bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
68431119e23eSHante Meuleman 	else
68441119e23eSHante Meuleman 		bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
684505491d2cSKalle Valo 	err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
684605491d2cSKalle Valo 	if (err) {
684716e64676SRafał Miłecki 		bphy_err(drvr, "bcn_timeout error (%d)\n", err);
68481119e23eSHante Meuleman 		goto roam_setup_done;
684905491d2cSKalle Valo 	}
685005491d2cSKalle Valo 
68511119e23eSHante Meuleman 	/* Enable/Disable built-in roaming to allow supplicant to take care of
68521119e23eSHante Meuleman 	 * roaming.
685305491d2cSKalle Valo 	 */
685405491d2cSKalle Valo 	brcmf_dbg(INFO, "Internal Roaming = %s\n",
68557d34b056SHante Meuleman 		  ifp->drvr->settings->roamoff ? "Off" : "On");
68567d34b056SHante Meuleman 	err = brcmf_fil_iovar_int_set(ifp, "roam_off",
68577d34b056SHante Meuleman 				      ifp->drvr->settings->roamoff);
685805491d2cSKalle Valo 	if (err) {
685916e64676SRafał Miłecki 		bphy_err(drvr, "roam_off error (%d)\n", err);
68601119e23eSHante Meuleman 		goto roam_setup_done;
686105491d2cSKalle Valo 	}
686205491d2cSKalle Valo 
686305491d2cSKalle Valo 	roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
686405491d2cSKalle Valo 	roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
686505491d2cSKalle Valo 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
686605491d2cSKalle Valo 				     (void *)roamtrigger, sizeof(roamtrigger));
6867c12c8913SSoeren Moch 	if (err)
686816e64676SRafał Miłecki 		bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err);
686905491d2cSKalle Valo 
687005491d2cSKalle Valo 	roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
687105491d2cSKalle Valo 	roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
687205491d2cSKalle Valo 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
687305491d2cSKalle Valo 				     (void *)roam_delta, sizeof(roam_delta));
6874c12c8913SSoeren Moch 	if (err)
687516e64676SRafał Miłecki 		bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err);
6876c12c8913SSoeren Moch 
6877c12c8913SSoeren Moch 	return 0;
687805491d2cSKalle Valo 
68791119e23eSHante Meuleman roam_setup_done:
688005491d2cSKalle Valo 	return err;
688105491d2cSKalle Valo }
688205491d2cSKalle Valo 
688305491d2cSKalle Valo static s32
brcmf_dongle_scantime(struct brcmf_if * ifp)68841678ba8eSHante Meuleman brcmf_dongle_scantime(struct brcmf_if *ifp)
688505491d2cSKalle Valo {
688616e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
688705491d2cSKalle Valo 	s32 err = 0;
688805491d2cSKalle Valo 
688905491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
68901678ba8eSHante Meuleman 				    BRCMF_SCAN_CHANNEL_TIME);
689105491d2cSKalle Valo 	if (err) {
689216e64676SRafał Miłecki 		bphy_err(drvr, "Scan assoc time error (%d)\n", err);
689305491d2cSKalle Valo 		goto dongle_scantime_out;
689405491d2cSKalle Valo 	}
689505491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
68961678ba8eSHante Meuleman 				    BRCMF_SCAN_UNASSOC_TIME);
689705491d2cSKalle Valo 	if (err) {
689816e64676SRafał Miłecki 		bphy_err(drvr, "Scan unassoc time error (%d)\n", err);
689905491d2cSKalle Valo 		goto dongle_scantime_out;
690005491d2cSKalle Valo 	}
690105491d2cSKalle Valo 
690205491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
69031678ba8eSHante Meuleman 				    BRCMF_SCAN_PASSIVE_TIME);
690405491d2cSKalle Valo 	if (err) {
690516e64676SRafał Miłecki 		bphy_err(drvr, "Scan passive time error (%d)\n", err);
690605491d2cSKalle Valo 		goto dongle_scantime_out;
690705491d2cSKalle Valo 	}
690805491d2cSKalle Valo 
690905491d2cSKalle Valo dongle_scantime_out:
691005491d2cSKalle Valo 	return err;
691105491d2cSKalle Valo }
691205491d2cSKalle Valo 
brcmf_update_bw40_channel_flag(struct ieee80211_channel * channel,struct brcmu_chan * ch)691305491d2cSKalle Valo static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
691405491d2cSKalle Valo 					   struct brcmu_chan *ch)
691505491d2cSKalle Valo {
691605491d2cSKalle Valo 	u32 ht40_flag;
691705491d2cSKalle Valo 
691805491d2cSKalle Valo 	ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
691905491d2cSKalle Valo 	if (ch->sb == BRCMU_CHAN_SB_U) {
692005491d2cSKalle Valo 		if (ht40_flag == IEEE80211_CHAN_NO_HT40)
692105491d2cSKalle Valo 			channel->flags &= ~IEEE80211_CHAN_NO_HT40;
692205491d2cSKalle Valo 		channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
692305491d2cSKalle Valo 	} else {
692405491d2cSKalle Valo 		/* It should be one of
692505491d2cSKalle Valo 		 * IEEE80211_CHAN_NO_HT40 or
692605491d2cSKalle Valo 		 * IEEE80211_CHAN_NO_HT40PLUS
692705491d2cSKalle Valo 		 */
692805491d2cSKalle Valo 		channel->flags &= ~IEEE80211_CHAN_NO_HT40;
692905491d2cSKalle Valo 		if (ht40_flag == IEEE80211_CHAN_NO_HT40)
693005491d2cSKalle Valo 			channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
693105491d2cSKalle Valo 	}
693205491d2cSKalle Valo }
693305491d2cSKalle Valo 
brcmf_construct_chaninfo(struct brcmf_cfg80211_info * cfg,u32 bw_cap[])693405491d2cSKalle Valo static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
693505491d2cSKalle Valo 				    u32 bw_cap[])
693605491d2cSKalle Valo {
69373ef005b8SRafał Miłecki 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
693816e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
693916e64676SRafał Miłecki 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
694005491d2cSKalle Valo 	struct ieee80211_supported_band *band;
694105491d2cSKalle Valo 	struct ieee80211_channel *channel;
694205491d2cSKalle Valo 	struct brcmf_chanspec_list *list;
694305491d2cSKalle Valo 	struct brcmu_chan ch;
694405491d2cSKalle Valo 	int err;
694505491d2cSKalle Valo 	u8 *pbuf;
694605491d2cSKalle Valo 	u32 i, j;
694705491d2cSKalle Valo 	u32 total;
694805491d2cSKalle Valo 	u32 chaninfo;
694905491d2cSKalle Valo 
695005491d2cSKalle Valo 	pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
695105491d2cSKalle Valo 
695205491d2cSKalle Valo 	if (pbuf == NULL)
695305491d2cSKalle Valo 		return -ENOMEM;
695405491d2cSKalle Valo 
695505491d2cSKalle Valo 	list = (struct brcmf_chanspec_list *)pbuf;
695605491d2cSKalle Valo 
695705491d2cSKalle Valo 	err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
695805491d2cSKalle Valo 				       BRCMF_DCMD_MEDLEN);
695905491d2cSKalle Valo 	if (err) {
696016e64676SRafał Miłecki 		bphy_err(drvr, "get chanspecs error (%d)\n", err);
696105491d2cSKalle Valo 		goto fail_pbuf;
696205491d2cSKalle Valo 	}
696305491d2cSKalle Valo 
696457fbcce3SJohannes Berg 	band = wiphy->bands[NL80211_BAND_2GHZ];
696505491d2cSKalle Valo 	if (band)
696605491d2cSKalle Valo 		for (i = 0; i < band->n_channels; i++)
696705491d2cSKalle Valo 			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
696857fbcce3SJohannes Berg 	band = wiphy->bands[NL80211_BAND_5GHZ];
696905491d2cSKalle Valo 	if (band)
697005491d2cSKalle Valo 		for (i = 0; i < band->n_channels; i++)
697105491d2cSKalle Valo 			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
697205491d2cSKalle Valo 
697305491d2cSKalle Valo 	total = le32_to_cpu(list->count);
69744920ab13SMinsuk Kang 	if (total > BRCMF_MAX_CHANSPEC_LIST) {
69754920ab13SMinsuk Kang 		bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
69764920ab13SMinsuk Kang 			 total);
69774920ab13SMinsuk Kang 		err = -EINVAL;
69784920ab13SMinsuk Kang 		goto fail_pbuf;
69794920ab13SMinsuk Kang 	}
69804920ab13SMinsuk Kang 
698105491d2cSKalle Valo 	for (i = 0; i < total; i++) {
698205491d2cSKalle Valo 		ch.chspec = (u16)le32_to_cpu(list->element[i]);
698305491d2cSKalle Valo 		cfg->d11inf.decchspec(&ch);
698405491d2cSKalle Valo 
698505491d2cSKalle Valo 		if (ch.band == BRCMU_CHAN_BAND_2G) {
698657fbcce3SJohannes Berg 			band = wiphy->bands[NL80211_BAND_2GHZ];
698705491d2cSKalle Valo 		} else if (ch.band == BRCMU_CHAN_BAND_5G) {
698857fbcce3SJohannes Berg 			band = wiphy->bands[NL80211_BAND_5GHZ];
698905491d2cSKalle Valo 		} else {
699016e64676SRafał Miłecki 			bphy_err(drvr, "Invalid channel Spec. 0x%x.\n",
69913ef005b8SRafał Miłecki 				 ch.chspec);
699205491d2cSKalle Valo 			continue;
699305491d2cSKalle Valo 		}
699405491d2cSKalle Valo 		if (!band)
699505491d2cSKalle Valo 			continue;
699605491d2cSKalle Valo 		if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
699705491d2cSKalle Valo 		    ch.bw == BRCMU_CHAN_BW_40)
699805491d2cSKalle Valo 			continue;
699905491d2cSKalle Valo 		if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
700005491d2cSKalle Valo 		    ch.bw == BRCMU_CHAN_BW_80)
700105491d2cSKalle Valo 			continue;
700205491d2cSKalle Valo 
700377c0d0cdSRafał Miłecki 		channel = NULL;
700405491d2cSKalle Valo 		for (j = 0; j < band->n_channels; j++) {
700577c0d0cdSRafał Miłecki 			if (band->channels[j].hw_value == ch.control_ch_num) {
700677c0d0cdSRafał Miłecki 				channel = &band->channels[j];
700705491d2cSKalle Valo 				break;
700805491d2cSKalle Valo 			}
700905491d2cSKalle Valo 		}
701077c0d0cdSRafał Miłecki 		if (!channel) {
701177c0d0cdSRafał Miłecki 			/* It seems firmware supports some channel we never
701277c0d0cdSRafał Miłecki 			 * considered. Something new in IEEE standard?
701377c0d0cdSRafał Miłecki 			 */
701416e64676SRafał Miłecki 			bphy_err(drvr, "Ignoring unexpected firmware channel %d\n",
701577c0d0cdSRafał Miłecki 				 ch.control_ch_num);
701677c0d0cdSRafał Miłecki 			continue;
701777c0d0cdSRafał Miłecki 		}
701805491d2cSKalle Valo 
70190f83ff69SRafał Miłecki 		if (channel->orig_flags & IEEE80211_CHAN_DISABLED)
70200f83ff69SRafał Miłecki 			continue;
70210f83ff69SRafał Miłecki 
702205491d2cSKalle Valo 		/* assuming the chanspecs order is HT20,
702305491d2cSKalle Valo 		 * HT40 upper, HT40 lower, and VHT80.
702405491d2cSKalle Valo 		 */
7025f4e18329SRafał Miłecki 		switch (ch.bw) {
702630519cbeSRafał Miłecki 		case BRCMU_CHAN_BW_160:
702730519cbeSRafał Miłecki 			channel->flags &= ~IEEE80211_CHAN_NO_160MHZ;
702830519cbeSRafał Miłecki 			break;
7029f4e18329SRafał Miłecki 		case BRCMU_CHAN_BW_80:
703077c0d0cdSRafał Miłecki 			channel->flags &= ~IEEE80211_CHAN_NO_80MHZ;
7031f4e18329SRafał Miłecki 			break;
7032f4e18329SRafał Miłecki 		case BRCMU_CHAN_BW_40:
703377c0d0cdSRafał Miłecki 			brcmf_update_bw40_channel_flag(channel, &ch);
7034f4e18329SRafał Miłecki 			break;
7035f4e18329SRafał Miłecki 		default:
7036f4e18329SRafał Miłecki 			wiphy_warn(wiphy, "Firmware reported unsupported bandwidth %d\n",
7037f4e18329SRafał Miłecki 				   ch.bw);
70380bde10dcSGustavo A. R. Silva 			fallthrough;
7039f4e18329SRafał Miłecki 		case BRCMU_CHAN_BW_20:
704005491d2cSKalle Valo 			/* enable the channel and disable other bandwidths
704105491d2cSKalle Valo 			 * for now as mentioned order assure they are enabled
704205491d2cSKalle Valo 			 * for subsequent chanspecs.
704305491d2cSKalle Valo 			 */
704477c0d0cdSRafał Miłecki 			channel->flags = IEEE80211_CHAN_NO_HT40 |
7045d1fe6ad6SRafał Miłecki 					 IEEE80211_CHAN_NO_80MHZ |
7046d1fe6ad6SRafał Miłecki 					 IEEE80211_CHAN_NO_160MHZ;
704705491d2cSKalle Valo 			ch.bw = BRCMU_CHAN_BW_20;
704805491d2cSKalle Valo 			cfg->d11inf.encchspec(&ch);
704905491d2cSKalle Valo 			chaninfo = ch.chspec;
7050*fb1862ceSArend van Spriel 			err = brcmf_fil_bsscfg_int_query(ifp, "per_chan_info",
705105491d2cSKalle Valo 							 &chaninfo);
705205491d2cSKalle Valo 			if (!err) {
705305491d2cSKalle Valo 				if (chaninfo & WL_CHAN_RADAR)
705477c0d0cdSRafał Miłecki 					channel->flags |=
705505491d2cSKalle Valo 						(IEEE80211_CHAN_RADAR |
705605491d2cSKalle Valo 						 IEEE80211_CHAN_NO_IR);
705705491d2cSKalle Valo 				if (chaninfo & WL_CHAN_PASSIVE)
705877c0d0cdSRafał Miłecki 					channel->flags |=
705905491d2cSKalle Valo 						IEEE80211_CHAN_NO_IR;
706005491d2cSKalle Valo 			}
706105491d2cSKalle Valo 		}
706205491d2cSKalle Valo 	}
706305491d2cSKalle Valo 
706405491d2cSKalle Valo fail_pbuf:
706505491d2cSKalle Valo 	kfree(pbuf);
706605491d2cSKalle Valo 	return err;
706705491d2cSKalle Valo }
706805491d2cSKalle Valo 
brcmf_enable_bw40_2g(struct brcmf_cfg80211_info * cfg)706905491d2cSKalle Valo static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
707005491d2cSKalle Valo {
707116e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
707216e64676SRafał Miłecki 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
707305491d2cSKalle Valo 	struct ieee80211_supported_band *band;
707405491d2cSKalle Valo 	struct brcmf_fil_bwcap_le band_bwcap;
707505491d2cSKalle Valo 	struct brcmf_chanspec_list *list;
707605491d2cSKalle Valo 	u8 *pbuf;
707705491d2cSKalle Valo 	u32 val;
707805491d2cSKalle Valo 	int err;
707905491d2cSKalle Valo 	struct brcmu_chan ch;
708005491d2cSKalle Valo 	u32 num_chan;
708105491d2cSKalle Valo 	int i, j;
708205491d2cSKalle Valo 
708305491d2cSKalle Valo 	/* verify support for bw_cap command */
708405491d2cSKalle Valo 	val = WLC_BAND_5G;
7085*fb1862ceSArend van Spriel 	err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &val);
708605491d2cSKalle Valo 
708705491d2cSKalle Valo 	if (!err) {
708805491d2cSKalle Valo 		/* only set 2G bandwidth using bw_cap command */
708905491d2cSKalle Valo 		band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
709005491d2cSKalle Valo 		band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
709105491d2cSKalle Valo 		err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
709205491d2cSKalle Valo 					       sizeof(band_bwcap));
709305491d2cSKalle Valo 	} else {
709405491d2cSKalle Valo 		brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
709505491d2cSKalle Valo 		val = WLC_N_BW_40ALL;
709605491d2cSKalle Valo 		err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
709705491d2cSKalle Valo 	}
709805491d2cSKalle Valo 
709905491d2cSKalle Valo 	if (!err) {
710005491d2cSKalle Valo 		/* update channel info in 2G band */
710105491d2cSKalle Valo 		pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
710205491d2cSKalle Valo 
710305491d2cSKalle Valo 		if (pbuf == NULL)
710405491d2cSKalle Valo 			return -ENOMEM;
710505491d2cSKalle Valo 
710605491d2cSKalle Valo 		ch.band = BRCMU_CHAN_BAND_2G;
710705491d2cSKalle Valo 		ch.bw = BRCMU_CHAN_BW_40;
710805491d2cSKalle Valo 		ch.sb = BRCMU_CHAN_SB_NONE;
710905491d2cSKalle Valo 		ch.chnum = 0;
711005491d2cSKalle Valo 		cfg->d11inf.encchspec(&ch);
711105491d2cSKalle Valo 
711205491d2cSKalle Valo 		/* pass encoded chanspec in query */
711305491d2cSKalle Valo 		*(__le16 *)pbuf = cpu_to_le16(ch.chspec);
711405491d2cSKalle Valo 
711505491d2cSKalle Valo 		err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
711605491d2cSKalle Valo 					       BRCMF_DCMD_MEDLEN);
711705491d2cSKalle Valo 		if (err) {
711816e64676SRafał Miłecki 			bphy_err(drvr, "get chanspecs error (%d)\n", err);
711905491d2cSKalle Valo 			kfree(pbuf);
712005491d2cSKalle Valo 			return err;
712105491d2cSKalle Valo 		}
712205491d2cSKalle Valo 
712357fbcce3SJohannes Berg 		band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
712405491d2cSKalle Valo 		list = (struct brcmf_chanspec_list *)pbuf;
712505491d2cSKalle Valo 		num_chan = le32_to_cpu(list->count);
71264920ab13SMinsuk Kang 		if (num_chan > BRCMF_MAX_CHANSPEC_LIST) {
71274920ab13SMinsuk Kang 			bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
71284920ab13SMinsuk Kang 				 num_chan);
71294920ab13SMinsuk Kang 			kfree(pbuf);
71304920ab13SMinsuk Kang 			return -EINVAL;
71314920ab13SMinsuk Kang 		}
71324920ab13SMinsuk Kang 
713305491d2cSKalle Valo 		for (i = 0; i < num_chan; i++) {
713405491d2cSKalle Valo 			ch.chspec = (u16)le32_to_cpu(list->element[i]);
713505491d2cSKalle Valo 			cfg->d11inf.decchspec(&ch);
713605491d2cSKalle Valo 			if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
713705491d2cSKalle Valo 				continue;
713805491d2cSKalle Valo 			if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
713905491d2cSKalle Valo 				continue;
714005491d2cSKalle Valo 			for (j = 0; j < band->n_channels; j++) {
71414712d88aSRafał Miłecki 				if (band->channels[j].hw_value == ch.control_ch_num)
714205491d2cSKalle Valo 					break;
714305491d2cSKalle Valo 			}
714405491d2cSKalle Valo 			if (WARN_ON(j == band->n_channels))
714505491d2cSKalle Valo 				continue;
714605491d2cSKalle Valo 
714705491d2cSKalle Valo 			brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
714805491d2cSKalle Valo 		}
714905491d2cSKalle Valo 		kfree(pbuf);
715005491d2cSKalle Valo 	}
715105491d2cSKalle Valo 	return err;
715205491d2cSKalle Valo }
715305491d2cSKalle Valo 
brcmf_get_bwcap(struct brcmf_if * ifp,u32 bw_cap[])715405491d2cSKalle Valo static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
715505491d2cSKalle Valo {
715616e64676SRafał Miłecki 	struct brcmf_pub *drvr = ifp->drvr;
715705491d2cSKalle Valo 	u32 band, mimo_bwcap;
715805491d2cSKalle Valo 	int err;
715905491d2cSKalle Valo 
716005491d2cSKalle Valo 	band = WLC_BAND_2G;
7161*fb1862ceSArend van Spriel 	err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
716205491d2cSKalle Valo 	if (!err) {
716357fbcce3SJohannes Berg 		bw_cap[NL80211_BAND_2GHZ] = band;
716405491d2cSKalle Valo 		band = WLC_BAND_5G;
7165*fb1862ceSArend van Spriel 		err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
716605491d2cSKalle Valo 		if (!err) {
716757fbcce3SJohannes Berg 			bw_cap[NL80211_BAND_5GHZ] = band;
716805491d2cSKalle Valo 			return;
716905491d2cSKalle Valo 		}
717005491d2cSKalle Valo 		WARN_ON(1);
717105491d2cSKalle Valo 		return;
717205491d2cSKalle Valo 	}
717305491d2cSKalle Valo 	brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
717405491d2cSKalle Valo 	err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
717505491d2cSKalle Valo 	if (err)
717605491d2cSKalle Valo 		/* assume 20MHz if firmware does not give a clue */
717705491d2cSKalle Valo 		mimo_bwcap = WLC_N_BW_20ALL;
717805491d2cSKalle Valo 
717905491d2cSKalle Valo 	switch (mimo_bwcap) {
718005491d2cSKalle Valo 	case WLC_N_BW_40ALL:
718157fbcce3SJohannes Berg 		bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
71820bde10dcSGustavo A. R. Silva 		fallthrough;
718305491d2cSKalle Valo 	case WLC_N_BW_20IN2G_40IN5G:
718457fbcce3SJohannes Berg 		bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
71850bde10dcSGustavo A. R. Silva 		fallthrough;
718605491d2cSKalle Valo 	case WLC_N_BW_20ALL:
718757fbcce3SJohannes Berg 		bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
718857fbcce3SJohannes Berg 		bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
718905491d2cSKalle Valo 		break;
719005491d2cSKalle Valo 	default:
719116e64676SRafał Miłecki 		bphy_err(drvr, "invalid mimo_bw_cap value\n");
719205491d2cSKalle Valo 	}
719305491d2cSKalle Valo }
719405491d2cSKalle Valo 
brcmf_update_ht_cap(struct ieee80211_supported_band * band,u32 bw_cap[2],u32 nchain)719505491d2cSKalle Valo static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
719605491d2cSKalle Valo 				u32 bw_cap[2], u32 nchain)
719705491d2cSKalle Valo {
719805491d2cSKalle Valo 	band->ht_cap.ht_supported = true;
719905491d2cSKalle Valo 	if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
720005491d2cSKalle Valo 		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
720105491d2cSKalle Valo 		band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
720205491d2cSKalle Valo 	}
720305491d2cSKalle Valo 	band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
720405491d2cSKalle Valo 	band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
720505491d2cSKalle Valo 	band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
720605491d2cSKalle Valo 	band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
720705491d2cSKalle Valo 	memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
720805491d2cSKalle Valo 	band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
720905491d2cSKalle Valo }
721005491d2cSKalle Valo 
brcmf_get_mcs_map(u32 nchain,enum ieee80211_vht_mcs_support supp)721105491d2cSKalle Valo static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
721205491d2cSKalle Valo {
721305491d2cSKalle Valo 	u16 mcs_map;
721405491d2cSKalle Valo 	int i;
721505491d2cSKalle Valo 
721605491d2cSKalle Valo 	for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
721705491d2cSKalle Valo 		mcs_map = (mcs_map << 2) | supp;
721805491d2cSKalle Valo 
721905491d2cSKalle Valo 	return cpu_to_le16(mcs_map);
722005491d2cSKalle Valo }
722105491d2cSKalle Valo 
brcmf_update_vht_cap(struct ieee80211_supported_band * band,u32 bw_cap[2],u32 nchain,u32 txstreams,u32 txbf_bfe_cap,u32 txbf_bfr_cap)722205491d2cSKalle Valo static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
72237bf65aa9SHante Meuleman 				 u32 bw_cap[2], u32 nchain, u32 txstreams,
72247bf65aa9SHante Meuleman 				 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
722505491d2cSKalle Valo {
722605491d2cSKalle Valo 	__le16 mcs_map;
722705491d2cSKalle Valo 
722805491d2cSKalle Valo 	/* not allowed in 2.4G band */
722957fbcce3SJohannes Berg 	if (band->band == NL80211_BAND_2GHZ)
723005491d2cSKalle Valo 		return;
723105491d2cSKalle Valo 
723205491d2cSKalle Valo 	band->vht_cap.vht_supported = true;
723305491d2cSKalle Valo 	/* 80MHz is mandatory */
723405491d2cSKalle Valo 	band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
723505491d2cSKalle Valo 	if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
723605491d2cSKalle Valo 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
723705491d2cSKalle Valo 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
723805491d2cSKalle Valo 	}
723905491d2cSKalle Valo 	/* all support 256-QAM */
724005491d2cSKalle Valo 	mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
724105491d2cSKalle Valo 	band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
724205491d2cSKalle Valo 	band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
72437bf65aa9SHante Meuleman 
72447bf65aa9SHante Meuleman 	/* Beamforming support information */
72457bf65aa9SHante Meuleman 	if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
72467bf65aa9SHante Meuleman 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
72477bf65aa9SHante Meuleman 	if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
72487bf65aa9SHante Meuleman 		band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
72497bf65aa9SHante Meuleman 	if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
72507bf65aa9SHante Meuleman 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
72517bf65aa9SHante Meuleman 	if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
72527bf65aa9SHante Meuleman 		band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
72537bf65aa9SHante Meuleman 
72547bf65aa9SHante Meuleman 	if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
72557bf65aa9SHante Meuleman 		band->vht_cap.cap |=
72567bf65aa9SHante Meuleman 			(2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
72577bf65aa9SHante Meuleman 		band->vht_cap.cap |= ((txstreams - 1) <<
72587bf65aa9SHante Meuleman 				IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
72597bf65aa9SHante Meuleman 		band->vht_cap.cap |=
72607bf65aa9SHante Meuleman 			IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
72617bf65aa9SHante Meuleman 	}
726205491d2cSKalle Valo }
726305491d2cSKalle Valo 
brcmf_setup_wiphybands(struct brcmf_cfg80211_info * cfg)7264856d5a01SArend Van Spriel static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
726505491d2cSKalle Valo {
726616e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
726716e64676SRafał Miłecki 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
72683ef005b8SRafał Miłecki 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
7269*fb1862ceSArend van Spriel 	u32 nmode;
727005491d2cSKalle Valo 	u32 vhtmode = 0;
727105491d2cSKalle Valo 	u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
7272*fb1862ceSArend van Spriel 	u32 rxchain;
727305491d2cSKalle Valo 	u32 nchain;
727405491d2cSKalle Valo 	int err;
727505491d2cSKalle Valo 	s32 i;
727605491d2cSKalle Valo 	struct ieee80211_supported_band *band;
72777bf65aa9SHante Meuleman 	u32 txstreams = 0;
72787bf65aa9SHante Meuleman 	u32 txbf_bfe_cap = 0;
72797bf65aa9SHante Meuleman 	u32 txbf_bfr_cap = 0;
728005491d2cSKalle Valo 
728105491d2cSKalle Valo 	(void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
728205491d2cSKalle Valo 	err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
728305491d2cSKalle Valo 	if (err) {
728416e64676SRafał Miłecki 		bphy_err(drvr, "nmode error (%d)\n", err);
728505491d2cSKalle Valo 	} else {
728605491d2cSKalle Valo 		brcmf_get_bwcap(ifp, bw_cap);
728705491d2cSKalle Valo 	}
728805491d2cSKalle Valo 	brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
728957fbcce3SJohannes Berg 		  nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
729057fbcce3SJohannes Berg 		  bw_cap[NL80211_BAND_5GHZ]);
729105491d2cSKalle Valo 
729205491d2cSKalle Valo 	err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
729305491d2cSKalle Valo 	if (err) {
729478f0a64fSDmitry Osipenko 		/* rxchain unsupported by firmware of older chips */
729578f0a64fSDmitry Osipenko 		if (err == -EBADE)
729678f0a64fSDmitry Osipenko 			bphy_info_once(drvr, "rxchain unsupported\n");
729778f0a64fSDmitry Osipenko 		else
729816e64676SRafał Miłecki 			bphy_err(drvr, "rxchain error (%d)\n", err);
729978f0a64fSDmitry Osipenko 
730005491d2cSKalle Valo 		nchain = 1;
730105491d2cSKalle Valo 	} else {
730205491d2cSKalle Valo 		for (nchain = 0; rxchain; nchain++)
730305491d2cSKalle Valo 			rxchain = rxchain & (rxchain - 1);
730405491d2cSKalle Valo 	}
730505491d2cSKalle Valo 	brcmf_dbg(INFO, "nchain=%d\n", nchain);
730605491d2cSKalle Valo 
730705491d2cSKalle Valo 	err = brcmf_construct_chaninfo(cfg, bw_cap);
730805491d2cSKalle Valo 	if (err) {
730916e64676SRafał Miłecki 		bphy_err(drvr, "brcmf_construct_chaninfo failed (%d)\n", err);
731005491d2cSKalle Valo 		return err;
731105491d2cSKalle Valo 	}
731205491d2cSKalle Valo 
73137bf65aa9SHante Meuleman 	if (vhtmode) {
73147bf65aa9SHante Meuleman 		(void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
73157bf65aa9SHante Meuleman 		(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
73167bf65aa9SHante Meuleman 					      &txbf_bfe_cap);
73177bf65aa9SHante Meuleman 		(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
73187bf65aa9SHante Meuleman 					      &txbf_bfr_cap);
73197bf65aa9SHante Meuleman 	}
73207bf65aa9SHante Meuleman 
732105491d2cSKalle Valo 	for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
732205491d2cSKalle Valo 		band = wiphy->bands[i];
732305491d2cSKalle Valo 		if (band == NULL)
732405491d2cSKalle Valo 			continue;
732505491d2cSKalle Valo 
732605491d2cSKalle Valo 		if (nmode)
732705491d2cSKalle Valo 			brcmf_update_ht_cap(band, bw_cap, nchain);
732805491d2cSKalle Valo 		if (vhtmode)
73297bf65aa9SHante Meuleman 			brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
73307bf65aa9SHante Meuleman 					     txbf_bfe_cap, txbf_bfr_cap);
733105491d2cSKalle Valo 	}
733205491d2cSKalle Valo 
733305491d2cSKalle Valo 	return 0;
733405491d2cSKalle Valo }
733505491d2cSKalle Valo 
733605491d2cSKalle Valo static const struct ieee80211_txrx_stypes
733705491d2cSKalle Valo brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
733805491d2cSKalle Valo 	[NL80211_IFTYPE_STATION] = {
733905491d2cSKalle Valo 		.tx = 0xffff,
734005491d2cSKalle Valo 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
734105491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
734205491d2cSKalle Valo 	},
734305491d2cSKalle Valo 	[NL80211_IFTYPE_P2P_CLIENT] = {
734405491d2cSKalle Valo 		.tx = 0xffff,
734505491d2cSKalle Valo 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
734605491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
734705491d2cSKalle Valo 	},
734805491d2cSKalle Valo 	[NL80211_IFTYPE_P2P_GO] = {
734905491d2cSKalle Valo 		.tx = 0xffff,
735005491d2cSKalle Valo 		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
735105491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
735205491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
735305491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_DISASSOC >> 4) |
735405491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_AUTH >> 4) |
735505491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_DEAUTH >> 4) |
735605491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_ACTION >> 4)
735705491d2cSKalle Valo 	},
735805491d2cSKalle Valo 	[NL80211_IFTYPE_P2P_DEVICE] = {
735905491d2cSKalle Valo 		.tx = 0xffff,
736005491d2cSKalle Valo 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
736105491d2cSKalle Valo 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
7362861cb5ebSStefan Wahren 	},
7363861cb5ebSStefan Wahren 	[NL80211_IFTYPE_AP] = {
7364861cb5ebSStefan Wahren 		.tx = 0xffff,
7365861cb5ebSStefan Wahren 		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
7366861cb5ebSStefan Wahren 		      BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
7367861cb5ebSStefan Wahren 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
7368861cb5ebSStefan Wahren 		      BIT(IEEE80211_STYPE_DISASSOC >> 4) |
7369861cb5ebSStefan Wahren 		      BIT(IEEE80211_STYPE_AUTH >> 4) |
7370861cb5ebSStefan Wahren 		      BIT(IEEE80211_STYPE_DEAUTH >> 4) |
7371861cb5ebSStefan Wahren 		      BIT(IEEE80211_STYPE_ACTION >> 4)
737205491d2cSKalle Valo 	}
737305491d2cSKalle Valo };
737405491d2cSKalle Valo 
737505491d2cSKalle Valo /**
737605491d2cSKalle Valo  * brcmf_setup_ifmodes() - determine interface modes and combinations.
737705491d2cSKalle Valo  *
737805491d2cSKalle Valo  * @wiphy: wiphy object.
737905491d2cSKalle Valo  * @ifp: interface object needed for feat module api.
738005491d2cSKalle Valo  *
738105491d2cSKalle Valo  * The interface modes and combinations are determined dynamically here
738205491d2cSKalle Valo  * based on firmware functionality.
738305491d2cSKalle Valo  *
738405491d2cSKalle Valo  * no p2p and no mbss:
738505491d2cSKalle Valo  *
738605491d2cSKalle Valo  *	#STA <= 1, #AP <= 1, channels = 1, 2 total
738705491d2cSKalle Valo  *
738805491d2cSKalle Valo  * no p2p and mbss:
738905491d2cSKalle Valo  *
739005491d2cSKalle Valo  *	#STA <= 1, #AP <= 1, channels = 1, 2 total
739105491d2cSKalle Valo  *	#AP <= 4, matching BI, channels = 1, 4 total
739205491d2cSKalle Valo  *
7393837482e6SWright Feng  * no p2p and rsdb:
7394ec3428bbSWright Feng  *	#STA <= 1, #AP <= 2, channels = 2, 4 total
7395837482e6SWright Feng  *
739605491d2cSKalle Valo  * p2p, no mchan, and mbss:
739705491d2cSKalle Valo  *
739805491d2cSKalle Valo  *	#STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
739905491d2cSKalle Valo  *	#STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
740005491d2cSKalle Valo  *	#AP <= 4, matching BI, channels = 1, 4 total
740105491d2cSKalle Valo  *
740205491d2cSKalle Valo  * p2p, mchan, and mbss:
740305491d2cSKalle Valo  *
74044388827bSWright Feng  *	#STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
740505491d2cSKalle Valo  *	#STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
740605491d2cSKalle Valo  *	#AP <= 4, matching BI, channels = 1, 4 total
7407837482e6SWright Feng  *
7408837482e6SWright Feng  * p2p, rsdb, and no mbss:
7409ec3428bbSWright Feng  *	#STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,
7410837482e6SWright Feng  *	 channels = 2, 4 total
741105491d2cSKalle Valo  */
brcmf_setup_ifmodes(struct wiphy * wiphy,struct brcmf_if * ifp)741205491d2cSKalle Valo static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
741305491d2cSKalle Valo {
741405491d2cSKalle Valo 	struct ieee80211_iface_combination *combo = NULL;
741505491d2cSKalle Valo 	struct ieee80211_iface_limit *c0_limits = NULL;
741605491d2cSKalle Valo 	struct ieee80211_iface_limit *p2p_limits = NULL;
741705491d2cSKalle Valo 	struct ieee80211_iface_limit *mbss_limits = NULL;
741820f2c5faSRafał Miłecki 	bool mon_flag, mbss, p2p, rsdb, mchan;
741920f2c5faSRafał Miłecki 	int i, c, n_combos, n_limits;
742005491d2cSKalle Valo 
742120f2c5faSRafał Miłecki 	mon_flag = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FLAG);
742205491d2cSKalle Valo 	mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
742305491d2cSKalle Valo 	p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
7424837482e6SWright Feng 	rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB);
742524332f80SRafał Miłecki 	mchan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN);
742605491d2cSKalle Valo 
7427837482e6SWright Feng 	n_combos = 1 + !!(p2p && !rsdb) + !!mbss;
742805491d2cSKalle Valo 	combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
742905491d2cSKalle Valo 	if (!combo)
743005491d2cSKalle Valo 		goto err;
743105491d2cSKalle Valo 
743205491d2cSKalle Valo 	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
743305491d2cSKalle Valo 				 BIT(NL80211_IFTYPE_ADHOC) |
743405491d2cSKalle Valo 				 BIT(NL80211_IFTYPE_AP);
743520f2c5faSRafał Miłecki 	if (mon_flag)
743620f2c5faSRafał Miłecki 		wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
743724332f80SRafał Miłecki 	if (p2p)
743824332f80SRafał Miłecki 		wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
743924332f80SRafał Miłecki 					  BIT(NL80211_IFTYPE_P2P_GO) |
744024332f80SRafał Miłecki 					  BIT(NL80211_IFTYPE_P2P_DEVICE);
744105491d2cSKalle Valo 
744205491d2cSKalle Valo 	c = 0;
744305491d2cSKalle Valo 	i = 0;
744420f2c5faSRafał Miłecki 	n_limits = 1 + mon_flag + (p2p ? 2 : 0) + (rsdb || !p2p);
744520f2c5faSRafał Miłecki 	c0_limits = kcalloc(n_limits, sizeof(*c0_limits), GFP_KERNEL);
7446f568adacSRafał Miłecki 	if (!c0_limits)
7447f568adacSRafał Miłecki 		goto err;
744824332f80SRafał Miłecki 
744924332f80SRafał Miłecki 	combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan));
74504388827bSWright Feng 	c0_limits[i].max = 1 + (p2p && mchan);
745105491d2cSKalle Valo 	c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
745220f2c5faSRafał Miłecki 	if (mon_flag) {
745320f2c5faSRafał Miłecki 		c0_limits[i].max = 1;
745420f2c5faSRafał Miłecki 		c0_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
745520f2c5faSRafał Miłecki 	}
745624332f80SRafał Miłecki 	if (p2p) {
7457837482e6SWright Feng 		c0_limits[i].max = 1;
7458837482e6SWright Feng 		c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
745924332f80SRafał Miłecki 		c0_limits[i].max = 1 + rsdb;
7460837482e6SWright Feng 		c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
7461837482e6SWright Feng 				       BIT(NL80211_IFTYPE_P2P_GO);
746224332f80SRafał Miłecki 	}
746324332f80SRafał Miłecki 	if (p2p && rsdb) {
7464837482e6SWright Feng 		c0_limits[i].max = 2;
7465837482e6SWright Feng 		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7466ec3428bbSWright Feng 		combo[c].max_interfaces = 4;
7467837482e6SWright Feng 	} else if (p2p) {
7468837482e6SWright Feng 		combo[c].max_interfaces = i;
7469837482e6SWright Feng 	} else if (rsdb) {
7470837482e6SWright Feng 		c0_limits[i].max = 2;
7471837482e6SWright Feng 		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7472837482e6SWright Feng 		combo[c].max_interfaces = 3;
747305491d2cSKalle Valo 	} else {
7474837482e6SWright Feng 		c0_limits[i].max = 1;
747505491d2cSKalle Valo 		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
747605491d2cSKalle Valo 		combo[c].max_interfaces = i;
7477837482e6SWright Feng 	}
747805491d2cSKalle Valo 	combo[c].n_limits = i;
747905491d2cSKalle Valo 	combo[c].limits = c0_limits;
748005491d2cSKalle Valo 
7481837482e6SWright Feng 	if (p2p && !rsdb) {
748205491d2cSKalle Valo 		c++;
748305491d2cSKalle Valo 		i = 0;
7484f568adacSRafał Miłecki 		p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
7485f568adacSRafał Miłecki 		if (!p2p_limits)
7486f568adacSRafał Miłecki 			goto err;
748705491d2cSKalle Valo 		p2p_limits[i].max = 1;
748805491d2cSKalle Valo 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
748905491d2cSKalle Valo 		p2p_limits[i].max = 1;
749005491d2cSKalle Valo 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
749105491d2cSKalle Valo 		p2p_limits[i].max = 1;
749205491d2cSKalle Valo 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
749305491d2cSKalle Valo 		p2p_limits[i].max = 1;
749405491d2cSKalle Valo 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
7495f568adacSRafał Miłecki 		combo[c].num_different_channels = 1;
749605491d2cSKalle Valo 		combo[c].max_interfaces = i;
749705491d2cSKalle Valo 		combo[c].n_limits = i;
749805491d2cSKalle Valo 		combo[c].limits = p2p_limits;
749905491d2cSKalle Valo 	}
750005491d2cSKalle Valo 
750105491d2cSKalle Valo 	if (mbss) {
750205491d2cSKalle Valo 		c++;
7503f568adacSRafał Miłecki 		i = 0;
750420f2c5faSRafał Miłecki 		n_limits = 1 + mon_flag;
750520f2c5faSRafał Miłecki 		mbss_limits = kcalloc(n_limits, sizeof(*mbss_limits),
750620f2c5faSRafał Miłecki 				      GFP_KERNEL);
7507f568adacSRafał Miłecki 		if (!mbss_limits)
7508f568adacSRafał Miłecki 			goto err;
7509f568adacSRafał Miłecki 		mbss_limits[i].max = 4;
7510f568adacSRafał Miłecki 		mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);
751120f2c5faSRafał Miłecki 		if (mon_flag) {
751220f2c5faSRafał Miłecki 			mbss_limits[i].max = 1;
751320f2c5faSRafał Miłecki 			mbss_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
751420f2c5faSRafał Miłecki 		}
751505491d2cSKalle Valo 		combo[c].beacon_int_infra_match = true;
751605491d2cSKalle Valo 		combo[c].num_different_channels = 1;
751720f2c5faSRafał Miłecki 		combo[c].max_interfaces = 4 + mon_flag;
7518f568adacSRafał Miłecki 		combo[c].n_limits = i;
751905491d2cSKalle Valo 		combo[c].limits = mbss_limits;
752005491d2cSKalle Valo 	}
7521f568adacSRafał Miłecki 
752205491d2cSKalle Valo 	wiphy->n_iface_combinations = n_combos;
752305491d2cSKalle Valo 	wiphy->iface_combinations = combo;
752405491d2cSKalle Valo 	return 0;
752505491d2cSKalle Valo 
752605491d2cSKalle Valo err:
752705491d2cSKalle Valo 	kfree(c0_limits);
752805491d2cSKalle Valo 	kfree(p2p_limits);
752905491d2cSKalle Valo 	kfree(mbss_limits);
753005491d2cSKalle Valo 	kfree(combo);
753105491d2cSKalle Valo 	return -ENOMEM;
753205491d2cSKalle Valo }
753305491d2cSKalle Valo 
753405491d2cSKalle Valo #ifdef CONFIG_PM
75350b57010fSArend Van Spriel static const struct wiphy_wowlan_support brcmf_wowlan_support = {
753605491d2cSKalle Valo 	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
753705491d2cSKalle Valo 	.n_patterns = BRCMF_WOWL_MAXPATTERNS,
753805491d2cSKalle Valo 	.pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
753905491d2cSKalle Valo 	.pattern_min_len = 1,
754005491d2cSKalle Valo 	.max_pkt_offset = 1500,
754105491d2cSKalle Valo };
754205491d2cSKalle Valo #endif
754305491d2cSKalle Valo 
brcmf_wiphy_wowl_params(struct wiphy * wiphy,struct brcmf_if * ifp)75443021ad9aSHante Meuleman static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
754505491d2cSKalle Valo {
754605491d2cSKalle Valo #ifdef CONFIG_PM
75473021ad9aSHante Meuleman 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
754816e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
75490b57010fSArend Van Spriel 	struct wiphy_wowlan_support *wowl;
75500b57010fSArend Van Spriel 
75510b57010fSArend Van Spriel 	wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
75520b57010fSArend Van Spriel 		       GFP_KERNEL);
75530b57010fSArend Van Spriel 	if (!wowl) {
755416e64676SRafał Miłecki 		bphy_err(drvr, "only support basic wowlan features\n");
75550b57010fSArend Van Spriel 		wiphy->wowlan = &brcmf_wowlan_support;
75560b57010fSArend Van Spriel 		return;
75570b57010fSArend Van Spriel 	}
75583021ad9aSHante Meuleman 
75593021ad9aSHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
75605c22fb85SHante Meuleman 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
75610b57010fSArend Van Spriel 			wowl->flags |= WIPHY_WOWLAN_NET_DETECT;
75620b57010fSArend Van Spriel 			wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
75633021ad9aSHante Meuleman 			init_waitqueue_head(&cfg->wowl.nd_data_wait);
75643021ad9aSHante Meuleman 		}
75653021ad9aSHante Meuleman 	}
75665c22fb85SHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
75670b57010fSArend Van Spriel 		wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
75680b57010fSArend Van Spriel 		wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
75693021ad9aSHante Meuleman 	}
75705c22fb85SHante Meuleman 
75710b57010fSArend Van Spriel 	wiphy->wowlan = wowl;
757205491d2cSKalle Valo #endif
757305491d2cSKalle Valo }
757405491d2cSKalle Valo 
brcmf_setup_wiphy(struct wiphy * wiphy,struct brcmf_if * ifp)757505491d2cSKalle Valo static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
757605491d2cSKalle Valo {
757705491d2cSKalle Valo 	struct brcmf_pub *drvr = ifp->drvr;
757805491d2cSKalle Valo 	const struct ieee80211_iface_combination *combo;
757905491d2cSKalle Valo 	struct ieee80211_supported_band *band;
758005491d2cSKalle Valo 	u16 max_interfaces = 0;
758194ed6ffbSArend Van Spriel 	bool gscan;
758205491d2cSKalle Valo 	__le32 bandlist[3];
758305491d2cSKalle Valo 	u32 n_bands;
758405491d2cSKalle Valo 	int err, i;
758505491d2cSKalle Valo 
758605491d2cSKalle Valo 	wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
758705491d2cSKalle Valo 	wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
75886c404f34SHante Meuleman 	wiphy->max_num_pmkids = BRCMF_MAXPMKID;
758905491d2cSKalle Valo 
759005491d2cSKalle Valo 	err = brcmf_setup_ifmodes(wiphy, ifp);
759105491d2cSKalle Valo 	if (err)
759205491d2cSKalle Valo 		return err;
759305491d2cSKalle Valo 
759405491d2cSKalle Valo 	for (i = 0, combo = wiphy->iface_combinations;
759505491d2cSKalle Valo 	     i < wiphy->n_iface_combinations; i++, combo++) {
759605491d2cSKalle Valo 		max_interfaces = max(max_interfaces, combo->max_interfaces);
759705491d2cSKalle Valo 	}
759805491d2cSKalle Valo 
759905491d2cSKalle Valo 	for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
760005491d2cSKalle Valo 	     i++) {
760105491d2cSKalle Valo 		u8 *addr = drvr->addresses[i].addr;
760205491d2cSKalle Valo 
760305491d2cSKalle Valo 		memcpy(addr, drvr->mac, ETH_ALEN);
760405491d2cSKalle Valo 		if (i) {
760505491d2cSKalle Valo 			addr[0] |= BIT(1);
760605491d2cSKalle Valo 			addr[ETH_ALEN - 1] ^= i;
760705491d2cSKalle Valo 		}
760805491d2cSKalle Valo 	}
760905491d2cSKalle Valo 	wiphy->addresses = drvr->addresses;
761005491d2cSKalle Valo 	wiphy->n_addresses = i;
761105491d2cSKalle Valo 
761205491d2cSKalle Valo 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
7613240d61a9SHante Meuleman 	wiphy->cipher_suites = brcmf_cipher_suites;
7614240d61a9SHante Meuleman 	wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
7615240d61a9SHante Meuleman 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
7616240d61a9SHante Meuleman 		wiphy->n_cipher_suites--;
76177705ba6fSArend van Spriel 	wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |
76187705ba6fSArend van Spriel 				    BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
76197705ba6fSArend van Spriel 				    BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
76207705ba6fSArend van Spriel 
76210cc0236cSArend Van Spriel 	wiphy->flags |= WIPHY_FLAG_NETNS_OK |
76220cc0236cSArend Van Spriel 			WIPHY_FLAG_PS_ON_BY_DEFAULT |
76231204aa17SRafał Miłecki 			WIPHY_FLAG_HAVE_AP_SME |
762405491d2cSKalle Valo 			WIPHY_FLAG_OFFCHAN_TX |
7625a7b82d47SHante Meuleman 			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
7626a7b82d47SHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
7627a7b82d47SHante Meuleman 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
76287d34b056SHante Meuleman 	if (!ifp->drvr->settings->roamoff)
762905491d2cSKalle Valo 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
76302526ff21SArend van Spriel 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) {
7631b8a64f0eSArend van Spriel 		wiphy_ext_feature_set(wiphy,
7632b8a64f0eSArend van Spriel 				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
76332526ff21SArend van Spriel 		wiphy_ext_feature_set(wiphy,
76342526ff21SArend van Spriel 				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
76353b1e0a7bSChung-Hsien Hsu 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
76363b1e0a7bSChung-Hsien Hsu 			wiphy_ext_feature_set(wiphy,
76373b1e0a7bSChung-Hsien Hsu 					      NL80211_EXT_FEATURE_SAE_OFFLOAD);
76382526ff21SArend van Spriel 	}
7639d5f59c96SChung-Hsien Hsu 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWAUTH)) {
7640787fb926SChung-Hsien Hsu 		wiphy_ext_feature_set(wiphy,
7641787fb926SChung-Hsien Hsu 				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK);
7642d5f59c96SChung-Hsien Hsu 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
7643d5f59c96SChung-Hsien Hsu 			wiphy_ext_feature_set(wiphy,
7644d5f59c96SChung-Hsien Hsu 					      NL80211_EXT_FEATURE_SAE_OFFLOAD_AP);
7645d5f59c96SChung-Hsien Hsu 	}
764605491d2cSKalle Valo 	wiphy->mgmt_stypes = brcmf_txrx_stypes;
764705491d2cSKalle Valo 	wiphy->max_remain_on_channel_duration = 5000;
764894ed6ffbSArend Van Spriel 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
764994ed6ffbSArend Van Spriel 		gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN);
765094ed6ffbSArend Van Spriel 		brcmf_pno_wiphy_params(wiphy, gscan);
765194ed6ffbSArend Van Spriel 	}
765205491d2cSKalle Valo 	/* vendor commands/events support */
765305491d2cSKalle Valo 	wiphy->vendor_commands = brcmf_vendor_cmds;
765405491d2cSKalle Valo 	wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
765505491d2cSKalle Valo 
765605491d2cSKalle Valo 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
76573021ad9aSHante Meuleman 		brcmf_wiphy_wowl_params(wiphy, ifp);
765805491d2cSKalle Valo 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
765905491d2cSKalle Valo 				     sizeof(bandlist));
766005491d2cSKalle Valo 	if (err) {
766116e64676SRafał Miłecki 		bphy_err(drvr, "could not obtain band info: err=%d\n", err);
766205491d2cSKalle Valo 		return err;
766305491d2cSKalle Valo 	}
766405491d2cSKalle Valo 	/* first entry in bandlist is number of bands */
766505491d2cSKalle Valo 	n_bands = le32_to_cpu(bandlist[0]);
766605491d2cSKalle Valo 	for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
766705491d2cSKalle Valo 		if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
766805491d2cSKalle Valo 			band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
766905491d2cSKalle Valo 				       GFP_KERNEL);
767005491d2cSKalle Valo 			if (!band)
767105491d2cSKalle Valo 				return -ENOMEM;
767205491d2cSKalle Valo 
767305491d2cSKalle Valo 			band->channels = kmemdup(&__wl_2ghz_channels,
767405491d2cSKalle Valo 						 sizeof(__wl_2ghz_channels),
767505491d2cSKalle Valo 						 GFP_KERNEL);
767605491d2cSKalle Valo 			if (!band->channels) {
767705491d2cSKalle Valo 				kfree(band);
767805491d2cSKalle Valo 				return -ENOMEM;
767905491d2cSKalle Valo 			}
768005491d2cSKalle Valo 
768105491d2cSKalle Valo 			band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
768257fbcce3SJohannes Berg 			wiphy->bands[NL80211_BAND_2GHZ] = band;
768305491d2cSKalle Valo 		}
768405491d2cSKalle Valo 		if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
768505491d2cSKalle Valo 			band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
768605491d2cSKalle Valo 				       GFP_KERNEL);
768705491d2cSKalle Valo 			if (!band)
768805491d2cSKalle Valo 				return -ENOMEM;
768905491d2cSKalle Valo 
769005491d2cSKalle Valo 			band->channels = kmemdup(&__wl_5ghz_channels,
769105491d2cSKalle Valo 						 sizeof(__wl_5ghz_channels),
769205491d2cSKalle Valo 						 GFP_KERNEL);
769305491d2cSKalle Valo 			if (!band->channels) {
769405491d2cSKalle Valo 				kfree(band);
769505491d2cSKalle Valo 				return -ENOMEM;
769605491d2cSKalle Valo 			}
769705491d2cSKalle Valo 
769805491d2cSKalle Valo 			band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
769957fbcce3SJohannes Berg 			wiphy->bands[NL80211_BAND_5GHZ] = band;
770005491d2cSKalle Valo 		}
770105491d2cSKalle Valo 	}
77020f83ff69SRafał Miłecki 
7703011a56a3SArend van Spriel 	if (wiphy->bands[NL80211_BAND_5GHZ] &&
7704011a56a3SArend van Spriel 	    brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DOT11H))
7705011a56a3SArend van Spriel 		wiphy_ext_feature_set(wiphy,
7706011a56a3SArend van Spriel 				      NL80211_EXT_FEATURE_DFS_OFFLOAD);
7707011a56a3SArend van Spriel 
77087dd56ea4SAlvin Šipraga 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
77097dd56ea4SAlvin Šipraga 
77100f83ff69SRafał Miłecki 	wiphy_read_of_freq_limits(wiphy);
77110f83ff69SRafał Miłecki 
7712ab99063fSRafał Miłecki 	return 0;
771305491d2cSKalle Valo }
771405491d2cSKalle Valo 
brcmf_config_dongle(struct brcmf_cfg80211_info * cfg)771505491d2cSKalle Valo static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
771605491d2cSKalle Valo {
771716e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
771805491d2cSKalle Valo 	struct net_device *ndev;
771905491d2cSKalle Valo 	struct wireless_dev *wdev;
772005491d2cSKalle Valo 	struct brcmf_if *ifp;
772105491d2cSKalle Valo 	s32 power_mode;
772205491d2cSKalle Valo 	s32 err = 0;
772305491d2cSKalle Valo 
772405491d2cSKalle Valo 	if (cfg->dongle_up)
772505491d2cSKalle Valo 		return err;
772605491d2cSKalle Valo 
772705491d2cSKalle Valo 	ndev = cfg_to_ndev(cfg);
772805491d2cSKalle Valo 	wdev = ndev->ieee80211_ptr;
772905491d2cSKalle Valo 	ifp = netdev_priv(ndev);
773005491d2cSKalle Valo 
773105491d2cSKalle Valo 	/* make sure RF is ready for work */
773205491d2cSKalle Valo 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
773305491d2cSKalle Valo 
77341678ba8eSHante Meuleman 	brcmf_dongle_scantime(ifp);
773505491d2cSKalle Valo 
773605491d2cSKalle Valo 	power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
773705491d2cSKalle Valo 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
773805491d2cSKalle Valo 	if (err)
773905491d2cSKalle Valo 		goto default_conf_out;
774005491d2cSKalle Valo 	brcmf_dbg(INFO, "power save set to %s\n",
774105491d2cSKalle Valo 		  (power_mode ? "enabled" : "disabled"));
774205491d2cSKalle Valo 
77431119e23eSHante Meuleman 	err = brcmf_dongle_roam(ifp);
774405491d2cSKalle Valo 	if (err)
774505491d2cSKalle Valo 		goto default_conf_out;
774605491d2cSKalle Valo 	err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
7747818a986eSJohannes Berg 					  NULL);
774805491d2cSKalle Valo 	if (err)
774905491d2cSKalle Valo 		goto default_conf_out;
775005491d2cSKalle Valo 
775152f22fb2SFranky Lin 	brcmf_configure_arp_nd_offload(ifp, true);
775205491d2cSKalle Valo 
7753a3bdc6deSWright Feng 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1);
7754a3bdc6deSWright Feng 	if (err) {
775516e64676SRafał Miłecki 		bphy_err(drvr, "failed to set frameburst mode\n");
7756a3bdc6deSWright Feng 		goto default_conf_out;
7757a3bdc6deSWright Feng 	}
7758a3bdc6deSWright Feng 
775905491d2cSKalle Valo 	cfg->dongle_up = true;
776005491d2cSKalle Valo default_conf_out:
776105491d2cSKalle Valo 
776205491d2cSKalle Valo 	return err;
776305491d2cSKalle Valo 
776405491d2cSKalle Valo }
776505491d2cSKalle Valo 
__brcmf_cfg80211_up(struct brcmf_if * ifp)776605491d2cSKalle Valo static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
776705491d2cSKalle Valo {
776805491d2cSKalle Valo 	set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
776905491d2cSKalle Valo 
777005491d2cSKalle Valo 	return brcmf_config_dongle(ifp->drvr->config);
777105491d2cSKalle Valo }
777205491d2cSKalle Valo 
__brcmf_cfg80211_down(struct brcmf_if * ifp)777305491d2cSKalle Valo static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
777405491d2cSKalle Valo {
777505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
777605491d2cSKalle Valo 
777705491d2cSKalle Valo 	/*
777805491d2cSKalle Valo 	 * While going down, if associated with AP disassociate
777905491d2cSKalle Valo 	 * from AP to save power
778005491d2cSKalle Valo 	 */
778105491d2cSKalle Valo 	if (check_vif_up(ifp->vif)) {
77821b050d97SSoontak Lee 		brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED, true);
778305491d2cSKalle Valo 
778405491d2cSKalle Valo 		/* Make sure WPA_Supplicant receives all the event
778505491d2cSKalle Valo 		   generated due to DISASSOC call to the fw to keep
778605491d2cSKalle Valo 		   the state fw and WPA_Supplicant state consistent
778705491d2cSKalle Valo 		 */
778805491d2cSKalle Valo 		brcmf_delay(500);
778905491d2cSKalle Valo 	}
779005491d2cSKalle Valo 
779105491d2cSKalle Valo 	brcmf_abort_scanning(cfg);
779205491d2cSKalle Valo 	clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
779305491d2cSKalle Valo 
779405491d2cSKalle Valo 	return 0;
779505491d2cSKalle Valo }
779605491d2cSKalle Valo 
brcmf_cfg80211_up(struct net_device * ndev)779705491d2cSKalle Valo s32 brcmf_cfg80211_up(struct net_device *ndev)
779805491d2cSKalle Valo {
779905491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
780005491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
780105491d2cSKalle Valo 	s32 err = 0;
780205491d2cSKalle Valo 
780305491d2cSKalle Valo 	mutex_lock(&cfg->usr_sync);
780405491d2cSKalle Valo 	err = __brcmf_cfg80211_up(ifp);
780505491d2cSKalle Valo 	mutex_unlock(&cfg->usr_sync);
780605491d2cSKalle Valo 
780705491d2cSKalle Valo 	return err;
780805491d2cSKalle Valo }
780905491d2cSKalle Valo 
brcmf_cfg80211_down(struct net_device * ndev)781005491d2cSKalle Valo s32 brcmf_cfg80211_down(struct net_device *ndev)
781105491d2cSKalle Valo {
781205491d2cSKalle Valo 	struct brcmf_if *ifp = netdev_priv(ndev);
781305491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
781405491d2cSKalle Valo 	s32 err = 0;
781505491d2cSKalle Valo 
781605491d2cSKalle Valo 	mutex_lock(&cfg->usr_sync);
781705491d2cSKalle Valo 	err = __brcmf_cfg80211_down(ifp);
781805491d2cSKalle Valo 	mutex_unlock(&cfg->usr_sync);
781905491d2cSKalle Valo 
782005491d2cSKalle Valo 	return err;
782105491d2cSKalle Valo }
782205491d2cSKalle Valo 
brcmf_cfg80211_get_iftype(struct brcmf_if * ifp)782305491d2cSKalle Valo enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
782405491d2cSKalle Valo {
782505491d2cSKalle Valo 	struct wireless_dev *wdev = &ifp->vif->wdev;
782605491d2cSKalle Valo 
782705491d2cSKalle Valo 	return wdev->iftype;
782805491d2cSKalle Valo }
782905491d2cSKalle Valo 
brcmf_get_vif_state_any(struct brcmf_cfg80211_info * cfg,unsigned long state)783005491d2cSKalle Valo bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
783105491d2cSKalle Valo 			     unsigned long state)
783205491d2cSKalle Valo {
783305491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
783405491d2cSKalle Valo 
783505491d2cSKalle Valo 	list_for_each_entry(vif, &cfg->vif_list, list) {
783605491d2cSKalle Valo 		if (test_bit(state, &vif->sme_state))
783705491d2cSKalle Valo 			return true;
783805491d2cSKalle Valo 	}
783905491d2cSKalle Valo 	return false;
784005491d2cSKalle Valo }
784105491d2cSKalle Valo 
vif_event_equals(struct brcmf_cfg80211_vif_event * event,u8 action)784205491d2cSKalle Valo static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
784305491d2cSKalle Valo 				    u8 action)
784405491d2cSKalle Valo {
784505491d2cSKalle Valo 	u8 evt_action;
784605491d2cSKalle Valo 
7847b64abcb7Smhiramat@kernel.org 	spin_lock(&event->vif_event_lock);
784805491d2cSKalle Valo 	evt_action = event->action;
7849b64abcb7Smhiramat@kernel.org 	spin_unlock(&event->vif_event_lock);
785005491d2cSKalle Valo 	return evt_action == action;
785105491d2cSKalle Valo }
785205491d2cSKalle Valo 
brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info * cfg,struct brcmf_cfg80211_vif * vif)785305491d2cSKalle Valo void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
785405491d2cSKalle Valo 				  struct brcmf_cfg80211_vif *vif)
785505491d2cSKalle Valo {
785605491d2cSKalle Valo 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
785705491d2cSKalle Valo 
7858b64abcb7Smhiramat@kernel.org 	spin_lock(&event->vif_event_lock);
785905491d2cSKalle Valo 	event->vif = vif;
786005491d2cSKalle Valo 	event->action = 0;
7861b64abcb7Smhiramat@kernel.org 	spin_unlock(&event->vif_event_lock);
786205491d2cSKalle Valo }
786305491d2cSKalle Valo 
brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info * cfg)786405491d2cSKalle Valo bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
786505491d2cSKalle Valo {
786605491d2cSKalle Valo 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
786705491d2cSKalle Valo 	bool armed;
786805491d2cSKalle Valo 
7869b64abcb7Smhiramat@kernel.org 	spin_lock(&event->vif_event_lock);
787005491d2cSKalle Valo 	armed = event->vif != NULL;
7871b64abcb7Smhiramat@kernel.org 	spin_unlock(&event->vif_event_lock);
787205491d2cSKalle Valo 
787305491d2cSKalle Valo 	return armed;
787405491d2cSKalle Valo }
7875a9eb0c4bSArend van Spriel 
brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info * cfg,u8 action,ulong timeout)7876a9eb0c4bSArend van Spriel int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
787705491d2cSKalle Valo 				  u8 action, ulong timeout)
787805491d2cSKalle Valo {
787905491d2cSKalle Valo 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
788005491d2cSKalle Valo 
788105491d2cSKalle Valo 	return wait_event_timeout(event->vif_wq,
788205491d2cSKalle Valo 				  vif_event_equals(event, action), timeout);
788305491d2cSKalle Valo }
788405491d2cSKalle Valo 
brmcf_use_iso3166_ccode_fallback(struct brcmf_pub * drvr)7885a21bf90eSHans de Goede static bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr)
7886a21bf90eSHans de Goede {
78875c54ab24SAlvin Šipraga 	if (drvr->settings->trivial_ccode_map)
78885c54ab24SAlvin Šipraga 		return true;
78895c54ab24SAlvin Šipraga 
7890a21bf90eSHans de Goede 	switch (drvr->bus_if->chip) {
7891d173d020SHans de Goede 	case BRCM_CC_43430_CHIP_ID:
7892a21bf90eSHans de Goede 	case BRCM_CC_4345_CHIP_ID:
7893659fda7fSHans de Goede 	case BRCM_CC_4356_CHIP_ID:
789421947f3aSHamid Zamani 	case BRCM_CC_43602_CHIP_ID:
7895a21bf90eSHans de Goede 		return true;
7896a21bf90eSHans de Goede 	default:
7897a21bf90eSHans de Goede 		return false;
7898a21bf90eSHans de Goede 	}
7899a21bf90eSHans de Goede }
7900a21bf90eSHans de Goede 
brcmf_translate_country_code(struct brcmf_pub * drvr,char alpha2[2],struct brcmf_fil_country_le * ccreq)790173345fd2SHante Meuleman static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
790273345fd2SHante Meuleman 					struct brcmf_fil_country_le *ccreq)
790373345fd2SHante Meuleman {
79044d792895SHante Meuleman 	struct brcmfmac_pd_cc *country_codes;
79054d792895SHante Meuleman 	struct brcmfmac_pd_cc_entry *cc;
790673345fd2SHante Meuleman 	s32 found_index;
790773345fd2SHante Meuleman 	int i;
790873345fd2SHante Meuleman 
790973345fd2SHante Meuleman 	if ((alpha2[0] == ccreq->country_abbrev[0]) &&
791073345fd2SHante Meuleman 	    (alpha2[1] == ccreq->country_abbrev[1])) {
791173345fd2SHante Meuleman 		brcmf_dbg(TRACE, "Country code already set\n");
791273345fd2SHante Meuleman 		return -EAGAIN;
791373345fd2SHante Meuleman 	}
791473345fd2SHante Meuleman 
7915a21bf90eSHans de Goede 	country_codes = drvr->settings->country_codes;
7916a21bf90eSHans de Goede 	if (!country_codes) {
7917a21bf90eSHans de Goede 		if (brmcf_use_iso3166_ccode_fallback(drvr)) {
7918a21bf90eSHans de Goede 			brcmf_dbg(TRACE, "No country codes configured for device, using ISO3166 code and 0 rev\n");
7919a21bf90eSHans de Goede 			memset(ccreq, 0, sizeof(*ccreq));
7920a21bf90eSHans de Goede 			ccreq->country_abbrev[0] = alpha2[0];
7921a21bf90eSHans de Goede 			ccreq->country_abbrev[1] = alpha2[1];
7922a21bf90eSHans de Goede 			ccreq->ccode[0] = alpha2[0];
7923a21bf90eSHans de Goede 			ccreq->ccode[1] = alpha2[1];
7924a21bf90eSHans de Goede 			return 0;
7925a21bf90eSHans de Goede 		}
7926a21bf90eSHans de Goede 
7927a21bf90eSHans de Goede 		brcmf_dbg(TRACE, "No country codes configured for device\n");
7928a21bf90eSHans de Goede 		return -EINVAL;
7929a21bf90eSHans de Goede 	}
7930a21bf90eSHans de Goede 
793173345fd2SHante Meuleman 	found_index = -1;
793273345fd2SHante Meuleman 	for (i = 0; i < country_codes->table_size; i++) {
793373345fd2SHante Meuleman 		cc = &country_codes->table[i];
793473345fd2SHante Meuleman 		if ((cc->iso3166[0] == '\0') && (found_index == -1))
793573345fd2SHante Meuleman 			found_index = i;
793673345fd2SHante Meuleman 		if ((cc->iso3166[0] == alpha2[0]) &&
793773345fd2SHante Meuleman 		    (cc->iso3166[1] == alpha2[1])) {
793873345fd2SHante Meuleman 			found_index = i;
793973345fd2SHante Meuleman 			break;
794073345fd2SHante Meuleman 		}
794173345fd2SHante Meuleman 	}
794273345fd2SHante Meuleman 	if (found_index == -1) {
794373345fd2SHante Meuleman 		brcmf_dbg(TRACE, "No country code match found\n");
794473345fd2SHante Meuleman 		return -EINVAL;
794573345fd2SHante Meuleman 	}
794673345fd2SHante Meuleman 	memset(ccreq, 0, sizeof(*ccreq));
794773345fd2SHante Meuleman 	ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
794873345fd2SHante Meuleman 	memcpy(ccreq->ccode, country_codes->table[found_index].cc,
794973345fd2SHante Meuleman 	       BRCMF_COUNTRY_BUF_SZ);
795073345fd2SHante Meuleman 	ccreq->country_abbrev[0] = alpha2[0];
795173345fd2SHante Meuleman 	ccreq->country_abbrev[1] = alpha2[1];
795273345fd2SHante Meuleman 	ccreq->country_abbrev[2] = 0;
795373345fd2SHante Meuleman 
795473345fd2SHante Meuleman 	return 0;
795573345fd2SHante Meuleman }
795673345fd2SHante Meuleman 
79576c04deaeSWright Feng static int
brcmf_parse_dump_obss(char * buf,struct brcmf_dump_survey * survey)79586c04deaeSWright Feng brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey)
79596c04deaeSWright Feng {
79606c04deaeSWright Feng 	int i;
79616c04deaeSWright Feng 	char *token;
79626c04deaeSWright Feng 	char delim[] = "\n ";
79636c04deaeSWright Feng 	unsigned long val;
79646c04deaeSWright Feng 	int err = 0;
79656c04deaeSWright Feng 
79666c04deaeSWright Feng 	token = strsep(&buf, delim);
79676c04deaeSWright Feng 	while (token) {
79686c04deaeSWright Feng 		if (!strcmp(token, "OBSS")) {
79696c04deaeSWright Feng 			for (i = 0; i < OBSS_TOKEN_IDX; i++)
79706c04deaeSWright Feng 				token = strsep(&buf, delim);
79716c04deaeSWright Feng 			err = kstrtoul(token, 10, &val);
79726c04deaeSWright Feng 			if (err)
79736c04deaeSWright Feng 				break;
79746c04deaeSWright Feng 			survey->obss = val;
79756c04deaeSWright Feng 		}
79766c04deaeSWright Feng 
79776c04deaeSWright Feng 		if (!strcmp(token, "IBSS")) {
79786c04deaeSWright Feng 			for (i = 0; i < IBSS_TOKEN_IDX; i++)
79796c04deaeSWright Feng 				token = strsep(&buf, delim);
79806c04deaeSWright Feng 			err = kstrtoul(token, 10, &val);
79816c04deaeSWright Feng 			if (err)
79826c04deaeSWright Feng 				break;
79836c04deaeSWright Feng 			survey->ibss = val;
79846c04deaeSWright Feng 		}
79856c04deaeSWright Feng 
79866c04deaeSWright Feng 		if (!strcmp(token, "TXDur")) {
79876c04deaeSWright Feng 			for (i = 0; i < TX_TOKEN_IDX; i++)
79886c04deaeSWright Feng 				token = strsep(&buf, delim);
79896c04deaeSWright Feng 			err = kstrtoul(token, 10, &val);
79906c04deaeSWright Feng 			if (err)
79916c04deaeSWright Feng 				break;
79926c04deaeSWright Feng 			survey->tx = val;
79936c04deaeSWright Feng 		}
79946c04deaeSWright Feng 
79956c04deaeSWright Feng 		if (!strcmp(token, "Category")) {
79966c04deaeSWright Feng 			for (i = 0; i < CTG_TOKEN_IDX; i++)
79976c04deaeSWright Feng 				token = strsep(&buf, delim);
79986c04deaeSWright Feng 			err = kstrtoul(token, 10, &val);
79996c04deaeSWright Feng 			if (err)
80006c04deaeSWright Feng 				break;
80016c04deaeSWright Feng 			survey->no_ctg = val;
80026c04deaeSWright Feng 		}
80036c04deaeSWright Feng 
80046c04deaeSWright Feng 		if (!strcmp(token, "Packet")) {
80056c04deaeSWright Feng 			for (i = 0; i < PKT_TOKEN_IDX; i++)
80066c04deaeSWright Feng 				token = strsep(&buf, delim);
80076c04deaeSWright Feng 			err = kstrtoul(token, 10, &val);
80086c04deaeSWright Feng 			if (err)
80096c04deaeSWright Feng 				break;
80106c04deaeSWright Feng 			survey->no_pckt = val;
80116c04deaeSWright Feng 		}
80126c04deaeSWright Feng 
80136c04deaeSWright Feng 		if (!strcmp(token, "Opp(time):")) {
80146c04deaeSWright Feng 			for (i = 0; i < IDLE_TOKEN_IDX; i++)
80156c04deaeSWright Feng 				token = strsep(&buf, delim);
80166c04deaeSWright Feng 			err = kstrtoul(token, 10, &val);
80176c04deaeSWright Feng 			if (err)
80186c04deaeSWright Feng 				break;
80196c04deaeSWright Feng 			survey->idle = val;
80206c04deaeSWright Feng 		}
80216c04deaeSWright Feng 
80226c04deaeSWright Feng 		token = strsep(&buf, delim);
80236c04deaeSWright Feng 	}
80246c04deaeSWright Feng 
80256c04deaeSWright Feng 	return err;
80266c04deaeSWright Feng }
80276c04deaeSWright Feng 
80286c04deaeSWright Feng static int
brcmf_dump_obss(struct brcmf_if * ifp,struct cca_msrmnt_query req,struct brcmf_dump_survey * survey)80296c04deaeSWright Feng brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req,
80306c04deaeSWright Feng 		struct brcmf_dump_survey *survey)
80316c04deaeSWright Feng {
80326c04deaeSWright Feng 	struct cca_stats_n_flags *results;
80336c04deaeSWright Feng 	char *buf;
80346c04deaeSWright Feng 	int err;
80356c04deaeSWright Feng 
80366c04deaeSWright Feng 	buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL);
8037216647e6SWright Feng 	if (!buf)
80386c04deaeSWright Feng 		return -ENOMEM;
80396c04deaeSWright Feng 
80406c04deaeSWright Feng 	memcpy(buf, &req, sizeof(struct cca_msrmnt_query));
80416c04deaeSWright Feng 	err = brcmf_fil_iovar_data_get(ifp, "dump_obss",
80426c04deaeSWright Feng 				       buf, BRCMF_DCMD_MEDLEN);
8043216647e6SWright Feng 	if (err) {
80446c04deaeSWright Feng 		brcmf_err("dump_obss error (%d)\n", err);
8045216647e6SWright Feng 		err = -EINVAL;
80466c04deaeSWright Feng 		goto exit;
80476c04deaeSWright Feng 	}
80486c04deaeSWright Feng 	results = (struct cca_stats_n_flags *)(buf);
80496c04deaeSWright Feng 
80506c04deaeSWright Feng 	if (req.msrmnt_query)
80516c04deaeSWright Feng 		brcmf_parse_dump_obss(results->buf, survey);
80526c04deaeSWright Feng 
80536c04deaeSWright Feng exit:
80546c04deaeSWright Feng 	kfree(buf);
8055216647e6SWright Feng 	return err;
80566c04deaeSWright Feng }
80576c04deaeSWright Feng 
80586c04deaeSWright Feng static s32
brcmf_set_channel(struct brcmf_cfg80211_info * cfg,struct ieee80211_channel * chan)80595fac1858SArend van Spriel brcmf_set_channel(struct brcmf_cfg80211_info *cfg, struct ieee80211_channel *chan)
80606c04deaeSWright Feng {
80616c04deaeSWright Feng 	u16 chspec = 0;
80626c04deaeSWright Feng 	int err = 0;
80636c04deaeSWright Feng 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
80646c04deaeSWright Feng 
8065e5d1ab1aSArend van Spriel 	if (chan->flags & IEEE80211_CHAN_DISABLED)
8066e5d1ab1aSArend van Spriel 		return -EINVAL;
8067e5d1ab1aSArend van Spriel 
80686c04deaeSWright Feng 	/* set_channel */
80696c04deaeSWright Feng 	chspec = channel_to_chanspec(&cfg->d11inf, chan);
80706c04deaeSWright Feng 	if (chspec != INVCHANSPEC) {
80716c04deaeSWright Feng 		err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec);
80726c04deaeSWright Feng 		if (err) {
80736c04deaeSWright Feng 			brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err);
80746c04deaeSWright Feng 			err = -EINVAL;
80756c04deaeSWright Feng 		}
80766c04deaeSWright Feng 	} else {
80776c04deaeSWright Feng 		brcmf_err("failed to convert host chanspec to fw chanspec\n");
80786c04deaeSWright Feng 		err = -EINVAL;
80796c04deaeSWright Feng 	}
80806c04deaeSWright Feng 
80816c04deaeSWright Feng 	return err;
80826c04deaeSWright Feng }
80836c04deaeSWright Feng 
80846c04deaeSWright Feng static int
brcmf_cfg80211_dump_survey(struct wiphy * wiphy,struct net_device * ndev,int idx,struct survey_info * info)80856c04deaeSWright Feng brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
80866c04deaeSWright Feng 			   int idx, struct survey_info *info)
80876c04deaeSWright Feng {
80886c04deaeSWright Feng 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
80896c04deaeSWright Feng 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
80906c04deaeSWright Feng 	struct brcmf_dump_survey survey = {};
80916c04deaeSWright Feng 	struct ieee80211_supported_band *band;
8092aadb50d1SArend van Spriel 	enum nl80211_band band_id;
80936c04deaeSWright Feng 	struct cca_msrmnt_query req;
8094216647e6SWright Feng 	u32 noise;
80956c04deaeSWright Feng 	int err;
80966c04deaeSWright Feng 
80976c04deaeSWright Feng 	brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx);
80986c04deaeSWright Feng 
809962ccb2e6SRamesh Rangavittal 	/* Do not run survey when VIF in CONNECTING / CONNECTED states */
810062ccb2e6SRamesh Rangavittal 	if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) ||
810162ccb2e6SRamesh Rangavittal 	    (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) {
810262ccb2e6SRamesh Rangavittal 		return -EBUSY;
810362ccb2e6SRamesh Rangavittal 	}
810462ccb2e6SRamesh Rangavittal 
8105aadb50d1SArend van Spriel 	for (band_id = 0; band_id < NUM_NL80211_BANDS; band_id++) {
8106aadb50d1SArend van Spriel 		band = wiphy->bands[band_id];
8107aadb50d1SArend van Spriel 		if (!band)
8108aadb50d1SArend van Spriel 			continue;
8109aadb50d1SArend van Spriel 		if (idx >= band->n_channels) {
81106c04deaeSWright Feng 			idx -= band->n_channels;
8111aadb50d1SArend van Spriel 			continue;
81126c04deaeSWright Feng 		}
81136c04deaeSWright Feng 
8114aadb50d1SArend van Spriel 		info->channel = &band->channels[idx];
8115aadb50d1SArend van Spriel 		break;
81166c04deaeSWright Feng 	}
8117aadb50d1SArend van Spriel 	if (band_id == NUM_NL80211_BANDS)
8118aadb50d1SArend van Spriel 		return -ENOENT;
81196c04deaeSWright Feng 
81206c04deaeSWright Feng 	/* Setting current channel to the requested channel */
81216c04deaeSWright Feng 	info->filled = 0;
81225fac1858SArend van Spriel 	if (brcmf_set_channel(cfg, info->channel))
81236c04deaeSWright Feng 		return 0;
81246c04deaeSWright Feng 
81256c04deaeSWright Feng 	/* Disable mpc */
8126216647e6SWright Feng 	brcmf_set_mpc(ifp, 0);
8127216647e6SWright Feng 
81286c04deaeSWright Feng 	/* Set interface up, explicitly. */
8129216647e6SWright Feng 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
81306c04deaeSWright Feng 	if (err) {
8131216647e6SWright Feng 		brcmf_err("set interface up failed, err = %d\n", err);
8132216647e6SWright Feng 		goto exit;
81336c04deaeSWright Feng 	}
81346c04deaeSWright Feng 
81356c04deaeSWright Feng 	/* Get noise value */
81366c04deaeSWright Feng 	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise);
81376c04deaeSWright Feng 	if (err) {
8138216647e6SWright Feng 		brcmf_err("Get Phy Noise failed, use dummy value\n");
81396c04deaeSWright Feng 		noise = CHAN_NOISE_DUMMY;
81406c04deaeSWright Feng 	}
81416c04deaeSWright Feng 
81426c04deaeSWright Feng 	/* Start Measurement for obss stats on current channel */
81436c04deaeSWright Feng 	req.msrmnt_query = 0;
81446c04deaeSWright Feng 	req.time_req = ACS_MSRMNT_DELAY;
81456c04deaeSWright Feng 	err = brcmf_dump_obss(ifp, req, &survey);
81466c04deaeSWright Feng 	if (err)
81476c04deaeSWright Feng 		goto exit;
81486c04deaeSWright Feng 
81496c04deaeSWright Feng 	/* Add 10 ms for IOVAR completion */
81506c04deaeSWright Feng 	msleep(ACS_MSRMNT_DELAY + 10);
81516c04deaeSWright Feng 
81526c04deaeSWright Feng 	/* Issue IOVAR to collect measurement results */
81536c04deaeSWright Feng 	req.msrmnt_query = 1;
81546c04deaeSWright Feng 	err = brcmf_dump_obss(ifp, req, &survey);
8155216647e6SWright Feng 	if (err)
81566c04deaeSWright Feng 		goto exit;
81576c04deaeSWright Feng 
81586c04deaeSWright Feng 	info->noise = noise;
81596c04deaeSWright Feng 	info->time = ACS_MSRMNT_DELAY;
81606c04deaeSWright Feng 	info->time_busy = ACS_MSRMNT_DELAY - survey.idle;
81616c04deaeSWright Feng 	info->time_rx = survey.obss + survey.ibss + survey.no_ctg +
81626c04deaeSWright Feng 		survey.no_pckt;
81636c04deaeSWright Feng 	info->time_tx = survey.tx;
81646c04deaeSWright Feng 	info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
81656c04deaeSWright Feng 		SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX |
81666c04deaeSWright Feng 		SURVEY_INFO_TIME_TX;
81676c04deaeSWright Feng 
81686c04deaeSWright Feng 	brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n",
8169e5d1ab1aSArend van Spriel 		  ieee80211_frequency_to_channel(info->channel->center_freq),
81706c04deaeSWright Feng 		  ACS_MSRMNT_DELAY);
81716c04deaeSWright Feng 	brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n",
81726c04deaeSWright Feng 		  info->noise, info->time_busy, info->time_rx, info->time_tx);
81736c04deaeSWright Feng 
81746c04deaeSWright Feng exit:
8175216647e6SWright Feng 	if (!brcmf_is_apmode(ifp->vif))
8176216647e6SWright Feng 		brcmf_set_mpc(ifp, 1);
81776c04deaeSWright Feng 	return err;
81786c04deaeSWright Feng }
81796c04deaeSWright Feng 
brcmf_cfg80211_reg_notifier(struct wiphy * wiphy,struct regulatory_request * req)818005491d2cSKalle Valo static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
818105491d2cSKalle Valo 					struct regulatory_request *req)
818205491d2cSKalle Valo {
8183856d5a01SArend Van Spriel 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
8184856d5a01SArend Van Spriel 	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
818516e64676SRafał Miłecki 	struct brcmf_pub *drvr = cfg->pub;
818605491d2cSKalle Valo 	struct brcmf_fil_country_le ccreq;
818773345fd2SHante Meuleman 	s32 err;
818805491d2cSKalle Valo 	int i;
818905491d2cSKalle Valo 
819026e53788SHans de Goede 	/* The country code gets set to "00" by default at boot, ignore */
819126e53788SHans de Goede 	if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
819226e53788SHans de Goede 		return;
819326e53788SHans de Goede 
819405491d2cSKalle Valo 	/* ignore non-ISO3166 country codes */
81959b9322dbSStefan Wahren 	for (i = 0; i < 2; i++)
819605491d2cSKalle Valo 		if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
819716e64676SRafał Miłecki 			bphy_err(drvr, "not an ISO3166 code (0x%02x 0x%02x)\n",
819873345fd2SHante Meuleman 				 req->alpha2[0], req->alpha2[1]);
819905491d2cSKalle Valo 			return;
820005491d2cSKalle Valo 		}
820173345fd2SHante Meuleman 
820273345fd2SHante Meuleman 	brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
820373345fd2SHante Meuleman 		  req->alpha2[0], req->alpha2[1]);
820473345fd2SHante Meuleman 
820573345fd2SHante Meuleman 	err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
820673345fd2SHante Meuleman 	if (err) {
820716e64676SRafał Miłecki 		bphy_err(drvr, "Country code iovar returned err = %d\n", err);
820873345fd2SHante Meuleman 		return;
820973345fd2SHante Meuleman 	}
821073345fd2SHante Meuleman 
821173345fd2SHante Meuleman 	err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
821273345fd2SHante Meuleman 	if (err)
821373345fd2SHante Meuleman 		return;
821473345fd2SHante Meuleman 
821573345fd2SHante Meuleman 	err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
821673345fd2SHante Meuleman 	if (err) {
821716e64676SRafał Miłecki 		bphy_err(drvr, "Firmware rejected country setting\n");
821805491d2cSKalle Valo 		return;
821905491d2cSKalle Valo 	}
8220856d5a01SArend Van Spriel 	brcmf_setup_wiphybands(cfg);
822105491d2cSKalle Valo }
822205491d2cSKalle Valo 
brcmf_free_wiphy(struct wiphy * wiphy)822305491d2cSKalle Valo static void brcmf_free_wiphy(struct wiphy *wiphy)
822405491d2cSKalle Valo {
822505491d2cSKalle Valo 	int i;
822605491d2cSKalle Valo 
822705491d2cSKalle Valo 	if (!wiphy)
822805491d2cSKalle Valo 		return;
822905491d2cSKalle Valo 
823005491d2cSKalle Valo 	if (wiphy->iface_combinations) {
823105491d2cSKalle Valo 		for (i = 0; i < wiphy->n_iface_combinations; i++)
823205491d2cSKalle Valo 			kfree(wiphy->iface_combinations[i].limits);
823305491d2cSKalle Valo 	}
823405491d2cSKalle Valo 	kfree(wiphy->iface_combinations);
823557fbcce3SJohannes Berg 	if (wiphy->bands[NL80211_BAND_2GHZ]) {
823657fbcce3SJohannes Berg 		kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
823757fbcce3SJohannes Berg 		kfree(wiphy->bands[NL80211_BAND_2GHZ]);
823805491d2cSKalle Valo 	}
823957fbcce3SJohannes Berg 	if (wiphy->bands[NL80211_BAND_5GHZ]) {
824057fbcce3SJohannes Berg 		kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
824157fbcce3SJohannes Berg 		kfree(wiphy->bands[NL80211_BAND_5GHZ]);
824205491d2cSKalle Valo 	}
82430b57010fSArend Van Spriel #if IS_ENABLED(CONFIG_PM)
82440b57010fSArend Van Spriel 	if (wiphy->wowlan != &brcmf_wowlan_support)
82450b57010fSArend Van Spriel 		kfree(wiphy->wowlan);
82460b57010fSArend Van Spriel #endif
824705491d2cSKalle Valo }
824805491d2cSKalle Valo 
brcmf_cfg80211_attach(struct brcmf_pub * drvr,struct cfg80211_ops * ops,bool p2pdev_forced)824905491d2cSKalle Valo struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
8250856d5a01SArend Van Spriel 						  struct cfg80211_ops *ops,
825105491d2cSKalle Valo 						  bool p2pdev_forced)
825205491d2cSKalle Valo {
8253856d5a01SArend Van Spriel 	struct wiphy *wiphy = drvr->wiphy;
825405491d2cSKalle Valo 	struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
825505491d2cSKalle Valo 	struct brcmf_cfg80211_info *cfg;
825605491d2cSKalle Valo 	struct brcmf_cfg80211_vif *vif;
825705491d2cSKalle Valo 	struct brcmf_if *ifp;
825805491d2cSKalle Valo 	s32 err = 0;
825905491d2cSKalle Valo 	s32 io_type;
826005491d2cSKalle Valo 	u16 *cap = NULL;
826105491d2cSKalle Valo 
826205491d2cSKalle Valo 	if (!ndev) {
826316e64676SRafał Miłecki 		bphy_err(drvr, "ndev is invalid\n");
826405491d2cSKalle Valo 		return NULL;
826505491d2cSKalle Valo 	}
826605491d2cSKalle Valo 
8267856d5a01SArend Van Spriel 	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
8268856d5a01SArend Van Spriel 	if (!cfg) {
826916e64676SRafał Miłecki 		bphy_err(drvr, "Could not allocate wiphy device\n");
8270856d5a01SArend Van Spriel 		return NULL;
827105491d2cSKalle Valo 	}
827205491d2cSKalle Valo 
827305491d2cSKalle Valo 	cfg->wiphy = wiphy;
827405491d2cSKalle Valo 	cfg->pub = drvr;
827505491d2cSKalle Valo 	init_vif_event(&cfg->vif_event);
827605491d2cSKalle Valo 	INIT_LIST_HEAD(&cfg->vif_list);
827705491d2cSKalle Valo 
827826072330SRafał Miłecki 	vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);
827905491d2cSKalle Valo 	if (IS_ERR(vif))
828005491d2cSKalle Valo 		goto wiphy_out;
828105491d2cSKalle Valo 
8282856d5a01SArend Van Spriel 	ifp = netdev_priv(ndev);
828305491d2cSKalle Valo 	vif->ifp = ifp;
828405491d2cSKalle Valo 	vif->wdev.netdev = ndev;
828505491d2cSKalle Valo 	ndev->ieee80211_ptr = &vif->wdev;
828605491d2cSKalle Valo 	SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
828705491d2cSKalle Valo 
828805491d2cSKalle Valo 	err = wl_init_priv(cfg);
828905491d2cSKalle Valo 	if (err) {
829016e64676SRafał Miłecki 		bphy_err(drvr, "Failed to init iwm_priv (%d)\n", err);
829105491d2cSKalle Valo 		brcmf_free_vif(vif);
829205491d2cSKalle Valo 		goto wiphy_out;
829305491d2cSKalle Valo 	}
829405491d2cSKalle Valo 	ifp->vif = vif;
829505491d2cSKalle Valo 
829605491d2cSKalle Valo 	/* determine d11 io type before wiphy setup */
829705491d2cSKalle Valo 	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
829805491d2cSKalle Valo 	if (err) {
829916e64676SRafał Miłecki 		bphy_err(drvr, "Failed to get D11 version (%d)\n", err);
830005491d2cSKalle Valo 		goto priv_out;
830105491d2cSKalle Valo 	}
830205491d2cSKalle Valo 	cfg->d11inf.io_type = (u8)io_type;
830305491d2cSKalle Valo 	brcmu_d11_attach(&cfg->d11inf);
830405491d2cSKalle Valo 
8305856d5a01SArend Van Spriel 	/* regulatory notifer below needs access to cfg so
8306856d5a01SArend Van Spriel 	 * assign it now.
8307856d5a01SArend Van Spriel 	 */
8308856d5a01SArend Van Spriel 	drvr->config = cfg;
8309856d5a01SArend Van Spriel 
831027a8aea1SWinnie Chang 	err = brcmf_setup_wiphy(wiphy, ifp);
831127a8aea1SWinnie Chang 	if (err < 0)
831227a8aea1SWinnie Chang 		goto priv_out;
831327a8aea1SWinnie Chang 
831405491d2cSKalle Valo 	brcmf_dbg(INFO, "Registering custom regulatory\n");
831505491d2cSKalle Valo 	wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
831605491d2cSKalle Valo 	wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
831705491d2cSKalle Valo 	wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
831805491d2cSKalle Valo 
831905491d2cSKalle Valo 	/* firmware defaults to 40MHz disabled in 2G band. We signal
832005491d2cSKalle Valo 	 * cfg80211 here that we do and have it decide we can enable
832105491d2cSKalle Valo 	 * it. But first check if device does support 2G operation.
832205491d2cSKalle Valo 	 */
832357fbcce3SJohannes Berg 	if (wiphy->bands[NL80211_BAND_2GHZ]) {
832457fbcce3SJohannes Berg 		cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
832505491d2cSKalle Valo 		*cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
832605491d2cSKalle Valo 	}
8327856d5a01SArend Van Spriel #ifdef CONFIG_PM
8328856d5a01SArend Van Spriel 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
8329856d5a01SArend Van Spriel 		ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
8330856d5a01SArend Van Spriel #endif
83316c04deaeSWright Feng 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS))
83326c04deaeSWright Feng 		ops->dump_survey = brcmf_cfg80211_dump_survey;
83336c04deaeSWright Feng 
833405491d2cSKalle Valo 	err = wiphy_register(wiphy);
833505491d2cSKalle Valo 	if (err < 0) {
833616e64676SRafał Miłecki 		bphy_err(drvr, "Could not register wiphy device (%d)\n", err);
833705491d2cSKalle Valo 		goto priv_out;
833805491d2cSKalle Valo 	}
833905491d2cSKalle Valo 
8340856d5a01SArend Van Spriel 	err = brcmf_setup_wiphybands(cfg);
8341ab99063fSRafał Miłecki 	if (err) {
834216e64676SRafał Miłecki 		bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err);
8343ab99063fSRafał Miłecki 		goto wiphy_unreg_out;
8344ab99063fSRafał Miłecki 	}
8345ab99063fSRafał Miłecki 
834605491d2cSKalle Valo 	/* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
834705491d2cSKalle Valo 	 * setup 40MHz in 2GHz band and enable OBSS scanning.
834805491d2cSKalle Valo 	 */
834905491d2cSKalle Valo 	if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
835005491d2cSKalle Valo 		err = brcmf_enable_bw40_2g(cfg);
835105491d2cSKalle Valo 		if (!err)
835205491d2cSKalle Valo 			err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
835305491d2cSKalle Valo 						      BRCMF_OBSS_COEX_AUTO);
835405491d2cSKalle Valo 		else
835505491d2cSKalle Valo 			*cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
835605491d2cSKalle Valo 	}
8357856d5a01SArend Van Spriel 
835805491d2cSKalle Valo 	err = brcmf_fweh_activate_events(ifp);
835905491d2cSKalle Valo 	if (err) {
836016e64676SRafał Miłecki 		bphy_err(drvr, "FWEH activation failed (%d)\n", err);
836105491d2cSKalle Valo 		goto wiphy_unreg_out;
836205491d2cSKalle Valo 	}
836305491d2cSKalle Valo 
836405491d2cSKalle Valo 	err = brcmf_p2p_attach(cfg, p2pdev_forced);
836505491d2cSKalle Valo 	if (err) {
836616e64676SRafał Miłecki 		bphy_err(drvr, "P2P initialisation failed (%d)\n", err);
836705491d2cSKalle Valo 		goto wiphy_unreg_out;
836805491d2cSKalle Valo 	}
836905491d2cSKalle Valo 	err = brcmf_btcoex_attach(cfg);
837005491d2cSKalle Valo 	if (err) {
837116e64676SRafał Miłecki 		bphy_err(drvr, "BT-coex initialisation failed (%d)\n", err);
837205491d2cSKalle Valo 		brcmf_p2p_detach(&cfg->p2p);
837305491d2cSKalle Valo 		goto wiphy_unreg_out;
837405491d2cSKalle Valo 	}
8375efc2c1faSArend Van Spriel 	err = brcmf_pno_attach(cfg);
8376efc2c1faSArend Van Spriel 	if (err) {
837716e64676SRafał Miłecki 		bphy_err(drvr, "PNO initialisation failed (%d)\n", err);
8378efc2c1faSArend Van Spriel 		brcmf_btcoex_detach(cfg);
8379efc2c1faSArend Van Spriel 		brcmf_p2p_detach(&cfg->p2p);
8380efc2c1faSArend Van Spriel 		goto wiphy_unreg_out;
8381efc2c1faSArend Van Spriel 	}
838205491d2cSKalle Valo 
8383a7b82d47SHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
838405491d2cSKalle Valo 		err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
838505491d2cSKalle Valo 		if (err) {
838605491d2cSKalle Valo 			brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
838705491d2cSKalle Valo 			wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
838805491d2cSKalle Valo 		} else {
838905491d2cSKalle Valo 			brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
839005491d2cSKalle Valo 					    brcmf_notify_tdls_peer_event);
839105491d2cSKalle Valo 		}
8392a7b82d47SHante Meuleman 	}
839305491d2cSKalle Valo 
839405491d2cSKalle Valo 	/* (re-) activate FWEH event handling */
839505491d2cSKalle Valo 	err = brcmf_fweh_activate_events(ifp);
839605491d2cSKalle Valo 	if (err) {
839716e64676SRafał Miłecki 		bphy_err(drvr, "FWEH activation failed (%d)\n", err);
8398cb853da3SArend Van Spriel 		goto detach;
839905491d2cSKalle Valo 	}
840005491d2cSKalle Valo 
840148ed16e8SHante Meuleman 	/* Fill in some of the advertised nl80211 supported features */
840248ed16e8SHante Meuleman 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
840348ed16e8SHante Meuleman 		wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
840448ed16e8SHante Meuleman #ifdef CONFIG_PM
84056ea09153SFranky Lin 		if (wiphy->wowlan &&
84066ea09153SFranky Lin 		    wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
840748ed16e8SHante Meuleman 			wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
840848ed16e8SHante Meuleman #endif
840948ed16e8SHante Meuleman 	}
841048ed16e8SHante Meuleman 
841105491d2cSKalle Valo 	return cfg;
841205491d2cSKalle Valo 
8413cb853da3SArend Van Spriel detach:
8414efc2c1faSArend Van Spriel 	brcmf_pno_detach(cfg);
8415cb853da3SArend Van Spriel 	brcmf_btcoex_detach(cfg);
8416cb853da3SArend Van Spriel 	brcmf_p2p_detach(&cfg->p2p);
841705491d2cSKalle Valo wiphy_unreg_out:
841805491d2cSKalle Valo 	wiphy_unregister(cfg->wiphy);
841905491d2cSKalle Valo priv_out:
842005491d2cSKalle Valo 	wl_deinit_priv(cfg);
842105491d2cSKalle Valo 	brcmf_free_vif(vif);
842205491d2cSKalle Valo 	ifp->vif = NULL;
842305491d2cSKalle Valo wiphy_out:
842405491d2cSKalle Valo 	brcmf_free_wiphy(wiphy);
8425856d5a01SArend Van Spriel 	kfree(cfg);
842605491d2cSKalle Valo 	return NULL;
842705491d2cSKalle Valo }
842805491d2cSKalle Valo 
brcmf_cfg80211_detach(struct brcmf_cfg80211_info * cfg)842905491d2cSKalle Valo void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
843005491d2cSKalle Valo {
843105491d2cSKalle Valo 	if (!cfg)
843205491d2cSKalle Valo 		return;
843305491d2cSKalle Valo 
8434efc2c1faSArend Van Spriel 	brcmf_pno_detach(cfg);
843505491d2cSKalle Valo 	brcmf_btcoex_detach(cfg);
843605491d2cSKalle Valo 	wiphy_unregister(cfg->wiphy);
843705491d2cSKalle Valo 	wl_deinit_priv(cfg);
843819079484SZheng Wang 	cancel_work_sync(&cfg->escan_timeout_work);
843905491d2cSKalle Valo 	brcmf_free_wiphy(cfg->wiphy);
8440856d5a01SArend Van Spriel 	kfree(cfg);
844105491d2cSKalle Valo }
8442