162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/vmalloc.h>
1262306a36Sopenharmony_ci#include <net/cfg80211.h>
1362306a36Sopenharmony_ci#include <net/netlink.h>
1462306a36Sopenharmony_ci#include <uapi/linux/if_arp.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <brcmu_utils.h>
1762306a36Sopenharmony_ci#include <defs.h>
1862306a36Sopenharmony_ci#include <brcmu_wifi.h>
1962306a36Sopenharmony_ci#include <brcm_hw_ids.h>
2062306a36Sopenharmony_ci#include "core.h"
2162306a36Sopenharmony_ci#include "debug.h"
2262306a36Sopenharmony_ci#include "tracepoint.h"
2362306a36Sopenharmony_ci#include "fwil_types.h"
2462306a36Sopenharmony_ci#include "p2p.h"
2562306a36Sopenharmony_ci#include "btcoex.h"
2662306a36Sopenharmony_ci#include "pno.h"
2762306a36Sopenharmony_ci#include "fwsignal.h"
2862306a36Sopenharmony_ci#include "cfg80211.h"
2962306a36Sopenharmony_ci#include "feature.h"
3062306a36Sopenharmony_ci#include "fwil.h"
3162306a36Sopenharmony_ci#include "proto.h"
3262306a36Sopenharmony_ci#include "vendor.h"
3362306a36Sopenharmony_ci#include "bus.h"
3462306a36Sopenharmony_ci#include "common.h"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define BRCMF_SCAN_IE_LEN_MAX		2048
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define WPA_OUI				"\x00\x50\xF2"	/* WPA OUI */
3962306a36Sopenharmony_ci#define WPA_OUI_TYPE			1
4062306a36Sopenharmony_ci#define RSN_OUI				"\x00\x0F\xAC"	/* RSN OUI */
4162306a36Sopenharmony_ci#define	WME_OUI_TYPE			2
4262306a36Sopenharmony_ci#define WPS_OUI_TYPE			4
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define VS_IE_FIXED_HDR_LEN		6
4562306a36Sopenharmony_ci#define WPA_IE_VERSION_LEN		2
4662306a36Sopenharmony_ci#define WPA_IE_MIN_OUI_LEN		4
4762306a36Sopenharmony_ci#define WPA_IE_SUITE_COUNT_LEN		2
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define WPA_CIPHER_NONE			0	/* None */
5062306a36Sopenharmony_ci#define WPA_CIPHER_WEP_40		1	/* WEP (40-bit) */
5162306a36Sopenharmony_ci#define WPA_CIPHER_TKIP			2	/* TKIP: default for WPA */
5262306a36Sopenharmony_ci#define WPA_CIPHER_AES_CCM		4	/* AES (CCM) */
5362306a36Sopenharmony_ci#define WPA_CIPHER_WEP_104		5	/* WEP (104-bit) */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define RSN_AKM_NONE			0	/* None (IBSS) */
5662306a36Sopenharmony_ci#define RSN_AKM_UNSPECIFIED		1	/* Over 802.1x */
5762306a36Sopenharmony_ci#define RSN_AKM_PSK			2	/* Pre-shared Key */
5862306a36Sopenharmony_ci#define RSN_AKM_SHA256_1X		5	/* SHA256, 802.1X */
5962306a36Sopenharmony_ci#define RSN_AKM_SHA256_PSK		6	/* SHA256, Pre-shared Key */
6062306a36Sopenharmony_ci#define RSN_AKM_SAE			8	/* SAE */
6162306a36Sopenharmony_ci#define RSN_CAP_LEN			2	/* Length of RSN capabilities */
6262306a36Sopenharmony_ci#define RSN_CAP_PTK_REPLAY_CNTR_MASK	(BIT(2) | BIT(3))
6362306a36Sopenharmony_ci#define RSN_CAP_MFPR_MASK		BIT(6)
6462306a36Sopenharmony_ci#define RSN_CAP_MFPC_MASK		BIT(7)
6562306a36Sopenharmony_ci#define RSN_PMKID_COUNT_LEN		2
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define VNDR_IE_CMD_LEN			4	/* length of the set command
6862306a36Sopenharmony_ci						 * string :"add", "del" (+ NUL)
6962306a36Sopenharmony_ci						 */
7062306a36Sopenharmony_ci#define VNDR_IE_COUNT_OFFSET		4
7162306a36Sopenharmony_ci#define VNDR_IE_PKTFLAG_OFFSET		8
7262306a36Sopenharmony_ci#define VNDR_IE_VSIE_OFFSET		12
7362306a36Sopenharmony_ci#define VNDR_IE_HDR_SIZE		12
7462306a36Sopenharmony_ci#define VNDR_IE_PARSE_LIMIT		5
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#define	DOT11_MGMT_HDR_LEN		24	/* d11 management header len */
7762306a36Sopenharmony_ci#define	DOT11_BCN_PRB_FIXED_LEN		12	/* beacon/probe fixed length */
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS	320
8062306a36Sopenharmony_ci#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS	400
8162306a36Sopenharmony_ci#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS	20
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci#define BRCMF_SCAN_CHANNEL_TIME		40
8462306a36Sopenharmony_ci#define BRCMF_SCAN_UNASSOC_TIME		40
8562306a36Sopenharmony_ci#define BRCMF_SCAN_PASSIVE_TIME		120
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci#define BRCMF_ND_INFO_TIMEOUT		msecs_to_jiffies(2000)
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci#define BRCMF_PS_MAX_TIMEOUT_MS		2000
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Dump obss definitions */
9262306a36Sopenharmony_ci#define ACS_MSRMNT_DELAY		80
9362306a36Sopenharmony_ci#define CHAN_NOISE_DUMMY		(-80)
9462306a36Sopenharmony_ci#define OBSS_TOKEN_IDX			15
9562306a36Sopenharmony_ci#define IBSS_TOKEN_IDX			15
9662306a36Sopenharmony_ci#define TX_TOKEN_IDX			14
9762306a36Sopenharmony_ci#define CTG_TOKEN_IDX			13
9862306a36Sopenharmony_ci#define PKT_TOKEN_IDX			15
9962306a36Sopenharmony_ci#define IDLE_TOKEN_IDX			12
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
10262306a36Sopenharmony_ci	(sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define BRCMF_MAX_CHANSPEC_LIST \
10562306a36Sopenharmony_ci	(BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistruct brcmf_dump_survey {
10862306a36Sopenharmony_ci	u32 obss;
10962306a36Sopenharmony_ci	u32 ibss;
11062306a36Sopenharmony_ci	u32 no_ctg;
11162306a36Sopenharmony_ci	u32 no_pckt;
11262306a36Sopenharmony_ci	u32 tx;
11362306a36Sopenharmony_ci	u32 idle;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistruct cca_stats_n_flags {
11762306a36Sopenharmony_ci	u32 msrmnt_time; /* Time for Measurement (msec) */
11862306a36Sopenharmony_ci	u32 msrmnt_done; /* flag set when measurement complete */
11962306a36Sopenharmony_ci	char buf[1];
12062306a36Sopenharmony_ci};
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistruct cca_msrmnt_query {
12362306a36Sopenharmony_ci	u32 msrmnt_query;
12462306a36Sopenharmony_ci	u32 time_req;
12562306a36Sopenharmony_ci};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic bool check_vif_up(struct brcmf_cfg80211_vif *vif)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
13062306a36Sopenharmony_ci		brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
13162306a36Sopenharmony_ci			  vif->sme_state);
13262306a36Sopenharmony_ci		return false;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	return true;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci#define RATE_TO_BASE100KBPS(rate)   (((rate) * 10) / 2)
13862306a36Sopenharmony_ci#define RATETAB_ENT(_rateid, _flags) \
13962306a36Sopenharmony_ci	{                                                               \
14062306a36Sopenharmony_ci		.bitrate        = RATE_TO_BASE100KBPS(_rateid),     \
14162306a36Sopenharmony_ci		.hw_value       = (_rateid),                            \
14262306a36Sopenharmony_ci		.flags          = (_flags),                             \
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic struct ieee80211_rate __wl_rates[] = {
14662306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_1M, 0),
14762306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
14862306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
14962306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
15062306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_6M, 0),
15162306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_9M, 0),
15262306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_12M, 0),
15362306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_18M, 0),
15462306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_24M, 0),
15562306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_36M, 0),
15662306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_48M, 0),
15762306a36Sopenharmony_ci	RATETAB_ENT(BRCM_RATE_54M, 0),
15862306a36Sopenharmony_ci};
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci#define wl_g_rates		(__wl_rates + 0)
16162306a36Sopenharmony_ci#define wl_g_rates_size		ARRAY_SIZE(__wl_rates)
16262306a36Sopenharmony_ci#define wl_a_rates		(__wl_rates + 4)
16362306a36Sopenharmony_ci#define wl_a_rates_size		(wl_g_rates_size - 4)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci#define CHAN2G(_channel, _freq) {				\
16662306a36Sopenharmony_ci	.band			= NL80211_BAND_2GHZ,		\
16762306a36Sopenharmony_ci	.center_freq		= (_freq),			\
16862306a36Sopenharmony_ci	.hw_value		= (_channel),			\
16962306a36Sopenharmony_ci	.max_antenna_gain	= 0,				\
17062306a36Sopenharmony_ci	.max_power		= 30,				\
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci#define CHAN5G(_channel) {					\
17462306a36Sopenharmony_ci	.band			= NL80211_BAND_5GHZ,		\
17562306a36Sopenharmony_ci	.center_freq		= 5000 + (5 * (_channel)),	\
17662306a36Sopenharmony_ci	.hw_value		= (_channel),			\
17762306a36Sopenharmony_ci	.max_antenna_gain	= 0,				\
17862306a36Sopenharmony_ci	.max_power		= 30,				\
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic struct ieee80211_channel __wl_2ghz_channels[] = {
18262306a36Sopenharmony_ci	CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
18362306a36Sopenharmony_ci	CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
18462306a36Sopenharmony_ci	CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
18562306a36Sopenharmony_ci	CHAN2G(13, 2472), CHAN2G(14, 2484)
18662306a36Sopenharmony_ci};
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic struct ieee80211_channel __wl_5ghz_channels[] = {
18962306a36Sopenharmony_ci	CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
19062306a36Sopenharmony_ci	CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
19162306a36Sopenharmony_ci	CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
19262306a36Sopenharmony_ci	CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
19362306a36Sopenharmony_ci	CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
19462306a36Sopenharmony_ci	CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
19562306a36Sopenharmony_ci};
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci/* Band templates duplicated per wiphy. The channel info
19862306a36Sopenharmony_ci * above is added to the band during setup.
19962306a36Sopenharmony_ci */
20062306a36Sopenharmony_cistatic const struct ieee80211_supported_band __wl_band_2ghz = {
20162306a36Sopenharmony_ci	.band = NL80211_BAND_2GHZ,
20262306a36Sopenharmony_ci	.bitrates = wl_g_rates,
20362306a36Sopenharmony_ci	.n_bitrates = wl_g_rates_size,
20462306a36Sopenharmony_ci};
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic const struct ieee80211_supported_band __wl_band_5ghz = {
20762306a36Sopenharmony_ci	.band = NL80211_BAND_5GHZ,
20862306a36Sopenharmony_ci	.bitrates = wl_a_rates,
20962306a36Sopenharmony_ci	.n_bitrates = wl_a_rates_size,
21062306a36Sopenharmony_ci};
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/* This is to override regulatory domains defined in cfg80211 module (reg.c)
21362306a36Sopenharmony_ci * By default world regulatory domain defined in reg.c puts the flags
21462306a36Sopenharmony_ci * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
21562306a36Sopenharmony_ci * With respect to these flags, wpa_supplicant doesn't * start p2p
21662306a36Sopenharmony_ci * operations on 5GHz channels. All the changes in world regulatory
21762306a36Sopenharmony_ci * domain are to be done here.
21862306a36Sopenharmony_ci */
21962306a36Sopenharmony_cistatic const struct ieee80211_regdomain brcmf_regdom = {
22062306a36Sopenharmony_ci	.n_reg_rules = 4,
22162306a36Sopenharmony_ci	.alpha2 =  "99",
22262306a36Sopenharmony_ci	.reg_rules = {
22362306a36Sopenharmony_ci		/* IEEE 802.11b/g, channels 1..11 */
22462306a36Sopenharmony_ci		REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
22562306a36Sopenharmony_ci		/* If any */
22662306a36Sopenharmony_ci		/* IEEE 802.11 channel 14 - Only JP enables
22762306a36Sopenharmony_ci		 * this and for 802.11b only
22862306a36Sopenharmony_ci		 */
22962306a36Sopenharmony_ci		REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
23062306a36Sopenharmony_ci		/* IEEE 802.11a, channel 36..64 */
23162306a36Sopenharmony_ci		REG_RULE(5150-10, 5350+10, 160, 6, 20, 0),
23262306a36Sopenharmony_ci		/* IEEE 802.11a, channel 100..165 */
23362306a36Sopenharmony_ci		REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), }
23462306a36Sopenharmony_ci};
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
23762306a36Sopenharmony_ci * are supported. A pointer to this array and the number of entries is passed
23862306a36Sopenharmony_ci * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
23962306a36Sopenharmony_ci * So the cipher suite AES_CMAC has to be the last one in the array, and when
24062306a36Sopenharmony_ci * device does not support MFP then the number of suites will be decreased by 1
24162306a36Sopenharmony_ci */
24262306a36Sopenharmony_cistatic const u32 brcmf_cipher_suites[] = {
24362306a36Sopenharmony_ci	WLAN_CIPHER_SUITE_WEP40,
24462306a36Sopenharmony_ci	WLAN_CIPHER_SUITE_WEP104,
24562306a36Sopenharmony_ci	WLAN_CIPHER_SUITE_TKIP,
24662306a36Sopenharmony_ci	WLAN_CIPHER_SUITE_CCMP,
24762306a36Sopenharmony_ci	/* Keep as last entry: */
24862306a36Sopenharmony_ci	WLAN_CIPHER_SUITE_AES_CMAC
24962306a36Sopenharmony_ci};
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci/* Vendor specific ie. id = 221, oui and type defines exact ie */
25262306a36Sopenharmony_cistruct brcmf_vs_tlv {
25362306a36Sopenharmony_ci	u8 id;
25462306a36Sopenharmony_ci	u8 len;
25562306a36Sopenharmony_ci	u8 oui[3];
25662306a36Sopenharmony_ci	u8 oui_type;
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistruct parsed_vndr_ie_info {
26062306a36Sopenharmony_ci	u8 *ie_ptr;
26162306a36Sopenharmony_ci	u32 ie_len;	/* total length including id & length field */
26262306a36Sopenharmony_ci	struct brcmf_vs_tlv vndrie;
26362306a36Sopenharmony_ci};
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistruct parsed_vndr_ies {
26662306a36Sopenharmony_ci	u32 count;
26762306a36Sopenharmony_ci	struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
26862306a36Sopenharmony_ci};
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_1		1
27162306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_2		2
27262306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_3		3
27362306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_MAX		WL_INTERFACE_CREATE_VER_3
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci#define WL_INTERFACE_MAC_DONT_USE	0x0
27662306a36Sopenharmony_ci#define WL_INTERFACE_MAC_USE		0x2
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_STA		0x0
27962306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_AP		0x1
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistruct wl_interface_create_v1 {
28262306a36Sopenharmony_ci	u16	ver;			/* structure version */
28362306a36Sopenharmony_ci	u32	flags;			/* flags for operation */
28462306a36Sopenharmony_ci	u8	mac_addr[ETH_ALEN];	/* MAC address */
28562306a36Sopenharmony_ci	u32	wlc_index;		/* optional for wlc index */
28662306a36Sopenharmony_ci};
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistruct wl_interface_create_v2 {
28962306a36Sopenharmony_ci	u16	ver;			/* structure version */
29062306a36Sopenharmony_ci	u8	pad1[2];
29162306a36Sopenharmony_ci	u32	flags;			/* flags for operation */
29262306a36Sopenharmony_ci	u8	mac_addr[ETH_ALEN];	/* MAC address */
29362306a36Sopenharmony_ci	u8	iftype;			/* type of interface created */
29462306a36Sopenharmony_ci	u8	pad2;
29562306a36Sopenharmony_ci	u32	wlc_index;		/* optional for wlc index */
29662306a36Sopenharmony_ci};
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistruct wl_interface_create_v3 {
29962306a36Sopenharmony_ci	u16 ver;			/* structure version */
30062306a36Sopenharmony_ci	u16 len;			/* length of structure + data */
30162306a36Sopenharmony_ci	u16 fixed_len;			/* length of structure */
30262306a36Sopenharmony_ci	u8 iftype;			/* type of interface created */
30362306a36Sopenharmony_ci	u8 wlc_index;			/* optional for wlc index */
30462306a36Sopenharmony_ci	u32 flags;			/* flags for operation */
30562306a36Sopenharmony_ci	u8 mac_addr[ETH_ALEN];		/* MAC address */
30662306a36Sopenharmony_ci	u8 bssid[ETH_ALEN];		/* optional for BSSID */
30762306a36Sopenharmony_ci	u8 if_index;			/* interface index request */
30862306a36Sopenharmony_ci	u8 pad[3];
30962306a36Sopenharmony_ci	u8 data[];			/* Optional for specific data */
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_cistatic u8 nl80211_band_to_fwil(enum nl80211_band band)
31362306a36Sopenharmony_ci{
31462306a36Sopenharmony_ci	switch (band) {
31562306a36Sopenharmony_ci	case NL80211_BAND_2GHZ:
31662306a36Sopenharmony_ci		return WLC_BAND_2G;
31762306a36Sopenharmony_ci	case NL80211_BAND_5GHZ:
31862306a36Sopenharmony_ci		return WLC_BAND_5G;
31962306a36Sopenharmony_ci	default:
32062306a36Sopenharmony_ci		WARN_ON(1);
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci	return 0;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
32762306a36Sopenharmony_ci			       struct cfg80211_chan_def *ch)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct brcmu_chan ch_inf;
33062306a36Sopenharmony_ci	s32 primary_offset;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
33362306a36Sopenharmony_ci		  ch->chan->center_freq, ch->center_freq1, ch->width);
33462306a36Sopenharmony_ci	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
33562306a36Sopenharmony_ci	primary_offset = ch->chan->center_freq - ch->center_freq1;
33662306a36Sopenharmony_ci	switch (ch->width) {
33762306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20:
33862306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_20_NOHT:
33962306a36Sopenharmony_ci		ch_inf.bw = BRCMU_CHAN_BW_20;
34062306a36Sopenharmony_ci		WARN_ON(primary_offset != 0);
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_40:
34362306a36Sopenharmony_ci		ch_inf.bw = BRCMU_CHAN_BW_40;
34462306a36Sopenharmony_ci		if (primary_offset > 0)
34562306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_U;
34662306a36Sopenharmony_ci		else
34762306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_L;
34862306a36Sopenharmony_ci		break;
34962306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80:
35062306a36Sopenharmony_ci		ch_inf.bw = BRCMU_CHAN_BW_80;
35162306a36Sopenharmony_ci		if (primary_offset == -30)
35262306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_LL;
35362306a36Sopenharmony_ci		else if (primary_offset == -10)
35462306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_LU;
35562306a36Sopenharmony_ci		else if (primary_offset == 10)
35662306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_UL;
35762306a36Sopenharmony_ci		else
35862306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_UU;
35962306a36Sopenharmony_ci		break;
36062306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_160:
36162306a36Sopenharmony_ci		ch_inf.bw = BRCMU_CHAN_BW_160;
36262306a36Sopenharmony_ci		if (primary_offset == -70)
36362306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_LLL;
36462306a36Sopenharmony_ci		else if (primary_offset == -50)
36562306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_LLU;
36662306a36Sopenharmony_ci		else if (primary_offset == -30)
36762306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_LUL;
36862306a36Sopenharmony_ci		else if (primary_offset == -10)
36962306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_LUU;
37062306a36Sopenharmony_ci		else if (primary_offset == 10)
37162306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_ULL;
37262306a36Sopenharmony_ci		else if (primary_offset == 30)
37362306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_ULU;
37462306a36Sopenharmony_ci		else if (primary_offset == 50)
37562306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_UUL;
37662306a36Sopenharmony_ci		else
37762306a36Sopenharmony_ci			ch_inf.sb = BRCMU_CHAN_SB_UUU;
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_80P80:
38062306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_5:
38162306a36Sopenharmony_ci	case NL80211_CHAN_WIDTH_10:
38262306a36Sopenharmony_ci	default:
38362306a36Sopenharmony_ci		WARN_ON_ONCE(1);
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci	switch (ch->chan->band) {
38662306a36Sopenharmony_ci	case NL80211_BAND_2GHZ:
38762306a36Sopenharmony_ci		ch_inf.band = BRCMU_CHAN_BAND_2G;
38862306a36Sopenharmony_ci		break;
38962306a36Sopenharmony_ci	case NL80211_BAND_5GHZ:
39062306a36Sopenharmony_ci		ch_inf.band = BRCMU_CHAN_BAND_5G;
39162306a36Sopenharmony_ci		break;
39262306a36Sopenharmony_ci	case NL80211_BAND_60GHZ:
39362306a36Sopenharmony_ci	default:
39462306a36Sopenharmony_ci		WARN_ON_ONCE(1);
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci	d11inf->encchspec(&ch_inf);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "chanspec: 0x%x\n", ch_inf.chspec);
39962306a36Sopenharmony_ci	return ch_inf.chspec;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ciu16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
40362306a36Sopenharmony_ci			struct ieee80211_channel *ch)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct brcmu_chan ch_inf;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
40862306a36Sopenharmony_ci	ch_inf.bw = BRCMU_CHAN_BW_20;
40962306a36Sopenharmony_ci	d11inf->encchspec(&ch_inf);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	return ch_inf.chspec;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci/* Traverse a string of 1-byte tag/1-byte length/variable-length value
41562306a36Sopenharmony_ci * triples, returning a pointer to the substring whose first element
41662306a36Sopenharmony_ci * matches tag
41762306a36Sopenharmony_ci */
41862306a36Sopenharmony_cistatic const struct brcmf_tlv *
41962306a36Sopenharmony_cibrcmf_parse_tlvs(const void *buf, int buflen, uint key)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	const struct brcmf_tlv *elt = buf;
42262306a36Sopenharmony_ci	int totlen = buflen;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	/* find tagged parameter */
42562306a36Sopenharmony_ci	while (totlen >= TLV_HDR_LEN) {
42662306a36Sopenharmony_ci		int len = elt->len;
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		/* validate remaining totlen */
42962306a36Sopenharmony_ci		if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
43062306a36Sopenharmony_ci			return elt;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
43362306a36Sopenharmony_ci		totlen -= (len + TLV_HDR_LEN);
43462306a36Sopenharmony_ci	}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return NULL;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/* Is any of the tlvs the expected entry? If
44062306a36Sopenharmony_ci * not update the tlvs buffer pointer/length.
44162306a36Sopenharmony_ci */
44262306a36Sopenharmony_cistatic bool
44362306a36Sopenharmony_cibrcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
44462306a36Sopenharmony_ci		 const u8 *oui, u32 oui_len, u8 type)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	/* If the contents match the OUI and the type */
44762306a36Sopenharmony_ci	if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
44862306a36Sopenharmony_ci	    !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
44962306a36Sopenharmony_ci	    type == ie[TLV_BODY_OFF + oui_len]) {
45062306a36Sopenharmony_ci		return true;
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (tlvs == NULL)
45462306a36Sopenharmony_ci		return false;
45562306a36Sopenharmony_ci	/* point to the next ie */
45662306a36Sopenharmony_ci	ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
45762306a36Sopenharmony_ci	/* calculate the length of the rest of the buffer */
45862306a36Sopenharmony_ci	*tlvs_len -= (int)(ie - *tlvs);
45962306a36Sopenharmony_ci	/* update the pointer to the start of the buffer */
46062306a36Sopenharmony_ci	*tlvs = ie;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	return false;
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_cistatic struct brcmf_vs_tlv *
46662306a36Sopenharmony_cibrcmf_find_wpaie(const u8 *parse, u32 len)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	const struct brcmf_tlv *ie;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
47162306a36Sopenharmony_ci		if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
47262306a36Sopenharmony_ci				     WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
47362306a36Sopenharmony_ci			return (struct brcmf_vs_tlv *)ie;
47462306a36Sopenharmony_ci	}
47562306a36Sopenharmony_ci	return NULL;
47662306a36Sopenharmony_ci}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic struct brcmf_vs_tlv *
47962306a36Sopenharmony_cibrcmf_find_wpsie(const u8 *parse, u32 len)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	const struct brcmf_tlv *ie;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
48462306a36Sopenharmony_ci		if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
48562306a36Sopenharmony_ci				     WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
48662306a36Sopenharmony_ci			return (struct brcmf_vs_tlv *)ie;
48762306a36Sopenharmony_ci	}
48862306a36Sopenharmony_ci	return NULL;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_cistatic int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
49262306a36Sopenharmony_ci				     struct brcmf_cfg80211_vif *vif,
49362306a36Sopenharmony_ci				     enum nl80211_iftype new_type)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *pos;
49662306a36Sopenharmony_ci	bool check_combos = false;
49762306a36Sopenharmony_ci	int ret = 0;
49862306a36Sopenharmony_ci	struct iface_combination_params params = {
49962306a36Sopenharmony_ci		.num_different_channels = 1,
50062306a36Sopenharmony_ci	};
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	list_for_each_entry(pos, &cfg->vif_list, list)
50362306a36Sopenharmony_ci		if (pos == vif) {
50462306a36Sopenharmony_ci			params.iftype_num[new_type]++;
50562306a36Sopenharmony_ci		} else {
50662306a36Sopenharmony_ci			/* concurrent interfaces so need check combinations */
50762306a36Sopenharmony_ci			check_combos = true;
50862306a36Sopenharmony_ci			params.iftype_num[pos->wdev.iftype]++;
50962306a36Sopenharmony_ci		}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	if (check_combos)
51262306a36Sopenharmony_ci		ret = cfg80211_check_combinations(cfg->wiphy, &params);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	return ret;
51562306a36Sopenharmony_ci}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_cistatic int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
51862306a36Sopenharmony_ci				  enum nl80211_iftype new_type)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *pos;
52162306a36Sopenharmony_ci	struct iface_combination_params params = {
52262306a36Sopenharmony_ci		.num_different_channels = 1,
52362306a36Sopenharmony_ci	};
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	list_for_each_entry(pos, &cfg->vif_list, list)
52662306a36Sopenharmony_ci		params.iftype_num[pos->wdev.iftype]++;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	params.iftype_num[new_type]++;
52962306a36Sopenharmony_ci	return cfg80211_check_combinations(cfg->wiphy, &params);
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic void convert_key_from_CPU(struct brcmf_wsec_key *key,
53362306a36Sopenharmony_ci				 struct brcmf_wsec_key_le *key_le)
53462306a36Sopenharmony_ci{
53562306a36Sopenharmony_ci	key_le->index = cpu_to_le32(key->index);
53662306a36Sopenharmony_ci	key_le->len = cpu_to_le32(key->len);
53762306a36Sopenharmony_ci	key_le->algo = cpu_to_le32(key->algo);
53862306a36Sopenharmony_ci	key_le->flags = cpu_to_le32(key->flags);
53962306a36Sopenharmony_ci	key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
54062306a36Sopenharmony_ci	key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
54162306a36Sopenharmony_ci	key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
54262306a36Sopenharmony_ci	memcpy(key_le->data, key->data, sizeof(key->data));
54362306a36Sopenharmony_ci	memcpy(key_le->ea, key->ea, sizeof(key->ea));
54462306a36Sopenharmony_ci}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_cistatic int
54762306a36Sopenharmony_cisend_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
55062306a36Sopenharmony_ci	int err;
55162306a36Sopenharmony_ci	struct brcmf_wsec_key_le key_le;
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	convert_key_from_CPU(key, &key_le);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	brcmf_netdev_wait_pend8021x(ifp);
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
55862306a36Sopenharmony_ci					sizeof(key_le));
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	if (err)
56162306a36Sopenharmony_ci		bphy_err(drvr, "wsec_key error (%d)\n", err);
56262306a36Sopenharmony_ci	return err;
56362306a36Sopenharmony_ci}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_cistatic void
56662306a36Sopenharmony_cibrcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
56962306a36Sopenharmony_ci	struct brcmf_if *ifp;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
57262306a36Sopenharmony_ci	ifp = vif->ifp;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
57562306a36Sopenharmony_ci	    (wdev->iftype == NL80211_IFTYPE_AP) ||
57662306a36Sopenharmony_ci	    (wdev->iftype == NL80211_IFTYPE_P2P_GO))
57762306a36Sopenharmony_ci		brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
57862306a36Sopenharmony_ci						ADDR_DIRECT);
57962306a36Sopenharmony_ci	else
58062306a36Sopenharmony_ci		brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
58162306a36Sopenharmony_ci						ADDR_INDIRECT);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	int bsscfgidx;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {
58962306a36Sopenharmony_ci		/* bsscfgidx 1 is reserved for legacy P2P */
59062306a36Sopenharmony_ci		if (bsscfgidx == 1)
59162306a36Sopenharmony_ci			continue;
59262306a36Sopenharmony_ci		if (!drvr->iflist[bsscfgidx])
59362306a36Sopenharmony_ci			return bsscfgidx;
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	return -ENOMEM;
59762306a36Sopenharmony_ci}
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	u8 mac_idx = ifp->drvr->sta_mac_idx;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* set difference MAC address with locally administered bit */
60462306a36Sopenharmony_ci	memcpy(mac_addr, ifp->mac_addr, ETH_ALEN);
60562306a36Sopenharmony_ci	mac_addr[0] |= 0x02;
60662306a36Sopenharmony_ci	mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0;
60762306a36Sopenharmony_ci	mac_idx++;
60862306a36Sopenharmony_ci	mac_idx = mac_idx % 2;
60962306a36Sopenharmony_ci	ifp->drvr->sta_mac_idx = mac_idx;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct wl_interface_create_v1 iface_v1;
61562306a36Sopenharmony_ci	struct wl_interface_create_v2 iface_v2;
61662306a36Sopenharmony_ci	struct wl_interface_create_v3 iface_v3;
61762306a36Sopenharmony_ci	u32 iface_create_ver;
61862306a36Sopenharmony_ci	int err;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* interface_create version 1 */
62162306a36Sopenharmony_ci	memset(&iface_v1, 0, sizeof(iface_v1));
62262306a36Sopenharmony_ci	iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
62362306a36Sopenharmony_ci	iface_v1.flags = WL_INTERFACE_CREATE_STA |
62462306a36Sopenharmony_ci			 WL_INTERFACE_MAC_USE;
62562306a36Sopenharmony_ci	if (!is_zero_ether_addr(macaddr))
62662306a36Sopenharmony_ci		memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN);
62762306a36Sopenharmony_ci	else
62862306a36Sopenharmony_ci		brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
63162306a36Sopenharmony_ci				       &iface_v1,
63262306a36Sopenharmony_ci				       sizeof(iface_v1));
63362306a36Sopenharmony_ci	if (err) {
63462306a36Sopenharmony_ci		brcmf_info("failed to create interface(v1), err=%d\n",
63562306a36Sopenharmony_ci			   err);
63662306a36Sopenharmony_ci	} else {
63762306a36Sopenharmony_ci		brcmf_dbg(INFO, "interface created(v1)\n");
63862306a36Sopenharmony_ci		return 0;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	/* interface_create version 2 */
64262306a36Sopenharmony_ci	memset(&iface_v2, 0, sizeof(iface_v2));
64362306a36Sopenharmony_ci	iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
64462306a36Sopenharmony_ci	iface_v2.flags = WL_INTERFACE_MAC_USE;
64562306a36Sopenharmony_ci	iface_v2.iftype = WL_INTERFACE_CREATE_STA;
64662306a36Sopenharmony_ci	if (!is_zero_ether_addr(macaddr))
64762306a36Sopenharmony_ci		memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN);
64862306a36Sopenharmony_ci	else
64962306a36Sopenharmony_ci		brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
65262306a36Sopenharmony_ci				       &iface_v2,
65362306a36Sopenharmony_ci				       sizeof(iface_v2));
65462306a36Sopenharmony_ci	if (err) {
65562306a36Sopenharmony_ci		brcmf_info("failed to create interface(v2), err=%d\n",
65662306a36Sopenharmony_ci			   err);
65762306a36Sopenharmony_ci	} else {
65862306a36Sopenharmony_ci		brcmf_dbg(INFO, "interface created(v2)\n");
65962306a36Sopenharmony_ci		return 0;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/* interface_create version 3+ */
66362306a36Sopenharmony_ci	/* get supported version from firmware side */
66462306a36Sopenharmony_ci	iface_create_ver = 0;
66562306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_get(ifp, "interface_create",
66662306a36Sopenharmony_ci				       &iface_create_ver);
66762306a36Sopenharmony_ci	if (err) {
66862306a36Sopenharmony_ci		brcmf_err("fail to get supported version, err=%d\n", err);
66962306a36Sopenharmony_ci		return -EOPNOTSUPP;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	switch (iface_create_ver) {
67362306a36Sopenharmony_ci	case WL_INTERFACE_CREATE_VER_3:
67462306a36Sopenharmony_ci		memset(&iface_v3, 0, sizeof(iface_v3));
67562306a36Sopenharmony_ci		iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
67662306a36Sopenharmony_ci		iface_v3.flags = WL_INTERFACE_MAC_USE;
67762306a36Sopenharmony_ci		iface_v3.iftype = WL_INTERFACE_CREATE_STA;
67862306a36Sopenharmony_ci		if (!is_zero_ether_addr(macaddr))
67962306a36Sopenharmony_ci			memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN);
68062306a36Sopenharmony_ci		else
68162306a36Sopenharmony_ci			brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_get(ifp, "interface_create",
68462306a36Sopenharmony_ci					       &iface_v3,
68562306a36Sopenharmony_ci					       sizeof(iface_v3));
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci		if (!err)
68862306a36Sopenharmony_ci			brcmf_dbg(INFO, "interface created(v3)\n");
68962306a36Sopenharmony_ci		break;
69062306a36Sopenharmony_ci	default:
69162306a36Sopenharmony_ci		brcmf_err("not support interface create(v%d)\n",
69262306a36Sopenharmony_ci			  iface_create_ver);
69362306a36Sopenharmony_ci		err = -EOPNOTSUPP;
69462306a36Sopenharmony_ci		break;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (err) {
69862306a36Sopenharmony_ci		brcmf_info("station interface creation failed (%d)\n",
69962306a36Sopenharmony_ci			   err);
70062306a36Sopenharmony_ci		return -EIO;
70162306a36Sopenharmony_ci	}
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	return 0;
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	struct wl_interface_create_v1 iface_v1;
70962306a36Sopenharmony_ci	struct wl_interface_create_v2 iface_v2;
71062306a36Sopenharmony_ci	struct wl_interface_create_v3 iface_v3;
71162306a36Sopenharmony_ci	u32 iface_create_ver;
71262306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
71362306a36Sopenharmony_ci	struct brcmf_mbss_ssid_le mbss_ssid_le;
71462306a36Sopenharmony_ci	int bsscfgidx;
71562306a36Sopenharmony_ci	int err;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* interface_create version 1 */
71862306a36Sopenharmony_ci	memset(&iface_v1, 0, sizeof(iface_v1));
71962306a36Sopenharmony_ci	iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
72062306a36Sopenharmony_ci	iface_v1.flags = WL_INTERFACE_CREATE_AP |
72162306a36Sopenharmony_ci			 WL_INTERFACE_MAC_USE;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci	brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
72662306a36Sopenharmony_ci				       &iface_v1,
72762306a36Sopenharmony_ci				       sizeof(iface_v1));
72862306a36Sopenharmony_ci	if (err) {
72962306a36Sopenharmony_ci		brcmf_info("failed to create interface(v1), err=%d\n",
73062306a36Sopenharmony_ci			   err);
73162306a36Sopenharmony_ci	} else {
73262306a36Sopenharmony_ci		brcmf_dbg(INFO, "interface created(v1)\n");
73362306a36Sopenharmony_ci		return 0;
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	/* interface_create version 2 */
73762306a36Sopenharmony_ci	memset(&iface_v2, 0, sizeof(iface_v2));
73862306a36Sopenharmony_ci	iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
73962306a36Sopenharmony_ci	iface_v2.flags = WL_INTERFACE_MAC_USE;
74062306a36Sopenharmony_ci	iface_v2.iftype = WL_INTERFACE_CREATE_AP;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
74562306a36Sopenharmony_ci				       &iface_v2,
74662306a36Sopenharmony_ci				       sizeof(iface_v2));
74762306a36Sopenharmony_ci	if (err) {
74862306a36Sopenharmony_ci		brcmf_info("failed to create interface(v2), err=%d\n",
74962306a36Sopenharmony_ci			   err);
75062306a36Sopenharmony_ci	} else {
75162306a36Sopenharmony_ci		brcmf_dbg(INFO, "interface created(v2)\n");
75262306a36Sopenharmony_ci		return 0;
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* interface_create version 3+ */
75662306a36Sopenharmony_ci	/* get supported version from firmware side */
75762306a36Sopenharmony_ci	iface_create_ver = 0;
75862306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_get(ifp, "interface_create",
75962306a36Sopenharmony_ci				       &iface_create_ver);
76062306a36Sopenharmony_ci	if (err) {
76162306a36Sopenharmony_ci		brcmf_err("fail to get supported version, err=%d\n", err);
76262306a36Sopenharmony_ci		return -EOPNOTSUPP;
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	switch (iface_create_ver) {
76662306a36Sopenharmony_ci	case WL_INTERFACE_CREATE_VER_3:
76762306a36Sopenharmony_ci		memset(&iface_v3, 0, sizeof(iface_v3));
76862306a36Sopenharmony_ci		iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
76962306a36Sopenharmony_ci		iface_v3.flags = WL_INTERFACE_MAC_USE;
77062306a36Sopenharmony_ci		iface_v3.iftype = WL_INTERFACE_CREATE_AP;
77162306a36Sopenharmony_ci		brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_get(ifp, "interface_create",
77462306a36Sopenharmony_ci					       &iface_v3,
77562306a36Sopenharmony_ci					       sizeof(iface_v3));
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		if (!err)
77862306a36Sopenharmony_ci			brcmf_dbg(INFO, "interface created(v3)\n");
77962306a36Sopenharmony_ci		break;
78062306a36Sopenharmony_ci	default:
78162306a36Sopenharmony_ci		brcmf_err("not support interface create(v%d)\n",
78262306a36Sopenharmony_ci			  iface_create_ver);
78362306a36Sopenharmony_ci		err = -EOPNOTSUPP;
78462306a36Sopenharmony_ci		break;
78562306a36Sopenharmony_ci	}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (err) {
78862306a36Sopenharmony_ci		brcmf_info("Does not support interface_create (%d)\n",
78962306a36Sopenharmony_ci			   err);
79062306a36Sopenharmony_ci		memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
79162306a36Sopenharmony_ci		bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);
79262306a36Sopenharmony_ci		if (bsscfgidx < 0)
79362306a36Sopenharmony_ci			return bsscfgidx;
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci		mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
79662306a36Sopenharmony_ci		mbss_ssid_le.SSID_len = cpu_to_le32(5);
79762306a36Sopenharmony_ci		sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
80062306a36Sopenharmony_ci						sizeof(mbss_ssid_le));
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci		if (err < 0)
80362306a36Sopenharmony_ci			bphy_err(drvr, "setting ssid failed %d\n", err);
80462306a36Sopenharmony_ci	}
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	return err;
80762306a36Sopenharmony_ci}
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci/**
81062306a36Sopenharmony_ci * brcmf_apsta_add_vif() - create a new AP or STA virtual interface
81162306a36Sopenharmony_ci *
81262306a36Sopenharmony_ci * @wiphy: wiphy device of new interface.
81362306a36Sopenharmony_ci * @name: name of the new interface.
81462306a36Sopenharmony_ci * @params: contains mac address for AP or STA device.
81562306a36Sopenharmony_ci * @type: interface type.
81662306a36Sopenharmony_ci */
81762306a36Sopenharmony_cistatic
81862306a36Sopenharmony_cistruct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name,
81962306a36Sopenharmony_ci					 struct vif_params *params,
82062306a36Sopenharmony_ci					 enum nl80211_iftype type)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
82362306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
82462306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
82562306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
82662306a36Sopenharmony_ci	int err;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP)
82962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	if (brcmf_cfg80211_vif_event_armed(cfg))
83262306a36Sopenharmony_ci		return ERR_PTR(-EBUSY);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	vif = brcmf_alloc_vif(cfg, type);
83762306a36Sopenharmony_ci	if (IS_ERR(vif))
83862306a36Sopenharmony_ci		return (struct wireless_dev *)vif;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	brcmf_cfg80211_arm_vif_event(cfg, vif);
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	if (type == NL80211_IFTYPE_STATION)
84362306a36Sopenharmony_ci		err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr);
84462306a36Sopenharmony_ci	else
84562306a36Sopenharmony_ci		err = brcmf_cfg80211_request_ap_if(ifp);
84662306a36Sopenharmony_ci	if (err) {
84762306a36Sopenharmony_ci		brcmf_cfg80211_arm_vif_event(cfg, NULL);
84862306a36Sopenharmony_ci		goto fail;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	/* wait for firmware event */
85262306a36Sopenharmony_ci	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
85362306a36Sopenharmony_ci					    BRCMF_VIF_EVENT_TIMEOUT);
85462306a36Sopenharmony_ci	brcmf_cfg80211_arm_vif_event(cfg, NULL);
85562306a36Sopenharmony_ci	if (!err) {
85662306a36Sopenharmony_ci		bphy_err(drvr, "timeout occurred\n");
85762306a36Sopenharmony_ci		err = -EIO;
85862306a36Sopenharmony_ci		goto fail;
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	/* interface created in firmware */
86262306a36Sopenharmony_ci	ifp = vif->ifp;
86362306a36Sopenharmony_ci	if (!ifp) {
86462306a36Sopenharmony_ci		bphy_err(drvr, "no if pointer provided\n");
86562306a36Sopenharmony_ci		err = -ENOENT;
86662306a36Sopenharmony_ci		goto fail;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
87062306a36Sopenharmony_ci	err = brcmf_net_attach(ifp, true);
87162306a36Sopenharmony_ci	if (err) {
87262306a36Sopenharmony_ci		bphy_err(drvr, "Registering netdevice failed\n");
87362306a36Sopenharmony_ci		free_netdev(ifp->ndev);
87462306a36Sopenharmony_ci		goto fail;
87562306a36Sopenharmony_ci	}
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	return &ifp->vif->wdev;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_cifail:
88062306a36Sopenharmony_ci	brcmf_free_vif(vif);
88162306a36Sopenharmony_ci	return ERR_PTR(err);
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_cistatic bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
88562306a36Sopenharmony_ci{
88662306a36Sopenharmony_ci	enum nl80211_iftype iftype;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	iftype = vif->wdev.iftype;
88962306a36Sopenharmony_ci	return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
89062306a36Sopenharmony_ci}
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_cistatic bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
89362306a36Sopenharmony_ci{
89462306a36Sopenharmony_ci	return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci/**
89862306a36Sopenharmony_ci * brcmf_mon_add_vif() - create monitor mode virtual interface
89962306a36Sopenharmony_ci *
90062306a36Sopenharmony_ci * @wiphy: wiphy device of new interface.
90162306a36Sopenharmony_ci * @name: name of the new interface.
90262306a36Sopenharmony_ci */
90362306a36Sopenharmony_cistatic struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy,
90462306a36Sopenharmony_ci					      const char *name)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
90762306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
90862306a36Sopenharmony_ci	struct net_device *ndev;
90962306a36Sopenharmony_ci	struct brcmf_if *ifp;
91062306a36Sopenharmony_ci	int err;
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	if (cfg->pub->mon_if) {
91362306a36Sopenharmony_ci		err = -EEXIST;
91462306a36Sopenharmony_ci		goto err_out;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_MONITOR);
91862306a36Sopenharmony_ci	if (IS_ERR(vif)) {
91962306a36Sopenharmony_ci		err = PTR_ERR(vif);
92062306a36Sopenharmony_ci		goto err_out;
92162306a36Sopenharmony_ci	}
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup);
92462306a36Sopenharmony_ci	if (!ndev) {
92562306a36Sopenharmony_ci		err = -ENOMEM;
92662306a36Sopenharmony_ci		goto err_free_vif;
92762306a36Sopenharmony_ci	}
92862306a36Sopenharmony_ci	ndev->type = ARPHRD_IEEE80211_RADIOTAP;
92962306a36Sopenharmony_ci	ndev->ieee80211_ptr = &vif->wdev;
93062306a36Sopenharmony_ci	ndev->needs_free_netdev = true;
93162306a36Sopenharmony_ci	ndev->priv_destructor = brcmf_cfg80211_free_netdev;
93262306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	ifp = netdev_priv(ndev);
93562306a36Sopenharmony_ci	ifp->vif = vif;
93662306a36Sopenharmony_ci	ifp->ndev = ndev;
93762306a36Sopenharmony_ci	ifp->drvr = cfg->pub;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	vif->ifp = ifp;
94062306a36Sopenharmony_ci	vif->wdev.netdev = ndev;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	err = brcmf_net_mon_attach(ifp);
94362306a36Sopenharmony_ci	if (err) {
94462306a36Sopenharmony_ci		brcmf_err("Failed to attach %s device\n", ndev->name);
94562306a36Sopenharmony_ci		free_netdev(ndev);
94662306a36Sopenharmony_ci		goto err_free_vif;
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	cfg->pub->mon_if = ifp;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	return &vif->wdev;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_cierr_free_vif:
95462306a36Sopenharmony_ci	brcmf_free_vif(vif);
95562306a36Sopenharmony_cierr_out:
95662306a36Sopenharmony_ci	return ERR_PTR(err);
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
96262306a36Sopenharmony_ci	struct net_device *ndev = wdev->netdev;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	ndev->netdev_ops->ndo_stop(ndev);
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	brcmf_net_detach(ndev, true);
96762306a36Sopenharmony_ci
96862306a36Sopenharmony_ci	cfg->pub->mon_if = NULL;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	return 0;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
97462306a36Sopenharmony_ci						     const char *name,
97562306a36Sopenharmony_ci						     unsigned char name_assign_type,
97662306a36Sopenharmony_ci						     enum nl80211_iftype type,
97762306a36Sopenharmony_ci						     struct vif_params *params)
97862306a36Sopenharmony_ci{
97962306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
98062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
98162306a36Sopenharmony_ci	struct wireless_dev *wdev;
98262306a36Sopenharmony_ci	int err;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
98562306a36Sopenharmony_ci	err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
98662306a36Sopenharmony_ci	if (err) {
98762306a36Sopenharmony_ci		bphy_err(drvr, "iface validation failed: err=%d\n", err);
98862306a36Sopenharmony_ci		return ERR_PTR(err);
98962306a36Sopenharmony_ci	}
99062306a36Sopenharmony_ci	switch (type) {
99162306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
99262306a36Sopenharmony_ci	case NL80211_IFTYPE_AP_VLAN:
99362306a36Sopenharmony_ci	case NL80211_IFTYPE_WDS:
99462306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
99562306a36Sopenharmony_ci		return ERR_PTR(-EOPNOTSUPP);
99662306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
99762306a36Sopenharmony_ci		return brcmf_mon_add_vif(wiphy, name);
99862306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
99962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
100062306a36Sopenharmony_ci		wdev = brcmf_apsta_add_vif(wiphy, name, params, type);
100162306a36Sopenharmony_ci		break;
100262306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
100362306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
100462306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
100562306a36Sopenharmony_ci		wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params);
100662306a36Sopenharmony_ci		break;
100762306a36Sopenharmony_ci	case NL80211_IFTYPE_UNSPECIFIED:
100862306a36Sopenharmony_ci	default:
100962306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
101062306a36Sopenharmony_ci	}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	if (IS_ERR(wdev))
101362306a36Sopenharmony_ci		bphy_err(drvr, "add iface %s type %d failed: err=%d\n", name,
101462306a36Sopenharmony_ci			 type, (int)PTR_ERR(wdev));
101562306a36Sopenharmony_ci	else
101662306a36Sopenharmony_ci		brcmf_cfg80211_update_proto_addr_mode(wdev);
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	return wdev;
101962306a36Sopenharmony_ci}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_cistatic void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
102462306a36Sopenharmony_ci		brcmf_set_mpc(ifp, mpc);
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_civoid brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
103062306a36Sopenharmony_ci	s32 err = 0;
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	if (check_vif_up(ifp->vif)) {
103362306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
103462306a36Sopenharmony_ci		if (err) {
103562306a36Sopenharmony_ci			bphy_err(drvr, "fail to set mpc\n");
103662306a36Sopenharmony_ci			return;
103762306a36Sopenharmony_ci		}
103862306a36Sopenharmony_ci		brcmf_dbg(INFO, "MPC : %d\n", mpc);
103962306a36Sopenharmony_ci	}
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_cistatic void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le,
104362306a36Sopenharmony_ci				       struct brcmf_scan_params_le *params_le)
104462306a36Sopenharmony_ci{
104562306a36Sopenharmony_ci	size_t params_size;
104662306a36Sopenharmony_ci	u32 ch;
104762306a36Sopenharmony_ci	int n_channels, n_ssids;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	memcpy(&params_le->ssid_le, &params_v2_le->ssid_le,
105062306a36Sopenharmony_ci	       sizeof(params_le->ssid_le));
105162306a36Sopenharmony_ci	memcpy(&params_le->bssid, &params_v2_le->bssid,
105262306a36Sopenharmony_ci	       sizeof(params_le->bssid));
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	params_le->bss_type = params_v2_le->bss_type;
105562306a36Sopenharmony_ci	params_le->scan_type = le32_to_cpu(params_v2_le->scan_type);
105662306a36Sopenharmony_ci	params_le->nprobes = params_v2_le->nprobes;
105762306a36Sopenharmony_ci	params_le->active_time = params_v2_le->active_time;
105862306a36Sopenharmony_ci	params_le->passive_time = params_v2_le->passive_time;
105962306a36Sopenharmony_ci	params_le->home_time = params_v2_le->home_time;
106062306a36Sopenharmony_ci	params_le->channel_num = params_v2_le->channel_num;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	ch = le32_to_cpu(params_v2_le->channel_num);
106362306a36Sopenharmony_ci	n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK;
106462306a36Sopenharmony_ci	n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT;
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	params_size = sizeof(u16) * n_channels;
106762306a36Sopenharmony_ci	if (n_ssids > 0) {
106862306a36Sopenharmony_ci		params_size = roundup(params_size, sizeof(u32));
106962306a36Sopenharmony_ci		params_size += sizeof(struct brcmf_ssid_le) * n_ssids;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	memcpy(&params_le->channel_list[0],
107362306a36Sopenharmony_ci	       &params_v2_le->channel_list[0], params_size);
107462306a36Sopenharmony_ci}
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_cistatic void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
107762306a36Sopenharmony_ci			     struct brcmf_scan_params_v2_le *params_le,
107862306a36Sopenharmony_ci			     struct cfg80211_scan_request *request)
107962306a36Sopenharmony_ci{
108062306a36Sopenharmony_ci	u32 n_ssids;
108162306a36Sopenharmony_ci	u32 n_channels;
108262306a36Sopenharmony_ci	s32 i;
108362306a36Sopenharmony_ci	s32 offset;
108462306a36Sopenharmony_ci	u16 chanspec;
108562306a36Sopenharmony_ci	char *ptr;
108662306a36Sopenharmony_ci	int length;
108762306a36Sopenharmony_ci	struct brcmf_ssid_le ssid_le;
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	eth_broadcast_addr(params_le->bssid);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);
109462306a36Sopenharmony_ci	params_le->bss_type = DOT11_BSSTYPE_ANY;
109562306a36Sopenharmony_ci	params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE);
109662306a36Sopenharmony_ci	params_le->channel_num = 0;
109762306a36Sopenharmony_ci	params_le->nprobes = cpu_to_le32(-1);
109862306a36Sopenharmony_ci	params_le->active_time = cpu_to_le32(-1);
109962306a36Sopenharmony_ci	params_le->passive_time = cpu_to_le32(-1);
110062306a36Sopenharmony_ci	params_le->home_time = cpu_to_le32(-1);
110162306a36Sopenharmony_ci	memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	/* Scan abort */
110462306a36Sopenharmony_ci	if (!request) {
110562306a36Sopenharmony_ci		length += sizeof(u16);
110662306a36Sopenharmony_ci		params_le->channel_num = cpu_to_le32(1);
110762306a36Sopenharmony_ci		params_le->channel_list[0] = cpu_to_le16(-1);
110862306a36Sopenharmony_ci		params_le->length = cpu_to_le16(length);
110962306a36Sopenharmony_ci		return;
111062306a36Sopenharmony_ci	}
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	n_ssids = request->n_ssids;
111362306a36Sopenharmony_ci	n_channels = request->n_channels;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/* Copy channel array if applicable */
111662306a36Sopenharmony_ci	brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
111762306a36Sopenharmony_ci		  n_channels);
111862306a36Sopenharmony_ci	if (n_channels > 0) {
111962306a36Sopenharmony_ci		length += roundup(sizeof(u16) * n_channels, sizeof(u32));
112062306a36Sopenharmony_ci		for (i = 0; i < n_channels; i++) {
112162306a36Sopenharmony_ci			chanspec = channel_to_chanspec(&cfg->d11inf,
112262306a36Sopenharmony_ci						       request->channels[i]);
112362306a36Sopenharmony_ci			brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
112462306a36Sopenharmony_ci				  request->channels[i]->hw_value, chanspec);
112562306a36Sopenharmony_ci			params_le->channel_list[i] = cpu_to_le16(chanspec);
112662306a36Sopenharmony_ci		}
112762306a36Sopenharmony_ci	} else {
112862306a36Sopenharmony_ci		brcmf_dbg(SCAN, "Scanning all channels\n");
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	/* Copy ssid array if applicable */
113262306a36Sopenharmony_ci	brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
113362306a36Sopenharmony_ci	if (n_ssids > 0) {
113462306a36Sopenharmony_ci		offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) +
113562306a36Sopenharmony_ci				n_channels * sizeof(u16);
113662306a36Sopenharmony_ci		offset = roundup(offset, sizeof(u32));
113762306a36Sopenharmony_ci		length += sizeof(ssid_le) * n_ssids,
113862306a36Sopenharmony_ci		ptr = (char *)params_le + offset;
113962306a36Sopenharmony_ci		for (i = 0; i < n_ssids; i++) {
114062306a36Sopenharmony_ci			memset(&ssid_le, 0, sizeof(ssid_le));
114162306a36Sopenharmony_ci			ssid_le.SSID_len =
114262306a36Sopenharmony_ci					cpu_to_le32(request->ssids[i].ssid_len);
114362306a36Sopenharmony_ci			memcpy(ssid_le.SSID, request->ssids[i].ssid,
114462306a36Sopenharmony_ci			       request->ssids[i].ssid_len);
114562306a36Sopenharmony_ci			if (!ssid_le.SSID_len)
114662306a36Sopenharmony_ci				brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
114762306a36Sopenharmony_ci			else
114862306a36Sopenharmony_ci				brcmf_dbg(SCAN, "%d: scan for  %.32s size=%d\n",
114962306a36Sopenharmony_ci					  i, ssid_le.SSID, ssid_le.SSID_len);
115062306a36Sopenharmony_ci			memcpy(ptr, &ssid_le, sizeof(ssid_le));
115162306a36Sopenharmony_ci			ptr += sizeof(ssid_le);
115262306a36Sopenharmony_ci		}
115362306a36Sopenharmony_ci	} else {
115462306a36Sopenharmony_ci		brcmf_dbg(SCAN, "Performing passive scan\n");
115562306a36Sopenharmony_ci		params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE);
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci	params_le->length = cpu_to_le16(length);
115862306a36Sopenharmony_ci	/* Adding mask to channel numbers */
115962306a36Sopenharmony_ci	params_le->channel_num =
116062306a36Sopenharmony_ci		cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
116162306a36Sopenharmony_ci			(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
116262306a36Sopenharmony_ci}
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_cis32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
116562306a36Sopenharmony_ci				struct brcmf_if *ifp, bool aborted,
116662306a36Sopenharmony_ci				bool fw_abort)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
116962306a36Sopenharmony_ci	struct brcmf_scan_params_v2_le params_v2_le;
117062306a36Sopenharmony_ci	struct cfg80211_scan_request *scan_request;
117162306a36Sopenharmony_ci	u64 reqid;
117262306a36Sopenharmony_ci	u32 bucket;
117362306a36Sopenharmony_ci	s32 err = 0;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	brcmf_dbg(SCAN, "Enter\n");
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	/* clear scan request, because the FW abort can cause a second call */
117862306a36Sopenharmony_ci	/* to this functon and might cause a double cfg80211_scan_done      */
117962306a36Sopenharmony_ci	scan_request = cfg->scan_request;
118062306a36Sopenharmony_ci	cfg->scan_request = NULL;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci	timer_delete_sync(&cfg->escan_timeout);
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	if (fw_abort) {
118562306a36Sopenharmony_ci		/* Do a scan abort to stop the driver's scan engine */
118662306a36Sopenharmony_ci		brcmf_dbg(SCAN, "ABORT scan in firmware\n");
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		brcmf_escan_prep(cfg, &params_v2_le, NULL);
118962306a36Sopenharmony_ci
119062306a36Sopenharmony_ci		/* E-Scan (or anyother type) can be aborted by SCAN */
119162306a36Sopenharmony_ci		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
119262306a36Sopenharmony_ci			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
119362306a36Sopenharmony_ci						     &params_v2_le,
119462306a36Sopenharmony_ci						     sizeof(params_v2_le));
119562306a36Sopenharmony_ci		} else {
119662306a36Sopenharmony_ci			struct brcmf_scan_params_le params_le;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci			brcmf_scan_params_v2_to_v1(&params_v2_le, &params_le);
119962306a36Sopenharmony_ci			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
120062306a36Sopenharmony_ci						     &params_le,
120162306a36Sopenharmony_ci						     sizeof(params_le));
120262306a36Sopenharmony_ci		}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		if (err)
120562306a36Sopenharmony_ci			bphy_err(drvr, "Scan abort failed\n");
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	brcmf_scan_config_mpc(ifp, 1);
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	/*
121162306a36Sopenharmony_ci	 * e-scan can be initiated internally
121262306a36Sopenharmony_ci	 * which takes precedence.
121362306a36Sopenharmony_ci	 */
121462306a36Sopenharmony_ci	if (cfg->int_escan_map) {
121562306a36Sopenharmony_ci		brcmf_dbg(SCAN, "scheduled scan completed (%x)\n",
121662306a36Sopenharmony_ci			  cfg->int_escan_map);
121762306a36Sopenharmony_ci		while (cfg->int_escan_map) {
121862306a36Sopenharmony_ci			bucket = __ffs(cfg->int_escan_map);
121962306a36Sopenharmony_ci			cfg->int_escan_map &= ~BIT(bucket);
122062306a36Sopenharmony_ci			reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno,
122162306a36Sopenharmony_ci							       bucket);
122262306a36Sopenharmony_ci			if (!aborted) {
122362306a36Sopenharmony_ci				brcmf_dbg(SCAN, "report results: reqid=%llu\n",
122462306a36Sopenharmony_ci					  reqid);
122562306a36Sopenharmony_ci				cfg80211_sched_scan_results(cfg_to_wiphy(cfg),
122662306a36Sopenharmony_ci							    reqid);
122762306a36Sopenharmony_ci			}
122862306a36Sopenharmony_ci		}
122962306a36Sopenharmony_ci	} else if (scan_request) {
123062306a36Sopenharmony_ci		struct cfg80211_scan_info info = {
123162306a36Sopenharmony_ci			.aborted = aborted,
123262306a36Sopenharmony_ci		};
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci		brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
123562306a36Sopenharmony_ci			  aborted ? "Aborted" : "Done");
123662306a36Sopenharmony_ci		cfg80211_scan_done(scan_request, &info);
123762306a36Sopenharmony_ci	}
123862306a36Sopenharmony_ci	if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
123962306a36Sopenharmony_ci		brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	return err;
124262306a36Sopenharmony_ci}
124362306a36Sopenharmony_ci
124462306a36Sopenharmony_cistatic int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy,
124562306a36Sopenharmony_ci					  struct wireless_dev *wdev)
124662306a36Sopenharmony_ci{
124762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
124862306a36Sopenharmony_ci	struct net_device *ndev = wdev->netdev;
124962306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
125062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
125162306a36Sopenharmony_ci	int ret;
125262306a36Sopenharmony_ci	int err;
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	brcmf_cfg80211_arm_vif_event(cfg, ifp->vif);
125562306a36Sopenharmony_ci
125662306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0);
125762306a36Sopenharmony_ci	if (err) {
125862306a36Sopenharmony_ci		bphy_err(drvr, "interface_remove failed %d\n", err);
125962306a36Sopenharmony_ci		goto err_unarm;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	/* wait for firmware event */
126362306a36Sopenharmony_ci	ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
126462306a36Sopenharmony_ci					    BRCMF_VIF_EVENT_TIMEOUT);
126562306a36Sopenharmony_ci	if (!ret) {
126662306a36Sopenharmony_ci		bphy_err(drvr, "timeout occurred\n");
126762306a36Sopenharmony_ci		err = -EIO;
126862306a36Sopenharmony_ci		goto err_unarm;
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	brcmf_remove_interface(ifp, true);
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_cierr_unarm:
127462306a36Sopenharmony_ci	brcmf_cfg80211_arm_vif_event(cfg, NULL);
127562306a36Sopenharmony_ci	return err;
127662306a36Sopenharmony_ci}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cistatic
127962306a36Sopenharmony_ciint brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
128262306a36Sopenharmony_ci	struct net_device *ndev = wdev->netdev;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	if (ndev && ndev == cfg_to_ndev(cfg))
128562306a36Sopenharmony_ci		return -ENOTSUPP;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	/* vif event pending in firmware */
128862306a36Sopenharmony_ci	if (brcmf_cfg80211_vif_event_armed(cfg))
128962306a36Sopenharmony_ci		return -EBUSY;
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (ndev) {
129262306a36Sopenharmony_ci		if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
129362306a36Sopenharmony_ci		    cfg->escan_info.ifp == netdev_priv(ndev))
129462306a36Sopenharmony_ci			brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
129562306a36Sopenharmony_ci						    true, true);
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci		brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
129862306a36Sopenharmony_ci	}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	switch (wdev->iftype) {
130162306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
130262306a36Sopenharmony_ci	case NL80211_IFTYPE_AP_VLAN:
130362306a36Sopenharmony_ci	case NL80211_IFTYPE_WDS:
130462306a36Sopenharmony_ci	case NL80211_IFTYPE_MESH_POINT:
130562306a36Sopenharmony_ci		return -EOPNOTSUPP;
130662306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
130762306a36Sopenharmony_ci		return brcmf_mon_del_vif(wiphy, wdev);
130862306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
130962306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
131062306a36Sopenharmony_ci		return brcmf_cfg80211_del_apsta_iface(wiphy, wdev);
131162306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_CLIENT:
131262306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
131362306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_DEVICE:
131462306a36Sopenharmony_ci		return brcmf_p2p_del_vif(wiphy, wdev);
131562306a36Sopenharmony_ci	case NL80211_IFTYPE_UNSPECIFIED:
131662306a36Sopenharmony_ci	default:
131762306a36Sopenharmony_ci		return -EINVAL;
131862306a36Sopenharmony_ci	}
131962306a36Sopenharmony_ci	return -EOPNOTSUPP;
132062306a36Sopenharmony_ci}
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_cistatic s32
132362306a36Sopenharmony_cibrcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
132462306a36Sopenharmony_ci			 enum nl80211_iftype type,
132562306a36Sopenharmony_ci			 struct vif_params *params)
132662306a36Sopenharmony_ci{
132762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
132862306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
132962306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif = ifp->vif;
133062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
133162306a36Sopenharmony_ci	s32 infra = 0;
133262306a36Sopenharmony_ci	s32 ap = 0;
133362306a36Sopenharmony_ci	s32 err = 0;
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
133662306a36Sopenharmony_ci		  type);
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	/* WAR: There are a number of p2p interface related problems which
133962306a36Sopenharmony_ci	 * need to be handled initially (before doing the validate).
134062306a36Sopenharmony_ci	 * wpa_supplicant tends to do iface changes on p2p device/client/go
134162306a36Sopenharmony_ci	 * which are not always possible/allowed. However we need to return
134262306a36Sopenharmony_ci	 * OK otherwise the wpa_supplicant wont start. The situation differs
134362306a36Sopenharmony_ci	 * on configuration and setup (p2pon=1 module param). The first check
134462306a36Sopenharmony_ci	 * is to see if the request is a change to station for p2p iface.
134562306a36Sopenharmony_ci	 */
134662306a36Sopenharmony_ci	if ((type == NL80211_IFTYPE_STATION) &&
134762306a36Sopenharmony_ci	    ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
134862306a36Sopenharmony_ci	     (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
134962306a36Sopenharmony_ci	     (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
135062306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
135162306a36Sopenharmony_ci		/* Now depending on whether module param p2pon=1 was used the
135262306a36Sopenharmony_ci		 * response needs to be either 0 or EOPNOTSUPP. The reason is
135362306a36Sopenharmony_ci		 * that if p2pon=1 is used, but a newer supplicant is used then
135462306a36Sopenharmony_ci		 * we should return an error, as this combination wont work.
135562306a36Sopenharmony_ci		 * In other situations 0 is returned and supplicant will start
135662306a36Sopenharmony_ci		 * normally. It will give a trace in cfg80211, but it is the
135762306a36Sopenharmony_ci		 * only way to get it working. Unfortunately this will result
135862306a36Sopenharmony_ci		 * in situation where we wont support new supplicant in
135962306a36Sopenharmony_ci		 * combination with module param p2pon=1, but that is the way
136062306a36Sopenharmony_ci		 * it is. If the user tries this then unloading of driver might
136162306a36Sopenharmony_ci		 * fail/lock.
136262306a36Sopenharmony_ci		 */
136362306a36Sopenharmony_ci		if (cfg->p2p.p2pdev_dynamically)
136462306a36Sopenharmony_ci			return -EOPNOTSUPP;
136562306a36Sopenharmony_ci		else
136662306a36Sopenharmony_ci			return 0;
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci	err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
136962306a36Sopenharmony_ci	if (err) {
137062306a36Sopenharmony_ci		bphy_err(drvr, "iface validation failed: err=%d\n", err);
137162306a36Sopenharmony_ci		return err;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci	switch (type) {
137462306a36Sopenharmony_ci	case NL80211_IFTYPE_MONITOR:
137562306a36Sopenharmony_ci	case NL80211_IFTYPE_WDS:
137662306a36Sopenharmony_ci		bphy_err(drvr, "type (%d) : currently we do not support this type\n",
137762306a36Sopenharmony_ci			 type);
137862306a36Sopenharmony_ci		return -EOPNOTSUPP;
137962306a36Sopenharmony_ci	case NL80211_IFTYPE_ADHOC:
138062306a36Sopenharmony_ci		infra = 0;
138162306a36Sopenharmony_ci		break;
138262306a36Sopenharmony_ci	case NL80211_IFTYPE_STATION:
138362306a36Sopenharmony_ci		infra = 1;
138462306a36Sopenharmony_ci		break;
138562306a36Sopenharmony_ci	case NL80211_IFTYPE_AP:
138662306a36Sopenharmony_ci	case NL80211_IFTYPE_P2P_GO:
138762306a36Sopenharmony_ci		ap = 1;
138862306a36Sopenharmony_ci		break;
138962306a36Sopenharmony_ci	default:
139062306a36Sopenharmony_ci		err = -EINVAL;
139162306a36Sopenharmony_ci		goto done;
139262306a36Sopenharmony_ci	}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if (ap) {
139562306a36Sopenharmony_ci		if (type == NL80211_IFTYPE_P2P_GO) {
139662306a36Sopenharmony_ci			brcmf_dbg(INFO, "IF Type = P2P GO\n");
139762306a36Sopenharmony_ci			err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
139862306a36Sopenharmony_ci		}
139962306a36Sopenharmony_ci		if (!err) {
140062306a36Sopenharmony_ci			brcmf_dbg(INFO, "IF Type = AP\n");
140162306a36Sopenharmony_ci		}
140262306a36Sopenharmony_ci	} else {
140362306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
140462306a36Sopenharmony_ci		if (err) {
140562306a36Sopenharmony_ci			bphy_err(drvr, "WLC_SET_INFRA error (%d)\n", err);
140662306a36Sopenharmony_ci			err = -EAGAIN;
140762306a36Sopenharmony_ci			goto done;
140862306a36Sopenharmony_ci		}
140962306a36Sopenharmony_ci		brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
141062306a36Sopenharmony_ci			  "Adhoc" : "Infra");
141162306a36Sopenharmony_ci	}
141262306a36Sopenharmony_ci	ndev->ieee80211_ptr->iftype = type;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_cidone:
141762306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci	return err;
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic s32
142362306a36Sopenharmony_cibrcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
142462306a36Sopenharmony_ci		struct cfg80211_scan_request *request)
142562306a36Sopenharmony_ci{
142662306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
142762306a36Sopenharmony_ci	s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE +
142862306a36Sopenharmony_ci			  offsetof(struct brcmf_escan_params_le, params_v2_le);
142962306a36Sopenharmony_ci	struct brcmf_escan_params_le *params;
143062306a36Sopenharmony_ci	s32 err = 0;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	brcmf_dbg(SCAN, "E-SCAN START\n");
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci	if (request != NULL) {
143562306a36Sopenharmony_ci		/* Allocate space for populating ssids in struct */
143662306a36Sopenharmony_ci		params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci		/* Allocate space for populating ssids in struct */
143962306a36Sopenharmony_ci		params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
144062306a36Sopenharmony_ci	}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	params = kzalloc(params_size, GFP_KERNEL);
144362306a36Sopenharmony_ci	if (!params) {
144462306a36Sopenharmony_ci		err = -ENOMEM;
144562306a36Sopenharmony_ci		goto exit;
144662306a36Sopenharmony_ci	}
144762306a36Sopenharmony_ci	BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
144862306a36Sopenharmony_ci	brcmf_escan_prep(cfg, &params->params_v2_le, request);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2);
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
145362306a36Sopenharmony_ci		struct brcmf_escan_params_le *params_v1;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci		params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
145662306a36Sopenharmony_ci		params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE;
145762306a36Sopenharmony_ci		params_v1 = kzalloc(params_size, GFP_KERNEL);
145862306a36Sopenharmony_ci		if (!params_v1) {
145962306a36Sopenharmony_ci			err = -ENOMEM;
146062306a36Sopenharmony_ci			goto exit_params;
146162306a36Sopenharmony_ci		}
146262306a36Sopenharmony_ci		params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
146362306a36Sopenharmony_ci		brcmf_scan_params_v2_to_v1(&params->params_v2_le, &params_v1->params_le);
146462306a36Sopenharmony_ci		kfree(params);
146562306a36Sopenharmony_ci		params = params_v1;
146662306a36Sopenharmony_ci	}
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
146962306a36Sopenharmony_ci	params->sync_id = cpu_to_le16(0x1234);
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
147262306a36Sopenharmony_ci	if (err) {
147362306a36Sopenharmony_ci		if (err == -EBUSY)
147462306a36Sopenharmony_ci			brcmf_dbg(INFO, "system busy : escan canceled\n");
147562306a36Sopenharmony_ci		else
147662306a36Sopenharmony_ci			bphy_err(drvr, "error (%d)\n", err);
147762306a36Sopenharmony_ci	}
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ciexit_params:
148062306a36Sopenharmony_ci	kfree(params);
148162306a36Sopenharmony_ciexit:
148262306a36Sopenharmony_ci	return err;
148362306a36Sopenharmony_ci}
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_cistatic s32
148662306a36Sopenharmony_cibrcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request)
148762306a36Sopenharmony_ci{
148862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
148962306a36Sopenharmony_ci	s32 err;
149062306a36Sopenharmony_ci	struct brcmf_scan_results *results;
149162306a36Sopenharmony_ci	struct escan_info *escan = &cfg->escan_info;
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	brcmf_dbg(SCAN, "Enter\n");
149462306a36Sopenharmony_ci	escan->ifp = ifp;
149562306a36Sopenharmony_ci	escan->wiphy = cfg->wiphy;
149662306a36Sopenharmony_ci	escan->escan_state = WL_ESCAN_STATE_SCANNING;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	brcmf_scan_config_mpc(ifp, 0);
149962306a36Sopenharmony_ci	results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
150062306a36Sopenharmony_ci	results->version = 0;
150162306a36Sopenharmony_ci	results->count = 0;
150262306a36Sopenharmony_ci	results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	err = escan->run(cfg, ifp, request);
150562306a36Sopenharmony_ci	if (err)
150662306a36Sopenharmony_ci		brcmf_scan_config_mpc(ifp, 1);
150762306a36Sopenharmony_ci	return err;
150862306a36Sopenharmony_ci}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_cistatic s32
151162306a36Sopenharmony_cibrcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
151262306a36Sopenharmony_ci{
151362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
151462306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
151562306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
151662306a36Sopenharmony_ci	s32 err = 0;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
151962306a36Sopenharmony_ci	vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
152062306a36Sopenharmony_ci	if (!check_vif_up(vif))
152162306a36Sopenharmony_ci		return -EIO;
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
152462306a36Sopenharmony_ci		bphy_err(drvr, "Scanning already: status (%lu)\n",
152562306a36Sopenharmony_ci			 cfg->scan_status);
152662306a36Sopenharmony_ci		return -EAGAIN;
152762306a36Sopenharmony_ci	}
152862306a36Sopenharmony_ci	if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
152962306a36Sopenharmony_ci		bphy_err(drvr, "Scanning being aborted: status (%lu)\n",
153062306a36Sopenharmony_ci			 cfg->scan_status);
153162306a36Sopenharmony_ci		return -EAGAIN;
153262306a36Sopenharmony_ci	}
153362306a36Sopenharmony_ci	if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
153462306a36Sopenharmony_ci		bphy_err(drvr, "Scanning suppressed: status (%lu)\n",
153562306a36Sopenharmony_ci			 cfg->scan_status);
153662306a36Sopenharmony_ci		return -EAGAIN;
153762306a36Sopenharmony_ci	}
153862306a36Sopenharmony_ci	if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) {
153962306a36Sopenharmony_ci		bphy_err(drvr, "Connecting: status (%lu)\n", vif->sme_state);
154062306a36Sopenharmony_ci		return -EAGAIN;
154162306a36Sopenharmony_ci	}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ci	/* If scan req comes for p2p0, send it over primary I/F */
154462306a36Sopenharmony_ci	if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
154562306a36Sopenharmony_ci		vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
154662306a36Sopenharmony_ci
154762306a36Sopenharmony_ci	brcmf_dbg(SCAN, "START ESCAN\n");
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	cfg->scan_request = request;
155062306a36Sopenharmony_ci	set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci	cfg->escan_info.run = brcmf_run_escan;
155362306a36Sopenharmony_ci	err = brcmf_p2p_scan_prep(wiphy, request, vif);
155462306a36Sopenharmony_ci	if (err)
155562306a36Sopenharmony_ci		goto scan_out;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
155862306a36Sopenharmony_ci				    request->ie, request->ie_len);
155962306a36Sopenharmony_ci	if (err)
156062306a36Sopenharmony_ci		goto scan_out;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	err = brcmf_do_escan(vif->ifp, request);
156362306a36Sopenharmony_ci	if (err)
156462306a36Sopenharmony_ci		goto scan_out;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	/* Arm scan timeout timer */
156762306a36Sopenharmony_ci	mod_timer(&cfg->escan_timeout,
156862306a36Sopenharmony_ci		  jiffies + msecs_to_jiffies(BRCMF_ESCAN_TIMER_INTERVAL_MS));
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	return 0;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ciscan_out:
157362306a36Sopenharmony_ci	bphy_err(drvr, "scan error (%d)\n", err);
157462306a36Sopenharmony_ci	clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
157562306a36Sopenharmony_ci	cfg->scan_request = NULL;
157662306a36Sopenharmony_ci	return err;
157762306a36Sopenharmony_ci}
157862306a36Sopenharmony_ci
157962306a36Sopenharmony_cistatic s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
158262306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
158362306a36Sopenharmony_ci	s32 err = 0;
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_set(ifp, "rtsthresh", rts_threshold);
158662306a36Sopenharmony_ci	if (err)
158762306a36Sopenharmony_ci		bphy_err(drvr, "Error (%d)\n", err);
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	return err;
159062306a36Sopenharmony_ci}
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_cistatic s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
159362306a36Sopenharmony_ci{
159462306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
159562306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
159662306a36Sopenharmony_ci	s32 err = 0;
159762306a36Sopenharmony_ci
159862306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_set(ifp, "fragthresh",
159962306a36Sopenharmony_ci				      frag_threshold);
160062306a36Sopenharmony_ci	if (err)
160162306a36Sopenharmony_ci		bphy_err(drvr, "Error (%d)\n", err);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	return err;
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_cistatic s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
160762306a36Sopenharmony_ci{
160862306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
160962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
161062306a36Sopenharmony_ci	s32 err = 0;
161162306a36Sopenharmony_ci	u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, cmd, retry);
161462306a36Sopenharmony_ci	if (err) {
161562306a36Sopenharmony_ci		bphy_err(drvr, "cmd (%d) , error (%d)\n", cmd, err);
161662306a36Sopenharmony_ci		return err;
161762306a36Sopenharmony_ci	}
161862306a36Sopenharmony_ci	return err;
161962306a36Sopenharmony_ci}
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_cistatic s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
162462306a36Sopenharmony_ci	struct net_device *ndev = cfg_to_ndev(cfg);
162562306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
162662306a36Sopenharmony_ci	s32 err = 0;
162762306a36Sopenharmony_ci
162862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
162962306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
163062306a36Sopenharmony_ci		return -EIO;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
163362306a36Sopenharmony_ci	    (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
163462306a36Sopenharmony_ci		cfg->conf->rts_threshold = wiphy->rts_threshold;
163562306a36Sopenharmony_ci		err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
163662306a36Sopenharmony_ci		if (!err)
163762306a36Sopenharmony_ci			goto done;
163862306a36Sopenharmony_ci	}
163962306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
164062306a36Sopenharmony_ci	    (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
164162306a36Sopenharmony_ci		cfg->conf->frag_threshold = wiphy->frag_threshold;
164262306a36Sopenharmony_ci		err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
164362306a36Sopenharmony_ci		if (!err)
164462306a36Sopenharmony_ci			goto done;
164562306a36Sopenharmony_ci	}
164662306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_LONG
164762306a36Sopenharmony_ci	    && (cfg->conf->retry_long != wiphy->retry_long)) {
164862306a36Sopenharmony_ci		cfg->conf->retry_long = wiphy->retry_long;
164962306a36Sopenharmony_ci		err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
165062306a36Sopenharmony_ci		if (!err)
165162306a36Sopenharmony_ci			goto done;
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci	if (changed & WIPHY_PARAM_RETRY_SHORT
165462306a36Sopenharmony_ci	    && (cfg->conf->retry_short != wiphy->retry_short)) {
165562306a36Sopenharmony_ci		cfg->conf->retry_short = wiphy->retry_short;
165662306a36Sopenharmony_ci		err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
165762306a36Sopenharmony_ci		if (!err)
165862306a36Sopenharmony_ci			goto done;
165962306a36Sopenharmony_ci	}
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_cidone:
166262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
166362306a36Sopenharmony_ci	return err;
166462306a36Sopenharmony_ci}
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	memset(prof, 0, sizeof(*prof));
166962306a36Sopenharmony_ci}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_cistatic u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
167262306a36Sopenharmony_ci{
167362306a36Sopenharmony_ci	u16 reason;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	switch (e->event_code) {
167662306a36Sopenharmony_ci	case BRCMF_E_DEAUTH:
167762306a36Sopenharmony_ci	case BRCMF_E_DEAUTH_IND:
167862306a36Sopenharmony_ci	case BRCMF_E_DISASSOC_IND:
167962306a36Sopenharmony_ci		reason = e->reason;
168062306a36Sopenharmony_ci		break;
168162306a36Sopenharmony_ci	case BRCMF_E_LINK:
168262306a36Sopenharmony_ci	default:
168362306a36Sopenharmony_ci		reason = 0;
168462306a36Sopenharmony_ci		break;
168562306a36Sopenharmony_ci	}
168662306a36Sopenharmony_ci	return reason;
168762306a36Sopenharmony_ci}
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_cistatic int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
169062306a36Sopenharmony_ci{
169162306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
169262306a36Sopenharmony_ci	struct brcmf_wsec_pmk_le pmk;
169362306a36Sopenharmony_ci	int err;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	memset(&pmk, 0, sizeof(pmk));
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	/* pass pmk directly */
169862306a36Sopenharmony_ci	pmk.key_len = cpu_to_le16(pmk_len);
169962306a36Sopenharmony_ci	pmk.flags = cpu_to_le16(0);
170062306a36Sopenharmony_ci	memcpy(pmk.key, pmk_data, pmk_len);
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	/* store psk in firmware */
170362306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
170462306a36Sopenharmony_ci				     &pmk, sizeof(pmk));
170562306a36Sopenharmony_ci	if (err < 0)
170662306a36Sopenharmony_ci		bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",
170762306a36Sopenharmony_ci			 pmk_len);
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	return err;
171062306a36Sopenharmony_ci}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_cistatic int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data,
171362306a36Sopenharmony_ci				  u16 pwd_len)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
171662306a36Sopenharmony_ci	struct brcmf_wsec_sae_pwd_le sae_pwd;
171762306a36Sopenharmony_ci	int err;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	if (pwd_len > BRCMF_WSEC_MAX_SAE_PASSWORD_LEN) {
172062306a36Sopenharmony_ci		bphy_err(drvr, "sae_password must be less than %d\n",
172162306a36Sopenharmony_ci			 BRCMF_WSEC_MAX_SAE_PASSWORD_LEN);
172262306a36Sopenharmony_ci		return -EINVAL;
172362306a36Sopenharmony_ci	}
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	sae_pwd.key_len = cpu_to_le16(pwd_len);
172662306a36Sopenharmony_ci	memcpy(sae_pwd.key, pwd_data, pwd_len);
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_set(ifp, "sae_password", &sae_pwd,
172962306a36Sopenharmony_ci				       sizeof(sae_pwd));
173062306a36Sopenharmony_ci	if (err < 0)
173162306a36Sopenharmony_ci		bphy_err(drvr, "failed to set SAE password in firmware (len=%u)\n",
173262306a36Sopenharmony_ci			 pwd_len);
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	return err;
173562306a36Sopenharmony_ci}
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_cistatic void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,
173862306a36Sopenharmony_ci			    bool locally_generated)
173962306a36Sopenharmony_ci{
174062306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
174162306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
174262306a36Sopenharmony_ci	bool bus_up = drvr->bus_if->state == BRCMF_BUS_UP;
174362306a36Sopenharmony_ci	s32 err = 0;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci	if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
174862306a36Sopenharmony_ci		if (bus_up) {
174962306a36Sopenharmony_ci			brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n");
175062306a36Sopenharmony_ci			err = brcmf_fil_cmd_data_set(vif->ifp,
175162306a36Sopenharmony_ci						     BRCMF_C_DISASSOC, NULL, 0);
175262306a36Sopenharmony_ci			if (err)
175362306a36Sopenharmony_ci				bphy_err(drvr, "WLC_DISASSOC failed (%d)\n",
175462306a36Sopenharmony_ci					 err);
175562306a36Sopenharmony_ci		}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci		if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
175862306a36Sopenharmony_ci		    (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
175962306a36Sopenharmony_ci			cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
176062306a36Sopenharmony_ci					      locally_generated, GFP_KERNEL);
176162306a36Sopenharmony_ci	}
176262306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
176362306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
176462306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
176562306a36Sopenharmony_ci	clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
176662306a36Sopenharmony_ci	brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
176762306a36Sopenharmony_ci	if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
176862306a36Sopenharmony_ci		if (bus_up)
176962306a36Sopenharmony_ci			brcmf_set_pmk(vif->ifp, NULL, 0);
177062306a36Sopenharmony_ci		vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
177162306a36Sopenharmony_ci	}
177262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
177362306a36Sopenharmony_ci}
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_cistatic s32
177662306a36Sopenharmony_cibrcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
177762306a36Sopenharmony_ci		      struct cfg80211_ibss_params *params)
177862306a36Sopenharmony_ci{
177962306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
178062306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
178162306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
178262306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
178362306a36Sopenharmony_ci	struct brcmf_join_params join_params;
178462306a36Sopenharmony_ci	size_t join_params_size = 0;
178562306a36Sopenharmony_ci	s32 err = 0;
178662306a36Sopenharmony_ci	s32 wsec = 0;
178762306a36Sopenharmony_ci	s32 bcnprd;
178862306a36Sopenharmony_ci	u16 chanspec;
178962306a36Sopenharmony_ci	u32 ssid_len;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
179262306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
179362306a36Sopenharmony_ci		return -EIO;
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	if (params->ssid)
179662306a36Sopenharmony_ci		brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
179762306a36Sopenharmony_ci	else {
179862306a36Sopenharmony_ci		brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
179962306a36Sopenharmony_ci		return -EOPNOTSUPP;
180062306a36Sopenharmony_ci	}
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	if (params->bssid)
180562306a36Sopenharmony_ci		brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
180662306a36Sopenharmony_ci	else
180762306a36Sopenharmony_ci		brcmf_dbg(CONN, "No BSSID specified\n");
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_ci	if (params->chandef.chan)
181062306a36Sopenharmony_ci		brcmf_dbg(CONN, "channel: %d\n",
181162306a36Sopenharmony_ci			  params->chandef.chan->center_freq);
181262306a36Sopenharmony_ci	else
181362306a36Sopenharmony_ci		brcmf_dbg(CONN, "no channel specified\n");
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	if (params->channel_fixed)
181662306a36Sopenharmony_ci		brcmf_dbg(CONN, "fixed channel required\n");
181762306a36Sopenharmony_ci	else
181862306a36Sopenharmony_ci		brcmf_dbg(CONN, "no fixed channel required\n");
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	if (params->ie && params->ie_len)
182162306a36Sopenharmony_ci		brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
182262306a36Sopenharmony_ci	else
182362306a36Sopenharmony_ci		brcmf_dbg(CONN, "no ie specified\n");
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	if (params->beacon_interval)
182662306a36Sopenharmony_ci		brcmf_dbg(CONN, "beacon interval: %d\n",
182762306a36Sopenharmony_ci			  params->beacon_interval);
182862306a36Sopenharmony_ci	else
182962306a36Sopenharmony_ci		brcmf_dbg(CONN, "no beacon interval specified\n");
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	if (params->basic_rates)
183262306a36Sopenharmony_ci		brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
183362306a36Sopenharmony_ci	else
183462306a36Sopenharmony_ci		brcmf_dbg(CONN, "no basic rates specified\n");
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	if (params->privacy)
183762306a36Sopenharmony_ci		brcmf_dbg(CONN, "privacy required\n");
183862306a36Sopenharmony_ci	else
183962306a36Sopenharmony_ci		brcmf_dbg(CONN, "no privacy required\n");
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci	/* Configure Privacy for starter */
184262306a36Sopenharmony_ci	if (params->privacy)
184362306a36Sopenharmony_ci		wsec |= WEP_ENABLED;
184462306a36Sopenharmony_ci
184562306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
184662306a36Sopenharmony_ci	if (err) {
184762306a36Sopenharmony_ci		bphy_err(drvr, "wsec failed (%d)\n", err);
184862306a36Sopenharmony_ci		goto done;
184962306a36Sopenharmony_ci	}
185062306a36Sopenharmony_ci
185162306a36Sopenharmony_ci	/* Configure Beacon Interval for starter */
185262306a36Sopenharmony_ci	if (params->beacon_interval)
185362306a36Sopenharmony_ci		bcnprd = params->beacon_interval;
185462306a36Sopenharmony_ci	else
185562306a36Sopenharmony_ci		bcnprd = 100;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
185862306a36Sopenharmony_ci	if (err) {
185962306a36Sopenharmony_ci		bphy_err(drvr, "WLC_SET_BCNPRD failed (%d)\n", err);
186062306a36Sopenharmony_ci		goto done;
186162306a36Sopenharmony_ci	}
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	/* Configure required join parameter */
186462306a36Sopenharmony_ci	memset(&join_params, 0, sizeof(struct brcmf_join_params));
186562306a36Sopenharmony_ci
186662306a36Sopenharmony_ci	/* SSID */
186762306a36Sopenharmony_ci	ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
186862306a36Sopenharmony_ci	memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
186962306a36Sopenharmony_ci	join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
187062306a36Sopenharmony_ci	join_params_size = sizeof(join_params.ssid_le);
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	/* BSSID */
187362306a36Sopenharmony_ci	if (params->bssid) {
187462306a36Sopenharmony_ci		memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
187562306a36Sopenharmony_ci		join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
187662306a36Sopenharmony_ci		memcpy(profile->bssid, params->bssid, ETH_ALEN);
187762306a36Sopenharmony_ci	} else {
187862306a36Sopenharmony_ci		eth_broadcast_addr(join_params.params_le.bssid);
187962306a36Sopenharmony_ci		eth_zero_addr(profile->bssid);
188062306a36Sopenharmony_ci	}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	/* Channel */
188362306a36Sopenharmony_ci	if (params->chandef.chan) {
188462306a36Sopenharmony_ci		u32 target_channel;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci		cfg->channel =
188762306a36Sopenharmony_ci			ieee80211_frequency_to_channel(
188862306a36Sopenharmony_ci				params->chandef.chan->center_freq);
188962306a36Sopenharmony_ci		if (params->channel_fixed) {
189062306a36Sopenharmony_ci			/* adding chanspec */
189162306a36Sopenharmony_ci			chanspec = chandef_to_chanspec(&cfg->d11inf,
189262306a36Sopenharmony_ci						       &params->chandef);
189362306a36Sopenharmony_ci			join_params.params_le.chanspec_list[0] =
189462306a36Sopenharmony_ci				cpu_to_le16(chanspec);
189562306a36Sopenharmony_ci			join_params.params_le.chanspec_num = cpu_to_le32(1);
189662306a36Sopenharmony_ci			join_params_size += sizeof(join_params.params_le);
189762306a36Sopenharmony_ci		}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		/* set channel for starter */
190062306a36Sopenharmony_ci		target_channel = cfg->channel;
190162306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
190262306a36Sopenharmony_ci					    target_channel);
190362306a36Sopenharmony_ci		if (err) {
190462306a36Sopenharmony_ci			bphy_err(drvr, "WLC_SET_CHANNEL failed (%d)\n", err);
190562306a36Sopenharmony_ci			goto done;
190662306a36Sopenharmony_ci		}
190762306a36Sopenharmony_ci	} else
190862306a36Sopenharmony_ci		cfg->channel = 0;
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	cfg->ibss_starter = false;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
191462306a36Sopenharmony_ci				     &join_params, join_params_size);
191562306a36Sopenharmony_ci	if (err) {
191662306a36Sopenharmony_ci		bphy_err(drvr, "WLC_SET_SSID failed (%d)\n", err);
191762306a36Sopenharmony_ci		goto done;
191862306a36Sopenharmony_ci	}
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_cidone:
192162306a36Sopenharmony_ci	if (err)
192262306a36Sopenharmony_ci		clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
192362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
192462306a36Sopenharmony_ci	return err;
192562306a36Sopenharmony_ci}
192662306a36Sopenharmony_ci
192762306a36Sopenharmony_cistatic s32
192862306a36Sopenharmony_cibrcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
192962306a36Sopenharmony_ci{
193062306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
193362306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif)) {
193462306a36Sopenharmony_ci		/* When driver is being unloaded, it can end up here. If an
193562306a36Sopenharmony_ci		 * error is returned then later on a debug trace in the wireless
193662306a36Sopenharmony_ci		 * core module will be printed. To avoid this 0 is returned.
193762306a36Sopenharmony_ci		 */
193862306a36Sopenharmony_ci		return 0;
193962306a36Sopenharmony_ci	}
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_ci	brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING, true);
194262306a36Sopenharmony_ci	brcmf_net_setcarrier(ifp, false);
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	return 0;
194762306a36Sopenharmony_ci}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_cistatic s32 brcmf_set_wpa_version(struct net_device *ndev,
195062306a36Sopenharmony_ci				 struct cfg80211_connect_params *sme)
195162306a36Sopenharmony_ci{
195262306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
195362306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
195462306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
195562306a36Sopenharmony_ci	struct brcmf_cfg80211_security *sec;
195662306a36Sopenharmony_ci	s32 val = 0;
195762306a36Sopenharmony_ci	s32 err = 0;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
196062306a36Sopenharmony_ci		val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
196162306a36Sopenharmony_ci	else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
196262306a36Sopenharmony_ci		val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
196362306a36Sopenharmony_ci	else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
196462306a36Sopenharmony_ci		val = WPA3_AUTH_SAE_PSK;
196562306a36Sopenharmony_ci	else
196662306a36Sopenharmony_ci		val = WPA_AUTH_DISABLED;
196762306a36Sopenharmony_ci	brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
196862306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val);
196962306a36Sopenharmony_ci	if (err) {
197062306a36Sopenharmony_ci		bphy_err(drvr, "set wpa_auth failed (%d)\n", err);
197162306a36Sopenharmony_ci		return err;
197262306a36Sopenharmony_ci	}
197362306a36Sopenharmony_ci	sec = &profile->sec;
197462306a36Sopenharmony_ci	sec->wpa_versions = sme->crypto.wpa_versions;
197562306a36Sopenharmony_ci	return err;
197662306a36Sopenharmony_ci}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_cistatic s32 brcmf_set_auth_type(struct net_device *ndev,
197962306a36Sopenharmony_ci			       struct cfg80211_connect_params *sme)
198062306a36Sopenharmony_ci{
198162306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
198262306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
198362306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
198462306a36Sopenharmony_ci	struct brcmf_cfg80211_security *sec;
198562306a36Sopenharmony_ci	s32 val = 0;
198662306a36Sopenharmony_ci	s32 err = 0;
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_ci	switch (sme->auth_type) {
198962306a36Sopenharmony_ci	case NL80211_AUTHTYPE_OPEN_SYSTEM:
199062306a36Sopenharmony_ci		val = 0;
199162306a36Sopenharmony_ci		brcmf_dbg(CONN, "open system\n");
199262306a36Sopenharmony_ci		break;
199362306a36Sopenharmony_ci	case NL80211_AUTHTYPE_SHARED_KEY:
199462306a36Sopenharmony_ci		val = 1;
199562306a36Sopenharmony_ci		brcmf_dbg(CONN, "shared key\n");
199662306a36Sopenharmony_ci		break;
199762306a36Sopenharmony_ci	case NL80211_AUTHTYPE_SAE:
199862306a36Sopenharmony_ci		val = 3;
199962306a36Sopenharmony_ci		brcmf_dbg(CONN, "SAE authentication\n");
200062306a36Sopenharmony_ci		break;
200162306a36Sopenharmony_ci	default:
200262306a36Sopenharmony_ci		val = 2;
200362306a36Sopenharmony_ci		brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
200462306a36Sopenharmony_ci		break;
200562306a36Sopenharmony_ci	}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
200862306a36Sopenharmony_ci	if (err) {
200962306a36Sopenharmony_ci		bphy_err(drvr, "set auth failed (%d)\n", err);
201062306a36Sopenharmony_ci		return err;
201162306a36Sopenharmony_ci	}
201262306a36Sopenharmony_ci	sec = &profile->sec;
201362306a36Sopenharmony_ci	sec->auth_type = sme->auth_type;
201462306a36Sopenharmony_ci	return err;
201562306a36Sopenharmony_ci}
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_cistatic s32
201862306a36Sopenharmony_cibrcmf_set_wsec_mode(struct net_device *ndev,
201962306a36Sopenharmony_ci		    struct cfg80211_connect_params *sme)
202062306a36Sopenharmony_ci{
202162306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
202262306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
202362306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
202462306a36Sopenharmony_ci	struct brcmf_cfg80211_security *sec;
202562306a36Sopenharmony_ci	s32 pval = 0;
202662306a36Sopenharmony_ci	s32 gval = 0;
202762306a36Sopenharmony_ci	s32 wsec;
202862306a36Sopenharmony_ci	s32 err = 0;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	if (sme->crypto.n_ciphers_pairwise) {
203162306a36Sopenharmony_ci		switch (sme->crypto.ciphers_pairwise[0]) {
203262306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP40:
203362306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP104:
203462306a36Sopenharmony_ci			pval = WEP_ENABLED;
203562306a36Sopenharmony_ci			break;
203662306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_TKIP:
203762306a36Sopenharmony_ci			pval = TKIP_ENABLED;
203862306a36Sopenharmony_ci			break;
203962306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_CCMP:
204062306a36Sopenharmony_ci			pval = AES_ENABLED;
204162306a36Sopenharmony_ci			break;
204262306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_AES_CMAC:
204362306a36Sopenharmony_ci			pval = AES_ENABLED;
204462306a36Sopenharmony_ci			break;
204562306a36Sopenharmony_ci		default:
204662306a36Sopenharmony_ci			bphy_err(drvr, "invalid cipher pairwise (%d)\n",
204762306a36Sopenharmony_ci				 sme->crypto.ciphers_pairwise[0]);
204862306a36Sopenharmony_ci			return -EINVAL;
204962306a36Sopenharmony_ci		}
205062306a36Sopenharmony_ci	}
205162306a36Sopenharmony_ci	if (sme->crypto.cipher_group) {
205262306a36Sopenharmony_ci		switch (sme->crypto.cipher_group) {
205362306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP40:
205462306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_WEP104:
205562306a36Sopenharmony_ci			gval = WEP_ENABLED;
205662306a36Sopenharmony_ci			break;
205762306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_TKIP:
205862306a36Sopenharmony_ci			gval = TKIP_ENABLED;
205962306a36Sopenharmony_ci			break;
206062306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_CCMP:
206162306a36Sopenharmony_ci			gval = AES_ENABLED;
206262306a36Sopenharmony_ci			break;
206362306a36Sopenharmony_ci		case WLAN_CIPHER_SUITE_AES_CMAC:
206462306a36Sopenharmony_ci			gval = AES_ENABLED;
206562306a36Sopenharmony_ci			break;
206662306a36Sopenharmony_ci		default:
206762306a36Sopenharmony_ci			bphy_err(drvr, "invalid cipher group (%d)\n",
206862306a36Sopenharmony_ci				 sme->crypto.cipher_group);
206962306a36Sopenharmony_ci			return -EINVAL;
207062306a36Sopenharmony_ci		}
207162306a36Sopenharmony_ci	}
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci	brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
207462306a36Sopenharmony_ci	/* In case of privacy, but no security and WPS then simulate */
207562306a36Sopenharmony_ci	/* setting AES. WPS-2.0 allows no security                   */
207662306a36Sopenharmony_ci	if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
207762306a36Sopenharmony_ci	    sme->privacy)
207862306a36Sopenharmony_ci		pval = AES_ENABLED;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	wsec = pval | gval;
208162306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
208262306a36Sopenharmony_ci	if (err) {
208362306a36Sopenharmony_ci		bphy_err(drvr, "error (%d)\n", err);
208462306a36Sopenharmony_ci		return err;
208562306a36Sopenharmony_ci	}
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	sec = &profile->sec;
208862306a36Sopenharmony_ci	sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
208962306a36Sopenharmony_ci	sec->cipher_group = sme->crypto.cipher_group;
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	return err;
209262306a36Sopenharmony_ci}
209362306a36Sopenharmony_ci
209462306a36Sopenharmony_cistatic s32
209562306a36Sopenharmony_cibrcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
209662306a36Sopenharmony_ci{
209762306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
209862306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
209962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
210062306a36Sopenharmony_ci	s32 val;
210162306a36Sopenharmony_ci	s32 err;
210262306a36Sopenharmony_ci	const struct brcmf_tlv *rsn_ie;
210362306a36Sopenharmony_ci	const u8 *ie;
210462306a36Sopenharmony_ci	u32 ie_len;
210562306a36Sopenharmony_ci	u32 offset;
210662306a36Sopenharmony_ci	u16 rsn_cap;
210762306a36Sopenharmony_ci	u32 mfp;
210862306a36Sopenharmony_ci	u16 count;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
211162306a36Sopenharmony_ci	profile->is_ft = false;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	if (!sme->crypto.n_akm_suites)
211462306a36Sopenharmony_ci		return 0;
211562306a36Sopenharmony_ci
211662306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val);
211762306a36Sopenharmony_ci	if (err) {
211862306a36Sopenharmony_ci		bphy_err(drvr, "could not get wpa_auth (%d)\n", err);
211962306a36Sopenharmony_ci		return err;
212062306a36Sopenharmony_ci	}
212162306a36Sopenharmony_ci	if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
212262306a36Sopenharmony_ci		switch (sme->crypto.akm_suites[0]) {
212362306a36Sopenharmony_ci		case WLAN_AKM_SUITE_8021X:
212462306a36Sopenharmony_ci			val = WPA_AUTH_UNSPECIFIED;
212562306a36Sopenharmony_ci			if (sme->want_1x)
212662306a36Sopenharmony_ci				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
212762306a36Sopenharmony_ci			break;
212862306a36Sopenharmony_ci		case WLAN_AKM_SUITE_PSK:
212962306a36Sopenharmony_ci			val = WPA_AUTH_PSK;
213062306a36Sopenharmony_ci			break;
213162306a36Sopenharmony_ci		default:
213262306a36Sopenharmony_ci			bphy_err(drvr, "invalid akm suite (%d)\n",
213362306a36Sopenharmony_ci				 sme->crypto.akm_suites[0]);
213462306a36Sopenharmony_ci			return -EINVAL;
213562306a36Sopenharmony_ci		}
213662306a36Sopenharmony_ci	} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
213762306a36Sopenharmony_ci		switch (sme->crypto.akm_suites[0]) {
213862306a36Sopenharmony_ci		case WLAN_AKM_SUITE_8021X:
213962306a36Sopenharmony_ci			val = WPA2_AUTH_UNSPECIFIED;
214062306a36Sopenharmony_ci			if (sme->want_1x)
214162306a36Sopenharmony_ci				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
214262306a36Sopenharmony_ci			break;
214362306a36Sopenharmony_ci		case WLAN_AKM_SUITE_8021X_SHA256:
214462306a36Sopenharmony_ci			val = WPA2_AUTH_1X_SHA256;
214562306a36Sopenharmony_ci			if (sme->want_1x)
214662306a36Sopenharmony_ci				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
214762306a36Sopenharmony_ci			break;
214862306a36Sopenharmony_ci		case WLAN_AKM_SUITE_PSK_SHA256:
214962306a36Sopenharmony_ci			val = WPA2_AUTH_PSK_SHA256;
215062306a36Sopenharmony_ci			break;
215162306a36Sopenharmony_ci		case WLAN_AKM_SUITE_PSK:
215262306a36Sopenharmony_ci			val = WPA2_AUTH_PSK;
215362306a36Sopenharmony_ci			break;
215462306a36Sopenharmony_ci		case WLAN_AKM_SUITE_FT_8021X:
215562306a36Sopenharmony_ci			val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;
215662306a36Sopenharmony_ci			profile->is_ft = true;
215762306a36Sopenharmony_ci			if (sme->want_1x)
215862306a36Sopenharmony_ci				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
215962306a36Sopenharmony_ci			break;
216062306a36Sopenharmony_ci		case WLAN_AKM_SUITE_FT_PSK:
216162306a36Sopenharmony_ci			val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
216262306a36Sopenharmony_ci			profile->is_ft = true;
216362306a36Sopenharmony_ci			break;
216462306a36Sopenharmony_ci		default:
216562306a36Sopenharmony_ci			bphy_err(drvr, "invalid akm suite (%d)\n",
216662306a36Sopenharmony_ci				 sme->crypto.akm_suites[0]);
216762306a36Sopenharmony_ci			return -EINVAL;
216862306a36Sopenharmony_ci		}
216962306a36Sopenharmony_ci	} else if (val & WPA3_AUTH_SAE_PSK) {
217062306a36Sopenharmony_ci		switch (sme->crypto.akm_suites[0]) {
217162306a36Sopenharmony_ci		case WLAN_AKM_SUITE_SAE:
217262306a36Sopenharmony_ci			val = WPA3_AUTH_SAE_PSK;
217362306a36Sopenharmony_ci			if (sme->crypto.sae_pwd) {
217462306a36Sopenharmony_ci				brcmf_dbg(INFO, "using SAE offload\n");
217562306a36Sopenharmony_ci				profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
217662306a36Sopenharmony_ci			}
217762306a36Sopenharmony_ci			break;
217862306a36Sopenharmony_ci		case WLAN_AKM_SUITE_FT_OVER_SAE:
217962306a36Sopenharmony_ci			val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT;
218062306a36Sopenharmony_ci			profile->is_ft = true;
218162306a36Sopenharmony_ci			if (sme->crypto.sae_pwd) {
218262306a36Sopenharmony_ci				brcmf_dbg(INFO, "using SAE offload\n");
218362306a36Sopenharmony_ci				profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
218462306a36Sopenharmony_ci			}
218562306a36Sopenharmony_ci			break;
218662306a36Sopenharmony_ci		default:
218762306a36Sopenharmony_ci			bphy_err(drvr, "invalid akm suite (%d)\n",
218862306a36Sopenharmony_ci				 sme->crypto.akm_suites[0]);
218962306a36Sopenharmony_ci			return -EINVAL;
219062306a36Sopenharmony_ci		}
219162306a36Sopenharmony_ci	}
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
219462306a36Sopenharmony_ci		brcmf_dbg(INFO, "using 1X offload\n");
219562306a36Sopenharmony_ci
219662306a36Sopenharmony_ci	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
219762306a36Sopenharmony_ci		goto skip_mfp_config;
219862306a36Sopenharmony_ci	/* The MFP mode (1 or 2) needs to be determined, parse IEs. The
219962306a36Sopenharmony_ci	 * IE will not be verified, just a quick search for MFP config
220062306a36Sopenharmony_ci	 */
220162306a36Sopenharmony_ci	rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
220262306a36Sopenharmony_ci				  WLAN_EID_RSN);
220362306a36Sopenharmony_ci	if (!rsn_ie)
220462306a36Sopenharmony_ci		goto skip_mfp_config;
220562306a36Sopenharmony_ci	ie = (const u8 *)rsn_ie;
220662306a36Sopenharmony_ci	ie_len = rsn_ie->len + TLV_HDR_LEN;
220762306a36Sopenharmony_ci	/* Skip unicast suite */
220862306a36Sopenharmony_ci	offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
220962306a36Sopenharmony_ci	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
221062306a36Sopenharmony_ci		goto skip_mfp_config;
221162306a36Sopenharmony_ci	/* Skip multicast suite */
221262306a36Sopenharmony_ci	count = ie[offset] + (ie[offset + 1] << 8);
221362306a36Sopenharmony_ci	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
221462306a36Sopenharmony_ci	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
221562306a36Sopenharmony_ci		goto skip_mfp_config;
221662306a36Sopenharmony_ci	/* Skip auth key management suite(s) */
221762306a36Sopenharmony_ci	count = ie[offset] + (ie[offset + 1] << 8);
221862306a36Sopenharmony_ci	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
221962306a36Sopenharmony_ci	if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
222062306a36Sopenharmony_ci		goto skip_mfp_config;
222162306a36Sopenharmony_ci	/* Ready to read capabilities */
222262306a36Sopenharmony_ci	mfp = BRCMF_MFP_NONE;
222362306a36Sopenharmony_ci	rsn_cap = ie[offset] + (ie[offset + 1] << 8);
222462306a36Sopenharmony_ci	if (rsn_cap & RSN_CAP_MFPR_MASK)
222562306a36Sopenharmony_ci		mfp = BRCMF_MFP_REQUIRED;
222662306a36Sopenharmony_ci	else if (rsn_cap & RSN_CAP_MFPC_MASK)
222762306a36Sopenharmony_ci		mfp = BRCMF_MFP_CAPABLE;
222862306a36Sopenharmony_ci	brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ciskip_mfp_config:
223162306a36Sopenharmony_ci	brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
223262306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
223362306a36Sopenharmony_ci	if (err) {
223462306a36Sopenharmony_ci		bphy_err(drvr, "could not set wpa_auth (%d)\n", err);
223562306a36Sopenharmony_ci		return err;
223662306a36Sopenharmony_ci	}
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	return err;
223962306a36Sopenharmony_ci}
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_cistatic s32
224262306a36Sopenharmony_cibrcmf_set_sharedkey(struct net_device *ndev,
224362306a36Sopenharmony_ci		    struct cfg80211_connect_params *sme)
224462306a36Sopenharmony_ci{
224562306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
224662306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
224762306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
224862306a36Sopenharmony_ci	struct brcmf_cfg80211_security *sec;
224962306a36Sopenharmony_ci	struct brcmf_wsec_key key;
225062306a36Sopenharmony_ci	s32 val;
225162306a36Sopenharmony_ci	s32 err = 0;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	if (sme->key_len == 0)
225662306a36Sopenharmony_ci		return 0;
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	sec = &profile->sec;
225962306a36Sopenharmony_ci	brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
226062306a36Sopenharmony_ci		  sec->wpa_versions, sec->cipher_pairwise);
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |
226362306a36Sopenharmony_ci				 NL80211_WPA_VERSION_3))
226462306a36Sopenharmony_ci		return 0;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	if (!(sec->cipher_pairwise &
226762306a36Sopenharmony_ci	    (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
226862306a36Sopenharmony_ci		return 0;
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	memset(&key, 0, sizeof(key));
227162306a36Sopenharmony_ci	key.len = (u32) sme->key_len;
227262306a36Sopenharmony_ci	key.index = (u32) sme->key_idx;
227362306a36Sopenharmony_ci	if (key.len > sizeof(key.data)) {
227462306a36Sopenharmony_ci		bphy_err(drvr, "Too long key length (%u)\n", key.len);
227562306a36Sopenharmony_ci		return -EINVAL;
227662306a36Sopenharmony_ci	}
227762306a36Sopenharmony_ci	memcpy(key.data, sme->key, key.len);
227862306a36Sopenharmony_ci	key.flags = BRCMF_PRIMARY_KEY;
227962306a36Sopenharmony_ci	switch (sec->cipher_pairwise) {
228062306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
228162306a36Sopenharmony_ci		key.algo = CRYPTO_ALGO_WEP1;
228262306a36Sopenharmony_ci		break;
228362306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
228462306a36Sopenharmony_ci		key.algo = CRYPTO_ALGO_WEP128;
228562306a36Sopenharmony_ci		break;
228662306a36Sopenharmony_ci	default:
228762306a36Sopenharmony_ci		bphy_err(drvr, "Invalid algorithm (%d)\n",
228862306a36Sopenharmony_ci			 sme->crypto.ciphers_pairwise[0]);
228962306a36Sopenharmony_ci		return -EINVAL;
229062306a36Sopenharmony_ci	}
229162306a36Sopenharmony_ci	/* Set the new key/index */
229262306a36Sopenharmony_ci	brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
229362306a36Sopenharmony_ci		  key.len, key.index, key.algo);
229462306a36Sopenharmony_ci	brcmf_dbg(CONN, "key \"%s\"\n", key.data);
229562306a36Sopenharmony_ci	err = send_key_to_dongle(ifp, &key);
229662306a36Sopenharmony_ci	if (err)
229762306a36Sopenharmony_ci		return err;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
230062306a36Sopenharmony_ci		brcmf_dbg(CONN, "set auth_type to shared key\n");
230162306a36Sopenharmony_ci		val = WL_AUTH_SHARED_KEY;	/* shared key */
230262306a36Sopenharmony_ci		err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
230362306a36Sopenharmony_ci		if (err)
230462306a36Sopenharmony_ci			bphy_err(drvr, "set auth failed (%d)\n", err);
230562306a36Sopenharmony_ci	}
230662306a36Sopenharmony_ci	return err;
230762306a36Sopenharmony_ci}
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_cistatic
231062306a36Sopenharmony_cienum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
231162306a36Sopenharmony_ci					   enum nl80211_auth_type type)
231262306a36Sopenharmony_ci{
231362306a36Sopenharmony_ci	if (type == NL80211_AUTHTYPE_AUTOMATIC &&
231462306a36Sopenharmony_ci	    brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
231562306a36Sopenharmony_ci		brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
231662306a36Sopenharmony_ci		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
231762306a36Sopenharmony_ci	}
231862306a36Sopenharmony_ci	return type;
231962306a36Sopenharmony_ci}
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_cistatic void brcmf_set_join_pref(struct brcmf_if *ifp,
232262306a36Sopenharmony_ci				struct cfg80211_bss_selection *bss_select)
232362306a36Sopenharmony_ci{
232462306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
232562306a36Sopenharmony_ci	struct brcmf_join_pref_params join_pref_params[2];
232662306a36Sopenharmony_ci	enum nl80211_band band;
232762306a36Sopenharmony_ci	int err, i = 0;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	join_pref_params[i].len = 2;
233062306a36Sopenharmony_ci	join_pref_params[i].rssi_gain = 0;
233162306a36Sopenharmony_ci
233262306a36Sopenharmony_ci	if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)
233362306a36Sopenharmony_ci		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	switch (bss_select->behaviour) {
233662306a36Sopenharmony_ci	case __NL80211_BSS_SELECT_ATTR_INVALID:
233762306a36Sopenharmony_ci		brcmf_c_set_joinpref_default(ifp);
233862306a36Sopenharmony_ci		return;
233962306a36Sopenharmony_ci	case NL80211_BSS_SELECT_ATTR_BAND_PREF:
234062306a36Sopenharmony_ci		join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;
234162306a36Sopenharmony_ci		band = bss_select->param.band_pref;
234262306a36Sopenharmony_ci		join_pref_params[i].band = nl80211_band_to_fwil(band);
234362306a36Sopenharmony_ci		i++;
234462306a36Sopenharmony_ci		break;
234562306a36Sopenharmony_ci	case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:
234662306a36Sopenharmony_ci		join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;
234762306a36Sopenharmony_ci		band = bss_select->param.adjust.band;
234862306a36Sopenharmony_ci		join_pref_params[i].band = nl80211_band_to_fwil(band);
234962306a36Sopenharmony_ci		join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;
235062306a36Sopenharmony_ci		i++;
235162306a36Sopenharmony_ci		break;
235262306a36Sopenharmony_ci	case NL80211_BSS_SELECT_ATTR_RSSI:
235362306a36Sopenharmony_ci	default:
235462306a36Sopenharmony_ci		break;
235562306a36Sopenharmony_ci	}
235662306a36Sopenharmony_ci	join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;
235762306a36Sopenharmony_ci	join_pref_params[i].len = 2;
235862306a36Sopenharmony_ci	join_pref_params[i].rssi_gain = 0;
235962306a36Sopenharmony_ci	join_pref_params[i].band = 0;
236062306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
236162306a36Sopenharmony_ci				       sizeof(join_pref_params));
236262306a36Sopenharmony_ci	if (err)
236362306a36Sopenharmony_ci		bphy_err(drvr, "Set join_pref error (%d)\n", err);
236462306a36Sopenharmony_ci}
236562306a36Sopenharmony_ci
236662306a36Sopenharmony_cistatic s32
236762306a36Sopenharmony_cibrcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
236862306a36Sopenharmony_ci		       struct cfg80211_connect_params *sme)
236962306a36Sopenharmony_ci{
237062306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
237162306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
237262306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
237362306a36Sopenharmony_ci	struct ieee80211_channel *chan = sme->channel;
237462306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
237562306a36Sopenharmony_ci	struct brcmf_join_params join_params;
237662306a36Sopenharmony_ci	size_t join_params_size;
237762306a36Sopenharmony_ci	const struct brcmf_tlv *rsn_ie;
237862306a36Sopenharmony_ci	const struct brcmf_vs_tlv *wpa_ie;
237962306a36Sopenharmony_ci	const void *ie;
238062306a36Sopenharmony_ci	u32 ie_len;
238162306a36Sopenharmony_ci	struct brcmf_ext_join_params_le *ext_join_params;
238262306a36Sopenharmony_ci	u16 chanspec;
238362306a36Sopenharmony_ci	s32 err = 0;
238462306a36Sopenharmony_ci	u32 ssid_len;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
238762306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
238862306a36Sopenharmony_ci		return -EIO;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	if (!sme->ssid) {
239162306a36Sopenharmony_ci		bphy_err(drvr, "Invalid ssid\n");
239262306a36Sopenharmony_ci		return -EOPNOTSUPP;
239362306a36Sopenharmony_ci	}
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	if (sme->channel_hint)
239662306a36Sopenharmony_ci		chan = sme->channel_hint;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	if (sme->bssid_hint)
239962306a36Sopenharmony_ci		sme->bssid = sme->bssid_hint;
240062306a36Sopenharmony_ci
240162306a36Sopenharmony_ci	if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
240262306a36Sopenharmony_ci		/* A normal (non P2P) connection request setup. */
240362306a36Sopenharmony_ci		ie = NULL;
240462306a36Sopenharmony_ci		ie_len = 0;
240562306a36Sopenharmony_ci		/* find the WPA_IE */
240662306a36Sopenharmony_ci		wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
240762306a36Sopenharmony_ci		if (wpa_ie) {
240862306a36Sopenharmony_ci			ie = wpa_ie;
240962306a36Sopenharmony_ci			ie_len = wpa_ie->len + TLV_HDR_LEN;
241062306a36Sopenharmony_ci		} else {
241162306a36Sopenharmony_ci			/* find the RSN_IE */
241262306a36Sopenharmony_ci			rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
241362306a36Sopenharmony_ci						  sme->ie_len,
241462306a36Sopenharmony_ci						  WLAN_EID_RSN);
241562306a36Sopenharmony_ci			if (rsn_ie) {
241662306a36Sopenharmony_ci				ie = rsn_ie;
241762306a36Sopenharmony_ci				ie_len = rsn_ie->len + TLV_HDR_LEN;
241862306a36Sopenharmony_ci			}
241962306a36Sopenharmony_ci		}
242062306a36Sopenharmony_ci		brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
242162306a36Sopenharmony_ci	}
242262306a36Sopenharmony_ci
242362306a36Sopenharmony_ci	err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
242462306a36Sopenharmony_ci				    sme->ie, sme->ie_len);
242562306a36Sopenharmony_ci	if (err)
242662306a36Sopenharmony_ci		bphy_err(drvr, "Set Assoc REQ IE Failed\n");
242762306a36Sopenharmony_ci	else
242862306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	if (chan) {
243362306a36Sopenharmony_ci		cfg->channel =
243462306a36Sopenharmony_ci			ieee80211_frequency_to_channel(chan->center_freq);
243562306a36Sopenharmony_ci		chanspec = channel_to_chanspec(&cfg->d11inf, chan);
243662306a36Sopenharmony_ci		brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
243762306a36Sopenharmony_ci			  cfg->channel, chan->center_freq, chanspec);
243862306a36Sopenharmony_ci	} else {
243962306a36Sopenharmony_ci		cfg->channel = 0;
244062306a36Sopenharmony_ci		chanspec = 0;
244162306a36Sopenharmony_ci	}
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	err = brcmf_set_wpa_version(ndev, sme);
244662306a36Sopenharmony_ci	if (err) {
244762306a36Sopenharmony_ci		bphy_err(drvr, "wl_set_wpa_version failed (%d)\n", err);
244862306a36Sopenharmony_ci		goto done;
244962306a36Sopenharmony_ci	}
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
245262306a36Sopenharmony_ci	err = brcmf_set_auth_type(ndev, sme);
245362306a36Sopenharmony_ci	if (err) {
245462306a36Sopenharmony_ci		bphy_err(drvr, "wl_set_auth_type failed (%d)\n", err);
245562306a36Sopenharmony_ci		goto done;
245662306a36Sopenharmony_ci	}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	err = brcmf_set_wsec_mode(ndev, sme);
245962306a36Sopenharmony_ci	if (err) {
246062306a36Sopenharmony_ci		bphy_err(drvr, "wl_set_set_cipher failed (%d)\n", err);
246162306a36Sopenharmony_ci		goto done;
246262306a36Sopenharmony_ci	}
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	err = brcmf_set_key_mgmt(ndev, sme);
246562306a36Sopenharmony_ci	if (err) {
246662306a36Sopenharmony_ci		bphy_err(drvr, "wl_set_key_mgmt failed (%d)\n", err);
246762306a36Sopenharmony_ci		goto done;
246862306a36Sopenharmony_ci	}
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci	err = brcmf_set_sharedkey(ndev, sme);
247162306a36Sopenharmony_ci	if (err) {
247262306a36Sopenharmony_ci		bphy_err(drvr, "brcmf_set_sharedkey failed (%d)\n", err);
247362306a36Sopenharmony_ci		goto done;
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	if (sme->crypto.psk &&
247762306a36Sopenharmony_ci	    profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
247862306a36Sopenharmony_ci		if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
247962306a36Sopenharmony_ci			err = -EINVAL;
248062306a36Sopenharmony_ci			goto done;
248162306a36Sopenharmony_ci		}
248262306a36Sopenharmony_ci		brcmf_dbg(INFO, "using PSK offload\n");
248362306a36Sopenharmony_ci		profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
248462306a36Sopenharmony_ci	}
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
248762306a36Sopenharmony_ci		/* enable firmware supplicant for this interface */
248862306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1);
248962306a36Sopenharmony_ci		if (err < 0) {
249062306a36Sopenharmony_ci			bphy_err(drvr, "failed to enable fw supplicant\n");
249162306a36Sopenharmony_ci			goto done;
249262306a36Sopenharmony_ci		}
249362306a36Sopenharmony_ci	}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)
249662306a36Sopenharmony_ci		err = brcmf_set_pmk(ifp, sme->crypto.psk,
249762306a36Sopenharmony_ci				    BRCMF_WSEC_MAX_PSK_LEN);
249862306a36Sopenharmony_ci	else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {
249962306a36Sopenharmony_ci		/* clean up user-space RSNE */
250062306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0);
250162306a36Sopenharmony_ci		if (err) {
250262306a36Sopenharmony_ci			bphy_err(drvr, "failed to clean up user-space RSNE\n");
250362306a36Sopenharmony_ci			goto done;
250462306a36Sopenharmony_ci		}
250562306a36Sopenharmony_ci		err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd,
250662306a36Sopenharmony_ci					     sme->crypto.sae_pwd_len);
250762306a36Sopenharmony_ci		if (!err && sme->crypto.psk)
250862306a36Sopenharmony_ci			err = brcmf_set_pmk(ifp, sme->crypto.psk,
250962306a36Sopenharmony_ci					    BRCMF_WSEC_MAX_PSK_LEN);
251062306a36Sopenharmony_ci	}
251162306a36Sopenharmony_ci	if (err)
251262306a36Sopenharmony_ci		goto done;
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci	/* Join with specific BSSID and cached SSID
251562306a36Sopenharmony_ci	 * If SSID is zero join based on BSSID only
251662306a36Sopenharmony_ci	 */
251762306a36Sopenharmony_ci	join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
251862306a36Sopenharmony_ci		offsetof(struct brcmf_assoc_params_le, chanspec_list);
251962306a36Sopenharmony_ci	if (cfg->channel)
252062306a36Sopenharmony_ci		join_params_size += sizeof(u16);
252162306a36Sopenharmony_ci	ext_join_params = kzalloc(sizeof(*ext_join_params), GFP_KERNEL);
252262306a36Sopenharmony_ci	if (ext_join_params == NULL) {
252362306a36Sopenharmony_ci		err = -ENOMEM;
252462306a36Sopenharmony_ci		goto done;
252562306a36Sopenharmony_ci	}
252662306a36Sopenharmony_ci	ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
252762306a36Sopenharmony_ci	ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
252862306a36Sopenharmony_ci	memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
252962306a36Sopenharmony_ci	if (ssid_len < IEEE80211_MAX_SSID_LEN)
253062306a36Sopenharmony_ci		brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
253162306a36Sopenharmony_ci			  ext_join_params->ssid_le.SSID, ssid_len);
253262306a36Sopenharmony_ci
253362306a36Sopenharmony_ci	/* Set up join scan parameters */
253462306a36Sopenharmony_ci	ext_join_params->scan_le.scan_type = -1;
253562306a36Sopenharmony_ci	ext_join_params->scan_le.home_time = cpu_to_le32(-1);
253662306a36Sopenharmony_ci
253762306a36Sopenharmony_ci	if (sme->bssid)
253862306a36Sopenharmony_ci		memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
253962306a36Sopenharmony_ci	else
254062306a36Sopenharmony_ci		eth_broadcast_addr(ext_join_params->assoc_le.bssid);
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_ci	if (cfg->channel) {
254362306a36Sopenharmony_ci		ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
254462306a36Sopenharmony_ci
254562306a36Sopenharmony_ci		ext_join_params->assoc_le.chanspec_list[0] =
254662306a36Sopenharmony_ci			cpu_to_le16(chanspec);
254762306a36Sopenharmony_ci		/* Increase dwell time to receive probe response or detect
254862306a36Sopenharmony_ci		 * beacon from target AP at a noisy air only during connect
254962306a36Sopenharmony_ci		 * command.
255062306a36Sopenharmony_ci		 */
255162306a36Sopenharmony_ci		ext_join_params->scan_le.active_time =
255262306a36Sopenharmony_ci			cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
255362306a36Sopenharmony_ci		ext_join_params->scan_le.passive_time =
255462306a36Sopenharmony_ci			cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
255562306a36Sopenharmony_ci		/* To sync with presence period of VSDB GO send probe request
255662306a36Sopenharmony_ci		 * more frequently. Probe request will be stopped when it gets
255762306a36Sopenharmony_ci		 * probe response from target AP/GO.
255862306a36Sopenharmony_ci		 */
255962306a36Sopenharmony_ci		ext_join_params->scan_le.nprobes =
256062306a36Sopenharmony_ci			cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
256162306a36Sopenharmony_ci				    BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
256262306a36Sopenharmony_ci	} else {
256362306a36Sopenharmony_ci		ext_join_params->scan_le.active_time = cpu_to_le32(-1);
256462306a36Sopenharmony_ci		ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
256562306a36Sopenharmony_ci		ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
256662306a36Sopenharmony_ci	}
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	brcmf_set_join_pref(ifp, &sme->bss_select);
256962306a36Sopenharmony_ci
257062306a36Sopenharmony_ci	err  = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
257162306a36Sopenharmony_ci					 join_params_size);
257262306a36Sopenharmony_ci	kfree(ext_join_params);
257362306a36Sopenharmony_ci	if (!err)
257462306a36Sopenharmony_ci		/* This is it. join command worked, we are done */
257562306a36Sopenharmony_ci		goto done;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	/* join command failed, fallback to set ssid */
257862306a36Sopenharmony_ci	memset(&join_params, 0, sizeof(join_params));
257962306a36Sopenharmony_ci	join_params_size = sizeof(join_params.ssid_le);
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
258262306a36Sopenharmony_ci	join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
258362306a36Sopenharmony_ci
258462306a36Sopenharmony_ci	if (sme->bssid)
258562306a36Sopenharmony_ci		memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
258662306a36Sopenharmony_ci	else
258762306a36Sopenharmony_ci		eth_broadcast_addr(join_params.params_le.bssid);
258862306a36Sopenharmony_ci
258962306a36Sopenharmony_ci	if (cfg->channel) {
259062306a36Sopenharmony_ci		join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
259162306a36Sopenharmony_ci		join_params.params_le.chanspec_num = cpu_to_le32(1);
259262306a36Sopenharmony_ci		join_params_size += sizeof(join_params.params_le);
259362306a36Sopenharmony_ci	}
259462306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
259562306a36Sopenharmony_ci				     &join_params, join_params_size);
259662306a36Sopenharmony_ci	if (err)
259762306a36Sopenharmony_ci		bphy_err(drvr, "BRCMF_C_SET_SSID failed (%d)\n", err);
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_cidone:
260062306a36Sopenharmony_ci	if (err)
260162306a36Sopenharmony_ci		clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
260262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
260362306a36Sopenharmony_ci	return err;
260462306a36Sopenharmony_ci}
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_cistatic s32
260762306a36Sopenharmony_cibrcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
260862306a36Sopenharmony_ci		       u16 reason_code)
260962306a36Sopenharmony_ci{
261062306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
261162306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
261262306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
261362306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
261462306a36Sopenharmony_ci	struct brcmf_scb_val_le scbval;
261562306a36Sopenharmony_ci	s32 err = 0;
261662306a36Sopenharmony_ci
261762306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
261862306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
261962306a36Sopenharmony_ci		return -EIO;
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
262262306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
262362306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state);
262462306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state);
262562306a36Sopenharmony_ci	cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ci	memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
262862306a36Sopenharmony_ci	scbval.val = cpu_to_le32(reason_code);
262962306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
263062306a36Sopenharmony_ci				     &scbval, sizeof(scbval));
263162306a36Sopenharmony_ci	if (err)
263262306a36Sopenharmony_ci		bphy_err(drvr, "error (%d)\n", err);
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
263562306a36Sopenharmony_ci	return err;
263662306a36Sopenharmony_ci}
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_cistatic s32
263962306a36Sopenharmony_cibrcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
264062306a36Sopenharmony_ci			    enum nl80211_tx_power_setting type, s32 mbm)
264162306a36Sopenharmony_ci{
264262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
264362306a36Sopenharmony_ci	struct net_device *ndev = cfg_to_ndev(cfg);
264462306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
264562306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
264662306a36Sopenharmony_ci	s32 err;
264762306a36Sopenharmony_ci	s32 disable;
264862306a36Sopenharmony_ci	u32 qdbm = 127;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
265162306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
265262306a36Sopenharmony_ci		return -EIO;
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci	switch (type) {
265562306a36Sopenharmony_ci	case NL80211_TX_POWER_AUTOMATIC:
265662306a36Sopenharmony_ci		break;
265762306a36Sopenharmony_ci	case NL80211_TX_POWER_LIMITED:
265862306a36Sopenharmony_ci	case NL80211_TX_POWER_FIXED:
265962306a36Sopenharmony_ci		if (mbm < 0) {
266062306a36Sopenharmony_ci			bphy_err(drvr, "TX_POWER_FIXED - dbm is negative\n");
266162306a36Sopenharmony_ci			err = -EINVAL;
266262306a36Sopenharmony_ci			goto done;
266362306a36Sopenharmony_ci		}
266462306a36Sopenharmony_ci		qdbm =  MBM_TO_DBM(4 * mbm);
266562306a36Sopenharmony_ci		if (qdbm > 127)
266662306a36Sopenharmony_ci			qdbm = 127;
266762306a36Sopenharmony_ci		qdbm |= WL_TXPWR_OVERRIDE;
266862306a36Sopenharmony_ci		break;
266962306a36Sopenharmony_ci	default:
267062306a36Sopenharmony_ci		bphy_err(drvr, "Unsupported type %d\n", type);
267162306a36Sopenharmony_ci		err = -EINVAL;
267262306a36Sopenharmony_ci		goto done;
267362306a36Sopenharmony_ci	}
267462306a36Sopenharmony_ci	/* Make sure radio is off or on as far as software is concerned */
267562306a36Sopenharmony_ci	disable = WL_RADIO_SW_DISABLE << 16;
267662306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
267762306a36Sopenharmony_ci	if (err)
267862306a36Sopenharmony_ci		bphy_err(drvr, "WLC_SET_RADIO error (%d)\n", err);
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
268162306a36Sopenharmony_ci	if (err)
268262306a36Sopenharmony_ci		bphy_err(drvr, "qtxpower error (%d)\n", err);
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_cidone:
268562306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
268662306a36Sopenharmony_ci	return err;
268762306a36Sopenharmony_ci}
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_cistatic s32
269062306a36Sopenharmony_cibrcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
269162306a36Sopenharmony_ci			    s32 *dbm)
269262306a36Sopenharmony_ci{
269362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
269462306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif = wdev_to_vif(wdev);
269562306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
269662306a36Sopenharmony_ci	s32 qdbm = 0;
269762306a36Sopenharmony_ci	s32 err;
269862306a36Sopenharmony_ci
269962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
270062306a36Sopenharmony_ci	if (!check_vif_up(vif))
270162306a36Sopenharmony_ci		return -EIO;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_get(vif->ifp, "qtxpower", &qdbm);
270462306a36Sopenharmony_ci	if (err) {
270562306a36Sopenharmony_ci		bphy_err(drvr, "error (%d)\n", err);
270662306a36Sopenharmony_ci		goto done;
270762306a36Sopenharmony_ci	}
270862306a36Sopenharmony_ci	*dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_cidone:
271162306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
271262306a36Sopenharmony_ci	return err;
271362306a36Sopenharmony_ci}
271462306a36Sopenharmony_ci
271562306a36Sopenharmony_cistatic s32
271662306a36Sopenharmony_cibrcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
271762306a36Sopenharmony_ci				  int link_id, u8 key_idx, bool unicast,
271862306a36Sopenharmony_ci				  bool multicast)
271962306a36Sopenharmony_ci{
272062306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
272162306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
272262306a36Sopenharmony_ci	u32 index;
272362306a36Sopenharmony_ci	u32 wsec;
272462306a36Sopenharmony_ci	s32 err = 0;
272562306a36Sopenharmony_ci
272662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
272762306a36Sopenharmony_ci	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
272862306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
272962306a36Sopenharmony_ci		return -EIO;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
273262306a36Sopenharmony_ci	if (err) {
273362306a36Sopenharmony_ci		bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
273462306a36Sopenharmony_ci		goto done;
273562306a36Sopenharmony_ci	}
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	if (wsec & WEP_ENABLED) {
273862306a36Sopenharmony_ci		/* Just select a new current key */
273962306a36Sopenharmony_ci		index = key_idx;
274062306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp,
274162306a36Sopenharmony_ci					    BRCMF_C_SET_KEY_PRIMARY, index);
274262306a36Sopenharmony_ci		if (err)
274362306a36Sopenharmony_ci			bphy_err(drvr, "error (%d)\n", err);
274462306a36Sopenharmony_ci	}
274562306a36Sopenharmony_cidone:
274662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
274762306a36Sopenharmony_ci	return err;
274862306a36Sopenharmony_ci}
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_cistatic s32
275162306a36Sopenharmony_cibrcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
275262306a36Sopenharmony_ci		       int link_id, u8 key_idx, bool pairwise,
275362306a36Sopenharmony_ci		       const u8 *mac_addr)
275462306a36Sopenharmony_ci{
275562306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
275662306a36Sopenharmony_ci	struct brcmf_wsec_key *key;
275762306a36Sopenharmony_ci	s32 err;
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
276062306a36Sopenharmony_ci	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
276362306a36Sopenharmony_ci		return -EIO;
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
276662306a36Sopenharmony_ci		/* we ignore this key index in this case */
276762306a36Sopenharmony_ci		return -EINVAL;
276862306a36Sopenharmony_ci	}
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	key = &ifp->vif->profile.key[key_idx];
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	if (key->algo == CRYPTO_ALGO_OFF) {
277362306a36Sopenharmony_ci		brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
277462306a36Sopenharmony_ci		return -EINVAL;
277562306a36Sopenharmony_ci	}
277662306a36Sopenharmony_ci
277762306a36Sopenharmony_ci	memset(key, 0, sizeof(*key));
277862306a36Sopenharmony_ci	key->index = (u32)key_idx;
277962306a36Sopenharmony_ci	key->flags = BRCMF_PRIMARY_KEY;
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_ci	/* Clear the key/index */
278262306a36Sopenharmony_ci	err = send_key_to_dongle(ifp, key);
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
278562306a36Sopenharmony_ci	return err;
278662306a36Sopenharmony_ci}
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_cistatic s32
278962306a36Sopenharmony_cibrcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
279062306a36Sopenharmony_ci		       int link_id, u8 key_idx, bool pairwise,
279162306a36Sopenharmony_ci		       const u8 *mac_addr, struct key_params *params)
279262306a36Sopenharmony_ci{
279362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
279462306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
279562306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
279662306a36Sopenharmony_ci	struct brcmf_wsec_key *key;
279762306a36Sopenharmony_ci	s32 val;
279862306a36Sopenharmony_ci	s32 wsec;
279962306a36Sopenharmony_ci	s32 err;
280062306a36Sopenharmony_ci	u8 keybuf[8];
280162306a36Sopenharmony_ci	bool ext_key;
280262306a36Sopenharmony_ci
280362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
280462306a36Sopenharmony_ci	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
280562306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
280662306a36Sopenharmony_ci		return -EIO;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
280962306a36Sopenharmony_ci		/* we ignore this key index in this case */
281062306a36Sopenharmony_ci		bphy_err(drvr, "invalid key index (%d)\n", key_idx);
281162306a36Sopenharmony_ci		return -EINVAL;
281262306a36Sopenharmony_ci	}
281362306a36Sopenharmony_ci
281462306a36Sopenharmony_ci	if (params->key_len == 0)
281562306a36Sopenharmony_ci		return brcmf_cfg80211_del_key(wiphy, ndev, -1, key_idx,
281662306a36Sopenharmony_ci					      pairwise, mac_addr);
281762306a36Sopenharmony_ci
281862306a36Sopenharmony_ci	if (params->key_len > sizeof(key->data)) {
281962306a36Sopenharmony_ci		bphy_err(drvr, "Too long key length (%u)\n", params->key_len);
282062306a36Sopenharmony_ci		return -EINVAL;
282162306a36Sopenharmony_ci	}
282262306a36Sopenharmony_ci
282362306a36Sopenharmony_ci	ext_key = false;
282462306a36Sopenharmony_ci	if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
282562306a36Sopenharmony_ci	    (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
282662306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
282762306a36Sopenharmony_ci		ext_key = true;
282862306a36Sopenharmony_ci	}
282962306a36Sopenharmony_ci
283062306a36Sopenharmony_ci	key = &ifp->vif->profile.key[key_idx];
283162306a36Sopenharmony_ci	memset(key, 0, sizeof(*key));
283262306a36Sopenharmony_ci	if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
283362306a36Sopenharmony_ci		memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
283462306a36Sopenharmony_ci	key->len = params->key_len;
283562306a36Sopenharmony_ci	key->index = key_idx;
283662306a36Sopenharmony_ci	memcpy(key->data, params->key, key->len);
283762306a36Sopenharmony_ci	if (!ext_key)
283862306a36Sopenharmony_ci		key->flags = BRCMF_PRIMARY_KEY;
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	if (params->seq && params->seq_len == 6) {
284162306a36Sopenharmony_ci		/* rx iv */
284262306a36Sopenharmony_ci		u8 *ivptr;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci		ivptr = (u8 *)params->seq;
284562306a36Sopenharmony_ci		key->rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
284662306a36Sopenharmony_ci			(ivptr[3] << 8) | ivptr[2];
284762306a36Sopenharmony_ci		key->rxiv.lo = (ivptr[1] << 8) | ivptr[0];
284862306a36Sopenharmony_ci		key->iv_initialized = true;
284962306a36Sopenharmony_ci	}
285062306a36Sopenharmony_ci
285162306a36Sopenharmony_ci	switch (params->cipher) {
285262306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP40:
285362306a36Sopenharmony_ci		key->algo = CRYPTO_ALGO_WEP1;
285462306a36Sopenharmony_ci		val = WEP_ENABLED;
285562306a36Sopenharmony_ci		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
285662306a36Sopenharmony_ci		break;
285762306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_WEP104:
285862306a36Sopenharmony_ci		key->algo = CRYPTO_ALGO_WEP128;
285962306a36Sopenharmony_ci		val = WEP_ENABLED;
286062306a36Sopenharmony_ci		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
286162306a36Sopenharmony_ci		break;
286262306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_TKIP:
286362306a36Sopenharmony_ci		if (!brcmf_is_apmode(ifp->vif)) {
286462306a36Sopenharmony_ci			brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
286562306a36Sopenharmony_ci			memcpy(keybuf, &key->data[24], sizeof(keybuf));
286662306a36Sopenharmony_ci			memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
286762306a36Sopenharmony_ci			memcpy(&key->data[16], keybuf, sizeof(keybuf));
286862306a36Sopenharmony_ci		}
286962306a36Sopenharmony_ci		key->algo = CRYPTO_ALGO_TKIP;
287062306a36Sopenharmony_ci		val = TKIP_ENABLED;
287162306a36Sopenharmony_ci		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
287262306a36Sopenharmony_ci		break;
287362306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_AES_CMAC:
287462306a36Sopenharmony_ci		key->algo = CRYPTO_ALGO_AES_CCM;
287562306a36Sopenharmony_ci		val = AES_ENABLED;
287662306a36Sopenharmony_ci		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
287762306a36Sopenharmony_ci		break;
287862306a36Sopenharmony_ci	case WLAN_CIPHER_SUITE_CCMP:
287962306a36Sopenharmony_ci		key->algo = CRYPTO_ALGO_AES_CCM;
288062306a36Sopenharmony_ci		val = AES_ENABLED;
288162306a36Sopenharmony_ci		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
288262306a36Sopenharmony_ci		break;
288362306a36Sopenharmony_ci	default:
288462306a36Sopenharmony_ci		bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher);
288562306a36Sopenharmony_ci		err = -EINVAL;
288662306a36Sopenharmony_ci		goto done;
288762306a36Sopenharmony_ci	}
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci	err = send_key_to_dongle(ifp, key);
289062306a36Sopenharmony_ci	if (ext_key || err)
289162306a36Sopenharmony_ci		goto done;
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
289462306a36Sopenharmony_ci	if (err) {
289562306a36Sopenharmony_ci		bphy_err(drvr, "get wsec error (%d)\n", err);
289662306a36Sopenharmony_ci		goto done;
289762306a36Sopenharmony_ci	}
289862306a36Sopenharmony_ci	wsec |= val;
289962306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
290062306a36Sopenharmony_ci	if (err) {
290162306a36Sopenharmony_ci		bphy_err(drvr, "set wsec error (%d)\n", err);
290262306a36Sopenharmony_ci		goto done;
290362306a36Sopenharmony_ci	}
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_cidone:
290662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
290762306a36Sopenharmony_ci	return err;
290862306a36Sopenharmony_ci}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_cistatic s32
291162306a36Sopenharmony_cibrcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
291262306a36Sopenharmony_ci		       int link_id, u8 key_idx, bool pairwise,
291362306a36Sopenharmony_ci		       const u8 *mac_addr, void *cookie,
291462306a36Sopenharmony_ci		       void (*callback)(void *cookie,
291562306a36Sopenharmony_ci					struct key_params *params))
291662306a36Sopenharmony_ci{
291762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
291862306a36Sopenharmony_ci	struct key_params params;
291962306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
292062306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
292162306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
292262306a36Sopenharmony_ci	struct brcmf_cfg80211_security *sec;
292362306a36Sopenharmony_ci	s32 wsec;
292462306a36Sopenharmony_ci	s32 err = 0;
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
292762306a36Sopenharmony_ci	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
292862306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
292962306a36Sopenharmony_ci		return -EIO;
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci	memset(&params, 0, sizeof(params));
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
293462306a36Sopenharmony_ci	if (err) {
293562306a36Sopenharmony_ci		bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
293662306a36Sopenharmony_ci		/* Ignore this error, may happen during DISASSOC */
293762306a36Sopenharmony_ci		err = -EAGAIN;
293862306a36Sopenharmony_ci		goto done;
293962306a36Sopenharmony_ci	}
294062306a36Sopenharmony_ci	if (wsec & WEP_ENABLED) {
294162306a36Sopenharmony_ci		sec = &profile->sec;
294262306a36Sopenharmony_ci		if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
294362306a36Sopenharmony_ci			params.cipher = WLAN_CIPHER_SUITE_WEP40;
294462306a36Sopenharmony_ci			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
294562306a36Sopenharmony_ci		} else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
294662306a36Sopenharmony_ci			params.cipher = WLAN_CIPHER_SUITE_WEP104;
294762306a36Sopenharmony_ci			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
294862306a36Sopenharmony_ci		}
294962306a36Sopenharmony_ci	} else if (wsec & TKIP_ENABLED) {
295062306a36Sopenharmony_ci		params.cipher = WLAN_CIPHER_SUITE_TKIP;
295162306a36Sopenharmony_ci		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
295262306a36Sopenharmony_ci	} else if (wsec & AES_ENABLED) {
295362306a36Sopenharmony_ci		params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
295462306a36Sopenharmony_ci		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
295562306a36Sopenharmony_ci	} else  {
295662306a36Sopenharmony_ci		bphy_err(drvr, "Invalid algo (0x%x)\n", wsec);
295762306a36Sopenharmony_ci		err = -EINVAL;
295862306a36Sopenharmony_ci		goto done;
295962306a36Sopenharmony_ci	}
296062306a36Sopenharmony_ci	callback(cookie, &params);
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_cidone:
296362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
296462306a36Sopenharmony_ci	return err;
296562306a36Sopenharmony_ci}
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_cistatic s32
296862306a36Sopenharmony_cibrcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
296962306a36Sopenharmony_ci				       struct net_device *ndev, int link_id,
297062306a36Sopenharmony_ci				       u8 key_idx)
297162306a36Sopenharmony_ci{
297262306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
297562306a36Sopenharmony_ci
297662306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
297762306a36Sopenharmony_ci		return 0;
297862306a36Sopenharmony_ci
297962306a36Sopenharmony_ci	brcmf_dbg(INFO, "Not supported\n");
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	return -EOPNOTSUPP;
298262306a36Sopenharmony_ci}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_cistatic void
298562306a36Sopenharmony_cibrcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
298662306a36Sopenharmony_ci{
298762306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
298862306a36Sopenharmony_ci	s32 err;
298962306a36Sopenharmony_ci	u8 key_idx;
299062306a36Sopenharmony_ci	struct brcmf_wsec_key *key;
299162306a36Sopenharmony_ci	s32 wsec;
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci	for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
299462306a36Sopenharmony_ci		key = &ifp->vif->profile.key[key_idx];
299562306a36Sopenharmony_ci		if ((key->algo == CRYPTO_ALGO_WEP1) ||
299662306a36Sopenharmony_ci		    (key->algo == CRYPTO_ALGO_WEP128))
299762306a36Sopenharmony_ci			break;
299862306a36Sopenharmony_ci	}
299962306a36Sopenharmony_ci	if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
300062306a36Sopenharmony_ci		return;
300162306a36Sopenharmony_ci
300262306a36Sopenharmony_ci	err = send_key_to_dongle(ifp, key);
300362306a36Sopenharmony_ci	if (err) {
300462306a36Sopenharmony_ci		bphy_err(drvr, "Setting WEP key failed (%d)\n", err);
300562306a36Sopenharmony_ci		return;
300662306a36Sopenharmony_ci	}
300762306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
300862306a36Sopenharmony_ci	if (err) {
300962306a36Sopenharmony_ci		bphy_err(drvr, "get wsec error (%d)\n", err);
301062306a36Sopenharmony_ci		return;
301162306a36Sopenharmony_ci	}
301262306a36Sopenharmony_ci	wsec |= WEP_ENABLED;
301362306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
301462306a36Sopenharmony_ci	if (err)
301562306a36Sopenharmony_ci		bphy_err(drvr, "set wsec error (%d)\n", err);
301662306a36Sopenharmony_ci}
301762306a36Sopenharmony_ci
301862306a36Sopenharmony_cistatic void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
301962306a36Sopenharmony_ci{
302062306a36Sopenharmony_ci	struct nl80211_sta_flag_update *sfu;
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
302362306a36Sopenharmony_ci	si->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);
302462306a36Sopenharmony_ci	sfu = &si->sta_flags;
302562306a36Sopenharmony_ci	sfu->mask = BIT(NL80211_STA_FLAG_WME) |
302662306a36Sopenharmony_ci		    BIT(NL80211_STA_FLAG_AUTHENTICATED) |
302762306a36Sopenharmony_ci		    BIT(NL80211_STA_FLAG_ASSOCIATED) |
302862306a36Sopenharmony_ci		    BIT(NL80211_STA_FLAG_AUTHORIZED);
302962306a36Sopenharmony_ci	if (fw_sta_flags & BRCMF_STA_WME)
303062306a36Sopenharmony_ci		sfu->set |= BIT(NL80211_STA_FLAG_WME);
303162306a36Sopenharmony_ci	if (fw_sta_flags & BRCMF_STA_AUTHE)
303262306a36Sopenharmony_ci		sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
303362306a36Sopenharmony_ci	if (fw_sta_flags & BRCMF_STA_ASSOC)
303462306a36Sopenharmony_ci		sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
303562306a36Sopenharmony_ci	if (fw_sta_flags & BRCMF_STA_AUTHO)
303662306a36Sopenharmony_ci		sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
303762306a36Sopenharmony_ci}
303862306a36Sopenharmony_ci
303962306a36Sopenharmony_cistatic void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
304062306a36Sopenharmony_ci{
304162306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
304262306a36Sopenharmony_ci	struct {
304362306a36Sopenharmony_ci		__le32 len;
304462306a36Sopenharmony_ci		struct brcmf_bss_info_le bss_le;
304562306a36Sopenharmony_ci	} *buf;
304662306a36Sopenharmony_ci	u16 capability;
304762306a36Sopenharmony_ci	int err;
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_ci	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
305062306a36Sopenharmony_ci	if (!buf)
305162306a36Sopenharmony_ci		return;
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci	buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
305462306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
305562306a36Sopenharmony_ci				     WL_BSS_INFO_MAX);
305662306a36Sopenharmony_ci	if (err) {
305762306a36Sopenharmony_ci		bphy_err(drvr, "Failed to get bss info (%d)\n", err);
305862306a36Sopenharmony_ci		goto out_kfree;
305962306a36Sopenharmony_ci	}
306062306a36Sopenharmony_ci	si->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);
306162306a36Sopenharmony_ci	si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
306262306a36Sopenharmony_ci	si->bss_param.dtim_period = buf->bss_le.dtim_period;
306362306a36Sopenharmony_ci	capability = le16_to_cpu(buf->bss_le.capability);
306462306a36Sopenharmony_ci	if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
306562306a36Sopenharmony_ci		si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
306662306a36Sopenharmony_ci	if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
306762306a36Sopenharmony_ci		si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
306862306a36Sopenharmony_ci	if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
306962306a36Sopenharmony_ci		si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ciout_kfree:
307262306a36Sopenharmony_ci	kfree(buf);
307362306a36Sopenharmony_ci}
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_cistatic s32
307662306a36Sopenharmony_cibrcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
307762306a36Sopenharmony_ci				struct station_info *sinfo)
307862306a36Sopenharmony_ci{
307962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
308062306a36Sopenharmony_ci	struct brcmf_scb_val_le scbval;
308162306a36Sopenharmony_ci	struct brcmf_pktcnt_le pktcnt;
308262306a36Sopenharmony_ci	s32 err;
308362306a36Sopenharmony_ci	u32 rate;
308462306a36Sopenharmony_ci	u32 rssi;
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	/* Get the current tx rate */
308762306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
308862306a36Sopenharmony_ci	if (err < 0) {
308962306a36Sopenharmony_ci		bphy_err(drvr, "BRCMF_C_GET_RATE error (%d)\n", err);
309062306a36Sopenharmony_ci		return err;
309162306a36Sopenharmony_ci	}
309262306a36Sopenharmony_ci	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
309362306a36Sopenharmony_ci	sinfo->txrate.legacy = rate * 5;
309462306a36Sopenharmony_ci
309562306a36Sopenharmony_ci	memset(&scbval, 0, sizeof(scbval));
309662306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
309762306a36Sopenharmony_ci				     sizeof(scbval));
309862306a36Sopenharmony_ci	if (err) {
309962306a36Sopenharmony_ci		bphy_err(drvr, "BRCMF_C_GET_RSSI error (%d)\n", err);
310062306a36Sopenharmony_ci		return err;
310162306a36Sopenharmony_ci	}
310262306a36Sopenharmony_ci	rssi = le32_to_cpu(scbval.val);
310362306a36Sopenharmony_ci	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
310462306a36Sopenharmony_ci	sinfo->signal = rssi;
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
310762306a36Sopenharmony_ci				     sizeof(pktcnt));
310862306a36Sopenharmony_ci	if (err) {
310962306a36Sopenharmony_ci		bphy_err(drvr, "BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
311062306a36Sopenharmony_ci		return err;
311162306a36Sopenharmony_ci	}
311262306a36Sopenharmony_ci	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
311362306a36Sopenharmony_ci			 BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
311462306a36Sopenharmony_ci			 BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
311562306a36Sopenharmony_ci			 BIT_ULL(NL80211_STA_INFO_TX_FAILED);
311662306a36Sopenharmony_ci	sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
311762306a36Sopenharmony_ci	sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
311862306a36Sopenharmony_ci	sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
311962306a36Sopenharmony_ci	sinfo->tx_failed  = le32_to_cpu(pktcnt.tx_bad_pkt);
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	return 0;
312262306a36Sopenharmony_ci}
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_cistatic s32
312562306a36Sopenharmony_cibrcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
312662306a36Sopenharmony_ci			   const u8 *mac, struct station_info *sinfo)
312762306a36Sopenharmony_ci{
312862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
312962306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
313062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
313162306a36Sopenharmony_ci	struct brcmf_scb_val_le scb_val;
313262306a36Sopenharmony_ci	s32 err = 0;
313362306a36Sopenharmony_ci	struct brcmf_sta_info_le sta_info_le;
313462306a36Sopenharmony_ci	u32 sta_flags;
313562306a36Sopenharmony_ci	u32 is_tdls_peer;
313662306a36Sopenharmony_ci	s32 total_rssi_avg = 0;
313762306a36Sopenharmony_ci	s32 total_rssi = 0;
313862306a36Sopenharmony_ci	s32 count_rssi = 0;
313962306a36Sopenharmony_ci	int rssi;
314062306a36Sopenharmony_ci	u32 i;
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
314362306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
314462306a36Sopenharmony_ci		return -EIO;
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci	if (brcmf_is_ibssmode(ifp->vif))
314762306a36Sopenharmony_ci		return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
314862306a36Sopenharmony_ci
314962306a36Sopenharmony_ci	memset(&sta_info_le, 0, sizeof(sta_info_le));
315062306a36Sopenharmony_ci	memcpy(&sta_info_le, mac, ETH_ALEN);
315162306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
315262306a36Sopenharmony_ci				       &sta_info_le,
315362306a36Sopenharmony_ci				       sizeof(sta_info_le));
315462306a36Sopenharmony_ci	is_tdls_peer = !err;
315562306a36Sopenharmony_ci	if (err) {
315662306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_get(ifp, "sta_info",
315762306a36Sopenharmony_ci					       &sta_info_le,
315862306a36Sopenharmony_ci					       sizeof(sta_info_le));
315962306a36Sopenharmony_ci		if (err < 0) {
316062306a36Sopenharmony_ci			bphy_err(drvr, "GET STA INFO failed, %d\n", err);
316162306a36Sopenharmony_ci			goto done;
316262306a36Sopenharmony_ci		}
316362306a36Sopenharmony_ci	}
316462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
316562306a36Sopenharmony_ci	sinfo->filled = BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
316662306a36Sopenharmony_ci	sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
316762306a36Sopenharmony_ci	sta_flags = le32_to_cpu(sta_info_le.flags);
316862306a36Sopenharmony_ci	brcmf_convert_sta_flags(sta_flags, sinfo);
316962306a36Sopenharmony_ci	sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
317062306a36Sopenharmony_ci	if (is_tdls_peer)
317162306a36Sopenharmony_ci		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
317262306a36Sopenharmony_ci	else
317362306a36Sopenharmony_ci		sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
317462306a36Sopenharmony_ci	if (sta_flags & BRCMF_STA_ASSOC) {
317562306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);
317662306a36Sopenharmony_ci		sinfo->connected_time = le32_to_cpu(sta_info_le.in);
317762306a36Sopenharmony_ci		brcmf_fill_bss_param(ifp, sinfo);
317862306a36Sopenharmony_ci	}
317962306a36Sopenharmony_ci	if (sta_flags & BRCMF_STA_SCBSTATS) {
318062306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
318162306a36Sopenharmony_ci		sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
318262306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
318362306a36Sopenharmony_ci		sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
318462306a36Sopenharmony_ci		sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
318562306a36Sopenharmony_ci		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
318662306a36Sopenharmony_ci		sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
318762306a36Sopenharmony_ci		sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
318862306a36Sopenharmony_ci		if (sinfo->tx_packets) {
318962306a36Sopenharmony_ci			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
319062306a36Sopenharmony_ci			sinfo->txrate.legacy =
319162306a36Sopenharmony_ci				le32_to_cpu(sta_info_le.tx_rate) / 100;
319262306a36Sopenharmony_ci		}
319362306a36Sopenharmony_ci		if (sinfo->rx_packets) {
319462306a36Sopenharmony_ci			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
319562306a36Sopenharmony_ci			sinfo->rxrate.legacy =
319662306a36Sopenharmony_ci				le32_to_cpu(sta_info_le.rx_rate) / 100;
319762306a36Sopenharmony_ci		}
319862306a36Sopenharmony_ci		if (le16_to_cpu(sta_info_le.ver) >= 4) {
319962306a36Sopenharmony_ci			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
320062306a36Sopenharmony_ci			sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
320162306a36Sopenharmony_ci			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
320262306a36Sopenharmony_ci			sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
320362306a36Sopenharmony_ci		}
320462306a36Sopenharmony_ci		for (i = 0; i < BRCMF_ANT_MAX; i++) {
320562306a36Sopenharmony_ci			if (sta_info_le.rssi[i] == 0 ||
320662306a36Sopenharmony_ci			    sta_info_le.rx_lastpkt_rssi[i] == 0)
320762306a36Sopenharmony_ci				continue;
320862306a36Sopenharmony_ci			sinfo->chains |= BIT(count_rssi);
320962306a36Sopenharmony_ci			sinfo->chain_signal[count_rssi] =
321062306a36Sopenharmony_ci				sta_info_le.rx_lastpkt_rssi[i];
321162306a36Sopenharmony_ci			sinfo->chain_signal_avg[count_rssi] =
321262306a36Sopenharmony_ci				sta_info_le.rssi[i];
321362306a36Sopenharmony_ci			total_rssi += sta_info_le.rx_lastpkt_rssi[i];
321462306a36Sopenharmony_ci			total_rssi_avg += sta_info_le.rssi[i];
321562306a36Sopenharmony_ci			count_rssi++;
321662306a36Sopenharmony_ci		}
321762306a36Sopenharmony_ci		if (count_rssi) {
321862306a36Sopenharmony_ci			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
321962306a36Sopenharmony_ci			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
322062306a36Sopenharmony_ci			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
322162306a36Sopenharmony_ci			sinfo->filled |=
322262306a36Sopenharmony_ci				BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
322362306a36Sopenharmony_ci			sinfo->signal = total_rssi / count_rssi;
322462306a36Sopenharmony_ci			sinfo->signal_avg = total_rssi_avg / count_rssi;
322562306a36Sopenharmony_ci		} else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
322662306a36Sopenharmony_ci			&ifp->vif->sme_state)) {
322762306a36Sopenharmony_ci			memset(&scb_val, 0, sizeof(scb_val));
322862306a36Sopenharmony_ci			err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
322962306a36Sopenharmony_ci						     &scb_val, sizeof(scb_val));
323062306a36Sopenharmony_ci			if (err) {
323162306a36Sopenharmony_ci				bphy_err(drvr, "Could not get rssi (%d)\n",
323262306a36Sopenharmony_ci					 err);
323362306a36Sopenharmony_ci				goto done;
323462306a36Sopenharmony_ci			} else {
323562306a36Sopenharmony_ci				rssi = le32_to_cpu(scb_val.val);
323662306a36Sopenharmony_ci				sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
323762306a36Sopenharmony_ci				sinfo->signal = rssi;
323862306a36Sopenharmony_ci				brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
323962306a36Sopenharmony_ci			}
324062306a36Sopenharmony_ci		}
324162306a36Sopenharmony_ci	}
324262306a36Sopenharmony_cidone:
324362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
324462306a36Sopenharmony_ci	return err;
324562306a36Sopenharmony_ci}
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_cistatic int
324862306a36Sopenharmony_cibrcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
324962306a36Sopenharmony_ci			    int idx, u8 *mac, struct station_info *sinfo)
325062306a36Sopenharmony_ci{
325162306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
325262306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
325362306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
325462306a36Sopenharmony_ci	s32 err;
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	if (idx == 0) {
325962306a36Sopenharmony_ci		cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
326062306a36Sopenharmony_ci		err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
326162306a36Sopenharmony_ci					     &cfg->assoclist,
326262306a36Sopenharmony_ci					     sizeof(cfg->assoclist));
326362306a36Sopenharmony_ci		if (err) {
326462306a36Sopenharmony_ci			/* GET_ASSOCLIST unsupported by firmware of older chips */
326562306a36Sopenharmony_ci			if (err == -EBADE)
326662306a36Sopenharmony_ci				bphy_info_once(drvr, "BRCMF_C_GET_ASSOCLIST unsupported\n");
326762306a36Sopenharmony_ci			else
326862306a36Sopenharmony_ci				bphy_err(drvr, "BRCMF_C_GET_ASSOCLIST failed, err=%d\n",
326962306a36Sopenharmony_ci					 err);
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci			cfg->assoclist.count = 0;
327262306a36Sopenharmony_ci			return -EOPNOTSUPP;
327362306a36Sopenharmony_ci		}
327462306a36Sopenharmony_ci	}
327562306a36Sopenharmony_ci	if (idx < le32_to_cpu(cfg->assoclist.count)) {
327662306a36Sopenharmony_ci		memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
327762306a36Sopenharmony_ci		return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
327862306a36Sopenharmony_ci	}
327962306a36Sopenharmony_ci	return -ENOENT;
328062306a36Sopenharmony_ci}
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_cistatic s32
328362306a36Sopenharmony_cibrcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
328462306a36Sopenharmony_ci			   bool enabled, s32 timeout)
328562306a36Sopenharmony_ci{
328662306a36Sopenharmony_ci	s32 pm;
328762306a36Sopenharmony_ci	s32 err = 0;
328862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
328962306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
329062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	/*
329562306a36Sopenharmony_ci	 * Powersave enable/disable request is coming from the
329662306a36Sopenharmony_ci	 * cfg80211 even before the interface is up. In that
329762306a36Sopenharmony_ci	 * scenario, driver will be storing the power save
329862306a36Sopenharmony_ci	 * preference in cfg struct to apply this to
329962306a36Sopenharmony_ci	 * FW later while initializing the dongle
330062306a36Sopenharmony_ci	 */
330162306a36Sopenharmony_ci	cfg->pwr_save = enabled;
330262306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif)) {
330362306a36Sopenharmony_ci
330462306a36Sopenharmony_ci		brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
330562306a36Sopenharmony_ci		goto done;
330662306a36Sopenharmony_ci	}
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_ci	pm = enabled ? PM_FAST : PM_OFF;
330962306a36Sopenharmony_ci	/* Do not enable the power save after assoc if it is a p2p interface */
331062306a36Sopenharmony_ci	if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
331162306a36Sopenharmony_ci		brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
331262306a36Sopenharmony_ci		pm = PM_OFF;
331362306a36Sopenharmony_ci	}
331462306a36Sopenharmony_ci	brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
331562306a36Sopenharmony_ci
331662306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
331762306a36Sopenharmony_ci	if (err) {
331862306a36Sopenharmony_ci		if (err == -ENODEV)
331962306a36Sopenharmony_ci			bphy_err(drvr, "net_device is not ready yet\n");
332062306a36Sopenharmony_ci		else
332162306a36Sopenharmony_ci			bphy_err(drvr, "error (%d)\n", err);
332262306a36Sopenharmony_ci	}
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret",
332562306a36Sopenharmony_ci				min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS));
332662306a36Sopenharmony_ci	if (err)
332762306a36Sopenharmony_ci		bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err);
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_cidone:
333062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
333162306a36Sopenharmony_ci	return err;
333262306a36Sopenharmony_ci}
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_cistatic s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
333562306a36Sopenharmony_ci				   struct brcmf_bss_info_le *bi)
333662306a36Sopenharmony_ci{
333762306a36Sopenharmony_ci	struct wiphy *wiphy = cfg_to_wiphy(cfg);
333862306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
333962306a36Sopenharmony_ci	struct cfg80211_bss *bss;
334062306a36Sopenharmony_ci	enum nl80211_band band;
334162306a36Sopenharmony_ci	struct brcmu_chan ch;
334262306a36Sopenharmony_ci	u16 channel;
334362306a36Sopenharmony_ci	u32 freq;
334462306a36Sopenharmony_ci	u16 notify_capability;
334562306a36Sopenharmony_ci	u16 notify_interval;
334662306a36Sopenharmony_ci	u8 *notify_ie;
334762306a36Sopenharmony_ci	size_t notify_ielen;
334862306a36Sopenharmony_ci	struct cfg80211_inform_bss bss_data = {};
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
335162306a36Sopenharmony_ci		bphy_err(drvr, "Bss info is larger than buffer. Discarding\n");
335262306a36Sopenharmony_ci		return -EINVAL;
335362306a36Sopenharmony_ci	}
335462306a36Sopenharmony_ci
335562306a36Sopenharmony_ci	if (!bi->ctl_ch) {
335662306a36Sopenharmony_ci		ch.chspec = le16_to_cpu(bi->chanspec);
335762306a36Sopenharmony_ci		cfg->d11inf.decchspec(&ch);
335862306a36Sopenharmony_ci		bi->ctl_ch = ch.control_ch_num;
335962306a36Sopenharmony_ci	}
336062306a36Sopenharmony_ci	channel = bi->ctl_ch;
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci	if (channel <= CH_MAX_2G_CHANNEL)
336362306a36Sopenharmony_ci		band = NL80211_BAND_2GHZ;
336462306a36Sopenharmony_ci	else
336562306a36Sopenharmony_ci		band = NL80211_BAND_5GHZ;
336662306a36Sopenharmony_ci
336762306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(channel, band);
336862306a36Sopenharmony_ci	bss_data.chan = ieee80211_get_channel(wiphy, freq);
336962306a36Sopenharmony_ci	bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20;
337062306a36Sopenharmony_ci	bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci	notify_capability = le16_to_cpu(bi->capability);
337362306a36Sopenharmony_ci	notify_interval = le16_to_cpu(bi->beacon_period);
337462306a36Sopenharmony_ci	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
337562306a36Sopenharmony_ci	notify_ielen = le32_to_cpu(bi->ie_length);
337662306a36Sopenharmony_ci	bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100;
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci	brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
337962306a36Sopenharmony_ci	brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
338062306a36Sopenharmony_ci	brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
338162306a36Sopenharmony_ci	brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
338262306a36Sopenharmony_ci	brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal);
338362306a36Sopenharmony_ci
338462306a36Sopenharmony_ci	bss = cfg80211_inform_bss_data(wiphy, &bss_data,
338562306a36Sopenharmony_ci				       CFG80211_BSS_FTYPE_UNKNOWN,
338662306a36Sopenharmony_ci				       (const u8 *)bi->BSSID,
338762306a36Sopenharmony_ci				       0, notify_capability,
338862306a36Sopenharmony_ci				       notify_interval, notify_ie,
338962306a36Sopenharmony_ci				       notify_ielen, GFP_KERNEL);
339062306a36Sopenharmony_ci
339162306a36Sopenharmony_ci	if (!bss)
339262306a36Sopenharmony_ci		return -ENOMEM;
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	cfg80211_put_bss(wiphy, bss);
339562306a36Sopenharmony_ci
339662306a36Sopenharmony_ci	return 0;
339762306a36Sopenharmony_ci}
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_cistatic struct brcmf_bss_info_le *
340062306a36Sopenharmony_cinext_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
340162306a36Sopenharmony_ci{
340262306a36Sopenharmony_ci	if (bss == NULL)
340362306a36Sopenharmony_ci		return list->bss_info_le;
340462306a36Sopenharmony_ci	return (struct brcmf_bss_info_le *)((unsigned long)bss +
340562306a36Sopenharmony_ci					    le32_to_cpu(bss->length));
340662306a36Sopenharmony_ci}
340762306a36Sopenharmony_ci
340862306a36Sopenharmony_cistatic s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
340962306a36Sopenharmony_ci{
341062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
341162306a36Sopenharmony_ci	struct brcmf_scan_results *bss_list;
341262306a36Sopenharmony_ci	struct brcmf_bss_info_le *bi = NULL;	/* must be initialized */
341362306a36Sopenharmony_ci	s32 err = 0;
341462306a36Sopenharmony_ci	int i;
341562306a36Sopenharmony_ci
341662306a36Sopenharmony_ci	bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
341762306a36Sopenharmony_ci	if (bss_list->count != 0 &&
341862306a36Sopenharmony_ci	    bss_list->version != BRCMF_BSS_INFO_VERSION) {
341962306a36Sopenharmony_ci		bphy_err(drvr, "Version %d != WL_BSS_INFO_VERSION\n",
342062306a36Sopenharmony_ci			 bss_list->version);
342162306a36Sopenharmony_ci		return -EOPNOTSUPP;
342262306a36Sopenharmony_ci	}
342362306a36Sopenharmony_ci	brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
342462306a36Sopenharmony_ci	for (i = 0; i < bss_list->count; i++) {
342562306a36Sopenharmony_ci		bi = next_bss_le(bss_list, bi);
342662306a36Sopenharmony_ci		err = brcmf_inform_single_bss(cfg, bi);
342762306a36Sopenharmony_ci		if (err)
342862306a36Sopenharmony_ci			break;
342962306a36Sopenharmony_ci	}
343062306a36Sopenharmony_ci	return err;
343162306a36Sopenharmony_ci}
343262306a36Sopenharmony_ci
343362306a36Sopenharmony_cistatic s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
343462306a36Sopenharmony_ci			     struct net_device *ndev, const u8 *bssid)
343562306a36Sopenharmony_ci{
343662306a36Sopenharmony_ci	struct wiphy *wiphy = cfg_to_wiphy(cfg);
343762306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
343862306a36Sopenharmony_ci	struct ieee80211_channel *notify_channel;
343962306a36Sopenharmony_ci	struct brcmf_bss_info_le *bi = NULL;
344062306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
344162306a36Sopenharmony_ci	struct cfg80211_bss *bss;
344262306a36Sopenharmony_ci	struct brcmu_chan ch;
344362306a36Sopenharmony_ci	u8 *buf = NULL;
344462306a36Sopenharmony_ci	s32 err = 0;
344562306a36Sopenharmony_ci	u32 freq;
344662306a36Sopenharmony_ci	u16 notify_capability;
344762306a36Sopenharmony_ci	u16 notify_interval;
344862306a36Sopenharmony_ci	u8 *notify_ie;
344962306a36Sopenharmony_ci	size_t notify_ielen;
345062306a36Sopenharmony_ci	s32 notify_signal;
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
345562306a36Sopenharmony_ci	if (buf == NULL) {
345662306a36Sopenharmony_ci		err = -ENOMEM;
345762306a36Sopenharmony_ci		goto CleanUp;
345862306a36Sopenharmony_ci	}
345962306a36Sopenharmony_ci
346062306a36Sopenharmony_ci	*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
346362306a36Sopenharmony_ci				     buf, WL_BSS_INFO_MAX);
346462306a36Sopenharmony_ci	if (err) {
346562306a36Sopenharmony_ci		bphy_err(drvr, "WLC_GET_BSS_INFO failed: %d\n", err);
346662306a36Sopenharmony_ci		goto CleanUp;
346762306a36Sopenharmony_ci	}
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	bi = (struct brcmf_bss_info_le *)(buf + 4);
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	ch.chspec = le16_to_cpu(bi->chanspec);
347262306a36Sopenharmony_ci	cfg->d11inf.decchspec(&ch);
347362306a36Sopenharmony_ci
347462306a36Sopenharmony_ci	if (ch.band == BRCMU_CHAN_BAND_2G)
347562306a36Sopenharmony_ci		band = wiphy->bands[NL80211_BAND_2GHZ];
347662306a36Sopenharmony_ci	else
347762306a36Sopenharmony_ci		band = wiphy->bands[NL80211_BAND_5GHZ];
347862306a36Sopenharmony_ci
347962306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
348062306a36Sopenharmony_ci	cfg->channel = freq;
348162306a36Sopenharmony_ci	notify_channel = ieee80211_get_channel(wiphy, freq);
348262306a36Sopenharmony_ci
348362306a36Sopenharmony_ci	notify_capability = le16_to_cpu(bi->capability);
348462306a36Sopenharmony_ci	notify_interval = le16_to_cpu(bi->beacon_period);
348562306a36Sopenharmony_ci	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
348662306a36Sopenharmony_ci	notify_ielen = le32_to_cpu(bi->ie_length);
348762306a36Sopenharmony_ci	notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
348862306a36Sopenharmony_ci
348962306a36Sopenharmony_ci	brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq);
349062306a36Sopenharmony_ci	brcmf_dbg(CONN, "capability: %X\n", notify_capability);
349162306a36Sopenharmony_ci	brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
349262306a36Sopenharmony_ci	brcmf_dbg(CONN, "signal: %d\n", notify_signal);
349362306a36Sopenharmony_ci
349462306a36Sopenharmony_ci	bss = cfg80211_inform_bss(wiphy, notify_channel,
349562306a36Sopenharmony_ci				  CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
349662306a36Sopenharmony_ci				  notify_capability, notify_interval,
349762306a36Sopenharmony_ci				  notify_ie, notify_ielen, notify_signal,
349862306a36Sopenharmony_ci				  GFP_KERNEL);
349962306a36Sopenharmony_ci
350062306a36Sopenharmony_ci	if (!bss) {
350162306a36Sopenharmony_ci		err = -ENOMEM;
350262306a36Sopenharmony_ci		goto CleanUp;
350362306a36Sopenharmony_ci	}
350462306a36Sopenharmony_ci
350562306a36Sopenharmony_ci	cfg80211_put_bss(wiphy, bss);
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ciCleanUp:
350862306a36Sopenharmony_ci
350962306a36Sopenharmony_ci	kfree(buf);
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	return err;
351462306a36Sopenharmony_ci}
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_cistatic s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
351762306a36Sopenharmony_ci				 struct brcmf_if *ifp)
351862306a36Sopenharmony_ci{
351962306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
352062306a36Sopenharmony_ci	struct brcmf_bss_info_le *bi = NULL;
352162306a36Sopenharmony_ci	s32 err = 0;
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
352462306a36Sopenharmony_ci	if (brcmf_is_ibssmode(ifp->vif))
352562306a36Sopenharmony_ci		return err;
352662306a36Sopenharmony_ci
352762306a36Sopenharmony_ci	*(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
352862306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
352962306a36Sopenharmony_ci				     cfg->extra_buf, WL_EXTRA_BUF_MAX);
353062306a36Sopenharmony_ci	if (err) {
353162306a36Sopenharmony_ci		bphy_err(drvr, "Could not get bss info %d\n", err);
353262306a36Sopenharmony_ci		goto update_bss_info_out;
353362306a36Sopenharmony_ci	}
353462306a36Sopenharmony_ci	bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
353562306a36Sopenharmony_ci	err = brcmf_inform_single_bss(cfg, bi);
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ciupdate_bss_info_out:
353862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit");
353962306a36Sopenharmony_ci	return err;
354062306a36Sopenharmony_ci}
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_civoid brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
354362306a36Sopenharmony_ci{
354462306a36Sopenharmony_ci	struct escan_info *escan = &cfg->escan_info;
354562306a36Sopenharmony_ci
354662306a36Sopenharmony_ci	set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
354762306a36Sopenharmony_ci	if (cfg->int_escan_map || cfg->scan_request) {
354862306a36Sopenharmony_ci		escan->escan_state = WL_ESCAN_STATE_IDLE;
354962306a36Sopenharmony_ci		brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
355062306a36Sopenharmony_ci	}
355162306a36Sopenharmony_ci	clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
355262306a36Sopenharmony_ci	clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
355362306a36Sopenharmony_ci}
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_cistatic void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
355662306a36Sopenharmony_ci{
355762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg =
355862306a36Sopenharmony_ci			container_of(work, struct brcmf_cfg80211_info,
355962306a36Sopenharmony_ci				     escan_timeout_work);
356062306a36Sopenharmony_ci
356162306a36Sopenharmony_ci	brcmf_inform_bss(cfg);
356262306a36Sopenharmony_ci	brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
356362306a36Sopenharmony_ci}
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_cistatic void brcmf_escan_timeout(struct timer_list *t)
356662306a36Sopenharmony_ci{
356762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg =
356862306a36Sopenharmony_ci			from_timer(cfg, t, escan_timeout);
356962306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_ci	if (cfg->int_escan_map || cfg->scan_request) {
357262306a36Sopenharmony_ci		bphy_err(drvr, "timer expired\n");
357362306a36Sopenharmony_ci		schedule_work(&cfg->escan_timeout_work);
357462306a36Sopenharmony_ci	}
357562306a36Sopenharmony_ci}
357662306a36Sopenharmony_ci
357762306a36Sopenharmony_cistatic s32
357862306a36Sopenharmony_cibrcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
357962306a36Sopenharmony_ci			      struct brcmf_bss_info_le *bss,
358062306a36Sopenharmony_ci			      struct brcmf_bss_info_le *bss_info_le)
358162306a36Sopenharmony_ci{
358262306a36Sopenharmony_ci	struct brcmu_chan ch_bss, ch_bss_info_le;
358362306a36Sopenharmony_ci
358462306a36Sopenharmony_ci	ch_bss.chspec = le16_to_cpu(bss->chanspec);
358562306a36Sopenharmony_ci	cfg->d11inf.decchspec(&ch_bss);
358662306a36Sopenharmony_ci	ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
358762306a36Sopenharmony_ci	cfg->d11inf.decchspec(&ch_bss_info_le);
358862306a36Sopenharmony_ci
358962306a36Sopenharmony_ci	if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
359062306a36Sopenharmony_ci		ch_bss.band == ch_bss_info_le.band &&
359162306a36Sopenharmony_ci		bss_info_le->SSID_len == bss->SSID_len &&
359262306a36Sopenharmony_ci		!memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
359362306a36Sopenharmony_ci		if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
359462306a36Sopenharmony_ci			(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
359562306a36Sopenharmony_ci			s16 bss_rssi = le16_to_cpu(bss->RSSI);
359662306a36Sopenharmony_ci			s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
359762306a36Sopenharmony_ci
359862306a36Sopenharmony_ci			/* preserve max RSSI if the measurements are
359962306a36Sopenharmony_ci			* both on-channel or both off-channel
360062306a36Sopenharmony_ci			*/
360162306a36Sopenharmony_ci			if (bss_info_rssi > bss_rssi)
360262306a36Sopenharmony_ci				bss->RSSI = bss_info_le->RSSI;
360362306a36Sopenharmony_ci		} else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
360462306a36Sopenharmony_ci			(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
360562306a36Sopenharmony_ci			/* preserve the on-channel rssi measurement
360662306a36Sopenharmony_ci			* if the new measurement is off channel
360762306a36Sopenharmony_ci			*/
360862306a36Sopenharmony_ci			bss->RSSI = bss_info_le->RSSI;
360962306a36Sopenharmony_ci			bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
361062306a36Sopenharmony_ci		}
361162306a36Sopenharmony_ci		return 1;
361262306a36Sopenharmony_ci	}
361362306a36Sopenharmony_ci	return 0;
361462306a36Sopenharmony_ci}
361562306a36Sopenharmony_ci
361662306a36Sopenharmony_cistatic s32
361762306a36Sopenharmony_cibrcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
361862306a36Sopenharmony_ci			     const struct brcmf_event_msg *e, void *data)
361962306a36Sopenharmony_ci{
362062306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
362162306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = drvr->config;
362262306a36Sopenharmony_ci	s32 status;
362362306a36Sopenharmony_ci	struct brcmf_escan_result_le *escan_result_le;
362462306a36Sopenharmony_ci	u32 escan_buflen;
362562306a36Sopenharmony_ci	struct brcmf_bss_info_le *bss_info_le;
362662306a36Sopenharmony_ci	struct brcmf_bss_info_le *bss = NULL;
362762306a36Sopenharmony_ci	u32 bi_length;
362862306a36Sopenharmony_ci	struct brcmf_scan_results *list;
362962306a36Sopenharmony_ci	u32 i;
363062306a36Sopenharmony_ci	bool aborted;
363162306a36Sopenharmony_ci
363262306a36Sopenharmony_ci	status = e->status;
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ci	if (status == BRCMF_E_STATUS_ABORT)
363562306a36Sopenharmony_ci		goto exit;
363662306a36Sopenharmony_ci
363762306a36Sopenharmony_ci	if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
363862306a36Sopenharmony_ci		bphy_err(drvr, "scan not ready, bsscfgidx=%d\n",
363962306a36Sopenharmony_ci			 ifp->bsscfgidx);
364062306a36Sopenharmony_ci		return -EPERM;
364162306a36Sopenharmony_ci	}
364262306a36Sopenharmony_ci
364362306a36Sopenharmony_ci	if (status == BRCMF_E_STATUS_PARTIAL) {
364462306a36Sopenharmony_ci		brcmf_dbg(SCAN, "ESCAN Partial result\n");
364562306a36Sopenharmony_ci		if (e->datalen < sizeof(*escan_result_le)) {
364662306a36Sopenharmony_ci			bphy_err(drvr, "invalid event data length\n");
364762306a36Sopenharmony_ci			goto exit;
364862306a36Sopenharmony_ci		}
364962306a36Sopenharmony_ci		escan_result_le = (struct brcmf_escan_result_le *) data;
365062306a36Sopenharmony_ci		if (!escan_result_le) {
365162306a36Sopenharmony_ci			bphy_err(drvr, "Invalid escan result (NULL pointer)\n");
365262306a36Sopenharmony_ci			goto exit;
365362306a36Sopenharmony_ci		}
365462306a36Sopenharmony_ci		escan_buflen = le32_to_cpu(escan_result_le->buflen);
365562306a36Sopenharmony_ci		if (escan_buflen > BRCMF_ESCAN_BUF_SIZE ||
365662306a36Sopenharmony_ci		    escan_buflen > e->datalen ||
365762306a36Sopenharmony_ci		    escan_buflen < sizeof(*escan_result_le)) {
365862306a36Sopenharmony_ci			bphy_err(drvr, "Invalid escan buffer length: %d\n",
365962306a36Sopenharmony_ci				 escan_buflen);
366062306a36Sopenharmony_ci			goto exit;
366162306a36Sopenharmony_ci		}
366262306a36Sopenharmony_ci		if (le16_to_cpu(escan_result_le->bss_count) != 1) {
366362306a36Sopenharmony_ci			bphy_err(drvr, "Invalid bss_count %d: ignoring\n",
366462306a36Sopenharmony_ci				 escan_result_le->bss_count);
366562306a36Sopenharmony_ci			goto exit;
366662306a36Sopenharmony_ci		}
366762306a36Sopenharmony_ci		bss_info_le = &escan_result_le->bss_info_le;
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci		if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
367062306a36Sopenharmony_ci			goto exit;
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci		if (!cfg->int_escan_map && !cfg->scan_request) {
367362306a36Sopenharmony_ci			brcmf_dbg(SCAN, "result without cfg80211 request\n");
367462306a36Sopenharmony_ci			goto exit;
367562306a36Sopenharmony_ci		}
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ci		bi_length = le32_to_cpu(bss_info_le->length);
367862306a36Sopenharmony_ci		if (bi_length != escan_buflen -	WL_ESCAN_RESULTS_FIXED_SIZE) {
367962306a36Sopenharmony_ci			bphy_err(drvr, "Ignoring invalid bss_info length: %d\n",
368062306a36Sopenharmony_ci				 bi_length);
368162306a36Sopenharmony_ci			goto exit;
368262306a36Sopenharmony_ci		}
368362306a36Sopenharmony_ci
368462306a36Sopenharmony_ci		if (!(cfg_to_wiphy(cfg)->interface_modes &
368562306a36Sopenharmony_ci					BIT(NL80211_IFTYPE_ADHOC))) {
368662306a36Sopenharmony_ci			if (le16_to_cpu(bss_info_le->capability) &
368762306a36Sopenharmony_ci						WLAN_CAPABILITY_IBSS) {
368862306a36Sopenharmony_ci				bphy_err(drvr, "Ignoring IBSS result\n");
368962306a36Sopenharmony_ci				goto exit;
369062306a36Sopenharmony_ci			}
369162306a36Sopenharmony_ci		}
369262306a36Sopenharmony_ci
369362306a36Sopenharmony_ci		list = (struct brcmf_scan_results *)
369462306a36Sopenharmony_ci				cfg->escan_info.escan_buf;
369562306a36Sopenharmony_ci		if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
369662306a36Sopenharmony_ci			bphy_err(drvr, "Buffer is too small: ignoring\n");
369762306a36Sopenharmony_ci			goto exit;
369862306a36Sopenharmony_ci		}
369962306a36Sopenharmony_ci
370062306a36Sopenharmony_ci		for (i = 0; i < list->count; i++) {
370162306a36Sopenharmony_ci			bss = bss ? (struct brcmf_bss_info_le *)
370262306a36Sopenharmony_ci				((unsigned char *)bss +
370362306a36Sopenharmony_ci				le32_to_cpu(bss->length)) : list->bss_info_le;
370462306a36Sopenharmony_ci			if (brcmf_compare_update_same_bss(cfg, bss,
370562306a36Sopenharmony_ci							  bss_info_le))
370662306a36Sopenharmony_ci				goto exit;
370762306a36Sopenharmony_ci		}
370862306a36Sopenharmony_ci		memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
370962306a36Sopenharmony_ci		       bi_length);
371062306a36Sopenharmony_ci		list->version = le32_to_cpu(bss_info_le->version);
371162306a36Sopenharmony_ci		list->buflen += bi_length;
371262306a36Sopenharmony_ci		list->count++;
371362306a36Sopenharmony_ci	} else {
371462306a36Sopenharmony_ci		cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
371562306a36Sopenharmony_ci		if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
371662306a36Sopenharmony_ci			goto exit;
371762306a36Sopenharmony_ci		if (cfg->int_escan_map || cfg->scan_request) {
371862306a36Sopenharmony_ci			brcmf_inform_bss(cfg);
371962306a36Sopenharmony_ci			aborted = status != BRCMF_E_STATUS_SUCCESS;
372062306a36Sopenharmony_ci			brcmf_notify_escan_complete(cfg, ifp, aborted, false);
372162306a36Sopenharmony_ci		} else
372262306a36Sopenharmony_ci			brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
372362306a36Sopenharmony_ci				  status);
372462306a36Sopenharmony_ci	}
372562306a36Sopenharmony_ciexit:
372662306a36Sopenharmony_ci	return 0;
372762306a36Sopenharmony_ci}
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_cistatic void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
373062306a36Sopenharmony_ci{
373162306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
373262306a36Sopenharmony_ci			    brcmf_cfg80211_escan_handler);
373362306a36Sopenharmony_ci	cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
373462306a36Sopenharmony_ci	/* Init scan_timeout timer */
373562306a36Sopenharmony_ci	timer_setup(&cfg->escan_timeout, brcmf_escan_timeout, 0);
373662306a36Sopenharmony_ci	INIT_WORK(&cfg->escan_timeout_work,
373762306a36Sopenharmony_ci		  brcmf_cfg80211_escan_timeout_worker);
373862306a36Sopenharmony_ci}
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_cistatic struct cfg80211_scan_request *
374162306a36Sopenharmony_cibrcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {
374262306a36Sopenharmony_ci	struct cfg80211_scan_request *req;
374362306a36Sopenharmony_ci	size_t req_size;
374462306a36Sopenharmony_ci
374562306a36Sopenharmony_ci	req_size = sizeof(*req) +
374662306a36Sopenharmony_ci		   n_netinfo * sizeof(req->channels[0]) +
374762306a36Sopenharmony_ci		   n_netinfo * sizeof(*req->ssids);
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_ci	req = kzalloc(req_size, GFP_KERNEL);
375062306a36Sopenharmony_ci	if (req) {
375162306a36Sopenharmony_ci		req->wiphy = wiphy;
375262306a36Sopenharmony_ci		req->ssids = (void *)(&req->channels[0]) +
375362306a36Sopenharmony_ci			     n_netinfo * sizeof(req->channels[0]);
375462306a36Sopenharmony_ci	}
375562306a36Sopenharmony_ci	return req;
375662306a36Sopenharmony_ci}
375762306a36Sopenharmony_ci
375862306a36Sopenharmony_cistatic int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
375962306a36Sopenharmony_ci					 u8 *ssid, u8 ssid_len, u8 channel)
376062306a36Sopenharmony_ci{
376162306a36Sopenharmony_ci	struct ieee80211_channel *chan;
376262306a36Sopenharmony_ci	enum nl80211_band band;
376362306a36Sopenharmony_ci	int freq, i;
376462306a36Sopenharmony_ci
376562306a36Sopenharmony_ci	if (channel <= CH_MAX_2G_CHANNEL)
376662306a36Sopenharmony_ci		band = NL80211_BAND_2GHZ;
376762306a36Sopenharmony_ci	else
376862306a36Sopenharmony_ci		band = NL80211_BAND_5GHZ;
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(channel, band);
377162306a36Sopenharmony_ci	if (!freq)
377262306a36Sopenharmony_ci		return -EINVAL;
377362306a36Sopenharmony_ci
377462306a36Sopenharmony_ci	chan = ieee80211_get_channel(req->wiphy, freq);
377562306a36Sopenharmony_ci	if (!chan)
377662306a36Sopenharmony_ci		return -EINVAL;
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	for (i = 0; i < req->n_channels; i++) {
377962306a36Sopenharmony_ci		if (req->channels[i] == chan)
378062306a36Sopenharmony_ci			break;
378162306a36Sopenharmony_ci	}
378262306a36Sopenharmony_ci	if (i == req->n_channels) {
378362306a36Sopenharmony_ci		req->n_channels++;
378462306a36Sopenharmony_ci		req->channels[i] = chan;
378562306a36Sopenharmony_ci	}
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci	for (i = 0; i < req->n_ssids; i++) {
378862306a36Sopenharmony_ci		if (req->ssids[i].ssid_len == ssid_len &&
378962306a36Sopenharmony_ci		    !memcmp(req->ssids[i].ssid, ssid, ssid_len))
379062306a36Sopenharmony_ci			break;
379162306a36Sopenharmony_ci	}
379262306a36Sopenharmony_ci	if (i == req->n_ssids) {
379362306a36Sopenharmony_ci		memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
379462306a36Sopenharmony_ci		req->ssids[req->n_ssids++].ssid_len = ssid_len;
379562306a36Sopenharmony_ci	}
379662306a36Sopenharmony_ci	return 0;
379762306a36Sopenharmony_ci}
379862306a36Sopenharmony_ci
379962306a36Sopenharmony_cistatic int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap,
380062306a36Sopenharmony_ci				      struct cfg80211_scan_request *request)
380162306a36Sopenharmony_ci{
380262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
380362306a36Sopenharmony_ci	int err;
380462306a36Sopenharmony_ci
380562306a36Sopenharmony_ci	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
380662306a36Sopenharmony_ci		if (cfg->int_escan_map)
380762306a36Sopenharmony_ci			brcmf_dbg(SCAN, "aborting internal scan: map=%u\n",
380862306a36Sopenharmony_ci				  cfg->int_escan_map);
380962306a36Sopenharmony_ci		/* Abort any on-going scan */
381062306a36Sopenharmony_ci		brcmf_abort_scanning(cfg);
381162306a36Sopenharmony_ci	}
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci	brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap);
381462306a36Sopenharmony_ci	set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
381562306a36Sopenharmony_ci	cfg->escan_info.run = brcmf_run_escan;
381662306a36Sopenharmony_ci	err = brcmf_do_escan(ifp, request);
381762306a36Sopenharmony_ci	if (err) {
381862306a36Sopenharmony_ci		clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
381962306a36Sopenharmony_ci		return err;
382062306a36Sopenharmony_ci	}
382162306a36Sopenharmony_ci	cfg->int_escan_map = fwmap;
382262306a36Sopenharmony_ci	return 0;
382362306a36Sopenharmony_ci}
382462306a36Sopenharmony_ci
382562306a36Sopenharmony_cistatic struct brcmf_pno_net_info_le *
382662306a36Sopenharmony_cibrcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)
382762306a36Sopenharmony_ci{
382862306a36Sopenharmony_ci	struct brcmf_pno_scanresults_v2_le *pfn_v2;
382962306a36Sopenharmony_ci	struct brcmf_pno_net_info_le *netinfo;
383062306a36Sopenharmony_ci
383162306a36Sopenharmony_ci	switch (pfn_v1->version) {
383262306a36Sopenharmony_ci	default:
383362306a36Sopenharmony_ci		WARN_ON(1);
383462306a36Sopenharmony_ci		fallthrough;
383562306a36Sopenharmony_ci	case cpu_to_le32(1):
383662306a36Sopenharmony_ci		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);
383762306a36Sopenharmony_ci		break;
383862306a36Sopenharmony_ci	case cpu_to_le32(2):
383962306a36Sopenharmony_ci		pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;
384062306a36Sopenharmony_ci		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);
384162306a36Sopenharmony_ci		break;
384262306a36Sopenharmony_ci	}
384362306a36Sopenharmony_ci
384462306a36Sopenharmony_ci	return netinfo;
384562306a36Sopenharmony_ci}
384662306a36Sopenharmony_ci
384762306a36Sopenharmony_ci/* PFN result doesn't have all the info which are required by the supplicant
384862306a36Sopenharmony_ci * (For e.g IEs) Do a target Escan so that sched scan results are reported
384962306a36Sopenharmony_ci * via wl_inform_single_bss in the required format. Escan does require the
385062306a36Sopenharmony_ci * scan request in the form of cfg80211_scan_request. For timebeing, create
385162306a36Sopenharmony_ci * cfg80211_scan_request one out of the received PNO event.
385262306a36Sopenharmony_ci */
385362306a36Sopenharmony_cistatic s32
385462306a36Sopenharmony_cibrcmf_notify_sched_scan_results(struct brcmf_if *ifp,
385562306a36Sopenharmony_ci				const struct brcmf_event_msg *e, void *data)
385662306a36Sopenharmony_ci{
385762306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
385862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = drvr->config;
385962306a36Sopenharmony_ci	struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
386062306a36Sopenharmony_ci	struct cfg80211_scan_request *request = NULL;
386162306a36Sopenharmony_ci	struct wiphy *wiphy = cfg_to_wiphy(cfg);
386262306a36Sopenharmony_ci	int i, err = 0;
386362306a36Sopenharmony_ci	struct brcmf_pno_scanresults_le *pfn_result;
386462306a36Sopenharmony_ci	u32 bucket_map;
386562306a36Sopenharmony_ci	u32 result_count;
386662306a36Sopenharmony_ci	u32 status;
386762306a36Sopenharmony_ci	u32 datalen;
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ci	brcmf_dbg(SCAN, "Enter\n");
387062306a36Sopenharmony_ci
387162306a36Sopenharmony_ci	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
387262306a36Sopenharmony_ci		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
387362306a36Sopenharmony_ci		return 0;
387462306a36Sopenharmony_ci	}
387562306a36Sopenharmony_ci
387662306a36Sopenharmony_ci	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
387762306a36Sopenharmony_ci		brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
387862306a36Sopenharmony_ci		return 0;
387962306a36Sopenharmony_ci	}
388062306a36Sopenharmony_ci
388162306a36Sopenharmony_ci	pfn_result = (struct brcmf_pno_scanresults_le *)data;
388262306a36Sopenharmony_ci	result_count = le32_to_cpu(pfn_result->count);
388362306a36Sopenharmony_ci	status = le32_to_cpu(pfn_result->status);
388462306a36Sopenharmony_ci
388562306a36Sopenharmony_ci	/* PFN event is limited to fit 512 bytes so we may get
388662306a36Sopenharmony_ci	 * multiple NET_FOUND events. For now place a warning here.
388762306a36Sopenharmony_ci	 */
388862306a36Sopenharmony_ci	WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
388962306a36Sopenharmony_ci	brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
389062306a36Sopenharmony_ci	if (!result_count) {
389162306a36Sopenharmony_ci		bphy_err(drvr, "FALSE PNO Event. (pfn_count == 0)\n");
389262306a36Sopenharmony_ci		goto out_err;
389362306a36Sopenharmony_ci	}
389462306a36Sopenharmony_ci
389562306a36Sopenharmony_ci	netinfo_start = brcmf_get_netinfo_array(pfn_result);
389662306a36Sopenharmony_ci	datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
389762306a36Sopenharmony_ci	if (datalen < result_count * sizeof(*netinfo)) {
389862306a36Sopenharmony_ci		bphy_err(drvr, "insufficient event data\n");
389962306a36Sopenharmony_ci		goto out_err;
390062306a36Sopenharmony_ci	}
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci	request = brcmf_alloc_internal_escan_request(wiphy,
390362306a36Sopenharmony_ci						     result_count);
390462306a36Sopenharmony_ci	if (!request) {
390562306a36Sopenharmony_ci		err = -ENOMEM;
390662306a36Sopenharmony_ci		goto out_err;
390762306a36Sopenharmony_ci	}
390862306a36Sopenharmony_ci
390962306a36Sopenharmony_ci	bucket_map = 0;
391062306a36Sopenharmony_ci	for (i = 0; i < result_count; i++) {
391162306a36Sopenharmony_ci		netinfo = &netinfo_start[i];
391262306a36Sopenharmony_ci
391362306a36Sopenharmony_ci		if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
391462306a36Sopenharmony_ci			netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
391562306a36Sopenharmony_ci		brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
391662306a36Sopenharmony_ci			  netinfo->SSID, netinfo->channel);
391762306a36Sopenharmony_ci		bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo);
391862306a36Sopenharmony_ci		err = brcmf_internal_escan_add_info(request,
391962306a36Sopenharmony_ci						    netinfo->SSID,
392062306a36Sopenharmony_ci						    netinfo->SSID_len,
392162306a36Sopenharmony_ci						    netinfo->channel);
392262306a36Sopenharmony_ci		if (err)
392362306a36Sopenharmony_ci			goto out_err;
392462306a36Sopenharmony_ci	}
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	if (!bucket_map)
392762306a36Sopenharmony_ci		goto free_req;
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci	err = brcmf_start_internal_escan(ifp, bucket_map, request);
393062306a36Sopenharmony_ci	if (!err)
393162306a36Sopenharmony_ci		goto free_req;
393262306a36Sopenharmony_ci
393362306a36Sopenharmony_ciout_err:
393462306a36Sopenharmony_ci	cfg80211_sched_scan_stopped(wiphy, 0);
393562306a36Sopenharmony_cifree_req:
393662306a36Sopenharmony_ci	kfree(request);
393762306a36Sopenharmony_ci	return err;
393862306a36Sopenharmony_ci}
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_cistatic int
394162306a36Sopenharmony_cibrcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
394262306a36Sopenharmony_ci				struct net_device *ndev,
394362306a36Sopenharmony_ci				struct cfg80211_sched_scan_request *req)
394462306a36Sopenharmony_ci{
394562306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
394662306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
394762306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci	brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n",
395062306a36Sopenharmony_ci		  req->n_match_sets, req->n_ssids);
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_ci	if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
395362306a36Sopenharmony_ci		bphy_err(drvr, "Scanning suppressed: status=%lu\n",
395462306a36Sopenharmony_ci			 cfg->scan_status);
395562306a36Sopenharmony_ci		return -EAGAIN;
395662306a36Sopenharmony_ci	}
395762306a36Sopenharmony_ci
395862306a36Sopenharmony_ci	if (req->n_match_sets <= 0) {
395962306a36Sopenharmony_ci		brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n",
396062306a36Sopenharmony_ci			  req->n_match_sets);
396162306a36Sopenharmony_ci		return -EINVAL;
396262306a36Sopenharmony_ci	}
396362306a36Sopenharmony_ci
396462306a36Sopenharmony_ci	return brcmf_pno_start_sched_scan(ifp, req);
396562306a36Sopenharmony_ci}
396662306a36Sopenharmony_ci
396762306a36Sopenharmony_cistatic int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
396862306a36Sopenharmony_ci					  struct net_device *ndev, u64 reqid)
396962306a36Sopenharmony_ci{
397062306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
397162306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
397262306a36Sopenharmony_ci
397362306a36Sopenharmony_ci	brcmf_dbg(SCAN, "enter\n");
397462306a36Sopenharmony_ci	brcmf_pno_stop_sched_scan(ifp, reqid);
397562306a36Sopenharmony_ci	if (cfg->int_escan_map)
397662306a36Sopenharmony_ci		brcmf_notify_escan_complete(cfg, ifp, true, true);
397762306a36Sopenharmony_ci	return 0;
397862306a36Sopenharmony_ci}
397962306a36Sopenharmony_ci
398062306a36Sopenharmony_cistatic __always_inline void brcmf_delay(u32 ms)
398162306a36Sopenharmony_ci{
398262306a36Sopenharmony_ci	if (ms < 1000 / HZ) {
398362306a36Sopenharmony_ci		cond_resched();
398462306a36Sopenharmony_ci		mdelay(ms);
398562306a36Sopenharmony_ci	} else {
398662306a36Sopenharmony_ci		msleep(ms);
398762306a36Sopenharmony_ci	}
398862306a36Sopenharmony_ci}
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_cistatic s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
399162306a36Sopenharmony_ci				     u8 *pattern, u32 patternsize, u8 *mask,
399262306a36Sopenharmony_ci				     u32 packet_offset)
399362306a36Sopenharmony_ci{
399462306a36Sopenharmony_ci	struct brcmf_fil_wowl_pattern_le *filter;
399562306a36Sopenharmony_ci	u32 masksize;
399662306a36Sopenharmony_ci	u32 patternoffset;
399762306a36Sopenharmony_ci	u8 *buf;
399862306a36Sopenharmony_ci	u32 bufsize;
399962306a36Sopenharmony_ci	s32 ret;
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci	masksize = (patternsize + 7) / 8;
400262306a36Sopenharmony_ci	patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
400362306a36Sopenharmony_ci
400462306a36Sopenharmony_ci	bufsize = sizeof(*filter) + patternsize + masksize;
400562306a36Sopenharmony_ci	buf = kzalloc(bufsize, GFP_KERNEL);
400662306a36Sopenharmony_ci	if (!buf)
400762306a36Sopenharmony_ci		return -ENOMEM;
400862306a36Sopenharmony_ci	filter = (struct brcmf_fil_wowl_pattern_le *)buf;
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci	memcpy(filter->cmd, cmd, 4);
401162306a36Sopenharmony_ci	filter->masksize = cpu_to_le32(masksize);
401262306a36Sopenharmony_ci	filter->offset = cpu_to_le32(packet_offset);
401362306a36Sopenharmony_ci	filter->patternoffset = cpu_to_le32(patternoffset);
401462306a36Sopenharmony_ci	filter->patternsize = cpu_to_le32(patternsize);
401562306a36Sopenharmony_ci	filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
401662306a36Sopenharmony_ci
401762306a36Sopenharmony_ci	if ((mask) && (masksize))
401862306a36Sopenharmony_ci		memcpy(buf + sizeof(*filter), mask, masksize);
401962306a36Sopenharmony_ci	if ((pattern) && (patternsize))
402062306a36Sopenharmony_ci		memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_ci	ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
402362306a36Sopenharmony_ci
402462306a36Sopenharmony_ci	kfree(buf);
402562306a36Sopenharmony_ci	return ret;
402662306a36Sopenharmony_ci}
402762306a36Sopenharmony_ci
402862306a36Sopenharmony_cistatic s32
402962306a36Sopenharmony_cibrcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
403062306a36Sopenharmony_ci		      void *data)
403162306a36Sopenharmony_ci{
403262306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
403362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = drvr->config;
403462306a36Sopenharmony_ci	struct brcmf_pno_scanresults_le *pfn_result;
403562306a36Sopenharmony_ci	struct brcmf_pno_net_info_le *netinfo;
403662306a36Sopenharmony_ci
403762306a36Sopenharmony_ci	brcmf_dbg(SCAN, "Enter\n");
403862306a36Sopenharmony_ci
403962306a36Sopenharmony_ci	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
404062306a36Sopenharmony_ci		brcmf_dbg(SCAN, "Event data to small. Ignore\n");
404162306a36Sopenharmony_ci		return 0;
404262306a36Sopenharmony_ci	}
404362306a36Sopenharmony_ci
404462306a36Sopenharmony_ci	pfn_result = (struct brcmf_pno_scanresults_le *)data;
404562306a36Sopenharmony_ci
404662306a36Sopenharmony_ci	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
404762306a36Sopenharmony_ci		brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
404862306a36Sopenharmony_ci		return 0;
404962306a36Sopenharmony_ci	}
405062306a36Sopenharmony_ci
405162306a36Sopenharmony_ci	if (le32_to_cpu(pfn_result->count) < 1) {
405262306a36Sopenharmony_ci		bphy_err(drvr, "Invalid result count, expected 1 (%d)\n",
405362306a36Sopenharmony_ci			 le32_to_cpu(pfn_result->count));
405462306a36Sopenharmony_ci		return -EINVAL;
405562306a36Sopenharmony_ci	}
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci	netinfo = brcmf_get_netinfo_array(pfn_result);
405862306a36Sopenharmony_ci	if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
405962306a36Sopenharmony_ci		netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
406062306a36Sopenharmony_ci	memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
406162306a36Sopenharmony_ci	cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
406262306a36Sopenharmony_ci	cfg->wowl.nd->n_channels = 1;
406362306a36Sopenharmony_ci	cfg->wowl.nd->channels[0] =
406462306a36Sopenharmony_ci		ieee80211_channel_to_frequency(netinfo->channel,
406562306a36Sopenharmony_ci			netinfo->channel <= CH_MAX_2G_CHANNEL ?
406662306a36Sopenharmony_ci					NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
406762306a36Sopenharmony_ci	cfg->wowl.nd_info->n_matches = 1;
406862306a36Sopenharmony_ci	cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci	/* Inform (the resume task) that the net detect information was recvd */
407162306a36Sopenharmony_ci	cfg->wowl.nd_data_completed = true;
407262306a36Sopenharmony_ci	wake_up(&cfg->wowl.nd_data_wait);
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	return 0;
407562306a36Sopenharmony_ci}
407662306a36Sopenharmony_ci
407762306a36Sopenharmony_ci#ifdef CONFIG_PM
407862306a36Sopenharmony_ci
407962306a36Sopenharmony_cistatic void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
408062306a36Sopenharmony_ci{
408162306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
408262306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
408362306a36Sopenharmony_ci	struct brcmf_wowl_wakeind_le wake_ind_le;
408462306a36Sopenharmony_ci	struct cfg80211_wowlan_wakeup wakeup_data;
408562306a36Sopenharmony_ci	struct cfg80211_wowlan_wakeup *wakeup;
408662306a36Sopenharmony_ci	u32 wakeind;
408762306a36Sopenharmony_ci	s32 err;
408862306a36Sopenharmony_ci	int timeout;
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
409162306a36Sopenharmony_ci				       sizeof(wake_ind_le));
409262306a36Sopenharmony_ci	if (err) {
409362306a36Sopenharmony_ci		bphy_err(drvr, "Get wowl_wakeind failed, err = %d\n", err);
409462306a36Sopenharmony_ci		return;
409562306a36Sopenharmony_ci	}
409662306a36Sopenharmony_ci
409762306a36Sopenharmony_ci	wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
409862306a36Sopenharmony_ci	if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
409962306a36Sopenharmony_ci		       BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
410062306a36Sopenharmony_ci		       BRCMF_WOWL_PFN_FOUND)) {
410162306a36Sopenharmony_ci		wakeup = &wakeup_data;
410262306a36Sopenharmony_ci		memset(&wakeup_data, 0, sizeof(wakeup_data));
410362306a36Sopenharmony_ci		wakeup_data.pattern_idx = -1;
410462306a36Sopenharmony_ci
410562306a36Sopenharmony_ci		if (wakeind & BRCMF_WOWL_MAGIC) {
410662306a36Sopenharmony_ci			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
410762306a36Sopenharmony_ci			wakeup_data.magic_pkt = true;
410862306a36Sopenharmony_ci		}
410962306a36Sopenharmony_ci		if (wakeind & BRCMF_WOWL_DIS) {
411062306a36Sopenharmony_ci			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
411162306a36Sopenharmony_ci			wakeup_data.disconnect = true;
411262306a36Sopenharmony_ci		}
411362306a36Sopenharmony_ci		if (wakeind & BRCMF_WOWL_BCN) {
411462306a36Sopenharmony_ci			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
411562306a36Sopenharmony_ci			wakeup_data.disconnect = true;
411662306a36Sopenharmony_ci		}
411762306a36Sopenharmony_ci		if (wakeind & BRCMF_WOWL_RETR) {
411862306a36Sopenharmony_ci			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
411962306a36Sopenharmony_ci			wakeup_data.disconnect = true;
412062306a36Sopenharmony_ci		}
412162306a36Sopenharmony_ci		if (wakeind & BRCMF_WOWL_NET) {
412262306a36Sopenharmony_ci			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
412362306a36Sopenharmony_ci			/* For now always map to pattern 0, no API to get
412462306a36Sopenharmony_ci			 * correct information available at the moment.
412562306a36Sopenharmony_ci			 */
412662306a36Sopenharmony_ci			wakeup_data.pattern_idx = 0;
412762306a36Sopenharmony_ci		}
412862306a36Sopenharmony_ci		if (wakeind & BRCMF_WOWL_PFN_FOUND) {
412962306a36Sopenharmony_ci			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
413062306a36Sopenharmony_ci			timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
413162306a36Sopenharmony_ci				cfg->wowl.nd_data_completed,
413262306a36Sopenharmony_ci				BRCMF_ND_INFO_TIMEOUT);
413362306a36Sopenharmony_ci			if (!timeout)
413462306a36Sopenharmony_ci				bphy_err(drvr, "No result for wowl net detect\n");
413562306a36Sopenharmony_ci			else
413662306a36Sopenharmony_ci				wakeup_data.net_detect = cfg->wowl.nd_info;
413762306a36Sopenharmony_ci		}
413862306a36Sopenharmony_ci		if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
413962306a36Sopenharmony_ci			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
414062306a36Sopenharmony_ci			wakeup_data.gtk_rekey_failure = true;
414162306a36Sopenharmony_ci		}
414262306a36Sopenharmony_ci	} else {
414362306a36Sopenharmony_ci		wakeup = NULL;
414462306a36Sopenharmony_ci	}
414562306a36Sopenharmony_ci	cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
414662306a36Sopenharmony_ci}
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci#else
414962306a36Sopenharmony_ci
415062306a36Sopenharmony_cistatic void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
415162306a36Sopenharmony_ci{
415262306a36Sopenharmony_ci}
415362306a36Sopenharmony_ci
415462306a36Sopenharmony_ci#endif /* CONFIG_PM */
415562306a36Sopenharmony_ci
415662306a36Sopenharmony_cistatic s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
415762306a36Sopenharmony_ci{
415862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
415962306a36Sopenharmony_ci	struct net_device *ndev = cfg_to_ndev(cfg);
416062306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
416162306a36Sopenharmony_ci
416262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	if (cfg->wowl.active) {
416562306a36Sopenharmony_ci		brcmf_report_wowl_wakeind(wiphy, ifp);
416662306a36Sopenharmony_ci		brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
416762306a36Sopenharmony_ci		brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
416862306a36Sopenharmony_ci		if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
416962306a36Sopenharmony_ci			brcmf_configure_arp_nd_offload(ifp, true);
417062306a36Sopenharmony_ci		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
417162306a36Sopenharmony_ci				      cfg->wowl.pre_pmmode);
417262306a36Sopenharmony_ci		cfg->wowl.active = false;
417362306a36Sopenharmony_ci		if (cfg->wowl.nd_enabled) {
417462306a36Sopenharmony_ci			brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0);
417562306a36Sopenharmony_ci			brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
417662306a36Sopenharmony_ci			brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
417762306a36Sopenharmony_ci					    brcmf_notify_sched_scan_results);
417862306a36Sopenharmony_ci			cfg->wowl.nd_enabled = false;
417962306a36Sopenharmony_ci		}
418062306a36Sopenharmony_ci	}
418162306a36Sopenharmony_ci	return 0;
418262306a36Sopenharmony_ci}
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_cistatic void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
418562306a36Sopenharmony_ci				 struct brcmf_if *ifp,
418662306a36Sopenharmony_ci				 struct cfg80211_wowlan *wowl)
418762306a36Sopenharmony_ci{
418862306a36Sopenharmony_ci	u32 wowl_config;
418962306a36Sopenharmony_ci	struct brcmf_wowl_wakeind_le wowl_wakeind;
419062306a36Sopenharmony_ci	u32 i;
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Suspend, wowl config.\n");
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
419562306a36Sopenharmony_ci		brcmf_configure_arp_nd_offload(ifp, false);
419662306a36Sopenharmony_ci	brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
419762306a36Sopenharmony_ci	brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
419862306a36Sopenharmony_ci
419962306a36Sopenharmony_ci	wowl_config = 0;
420062306a36Sopenharmony_ci	if (wowl->disconnect)
420162306a36Sopenharmony_ci		wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
420262306a36Sopenharmony_ci	if (wowl->magic_pkt)
420362306a36Sopenharmony_ci		wowl_config |= BRCMF_WOWL_MAGIC;
420462306a36Sopenharmony_ci	if ((wowl->patterns) && (wowl->n_patterns)) {
420562306a36Sopenharmony_ci		wowl_config |= BRCMF_WOWL_NET;
420662306a36Sopenharmony_ci		for (i = 0; i < wowl->n_patterns; i++) {
420762306a36Sopenharmony_ci			brcmf_config_wowl_pattern(ifp, "add",
420862306a36Sopenharmony_ci				(u8 *)wowl->patterns[i].pattern,
420962306a36Sopenharmony_ci				wowl->patterns[i].pattern_len,
421062306a36Sopenharmony_ci				(u8 *)wowl->patterns[i].mask,
421162306a36Sopenharmony_ci				wowl->patterns[i].pkt_offset);
421262306a36Sopenharmony_ci		}
421362306a36Sopenharmony_ci	}
421462306a36Sopenharmony_ci	if (wowl->nd_config) {
421562306a36Sopenharmony_ci		brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
421662306a36Sopenharmony_ci						wowl->nd_config);
421762306a36Sopenharmony_ci		wowl_config |= BRCMF_WOWL_PFN_FOUND;
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci		cfg->wowl.nd_data_completed = false;
422062306a36Sopenharmony_ci		cfg->wowl.nd_enabled = true;
422162306a36Sopenharmony_ci		/* Now reroute the event for PFN to the wowl function. */
422262306a36Sopenharmony_ci		brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
422362306a36Sopenharmony_ci		brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
422462306a36Sopenharmony_ci				    brcmf_wowl_nd_results);
422562306a36Sopenharmony_ci	}
422662306a36Sopenharmony_ci	if (wowl->gtk_rekey_failure)
422762306a36Sopenharmony_ci		wowl_config |= BRCMF_WOWL_GTK_FAILURE;
422862306a36Sopenharmony_ci	if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
422962306a36Sopenharmony_ci		wowl_config |= BRCMF_WOWL_UNASSOC;
423062306a36Sopenharmony_ci
423162306a36Sopenharmony_ci	memcpy(&wowl_wakeind, "clear", 6);
423262306a36Sopenharmony_ci	brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind,
423362306a36Sopenharmony_ci				 sizeof(wowl_wakeind));
423462306a36Sopenharmony_ci	brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
423562306a36Sopenharmony_ci	brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
423662306a36Sopenharmony_ci	brcmf_bus_wowl_config(cfg->pub->bus_if, true);
423762306a36Sopenharmony_ci	cfg->wowl.active = true;
423862306a36Sopenharmony_ci}
423962306a36Sopenharmony_ci
424062306a36Sopenharmony_cistatic int brcmf_keepalive_start(struct brcmf_if *ifp, unsigned int interval)
424162306a36Sopenharmony_ci{
424262306a36Sopenharmony_ci	struct brcmf_mkeep_alive_pkt_le kalive = {0};
424362306a36Sopenharmony_ci	int ret = 0;
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	/* Configure Null function/data keepalive */
424662306a36Sopenharmony_ci	kalive.version = cpu_to_le16(1);
424762306a36Sopenharmony_ci	kalive.period_msec = cpu_to_le32(interval * MSEC_PER_SEC);
424862306a36Sopenharmony_ci	kalive.len_bytes = cpu_to_le16(0);
424962306a36Sopenharmony_ci	kalive.keep_alive_id = 0;
425062306a36Sopenharmony_ci
425162306a36Sopenharmony_ci	ret = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &kalive, sizeof(kalive));
425262306a36Sopenharmony_ci	if (ret)
425362306a36Sopenharmony_ci		brcmf_err("keep-alive packet config failed, ret=%d\n", ret);
425462306a36Sopenharmony_ci
425562306a36Sopenharmony_ci	return ret;
425662306a36Sopenharmony_ci}
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_cistatic s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
425962306a36Sopenharmony_ci				  struct cfg80211_wowlan *wowl)
426062306a36Sopenharmony_ci{
426162306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
426262306a36Sopenharmony_ci	struct net_device *ndev = cfg_to_ndev(cfg);
426362306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
426462306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
426562306a36Sopenharmony_ci
426662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
426762306a36Sopenharmony_ci
426862306a36Sopenharmony_ci	/* if the primary net_device is not READY there is nothing
426962306a36Sopenharmony_ci	 * we can do but pray resume goes smoothly.
427062306a36Sopenharmony_ci	 */
427162306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
427262306a36Sopenharmony_ci		goto exit;
427362306a36Sopenharmony_ci
427462306a36Sopenharmony_ci	/* Stop scheduled scan */
427562306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
427662306a36Sopenharmony_ci		brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0);
427762306a36Sopenharmony_ci
427862306a36Sopenharmony_ci	/* end any scanning */
427962306a36Sopenharmony_ci	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
428062306a36Sopenharmony_ci		brcmf_abort_scanning(cfg);
428162306a36Sopenharmony_ci
428262306a36Sopenharmony_ci	if (wowl == NULL) {
428362306a36Sopenharmony_ci		brcmf_bus_wowl_config(cfg->pub->bus_if, false);
428462306a36Sopenharmony_ci		list_for_each_entry(vif, &cfg->vif_list, list) {
428562306a36Sopenharmony_ci			if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
428662306a36Sopenharmony_ci				continue;
428762306a36Sopenharmony_ci			/* While going to suspend if associated with AP
428862306a36Sopenharmony_ci			 * disassociate from AP to save power while system is
428962306a36Sopenharmony_ci			 * in suspended state
429062306a36Sopenharmony_ci			 */
429162306a36Sopenharmony_ci			brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED, true);
429262306a36Sopenharmony_ci			/* Make sure WPA_Supplicant receives all the event
429362306a36Sopenharmony_ci			 * generated due to DISASSOC call to the fw to keep
429462306a36Sopenharmony_ci			 * the state fw and WPA_Supplicant state consistent
429562306a36Sopenharmony_ci			 */
429662306a36Sopenharmony_ci			brcmf_delay(500);
429762306a36Sopenharmony_ci		}
429862306a36Sopenharmony_ci		/* Configure MPC */
429962306a36Sopenharmony_ci		brcmf_set_mpc(ifp, 1);
430062306a36Sopenharmony_ci
430162306a36Sopenharmony_ci	} else {
430262306a36Sopenharmony_ci		/* Configure WOWL paramaters */
430362306a36Sopenharmony_ci		brcmf_configure_wowl(cfg, ifp, wowl);
430462306a36Sopenharmony_ci
430562306a36Sopenharmony_ci		/* Prevent disassociation due to inactivity with keep-alive */
430662306a36Sopenharmony_ci		brcmf_keepalive_start(ifp, 30);
430762306a36Sopenharmony_ci	}
430862306a36Sopenharmony_ci
430962306a36Sopenharmony_ciexit:
431062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
431162306a36Sopenharmony_ci	/* clear any scanning activity */
431262306a36Sopenharmony_ci	cfg->scan_status = 0;
431362306a36Sopenharmony_ci	return 0;
431462306a36Sopenharmony_ci}
431562306a36Sopenharmony_ci
431662306a36Sopenharmony_cistatic s32
431762306a36Sopenharmony_cibrcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa,
431862306a36Sopenharmony_ci		  bool alive)
431962306a36Sopenharmony_ci{
432062306a36Sopenharmony_ci	struct brcmf_pmk_op_v3_le *pmk_op;
432162306a36Sopenharmony_ci	int length = offsetof(struct brcmf_pmk_op_v3_le, pmk);
432262306a36Sopenharmony_ci	int ret;
432362306a36Sopenharmony_ci
432462306a36Sopenharmony_ci	pmk_op = kzalloc(sizeof(*pmk_op), GFP_KERNEL);
432562306a36Sopenharmony_ci	if (!pmk_op)
432662306a36Sopenharmony_ci		return -ENOMEM;
432762306a36Sopenharmony_ci
432862306a36Sopenharmony_ci	pmk_op->version = cpu_to_le16(BRCMF_PMKSA_VER_3);
432962306a36Sopenharmony_ci
433062306a36Sopenharmony_ci	if (!pmksa) {
433162306a36Sopenharmony_ci		/* Flush operation, operate on entire list */
433262306a36Sopenharmony_ci		pmk_op->count = cpu_to_le16(0);
433362306a36Sopenharmony_ci	} else {
433462306a36Sopenharmony_ci		/* Single PMK operation */
433562306a36Sopenharmony_ci		pmk_op->count = cpu_to_le16(1);
433662306a36Sopenharmony_ci		length += sizeof(struct brcmf_pmksa_v3);
433762306a36Sopenharmony_ci		memcpy(pmk_op->pmk[0].bssid, pmksa->bssid, ETH_ALEN);
433862306a36Sopenharmony_ci		memcpy(pmk_op->pmk[0].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
433962306a36Sopenharmony_ci		pmk_op->pmk[0].pmkid_len = WLAN_PMKID_LEN;
434062306a36Sopenharmony_ci		pmk_op->pmk[0].time_left = cpu_to_le32(alive ? BRCMF_PMKSA_NO_EXPIRY : 0);
434162306a36Sopenharmony_ci	}
434262306a36Sopenharmony_ci
434362306a36Sopenharmony_ci	pmk_op->length = cpu_to_le16(length);
434462306a36Sopenharmony_ci
434562306a36Sopenharmony_ci	ret = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_op, sizeof(*pmk_op));
434662306a36Sopenharmony_ci	kfree(pmk_op);
434762306a36Sopenharmony_ci	return ret;
434862306a36Sopenharmony_ci}
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_cistatic __used s32
435162306a36Sopenharmony_cibrcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
435262306a36Sopenharmony_ci{
435362306a36Sopenharmony_ci	struct brcmf_pmk_list_le *pmk_list;
435462306a36Sopenharmony_ci	int i;
435562306a36Sopenharmony_ci	u32 npmk;
435662306a36Sopenharmony_ci
435762306a36Sopenharmony_ci	pmk_list = &cfg->pmk_list;
435862306a36Sopenharmony_ci	npmk = le32_to_cpu(pmk_list->npmk);
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_ci	brcmf_dbg(CONN, "No of elements %d\n", npmk);
436162306a36Sopenharmony_ci	for (i = 0; i < npmk; i++)
436262306a36Sopenharmony_ci		brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci	return brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
436562306a36Sopenharmony_ci			sizeof(*pmk_list));
436662306a36Sopenharmony_ci}
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_cistatic s32
436962306a36Sopenharmony_cibrcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
437062306a36Sopenharmony_ci			 struct cfg80211_pmksa *pmksa)
437162306a36Sopenharmony_ci{
437262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
437362306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
437462306a36Sopenharmony_ci	struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
437562306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
437662306a36Sopenharmony_ci	s32 err;
437762306a36Sopenharmony_ci	u32 npmk, i;
437862306a36Sopenharmony_ci
437962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
438062306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
438162306a36Sopenharmony_ci		return -EIO;
438262306a36Sopenharmony_ci
438362306a36Sopenharmony_ci	brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmksa->bssid);
438462306a36Sopenharmony_ci	brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmksa->pmkid);
438562306a36Sopenharmony_ci
438662306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
438762306a36Sopenharmony_ci		return brcmf_pmksa_v3_op(ifp, pmksa, true);
438862306a36Sopenharmony_ci
438962306a36Sopenharmony_ci	/* TODO: implement PMKID_V2 */
439062306a36Sopenharmony_ci
439162306a36Sopenharmony_ci	npmk = le32_to_cpu(cfg->pmk_list.npmk);
439262306a36Sopenharmony_ci	for (i = 0; i < npmk; i++)
439362306a36Sopenharmony_ci		if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
439462306a36Sopenharmony_ci			break;
439562306a36Sopenharmony_ci	if (i < BRCMF_MAXPMKID) {
439662306a36Sopenharmony_ci		memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
439762306a36Sopenharmony_ci		memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
439862306a36Sopenharmony_ci		if (i == npmk) {
439962306a36Sopenharmony_ci			npmk++;
440062306a36Sopenharmony_ci			cfg->pmk_list.npmk = cpu_to_le32(npmk);
440162306a36Sopenharmony_ci		}
440262306a36Sopenharmony_ci	} else {
440362306a36Sopenharmony_ci		bphy_err(drvr, "Too many PMKSA entries cached %d\n", npmk);
440462306a36Sopenharmony_ci		return -EINVAL;
440562306a36Sopenharmony_ci	}
440662306a36Sopenharmony_ci
440762306a36Sopenharmony_ci	err = brcmf_update_pmklist(cfg, ifp);
440862306a36Sopenharmony_ci
440962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
441062306a36Sopenharmony_ci	return err;
441162306a36Sopenharmony_ci}
441262306a36Sopenharmony_ci
441362306a36Sopenharmony_cistatic s32
441462306a36Sopenharmony_cibrcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
441562306a36Sopenharmony_ci			 struct cfg80211_pmksa *pmksa)
441662306a36Sopenharmony_ci{
441762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
441862306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
441962306a36Sopenharmony_ci	struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
442062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
442162306a36Sopenharmony_ci	s32 err;
442262306a36Sopenharmony_ci	u32 npmk, i;
442362306a36Sopenharmony_ci
442462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
442562306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
442662306a36Sopenharmony_ci		return -EIO;
442762306a36Sopenharmony_ci
442862306a36Sopenharmony_ci	brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid);
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
443162306a36Sopenharmony_ci		return brcmf_pmksa_v3_op(ifp, pmksa, false);
443262306a36Sopenharmony_ci
443362306a36Sopenharmony_ci	/* TODO: implement PMKID_V2 */
443462306a36Sopenharmony_ci
443562306a36Sopenharmony_ci	npmk = le32_to_cpu(cfg->pmk_list.npmk);
443662306a36Sopenharmony_ci	for (i = 0; i < npmk; i++)
443762306a36Sopenharmony_ci		if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
443862306a36Sopenharmony_ci			break;
443962306a36Sopenharmony_ci
444062306a36Sopenharmony_ci	if ((npmk > 0) && (i < npmk)) {
444162306a36Sopenharmony_ci		for (; i < (npmk - 1); i++) {
444262306a36Sopenharmony_ci			memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
444362306a36Sopenharmony_ci			memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
444462306a36Sopenharmony_ci			       WLAN_PMKID_LEN);
444562306a36Sopenharmony_ci		}
444662306a36Sopenharmony_ci		memset(&pmk[i], 0, sizeof(*pmk));
444762306a36Sopenharmony_ci		cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
444862306a36Sopenharmony_ci	} else {
444962306a36Sopenharmony_ci		bphy_err(drvr, "Cache entry not found\n");
445062306a36Sopenharmony_ci		return -EINVAL;
445162306a36Sopenharmony_ci	}
445262306a36Sopenharmony_ci
445362306a36Sopenharmony_ci	err = brcmf_update_pmklist(cfg, ifp);
445462306a36Sopenharmony_ci
445562306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
445662306a36Sopenharmony_ci	return err;
445762306a36Sopenharmony_ci
445862306a36Sopenharmony_ci}
445962306a36Sopenharmony_ci
446062306a36Sopenharmony_cistatic s32
446162306a36Sopenharmony_cibrcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
446262306a36Sopenharmony_ci{
446362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
446462306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
446562306a36Sopenharmony_ci	s32 err;
446662306a36Sopenharmony_ci
446762306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
446862306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
446962306a36Sopenharmony_ci		return -EIO;
447062306a36Sopenharmony_ci
447162306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
447262306a36Sopenharmony_ci		return brcmf_pmksa_v3_op(ifp, NULL, false);
447362306a36Sopenharmony_ci
447462306a36Sopenharmony_ci	/* TODO: implement PMKID_V2 */
447562306a36Sopenharmony_ci
447662306a36Sopenharmony_ci	memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
447762306a36Sopenharmony_ci	err = brcmf_update_pmklist(cfg, ifp);
447862306a36Sopenharmony_ci
447962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
448062306a36Sopenharmony_ci	return err;
448162306a36Sopenharmony_ci
448262306a36Sopenharmony_ci}
448362306a36Sopenharmony_ci
448462306a36Sopenharmony_cistatic s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
448562306a36Sopenharmony_ci{
448662306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
448762306a36Sopenharmony_ci	s32 err;
448862306a36Sopenharmony_ci	s32 wpa_val;
448962306a36Sopenharmony_ci
449062306a36Sopenharmony_ci	/* set auth */
449162306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
449262306a36Sopenharmony_ci	if (err < 0) {
449362306a36Sopenharmony_ci		bphy_err(drvr, "auth error %d\n", err);
449462306a36Sopenharmony_ci		return err;
449562306a36Sopenharmony_ci	}
449662306a36Sopenharmony_ci	/* set wsec */
449762306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
449862306a36Sopenharmony_ci	if (err < 0) {
449962306a36Sopenharmony_ci		bphy_err(drvr, "wsec error %d\n", err);
450062306a36Sopenharmony_ci		return err;
450162306a36Sopenharmony_ci	}
450262306a36Sopenharmony_ci	/* set upper-layer auth */
450362306a36Sopenharmony_ci	if (brcmf_is_ibssmode(ifp->vif))
450462306a36Sopenharmony_ci		wpa_val = WPA_AUTH_NONE;
450562306a36Sopenharmony_ci	else
450662306a36Sopenharmony_ci		wpa_val = WPA_AUTH_DISABLED;
450762306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val);
450862306a36Sopenharmony_ci	if (err < 0) {
450962306a36Sopenharmony_ci		bphy_err(drvr, "wpa_auth error %d\n", err);
451062306a36Sopenharmony_ci		return err;
451162306a36Sopenharmony_ci	}
451262306a36Sopenharmony_ci
451362306a36Sopenharmony_ci	return 0;
451462306a36Sopenharmony_ci}
451562306a36Sopenharmony_ci
451662306a36Sopenharmony_cistatic bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
451762306a36Sopenharmony_ci{
451862306a36Sopenharmony_ci	if (is_rsn_ie)
451962306a36Sopenharmony_ci		return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
452062306a36Sopenharmony_ci
452162306a36Sopenharmony_ci	return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
452262306a36Sopenharmony_ci}
452362306a36Sopenharmony_ci
452462306a36Sopenharmony_cistatic s32
452562306a36Sopenharmony_cibrcmf_configure_wpaie(struct brcmf_if *ifp,
452662306a36Sopenharmony_ci		      const struct brcmf_vs_tlv *wpa_ie,
452762306a36Sopenharmony_ci		      bool is_rsn_ie)
452862306a36Sopenharmony_ci{
452962306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
453062306a36Sopenharmony_ci	u32 auth = 0; /* d11 open authentication */
453162306a36Sopenharmony_ci	u16 count;
453262306a36Sopenharmony_ci	s32 err = 0;
453362306a36Sopenharmony_ci	s32 len;
453462306a36Sopenharmony_ci	u32 i;
453562306a36Sopenharmony_ci	u32 wsec;
453662306a36Sopenharmony_ci	u32 pval = 0;
453762306a36Sopenharmony_ci	u32 gval = 0;
453862306a36Sopenharmony_ci	u32 wpa_auth = 0;
453962306a36Sopenharmony_ci	u32 offset;
454062306a36Sopenharmony_ci	u8 *data;
454162306a36Sopenharmony_ci	u16 rsn_cap;
454262306a36Sopenharmony_ci	u32 wme_bss_disable;
454362306a36Sopenharmony_ci	u32 mfp;
454462306a36Sopenharmony_ci
454562306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
454662306a36Sopenharmony_ci	if (wpa_ie == NULL)
454762306a36Sopenharmony_ci		goto exit;
454862306a36Sopenharmony_ci
454962306a36Sopenharmony_ci	len = wpa_ie->len + TLV_HDR_LEN;
455062306a36Sopenharmony_ci	data = (u8 *)wpa_ie;
455162306a36Sopenharmony_ci	offset = TLV_HDR_LEN;
455262306a36Sopenharmony_ci	if (!is_rsn_ie)
455362306a36Sopenharmony_ci		offset += VS_IE_FIXED_HDR_LEN;
455462306a36Sopenharmony_ci	else
455562306a36Sopenharmony_ci		offset += WPA_IE_VERSION_LEN;
455662306a36Sopenharmony_ci
455762306a36Sopenharmony_ci	/* check for multicast cipher suite */
455862306a36Sopenharmony_ci	if (offset + WPA_IE_MIN_OUI_LEN > len) {
455962306a36Sopenharmony_ci		err = -EINVAL;
456062306a36Sopenharmony_ci		bphy_err(drvr, "no multicast cipher suite\n");
456162306a36Sopenharmony_ci		goto exit;
456262306a36Sopenharmony_ci	}
456362306a36Sopenharmony_ci
456462306a36Sopenharmony_ci	if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
456562306a36Sopenharmony_ci		err = -EINVAL;
456662306a36Sopenharmony_ci		bphy_err(drvr, "ivalid OUI\n");
456762306a36Sopenharmony_ci		goto exit;
456862306a36Sopenharmony_ci	}
456962306a36Sopenharmony_ci	offset += TLV_OUI_LEN;
457062306a36Sopenharmony_ci
457162306a36Sopenharmony_ci	/* pick up multicast cipher */
457262306a36Sopenharmony_ci	switch (data[offset]) {
457362306a36Sopenharmony_ci	case WPA_CIPHER_NONE:
457462306a36Sopenharmony_ci		gval = 0;
457562306a36Sopenharmony_ci		break;
457662306a36Sopenharmony_ci	case WPA_CIPHER_WEP_40:
457762306a36Sopenharmony_ci	case WPA_CIPHER_WEP_104:
457862306a36Sopenharmony_ci		gval = WEP_ENABLED;
457962306a36Sopenharmony_ci		break;
458062306a36Sopenharmony_ci	case WPA_CIPHER_TKIP:
458162306a36Sopenharmony_ci		gval = TKIP_ENABLED;
458262306a36Sopenharmony_ci		break;
458362306a36Sopenharmony_ci	case WPA_CIPHER_AES_CCM:
458462306a36Sopenharmony_ci		gval = AES_ENABLED;
458562306a36Sopenharmony_ci		break;
458662306a36Sopenharmony_ci	default:
458762306a36Sopenharmony_ci		err = -EINVAL;
458862306a36Sopenharmony_ci		bphy_err(drvr, "Invalid multi cast cipher info\n");
458962306a36Sopenharmony_ci		goto exit;
459062306a36Sopenharmony_ci	}
459162306a36Sopenharmony_ci
459262306a36Sopenharmony_ci	offset++;
459362306a36Sopenharmony_ci	/* walk thru unicast cipher list and pick up what we recognize */
459462306a36Sopenharmony_ci	count = data[offset] + (data[offset + 1] << 8);
459562306a36Sopenharmony_ci	offset += WPA_IE_SUITE_COUNT_LEN;
459662306a36Sopenharmony_ci	/* Check for unicast suite(s) */
459762306a36Sopenharmony_ci	if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
459862306a36Sopenharmony_ci		err = -EINVAL;
459962306a36Sopenharmony_ci		bphy_err(drvr, "no unicast cipher suite\n");
460062306a36Sopenharmony_ci		goto exit;
460162306a36Sopenharmony_ci	}
460262306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
460362306a36Sopenharmony_ci		if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
460462306a36Sopenharmony_ci			err = -EINVAL;
460562306a36Sopenharmony_ci			bphy_err(drvr, "ivalid OUI\n");
460662306a36Sopenharmony_ci			goto exit;
460762306a36Sopenharmony_ci		}
460862306a36Sopenharmony_ci		offset += TLV_OUI_LEN;
460962306a36Sopenharmony_ci		switch (data[offset]) {
461062306a36Sopenharmony_ci		case WPA_CIPHER_NONE:
461162306a36Sopenharmony_ci			break;
461262306a36Sopenharmony_ci		case WPA_CIPHER_WEP_40:
461362306a36Sopenharmony_ci		case WPA_CIPHER_WEP_104:
461462306a36Sopenharmony_ci			pval |= WEP_ENABLED;
461562306a36Sopenharmony_ci			break;
461662306a36Sopenharmony_ci		case WPA_CIPHER_TKIP:
461762306a36Sopenharmony_ci			pval |= TKIP_ENABLED;
461862306a36Sopenharmony_ci			break;
461962306a36Sopenharmony_ci		case WPA_CIPHER_AES_CCM:
462062306a36Sopenharmony_ci			pval |= AES_ENABLED;
462162306a36Sopenharmony_ci			break;
462262306a36Sopenharmony_ci		default:
462362306a36Sopenharmony_ci			bphy_err(drvr, "Invalid unicast security info\n");
462462306a36Sopenharmony_ci		}
462562306a36Sopenharmony_ci		offset++;
462662306a36Sopenharmony_ci	}
462762306a36Sopenharmony_ci	/* walk thru auth management suite list and pick up what we recognize */
462862306a36Sopenharmony_ci	count = data[offset] + (data[offset + 1] << 8);
462962306a36Sopenharmony_ci	offset += WPA_IE_SUITE_COUNT_LEN;
463062306a36Sopenharmony_ci	/* Check for auth key management suite(s) */
463162306a36Sopenharmony_ci	if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
463262306a36Sopenharmony_ci		err = -EINVAL;
463362306a36Sopenharmony_ci		bphy_err(drvr, "no auth key mgmt suite\n");
463462306a36Sopenharmony_ci		goto exit;
463562306a36Sopenharmony_ci	}
463662306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
463762306a36Sopenharmony_ci		if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
463862306a36Sopenharmony_ci			err = -EINVAL;
463962306a36Sopenharmony_ci			bphy_err(drvr, "ivalid OUI\n");
464062306a36Sopenharmony_ci			goto exit;
464162306a36Sopenharmony_ci		}
464262306a36Sopenharmony_ci		offset += TLV_OUI_LEN;
464362306a36Sopenharmony_ci		switch (data[offset]) {
464462306a36Sopenharmony_ci		case RSN_AKM_NONE:
464562306a36Sopenharmony_ci			brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
464662306a36Sopenharmony_ci			wpa_auth |= WPA_AUTH_NONE;
464762306a36Sopenharmony_ci			break;
464862306a36Sopenharmony_ci		case RSN_AKM_UNSPECIFIED:
464962306a36Sopenharmony_ci			brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
465062306a36Sopenharmony_ci			is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
465162306a36Sopenharmony_ci				    (wpa_auth |= WPA_AUTH_UNSPECIFIED);
465262306a36Sopenharmony_ci			break;
465362306a36Sopenharmony_ci		case RSN_AKM_PSK:
465462306a36Sopenharmony_ci			brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
465562306a36Sopenharmony_ci			is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
465662306a36Sopenharmony_ci				    (wpa_auth |= WPA_AUTH_PSK);
465762306a36Sopenharmony_ci			break;
465862306a36Sopenharmony_ci		case RSN_AKM_SHA256_PSK:
465962306a36Sopenharmony_ci			brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
466062306a36Sopenharmony_ci			wpa_auth |= WPA2_AUTH_PSK_SHA256;
466162306a36Sopenharmony_ci			break;
466262306a36Sopenharmony_ci		case RSN_AKM_SHA256_1X:
466362306a36Sopenharmony_ci			brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
466462306a36Sopenharmony_ci			wpa_auth |= WPA2_AUTH_1X_SHA256;
466562306a36Sopenharmony_ci			break;
466662306a36Sopenharmony_ci		case RSN_AKM_SAE:
466762306a36Sopenharmony_ci			brcmf_dbg(TRACE, "RSN_AKM_SAE\n");
466862306a36Sopenharmony_ci			wpa_auth |= WPA3_AUTH_SAE_PSK;
466962306a36Sopenharmony_ci			break;
467062306a36Sopenharmony_ci		default:
467162306a36Sopenharmony_ci			bphy_err(drvr, "Invalid key mgmt info\n");
467262306a36Sopenharmony_ci		}
467362306a36Sopenharmony_ci		offset++;
467462306a36Sopenharmony_ci	}
467562306a36Sopenharmony_ci
467662306a36Sopenharmony_ci	mfp = BRCMF_MFP_NONE;
467762306a36Sopenharmony_ci	if (is_rsn_ie) {
467862306a36Sopenharmony_ci		wme_bss_disable = 1;
467962306a36Sopenharmony_ci		if ((offset + RSN_CAP_LEN) <= len) {
468062306a36Sopenharmony_ci			rsn_cap = data[offset] + (data[offset + 1] << 8);
468162306a36Sopenharmony_ci			if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
468262306a36Sopenharmony_ci				wme_bss_disable = 0;
468362306a36Sopenharmony_ci			if (rsn_cap & RSN_CAP_MFPR_MASK) {
468462306a36Sopenharmony_ci				brcmf_dbg(TRACE, "MFP Required\n");
468562306a36Sopenharmony_ci				mfp = BRCMF_MFP_REQUIRED;
468662306a36Sopenharmony_ci				/* Firmware only supports mfp required in
468762306a36Sopenharmony_ci				 * combination with WPA2_AUTH_PSK_SHA256,
468862306a36Sopenharmony_ci				 * WPA2_AUTH_1X_SHA256, or WPA3_AUTH_SAE_PSK.
468962306a36Sopenharmony_ci				 */
469062306a36Sopenharmony_ci				if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
469162306a36Sopenharmony_ci						  WPA2_AUTH_1X_SHA256 |
469262306a36Sopenharmony_ci						  WPA3_AUTH_SAE_PSK))) {
469362306a36Sopenharmony_ci					err = -EINVAL;
469462306a36Sopenharmony_ci					goto exit;
469562306a36Sopenharmony_ci				}
469662306a36Sopenharmony_ci				/* Firmware has requirement that WPA2_AUTH_PSK/
469762306a36Sopenharmony_ci				 * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
469862306a36Sopenharmony_ci				 * is to be included in the rsn ie.
469962306a36Sopenharmony_ci				 */
470062306a36Sopenharmony_ci				if (wpa_auth & WPA2_AUTH_PSK_SHA256)
470162306a36Sopenharmony_ci					wpa_auth |= WPA2_AUTH_PSK;
470262306a36Sopenharmony_ci				else if (wpa_auth & WPA2_AUTH_1X_SHA256)
470362306a36Sopenharmony_ci					wpa_auth |= WPA2_AUTH_UNSPECIFIED;
470462306a36Sopenharmony_ci			} else if (rsn_cap & RSN_CAP_MFPC_MASK) {
470562306a36Sopenharmony_ci				brcmf_dbg(TRACE, "MFP Capable\n");
470662306a36Sopenharmony_ci				mfp = BRCMF_MFP_CAPABLE;
470762306a36Sopenharmony_ci			}
470862306a36Sopenharmony_ci		}
470962306a36Sopenharmony_ci		offset += RSN_CAP_LEN;
471062306a36Sopenharmony_ci		/* set wme_bss_disable to sync RSN Capabilities */
471162306a36Sopenharmony_ci		err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
471262306a36Sopenharmony_ci					       wme_bss_disable);
471362306a36Sopenharmony_ci		if (err < 0) {
471462306a36Sopenharmony_ci			bphy_err(drvr, "wme_bss_disable error %d\n", err);
471562306a36Sopenharmony_ci			goto exit;
471662306a36Sopenharmony_ci		}
471762306a36Sopenharmony_ci
471862306a36Sopenharmony_ci		/* Skip PMKID cnt as it is know to be 0 for AP. */
471962306a36Sopenharmony_ci		offset += RSN_PMKID_COUNT_LEN;
472062306a36Sopenharmony_ci
472162306a36Sopenharmony_ci		/* See if there is BIP wpa suite left for MFP */
472262306a36Sopenharmony_ci		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
472362306a36Sopenharmony_ci		    ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
472462306a36Sopenharmony_ci			err = brcmf_fil_bsscfg_data_set(ifp, "bip",
472562306a36Sopenharmony_ci							&data[offset],
472662306a36Sopenharmony_ci							WPA_IE_MIN_OUI_LEN);
472762306a36Sopenharmony_ci			if (err < 0) {
472862306a36Sopenharmony_ci				bphy_err(drvr, "bip error %d\n", err);
472962306a36Sopenharmony_ci				goto exit;
473062306a36Sopenharmony_ci			}
473162306a36Sopenharmony_ci		}
473262306a36Sopenharmony_ci	}
473362306a36Sopenharmony_ci	/* FOR WPS , set SES_OW_ENABLED */
473462306a36Sopenharmony_ci	wsec = (pval | gval | SES_OW_ENABLED);
473562306a36Sopenharmony_ci
473662306a36Sopenharmony_ci	/* set auth */
473762306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
473862306a36Sopenharmony_ci	if (err < 0) {
473962306a36Sopenharmony_ci		bphy_err(drvr, "auth error %d\n", err);
474062306a36Sopenharmony_ci		goto exit;
474162306a36Sopenharmony_ci	}
474262306a36Sopenharmony_ci	/* set wsec */
474362306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
474462306a36Sopenharmony_ci	if (err < 0) {
474562306a36Sopenharmony_ci		bphy_err(drvr, "wsec error %d\n", err);
474662306a36Sopenharmony_ci		goto exit;
474762306a36Sopenharmony_ci	}
474862306a36Sopenharmony_ci	/* Configure MFP, this needs to go after wsec otherwise the wsec command
474962306a36Sopenharmony_ci	 * will overwrite the values set by MFP
475062306a36Sopenharmony_ci	 */
475162306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
475262306a36Sopenharmony_ci		err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
475362306a36Sopenharmony_ci		if (err < 0) {
475462306a36Sopenharmony_ci			bphy_err(drvr, "mfp error %d\n", err);
475562306a36Sopenharmony_ci			goto exit;
475662306a36Sopenharmony_ci		}
475762306a36Sopenharmony_ci	}
475862306a36Sopenharmony_ci	/* set upper-layer auth */
475962306a36Sopenharmony_ci	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
476062306a36Sopenharmony_ci	if (err < 0) {
476162306a36Sopenharmony_ci		bphy_err(drvr, "wpa_auth error %d\n", err);
476262306a36Sopenharmony_ci		goto exit;
476362306a36Sopenharmony_ci	}
476462306a36Sopenharmony_ci
476562306a36Sopenharmony_ciexit:
476662306a36Sopenharmony_ci	return err;
476762306a36Sopenharmony_ci}
476862306a36Sopenharmony_ci
476962306a36Sopenharmony_cistatic s32
477062306a36Sopenharmony_cibrcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
477162306a36Sopenharmony_ci		     struct parsed_vndr_ies *vndr_ies)
477262306a36Sopenharmony_ci{
477362306a36Sopenharmony_ci	struct brcmf_vs_tlv *vndrie;
477462306a36Sopenharmony_ci	struct brcmf_tlv *ie;
477562306a36Sopenharmony_ci	struct parsed_vndr_ie_info *parsed_info;
477662306a36Sopenharmony_ci	s32 remaining_len;
477762306a36Sopenharmony_ci
477862306a36Sopenharmony_ci	remaining_len = (s32)vndr_ie_len;
477962306a36Sopenharmony_ci	memset(vndr_ies, 0, sizeof(*vndr_ies));
478062306a36Sopenharmony_ci
478162306a36Sopenharmony_ci	ie = (struct brcmf_tlv *)vndr_ie_buf;
478262306a36Sopenharmony_ci	while (ie) {
478362306a36Sopenharmony_ci		if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
478462306a36Sopenharmony_ci			goto next;
478562306a36Sopenharmony_ci		vndrie = (struct brcmf_vs_tlv *)ie;
478662306a36Sopenharmony_ci		/* len should be bigger than OUI length + one */
478762306a36Sopenharmony_ci		if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
478862306a36Sopenharmony_ci			brcmf_err("invalid vndr ie. length is too small %d\n",
478962306a36Sopenharmony_ci				  vndrie->len);
479062306a36Sopenharmony_ci			goto next;
479162306a36Sopenharmony_ci		}
479262306a36Sopenharmony_ci		/* if wpa or wme ie, do not add ie */
479362306a36Sopenharmony_ci		if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
479462306a36Sopenharmony_ci		    ((vndrie->oui_type == WPA_OUI_TYPE) ||
479562306a36Sopenharmony_ci		    (vndrie->oui_type == WME_OUI_TYPE))) {
479662306a36Sopenharmony_ci			brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
479762306a36Sopenharmony_ci			goto next;
479862306a36Sopenharmony_ci		}
479962306a36Sopenharmony_ci
480062306a36Sopenharmony_ci		parsed_info = &vndr_ies->ie_info[vndr_ies->count];
480162306a36Sopenharmony_ci
480262306a36Sopenharmony_ci		/* save vndr ie information */
480362306a36Sopenharmony_ci		parsed_info->ie_ptr = (char *)vndrie;
480462306a36Sopenharmony_ci		parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
480562306a36Sopenharmony_ci		memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci		vndr_ies->count++;
480862306a36Sopenharmony_ci
480962306a36Sopenharmony_ci		brcmf_dbg(TRACE, "** OUI %3ph, type 0x%02x\n",
481062306a36Sopenharmony_ci			  parsed_info->vndrie.oui,
481162306a36Sopenharmony_ci			  parsed_info->vndrie.oui_type);
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci		if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
481462306a36Sopenharmony_ci			break;
481562306a36Sopenharmony_cinext:
481662306a36Sopenharmony_ci		remaining_len -= (ie->len + TLV_HDR_LEN);
481762306a36Sopenharmony_ci		if (remaining_len <= TLV_HDR_LEN)
481862306a36Sopenharmony_ci			ie = NULL;
481962306a36Sopenharmony_ci		else
482062306a36Sopenharmony_ci			ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
482162306a36Sopenharmony_ci				TLV_HDR_LEN);
482262306a36Sopenharmony_ci	}
482362306a36Sopenharmony_ci	return 0;
482462306a36Sopenharmony_ci}
482562306a36Sopenharmony_ci
482662306a36Sopenharmony_cistatic u32
482762306a36Sopenharmony_cibrcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
482862306a36Sopenharmony_ci{
482962306a36Sopenharmony_ci	strscpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN);
483062306a36Sopenharmony_ci
483162306a36Sopenharmony_ci	put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
483462306a36Sopenharmony_ci
483562306a36Sopenharmony_ci	memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
483662306a36Sopenharmony_ci
483762306a36Sopenharmony_ci	return ie_len + VNDR_IE_HDR_SIZE;
483862306a36Sopenharmony_ci}
483962306a36Sopenharmony_ci
484062306a36Sopenharmony_cis32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
484162306a36Sopenharmony_ci			  const u8 *vndr_ie_buf, u32 vndr_ie_len)
484262306a36Sopenharmony_ci{
484362306a36Sopenharmony_ci	struct brcmf_pub *drvr;
484462306a36Sopenharmony_ci	struct brcmf_if *ifp;
484562306a36Sopenharmony_ci	struct vif_saved_ie *saved_ie;
484662306a36Sopenharmony_ci	s32 err = 0;
484762306a36Sopenharmony_ci	u8  *iovar_ie_buf;
484862306a36Sopenharmony_ci	u8  *curr_ie_buf;
484962306a36Sopenharmony_ci	u8  *mgmt_ie_buf = NULL;
485062306a36Sopenharmony_ci	int mgmt_ie_buf_len;
485162306a36Sopenharmony_ci	u32 *mgmt_ie_len;
485262306a36Sopenharmony_ci	u32 del_add_ie_buf_len = 0;
485362306a36Sopenharmony_ci	u32 total_ie_buf_len = 0;
485462306a36Sopenharmony_ci	u32 parsed_ie_buf_len = 0;
485562306a36Sopenharmony_ci	struct parsed_vndr_ies old_vndr_ies;
485662306a36Sopenharmony_ci	struct parsed_vndr_ies new_vndr_ies;
485762306a36Sopenharmony_ci	struct parsed_vndr_ie_info *vndrie_info;
485862306a36Sopenharmony_ci	s32 i;
485962306a36Sopenharmony_ci	u8 *ptr;
486062306a36Sopenharmony_ci	int remained_buf_len;
486162306a36Sopenharmony_ci
486262306a36Sopenharmony_ci	if (!vif)
486362306a36Sopenharmony_ci		return -ENODEV;
486462306a36Sopenharmony_ci	ifp = vif->ifp;
486562306a36Sopenharmony_ci	drvr = ifp->drvr;
486662306a36Sopenharmony_ci	saved_ie = &vif->saved_ie;
486762306a36Sopenharmony_ci
486862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
486962306a36Sopenharmony_ci		  pktflag);
487062306a36Sopenharmony_ci	iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
487162306a36Sopenharmony_ci	if (!iovar_ie_buf)
487262306a36Sopenharmony_ci		return -ENOMEM;
487362306a36Sopenharmony_ci	curr_ie_buf = iovar_ie_buf;
487462306a36Sopenharmony_ci	switch (pktflag) {
487562306a36Sopenharmony_ci	case BRCMF_VNDR_IE_PRBREQ_FLAG:
487662306a36Sopenharmony_ci		mgmt_ie_buf = saved_ie->probe_req_ie;
487762306a36Sopenharmony_ci		mgmt_ie_len = &saved_ie->probe_req_ie_len;
487862306a36Sopenharmony_ci		mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
487962306a36Sopenharmony_ci		break;
488062306a36Sopenharmony_ci	case BRCMF_VNDR_IE_PRBRSP_FLAG:
488162306a36Sopenharmony_ci		mgmt_ie_buf = saved_ie->probe_res_ie;
488262306a36Sopenharmony_ci		mgmt_ie_len = &saved_ie->probe_res_ie_len;
488362306a36Sopenharmony_ci		mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
488462306a36Sopenharmony_ci		break;
488562306a36Sopenharmony_ci	case BRCMF_VNDR_IE_BEACON_FLAG:
488662306a36Sopenharmony_ci		mgmt_ie_buf = saved_ie->beacon_ie;
488762306a36Sopenharmony_ci		mgmt_ie_len = &saved_ie->beacon_ie_len;
488862306a36Sopenharmony_ci		mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
488962306a36Sopenharmony_ci		break;
489062306a36Sopenharmony_ci	case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
489162306a36Sopenharmony_ci		mgmt_ie_buf = saved_ie->assoc_req_ie;
489262306a36Sopenharmony_ci		mgmt_ie_len = &saved_ie->assoc_req_ie_len;
489362306a36Sopenharmony_ci		mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
489462306a36Sopenharmony_ci		break;
489562306a36Sopenharmony_ci	case BRCMF_VNDR_IE_ASSOCRSP_FLAG:
489662306a36Sopenharmony_ci		mgmt_ie_buf = saved_ie->assoc_res_ie;
489762306a36Sopenharmony_ci		mgmt_ie_len = &saved_ie->assoc_res_ie_len;
489862306a36Sopenharmony_ci		mgmt_ie_buf_len = sizeof(saved_ie->assoc_res_ie);
489962306a36Sopenharmony_ci		break;
490062306a36Sopenharmony_ci	default:
490162306a36Sopenharmony_ci		err = -EPERM;
490262306a36Sopenharmony_ci		bphy_err(drvr, "not suitable type\n");
490362306a36Sopenharmony_ci		goto exit;
490462306a36Sopenharmony_ci	}
490562306a36Sopenharmony_ci
490662306a36Sopenharmony_ci	if (vndr_ie_len > mgmt_ie_buf_len) {
490762306a36Sopenharmony_ci		err = -ENOMEM;
490862306a36Sopenharmony_ci		bphy_err(drvr, "extra IE size too big\n");
490962306a36Sopenharmony_ci		goto exit;
491062306a36Sopenharmony_ci	}
491162306a36Sopenharmony_ci
491262306a36Sopenharmony_ci	/* parse and save new vndr_ie in curr_ie_buff before comparing it */
491362306a36Sopenharmony_ci	if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
491462306a36Sopenharmony_ci		ptr = curr_ie_buf;
491562306a36Sopenharmony_ci		brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
491662306a36Sopenharmony_ci		for (i = 0; i < new_vndr_ies.count; i++) {
491762306a36Sopenharmony_ci			vndrie_info = &new_vndr_ies.ie_info[i];
491862306a36Sopenharmony_ci			memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
491962306a36Sopenharmony_ci			       vndrie_info->ie_len);
492062306a36Sopenharmony_ci			parsed_ie_buf_len += vndrie_info->ie_len;
492162306a36Sopenharmony_ci		}
492262306a36Sopenharmony_ci	}
492362306a36Sopenharmony_ci
492462306a36Sopenharmony_ci	if (mgmt_ie_buf && *mgmt_ie_len) {
492562306a36Sopenharmony_ci		if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
492662306a36Sopenharmony_ci		    (memcmp(mgmt_ie_buf, curr_ie_buf,
492762306a36Sopenharmony_ci			    parsed_ie_buf_len) == 0)) {
492862306a36Sopenharmony_ci			brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
492962306a36Sopenharmony_ci			goto exit;
493062306a36Sopenharmony_ci		}
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci		/* parse old vndr_ie */
493362306a36Sopenharmony_ci		brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
493462306a36Sopenharmony_ci
493562306a36Sopenharmony_ci		/* make a command to delete old ie */
493662306a36Sopenharmony_ci		for (i = 0; i < old_vndr_ies.count; i++) {
493762306a36Sopenharmony_ci			vndrie_info = &old_vndr_ies.ie_info[i];
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci			brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%3ph\n",
494062306a36Sopenharmony_ci				  vndrie_info->vndrie.id,
494162306a36Sopenharmony_ci				  vndrie_info->vndrie.len,
494262306a36Sopenharmony_ci				  vndrie_info->vndrie.oui);
494362306a36Sopenharmony_ci
494462306a36Sopenharmony_ci			del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
494562306a36Sopenharmony_ci							   vndrie_info->ie_ptr,
494662306a36Sopenharmony_ci							   vndrie_info->ie_len,
494762306a36Sopenharmony_ci							   "del");
494862306a36Sopenharmony_ci			curr_ie_buf += del_add_ie_buf_len;
494962306a36Sopenharmony_ci			total_ie_buf_len += del_add_ie_buf_len;
495062306a36Sopenharmony_ci		}
495162306a36Sopenharmony_ci	}
495262306a36Sopenharmony_ci
495362306a36Sopenharmony_ci	*mgmt_ie_len = 0;
495462306a36Sopenharmony_ci	/* Add if there is any extra IE */
495562306a36Sopenharmony_ci	if (mgmt_ie_buf && parsed_ie_buf_len) {
495662306a36Sopenharmony_ci		ptr = mgmt_ie_buf;
495762306a36Sopenharmony_ci
495862306a36Sopenharmony_ci		remained_buf_len = mgmt_ie_buf_len;
495962306a36Sopenharmony_ci
496062306a36Sopenharmony_ci		/* make a command to add new ie */
496162306a36Sopenharmony_ci		for (i = 0; i < new_vndr_ies.count; i++) {
496262306a36Sopenharmony_ci			vndrie_info = &new_vndr_ies.ie_info[i];
496362306a36Sopenharmony_ci
496462306a36Sopenharmony_ci			/* verify remained buf size before copy data */
496562306a36Sopenharmony_ci			if (remained_buf_len < (vndrie_info->vndrie.len +
496662306a36Sopenharmony_ci							VNDR_IE_VSIE_OFFSET)) {
496762306a36Sopenharmony_ci				bphy_err(drvr, "no space in mgmt_ie_buf: len left %d",
496862306a36Sopenharmony_ci					 remained_buf_len);
496962306a36Sopenharmony_ci				break;
497062306a36Sopenharmony_ci			}
497162306a36Sopenharmony_ci			remained_buf_len -= (vndrie_info->ie_len +
497262306a36Sopenharmony_ci					     VNDR_IE_VSIE_OFFSET);
497362306a36Sopenharmony_ci
497462306a36Sopenharmony_ci			brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%3ph\n",
497562306a36Sopenharmony_ci				  vndrie_info->vndrie.id,
497662306a36Sopenharmony_ci				  vndrie_info->vndrie.len,
497762306a36Sopenharmony_ci				  vndrie_info->vndrie.oui);
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci			del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
498062306a36Sopenharmony_ci							   vndrie_info->ie_ptr,
498162306a36Sopenharmony_ci							   vndrie_info->ie_len,
498262306a36Sopenharmony_ci							   "add");
498362306a36Sopenharmony_ci
498462306a36Sopenharmony_ci			/* save the parsed IE in wl struct */
498562306a36Sopenharmony_ci			memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
498662306a36Sopenharmony_ci			       vndrie_info->ie_len);
498762306a36Sopenharmony_ci			*mgmt_ie_len += vndrie_info->ie_len;
498862306a36Sopenharmony_ci
498962306a36Sopenharmony_ci			curr_ie_buf += del_add_ie_buf_len;
499062306a36Sopenharmony_ci			total_ie_buf_len += del_add_ie_buf_len;
499162306a36Sopenharmony_ci		}
499262306a36Sopenharmony_ci	}
499362306a36Sopenharmony_ci	if (total_ie_buf_len) {
499462306a36Sopenharmony_ci		err  = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
499562306a36Sopenharmony_ci						 total_ie_buf_len);
499662306a36Sopenharmony_ci		if (err)
499762306a36Sopenharmony_ci			bphy_err(drvr, "vndr ie set error : %d\n", err);
499862306a36Sopenharmony_ci	}
499962306a36Sopenharmony_ci
500062306a36Sopenharmony_ciexit:
500162306a36Sopenharmony_ci	kfree(iovar_ie_buf);
500262306a36Sopenharmony_ci	return err;
500362306a36Sopenharmony_ci}
500462306a36Sopenharmony_ci
500562306a36Sopenharmony_cis32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
500662306a36Sopenharmony_ci{
500762306a36Sopenharmony_ci	static const s32 pktflags[] = {
500862306a36Sopenharmony_ci		BRCMF_VNDR_IE_PRBREQ_FLAG,
500962306a36Sopenharmony_ci		BRCMF_VNDR_IE_PRBRSP_FLAG,
501062306a36Sopenharmony_ci		BRCMF_VNDR_IE_BEACON_FLAG
501162306a36Sopenharmony_ci	};
501262306a36Sopenharmony_ci	int i;
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(pktflags); i++)
501562306a36Sopenharmony_ci		brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_ci	memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
501862306a36Sopenharmony_ci	return 0;
501962306a36Sopenharmony_ci}
502062306a36Sopenharmony_ci
502162306a36Sopenharmony_cistatic s32
502262306a36Sopenharmony_cibrcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
502362306a36Sopenharmony_ci			struct cfg80211_beacon_data *beacon)
502462306a36Sopenharmony_ci{
502562306a36Sopenharmony_ci	struct brcmf_pub *drvr = vif->ifp->drvr;
502662306a36Sopenharmony_ci	s32 err;
502762306a36Sopenharmony_ci
502862306a36Sopenharmony_ci	/* Set Beacon IEs to FW */
502962306a36Sopenharmony_ci	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
503062306a36Sopenharmony_ci				    beacon->tail, beacon->tail_len);
503162306a36Sopenharmony_ci	if (err) {
503262306a36Sopenharmony_ci		bphy_err(drvr, "Set Beacon IE Failed\n");
503362306a36Sopenharmony_ci		return err;
503462306a36Sopenharmony_ci	}
503562306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci	/* Set Probe Response IEs to FW */
503862306a36Sopenharmony_ci	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
503962306a36Sopenharmony_ci				    beacon->proberesp_ies,
504062306a36Sopenharmony_ci				    beacon->proberesp_ies_len);
504162306a36Sopenharmony_ci	if (err)
504262306a36Sopenharmony_ci		bphy_err(drvr, "Set Probe Resp IE Failed\n");
504362306a36Sopenharmony_ci	else
504462306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
504562306a36Sopenharmony_ci
504662306a36Sopenharmony_ci	/* Set Assoc Response IEs to FW */
504762306a36Sopenharmony_ci	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG,
504862306a36Sopenharmony_ci				    beacon->assocresp_ies,
504962306a36Sopenharmony_ci				    beacon->assocresp_ies_len);
505062306a36Sopenharmony_ci	if (err)
505162306a36Sopenharmony_ci		brcmf_err("Set Assoc Resp IE Failed\n");
505262306a36Sopenharmony_ci	else
505362306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc Resp\n");
505462306a36Sopenharmony_ci
505562306a36Sopenharmony_ci	return err;
505662306a36Sopenharmony_ci}
505762306a36Sopenharmony_ci
505862306a36Sopenharmony_cistatic s32
505962306a36Sopenharmony_cibrcmf_parse_configure_security(struct brcmf_if *ifp,
506062306a36Sopenharmony_ci			       struct cfg80211_ap_settings *settings,
506162306a36Sopenharmony_ci			       enum nl80211_iftype dev_role)
506262306a36Sopenharmony_ci{
506362306a36Sopenharmony_ci	const struct brcmf_tlv *rsn_ie;
506462306a36Sopenharmony_ci	const struct brcmf_vs_tlv *wpa_ie;
506562306a36Sopenharmony_ci	s32 err = 0;
506662306a36Sopenharmony_ci
506762306a36Sopenharmony_ci	/* find the RSN_IE */
506862306a36Sopenharmony_ci	rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
506962306a36Sopenharmony_ci				  settings->beacon.tail_len, WLAN_EID_RSN);
507062306a36Sopenharmony_ci
507162306a36Sopenharmony_ci	/* find the WPA_IE */
507262306a36Sopenharmony_ci	wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
507362306a36Sopenharmony_ci				  settings->beacon.tail_len);
507462306a36Sopenharmony_ci
507562306a36Sopenharmony_ci	if (wpa_ie || rsn_ie) {
507662306a36Sopenharmony_ci		brcmf_dbg(TRACE, "WPA(2) IE is found\n");
507762306a36Sopenharmony_ci		if (wpa_ie) {
507862306a36Sopenharmony_ci			/* WPA IE */
507962306a36Sopenharmony_ci			err = brcmf_configure_wpaie(ifp, wpa_ie, false);
508062306a36Sopenharmony_ci			if (err < 0)
508162306a36Sopenharmony_ci				return err;
508262306a36Sopenharmony_ci		} else {
508362306a36Sopenharmony_ci			struct brcmf_vs_tlv *tmp_ie;
508462306a36Sopenharmony_ci
508562306a36Sopenharmony_ci			tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
508662306a36Sopenharmony_ci
508762306a36Sopenharmony_ci			/* RSN IE */
508862306a36Sopenharmony_ci			err = brcmf_configure_wpaie(ifp, tmp_ie, true);
508962306a36Sopenharmony_ci			if (err < 0)
509062306a36Sopenharmony_ci				return err;
509162306a36Sopenharmony_ci		}
509262306a36Sopenharmony_ci	} else {
509362306a36Sopenharmony_ci		brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
509462306a36Sopenharmony_ci		brcmf_configure_opensecurity(ifp);
509562306a36Sopenharmony_ci	}
509662306a36Sopenharmony_ci
509762306a36Sopenharmony_ci	return err;
509862306a36Sopenharmony_ci}
509962306a36Sopenharmony_ci
510062306a36Sopenharmony_cistatic s32
510162306a36Sopenharmony_cibrcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
510262306a36Sopenharmony_ci			struct cfg80211_ap_settings *settings)
510362306a36Sopenharmony_ci{
510462306a36Sopenharmony_ci	s32 ie_offset;
510562306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
510662306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
510762306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
510862306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
510962306a36Sopenharmony_ci	struct cfg80211_crypto_settings *crypto = &settings->crypto;
511062306a36Sopenharmony_ci	const struct brcmf_tlv *ssid_ie;
511162306a36Sopenharmony_ci	const struct brcmf_tlv *country_ie;
511262306a36Sopenharmony_ci	struct brcmf_ssid_le ssid_le;
511362306a36Sopenharmony_ci	s32 err = -EPERM;
511462306a36Sopenharmony_ci	struct brcmf_join_params join_params;
511562306a36Sopenharmony_ci	enum nl80211_iftype dev_role;
511662306a36Sopenharmony_ci	struct brcmf_fil_bss_enable_le bss_enable;
511762306a36Sopenharmony_ci	u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
511862306a36Sopenharmony_ci	bool mbss;
511962306a36Sopenharmony_ci	int is_11d;
512062306a36Sopenharmony_ci	bool supports_11d;
512162306a36Sopenharmony_ci
512262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
512362306a36Sopenharmony_ci		  settings->chandef.chan->hw_value,
512462306a36Sopenharmony_ci		  settings->chandef.center_freq1, settings->chandef.width,
512562306a36Sopenharmony_ci		  settings->beacon_interval, settings->dtim_period);
512662306a36Sopenharmony_ci	brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
512762306a36Sopenharmony_ci		  settings->ssid, settings->ssid_len, settings->auth_type,
512862306a36Sopenharmony_ci		  settings->inactivity_timeout);
512962306a36Sopenharmony_ci	dev_role = ifp->vif->wdev.iftype;
513062306a36Sopenharmony_ci	mbss = ifp->vif->mbss;
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_ci	/* store current 11d setting */
513362306a36Sopenharmony_ci	if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY,
513462306a36Sopenharmony_ci				  &ifp->vif->is_11d)) {
513562306a36Sopenharmony_ci		is_11d = supports_11d = false;
513662306a36Sopenharmony_ci	} else {
513762306a36Sopenharmony_ci		country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
513862306a36Sopenharmony_ci					      settings->beacon.tail_len,
513962306a36Sopenharmony_ci					      WLAN_EID_COUNTRY);
514062306a36Sopenharmony_ci		is_11d = country_ie ? 1 : 0;
514162306a36Sopenharmony_ci		supports_11d = true;
514262306a36Sopenharmony_ci	}
514362306a36Sopenharmony_ci
514462306a36Sopenharmony_ci	memset(&ssid_le, 0, sizeof(ssid_le));
514562306a36Sopenharmony_ci	if (settings->ssid == NULL || settings->ssid_len == 0) {
514662306a36Sopenharmony_ci		ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
514762306a36Sopenharmony_ci		ssid_ie = brcmf_parse_tlvs(
514862306a36Sopenharmony_ci				(u8 *)&settings->beacon.head[ie_offset],
514962306a36Sopenharmony_ci				settings->beacon.head_len - ie_offset,
515062306a36Sopenharmony_ci				WLAN_EID_SSID);
515162306a36Sopenharmony_ci		if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
515262306a36Sopenharmony_ci			return -EINVAL;
515362306a36Sopenharmony_ci
515462306a36Sopenharmony_ci		memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
515562306a36Sopenharmony_ci		ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
515662306a36Sopenharmony_ci		brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
515762306a36Sopenharmony_ci	} else {
515862306a36Sopenharmony_ci		memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
515962306a36Sopenharmony_ci		ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
516062306a36Sopenharmony_ci	}
516162306a36Sopenharmony_ci
516262306a36Sopenharmony_ci	if (!mbss) {
516362306a36Sopenharmony_ci		brcmf_set_mpc(ifp, 0);
516462306a36Sopenharmony_ci		brcmf_configure_arp_nd_offload(ifp, false);
516562306a36Sopenharmony_ci	}
516662306a36Sopenharmony_ci
516762306a36Sopenharmony_ci	/* Parameters shared by all radio interfaces */
516862306a36Sopenharmony_ci	if (!mbss) {
516962306a36Sopenharmony_ci		if ((supports_11d) && (is_11d != ifp->vif->is_11d)) {
517062306a36Sopenharmony_ci			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
517162306a36Sopenharmony_ci						    is_11d);
517262306a36Sopenharmony_ci			if (err < 0) {
517362306a36Sopenharmony_ci				bphy_err(drvr, "Regulatory Set Error, %d\n",
517462306a36Sopenharmony_ci					 err);
517562306a36Sopenharmony_ci				goto exit;
517662306a36Sopenharmony_ci			}
517762306a36Sopenharmony_ci		}
517862306a36Sopenharmony_ci		if (settings->beacon_interval) {
517962306a36Sopenharmony_ci			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
518062306a36Sopenharmony_ci						    settings->beacon_interval);
518162306a36Sopenharmony_ci			if (err < 0) {
518262306a36Sopenharmony_ci				bphy_err(drvr, "Beacon Interval Set Error, %d\n",
518362306a36Sopenharmony_ci					 err);
518462306a36Sopenharmony_ci				goto exit;
518562306a36Sopenharmony_ci			}
518662306a36Sopenharmony_ci		}
518762306a36Sopenharmony_ci		if (settings->dtim_period) {
518862306a36Sopenharmony_ci			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
518962306a36Sopenharmony_ci						    settings->dtim_period);
519062306a36Sopenharmony_ci			if (err < 0) {
519162306a36Sopenharmony_ci				bphy_err(drvr, "DTIM Interval Set Error, %d\n",
519262306a36Sopenharmony_ci					 err);
519362306a36Sopenharmony_ci				goto exit;
519462306a36Sopenharmony_ci			}
519562306a36Sopenharmony_ci		}
519662306a36Sopenharmony_ci
519762306a36Sopenharmony_ci		if ((dev_role == NL80211_IFTYPE_AP) &&
519862306a36Sopenharmony_ci		    ((ifp->ifidx == 0) ||
519962306a36Sopenharmony_ci		     (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB) &&
520062306a36Sopenharmony_ci		      !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)))) {
520162306a36Sopenharmony_ci			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
520262306a36Sopenharmony_ci			if (err < 0) {
520362306a36Sopenharmony_ci				bphy_err(drvr, "BRCMF_C_DOWN error %d\n",
520462306a36Sopenharmony_ci					 err);
520562306a36Sopenharmony_ci				goto exit;
520662306a36Sopenharmony_ci			}
520762306a36Sopenharmony_ci			brcmf_fil_iovar_int_set(ifp, "apsta", 0);
520862306a36Sopenharmony_ci		}
520962306a36Sopenharmony_ci
521062306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
521162306a36Sopenharmony_ci		if (err < 0) {
521262306a36Sopenharmony_ci			bphy_err(drvr, "SET INFRA error %d\n", err);
521362306a36Sopenharmony_ci			goto exit;
521462306a36Sopenharmony_ci		}
521562306a36Sopenharmony_ci	} else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) {
521662306a36Sopenharmony_ci		/* Multiple-BSS should use same 11d configuration */
521762306a36Sopenharmony_ci		err = -EINVAL;
521862306a36Sopenharmony_ci		goto exit;
521962306a36Sopenharmony_ci	}
522062306a36Sopenharmony_ci
522162306a36Sopenharmony_ci	/* Interface specific setup */
522262306a36Sopenharmony_ci	if (dev_role == NL80211_IFTYPE_AP) {
522362306a36Sopenharmony_ci		if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
522462306a36Sopenharmony_ci			brcmf_fil_iovar_int_set(ifp, "mbss", 1);
522562306a36Sopenharmony_ci
522662306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
522762306a36Sopenharmony_ci		if (err < 0) {
522862306a36Sopenharmony_ci			bphy_err(drvr, "setting AP mode failed %d\n",
522962306a36Sopenharmony_ci				 err);
523062306a36Sopenharmony_ci			goto exit;
523162306a36Sopenharmony_ci		}
523262306a36Sopenharmony_ci		if (!mbss) {
523362306a36Sopenharmony_ci			/* Firmware 10.x requires setting channel after enabling
523462306a36Sopenharmony_ci			 * AP and before bringing interface up.
523562306a36Sopenharmony_ci			 */
523662306a36Sopenharmony_ci			err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
523762306a36Sopenharmony_ci			if (err < 0) {
523862306a36Sopenharmony_ci				bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
523962306a36Sopenharmony_ci					 chanspec, err);
524062306a36Sopenharmony_ci				goto exit;
524162306a36Sopenharmony_ci			}
524262306a36Sopenharmony_ci		}
524362306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
524462306a36Sopenharmony_ci		if (err < 0) {
524562306a36Sopenharmony_ci			bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err);
524662306a36Sopenharmony_ci			goto exit;
524762306a36Sopenharmony_ci		}
524862306a36Sopenharmony_ci
524962306a36Sopenharmony_ci		if (crypto->psk) {
525062306a36Sopenharmony_ci			brcmf_dbg(INFO, "using PSK offload\n");
525162306a36Sopenharmony_ci			profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_PSK);
525262306a36Sopenharmony_ci			err = brcmf_set_pmk(ifp, crypto->psk,
525362306a36Sopenharmony_ci					    BRCMF_WSEC_MAX_PSK_LEN);
525462306a36Sopenharmony_ci			if (err < 0)
525562306a36Sopenharmony_ci				goto exit;
525662306a36Sopenharmony_ci		}
525762306a36Sopenharmony_ci		if (crypto->sae_pwd) {
525862306a36Sopenharmony_ci			brcmf_dbg(INFO, "using SAE offload\n");
525962306a36Sopenharmony_ci			profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE);
526062306a36Sopenharmony_ci			err = brcmf_set_sae_password(ifp, crypto->sae_pwd,
526162306a36Sopenharmony_ci						     crypto->sae_pwd_len);
526262306a36Sopenharmony_ci			if (err < 0)
526362306a36Sopenharmony_ci				goto exit;
526462306a36Sopenharmony_ci		}
526562306a36Sopenharmony_ci		if (profile->use_fwauth == 0)
526662306a36Sopenharmony_ci			profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
526762306a36Sopenharmony_ci
526862306a36Sopenharmony_ci		err = brcmf_parse_configure_security(ifp, settings,
526962306a36Sopenharmony_ci						     NL80211_IFTYPE_AP);
527062306a36Sopenharmony_ci		if (err < 0) {
527162306a36Sopenharmony_ci			bphy_err(drvr, "brcmf_parse_configure_security error\n");
527262306a36Sopenharmony_ci			goto exit;
527362306a36Sopenharmony_ci		}
527462306a36Sopenharmony_ci
527562306a36Sopenharmony_ci		/* On DOWN the firmware removes the WEP keys, reconfigure
527662306a36Sopenharmony_ci		 * them if they were set.
527762306a36Sopenharmony_ci		 */
527862306a36Sopenharmony_ci		brcmf_cfg80211_reconfigure_wep(ifp);
527962306a36Sopenharmony_ci
528062306a36Sopenharmony_ci		memset(&join_params, 0, sizeof(join_params));
528162306a36Sopenharmony_ci		/* join parameters starts with ssid */
528262306a36Sopenharmony_ci		memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
528362306a36Sopenharmony_ci		/* create softap */
528462306a36Sopenharmony_ci		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
528562306a36Sopenharmony_ci					     &join_params, sizeof(join_params));
528662306a36Sopenharmony_ci		if (err < 0) {
528762306a36Sopenharmony_ci			bphy_err(drvr, "SET SSID error (%d)\n", err);
528862306a36Sopenharmony_ci			goto exit;
528962306a36Sopenharmony_ci		}
529062306a36Sopenharmony_ci
529162306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_set(ifp, "closednet",
529262306a36Sopenharmony_ci					      settings->hidden_ssid);
529362306a36Sopenharmony_ci		if (err) {
529462306a36Sopenharmony_ci			bphy_err(drvr, "%s closednet error (%d)\n",
529562306a36Sopenharmony_ci				 settings->hidden_ssid ?
529662306a36Sopenharmony_ci				 "enabled" : "disabled",
529762306a36Sopenharmony_ci				 err);
529862306a36Sopenharmony_ci			goto exit;
529962306a36Sopenharmony_ci		}
530062306a36Sopenharmony_ci
530162306a36Sopenharmony_ci		brcmf_dbg(TRACE, "AP mode configuration complete\n");
530262306a36Sopenharmony_ci	} else if (dev_role == NL80211_IFTYPE_P2P_GO) {
530362306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
530462306a36Sopenharmony_ci		if (err < 0) {
530562306a36Sopenharmony_ci			bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
530662306a36Sopenharmony_ci				 chanspec, err);
530762306a36Sopenharmony_ci			goto exit;
530862306a36Sopenharmony_ci		}
530962306a36Sopenharmony_ci
531062306a36Sopenharmony_ci		err = brcmf_parse_configure_security(ifp, settings,
531162306a36Sopenharmony_ci						     NL80211_IFTYPE_P2P_GO);
531262306a36Sopenharmony_ci		if (err < 0) {
531362306a36Sopenharmony_ci			brcmf_err("brcmf_parse_configure_security error\n");
531462306a36Sopenharmony_ci			goto exit;
531562306a36Sopenharmony_ci		}
531662306a36Sopenharmony_ci
531762306a36Sopenharmony_ci		err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
531862306a36Sopenharmony_ci						sizeof(ssid_le));
531962306a36Sopenharmony_ci		if (err < 0) {
532062306a36Sopenharmony_ci			bphy_err(drvr, "setting ssid failed %d\n", err);
532162306a36Sopenharmony_ci			goto exit;
532262306a36Sopenharmony_ci		}
532362306a36Sopenharmony_ci		bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
532462306a36Sopenharmony_ci		bss_enable.enable = cpu_to_le32(1);
532562306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
532662306a36Sopenharmony_ci					       sizeof(bss_enable));
532762306a36Sopenharmony_ci		if (err < 0) {
532862306a36Sopenharmony_ci			bphy_err(drvr, "bss_enable config failed %d\n", err);
532962306a36Sopenharmony_ci			goto exit;
533062306a36Sopenharmony_ci		}
533162306a36Sopenharmony_ci
533262306a36Sopenharmony_ci		brcmf_dbg(TRACE, "GO mode configuration complete\n");
533362306a36Sopenharmony_ci	} else {
533462306a36Sopenharmony_ci		WARN_ON(1);
533562306a36Sopenharmony_ci	}
533662306a36Sopenharmony_ci
533762306a36Sopenharmony_ci	brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
533862306a36Sopenharmony_ci	set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
533962306a36Sopenharmony_ci	brcmf_net_setcarrier(ifp, true);
534062306a36Sopenharmony_ci
534162306a36Sopenharmony_ciexit:
534262306a36Sopenharmony_ci	if ((err) && (!mbss)) {
534362306a36Sopenharmony_ci		brcmf_set_mpc(ifp, 1);
534462306a36Sopenharmony_ci		brcmf_configure_arp_nd_offload(ifp, true);
534562306a36Sopenharmony_ci	}
534662306a36Sopenharmony_ci	return err;
534762306a36Sopenharmony_ci}
534862306a36Sopenharmony_ci
534962306a36Sopenharmony_cistatic int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev,
535062306a36Sopenharmony_ci				  unsigned int link_id)
535162306a36Sopenharmony_ci{
535262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
535362306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
535462306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
535562306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
535662306a36Sopenharmony_ci	s32 err;
535762306a36Sopenharmony_ci	struct brcmf_fil_bss_enable_le bss_enable;
535862306a36Sopenharmony_ci	struct brcmf_join_params join_params;
535962306a36Sopenharmony_ci
536062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
536162306a36Sopenharmony_ci
536262306a36Sopenharmony_ci	if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
536362306a36Sopenharmony_ci		/* Due to most likely deauths outstanding we sleep */
536462306a36Sopenharmony_ci		/* first to make sure they get processed by fw. */
536562306a36Sopenharmony_ci		msleep(400);
536662306a36Sopenharmony_ci
536762306a36Sopenharmony_ci		if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) {
536862306a36Sopenharmony_ci			if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK))
536962306a36Sopenharmony_ci				brcmf_set_pmk(ifp, NULL, 0);
537062306a36Sopenharmony_ci			if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE))
537162306a36Sopenharmony_ci				brcmf_set_sae_password(ifp, NULL, 0);
537262306a36Sopenharmony_ci			profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
537362306a36Sopenharmony_ci		}
537462306a36Sopenharmony_ci
537562306a36Sopenharmony_ci		if (ifp->vif->mbss) {
537662306a36Sopenharmony_ci			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
537762306a36Sopenharmony_ci			return err;
537862306a36Sopenharmony_ci		}
537962306a36Sopenharmony_ci
538062306a36Sopenharmony_ci		/* First BSS doesn't get a full reset */
538162306a36Sopenharmony_ci		if (ifp->bsscfgidx == 0)
538262306a36Sopenharmony_ci			brcmf_fil_iovar_int_set(ifp, "closednet", 0);
538362306a36Sopenharmony_ci
538462306a36Sopenharmony_ci		memset(&join_params, 0, sizeof(join_params));
538562306a36Sopenharmony_ci		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
538662306a36Sopenharmony_ci					     &join_params, sizeof(join_params));
538762306a36Sopenharmony_ci		if (err < 0)
538862306a36Sopenharmony_ci			bphy_err(drvr, "SET SSID error (%d)\n", err);
538962306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
539062306a36Sopenharmony_ci		if (err < 0)
539162306a36Sopenharmony_ci			bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err);
539262306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
539362306a36Sopenharmony_ci		if (err < 0)
539462306a36Sopenharmony_ci			bphy_err(drvr, "setting AP mode failed %d\n", err);
539562306a36Sopenharmony_ci		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
539662306a36Sopenharmony_ci			brcmf_fil_iovar_int_set(ifp, "mbss", 0);
539762306a36Sopenharmony_ci		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
539862306a36Sopenharmony_ci				      ifp->vif->is_11d);
539962306a36Sopenharmony_ci		/* Bring device back up so it can be used again */
540062306a36Sopenharmony_ci		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
540162306a36Sopenharmony_ci		if (err < 0)
540262306a36Sopenharmony_ci			bphy_err(drvr, "BRCMF_C_UP error %d\n", err);
540362306a36Sopenharmony_ci
540462306a36Sopenharmony_ci		brcmf_vif_clear_mgmt_ies(ifp->vif);
540562306a36Sopenharmony_ci	} else {
540662306a36Sopenharmony_ci		bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
540762306a36Sopenharmony_ci		bss_enable.enable = cpu_to_le32(0);
540862306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
540962306a36Sopenharmony_ci					       sizeof(bss_enable));
541062306a36Sopenharmony_ci		if (err < 0)
541162306a36Sopenharmony_ci			bphy_err(drvr, "bss_enable config failed %d\n", err);
541262306a36Sopenharmony_ci	}
541362306a36Sopenharmony_ci	brcmf_set_mpc(ifp, 1);
541462306a36Sopenharmony_ci	brcmf_configure_arp_nd_offload(ifp, true);
541562306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
541662306a36Sopenharmony_ci	brcmf_net_setcarrier(ifp, false);
541762306a36Sopenharmony_ci
541862306a36Sopenharmony_ci	return err;
541962306a36Sopenharmony_ci}
542062306a36Sopenharmony_ci
542162306a36Sopenharmony_cistatic s32
542262306a36Sopenharmony_cibrcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
542362306a36Sopenharmony_ci			     struct cfg80211_beacon_data *info)
542462306a36Sopenharmony_ci{
542562306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
542662306a36Sopenharmony_ci
542762306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
542862306a36Sopenharmony_ci
542962306a36Sopenharmony_ci	return brcmf_config_ap_mgmt_ie(ifp->vif, info);
543062306a36Sopenharmony_ci}
543162306a36Sopenharmony_ci
543262306a36Sopenharmony_cistatic int
543362306a36Sopenharmony_cibrcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
543462306a36Sopenharmony_ci			   struct station_del_parameters *params)
543562306a36Sopenharmony_ci{
543662306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
543762306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
543862306a36Sopenharmony_ci	struct brcmf_scb_val_le scbval;
543962306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
544062306a36Sopenharmony_ci	s32 err;
544162306a36Sopenharmony_ci
544262306a36Sopenharmony_ci	if (!params->mac)
544362306a36Sopenharmony_ci		return -EFAULT;
544462306a36Sopenharmony_ci
544562306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
544662306a36Sopenharmony_ci
544762306a36Sopenharmony_ci	if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
544862306a36Sopenharmony_ci		ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
544962306a36Sopenharmony_ci	if (!check_vif_up(ifp->vif))
545062306a36Sopenharmony_ci		return -EIO;
545162306a36Sopenharmony_ci
545262306a36Sopenharmony_ci	memcpy(&scbval.ea, params->mac, ETH_ALEN);
545362306a36Sopenharmony_ci	scbval.val = cpu_to_le32(params->reason_code);
545462306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
545562306a36Sopenharmony_ci				     &scbval, sizeof(scbval));
545662306a36Sopenharmony_ci	if (err)
545762306a36Sopenharmony_ci		bphy_err(drvr, "SCB_DEAUTHENTICATE_FOR_REASON failed %d\n",
545862306a36Sopenharmony_ci			 err);
545962306a36Sopenharmony_ci
546062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
546162306a36Sopenharmony_ci	return err;
546262306a36Sopenharmony_ci}
546362306a36Sopenharmony_ci
546462306a36Sopenharmony_cistatic int
546562306a36Sopenharmony_cibrcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
546662306a36Sopenharmony_ci			      const u8 *mac, struct station_parameters *params)
546762306a36Sopenharmony_ci{
546862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
546962306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
547062306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
547162306a36Sopenharmony_ci	s32 err;
547262306a36Sopenharmony_ci
547362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
547462306a36Sopenharmony_ci		  params->sta_flags_mask, params->sta_flags_set);
547562306a36Sopenharmony_ci
547662306a36Sopenharmony_ci	/* Ignore all 00 MAC */
547762306a36Sopenharmony_ci	if (is_zero_ether_addr(mac))
547862306a36Sopenharmony_ci		return 0;
547962306a36Sopenharmony_ci
548062306a36Sopenharmony_ci	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
548162306a36Sopenharmony_ci		return 0;
548262306a36Sopenharmony_ci
548362306a36Sopenharmony_ci	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
548462306a36Sopenharmony_ci		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
548562306a36Sopenharmony_ci					     (void *)mac, ETH_ALEN);
548662306a36Sopenharmony_ci	else
548762306a36Sopenharmony_ci		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
548862306a36Sopenharmony_ci					     (void *)mac, ETH_ALEN);
548962306a36Sopenharmony_ci	if (err < 0)
549062306a36Sopenharmony_ci		bphy_err(drvr, "Setting SCB (de-)authorize failed, %d\n", err);
549162306a36Sopenharmony_ci
549262306a36Sopenharmony_ci	return err;
549362306a36Sopenharmony_ci}
549462306a36Sopenharmony_ci
549562306a36Sopenharmony_cistatic void
549662306a36Sopenharmony_cibrcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
549762306a36Sopenharmony_ci					       struct wireless_dev *wdev,
549862306a36Sopenharmony_ci					       struct mgmt_frame_regs *upd)
549962306a36Sopenharmony_ci{
550062306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
550162306a36Sopenharmony_ci
550262306a36Sopenharmony_ci	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
550362306a36Sopenharmony_ci
550462306a36Sopenharmony_ci	vif->mgmt_rx_reg = upd->interface_stypes;
550562306a36Sopenharmony_ci}
550662306a36Sopenharmony_ci
550762306a36Sopenharmony_ci
550862306a36Sopenharmony_cistatic int
550962306a36Sopenharmony_cibrcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
551062306a36Sopenharmony_ci		       struct cfg80211_mgmt_tx_params *params, u64 *cookie)
551162306a36Sopenharmony_ci{
551262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
551362306a36Sopenharmony_ci	struct ieee80211_channel *chan = params->chan;
551462306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
551562306a36Sopenharmony_ci	const u8 *buf = params->buf;
551662306a36Sopenharmony_ci	size_t len = params->len;
551762306a36Sopenharmony_ci	const struct ieee80211_mgmt *mgmt;
551862306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
551962306a36Sopenharmony_ci	s32 err = 0;
552062306a36Sopenharmony_ci	s32 ie_offset;
552162306a36Sopenharmony_ci	s32 ie_len;
552262306a36Sopenharmony_ci	struct brcmf_fil_action_frame_le *action_frame;
552362306a36Sopenharmony_ci	struct brcmf_fil_af_params_le *af_params;
552462306a36Sopenharmony_ci	bool ack;
552562306a36Sopenharmony_ci	s32 chan_nr;
552662306a36Sopenharmony_ci	u32 freq;
552762306a36Sopenharmony_ci
552862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
552962306a36Sopenharmony_ci
553062306a36Sopenharmony_ci	*cookie = 0;
553162306a36Sopenharmony_ci
553262306a36Sopenharmony_ci	mgmt = (const struct ieee80211_mgmt *)buf;
553362306a36Sopenharmony_ci
553462306a36Sopenharmony_ci	if (!ieee80211_is_mgmt(mgmt->frame_control)) {
553562306a36Sopenharmony_ci		bphy_err(drvr, "Driver only allows MGMT packet type\n");
553662306a36Sopenharmony_ci		return -EPERM;
553762306a36Sopenharmony_ci	}
553862306a36Sopenharmony_ci
553962306a36Sopenharmony_ci	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
554062306a36Sopenharmony_ci
554162306a36Sopenharmony_ci	if (ieee80211_is_probe_resp(mgmt->frame_control)) {
554262306a36Sopenharmony_ci		/* Right now the only reason to get a probe response */
554362306a36Sopenharmony_ci		/* is for p2p listen response or for p2p GO from     */
554462306a36Sopenharmony_ci		/* wpa_supplicant. Unfortunately the probe is send   */
554562306a36Sopenharmony_ci		/* on primary ndev, while dongle wants it on the p2p */
554662306a36Sopenharmony_ci		/* vif. Since this is only reason for a probe        */
554762306a36Sopenharmony_ci		/* response to be sent, the vif is taken from cfg.   */
554862306a36Sopenharmony_ci		/* If ever desired to send proberesp for non p2p     */
554962306a36Sopenharmony_ci		/* response then data should be checked for          */
555062306a36Sopenharmony_ci		/* "DIRECT-". Note in future supplicant will take    */
555162306a36Sopenharmony_ci		/* dedicated p2p wdev to do this and then this 'hack'*/
555262306a36Sopenharmony_ci		/* is not needed anymore.                            */
555362306a36Sopenharmony_ci		ie_offset =  DOT11_MGMT_HDR_LEN +
555462306a36Sopenharmony_ci			     DOT11_BCN_PRB_FIXED_LEN;
555562306a36Sopenharmony_ci		ie_len = len - ie_offset;
555662306a36Sopenharmony_ci		if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
555762306a36Sopenharmony_ci			vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
555862306a36Sopenharmony_ci		err = brcmf_vif_set_mgmt_ie(vif,
555962306a36Sopenharmony_ci					    BRCMF_VNDR_IE_PRBRSP_FLAG,
556062306a36Sopenharmony_ci					    &buf[ie_offset],
556162306a36Sopenharmony_ci					    ie_len);
556262306a36Sopenharmony_ci		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
556362306a36Sopenharmony_ci					GFP_KERNEL);
556462306a36Sopenharmony_ci	} else if (ieee80211_is_action(mgmt->frame_control)) {
556562306a36Sopenharmony_ci		if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) {
556662306a36Sopenharmony_ci			bphy_err(drvr, "invalid action frame length\n");
556762306a36Sopenharmony_ci			err = -EINVAL;
556862306a36Sopenharmony_ci			goto exit;
556962306a36Sopenharmony_ci		}
557062306a36Sopenharmony_ci		af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
557162306a36Sopenharmony_ci		if (af_params == NULL) {
557262306a36Sopenharmony_ci			bphy_err(drvr, "unable to allocate frame\n");
557362306a36Sopenharmony_ci			err = -ENOMEM;
557462306a36Sopenharmony_ci			goto exit;
557562306a36Sopenharmony_ci		}
557662306a36Sopenharmony_ci		action_frame = &af_params->action_frame;
557762306a36Sopenharmony_ci		/* Add the packet Id */
557862306a36Sopenharmony_ci		action_frame->packet_id = cpu_to_le32(*cookie);
557962306a36Sopenharmony_ci		/* Add BSSID */
558062306a36Sopenharmony_ci		memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
558162306a36Sopenharmony_ci		memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
558262306a36Sopenharmony_ci		/* Add the length exepted for 802.11 header  */
558362306a36Sopenharmony_ci		action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
558462306a36Sopenharmony_ci		/* Add the channel. Use the one specified as parameter if any or
558562306a36Sopenharmony_ci		 * the current one (got from the firmware) otherwise
558662306a36Sopenharmony_ci		 */
558762306a36Sopenharmony_ci		if (chan)
558862306a36Sopenharmony_ci			freq = chan->center_freq;
558962306a36Sopenharmony_ci		else
559062306a36Sopenharmony_ci			brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
559162306a36Sopenharmony_ci					      &freq);
559262306a36Sopenharmony_ci		chan_nr = ieee80211_frequency_to_channel(freq);
559362306a36Sopenharmony_ci		af_params->channel = cpu_to_le32(chan_nr);
559462306a36Sopenharmony_ci		af_params->dwell_time = cpu_to_le32(params->wait);
559562306a36Sopenharmony_ci		memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
559662306a36Sopenharmony_ci		       le16_to_cpu(action_frame->len));
559762306a36Sopenharmony_ci
559862306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
559962306a36Sopenharmony_ci			  *cookie, le16_to_cpu(action_frame->len), freq);
560062306a36Sopenharmony_ci
560162306a36Sopenharmony_ci		ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
560262306a36Sopenharmony_ci						  af_params);
560362306a36Sopenharmony_ci
560462306a36Sopenharmony_ci		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
560562306a36Sopenharmony_ci					GFP_KERNEL);
560662306a36Sopenharmony_ci		kfree(af_params);
560762306a36Sopenharmony_ci	} else {
560862306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
560962306a36Sopenharmony_ci		brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len);
561062306a36Sopenharmony_ci	}
561162306a36Sopenharmony_ci
561262306a36Sopenharmony_ciexit:
561362306a36Sopenharmony_ci	return err;
561462306a36Sopenharmony_ci}
561562306a36Sopenharmony_ci
561662306a36Sopenharmony_cistatic int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
561762306a36Sopenharmony_ci						    struct net_device *ndev,
561862306a36Sopenharmony_ci						    s32 rssi_low, s32 rssi_high)
561962306a36Sopenharmony_ci{
562062306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
562162306a36Sopenharmony_ci	struct brcmf_if *ifp;
562262306a36Sopenharmony_ci	int err = 0;
562362306a36Sopenharmony_ci
562462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "low=%d high=%d", rssi_low, rssi_high);
562562306a36Sopenharmony_ci
562662306a36Sopenharmony_ci	ifp = netdev_priv(ndev);
562762306a36Sopenharmony_ci	vif = ifp->vif;
562862306a36Sopenharmony_ci
562962306a36Sopenharmony_ci	if (rssi_low != vif->cqm_rssi_low || rssi_high != vif->cqm_rssi_high) {
563062306a36Sopenharmony_ci		/* The firmware will send an event when the RSSI is less than or
563162306a36Sopenharmony_ci		 * equal to a configured level and the previous RSSI event was
563262306a36Sopenharmony_ci		 * less than or equal to a different level. Set a third level
563362306a36Sopenharmony_ci		 * so that we also detect the transition from rssi <= rssi_high
563462306a36Sopenharmony_ci		 * to rssi > rssi_high.
563562306a36Sopenharmony_ci		 */
563662306a36Sopenharmony_ci		struct brcmf_rssi_event_le config = {
563762306a36Sopenharmony_ci			.rate_limit_msec = cpu_to_le32(0),
563862306a36Sopenharmony_ci			.rssi_level_num = 3,
563962306a36Sopenharmony_ci			.rssi_levels = {
564062306a36Sopenharmony_ci				clamp_val(rssi_low, S8_MIN, S8_MAX - 2),
564162306a36Sopenharmony_ci				clamp_val(rssi_high, S8_MIN + 1, S8_MAX - 1),
564262306a36Sopenharmony_ci				S8_MAX,
564362306a36Sopenharmony_ci			},
564462306a36Sopenharmony_ci		};
564562306a36Sopenharmony_ci
564662306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &config,
564762306a36Sopenharmony_ci					       sizeof(config));
564862306a36Sopenharmony_ci		if (err) {
564962306a36Sopenharmony_ci			err = -EINVAL;
565062306a36Sopenharmony_ci		} else {
565162306a36Sopenharmony_ci			vif->cqm_rssi_low = rssi_low;
565262306a36Sopenharmony_ci			vif->cqm_rssi_high = rssi_high;
565362306a36Sopenharmony_ci		}
565462306a36Sopenharmony_ci	}
565562306a36Sopenharmony_ci
565662306a36Sopenharmony_ci	return err;
565762306a36Sopenharmony_ci}
565862306a36Sopenharmony_ci
565962306a36Sopenharmony_cistatic int
566062306a36Sopenharmony_cibrcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
566162306a36Sopenharmony_ci					struct wireless_dev *wdev,
566262306a36Sopenharmony_ci					u64 cookie)
566362306a36Sopenharmony_ci{
566462306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
566562306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
566662306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
566762306a36Sopenharmony_ci	int err = 0;
566862306a36Sopenharmony_ci
566962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
567062306a36Sopenharmony_ci
567162306a36Sopenharmony_ci	vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
567262306a36Sopenharmony_ci	if (vif == NULL) {
567362306a36Sopenharmony_ci		bphy_err(drvr, "No p2p device available for probe response\n");
567462306a36Sopenharmony_ci		err = -ENODEV;
567562306a36Sopenharmony_ci		goto exit;
567662306a36Sopenharmony_ci	}
567762306a36Sopenharmony_ci	brcmf_p2p_cancel_remain_on_channel(vif->ifp);
567862306a36Sopenharmony_ciexit:
567962306a36Sopenharmony_ci	return err;
568062306a36Sopenharmony_ci}
568162306a36Sopenharmony_ci
568262306a36Sopenharmony_cistatic int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
568362306a36Sopenharmony_ci				      struct wireless_dev *wdev,
568462306a36Sopenharmony_ci				      unsigned int link_id,
568562306a36Sopenharmony_ci				      struct cfg80211_chan_def *chandef)
568662306a36Sopenharmony_ci{
568762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
568862306a36Sopenharmony_ci	struct net_device *ndev = wdev->netdev;
568962306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
569062306a36Sopenharmony_ci	struct brcmu_chan ch;
569162306a36Sopenharmony_ci	enum nl80211_band band = 0;
569262306a36Sopenharmony_ci	enum nl80211_chan_width width = 0;
569362306a36Sopenharmony_ci	u32 chanspec;
569462306a36Sopenharmony_ci	int freq, err;
569562306a36Sopenharmony_ci
569662306a36Sopenharmony_ci	if (!ndev || drvr->bus_if->state != BRCMF_BUS_UP)
569762306a36Sopenharmony_ci		return -ENODEV;
569862306a36Sopenharmony_ci
569962306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_get(netdev_priv(ndev), "chanspec", &chanspec);
570062306a36Sopenharmony_ci	if (err) {
570162306a36Sopenharmony_ci		bphy_err(drvr, "chanspec failed (%d)\n", err);
570262306a36Sopenharmony_ci		return err;
570362306a36Sopenharmony_ci	}
570462306a36Sopenharmony_ci
570562306a36Sopenharmony_ci	ch.chspec = chanspec;
570662306a36Sopenharmony_ci	cfg->d11inf.decchspec(&ch);
570762306a36Sopenharmony_ci
570862306a36Sopenharmony_ci	switch (ch.band) {
570962306a36Sopenharmony_ci	case BRCMU_CHAN_BAND_2G:
571062306a36Sopenharmony_ci		band = NL80211_BAND_2GHZ;
571162306a36Sopenharmony_ci		break;
571262306a36Sopenharmony_ci	case BRCMU_CHAN_BAND_5G:
571362306a36Sopenharmony_ci		band = NL80211_BAND_5GHZ;
571462306a36Sopenharmony_ci		break;
571562306a36Sopenharmony_ci	}
571662306a36Sopenharmony_ci
571762306a36Sopenharmony_ci	switch (ch.bw) {
571862306a36Sopenharmony_ci	case BRCMU_CHAN_BW_80:
571962306a36Sopenharmony_ci		width = NL80211_CHAN_WIDTH_80;
572062306a36Sopenharmony_ci		break;
572162306a36Sopenharmony_ci	case BRCMU_CHAN_BW_40:
572262306a36Sopenharmony_ci		width = NL80211_CHAN_WIDTH_40;
572362306a36Sopenharmony_ci		break;
572462306a36Sopenharmony_ci	case BRCMU_CHAN_BW_20:
572562306a36Sopenharmony_ci		width = NL80211_CHAN_WIDTH_20;
572662306a36Sopenharmony_ci		break;
572762306a36Sopenharmony_ci	case BRCMU_CHAN_BW_80P80:
572862306a36Sopenharmony_ci		width = NL80211_CHAN_WIDTH_80P80;
572962306a36Sopenharmony_ci		break;
573062306a36Sopenharmony_ci	case BRCMU_CHAN_BW_160:
573162306a36Sopenharmony_ci		width = NL80211_CHAN_WIDTH_160;
573262306a36Sopenharmony_ci		break;
573362306a36Sopenharmony_ci	}
573462306a36Sopenharmony_ci
573562306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
573662306a36Sopenharmony_ci	chandef->chan = ieee80211_get_channel(wiphy, freq);
573762306a36Sopenharmony_ci	chandef->width = width;
573862306a36Sopenharmony_ci	chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);
573962306a36Sopenharmony_ci	chandef->center_freq2 = 0;
574062306a36Sopenharmony_ci
574162306a36Sopenharmony_ci	return 0;
574262306a36Sopenharmony_ci}
574362306a36Sopenharmony_ci
574462306a36Sopenharmony_cistatic int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
574562306a36Sopenharmony_ci					   struct wireless_dev *wdev,
574662306a36Sopenharmony_ci					   enum nl80211_crit_proto_id proto,
574762306a36Sopenharmony_ci					   u16 duration)
574862306a36Sopenharmony_ci{
574962306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
575062306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
575162306a36Sopenharmony_ci
575262306a36Sopenharmony_ci	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
575362306a36Sopenharmony_ci
575462306a36Sopenharmony_ci	/* only DHCP support for now */
575562306a36Sopenharmony_ci	if (proto != NL80211_CRIT_PROTO_DHCP)
575662306a36Sopenharmony_ci		return -EINVAL;
575762306a36Sopenharmony_ci
575862306a36Sopenharmony_ci	/* suppress and abort scanning */
575962306a36Sopenharmony_ci	set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
576062306a36Sopenharmony_ci	brcmf_abort_scanning(cfg);
576162306a36Sopenharmony_ci
576262306a36Sopenharmony_ci	return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
576362306a36Sopenharmony_ci}
576462306a36Sopenharmony_ci
576562306a36Sopenharmony_cistatic void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
576662306a36Sopenharmony_ci					   struct wireless_dev *wdev)
576762306a36Sopenharmony_ci{
576862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
576962306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
577062306a36Sopenharmony_ci
577162306a36Sopenharmony_ci	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
577262306a36Sopenharmony_ci
577362306a36Sopenharmony_ci	brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
577462306a36Sopenharmony_ci	clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
577562306a36Sopenharmony_ci}
577662306a36Sopenharmony_ci
577762306a36Sopenharmony_cistatic s32
577862306a36Sopenharmony_cibrcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
577962306a36Sopenharmony_ci			     const struct brcmf_event_msg *e, void *data)
578062306a36Sopenharmony_ci{
578162306a36Sopenharmony_ci	switch (e->reason) {
578262306a36Sopenharmony_ci	case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
578362306a36Sopenharmony_ci		brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
578462306a36Sopenharmony_ci		break;
578562306a36Sopenharmony_ci	case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
578662306a36Sopenharmony_ci		brcmf_dbg(TRACE, "TDLS Peer Connected\n");
578762306a36Sopenharmony_ci		brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
578862306a36Sopenharmony_ci		break;
578962306a36Sopenharmony_ci	case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
579062306a36Sopenharmony_ci		brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
579162306a36Sopenharmony_ci		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
579262306a36Sopenharmony_ci		break;
579362306a36Sopenharmony_ci	}
579462306a36Sopenharmony_ci
579562306a36Sopenharmony_ci	return 0;
579662306a36Sopenharmony_ci}
579762306a36Sopenharmony_ci
579862306a36Sopenharmony_cistatic int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
579962306a36Sopenharmony_ci{
580062306a36Sopenharmony_ci	int ret;
580162306a36Sopenharmony_ci
580262306a36Sopenharmony_ci	switch (oper) {
580362306a36Sopenharmony_ci	case NL80211_TDLS_DISCOVERY_REQ:
580462306a36Sopenharmony_ci		ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
580562306a36Sopenharmony_ci		break;
580662306a36Sopenharmony_ci	case NL80211_TDLS_SETUP:
580762306a36Sopenharmony_ci		ret = BRCMF_TDLS_MANUAL_EP_CREATE;
580862306a36Sopenharmony_ci		break;
580962306a36Sopenharmony_ci	case NL80211_TDLS_TEARDOWN:
581062306a36Sopenharmony_ci		ret = BRCMF_TDLS_MANUAL_EP_DELETE;
581162306a36Sopenharmony_ci		break;
581262306a36Sopenharmony_ci	default:
581362306a36Sopenharmony_ci		brcmf_err("unsupported operation: %d\n", oper);
581462306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
581562306a36Sopenharmony_ci	}
581662306a36Sopenharmony_ci	return ret;
581762306a36Sopenharmony_ci}
581862306a36Sopenharmony_ci
581962306a36Sopenharmony_cistatic int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
582062306a36Sopenharmony_ci				    struct net_device *ndev, const u8 *peer,
582162306a36Sopenharmony_ci				    enum nl80211_tdls_operation oper)
582262306a36Sopenharmony_ci{
582362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
582462306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
582562306a36Sopenharmony_ci	struct brcmf_if *ifp;
582662306a36Sopenharmony_ci	struct brcmf_tdls_iovar_le info;
582762306a36Sopenharmony_ci	int ret = 0;
582862306a36Sopenharmony_ci
582962306a36Sopenharmony_ci	ret = brcmf_convert_nl80211_tdls_oper(oper);
583062306a36Sopenharmony_ci	if (ret < 0)
583162306a36Sopenharmony_ci		return ret;
583262306a36Sopenharmony_ci
583362306a36Sopenharmony_ci	ifp = netdev_priv(ndev);
583462306a36Sopenharmony_ci	memset(&info, 0, sizeof(info));
583562306a36Sopenharmony_ci	info.mode = (u8)ret;
583662306a36Sopenharmony_ci	if (peer)
583762306a36Sopenharmony_ci		memcpy(info.ea, peer, ETH_ALEN);
583862306a36Sopenharmony_ci
583962306a36Sopenharmony_ci	ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
584062306a36Sopenharmony_ci				       &info, sizeof(info));
584162306a36Sopenharmony_ci	if (ret < 0)
584262306a36Sopenharmony_ci		bphy_err(drvr, "tdls_endpoint iovar failed: ret=%d\n", ret);
584362306a36Sopenharmony_ci
584462306a36Sopenharmony_ci	return ret;
584562306a36Sopenharmony_ci}
584662306a36Sopenharmony_ci
584762306a36Sopenharmony_cistatic int
584862306a36Sopenharmony_cibrcmf_cfg80211_update_conn_params(struct wiphy *wiphy,
584962306a36Sopenharmony_ci				  struct net_device *ndev,
585062306a36Sopenharmony_ci				  struct cfg80211_connect_params *sme,
585162306a36Sopenharmony_ci				  u32 changed)
585262306a36Sopenharmony_ci{
585362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
585462306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
585562306a36Sopenharmony_ci	struct brcmf_if *ifp;
585662306a36Sopenharmony_ci	int err;
585762306a36Sopenharmony_ci
585862306a36Sopenharmony_ci	if (!(changed & UPDATE_ASSOC_IES))
585962306a36Sopenharmony_ci		return 0;
586062306a36Sopenharmony_ci
586162306a36Sopenharmony_ci	ifp = netdev_priv(ndev);
586262306a36Sopenharmony_ci	err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
586362306a36Sopenharmony_ci				    sme->ie, sme->ie_len);
586462306a36Sopenharmony_ci	if (err)
586562306a36Sopenharmony_ci		bphy_err(drvr, "Set Assoc REQ IE Failed\n");
586662306a36Sopenharmony_ci	else
586762306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
586862306a36Sopenharmony_ci
586962306a36Sopenharmony_ci	return err;
587062306a36Sopenharmony_ci}
587162306a36Sopenharmony_ci
587262306a36Sopenharmony_ci#ifdef CONFIG_PM
587362306a36Sopenharmony_cistatic int
587462306a36Sopenharmony_cibrcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
587562306a36Sopenharmony_ci			      struct cfg80211_gtk_rekey_data *gtk)
587662306a36Sopenharmony_ci{
587762306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
587862306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
587962306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
588062306a36Sopenharmony_ci	struct brcmf_gtk_keyinfo_le gtk_le;
588162306a36Sopenharmony_ci	int ret;
588262306a36Sopenharmony_ci
588362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
588462306a36Sopenharmony_ci
588562306a36Sopenharmony_ci	memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
588662306a36Sopenharmony_ci	memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
588762306a36Sopenharmony_ci	memcpy(gtk_le.replay_counter, gtk->replay_ctr,
588862306a36Sopenharmony_ci	       sizeof(gtk_le.replay_counter));
588962306a36Sopenharmony_ci
589062306a36Sopenharmony_ci	ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", &gtk_le,
589162306a36Sopenharmony_ci				       sizeof(gtk_le));
589262306a36Sopenharmony_ci	if (ret < 0)
589362306a36Sopenharmony_ci		bphy_err(drvr, "gtk_key_info iovar failed: ret=%d\n", ret);
589462306a36Sopenharmony_ci
589562306a36Sopenharmony_ci	return ret;
589662306a36Sopenharmony_ci}
589762306a36Sopenharmony_ci#endif
589862306a36Sopenharmony_ci
589962306a36Sopenharmony_cistatic int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,
590062306a36Sopenharmony_ci				  const struct cfg80211_pmk_conf *conf)
590162306a36Sopenharmony_ci{
590262306a36Sopenharmony_ci	struct brcmf_if *ifp;
590362306a36Sopenharmony_ci
590462306a36Sopenharmony_ci	brcmf_dbg(TRACE, "enter\n");
590562306a36Sopenharmony_ci
590662306a36Sopenharmony_ci	/* expect using firmware supplicant for 1X */
590762306a36Sopenharmony_ci	ifp = netdev_priv(dev);
590862306a36Sopenharmony_ci	if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
590962306a36Sopenharmony_ci		return -EINVAL;
591062306a36Sopenharmony_ci
591162306a36Sopenharmony_ci	if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN)
591262306a36Sopenharmony_ci		return -ERANGE;
591362306a36Sopenharmony_ci
591462306a36Sopenharmony_ci	return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);
591562306a36Sopenharmony_ci}
591662306a36Sopenharmony_ci
591762306a36Sopenharmony_cistatic int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,
591862306a36Sopenharmony_ci				  const u8 *aa)
591962306a36Sopenharmony_ci{
592062306a36Sopenharmony_ci	struct brcmf_if *ifp;
592162306a36Sopenharmony_ci
592262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "enter\n");
592362306a36Sopenharmony_ci	ifp = netdev_priv(dev);
592462306a36Sopenharmony_ci	if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
592562306a36Sopenharmony_ci		return -EINVAL;
592662306a36Sopenharmony_ci
592762306a36Sopenharmony_ci	return brcmf_set_pmk(ifp, NULL, 0);
592862306a36Sopenharmony_ci}
592962306a36Sopenharmony_ci
593062306a36Sopenharmony_cistatic struct cfg80211_ops brcmf_cfg80211_ops = {
593162306a36Sopenharmony_ci	.add_virtual_intf = brcmf_cfg80211_add_iface,
593262306a36Sopenharmony_ci	.del_virtual_intf = brcmf_cfg80211_del_iface,
593362306a36Sopenharmony_ci	.change_virtual_intf = brcmf_cfg80211_change_iface,
593462306a36Sopenharmony_ci	.scan = brcmf_cfg80211_scan,
593562306a36Sopenharmony_ci	.set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
593662306a36Sopenharmony_ci	.join_ibss = brcmf_cfg80211_join_ibss,
593762306a36Sopenharmony_ci	.leave_ibss = brcmf_cfg80211_leave_ibss,
593862306a36Sopenharmony_ci	.get_station = brcmf_cfg80211_get_station,
593962306a36Sopenharmony_ci	.dump_station = brcmf_cfg80211_dump_station,
594062306a36Sopenharmony_ci	.set_tx_power = brcmf_cfg80211_set_tx_power,
594162306a36Sopenharmony_ci	.get_tx_power = brcmf_cfg80211_get_tx_power,
594262306a36Sopenharmony_ci	.add_key = brcmf_cfg80211_add_key,
594362306a36Sopenharmony_ci	.del_key = brcmf_cfg80211_del_key,
594462306a36Sopenharmony_ci	.get_key = brcmf_cfg80211_get_key,
594562306a36Sopenharmony_ci	.set_default_key = brcmf_cfg80211_config_default_key,
594662306a36Sopenharmony_ci	.set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
594762306a36Sopenharmony_ci	.set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
594862306a36Sopenharmony_ci	.connect = brcmf_cfg80211_connect,
594962306a36Sopenharmony_ci	.disconnect = brcmf_cfg80211_disconnect,
595062306a36Sopenharmony_ci	.suspend = brcmf_cfg80211_suspend,
595162306a36Sopenharmony_ci	.resume = brcmf_cfg80211_resume,
595262306a36Sopenharmony_ci	.set_pmksa = brcmf_cfg80211_set_pmksa,
595362306a36Sopenharmony_ci	.del_pmksa = brcmf_cfg80211_del_pmksa,
595462306a36Sopenharmony_ci	.flush_pmksa = brcmf_cfg80211_flush_pmksa,
595562306a36Sopenharmony_ci	.start_ap = brcmf_cfg80211_start_ap,
595662306a36Sopenharmony_ci	.stop_ap = brcmf_cfg80211_stop_ap,
595762306a36Sopenharmony_ci	.change_beacon = brcmf_cfg80211_change_beacon,
595862306a36Sopenharmony_ci	.del_station = brcmf_cfg80211_del_station,
595962306a36Sopenharmony_ci	.change_station = brcmf_cfg80211_change_station,
596062306a36Sopenharmony_ci	.sched_scan_start = brcmf_cfg80211_sched_scan_start,
596162306a36Sopenharmony_ci	.sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
596262306a36Sopenharmony_ci	.update_mgmt_frame_registrations =
596362306a36Sopenharmony_ci		brcmf_cfg80211_update_mgmt_frame_registrations,
596462306a36Sopenharmony_ci	.mgmt_tx = brcmf_cfg80211_mgmt_tx,
596562306a36Sopenharmony_ci	.set_cqm_rssi_range_config = brcmf_cfg80211_set_cqm_rssi_range_config,
596662306a36Sopenharmony_ci	.remain_on_channel = brcmf_p2p_remain_on_channel,
596762306a36Sopenharmony_ci	.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
596862306a36Sopenharmony_ci	.get_channel = brcmf_cfg80211_get_channel,
596962306a36Sopenharmony_ci	.start_p2p_device = brcmf_p2p_start_device,
597062306a36Sopenharmony_ci	.stop_p2p_device = brcmf_p2p_stop_device,
597162306a36Sopenharmony_ci	.crit_proto_start = brcmf_cfg80211_crit_proto_start,
597262306a36Sopenharmony_ci	.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
597362306a36Sopenharmony_ci	.tdls_oper = brcmf_cfg80211_tdls_oper,
597462306a36Sopenharmony_ci	.update_connect_params = brcmf_cfg80211_update_conn_params,
597562306a36Sopenharmony_ci	.set_pmk = brcmf_cfg80211_set_pmk,
597662306a36Sopenharmony_ci	.del_pmk = brcmf_cfg80211_del_pmk,
597762306a36Sopenharmony_ci};
597862306a36Sopenharmony_ci
597962306a36Sopenharmony_cistruct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings)
598062306a36Sopenharmony_ci{
598162306a36Sopenharmony_ci	struct cfg80211_ops *ops;
598262306a36Sopenharmony_ci
598362306a36Sopenharmony_ci	ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),
598462306a36Sopenharmony_ci		       GFP_KERNEL);
598562306a36Sopenharmony_ci
598662306a36Sopenharmony_ci	if (ops && settings->roamoff)
598762306a36Sopenharmony_ci		ops->update_connect_params = NULL;
598862306a36Sopenharmony_ci
598962306a36Sopenharmony_ci	return ops;
599062306a36Sopenharmony_ci}
599162306a36Sopenharmony_ci
599262306a36Sopenharmony_cistruct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
599362306a36Sopenharmony_ci					   enum nl80211_iftype type)
599462306a36Sopenharmony_ci{
599562306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif_walk;
599662306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
599762306a36Sopenharmony_ci	bool mbss;
599862306a36Sopenharmony_ci	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
599962306a36Sopenharmony_ci
600062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
600162306a36Sopenharmony_ci		  sizeof(*vif));
600262306a36Sopenharmony_ci	vif = kzalloc(sizeof(*vif), GFP_KERNEL);
600362306a36Sopenharmony_ci	if (!vif)
600462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
600562306a36Sopenharmony_ci
600662306a36Sopenharmony_ci	vif->wdev.wiphy = cfg->wiphy;
600762306a36Sopenharmony_ci	vif->wdev.iftype = type;
600862306a36Sopenharmony_ci
600962306a36Sopenharmony_ci	brcmf_init_prof(&vif->profile);
601062306a36Sopenharmony_ci
601162306a36Sopenharmony_ci	if (type == NL80211_IFTYPE_AP &&
601262306a36Sopenharmony_ci	    brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
601362306a36Sopenharmony_ci		mbss = false;
601462306a36Sopenharmony_ci		list_for_each_entry(vif_walk, &cfg->vif_list, list) {
601562306a36Sopenharmony_ci			if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
601662306a36Sopenharmony_ci				mbss = true;
601762306a36Sopenharmony_ci				break;
601862306a36Sopenharmony_ci			}
601962306a36Sopenharmony_ci		}
602062306a36Sopenharmony_ci		vif->mbss = mbss;
602162306a36Sopenharmony_ci	}
602262306a36Sopenharmony_ci
602362306a36Sopenharmony_ci	list_add_tail(&vif->list, &cfg->vif_list);
602462306a36Sopenharmony_ci	return vif;
602562306a36Sopenharmony_ci}
602662306a36Sopenharmony_ci
602762306a36Sopenharmony_civoid brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
602862306a36Sopenharmony_ci{
602962306a36Sopenharmony_ci	list_del(&vif->list);
603062306a36Sopenharmony_ci	kfree(vif);
603162306a36Sopenharmony_ci}
603262306a36Sopenharmony_ci
603362306a36Sopenharmony_civoid brcmf_cfg80211_free_netdev(struct net_device *ndev)
603462306a36Sopenharmony_ci{
603562306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
603662306a36Sopenharmony_ci	struct brcmf_if *ifp;
603762306a36Sopenharmony_ci
603862306a36Sopenharmony_ci	ifp = netdev_priv(ndev);
603962306a36Sopenharmony_ci	vif = ifp->vif;
604062306a36Sopenharmony_ci
604162306a36Sopenharmony_ci	if (vif)
604262306a36Sopenharmony_ci		brcmf_free_vif(vif);
604362306a36Sopenharmony_ci}
604462306a36Sopenharmony_ci
604562306a36Sopenharmony_cistatic bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
604662306a36Sopenharmony_ci			    const struct brcmf_event_msg *e)
604762306a36Sopenharmony_ci{
604862306a36Sopenharmony_ci	u32 event = e->event_code;
604962306a36Sopenharmony_ci	u32 status = e->status;
605062306a36Sopenharmony_ci
605162306a36Sopenharmony_ci	if ((vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK ||
605262306a36Sopenharmony_ci	     vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_SAE) &&
605362306a36Sopenharmony_ci	    event == BRCMF_E_PSK_SUP &&
605462306a36Sopenharmony_ci	    status == BRCMF_E_STATUS_FWSUP_COMPLETED)
605562306a36Sopenharmony_ci		set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
605662306a36Sopenharmony_ci	if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
605762306a36Sopenharmony_ci		brcmf_dbg(CONN, "Processing set ssid\n");
605862306a36Sopenharmony_ci		memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
605962306a36Sopenharmony_ci		if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&
606062306a36Sopenharmony_ci		    vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)
606162306a36Sopenharmony_ci			return true;
606262306a36Sopenharmony_ci
606362306a36Sopenharmony_ci		set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
606462306a36Sopenharmony_ci	}
606562306a36Sopenharmony_ci
606662306a36Sopenharmony_ci	if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) &&
606762306a36Sopenharmony_ci	    test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) {
606862306a36Sopenharmony_ci		clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
606962306a36Sopenharmony_ci		clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
607062306a36Sopenharmony_ci		return true;
607162306a36Sopenharmony_ci	}
607262306a36Sopenharmony_ci	return false;
607362306a36Sopenharmony_ci}
607462306a36Sopenharmony_ci
607562306a36Sopenharmony_cistatic bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif,
607662306a36Sopenharmony_ci			    const struct brcmf_event_msg *e)
607762306a36Sopenharmony_ci{
607862306a36Sopenharmony_ci	u32 event = e->event_code;
607962306a36Sopenharmony_ci	u16 flags = e->flags;
608062306a36Sopenharmony_ci
608162306a36Sopenharmony_ci	if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
608262306a36Sopenharmony_ci	    (event == BRCMF_E_DISASSOC_IND) ||
608362306a36Sopenharmony_ci	    ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
608462306a36Sopenharmony_ci		brcmf_dbg(CONN, "Processing link down\n");
608562306a36Sopenharmony_ci		clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
608662306a36Sopenharmony_ci		clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
608762306a36Sopenharmony_ci		return true;
608862306a36Sopenharmony_ci	}
608962306a36Sopenharmony_ci	return false;
609062306a36Sopenharmony_ci}
609162306a36Sopenharmony_ci
609262306a36Sopenharmony_cistatic bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
609362306a36Sopenharmony_ci			       const struct brcmf_event_msg *e)
609462306a36Sopenharmony_ci{
609562306a36Sopenharmony_ci	u32 event = e->event_code;
609662306a36Sopenharmony_ci	u32 status = e->status;
609762306a36Sopenharmony_ci
609862306a36Sopenharmony_ci	if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
609962306a36Sopenharmony_ci		brcmf_dbg(CONN, "Processing Link %s & no network found\n",
610062306a36Sopenharmony_ci			  e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
610162306a36Sopenharmony_ci		return true;
610262306a36Sopenharmony_ci	}
610362306a36Sopenharmony_ci
610462306a36Sopenharmony_ci	if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
610562306a36Sopenharmony_ci		brcmf_dbg(CONN, "Processing connecting & no network found\n");
610662306a36Sopenharmony_ci		return true;
610762306a36Sopenharmony_ci	}
610862306a36Sopenharmony_ci
610962306a36Sopenharmony_ci	if (event == BRCMF_E_PSK_SUP &&
611062306a36Sopenharmony_ci	    status != BRCMF_E_STATUS_FWSUP_COMPLETED) {
611162306a36Sopenharmony_ci		brcmf_dbg(CONN, "Processing failed supplicant state: %u\n",
611262306a36Sopenharmony_ci			  status);
611362306a36Sopenharmony_ci		return true;
611462306a36Sopenharmony_ci	}
611562306a36Sopenharmony_ci
611662306a36Sopenharmony_ci	return false;
611762306a36Sopenharmony_ci}
611862306a36Sopenharmony_ci
611962306a36Sopenharmony_cistatic void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
612062306a36Sopenharmony_ci{
612162306a36Sopenharmony_ci	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
612262306a36Sopenharmony_ci
612362306a36Sopenharmony_ci	kfree(conn_info->req_ie);
612462306a36Sopenharmony_ci	conn_info->req_ie = NULL;
612562306a36Sopenharmony_ci	conn_info->req_ie_len = 0;
612662306a36Sopenharmony_ci	kfree(conn_info->resp_ie);
612762306a36Sopenharmony_ci	conn_info->resp_ie = NULL;
612862306a36Sopenharmony_ci	conn_info->resp_ie_len = 0;
612962306a36Sopenharmony_ci}
613062306a36Sopenharmony_ci
613162306a36Sopenharmony_ciu8 brcmf_map_prio_to_prec(void *config, u8 prio)
613262306a36Sopenharmony_ci{
613362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
613462306a36Sopenharmony_ci
613562306a36Sopenharmony_ci	if (!cfg)
613662306a36Sopenharmony_ci		return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
613762306a36Sopenharmony_ci		       (prio ^ 2) : prio;
613862306a36Sopenharmony_ci
613962306a36Sopenharmony_ci	/* For those AC(s) with ACM flag set to 1, convert its 4-level priority
614062306a36Sopenharmony_ci	 * to an 8-level precedence which is the same as BE's
614162306a36Sopenharmony_ci	 */
614262306a36Sopenharmony_ci	if (prio > PRIO_8021D_EE &&
614362306a36Sopenharmony_ci	    cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE])
614462306a36Sopenharmony_ci		return cfg->ac_priority[prio] * 2;
614562306a36Sopenharmony_ci
614662306a36Sopenharmony_ci	/* Conversion of 4-level priority to 8-level precedence */
614762306a36Sopenharmony_ci	if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK ||
614862306a36Sopenharmony_ci	    prio == PRIO_8021D_CL || prio == PRIO_8021D_VO)
614962306a36Sopenharmony_ci		return cfg->ac_priority[prio] * 2;
615062306a36Sopenharmony_ci	else
615162306a36Sopenharmony_ci		return cfg->ac_priority[prio] * 2 + 1;
615262306a36Sopenharmony_ci}
615362306a36Sopenharmony_ci
615462306a36Sopenharmony_ciu8 brcmf_map_prio_to_aci(void *config, u8 prio)
615562306a36Sopenharmony_ci{
615662306a36Sopenharmony_ci	/* Prio here refers to the 802.1d priority in range of 0 to 7.
615762306a36Sopenharmony_ci	 * ACI here refers to the WLAN AC Index in range of 0 to 3.
615862306a36Sopenharmony_ci	 * This function will return ACI corresponding to input prio.
615962306a36Sopenharmony_ci	 */
616062306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
616162306a36Sopenharmony_ci
616262306a36Sopenharmony_ci	if (cfg)
616362306a36Sopenharmony_ci		return cfg->ac_priority[prio];
616462306a36Sopenharmony_ci
616562306a36Sopenharmony_ci	return prio;
616662306a36Sopenharmony_ci}
616762306a36Sopenharmony_ci
616862306a36Sopenharmony_cistatic void brcmf_init_wmm_prio(u8 *priority)
616962306a36Sopenharmony_ci{
617062306a36Sopenharmony_ci	/* Initialize AC priority array to default
617162306a36Sopenharmony_ci	 * 802.1d priority as per following table:
617262306a36Sopenharmony_ci	 * 802.1d prio 0,3 maps to BE
617362306a36Sopenharmony_ci	 * 802.1d prio 1,2 maps to BK
617462306a36Sopenharmony_ci	 * 802.1d prio 4,5 maps to VI
617562306a36Sopenharmony_ci	 * 802.1d prio 6,7 maps to VO
617662306a36Sopenharmony_ci	 */
617762306a36Sopenharmony_ci	priority[0] = BRCMF_FWS_FIFO_AC_BE;
617862306a36Sopenharmony_ci	priority[3] = BRCMF_FWS_FIFO_AC_BE;
617962306a36Sopenharmony_ci	priority[1] = BRCMF_FWS_FIFO_AC_BK;
618062306a36Sopenharmony_ci	priority[2] = BRCMF_FWS_FIFO_AC_BK;
618162306a36Sopenharmony_ci	priority[4] = BRCMF_FWS_FIFO_AC_VI;
618262306a36Sopenharmony_ci	priority[5] = BRCMF_FWS_FIFO_AC_VI;
618362306a36Sopenharmony_ci	priority[6] = BRCMF_FWS_FIFO_AC_VO;
618462306a36Sopenharmony_ci	priority[7] = BRCMF_FWS_FIFO_AC_VO;
618562306a36Sopenharmony_ci}
618662306a36Sopenharmony_ci
618762306a36Sopenharmony_cistatic void brcmf_wifi_prioritize_acparams(const
618862306a36Sopenharmony_ci	struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)
618962306a36Sopenharmony_ci{
619062306a36Sopenharmony_ci	u8 aci;
619162306a36Sopenharmony_ci	u8 aifsn;
619262306a36Sopenharmony_ci	u8 ecwmin;
619362306a36Sopenharmony_ci	u8 ecwmax;
619462306a36Sopenharmony_ci	u8 acm;
619562306a36Sopenharmony_ci	u8 ranking_basis[EDCF_AC_COUNT];
619662306a36Sopenharmony_ci	u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */
619762306a36Sopenharmony_ci	u8 index;
619862306a36Sopenharmony_ci
619962306a36Sopenharmony_ci	for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) {
620062306a36Sopenharmony_ci		aifsn  = acp->ACI & EDCF_AIFSN_MASK;
620162306a36Sopenharmony_ci		acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;
620262306a36Sopenharmony_ci		ecwmin = acp->ECW & EDCF_ECWMIN_MASK;
620362306a36Sopenharmony_ci		ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
620462306a36Sopenharmony_ci		brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n",
620562306a36Sopenharmony_ci			  aci, aifsn, acm, ecwmin, ecwmax);
620662306a36Sopenharmony_ci		/* Default AC_VO will be the lowest ranking value */
620762306a36Sopenharmony_ci		ranking_basis[aci] = aifsn + ecwmin + ecwmax;
620862306a36Sopenharmony_ci		/* Initialise priority starting at 0 (AC_BE) */
620962306a36Sopenharmony_ci		aci_prio[aci] = 0;
621062306a36Sopenharmony_ci
621162306a36Sopenharmony_ci		/* If ACM is set, STA can't use this AC as per 802.11.
621262306a36Sopenharmony_ci		 * Change the ranking to BE
621362306a36Sopenharmony_ci		 */
621462306a36Sopenharmony_ci		if (aci != AC_BE && aci != AC_BK && acm == 1)
621562306a36Sopenharmony_ci			ranking_basis[aci] = ranking_basis[AC_BE];
621662306a36Sopenharmony_ci	}
621762306a36Sopenharmony_ci
621862306a36Sopenharmony_ci	/* Ranking method which works for AC priority
621962306a36Sopenharmony_ci	 * swapping when values for cwmin, cwmax and aifsn are varied
622062306a36Sopenharmony_ci	 * Compare each aci_prio against each other aci_prio
622162306a36Sopenharmony_ci	 */
622262306a36Sopenharmony_ci	for (aci = 0; aci < EDCF_AC_COUNT; aci++) {
622362306a36Sopenharmony_ci		for (index = 0; index < EDCF_AC_COUNT; index++) {
622462306a36Sopenharmony_ci			if (index != aci) {
622562306a36Sopenharmony_ci				/* Smaller ranking value has higher priority,
622662306a36Sopenharmony_ci				 * so increment priority for each ACI which has
622762306a36Sopenharmony_ci				 * a higher ranking value
622862306a36Sopenharmony_ci				 */
622962306a36Sopenharmony_ci				if (ranking_basis[aci] < ranking_basis[index])
623062306a36Sopenharmony_ci					aci_prio[aci]++;
623162306a36Sopenharmony_ci			}
623262306a36Sopenharmony_ci		}
623362306a36Sopenharmony_ci	}
623462306a36Sopenharmony_ci
623562306a36Sopenharmony_ci	/* By now, aci_prio[] will be in range of 0 to 3.
623662306a36Sopenharmony_ci	 * Use ACI prio to get the new priority value for
623762306a36Sopenharmony_ci	 * each 802.1d traffic type, in this range.
623862306a36Sopenharmony_ci	 */
623962306a36Sopenharmony_ci	if (!(aci_prio[AC_BE] == aci_prio[AC_BK] &&
624062306a36Sopenharmony_ci	      aci_prio[AC_BK] == aci_prio[AC_VI] &&
624162306a36Sopenharmony_ci	      aci_prio[AC_VI] == aci_prio[AC_VO])) {
624262306a36Sopenharmony_ci		/* 802.1d 0,3 maps to BE */
624362306a36Sopenharmony_ci		priority[0] = aci_prio[AC_BE];
624462306a36Sopenharmony_ci		priority[3] = aci_prio[AC_BE];
624562306a36Sopenharmony_ci
624662306a36Sopenharmony_ci		/* 802.1d 1,2 maps to BK */
624762306a36Sopenharmony_ci		priority[1] = aci_prio[AC_BK];
624862306a36Sopenharmony_ci		priority[2] = aci_prio[AC_BK];
624962306a36Sopenharmony_ci
625062306a36Sopenharmony_ci		/* 802.1d 4,5 maps to VO */
625162306a36Sopenharmony_ci		priority[4] = aci_prio[AC_VI];
625262306a36Sopenharmony_ci		priority[5] = aci_prio[AC_VI];
625362306a36Sopenharmony_ci
625462306a36Sopenharmony_ci		/* 802.1d 6,7 maps to VO */
625562306a36Sopenharmony_ci		priority[6] = aci_prio[AC_VO];
625662306a36Sopenharmony_ci		priority[7] = aci_prio[AC_VO];
625762306a36Sopenharmony_ci	} else {
625862306a36Sopenharmony_ci		/* Initialize to default priority */
625962306a36Sopenharmony_ci		brcmf_init_wmm_prio(priority);
626062306a36Sopenharmony_ci	}
626162306a36Sopenharmony_ci
626262306a36Sopenharmony_ci	brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",
626362306a36Sopenharmony_ci		  priority[0], priority[1], priority[2], priority[3]);
626462306a36Sopenharmony_ci
626562306a36Sopenharmony_ci	brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n",
626662306a36Sopenharmony_ci		  priority[4], priority[5], priority[6], priority[7]);
626762306a36Sopenharmony_ci}
626862306a36Sopenharmony_ci
626962306a36Sopenharmony_cistatic s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
627062306a36Sopenharmony_ci			       struct brcmf_if *ifp)
627162306a36Sopenharmony_ci{
627262306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
627362306a36Sopenharmony_ci	struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
627462306a36Sopenharmony_ci	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
627562306a36Sopenharmony_ci	struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT];
627662306a36Sopenharmony_ci	u32 req_len;
627762306a36Sopenharmony_ci	u32 resp_len;
627862306a36Sopenharmony_ci	s32 err = 0;
627962306a36Sopenharmony_ci
628062306a36Sopenharmony_ci	brcmf_clear_assoc_ies(cfg);
628162306a36Sopenharmony_ci
628262306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
628362306a36Sopenharmony_ci				       cfg->extra_buf, WL_ASSOC_INFO_MAX);
628462306a36Sopenharmony_ci	if (err) {
628562306a36Sopenharmony_ci		bphy_err(drvr, "could not get assoc info (%d)\n", err);
628662306a36Sopenharmony_ci		return err;
628762306a36Sopenharmony_ci	}
628862306a36Sopenharmony_ci	assoc_info =
628962306a36Sopenharmony_ci		(struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
629062306a36Sopenharmony_ci	req_len = le32_to_cpu(assoc_info->req_len);
629162306a36Sopenharmony_ci	resp_len = le32_to_cpu(assoc_info->resp_len);
629262306a36Sopenharmony_ci	if (req_len > WL_EXTRA_BUF_MAX || resp_len > WL_EXTRA_BUF_MAX) {
629362306a36Sopenharmony_ci		bphy_err(drvr, "invalid lengths in assoc info: req %u resp %u\n",
629462306a36Sopenharmony_ci			 req_len, resp_len);
629562306a36Sopenharmony_ci		return -EINVAL;
629662306a36Sopenharmony_ci	}
629762306a36Sopenharmony_ci	if (req_len) {
629862306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
629962306a36Sopenharmony_ci					       cfg->extra_buf,
630062306a36Sopenharmony_ci					       WL_ASSOC_INFO_MAX);
630162306a36Sopenharmony_ci		if (err) {
630262306a36Sopenharmony_ci			bphy_err(drvr, "could not get assoc req (%d)\n", err);
630362306a36Sopenharmony_ci			return err;
630462306a36Sopenharmony_ci		}
630562306a36Sopenharmony_ci		conn_info->req_ie_len = req_len;
630662306a36Sopenharmony_ci		conn_info->req_ie =
630762306a36Sopenharmony_ci		    kmemdup(cfg->extra_buf, conn_info->req_ie_len,
630862306a36Sopenharmony_ci			    GFP_KERNEL);
630962306a36Sopenharmony_ci		if (!conn_info->req_ie)
631062306a36Sopenharmony_ci			conn_info->req_ie_len = 0;
631162306a36Sopenharmony_ci	} else {
631262306a36Sopenharmony_ci		conn_info->req_ie_len = 0;
631362306a36Sopenharmony_ci		conn_info->req_ie = NULL;
631462306a36Sopenharmony_ci	}
631562306a36Sopenharmony_ci	if (resp_len) {
631662306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
631762306a36Sopenharmony_ci					       cfg->extra_buf,
631862306a36Sopenharmony_ci					       WL_ASSOC_INFO_MAX);
631962306a36Sopenharmony_ci		if (err) {
632062306a36Sopenharmony_ci			bphy_err(drvr, "could not get assoc resp (%d)\n", err);
632162306a36Sopenharmony_ci			return err;
632262306a36Sopenharmony_ci		}
632362306a36Sopenharmony_ci		conn_info->resp_ie_len = resp_len;
632462306a36Sopenharmony_ci		conn_info->resp_ie =
632562306a36Sopenharmony_ci		    kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
632662306a36Sopenharmony_ci			    GFP_KERNEL);
632762306a36Sopenharmony_ci		if (!conn_info->resp_ie)
632862306a36Sopenharmony_ci			conn_info->resp_ie_len = 0;
632962306a36Sopenharmony_ci
633062306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta",
633162306a36Sopenharmony_ci					       edcf_acparam_info,
633262306a36Sopenharmony_ci					       sizeof(edcf_acparam_info));
633362306a36Sopenharmony_ci		if (err) {
633462306a36Sopenharmony_ci			brcmf_err("could not get wme_ac_sta (%d)\n", err);
633562306a36Sopenharmony_ci			return err;
633662306a36Sopenharmony_ci		}
633762306a36Sopenharmony_ci
633862306a36Sopenharmony_ci		brcmf_wifi_prioritize_acparams(edcf_acparam_info,
633962306a36Sopenharmony_ci					       cfg->ac_priority);
634062306a36Sopenharmony_ci	} else {
634162306a36Sopenharmony_ci		conn_info->resp_ie_len = 0;
634262306a36Sopenharmony_ci		conn_info->resp_ie = NULL;
634362306a36Sopenharmony_ci	}
634462306a36Sopenharmony_ci	brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
634562306a36Sopenharmony_ci		  conn_info->req_ie_len, conn_info->resp_ie_len);
634662306a36Sopenharmony_ci
634762306a36Sopenharmony_ci	return err;
634862306a36Sopenharmony_ci}
634962306a36Sopenharmony_ci
635062306a36Sopenharmony_cistatic s32
635162306a36Sopenharmony_cibrcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
635262306a36Sopenharmony_ci		       struct net_device *ndev,
635362306a36Sopenharmony_ci		       const struct brcmf_event_msg *e)
635462306a36Sopenharmony_ci{
635562306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
635662306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
635762306a36Sopenharmony_ci	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
635862306a36Sopenharmony_ci	struct wiphy *wiphy = cfg_to_wiphy(cfg);
635962306a36Sopenharmony_ci	struct ieee80211_channel *notify_channel = NULL;
636062306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
636162306a36Sopenharmony_ci	struct brcmf_bss_info_le *bi;
636262306a36Sopenharmony_ci	struct brcmu_chan ch;
636362306a36Sopenharmony_ci	struct cfg80211_roam_info roam_info = {};
636462306a36Sopenharmony_ci	u32 freq;
636562306a36Sopenharmony_ci	s32 err = 0;
636662306a36Sopenharmony_ci	u8 *buf;
636762306a36Sopenharmony_ci
636862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
636962306a36Sopenharmony_ci
637062306a36Sopenharmony_ci	brcmf_get_assoc_ies(cfg, ifp);
637162306a36Sopenharmony_ci	memcpy(profile->bssid, e->addr, ETH_ALEN);
637262306a36Sopenharmony_ci	brcmf_update_bss_info(cfg, ifp);
637362306a36Sopenharmony_ci
637462306a36Sopenharmony_ci	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
637562306a36Sopenharmony_ci	if (buf == NULL) {
637662306a36Sopenharmony_ci		err = -ENOMEM;
637762306a36Sopenharmony_ci		goto done;
637862306a36Sopenharmony_ci	}
637962306a36Sopenharmony_ci
638062306a36Sopenharmony_ci	/* data sent to dongle has to be little endian */
638162306a36Sopenharmony_ci	*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
638262306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
638362306a36Sopenharmony_ci				     buf, WL_BSS_INFO_MAX);
638462306a36Sopenharmony_ci
638562306a36Sopenharmony_ci	if (err)
638662306a36Sopenharmony_ci		goto done;
638762306a36Sopenharmony_ci
638862306a36Sopenharmony_ci	bi = (struct brcmf_bss_info_le *)(buf + 4);
638962306a36Sopenharmony_ci	ch.chspec = le16_to_cpu(bi->chanspec);
639062306a36Sopenharmony_ci	cfg->d11inf.decchspec(&ch);
639162306a36Sopenharmony_ci
639262306a36Sopenharmony_ci	if (ch.band == BRCMU_CHAN_BAND_2G)
639362306a36Sopenharmony_ci		band = wiphy->bands[NL80211_BAND_2GHZ];
639462306a36Sopenharmony_ci	else
639562306a36Sopenharmony_ci		band = wiphy->bands[NL80211_BAND_5GHZ];
639662306a36Sopenharmony_ci
639762306a36Sopenharmony_ci	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
639862306a36Sopenharmony_ci	notify_channel = ieee80211_get_channel(wiphy, freq);
639962306a36Sopenharmony_ci
640062306a36Sopenharmony_cidone:
640162306a36Sopenharmony_ci	kfree(buf);
640262306a36Sopenharmony_ci
640362306a36Sopenharmony_ci	roam_info.links[0].channel = notify_channel;
640462306a36Sopenharmony_ci	roam_info.links[0].bssid = profile->bssid;
640562306a36Sopenharmony_ci	roam_info.req_ie = conn_info->req_ie;
640662306a36Sopenharmony_ci	roam_info.req_ie_len = conn_info->req_ie_len;
640762306a36Sopenharmony_ci	roam_info.resp_ie = conn_info->resp_ie;
640862306a36Sopenharmony_ci	roam_info.resp_ie_len = conn_info->resp_ie_len;
640962306a36Sopenharmony_ci
641062306a36Sopenharmony_ci	cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
641162306a36Sopenharmony_ci	brcmf_dbg(CONN, "Report roaming result\n");
641262306a36Sopenharmony_ci
641362306a36Sopenharmony_ci	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) {
641462306a36Sopenharmony_ci		cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL);
641562306a36Sopenharmony_ci		brcmf_dbg(CONN, "Report port authorized\n");
641662306a36Sopenharmony_ci	}
641762306a36Sopenharmony_ci
641862306a36Sopenharmony_ci	set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
641962306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
642062306a36Sopenharmony_ci	return err;
642162306a36Sopenharmony_ci}
642262306a36Sopenharmony_ci
642362306a36Sopenharmony_cistatic s32
642462306a36Sopenharmony_cibrcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
642562306a36Sopenharmony_ci		       struct net_device *ndev, const struct brcmf_event_msg *e,
642662306a36Sopenharmony_ci		       bool completed)
642762306a36Sopenharmony_ci{
642862306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
642962306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
643062306a36Sopenharmony_ci	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
643162306a36Sopenharmony_ci	struct cfg80211_connect_resp_params conn_params;
643262306a36Sopenharmony_ci
643362306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter\n");
643462306a36Sopenharmony_ci
643562306a36Sopenharmony_ci	if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
643662306a36Sopenharmony_ci			       &ifp->vif->sme_state)) {
643762306a36Sopenharmony_ci		memset(&conn_params, 0, sizeof(conn_params));
643862306a36Sopenharmony_ci		if (completed) {
643962306a36Sopenharmony_ci			brcmf_get_assoc_ies(cfg, ifp);
644062306a36Sopenharmony_ci			brcmf_update_bss_info(cfg, ifp);
644162306a36Sopenharmony_ci			set_bit(BRCMF_VIF_STATUS_CONNECTED,
644262306a36Sopenharmony_ci				&ifp->vif->sme_state);
644362306a36Sopenharmony_ci			conn_params.status = WLAN_STATUS_SUCCESS;
644462306a36Sopenharmony_ci		} else {
644562306a36Sopenharmony_ci			clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS,
644662306a36Sopenharmony_ci				  &ifp->vif->sme_state);
644762306a36Sopenharmony_ci			clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS,
644862306a36Sopenharmony_ci				  &ifp->vif->sme_state);
644962306a36Sopenharmony_ci			conn_params.status = WLAN_STATUS_AUTH_TIMEOUT;
645062306a36Sopenharmony_ci		}
645162306a36Sopenharmony_ci		conn_params.links[0].bssid = profile->bssid;
645262306a36Sopenharmony_ci		conn_params.req_ie = conn_info->req_ie;
645362306a36Sopenharmony_ci		conn_params.req_ie_len = conn_info->req_ie_len;
645462306a36Sopenharmony_ci		conn_params.resp_ie = conn_info->resp_ie;
645562306a36Sopenharmony_ci		conn_params.resp_ie_len = conn_info->resp_ie_len;
645662306a36Sopenharmony_ci		cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL);
645762306a36Sopenharmony_ci		brcmf_dbg(CONN, "Report connect result - connection %s\n",
645862306a36Sopenharmony_ci			  completed ? "succeeded" : "failed");
645962306a36Sopenharmony_ci	}
646062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Exit\n");
646162306a36Sopenharmony_ci	return 0;
646262306a36Sopenharmony_ci}
646362306a36Sopenharmony_ci
646462306a36Sopenharmony_cistatic s32
646562306a36Sopenharmony_cibrcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
646662306a36Sopenharmony_ci			       struct net_device *ndev,
646762306a36Sopenharmony_ci			       const struct brcmf_event_msg *e, void *data)
646862306a36Sopenharmony_ci{
646962306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
647062306a36Sopenharmony_ci	static int generation;
647162306a36Sopenharmony_ci	u32 event = e->event_code;
647262306a36Sopenharmony_ci	u32 reason = e->reason;
647362306a36Sopenharmony_ci	struct station_info *sinfo;
647462306a36Sopenharmony_ci
647562306a36Sopenharmony_ci	brcmf_dbg(CONN, "event %s (%u), reason %d\n",
647662306a36Sopenharmony_ci		  brcmf_fweh_event_name(event), event, reason);
647762306a36Sopenharmony_ci	if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
647862306a36Sopenharmony_ci	    ndev != cfg_to_ndev(cfg)) {
647962306a36Sopenharmony_ci		brcmf_dbg(CONN, "AP mode link down\n");
648062306a36Sopenharmony_ci		complete(&cfg->vif_disabled);
648162306a36Sopenharmony_ci		return 0;
648262306a36Sopenharmony_ci	}
648362306a36Sopenharmony_ci
648462306a36Sopenharmony_ci	if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
648562306a36Sopenharmony_ci	    (reason == BRCMF_E_STATUS_SUCCESS)) {
648662306a36Sopenharmony_ci		if (!data) {
648762306a36Sopenharmony_ci			bphy_err(drvr, "No IEs present in ASSOC/REASSOC_IND\n");
648862306a36Sopenharmony_ci			return -EINVAL;
648962306a36Sopenharmony_ci		}
649062306a36Sopenharmony_ci
649162306a36Sopenharmony_ci		sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
649262306a36Sopenharmony_ci		if (!sinfo)
649362306a36Sopenharmony_ci			return -ENOMEM;
649462306a36Sopenharmony_ci
649562306a36Sopenharmony_ci		sinfo->assoc_req_ies = data;
649662306a36Sopenharmony_ci		sinfo->assoc_req_ies_len = e->datalen;
649762306a36Sopenharmony_ci		generation++;
649862306a36Sopenharmony_ci		sinfo->generation = generation;
649962306a36Sopenharmony_ci		cfg80211_new_sta(ndev, e->addr, sinfo, GFP_KERNEL);
650062306a36Sopenharmony_ci
650162306a36Sopenharmony_ci		kfree(sinfo);
650262306a36Sopenharmony_ci	} else if ((event == BRCMF_E_DISASSOC_IND) ||
650362306a36Sopenharmony_ci		   (event == BRCMF_E_DEAUTH_IND) ||
650462306a36Sopenharmony_ci		   (event == BRCMF_E_DEAUTH)) {
650562306a36Sopenharmony_ci		cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
650662306a36Sopenharmony_ci	}
650762306a36Sopenharmony_ci	return 0;
650862306a36Sopenharmony_ci}
650962306a36Sopenharmony_ci
651062306a36Sopenharmony_cistatic s32
651162306a36Sopenharmony_cibrcmf_notify_connect_status(struct brcmf_if *ifp,
651262306a36Sopenharmony_ci			    const struct brcmf_event_msg *e, void *data)
651362306a36Sopenharmony_ci{
651462306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
651562306a36Sopenharmony_ci	struct net_device *ndev = ifp->ndev;
651662306a36Sopenharmony_ci	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
651762306a36Sopenharmony_ci	struct ieee80211_channel *chan;
651862306a36Sopenharmony_ci	s32 err = 0;
651962306a36Sopenharmony_ci
652062306a36Sopenharmony_ci	if ((e->event_code == BRCMF_E_DEAUTH) ||
652162306a36Sopenharmony_ci	    (e->event_code == BRCMF_E_DEAUTH_IND) ||
652262306a36Sopenharmony_ci	    (e->event_code == BRCMF_E_DISASSOC_IND) ||
652362306a36Sopenharmony_ci	    ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
652462306a36Sopenharmony_ci		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
652562306a36Sopenharmony_ci	}
652662306a36Sopenharmony_ci
652762306a36Sopenharmony_ci	if (brcmf_is_apmode(ifp->vif)) {
652862306a36Sopenharmony_ci		err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
652962306a36Sopenharmony_ci	} else if (brcmf_is_linkup(ifp->vif, e)) {
653062306a36Sopenharmony_ci		brcmf_dbg(CONN, "Linkup\n");
653162306a36Sopenharmony_ci		if (brcmf_is_ibssmode(ifp->vif)) {
653262306a36Sopenharmony_ci			brcmf_inform_ibss(cfg, ndev, e->addr);
653362306a36Sopenharmony_ci			chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
653462306a36Sopenharmony_ci			memcpy(profile->bssid, e->addr, ETH_ALEN);
653562306a36Sopenharmony_ci			cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
653662306a36Sopenharmony_ci			clear_bit(BRCMF_VIF_STATUS_CONNECTING,
653762306a36Sopenharmony_ci				  &ifp->vif->sme_state);
653862306a36Sopenharmony_ci			set_bit(BRCMF_VIF_STATUS_CONNECTED,
653962306a36Sopenharmony_ci				&ifp->vif->sme_state);
654062306a36Sopenharmony_ci		} else
654162306a36Sopenharmony_ci			brcmf_bss_connect_done(cfg, ndev, e, true);
654262306a36Sopenharmony_ci		brcmf_net_setcarrier(ifp, true);
654362306a36Sopenharmony_ci	} else if (brcmf_is_linkdown(ifp->vif, e)) {
654462306a36Sopenharmony_ci		brcmf_dbg(CONN, "Linkdown\n");
654562306a36Sopenharmony_ci		if (!brcmf_is_ibssmode(ifp->vif) &&
654662306a36Sopenharmony_ci		    (test_bit(BRCMF_VIF_STATUS_CONNECTED,
654762306a36Sopenharmony_ci			      &ifp->vif->sme_state) ||
654862306a36Sopenharmony_ci		     test_bit(BRCMF_VIF_STATUS_CONNECTING,
654962306a36Sopenharmony_ci			      &ifp->vif->sme_state))) {
655062306a36Sopenharmony_ci			if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
655162306a36Sopenharmony_ci				     &ifp->vif->sme_state) &&
655262306a36Sopenharmony_ci			    memcmp(profile->bssid, e->addr, ETH_ALEN))
655362306a36Sopenharmony_ci				return err;
655462306a36Sopenharmony_ci
655562306a36Sopenharmony_ci			brcmf_bss_connect_done(cfg, ndev, e, false);
655662306a36Sopenharmony_ci			brcmf_link_down(ifp->vif,
655762306a36Sopenharmony_ci					brcmf_map_fw_linkdown_reason(e),
655862306a36Sopenharmony_ci					e->event_code &
655962306a36Sopenharmony_ci					(BRCMF_E_DEAUTH_IND |
656062306a36Sopenharmony_ci					BRCMF_E_DISASSOC_IND)
656162306a36Sopenharmony_ci					? false : true);
656262306a36Sopenharmony_ci			brcmf_init_prof(ndev_to_prof(ndev));
656362306a36Sopenharmony_ci			if (ndev != cfg_to_ndev(cfg))
656462306a36Sopenharmony_ci				complete(&cfg->vif_disabled);
656562306a36Sopenharmony_ci			brcmf_net_setcarrier(ifp, false);
656662306a36Sopenharmony_ci		}
656762306a36Sopenharmony_ci	} else if (brcmf_is_nonetwork(cfg, e)) {
656862306a36Sopenharmony_ci		if (brcmf_is_ibssmode(ifp->vif))
656962306a36Sopenharmony_ci			clear_bit(BRCMF_VIF_STATUS_CONNECTING,
657062306a36Sopenharmony_ci				  &ifp->vif->sme_state);
657162306a36Sopenharmony_ci		else
657262306a36Sopenharmony_ci			brcmf_bss_connect_done(cfg, ndev, e, false);
657362306a36Sopenharmony_ci	}
657462306a36Sopenharmony_ci
657562306a36Sopenharmony_ci	return err;
657662306a36Sopenharmony_ci}
657762306a36Sopenharmony_ci
657862306a36Sopenharmony_cistatic s32
657962306a36Sopenharmony_cibrcmf_notify_roaming_status(struct brcmf_if *ifp,
658062306a36Sopenharmony_ci			    const struct brcmf_event_msg *e, void *data)
658162306a36Sopenharmony_ci{
658262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
658362306a36Sopenharmony_ci	u32 event = e->event_code;
658462306a36Sopenharmony_ci	u32 status = e->status;
658562306a36Sopenharmony_ci
658662306a36Sopenharmony_ci	if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
658762306a36Sopenharmony_ci		if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
658862306a36Sopenharmony_ci			     &ifp->vif->sme_state)) {
658962306a36Sopenharmony_ci			brcmf_bss_roaming_done(cfg, ifp->ndev, e);
659062306a36Sopenharmony_ci		} else {
659162306a36Sopenharmony_ci			brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
659262306a36Sopenharmony_ci			brcmf_net_setcarrier(ifp, true);
659362306a36Sopenharmony_ci		}
659462306a36Sopenharmony_ci	}
659562306a36Sopenharmony_ci
659662306a36Sopenharmony_ci	return 0;
659762306a36Sopenharmony_ci}
659862306a36Sopenharmony_ci
659962306a36Sopenharmony_cistatic s32
660062306a36Sopenharmony_cibrcmf_notify_mic_status(struct brcmf_if *ifp,
660162306a36Sopenharmony_ci			const struct brcmf_event_msg *e, void *data)
660262306a36Sopenharmony_ci{
660362306a36Sopenharmony_ci	u16 flags = e->flags;
660462306a36Sopenharmony_ci	enum nl80211_key_type key_type;
660562306a36Sopenharmony_ci
660662306a36Sopenharmony_ci	if (flags & BRCMF_EVENT_MSG_GROUP)
660762306a36Sopenharmony_ci		key_type = NL80211_KEYTYPE_GROUP;
660862306a36Sopenharmony_ci	else
660962306a36Sopenharmony_ci		key_type = NL80211_KEYTYPE_PAIRWISE;
661062306a36Sopenharmony_ci
661162306a36Sopenharmony_ci	cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
661262306a36Sopenharmony_ci				     NULL, GFP_KERNEL);
661362306a36Sopenharmony_ci
661462306a36Sopenharmony_ci	return 0;
661562306a36Sopenharmony_ci}
661662306a36Sopenharmony_ci
661762306a36Sopenharmony_cistatic s32 brcmf_notify_rssi(struct brcmf_if *ifp,
661862306a36Sopenharmony_ci			     const struct brcmf_event_msg *e, void *data)
661962306a36Sopenharmony_ci{
662062306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif = ifp->vif;
662162306a36Sopenharmony_ci	struct brcmf_rssi_be *info = data;
662262306a36Sopenharmony_ci	s32 rssi, snr = 0, noise = 0;
662362306a36Sopenharmony_ci	s32 low, high, last;
662462306a36Sopenharmony_ci
662562306a36Sopenharmony_ci	if (e->datalen >= sizeof(*info)) {
662662306a36Sopenharmony_ci		rssi = be32_to_cpu(info->rssi);
662762306a36Sopenharmony_ci		snr = be32_to_cpu(info->snr);
662862306a36Sopenharmony_ci		noise = be32_to_cpu(info->noise);
662962306a36Sopenharmony_ci	} else if (e->datalen >= sizeof(rssi)) {
663062306a36Sopenharmony_ci		rssi = be32_to_cpu(*(__be32 *)data);
663162306a36Sopenharmony_ci	} else {
663262306a36Sopenharmony_ci		brcmf_err("insufficient RSSI event data\n");
663362306a36Sopenharmony_ci		return 0;
663462306a36Sopenharmony_ci	}
663562306a36Sopenharmony_ci
663662306a36Sopenharmony_ci	low = vif->cqm_rssi_low;
663762306a36Sopenharmony_ci	high = vif->cqm_rssi_high;
663862306a36Sopenharmony_ci	last = vif->cqm_rssi_last;
663962306a36Sopenharmony_ci
664062306a36Sopenharmony_ci	brcmf_dbg(TRACE, "rssi=%d snr=%d noise=%d low=%d high=%d last=%d\n",
664162306a36Sopenharmony_ci		  rssi, snr, noise, low, high, last);
664262306a36Sopenharmony_ci
664362306a36Sopenharmony_ci	vif->cqm_rssi_last = rssi;
664462306a36Sopenharmony_ci
664562306a36Sopenharmony_ci	if (rssi <= low || rssi == 0) {
664662306a36Sopenharmony_ci		brcmf_dbg(INFO, "LOW rssi=%d\n", rssi);
664762306a36Sopenharmony_ci		cfg80211_cqm_rssi_notify(ifp->ndev,
664862306a36Sopenharmony_ci					 NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
664962306a36Sopenharmony_ci					 rssi, GFP_KERNEL);
665062306a36Sopenharmony_ci	} else if (rssi > high) {
665162306a36Sopenharmony_ci		brcmf_dbg(INFO, "HIGH rssi=%d\n", rssi);
665262306a36Sopenharmony_ci		cfg80211_cqm_rssi_notify(ifp->ndev,
665362306a36Sopenharmony_ci					 NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
665462306a36Sopenharmony_ci					 rssi, GFP_KERNEL);
665562306a36Sopenharmony_ci	}
665662306a36Sopenharmony_ci
665762306a36Sopenharmony_ci	return 0;
665862306a36Sopenharmony_ci}
665962306a36Sopenharmony_ci
666062306a36Sopenharmony_cistatic s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
666162306a36Sopenharmony_ci				  const struct brcmf_event_msg *e, void *data)
666262306a36Sopenharmony_ci{
666362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
666462306a36Sopenharmony_ci	struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
666562306a36Sopenharmony_ci	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
666662306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
666762306a36Sopenharmony_ci
666862306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
666962306a36Sopenharmony_ci		  ifevent->action, ifevent->flags, ifevent->ifidx,
667062306a36Sopenharmony_ci		  ifevent->bsscfgidx);
667162306a36Sopenharmony_ci
667262306a36Sopenharmony_ci	spin_lock(&event->vif_event_lock);
667362306a36Sopenharmony_ci	event->action = ifevent->action;
667462306a36Sopenharmony_ci	vif = event->vif;
667562306a36Sopenharmony_ci
667662306a36Sopenharmony_ci	switch (ifevent->action) {
667762306a36Sopenharmony_ci	case BRCMF_E_IF_ADD:
667862306a36Sopenharmony_ci		/* waiting process may have timed out */
667962306a36Sopenharmony_ci		if (!cfg->vif_event.vif) {
668062306a36Sopenharmony_ci			spin_unlock(&event->vif_event_lock);
668162306a36Sopenharmony_ci			return -EBADF;
668262306a36Sopenharmony_ci		}
668362306a36Sopenharmony_ci
668462306a36Sopenharmony_ci		ifp->vif = vif;
668562306a36Sopenharmony_ci		vif->ifp = ifp;
668662306a36Sopenharmony_ci		if (ifp->ndev) {
668762306a36Sopenharmony_ci			vif->wdev.netdev = ifp->ndev;
668862306a36Sopenharmony_ci			ifp->ndev->ieee80211_ptr = &vif->wdev;
668962306a36Sopenharmony_ci			SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
669062306a36Sopenharmony_ci		}
669162306a36Sopenharmony_ci		spin_unlock(&event->vif_event_lock);
669262306a36Sopenharmony_ci		wake_up(&event->vif_wq);
669362306a36Sopenharmony_ci		return 0;
669462306a36Sopenharmony_ci
669562306a36Sopenharmony_ci	case BRCMF_E_IF_DEL:
669662306a36Sopenharmony_ci		spin_unlock(&event->vif_event_lock);
669762306a36Sopenharmony_ci		/* event may not be upon user request */
669862306a36Sopenharmony_ci		if (brcmf_cfg80211_vif_event_armed(cfg))
669962306a36Sopenharmony_ci			wake_up(&event->vif_wq);
670062306a36Sopenharmony_ci		return 0;
670162306a36Sopenharmony_ci
670262306a36Sopenharmony_ci	case BRCMF_E_IF_CHANGE:
670362306a36Sopenharmony_ci		spin_unlock(&event->vif_event_lock);
670462306a36Sopenharmony_ci		wake_up(&event->vif_wq);
670562306a36Sopenharmony_ci		return 0;
670662306a36Sopenharmony_ci
670762306a36Sopenharmony_ci	default:
670862306a36Sopenharmony_ci		spin_unlock(&event->vif_event_lock);
670962306a36Sopenharmony_ci		break;
671062306a36Sopenharmony_ci	}
671162306a36Sopenharmony_ci	return -EINVAL;
671262306a36Sopenharmony_ci}
671362306a36Sopenharmony_ci
671462306a36Sopenharmony_cistatic void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
671562306a36Sopenharmony_ci{
671662306a36Sopenharmony_ci	conf->frag_threshold = (u32)-1;
671762306a36Sopenharmony_ci	conf->rts_threshold = (u32)-1;
671862306a36Sopenharmony_ci	conf->retry_short = (u32)-1;
671962306a36Sopenharmony_ci	conf->retry_long = (u32)-1;
672062306a36Sopenharmony_ci}
672162306a36Sopenharmony_ci
672262306a36Sopenharmony_cistatic void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
672362306a36Sopenharmony_ci{
672462306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
672562306a36Sopenharmony_ci			    brcmf_notify_connect_status);
672662306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
672762306a36Sopenharmony_ci			    brcmf_notify_connect_status);
672862306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
672962306a36Sopenharmony_ci			    brcmf_notify_connect_status);
673062306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
673162306a36Sopenharmony_ci			    brcmf_notify_connect_status);
673262306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
673362306a36Sopenharmony_ci			    brcmf_notify_connect_status);
673462306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
673562306a36Sopenharmony_ci			    brcmf_notify_connect_status);
673662306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
673762306a36Sopenharmony_ci			    brcmf_notify_roaming_status);
673862306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
673962306a36Sopenharmony_ci			    brcmf_notify_mic_status);
674062306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
674162306a36Sopenharmony_ci			    brcmf_notify_connect_status);
674262306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
674362306a36Sopenharmony_ci			    brcmf_notify_sched_scan_results);
674462306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
674562306a36Sopenharmony_ci			    brcmf_notify_vif_event);
674662306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
674762306a36Sopenharmony_ci			    brcmf_p2p_notify_rx_mgmt_p2p_probereq);
674862306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
674962306a36Sopenharmony_ci			    brcmf_p2p_notify_listen_complete);
675062306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
675162306a36Sopenharmony_ci			    brcmf_p2p_notify_action_frame_rx);
675262306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
675362306a36Sopenharmony_ci			    brcmf_p2p_notify_action_tx_complete);
675462306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
675562306a36Sopenharmony_ci			    brcmf_p2p_notify_action_tx_complete);
675662306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
675762306a36Sopenharmony_ci			    brcmf_notify_connect_status);
675862306a36Sopenharmony_ci	brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi);
675962306a36Sopenharmony_ci}
676062306a36Sopenharmony_ci
676162306a36Sopenharmony_cistatic void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
676262306a36Sopenharmony_ci{
676362306a36Sopenharmony_ci	kfree(cfg->conf);
676462306a36Sopenharmony_ci	cfg->conf = NULL;
676562306a36Sopenharmony_ci	kfree(cfg->extra_buf);
676662306a36Sopenharmony_ci	cfg->extra_buf = NULL;
676762306a36Sopenharmony_ci	kfree(cfg->wowl.nd);
676862306a36Sopenharmony_ci	cfg->wowl.nd = NULL;
676962306a36Sopenharmony_ci	kfree(cfg->wowl.nd_info);
677062306a36Sopenharmony_ci	cfg->wowl.nd_info = NULL;
677162306a36Sopenharmony_ci	kfree(cfg->escan_info.escan_buf);
677262306a36Sopenharmony_ci	cfg->escan_info.escan_buf = NULL;
677362306a36Sopenharmony_ci}
677462306a36Sopenharmony_ci
677562306a36Sopenharmony_cistatic s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
677662306a36Sopenharmony_ci{
677762306a36Sopenharmony_ci	cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
677862306a36Sopenharmony_ci	if (!cfg->conf)
677962306a36Sopenharmony_ci		goto init_priv_mem_out;
678062306a36Sopenharmony_ci	cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
678162306a36Sopenharmony_ci	if (!cfg->extra_buf)
678262306a36Sopenharmony_ci		goto init_priv_mem_out;
678362306a36Sopenharmony_ci	cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
678462306a36Sopenharmony_ci	if (!cfg->wowl.nd)
678562306a36Sopenharmony_ci		goto init_priv_mem_out;
678662306a36Sopenharmony_ci	cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
678762306a36Sopenharmony_ci				    sizeof(struct cfg80211_wowlan_nd_match *),
678862306a36Sopenharmony_ci				    GFP_KERNEL);
678962306a36Sopenharmony_ci	if (!cfg->wowl.nd_info)
679062306a36Sopenharmony_ci		goto init_priv_mem_out;
679162306a36Sopenharmony_ci	cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
679262306a36Sopenharmony_ci	if (!cfg->escan_info.escan_buf)
679362306a36Sopenharmony_ci		goto init_priv_mem_out;
679462306a36Sopenharmony_ci
679562306a36Sopenharmony_ci	return 0;
679662306a36Sopenharmony_ci
679762306a36Sopenharmony_ciinit_priv_mem_out:
679862306a36Sopenharmony_ci	brcmf_deinit_priv_mem(cfg);
679962306a36Sopenharmony_ci
680062306a36Sopenharmony_ci	return -ENOMEM;
680162306a36Sopenharmony_ci}
680262306a36Sopenharmony_ci
680362306a36Sopenharmony_cistatic s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
680462306a36Sopenharmony_ci{
680562306a36Sopenharmony_ci	s32 err = 0;
680662306a36Sopenharmony_ci
680762306a36Sopenharmony_ci	cfg->scan_request = NULL;
680862306a36Sopenharmony_ci	cfg->pwr_save = true;
680962306a36Sopenharmony_ci	cfg->dongle_up = false;		/* dongle is not up yet */
681062306a36Sopenharmony_ci	err = brcmf_init_priv_mem(cfg);
681162306a36Sopenharmony_ci	if (err)
681262306a36Sopenharmony_ci		return err;
681362306a36Sopenharmony_ci	brcmf_register_event_handlers(cfg);
681462306a36Sopenharmony_ci	mutex_init(&cfg->usr_sync);
681562306a36Sopenharmony_ci	brcmf_init_escan(cfg);
681662306a36Sopenharmony_ci	brcmf_init_conf(cfg->conf);
681762306a36Sopenharmony_ci	brcmf_init_wmm_prio(cfg->ac_priority);
681862306a36Sopenharmony_ci	init_completion(&cfg->vif_disabled);
681962306a36Sopenharmony_ci	return err;
682062306a36Sopenharmony_ci}
682162306a36Sopenharmony_ci
682262306a36Sopenharmony_cistatic void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
682362306a36Sopenharmony_ci{
682462306a36Sopenharmony_ci	cfg->dongle_up = false;	/* dongle down */
682562306a36Sopenharmony_ci	brcmf_abort_scanning(cfg);
682662306a36Sopenharmony_ci	brcmf_deinit_priv_mem(cfg);
682762306a36Sopenharmony_ci	brcmf_clear_assoc_ies(cfg);
682862306a36Sopenharmony_ci}
682962306a36Sopenharmony_ci
683062306a36Sopenharmony_cistatic void init_vif_event(struct brcmf_cfg80211_vif_event *event)
683162306a36Sopenharmony_ci{
683262306a36Sopenharmony_ci	init_waitqueue_head(&event->vif_wq);
683362306a36Sopenharmony_ci	spin_lock_init(&event->vif_event_lock);
683462306a36Sopenharmony_ci}
683562306a36Sopenharmony_ci
683662306a36Sopenharmony_cistatic s32 brcmf_dongle_roam(struct brcmf_if *ifp)
683762306a36Sopenharmony_ci{
683862306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
683962306a36Sopenharmony_ci	s32 err;
684062306a36Sopenharmony_ci	u32 bcn_timeout;
684162306a36Sopenharmony_ci	__le32 roamtrigger[2];
684262306a36Sopenharmony_ci	__le32 roam_delta[2];
684362306a36Sopenharmony_ci
684462306a36Sopenharmony_ci	/* Configure beacon timeout value based upon roaming setting */
684562306a36Sopenharmony_ci	if (ifp->drvr->settings->roamoff)
684662306a36Sopenharmony_ci		bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
684762306a36Sopenharmony_ci	else
684862306a36Sopenharmony_ci		bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
684962306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
685062306a36Sopenharmony_ci	if (err) {
685162306a36Sopenharmony_ci		bphy_err(drvr, "bcn_timeout error (%d)\n", err);
685262306a36Sopenharmony_ci		goto roam_setup_done;
685362306a36Sopenharmony_ci	}
685462306a36Sopenharmony_ci
685562306a36Sopenharmony_ci	/* Enable/Disable built-in roaming to allow supplicant to take care of
685662306a36Sopenharmony_ci	 * roaming.
685762306a36Sopenharmony_ci	 */
685862306a36Sopenharmony_ci	brcmf_dbg(INFO, "Internal Roaming = %s\n",
685962306a36Sopenharmony_ci		  ifp->drvr->settings->roamoff ? "Off" : "On");
686062306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_set(ifp, "roam_off",
686162306a36Sopenharmony_ci				      ifp->drvr->settings->roamoff);
686262306a36Sopenharmony_ci	if (err) {
686362306a36Sopenharmony_ci		bphy_err(drvr, "roam_off error (%d)\n", err);
686462306a36Sopenharmony_ci		goto roam_setup_done;
686562306a36Sopenharmony_ci	}
686662306a36Sopenharmony_ci
686762306a36Sopenharmony_ci	roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
686862306a36Sopenharmony_ci	roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
686962306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
687062306a36Sopenharmony_ci				     (void *)roamtrigger, sizeof(roamtrigger));
687162306a36Sopenharmony_ci	if (err)
687262306a36Sopenharmony_ci		bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err);
687362306a36Sopenharmony_ci
687462306a36Sopenharmony_ci	roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
687562306a36Sopenharmony_ci	roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
687662306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
687762306a36Sopenharmony_ci				     (void *)roam_delta, sizeof(roam_delta));
687862306a36Sopenharmony_ci	if (err)
687962306a36Sopenharmony_ci		bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err);
688062306a36Sopenharmony_ci
688162306a36Sopenharmony_ci	return 0;
688262306a36Sopenharmony_ci
688362306a36Sopenharmony_ciroam_setup_done:
688462306a36Sopenharmony_ci	return err;
688562306a36Sopenharmony_ci}
688662306a36Sopenharmony_ci
688762306a36Sopenharmony_cistatic s32
688862306a36Sopenharmony_cibrcmf_dongle_scantime(struct brcmf_if *ifp)
688962306a36Sopenharmony_ci{
689062306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
689162306a36Sopenharmony_ci	s32 err = 0;
689262306a36Sopenharmony_ci
689362306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
689462306a36Sopenharmony_ci				    BRCMF_SCAN_CHANNEL_TIME);
689562306a36Sopenharmony_ci	if (err) {
689662306a36Sopenharmony_ci		bphy_err(drvr, "Scan assoc time error (%d)\n", err);
689762306a36Sopenharmony_ci		goto dongle_scantime_out;
689862306a36Sopenharmony_ci	}
689962306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
690062306a36Sopenharmony_ci				    BRCMF_SCAN_UNASSOC_TIME);
690162306a36Sopenharmony_ci	if (err) {
690262306a36Sopenharmony_ci		bphy_err(drvr, "Scan unassoc time error (%d)\n", err);
690362306a36Sopenharmony_ci		goto dongle_scantime_out;
690462306a36Sopenharmony_ci	}
690562306a36Sopenharmony_ci
690662306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
690762306a36Sopenharmony_ci				    BRCMF_SCAN_PASSIVE_TIME);
690862306a36Sopenharmony_ci	if (err) {
690962306a36Sopenharmony_ci		bphy_err(drvr, "Scan passive time error (%d)\n", err);
691062306a36Sopenharmony_ci		goto dongle_scantime_out;
691162306a36Sopenharmony_ci	}
691262306a36Sopenharmony_ci
691362306a36Sopenharmony_cidongle_scantime_out:
691462306a36Sopenharmony_ci	return err;
691562306a36Sopenharmony_ci}
691662306a36Sopenharmony_ci
691762306a36Sopenharmony_cistatic void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
691862306a36Sopenharmony_ci					   struct brcmu_chan *ch)
691962306a36Sopenharmony_ci{
692062306a36Sopenharmony_ci	u32 ht40_flag;
692162306a36Sopenharmony_ci
692262306a36Sopenharmony_ci	ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
692362306a36Sopenharmony_ci	if (ch->sb == BRCMU_CHAN_SB_U) {
692462306a36Sopenharmony_ci		if (ht40_flag == IEEE80211_CHAN_NO_HT40)
692562306a36Sopenharmony_ci			channel->flags &= ~IEEE80211_CHAN_NO_HT40;
692662306a36Sopenharmony_ci		channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
692762306a36Sopenharmony_ci	} else {
692862306a36Sopenharmony_ci		/* It should be one of
692962306a36Sopenharmony_ci		 * IEEE80211_CHAN_NO_HT40 or
693062306a36Sopenharmony_ci		 * IEEE80211_CHAN_NO_HT40PLUS
693162306a36Sopenharmony_ci		 */
693262306a36Sopenharmony_ci		channel->flags &= ~IEEE80211_CHAN_NO_HT40;
693362306a36Sopenharmony_ci		if (ht40_flag == IEEE80211_CHAN_NO_HT40)
693462306a36Sopenharmony_ci			channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
693562306a36Sopenharmony_ci	}
693662306a36Sopenharmony_ci}
693762306a36Sopenharmony_ci
693862306a36Sopenharmony_cistatic int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
693962306a36Sopenharmony_ci				    u32 bw_cap[])
694062306a36Sopenharmony_ci{
694162306a36Sopenharmony_ci	struct wiphy *wiphy = cfg_to_wiphy(cfg);
694262306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
694362306a36Sopenharmony_ci	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
694462306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
694562306a36Sopenharmony_ci	struct ieee80211_channel *channel;
694662306a36Sopenharmony_ci	struct brcmf_chanspec_list *list;
694762306a36Sopenharmony_ci	struct brcmu_chan ch;
694862306a36Sopenharmony_ci	int err;
694962306a36Sopenharmony_ci	u8 *pbuf;
695062306a36Sopenharmony_ci	u32 i, j;
695162306a36Sopenharmony_ci	u32 total;
695262306a36Sopenharmony_ci	u32 chaninfo;
695362306a36Sopenharmony_ci
695462306a36Sopenharmony_ci	pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
695562306a36Sopenharmony_ci
695662306a36Sopenharmony_ci	if (pbuf == NULL)
695762306a36Sopenharmony_ci		return -ENOMEM;
695862306a36Sopenharmony_ci
695962306a36Sopenharmony_ci	list = (struct brcmf_chanspec_list *)pbuf;
696062306a36Sopenharmony_ci
696162306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
696262306a36Sopenharmony_ci				       BRCMF_DCMD_MEDLEN);
696362306a36Sopenharmony_ci	if (err) {
696462306a36Sopenharmony_ci		bphy_err(drvr, "get chanspecs error (%d)\n", err);
696562306a36Sopenharmony_ci		goto fail_pbuf;
696662306a36Sopenharmony_ci	}
696762306a36Sopenharmony_ci
696862306a36Sopenharmony_ci	band = wiphy->bands[NL80211_BAND_2GHZ];
696962306a36Sopenharmony_ci	if (band)
697062306a36Sopenharmony_ci		for (i = 0; i < band->n_channels; i++)
697162306a36Sopenharmony_ci			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
697262306a36Sopenharmony_ci	band = wiphy->bands[NL80211_BAND_5GHZ];
697362306a36Sopenharmony_ci	if (band)
697462306a36Sopenharmony_ci		for (i = 0; i < band->n_channels; i++)
697562306a36Sopenharmony_ci			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
697662306a36Sopenharmony_ci
697762306a36Sopenharmony_ci	total = le32_to_cpu(list->count);
697862306a36Sopenharmony_ci	if (total > BRCMF_MAX_CHANSPEC_LIST) {
697962306a36Sopenharmony_ci		bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
698062306a36Sopenharmony_ci			 total);
698162306a36Sopenharmony_ci		err = -EINVAL;
698262306a36Sopenharmony_ci		goto fail_pbuf;
698362306a36Sopenharmony_ci	}
698462306a36Sopenharmony_ci
698562306a36Sopenharmony_ci	for (i = 0; i < total; i++) {
698662306a36Sopenharmony_ci		ch.chspec = (u16)le32_to_cpu(list->element[i]);
698762306a36Sopenharmony_ci		cfg->d11inf.decchspec(&ch);
698862306a36Sopenharmony_ci
698962306a36Sopenharmony_ci		if (ch.band == BRCMU_CHAN_BAND_2G) {
699062306a36Sopenharmony_ci			band = wiphy->bands[NL80211_BAND_2GHZ];
699162306a36Sopenharmony_ci		} else if (ch.band == BRCMU_CHAN_BAND_5G) {
699262306a36Sopenharmony_ci			band = wiphy->bands[NL80211_BAND_5GHZ];
699362306a36Sopenharmony_ci		} else {
699462306a36Sopenharmony_ci			bphy_err(drvr, "Invalid channel Spec. 0x%x.\n",
699562306a36Sopenharmony_ci				 ch.chspec);
699662306a36Sopenharmony_ci			continue;
699762306a36Sopenharmony_ci		}
699862306a36Sopenharmony_ci		if (!band)
699962306a36Sopenharmony_ci			continue;
700062306a36Sopenharmony_ci		if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
700162306a36Sopenharmony_ci		    ch.bw == BRCMU_CHAN_BW_40)
700262306a36Sopenharmony_ci			continue;
700362306a36Sopenharmony_ci		if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
700462306a36Sopenharmony_ci		    ch.bw == BRCMU_CHAN_BW_80)
700562306a36Sopenharmony_ci			continue;
700662306a36Sopenharmony_ci
700762306a36Sopenharmony_ci		channel = NULL;
700862306a36Sopenharmony_ci		for (j = 0; j < band->n_channels; j++) {
700962306a36Sopenharmony_ci			if (band->channels[j].hw_value == ch.control_ch_num) {
701062306a36Sopenharmony_ci				channel = &band->channels[j];
701162306a36Sopenharmony_ci				break;
701262306a36Sopenharmony_ci			}
701362306a36Sopenharmony_ci		}
701462306a36Sopenharmony_ci		if (!channel) {
701562306a36Sopenharmony_ci			/* It seems firmware supports some channel we never
701662306a36Sopenharmony_ci			 * considered. Something new in IEEE standard?
701762306a36Sopenharmony_ci			 */
701862306a36Sopenharmony_ci			bphy_err(drvr, "Ignoring unexpected firmware channel %d\n",
701962306a36Sopenharmony_ci				 ch.control_ch_num);
702062306a36Sopenharmony_ci			continue;
702162306a36Sopenharmony_ci		}
702262306a36Sopenharmony_ci
702362306a36Sopenharmony_ci		if (channel->orig_flags & IEEE80211_CHAN_DISABLED)
702462306a36Sopenharmony_ci			continue;
702562306a36Sopenharmony_ci
702662306a36Sopenharmony_ci		/* assuming the chanspecs order is HT20,
702762306a36Sopenharmony_ci		 * HT40 upper, HT40 lower, and VHT80.
702862306a36Sopenharmony_ci		 */
702962306a36Sopenharmony_ci		switch (ch.bw) {
703062306a36Sopenharmony_ci		case BRCMU_CHAN_BW_160:
703162306a36Sopenharmony_ci			channel->flags &= ~IEEE80211_CHAN_NO_160MHZ;
703262306a36Sopenharmony_ci			break;
703362306a36Sopenharmony_ci		case BRCMU_CHAN_BW_80:
703462306a36Sopenharmony_ci			channel->flags &= ~IEEE80211_CHAN_NO_80MHZ;
703562306a36Sopenharmony_ci			break;
703662306a36Sopenharmony_ci		case BRCMU_CHAN_BW_40:
703762306a36Sopenharmony_ci			brcmf_update_bw40_channel_flag(channel, &ch);
703862306a36Sopenharmony_ci			break;
703962306a36Sopenharmony_ci		default:
704062306a36Sopenharmony_ci			wiphy_warn(wiphy, "Firmware reported unsupported bandwidth %d\n",
704162306a36Sopenharmony_ci				   ch.bw);
704262306a36Sopenharmony_ci			fallthrough;
704362306a36Sopenharmony_ci		case BRCMU_CHAN_BW_20:
704462306a36Sopenharmony_ci			/* enable the channel and disable other bandwidths
704562306a36Sopenharmony_ci			 * for now as mentioned order assure they are enabled
704662306a36Sopenharmony_ci			 * for subsequent chanspecs.
704762306a36Sopenharmony_ci			 */
704862306a36Sopenharmony_ci			channel->flags = IEEE80211_CHAN_NO_HT40 |
704962306a36Sopenharmony_ci					 IEEE80211_CHAN_NO_80MHZ |
705062306a36Sopenharmony_ci					 IEEE80211_CHAN_NO_160MHZ;
705162306a36Sopenharmony_ci			ch.bw = BRCMU_CHAN_BW_20;
705262306a36Sopenharmony_ci			cfg->d11inf.encchspec(&ch);
705362306a36Sopenharmony_ci			chaninfo = ch.chspec;
705462306a36Sopenharmony_ci			err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
705562306a36Sopenharmony_ci						       &chaninfo);
705662306a36Sopenharmony_ci			if (!err) {
705762306a36Sopenharmony_ci				if (chaninfo & WL_CHAN_RADAR)
705862306a36Sopenharmony_ci					channel->flags |=
705962306a36Sopenharmony_ci						(IEEE80211_CHAN_RADAR |
706062306a36Sopenharmony_ci						 IEEE80211_CHAN_NO_IR);
706162306a36Sopenharmony_ci				if (chaninfo & WL_CHAN_PASSIVE)
706262306a36Sopenharmony_ci					channel->flags |=
706362306a36Sopenharmony_ci						IEEE80211_CHAN_NO_IR;
706462306a36Sopenharmony_ci			}
706562306a36Sopenharmony_ci		}
706662306a36Sopenharmony_ci	}
706762306a36Sopenharmony_ci
706862306a36Sopenharmony_cifail_pbuf:
706962306a36Sopenharmony_ci	kfree(pbuf);
707062306a36Sopenharmony_ci	return err;
707162306a36Sopenharmony_ci}
707262306a36Sopenharmony_ci
707362306a36Sopenharmony_cistatic int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
707462306a36Sopenharmony_ci{
707562306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
707662306a36Sopenharmony_ci	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
707762306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
707862306a36Sopenharmony_ci	struct brcmf_fil_bwcap_le band_bwcap;
707962306a36Sopenharmony_ci	struct brcmf_chanspec_list *list;
708062306a36Sopenharmony_ci	u8 *pbuf;
708162306a36Sopenharmony_ci	u32 val;
708262306a36Sopenharmony_ci	int err;
708362306a36Sopenharmony_ci	struct brcmu_chan ch;
708462306a36Sopenharmony_ci	u32 num_chan;
708562306a36Sopenharmony_ci	int i, j;
708662306a36Sopenharmony_ci
708762306a36Sopenharmony_ci	/* verify support for bw_cap command */
708862306a36Sopenharmony_ci	val = WLC_BAND_5G;
708962306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
709062306a36Sopenharmony_ci
709162306a36Sopenharmony_ci	if (!err) {
709262306a36Sopenharmony_ci		/* only set 2G bandwidth using bw_cap command */
709362306a36Sopenharmony_ci		band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
709462306a36Sopenharmony_ci		band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
709562306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
709662306a36Sopenharmony_ci					       sizeof(band_bwcap));
709762306a36Sopenharmony_ci	} else {
709862306a36Sopenharmony_ci		brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
709962306a36Sopenharmony_ci		val = WLC_N_BW_40ALL;
710062306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
710162306a36Sopenharmony_ci	}
710262306a36Sopenharmony_ci
710362306a36Sopenharmony_ci	if (!err) {
710462306a36Sopenharmony_ci		/* update channel info in 2G band */
710562306a36Sopenharmony_ci		pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
710662306a36Sopenharmony_ci
710762306a36Sopenharmony_ci		if (pbuf == NULL)
710862306a36Sopenharmony_ci			return -ENOMEM;
710962306a36Sopenharmony_ci
711062306a36Sopenharmony_ci		ch.band = BRCMU_CHAN_BAND_2G;
711162306a36Sopenharmony_ci		ch.bw = BRCMU_CHAN_BW_40;
711262306a36Sopenharmony_ci		ch.sb = BRCMU_CHAN_SB_NONE;
711362306a36Sopenharmony_ci		ch.chnum = 0;
711462306a36Sopenharmony_ci		cfg->d11inf.encchspec(&ch);
711562306a36Sopenharmony_ci
711662306a36Sopenharmony_ci		/* pass encoded chanspec in query */
711762306a36Sopenharmony_ci		*(__le16 *)pbuf = cpu_to_le16(ch.chspec);
711862306a36Sopenharmony_ci
711962306a36Sopenharmony_ci		err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
712062306a36Sopenharmony_ci					       BRCMF_DCMD_MEDLEN);
712162306a36Sopenharmony_ci		if (err) {
712262306a36Sopenharmony_ci			bphy_err(drvr, "get chanspecs error (%d)\n", err);
712362306a36Sopenharmony_ci			kfree(pbuf);
712462306a36Sopenharmony_ci			return err;
712562306a36Sopenharmony_ci		}
712662306a36Sopenharmony_ci
712762306a36Sopenharmony_ci		band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
712862306a36Sopenharmony_ci		list = (struct brcmf_chanspec_list *)pbuf;
712962306a36Sopenharmony_ci		num_chan = le32_to_cpu(list->count);
713062306a36Sopenharmony_ci		if (num_chan > BRCMF_MAX_CHANSPEC_LIST) {
713162306a36Sopenharmony_ci			bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
713262306a36Sopenharmony_ci				 num_chan);
713362306a36Sopenharmony_ci			kfree(pbuf);
713462306a36Sopenharmony_ci			return -EINVAL;
713562306a36Sopenharmony_ci		}
713662306a36Sopenharmony_ci
713762306a36Sopenharmony_ci		for (i = 0; i < num_chan; i++) {
713862306a36Sopenharmony_ci			ch.chspec = (u16)le32_to_cpu(list->element[i]);
713962306a36Sopenharmony_ci			cfg->d11inf.decchspec(&ch);
714062306a36Sopenharmony_ci			if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
714162306a36Sopenharmony_ci				continue;
714262306a36Sopenharmony_ci			if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
714362306a36Sopenharmony_ci				continue;
714462306a36Sopenharmony_ci			for (j = 0; j < band->n_channels; j++) {
714562306a36Sopenharmony_ci				if (band->channels[j].hw_value == ch.control_ch_num)
714662306a36Sopenharmony_ci					break;
714762306a36Sopenharmony_ci			}
714862306a36Sopenharmony_ci			if (WARN_ON(j == band->n_channels))
714962306a36Sopenharmony_ci				continue;
715062306a36Sopenharmony_ci
715162306a36Sopenharmony_ci			brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
715262306a36Sopenharmony_ci		}
715362306a36Sopenharmony_ci		kfree(pbuf);
715462306a36Sopenharmony_ci	}
715562306a36Sopenharmony_ci	return err;
715662306a36Sopenharmony_ci}
715762306a36Sopenharmony_ci
715862306a36Sopenharmony_cistatic void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
715962306a36Sopenharmony_ci{
716062306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
716162306a36Sopenharmony_ci	u32 band, mimo_bwcap;
716262306a36Sopenharmony_ci	int err;
716362306a36Sopenharmony_ci
716462306a36Sopenharmony_ci	band = WLC_BAND_2G;
716562306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
716662306a36Sopenharmony_ci	if (!err) {
716762306a36Sopenharmony_ci		bw_cap[NL80211_BAND_2GHZ] = band;
716862306a36Sopenharmony_ci		band = WLC_BAND_5G;
716962306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
717062306a36Sopenharmony_ci		if (!err) {
717162306a36Sopenharmony_ci			bw_cap[NL80211_BAND_5GHZ] = band;
717262306a36Sopenharmony_ci			return;
717362306a36Sopenharmony_ci		}
717462306a36Sopenharmony_ci		WARN_ON(1);
717562306a36Sopenharmony_ci		return;
717662306a36Sopenharmony_ci	}
717762306a36Sopenharmony_ci	brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
717862306a36Sopenharmony_ci	mimo_bwcap = 0;
717962306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
718062306a36Sopenharmony_ci	if (err)
718162306a36Sopenharmony_ci		/* assume 20MHz if firmware does not give a clue */
718262306a36Sopenharmony_ci		mimo_bwcap = WLC_N_BW_20ALL;
718362306a36Sopenharmony_ci
718462306a36Sopenharmony_ci	switch (mimo_bwcap) {
718562306a36Sopenharmony_ci	case WLC_N_BW_40ALL:
718662306a36Sopenharmony_ci		bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
718762306a36Sopenharmony_ci		fallthrough;
718862306a36Sopenharmony_ci	case WLC_N_BW_20IN2G_40IN5G:
718962306a36Sopenharmony_ci		bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
719062306a36Sopenharmony_ci		fallthrough;
719162306a36Sopenharmony_ci	case WLC_N_BW_20ALL:
719262306a36Sopenharmony_ci		bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
719362306a36Sopenharmony_ci		bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
719462306a36Sopenharmony_ci		break;
719562306a36Sopenharmony_ci	default:
719662306a36Sopenharmony_ci		bphy_err(drvr, "invalid mimo_bw_cap value\n");
719762306a36Sopenharmony_ci	}
719862306a36Sopenharmony_ci}
719962306a36Sopenharmony_ci
720062306a36Sopenharmony_cistatic void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
720162306a36Sopenharmony_ci				u32 bw_cap[2], u32 nchain)
720262306a36Sopenharmony_ci{
720362306a36Sopenharmony_ci	band->ht_cap.ht_supported = true;
720462306a36Sopenharmony_ci	if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
720562306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
720662306a36Sopenharmony_ci		band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
720762306a36Sopenharmony_ci	}
720862306a36Sopenharmony_ci	band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
720962306a36Sopenharmony_ci	band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
721062306a36Sopenharmony_ci	band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
721162306a36Sopenharmony_ci	band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
721262306a36Sopenharmony_ci	memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
721362306a36Sopenharmony_ci	band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
721462306a36Sopenharmony_ci}
721562306a36Sopenharmony_ci
721662306a36Sopenharmony_cistatic __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
721762306a36Sopenharmony_ci{
721862306a36Sopenharmony_ci	u16 mcs_map;
721962306a36Sopenharmony_ci	int i;
722062306a36Sopenharmony_ci
722162306a36Sopenharmony_ci	for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
722262306a36Sopenharmony_ci		mcs_map = (mcs_map << 2) | supp;
722362306a36Sopenharmony_ci
722462306a36Sopenharmony_ci	return cpu_to_le16(mcs_map);
722562306a36Sopenharmony_ci}
722662306a36Sopenharmony_ci
722762306a36Sopenharmony_cistatic void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
722862306a36Sopenharmony_ci				 u32 bw_cap[2], u32 nchain, u32 txstreams,
722962306a36Sopenharmony_ci				 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
723062306a36Sopenharmony_ci{
723162306a36Sopenharmony_ci	__le16 mcs_map;
723262306a36Sopenharmony_ci
723362306a36Sopenharmony_ci	/* not allowed in 2.4G band */
723462306a36Sopenharmony_ci	if (band->band == NL80211_BAND_2GHZ)
723562306a36Sopenharmony_ci		return;
723662306a36Sopenharmony_ci
723762306a36Sopenharmony_ci	band->vht_cap.vht_supported = true;
723862306a36Sopenharmony_ci	/* 80MHz is mandatory */
723962306a36Sopenharmony_ci	band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
724062306a36Sopenharmony_ci	if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
724162306a36Sopenharmony_ci		band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
724262306a36Sopenharmony_ci		band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
724362306a36Sopenharmony_ci	}
724462306a36Sopenharmony_ci	/* all support 256-QAM */
724562306a36Sopenharmony_ci	mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
724662306a36Sopenharmony_ci	band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
724762306a36Sopenharmony_ci	band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
724862306a36Sopenharmony_ci
724962306a36Sopenharmony_ci	/* Beamforming support information */
725062306a36Sopenharmony_ci	if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
725162306a36Sopenharmony_ci		band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
725262306a36Sopenharmony_ci	if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
725362306a36Sopenharmony_ci		band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
725462306a36Sopenharmony_ci	if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
725562306a36Sopenharmony_ci		band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
725662306a36Sopenharmony_ci	if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
725762306a36Sopenharmony_ci		band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
725862306a36Sopenharmony_ci
725962306a36Sopenharmony_ci	if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
726062306a36Sopenharmony_ci		band->vht_cap.cap |=
726162306a36Sopenharmony_ci			(2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
726262306a36Sopenharmony_ci		band->vht_cap.cap |= ((txstreams - 1) <<
726362306a36Sopenharmony_ci				IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
726462306a36Sopenharmony_ci		band->vht_cap.cap |=
726562306a36Sopenharmony_ci			IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
726662306a36Sopenharmony_ci	}
726762306a36Sopenharmony_ci}
726862306a36Sopenharmony_ci
726962306a36Sopenharmony_cistatic int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
727062306a36Sopenharmony_ci{
727162306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
727262306a36Sopenharmony_ci	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
727362306a36Sopenharmony_ci	struct wiphy *wiphy = cfg_to_wiphy(cfg);
727462306a36Sopenharmony_ci	u32 nmode = 0;
727562306a36Sopenharmony_ci	u32 vhtmode = 0;
727662306a36Sopenharmony_ci	u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
727762306a36Sopenharmony_ci	u32 rxchain;
727862306a36Sopenharmony_ci	u32 nchain;
727962306a36Sopenharmony_ci	int err;
728062306a36Sopenharmony_ci	s32 i;
728162306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
728262306a36Sopenharmony_ci	u32 txstreams = 0;
728362306a36Sopenharmony_ci	u32 txbf_bfe_cap = 0;
728462306a36Sopenharmony_ci	u32 txbf_bfr_cap = 0;
728562306a36Sopenharmony_ci
728662306a36Sopenharmony_ci	(void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
728762306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
728862306a36Sopenharmony_ci	if (err) {
728962306a36Sopenharmony_ci		bphy_err(drvr, "nmode error (%d)\n", err);
729062306a36Sopenharmony_ci	} else {
729162306a36Sopenharmony_ci		brcmf_get_bwcap(ifp, bw_cap);
729262306a36Sopenharmony_ci	}
729362306a36Sopenharmony_ci	brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
729462306a36Sopenharmony_ci		  nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
729562306a36Sopenharmony_ci		  bw_cap[NL80211_BAND_5GHZ]);
729662306a36Sopenharmony_ci
729762306a36Sopenharmony_ci	err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
729862306a36Sopenharmony_ci	if (err) {
729962306a36Sopenharmony_ci		/* rxchain unsupported by firmware of older chips */
730062306a36Sopenharmony_ci		if (err == -EBADE)
730162306a36Sopenharmony_ci			bphy_info_once(drvr, "rxchain unsupported\n");
730262306a36Sopenharmony_ci		else
730362306a36Sopenharmony_ci			bphy_err(drvr, "rxchain error (%d)\n", err);
730462306a36Sopenharmony_ci
730562306a36Sopenharmony_ci		nchain = 1;
730662306a36Sopenharmony_ci	} else {
730762306a36Sopenharmony_ci		for (nchain = 0; rxchain; nchain++)
730862306a36Sopenharmony_ci			rxchain = rxchain & (rxchain - 1);
730962306a36Sopenharmony_ci	}
731062306a36Sopenharmony_ci	brcmf_dbg(INFO, "nchain=%d\n", nchain);
731162306a36Sopenharmony_ci
731262306a36Sopenharmony_ci	err = brcmf_construct_chaninfo(cfg, bw_cap);
731362306a36Sopenharmony_ci	if (err) {
731462306a36Sopenharmony_ci		bphy_err(drvr, "brcmf_construct_chaninfo failed (%d)\n", err);
731562306a36Sopenharmony_ci		return err;
731662306a36Sopenharmony_ci	}
731762306a36Sopenharmony_ci
731862306a36Sopenharmony_ci	if (vhtmode) {
731962306a36Sopenharmony_ci		(void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
732062306a36Sopenharmony_ci		(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
732162306a36Sopenharmony_ci					      &txbf_bfe_cap);
732262306a36Sopenharmony_ci		(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
732362306a36Sopenharmony_ci					      &txbf_bfr_cap);
732462306a36Sopenharmony_ci	}
732562306a36Sopenharmony_ci
732662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
732762306a36Sopenharmony_ci		band = wiphy->bands[i];
732862306a36Sopenharmony_ci		if (band == NULL)
732962306a36Sopenharmony_ci			continue;
733062306a36Sopenharmony_ci
733162306a36Sopenharmony_ci		if (nmode)
733262306a36Sopenharmony_ci			brcmf_update_ht_cap(band, bw_cap, nchain);
733362306a36Sopenharmony_ci		if (vhtmode)
733462306a36Sopenharmony_ci			brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
733562306a36Sopenharmony_ci					     txbf_bfe_cap, txbf_bfr_cap);
733662306a36Sopenharmony_ci	}
733762306a36Sopenharmony_ci
733862306a36Sopenharmony_ci	return 0;
733962306a36Sopenharmony_ci}
734062306a36Sopenharmony_ci
734162306a36Sopenharmony_cistatic const struct ieee80211_txrx_stypes
734262306a36Sopenharmony_cibrcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
734362306a36Sopenharmony_ci	[NL80211_IFTYPE_STATION] = {
734462306a36Sopenharmony_ci		.tx = 0xffff,
734562306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
734662306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
734762306a36Sopenharmony_ci	},
734862306a36Sopenharmony_ci	[NL80211_IFTYPE_P2P_CLIENT] = {
734962306a36Sopenharmony_ci		.tx = 0xffff,
735062306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
735162306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
735262306a36Sopenharmony_ci	},
735362306a36Sopenharmony_ci	[NL80211_IFTYPE_P2P_GO] = {
735462306a36Sopenharmony_ci		.tx = 0xffff,
735562306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
735662306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
735762306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
735862306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_DISASSOC >> 4) |
735962306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_AUTH >> 4) |
736062306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_DEAUTH >> 4) |
736162306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_ACTION >> 4)
736262306a36Sopenharmony_ci	},
736362306a36Sopenharmony_ci	[NL80211_IFTYPE_P2P_DEVICE] = {
736462306a36Sopenharmony_ci		.tx = 0xffff,
736562306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
736662306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
736762306a36Sopenharmony_ci	},
736862306a36Sopenharmony_ci	[NL80211_IFTYPE_AP] = {
736962306a36Sopenharmony_ci		.tx = 0xffff,
737062306a36Sopenharmony_ci		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
737162306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
737262306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
737362306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_DISASSOC >> 4) |
737462306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_AUTH >> 4) |
737562306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_DEAUTH >> 4) |
737662306a36Sopenharmony_ci		      BIT(IEEE80211_STYPE_ACTION >> 4)
737762306a36Sopenharmony_ci	}
737862306a36Sopenharmony_ci};
737962306a36Sopenharmony_ci
738062306a36Sopenharmony_ci/**
738162306a36Sopenharmony_ci * brcmf_setup_ifmodes() - determine interface modes and combinations.
738262306a36Sopenharmony_ci *
738362306a36Sopenharmony_ci * @wiphy: wiphy object.
738462306a36Sopenharmony_ci * @ifp: interface object needed for feat module api.
738562306a36Sopenharmony_ci *
738662306a36Sopenharmony_ci * The interface modes and combinations are determined dynamically here
738762306a36Sopenharmony_ci * based on firmware functionality.
738862306a36Sopenharmony_ci *
738962306a36Sopenharmony_ci * no p2p and no mbss:
739062306a36Sopenharmony_ci *
739162306a36Sopenharmony_ci *	#STA <= 1, #AP <= 1, channels = 1, 2 total
739262306a36Sopenharmony_ci *
739362306a36Sopenharmony_ci * no p2p and mbss:
739462306a36Sopenharmony_ci *
739562306a36Sopenharmony_ci *	#STA <= 1, #AP <= 1, channels = 1, 2 total
739662306a36Sopenharmony_ci *	#AP <= 4, matching BI, channels = 1, 4 total
739762306a36Sopenharmony_ci *
739862306a36Sopenharmony_ci * no p2p and rsdb:
739962306a36Sopenharmony_ci *	#STA <= 1, #AP <= 2, channels = 2, 4 total
740062306a36Sopenharmony_ci *
740162306a36Sopenharmony_ci * p2p, no mchan, and mbss:
740262306a36Sopenharmony_ci *
740362306a36Sopenharmony_ci *	#STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
740462306a36Sopenharmony_ci *	#STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
740562306a36Sopenharmony_ci *	#AP <= 4, matching BI, channels = 1, 4 total
740662306a36Sopenharmony_ci *
740762306a36Sopenharmony_ci * p2p, mchan, and mbss:
740862306a36Sopenharmony_ci *
740962306a36Sopenharmony_ci *	#STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
741062306a36Sopenharmony_ci *	#STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
741162306a36Sopenharmony_ci *	#AP <= 4, matching BI, channels = 1, 4 total
741262306a36Sopenharmony_ci *
741362306a36Sopenharmony_ci * p2p, rsdb, and no mbss:
741462306a36Sopenharmony_ci *	#STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,
741562306a36Sopenharmony_ci *	 channels = 2, 4 total
741662306a36Sopenharmony_ci */
741762306a36Sopenharmony_cistatic int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
741862306a36Sopenharmony_ci{
741962306a36Sopenharmony_ci	struct ieee80211_iface_combination *combo = NULL;
742062306a36Sopenharmony_ci	struct ieee80211_iface_limit *c0_limits = NULL;
742162306a36Sopenharmony_ci	struct ieee80211_iface_limit *p2p_limits = NULL;
742262306a36Sopenharmony_ci	struct ieee80211_iface_limit *mbss_limits = NULL;
742362306a36Sopenharmony_ci	bool mon_flag, mbss, p2p, rsdb, mchan;
742462306a36Sopenharmony_ci	int i, c, n_combos, n_limits;
742562306a36Sopenharmony_ci
742662306a36Sopenharmony_ci	mon_flag = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FLAG);
742762306a36Sopenharmony_ci	mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
742862306a36Sopenharmony_ci	p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
742962306a36Sopenharmony_ci	rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB);
743062306a36Sopenharmony_ci	mchan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN);
743162306a36Sopenharmony_ci
743262306a36Sopenharmony_ci	n_combos = 1 + !!(p2p && !rsdb) + !!mbss;
743362306a36Sopenharmony_ci	combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
743462306a36Sopenharmony_ci	if (!combo)
743562306a36Sopenharmony_ci		goto err;
743662306a36Sopenharmony_ci
743762306a36Sopenharmony_ci	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
743862306a36Sopenharmony_ci				 BIT(NL80211_IFTYPE_ADHOC) |
743962306a36Sopenharmony_ci				 BIT(NL80211_IFTYPE_AP);
744062306a36Sopenharmony_ci	if (mon_flag)
744162306a36Sopenharmony_ci		wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
744262306a36Sopenharmony_ci	if (p2p)
744362306a36Sopenharmony_ci		wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
744462306a36Sopenharmony_ci					  BIT(NL80211_IFTYPE_P2P_GO) |
744562306a36Sopenharmony_ci					  BIT(NL80211_IFTYPE_P2P_DEVICE);
744662306a36Sopenharmony_ci
744762306a36Sopenharmony_ci	c = 0;
744862306a36Sopenharmony_ci	i = 0;
744962306a36Sopenharmony_ci	n_limits = 1 + mon_flag + (p2p ? 2 : 0) + (rsdb || !p2p);
745062306a36Sopenharmony_ci	c0_limits = kcalloc(n_limits, sizeof(*c0_limits), GFP_KERNEL);
745162306a36Sopenharmony_ci	if (!c0_limits)
745262306a36Sopenharmony_ci		goto err;
745362306a36Sopenharmony_ci
745462306a36Sopenharmony_ci	combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan));
745562306a36Sopenharmony_ci	c0_limits[i].max = 1 + (p2p && mchan);
745662306a36Sopenharmony_ci	c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
745762306a36Sopenharmony_ci	if (mon_flag) {
745862306a36Sopenharmony_ci		c0_limits[i].max = 1;
745962306a36Sopenharmony_ci		c0_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
746062306a36Sopenharmony_ci	}
746162306a36Sopenharmony_ci	if (p2p) {
746262306a36Sopenharmony_ci		c0_limits[i].max = 1;
746362306a36Sopenharmony_ci		c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
746462306a36Sopenharmony_ci		c0_limits[i].max = 1 + rsdb;
746562306a36Sopenharmony_ci		c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
746662306a36Sopenharmony_ci				       BIT(NL80211_IFTYPE_P2P_GO);
746762306a36Sopenharmony_ci	}
746862306a36Sopenharmony_ci	if (p2p && rsdb) {
746962306a36Sopenharmony_ci		c0_limits[i].max = 2;
747062306a36Sopenharmony_ci		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
747162306a36Sopenharmony_ci		combo[c].max_interfaces = 4;
747262306a36Sopenharmony_ci	} else if (p2p) {
747362306a36Sopenharmony_ci		combo[c].max_interfaces = i;
747462306a36Sopenharmony_ci	} else if (rsdb) {
747562306a36Sopenharmony_ci		c0_limits[i].max = 2;
747662306a36Sopenharmony_ci		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
747762306a36Sopenharmony_ci		combo[c].max_interfaces = 3;
747862306a36Sopenharmony_ci	} else {
747962306a36Sopenharmony_ci		c0_limits[i].max = 1;
748062306a36Sopenharmony_ci		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
748162306a36Sopenharmony_ci		combo[c].max_interfaces = i;
748262306a36Sopenharmony_ci	}
748362306a36Sopenharmony_ci	combo[c].n_limits = i;
748462306a36Sopenharmony_ci	combo[c].limits = c0_limits;
748562306a36Sopenharmony_ci
748662306a36Sopenharmony_ci	if (p2p && !rsdb) {
748762306a36Sopenharmony_ci		c++;
748862306a36Sopenharmony_ci		i = 0;
748962306a36Sopenharmony_ci		p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
749062306a36Sopenharmony_ci		if (!p2p_limits)
749162306a36Sopenharmony_ci			goto err;
749262306a36Sopenharmony_ci		p2p_limits[i].max = 1;
749362306a36Sopenharmony_ci		p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
749462306a36Sopenharmony_ci		p2p_limits[i].max = 1;
749562306a36Sopenharmony_ci		p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
749662306a36Sopenharmony_ci		p2p_limits[i].max = 1;
749762306a36Sopenharmony_ci		p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
749862306a36Sopenharmony_ci		p2p_limits[i].max = 1;
749962306a36Sopenharmony_ci		p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
750062306a36Sopenharmony_ci		combo[c].num_different_channels = 1;
750162306a36Sopenharmony_ci		combo[c].max_interfaces = i;
750262306a36Sopenharmony_ci		combo[c].n_limits = i;
750362306a36Sopenharmony_ci		combo[c].limits = p2p_limits;
750462306a36Sopenharmony_ci	}
750562306a36Sopenharmony_ci
750662306a36Sopenharmony_ci	if (mbss) {
750762306a36Sopenharmony_ci		c++;
750862306a36Sopenharmony_ci		i = 0;
750962306a36Sopenharmony_ci		n_limits = 1 + mon_flag;
751062306a36Sopenharmony_ci		mbss_limits = kcalloc(n_limits, sizeof(*mbss_limits),
751162306a36Sopenharmony_ci				      GFP_KERNEL);
751262306a36Sopenharmony_ci		if (!mbss_limits)
751362306a36Sopenharmony_ci			goto err;
751462306a36Sopenharmony_ci		mbss_limits[i].max = 4;
751562306a36Sopenharmony_ci		mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);
751662306a36Sopenharmony_ci		if (mon_flag) {
751762306a36Sopenharmony_ci			mbss_limits[i].max = 1;
751862306a36Sopenharmony_ci			mbss_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
751962306a36Sopenharmony_ci		}
752062306a36Sopenharmony_ci		combo[c].beacon_int_infra_match = true;
752162306a36Sopenharmony_ci		combo[c].num_different_channels = 1;
752262306a36Sopenharmony_ci		combo[c].max_interfaces = 4 + mon_flag;
752362306a36Sopenharmony_ci		combo[c].n_limits = i;
752462306a36Sopenharmony_ci		combo[c].limits = mbss_limits;
752562306a36Sopenharmony_ci	}
752662306a36Sopenharmony_ci
752762306a36Sopenharmony_ci	wiphy->n_iface_combinations = n_combos;
752862306a36Sopenharmony_ci	wiphy->iface_combinations = combo;
752962306a36Sopenharmony_ci	return 0;
753062306a36Sopenharmony_ci
753162306a36Sopenharmony_cierr:
753262306a36Sopenharmony_ci	kfree(c0_limits);
753362306a36Sopenharmony_ci	kfree(p2p_limits);
753462306a36Sopenharmony_ci	kfree(mbss_limits);
753562306a36Sopenharmony_ci	kfree(combo);
753662306a36Sopenharmony_ci	return -ENOMEM;
753762306a36Sopenharmony_ci}
753862306a36Sopenharmony_ci
753962306a36Sopenharmony_ci#ifdef CONFIG_PM
754062306a36Sopenharmony_cistatic const struct wiphy_wowlan_support brcmf_wowlan_support = {
754162306a36Sopenharmony_ci	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
754262306a36Sopenharmony_ci	.n_patterns = BRCMF_WOWL_MAXPATTERNS,
754362306a36Sopenharmony_ci	.pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
754462306a36Sopenharmony_ci	.pattern_min_len = 1,
754562306a36Sopenharmony_ci	.max_pkt_offset = 1500,
754662306a36Sopenharmony_ci};
754762306a36Sopenharmony_ci#endif
754862306a36Sopenharmony_ci
754962306a36Sopenharmony_cistatic void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
755062306a36Sopenharmony_ci{
755162306a36Sopenharmony_ci#ifdef CONFIG_PM
755262306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
755362306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
755462306a36Sopenharmony_ci	struct wiphy_wowlan_support *wowl;
755562306a36Sopenharmony_ci
755662306a36Sopenharmony_ci	wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
755762306a36Sopenharmony_ci		       GFP_KERNEL);
755862306a36Sopenharmony_ci	if (!wowl) {
755962306a36Sopenharmony_ci		bphy_err(drvr, "only support basic wowlan features\n");
756062306a36Sopenharmony_ci		wiphy->wowlan = &brcmf_wowlan_support;
756162306a36Sopenharmony_ci		return;
756262306a36Sopenharmony_ci	}
756362306a36Sopenharmony_ci
756462306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
756562306a36Sopenharmony_ci		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
756662306a36Sopenharmony_ci			wowl->flags |= WIPHY_WOWLAN_NET_DETECT;
756762306a36Sopenharmony_ci			wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
756862306a36Sopenharmony_ci			init_waitqueue_head(&cfg->wowl.nd_data_wait);
756962306a36Sopenharmony_ci		}
757062306a36Sopenharmony_ci	}
757162306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
757262306a36Sopenharmony_ci		wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
757362306a36Sopenharmony_ci		wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
757462306a36Sopenharmony_ci	}
757562306a36Sopenharmony_ci
757662306a36Sopenharmony_ci	wiphy->wowlan = wowl;
757762306a36Sopenharmony_ci#endif
757862306a36Sopenharmony_ci}
757962306a36Sopenharmony_ci
758062306a36Sopenharmony_cistatic int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
758162306a36Sopenharmony_ci{
758262306a36Sopenharmony_ci	struct brcmf_pub *drvr = ifp->drvr;
758362306a36Sopenharmony_ci	const struct ieee80211_iface_combination *combo;
758462306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
758562306a36Sopenharmony_ci	u16 max_interfaces = 0;
758662306a36Sopenharmony_ci	bool gscan;
758762306a36Sopenharmony_ci	__le32 bandlist[3];
758862306a36Sopenharmony_ci	u32 n_bands;
758962306a36Sopenharmony_ci	int err, i;
759062306a36Sopenharmony_ci
759162306a36Sopenharmony_ci	wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
759262306a36Sopenharmony_ci	wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
759362306a36Sopenharmony_ci	wiphy->max_num_pmkids = BRCMF_MAXPMKID;
759462306a36Sopenharmony_ci
759562306a36Sopenharmony_ci	err = brcmf_setup_ifmodes(wiphy, ifp);
759662306a36Sopenharmony_ci	if (err)
759762306a36Sopenharmony_ci		return err;
759862306a36Sopenharmony_ci
759962306a36Sopenharmony_ci	for (i = 0, combo = wiphy->iface_combinations;
760062306a36Sopenharmony_ci	     i < wiphy->n_iface_combinations; i++, combo++) {
760162306a36Sopenharmony_ci		max_interfaces = max(max_interfaces, combo->max_interfaces);
760262306a36Sopenharmony_ci	}
760362306a36Sopenharmony_ci
760462306a36Sopenharmony_ci	for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
760562306a36Sopenharmony_ci	     i++) {
760662306a36Sopenharmony_ci		u8 *addr = drvr->addresses[i].addr;
760762306a36Sopenharmony_ci
760862306a36Sopenharmony_ci		memcpy(addr, drvr->mac, ETH_ALEN);
760962306a36Sopenharmony_ci		if (i) {
761062306a36Sopenharmony_ci			addr[0] |= BIT(1);
761162306a36Sopenharmony_ci			addr[ETH_ALEN - 1] ^= i;
761262306a36Sopenharmony_ci		}
761362306a36Sopenharmony_ci	}
761462306a36Sopenharmony_ci	wiphy->addresses = drvr->addresses;
761562306a36Sopenharmony_ci	wiphy->n_addresses = i;
761662306a36Sopenharmony_ci
761762306a36Sopenharmony_ci	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
761862306a36Sopenharmony_ci	wiphy->cipher_suites = brcmf_cipher_suites;
761962306a36Sopenharmony_ci	wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
762062306a36Sopenharmony_ci	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
762162306a36Sopenharmony_ci		wiphy->n_cipher_suites--;
762262306a36Sopenharmony_ci	wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |
762362306a36Sopenharmony_ci				    BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
762462306a36Sopenharmony_ci				    BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
762562306a36Sopenharmony_ci
762662306a36Sopenharmony_ci	wiphy->flags |= WIPHY_FLAG_NETNS_OK |
762762306a36Sopenharmony_ci			WIPHY_FLAG_PS_ON_BY_DEFAULT |
762862306a36Sopenharmony_ci			WIPHY_FLAG_HAVE_AP_SME |
762962306a36Sopenharmony_ci			WIPHY_FLAG_OFFCHAN_TX |
763062306a36Sopenharmony_ci			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
763162306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
763262306a36Sopenharmony_ci		wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
763362306a36Sopenharmony_ci	if (!ifp->drvr->settings->roamoff)
763462306a36Sopenharmony_ci		wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
763562306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) {
763662306a36Sopenharmony_ci		wiphy_ext_feature_set(wiphy,
763762306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
763862306a36Sopenharmony_ci		wiphy_ext_feature_set(wiphy,
763962306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
764062306a36Sopenharmony_ci		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
764162306a36Sopenharmony_ci			wiphy_ext_feature_set(wiphy,
764262306a36Sopenharmony_ci					      NL80211_EXT_FEATURE_SAE_OFFLOAD);
764362306a36Sopenharmony_ci	}
764462306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWAUTH)) {
764562306a36Sopenharmony_ci		wiphy_ext_feature_set(wiphy,
764662306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK);
764762306a36Sopenharmony_ci		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
764862306a36Sopenharmony_ci			wiphy_ext_feature_set(wiphy,
764962306a36Sopenharmony_ci					      NL80211_EXT_FEATURE_SAE_OFFLOAD_AP);
765062306a36Sopenharmony_ci	}
765162306a36Sopenharmony_ci	wiphy->mgmt_stypes = brcmf_txrx_stypes;
765262306a36Sopenharmony_ci	wiphy->max_remain_on_channel_duration = 5000;
765362306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
765462306a36Sopenharmony_ci		gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN);
765562306a36Sopenharmony_ci		brcmf_pno_wiphy_params(wiphy, gscan);
765662306a36Sopenharmony_ci	}
765762306a36Sopenharmony_ci	/* vendor commands/events support */
765862306a36Sopenharmony_ci	wiphy->vendor_commands = brcmf_vendor_cmds;
765962306a36Sopenharmony_ci	wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
766062306a36Sopenharmony_ci
766162306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
766262306a36Sopenharmony_ci		brcmf_wiphy_wowl_params(wiphy, ifp);
766362306a36Sopenharmony_ci	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
766462306a36Sopenharmony_ci				     sizeof(bandlist));
766562306a36Sopenharmony_ci	if (err) {
766662306a36Sopenharmony_ci		bphy_err(drvr, "could not obtain band info: err=%d\n", err);
766762306a36Sopenharmony_ci		return err;
766862306a36Sopenharmony_ci	}
766962306a36Sopenharmony_ci	/* first entry in bandlist is number of bands */
767062306a36Sopenharmony_ci	n_bands = le32_to_cpu(bandlist[0]);
767162306a36Sopenharmony_ci	for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
767262306a36Sopenharmony_ci		if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
767362306a36Sopenharmony_ci			band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
767462306a36Sopenharmony_ci				       GFP_KERNEL);
767562306a36Sopenharmony_ci			if (!band)
767662306a36Sopenharmony_ci				return -ENOMEM;
767762306a36Sopenharmony_ci
767862306a36Sopenharmony_ci			band->channels = kmemdup(&__wl_2ghz_channels,
767962306a36Sopenharmony_ci						 sizeof(__wl_2ghz_channels),
768062306a36Sopenharmony_ci						 GFP_KERNEL);
768162306a36Sopenharmony_ci			if (!band->channels) {
768262306a36Sopenharmony_ci				kfree(band);
768362306a36Sopenharmony_ci				return -ENOMEM;
768462306a36Sopenharmony_ci			}
768562306a36Sopenharmony_ci
768662306a36Sopenharmony_ci			band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
768762306a36Sopenharmony_ci			wiphy->bands[NL80211_BAND_2GHZ] = band;
768862306a36Sopenharmony_ci		}
768962306a36Sopenharmony_ci		if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
769062306a36Sopenharmony_ci			band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
769162306a36Sopenharmony_ci				       GFP_KERNEL);
769262306a36Sopenharmony_ci			if (!band)
769362306a36Sopenharmony_ci				return -ENOMEM;
769462306a36Sopenharmony_ci
769562306a36Sopenharmony_ci			band->channels = kmemdup(&__wl_5ghz_channels,
769662306a36Sopenharmony_ci						 sizeof(__wl_5ghz_channels),
769762306a36Sopenharmony_ci						 GFP_KERNEL);
769862306a36Sopenharmony_ci			if (!band->channels) {
769962306a36Sopenharmony_ci				kfree(band);
770062306a36Sopenharmony_ci				return -ENOMEM;
770162306a36Sopenharmony_ci			}
770262306a36Sopenharmony_ci
770362306a36Sopenharmony_ci			band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
770462306a36Sopenharmony_ci			wiphy->bands[NL80211_BAND_5GHZ] = band;
770562306a36Sopenharmony_ci		}
770662306a36Sopenharmony_ci	}
770762306a36Sopenharmony_ci
770862306a36Sopenharmony_ci	if (wiphy->bands[NL80211_BAND_5GHZ] &&
770962306a36Sopenharmony_ci	    brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DOT11H))
771062306a36Sopenharmony_ci		wiphy_ext_feature_set(wiphy,
771162306a36Sopenharmony_ci				      NL80211_EXT_FEATURE_DFS_OFFLOAD);
771262306a36Sopenharmony_ci
771362306a36Sopenharmony_ci	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
771462306a36Sopenharmony_ci
771562306a36Sopenharmony_ci	wiphy_read_of_freq_limits(wiphy);
771662306a36Sopenharmony_ci
771762306a36Sopenharmony_ci	return 0;
771862306a36Sopenharmony_ci}
771962306a36Sopenharmony_ci
772062306a36Sopenharmony_cistatic s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
772162306a36Sopenharmony_ci{
772262306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
772362306a36Sopenharmony_ci	struct net_device *ndev;
772462306a36Sopenharmony_ci	struct wireless_dev *wdev;
772562306a36Sopenharmony_ci	struct brcmf_if *ifp;
772662306a36Sopenharmony_ci	s32 power_mode;
772762306a36Sopenharmony_ci	s32 err = 0;
772862306a36Sopenharmony_ci
772962306a36Sopenharmony_ci	if (cfg->dongle_up)
773062306a36Sopenharmony_ci		return err;
773162306a36Sopenharmony_ci
773262306a36Sopenharmony_ci	ndev = cfg_to_ndev(cfg);
773362306a36Sopenharmony_ci	wdev = ndev->ieee80211_ptr;
773462306a36Sopenharmony_ci	ifp = netdev_priv(ndev);
773562306a36Sopenharmony_ci
773662306a36Sopenharmony_ci	/* make sure RF is ready for work */
773762306a36Sopenharmony_ci	brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
773862306a36Sopenharmony_ci
773962306a36Sopenharmony_ci	brcmf_dongle_scantime(ifp);
774062306a36Sopenharmony_ci
774162306a36Sopenharmony_ci	power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
774262306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
774362306a36Sopenharmony_ci	if (err)
774462306a36Sopenharmony_ci		goto default_conf_out;
774562306a36Sopenharmony_ci	brcmf_dbg(INFO, "power save set to %s\n",
774662306a36Sopenharmony_ci		  (power_mode ? "enabled" : "disabled"));
774762306a36Sopenharmony_ci
774862306a36Sopenharmony_ci	err = brcmf_dongle_roam(ifp);
774962306a36Sopenharmony_ci	if (err)
775062306a36Sopenharmony_ci		goto default_conf_out;
775162306a36Sopenharmony_ci	err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
775262306a36Sopenharmony_ci					  NULL);
775362306a36Sopenharmony_ci	if (err)
775462306a36Sopenharmony_ci		goto default_conf_out;
775562306a36Sopenharmony_ci
775662306a36Sopenharmony_ci	brcmf_configure_arp_nd_offload(ifp, true);
775762306a36Sopenharmony_ci
775862306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1);
775962306a36Sopenharmony_ci	if (err) {
776062306a36Sopenharmony_ci		bphy_err(drvr, "failed to set frameburst mode\n");
776162306a36Sopenharmony_ci		goto default_conf_out;
776262306a36Sopenharmony_ci	}
776362306a36Sopenharmony_ci
776462306a36Sopenharmony_ci	cfg->dongle_up = true;
776562306a36Sopenharmony_cidefault_conf_out:
776662306a36Sopenharmony_ci
776762306a36Sopenharmony_ci	return err;
776862306a36Sopenharmony_ci
776962306a36Sopenharmony_ci}
777062306a36Sopenharmony_ci
777162306a36Sopenharmony_cistatic s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
777262306a36Sopenharmony_ci{
777362306a36Sopenharmony_ci	set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
777462306a36Sopenharmony_ci
777562306a36Sopenharmony_ci	return brcmf_config_dongle(ifp->drvr->config);
777662306a36Sopenharmony_ci}
777762306a36Sopenharmony_ci
777862306a36Sopenharmony_cistatic s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
777962306a36Sopenharmony_ci{
778062306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
778162306a36Sopenharmony_ci
778262306a36Sopenharmony_ci	/*
778362306a36Sopenharmony_ci	 * While going down, if associated with AP disassociate
778462306a36Sopenharmony_ci	 * from AP to save power
778562306a36Sopenharmony_ci	 */
778662306a36Sopenharmony_ci	if (check_vif_up(ifp->vif)) {
778762306a36Sopenharmony_ci		brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED, true);
778862306a36Sopenharmony_ci
778962306a36Sopenharmony_ci		/* Make sure WPA_Supplicant receives all the event
779062306a36Sopenharmony_ci		   generated due to DISASSOC call to the fw to keep
779162306a36Sopenharmony_ci		   the state fw and WPA_Supplicant state consistent
779262306a36Sopenharmony_ci		 */
779362306a36Sopenharmony_ci		brcmf_delay(500);
779462306a36Sopenharmony_ci	}
779562306a36Sopenharmony_ci
779662306a36Sopenharmony_ci	brcmf_abort_scanning(cfg);
779762306a36Sopenharmony_ci	clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
779862306a36Sopenharmony_ci
779962306a36Sopenharmony_ci	return 0;
780062306a36Sopenharmony_ci}
780162306a36Sopenharmony_ci
780262306a36Sopenharmony_cis32 brcmf_cfg80211_up(struct net_device *ndev)
780362306a36Sopenharmony_ci{
780462306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
780562306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
780662306a36Sopenharmony_ci	s32 err = 0;
780762306a36Sopenharmony_ci
780862306a36Sopenharmony_ci	mutex_lock(&cfg->usr_sync);
780962306a36Sopenharmony_ci	err = __brcmf_cfg80211_up(ifp);
781062306a36Sopenharmony_ci	mutex_unlock(&cfg->usr_sync);
781162306a36Sopenharmony_ci
781262306a36Sopenharmony_ci	return err;
781362306a36Sopenharmony_ci}
781462306a36Sopenharmony_ci
781562306a36Sopenharmony_cis32 brcmf_cfg80211_down(struct net_device *ndev)
781662306a36Sopenharmony_ci{
781762306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(ndev);
781862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
781962306a36Sopenharmony_ci	s32 err = 0;
782062306a36Sopenharmony_ci
782162306a36Sopenharmony_ci	mutex_lock(&cfg->usr_sync);
782262306a36Sopenharmony_ci	err = __brcmf_cfg80211_down(ifp);
782362306a36Sopenharmony_ci	mutex_unlock(&cfg->usr_sync);
782462306a36Sopenharmony_ci
782562306a36Sopenharmony_ci	return err;
782662306a36Sopenharmony_ci}
782762306a36Sopenharmony_ci
782862306a36Sopenharmony_cienum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
782962306a36Sopenharmony_ci{
783062306a36Sopenharmony_ci	struct wireless_dev *wdev = &ifp->vif->wdev;
783162306a36Sopenharmony_ci
783262306a36Sopenharmony_ci	return wdev->iftype;
783362306a36Sopenharmony_ci}
783462306a36Sopenharmony_ci
783562306a36Sopenharmony_cibool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
783662306a36Sopenharmony_ci			     unsigned long state)
783762306a36Sopenharmony_ci{
783862306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
783962306a36Sopenharmony_ci
784062306a36Sopenharmony_ci	list_for_each_entry(vif, &cfg->vif_list, list) {
784162306a36Sopenharmony_ci		if (test_bit(state, &vif->sme_state))
784262306a36Sopenharmony_ci			return true;
784362306a36Sopenharmony_ci	}
784462306a36Sopenharmony_ci	return false;
784562306a36Sopenharmony_ci}
784662306a36Sopenharmony_ci
784762306a36Sopenharmony_cistatic inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
784862306a36Sopenharmony_ci				    u8 action)
784962306a36Sopenharmony_ci{
785062306a36Sopenharmony_ci	u8 evt_action;
785162306a36Sopenharmony_ci
785262306a36Sopenharmony_ci	spin_lock(&event->vif_event_lock);
785362306a36Sopenharmony_ci	evt_action = event->action;
785462306a36Sopenharmony_ci	spin_unlock(&event->vif_event_lock);
785562306a36Sopenharmony_ci	return evt_action == action;
785662306a36Sopenharmony_ci}
785762306a36Sopenharmony_ci
785862306a36Sopenharmony_civoid brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
785962306a36Sopenharmony_ci				  struct brcmf_cfg80211_vif *vif)
786062306a36Sopenharmony_ci{
786162306a36Sopenharmony_ci	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
786262306a36Sopenharmony_ci
786362306a36Sopenharmony_ci	spin_lock(&event->vif_event_lock);
786462306a36Sopenharmony_ci	event->vif = vif;
786562306a36Sopenharmony_ci	event->action = 0;
786662306a36Sopenharmony_ci	spin_unlock(&event->vif_event_lock);
786762306a36Sopenharmony_ci}
786862306a36Sopenharmony_ci
786962306a36Sopenharmony_cibool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
787062306a36Sopenharmony_ci{
787162306a36Sopenharmony_ci	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
787262306a36Sopenharmony_ci	bool armed;
787362306a36Sopenharmony_ci
787462306a36Sopenharmony_ci	spin_lock(&event->vif_event_lock);
787562306a36Sopenharmony_ci	armed = event->vif != NULL;
787662306a36Sopenharmony_ci	spin_unlock(&event->vif_event_lock);
787762306a36Sopenharmony_ci
787862306a36Sopenharmony_ci	return armed;
787962306a36Sopenharmony_ci}
788062306a36Sopenharmony_ci
788162306a36Sopenharmony_ciint brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
788262306a36Sopenharmony_ci				  u8 action, ulong timeout)
788362306a36Sopenharmony_ci{
788462306a36Sopenharmony_ci	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
788562306a36Sopenharmony_ci
788662306a36Sopenharmony_ci	return wait_event_timeout(event->vif_wq,
788762306a36Sopenharmony_ci				  vif_event_equals(event, action), timeout);
788862306a36Sopenharmony_ci}
788962306a36Sopenharmony_ci
789062306a36Sopenharmony_cistatic bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr)
789162306a36Sopenharmony_ci{
789262306a36Sopenharmony_ci	if (drvr->settings->trivial_ccode_map)
789362306a36Sopenharmony_ci		return true;
789462306a36Sopenharmony_ci
789562306a36Sopenharmony_ci	switch (drvr->bus_if->chip) {
789662306a36Sopenharmony_ci	case BRCM_CC_43430_CHIP_ID:
789762306a36Sopenharmony_ci	case BRCM_CC_4345_CHIP_ID:
789862306a36Sopenharmony_ci	case BRCM_CC_4356_CHIP_ID:
789962306a36Sopenharmony_ci	case BRCM_CC_43602_CHIP_ID:
790062306a36Sopenharmony_ci		return true;
790162306a36Sopenharmony_ci	default:
790262306a36Sopenharmony_ci		return false;
790362306a36Sopenharmony_ci	}
790462306a36Sopenharmony_ci}
790562306a36Sopenharmony_ci
790662306a36Sopenharmony_cistatic s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
790762306a36Sopenharmony_ci					struct brcmf_fil_country_le *ccreq)
790862306a36Sopenharmony_ci{
790962306a36Sopenharmony_ci	struct brcmfmac_pd_cc *country_codes;
791062306a36Sopenharmony_ci	struct brcmfmac_pd_cc_entry *cc;
791162306a36Sopenharmony_ci	s32 found_index;
791262306a36Sopenharmony_ci	int i;
791362306a36Sopenharmony_ci
791462306a36Sopenharmony_ci	if ((alpha2[0] == ccreq->country_abbrev[0]) &&
791562306a36Sopenharmony_ci	    (alpha2[1] == ccreq->country_abbrev[1])) {
791662306a36Sopenharmony_ci		brcmf_dbg(TRACE, "Country code already set\n");
791762306a36Sopenharmony_ci		return -EAGAIN;
791862306a36Sopenharmony_ci	}
791962306a36Sopenharmony_ci
792062306a36Sopenharmony_ci	country_codes = drvr->settings->country_codes;
792162306a36Sopenharmony_ci	if (!country_codes) {
792262306a36Sopenharmony_ci		if (brmcf_use_iso3166_ccode_fallback(drvr)) {
792362306a36Sopenharmony_ci			brcmf_dbg(TRACE, "No country codes configured for device, using ISO3166 code and 0 rev\n");
792462306a36Sopenharmony_ci			memset(ccreq, 0, sizeof(*ccreq));
792562306a36Sopenharmony_ci			ccreq->country_abbrev[0] = alpha2[0];
792662306a36Sopenharmony_ci			ccreq->country_abbrev[1] = alpha2[1];
792762306a36Sopenharmony_ci			ccreq->ccode[0] = alpha2[0];
792862306a36Sopenharmony_ci			ccreq->ccode[1] = alpha2[1];
792962306a36Sopenharmony_ci			return 0;
793062306a36Sopenharmony_ci		}
793162306a36Sopenharmony_ci
793262306a36Sopenharmony_ci		brcmf_dbg(TRACE, "No country codes configured for device\n");
793362306a36Sopenharmony_ci		return -EINVAL;
793462306a36Sopenharmony_ci	}
793562306a36Sopenharmony_ci
793662306a36Sopenharmony_ci	found_index = -1;
793762306a36Sopenharmony_ci	for (i = 0; i < country_codes->table_size; i++) {
793862306a36Sopenharmony_ci		cc = &country_codes->table[i];
793962306a36Sopenharmony_ci		if ((cc->iso3166[0] == '\0') && (found_index == -1))
794062306a36Sopenharmony_ci			found_index = i;
794162306a36Sopenharmony_ci		if ((cc->iso3166[0] == alpha2[0]) &&
794262306a36Sopenharmony_ci		    (cc->iso3166[1] == alpha2[1])) {
794362306a36Sopenharmony_ci			found_index = i;
794462306a36Sopenharmony_ci			break;
794562306a36Sopenharmony_ci		}
794662306a36Sopenharmony_ci	}
794762306a36Sopenharmony_ci	if (found_index == -1) {
794862306a36Sopenharmony_ci		brcmf_dbg(TRACE, "No country code match found\n");
794962306a36Sopenharmony_ci		return -EINVAL;
795062306a36Sopenharmony_ci	}
795162306a36Sopenharmony_ci	memset(ccreq, 0, sizeof(*ccreq));
795262306a36Sopenharmony_ci	ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
795362306a36Sopenharmony_ci	memcpy(ccreq->ccode, country_codes->table[found_index].cc,
795462306a36Sopenharmony_ci	       BRCMF_COUNTRY_BUF_SZ);
795562306a36Sopenharmony_ci	ccreq->country_abbrev[0] = alpha2[0];
795662306a36Sopenharmony_ci	ccreq->country_abbrev[1] = alpha2[1];
795762306a36Sopenharmony_ci	ccreq->country_abbrev[2] = 0;
795862306a36Sopenharmony_ci
795962306a36Sopenharmony_ci	return 0;
796062306a36Sopenharmony_ci}
796162306a36Sopenharmony_ci
796262306a36Sopenharmony_cistatic int
796362306a36Sopenharmony_cibrcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey)
796462306a36Sopenharmony_ci{
796562306a36Sopenharmony_ci	int i;
796662306a36Sopenharmony_ci	char *token;
796762306a36Sopenharmony_ci	char delim[] = "\n ";
796862306a36Sopenharmony_ci	unsigned long val;
796962306a36Sopenharmony_ci	int err = 0;
797062306a36Sopenharmony_ci
797162306a36Sopenharmony_ci	token = strsep(&buf, delim);
797262306a36Sopenharmony_ci	while (token) {
797362306a36Sopenharmony_ci		if (!strcmp(token, "OBSS")) {
797462306a36Sopenharmony_ci			for (i = 0; i < OBSS_TOKEN_IDX; i++)
797562306a36Sopenharmony_ci				token = strsep(&buf, delim);
797662306a36Sopenharmony_ci			err = kstrtoul(token, 10, &val);
797762306a36Sopenharmony_ci			if (err)
797862306a36Sopenharmony_ci				break;
797962306a36Sopenharmony_ci			survey->obss = val;
798062306a36Sopenharmony_ci		}
798162306a36Sopenharmony_ci
798262306a36Sopenharmony_ci		if (!strcmp(token, "IBSS")) {
798362306a36Sopenharmony_ci			for (i = 0; i < IBSS_TOKEN_IDX; i++)
798462306a36Sopenharmony_ci				token = strsep(&buf, delim);
798562306a36Sopenharmony_ci			err = kstrtoul(token, 10, &val);
798662306a36Sopenharmony_ci			if (err)
798762306a36Sopenharmony_ci				break;
798862306a36Sopenharmony_ci			survey->ibss = val;
798962306a36Sopenharmony_ci		}
799062306a36Sopenharmony_ci
799162306a36Sopenharmony_ci		if (!strcmp(token, "TXDur")) {
799262306a36Sopenharmony_ci			for (i = 0; i < TX_TOKEN_IDX; i++)
799362306a36Sopenharmony_ci				token = strsep(&buf, delim);
799462306a36Sopenharmony_ci			err = kstrtoul(token, 10, &val);
799562306a36Sopenharmony_ci			if (err)
799662306a36Sopenharmony_ci				break;
799762306a36Sopenharmony_ci			survey->tx = val;
799862306a36Sopenharmony_ci		}
799962306a36Sopenharmony_ci
800062306a36Sopenharmony_ci		if (!strcmp(token, "Category")) {
800162306a36Sopenharmony_ci			for (i = 0; i < CTG_TOKEN_IDX; i++)
800262306a36Sopenharmony_ci				token = strsep(&buf, delim);
800362306a36Sopenharmony_ci			err = kstrtoul(token, 10, &val);
800462306a36Sopenharmony_ci			if (err)
800562306a36Sopenharmony_ci				break;
800662306a36Sopenharmony_ci			survey->no_ctg = val;
800762306a36Sopenharmony_ci		}
800862306a36Sopenharmony_ci
800962306a36Sopenharmony_ci		if (!strcmp(token, "Packet")) {
801062306a36Sopenharmony_ci			for (i = 0; i < PKT_TOKEN_IDX; i++)
801162306a36Sopenharmony_ci				token = strsep(&buf, delim);
801262306a36Sopenharmony_ci			err = kstrtoul(token, 10, &val);
801362306a36Sopenharmony_ci			if (err)
801462306a36Sopenharmony_ci				break;
801562306a36Sopenharmony_ci			survey->no_pckt = val;
801662306a36Sopenharmony_ci		}
801762306a36Sopenharmony_ci
801862306a36Sopenharmony_ci		if (!strcmp(token, "Opp(time):")) {
801962306a36Sopenharmony_ci			for (i = 0; i < IDLE_TOKEN_IDX; i++)
802062306a36Sopenharmony_ci				token = strsep(&buf, delim);
802162306a36Sopenharmony_ci			err = kstrtoul(token, 10, &val);
802262306a36Sopenharmony_ci			if (err)
802362306a36Sopenharmony_ci				break;
802462306a36Sopenharmony_ci			survey->idle = val;
802562306a36Sopenharmony_ci		}
802662306a36Sopenharmony_ci
802762306a36Sopenharmony_ci		token = strsep(&buf, delim);
802862306a36Sopenharmony_ci	}
802962306a36Sopenharmony_ci
803062306a36Sopenharmony_ci	return err;
803162306a36Sopenharmony_ci}
803262306a36Sopenharmony_ci
803362306a36Sopenharmony_cistatic int
803462306a36Sopenharmony_cibrcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req,
803562306a36Sopenharmony_ci		struct brcmf_dump_survey *survey)
803662306a36Sopenharmony_ci{
803762306a36Sopenharmony_ci	struct cca_stats_n_flags *results;
803862306a36Sopenharmony_ci	char *buf;
803962306a36Sopenharmony_ci	int err;
804062306a36Sopenharmony_ci
804162306a36Sopenharmony_ci	buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL);
804262306a36Sopenharmony_ci	if (!buf)
804362306a36Sopenharmony_ci		return -ENOMEM;
804462306a36Sopenharmony_ci
804562306a36Sopenharmony_ci	memcpy(buf, &req, sizeof(struct cca_msrmnt_query));
804662306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "dump_obss",
804762306a36Sopenharmony_ci				       buf, BRCMF_DCMD_MEDLEN);
804862306a36Sopenharmony_ci	if (err) {
804962306a36Sopenharmony_ci		brcmf_err("dump_obss error (%d)\n", err);
805062306a36Sopenharmony_ci		err = -EINVAL;
805162306a36Sopenharmony_ci		goto exit;
805262306a36Sopenharmony_ci	}
805362306a36Sopenharmony_ci	results = (struct cca_stats_n_flags *)(buf);
805462306a36Sopenharmony_ci
805562306a36Sopenharmony_ci	if (req.msrmnt_query)
805662306a36Sopenharmony_ci		brcmf_parse_dump_obss(results->buf, survey);
805762306a36Sopenharmony_ci
805862306a36Sopenharmony_ciexit:
805962306a36Sopenharmony_ci	kfree(buf);
806062306a36Sopenharmony_ci	return err;
806162306a36Sopenharmony_ci}
806262306a36Sopenharmony_ci
806362306a36Sopenharmony_cistatic s32
806462306a36Sopenharmony_cibrcmf_set_channel(struct brcmf_cfg80211_info *cfg, struct ieee80211_channel *chan)
806562306a36Sopenharmony_ci{
806662306a36Sopenharmony_ci	u16 chspec = 0;
806762306a36Sopenharmony_ci	int err = 0;
806862306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
806962306a36Sopenharmony_ci
807062306a36Sopenharmony_ci	if (chan->flags & IEEE80211_CHAN_DISABLED)
807162306a36Sopenharmony_ci		return -EINVAL;
807262306a36Sopenharmony_ci
807362306a36Sopenharmony_ci	/* set_channel */
807462306a36Sopenharmony_ci	chspec = channel_to_chanspec(&cfg->d11inf, chan);
807562306a36Sopenharmony_ci	if (chspec != INVCHANSPEC) {
807662306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec);
807762306a36Sopenharmony_ci		if (err) {
807862306a36Sopenharmony_ci			brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err);
807962306a36Sopenharmony_ci			err = -EINVAL;
808062306a36Sopenharmony_ci		}
808162306a36Sopenharmony_ci	} else {
808262306a36Sopenharmony_ci		brcmf_err("failed to convert host chanspec to fw chanspec\n");
808362306a36Sopenharmony_ci		err = -EINVAL;
808462306a36Sopenharmony_ci	}
808562306a36Sopenharmony_ci
808662306a36Sopenharmony_ci	return err;
808762306a36Sopenharmony_ci}
808862306a36Sopenharmony_ci
808962306a36Sopenharmony_cistatic int
809062306a36Sopenharmony_cibrcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
809162306a36Sopenharmony_ci			   int idx, struct survey_info *info)
809262306a36Sopenharmony_ci{
809362306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
809462306a36Sopenharmony_ci	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
809562306a36Sopenharmony_ci	struct brcmf_dump_survey survey = {};
809662306a36Sopenharmony_ci	struct ieee80211_supported_band *band;
809762306a36Sopenharmony_ci	enum nl80211_band band_id;
809862306a36Sopenharmony_ci	struct cca_msrmnt_query req;
809962306a36Sopenharmony_ci	u32 noise;
810062306a36Sopenharmony_ci	int err;
810162306a36Sopenharmony_ci
810262306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx);
810362306a36Sopenharmony_ci
810462306a36Sopenharmony_ci	/* Do not run survey when VIF in CONNECTING / CONNECTED states */
810562306a36Sopenharmony_ci	if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) ||
810662306a36Sopenharmony_ci	    (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) {
810762306a36Sopenharmony_ci		return -EBUSY;
810862306a36Sopenharmony_ci	}
810962306a36Sopenharmony_ci
811062306a36Sopenharmony_ci	for (band_id = 0; band_id < NUM_NL80211_BANDS; band_id++) {
811162306a36Sopenharmony_ci		band = wiphy->bands[band_id];
811262306a36Sopenharmony_ci		if (!band)
811362306a36Sopenharmony_ci			continue;
811462306a36Sopenharmony_ci		if (idx >= band->n_channels) {
811562306a36Sopenharmony_ci			idx -= band->n_channels;
811662306a36Sopenharmony_ci			continue;
811762306a36Sopenharmony_ci		}
811862306a36Sopenharmony_ci
811962306a36Sopenharmony_ci		info->channel = &band->channels[idx];
812062306a36Sopenharmony_ci		break;
812162306a36Sopenharmony_ci	}
812262306a36Sopenharmony_ci	if (band_id == NUM_NL80211_BANDS)
812362306a36Sopenharmony_ci		return -ENOENT;
812462306a36Sopenharmony_ci
812562306a36Sopenharmony_ci	/* Setting current channel to the requested channel */
812662306a36Sopenharmony_ci	info->filled = 0;
812762306a36Sopenharmony_ci	if (brcmf_set_channel(cfg, info->channel))
812862306a36Sopenharmony_ci		return 0;
812962306a36Sopenharmony_ci
813062306a36Sopenharmony_ci	/* Disable mpc */
813162306a36Sopenharmony_ci	brcmf_set_mpc(ifp, 0);
813262306a36Sopenharmony_ci
813362306a36Sopenharmony_ci	/* Set interface up, explicitly. */
813462306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
813562306a36Sopenharmony_ci	if (err) {
813662306a36Sopenharmony_ci		brcmf_err("set interface up failed, err = %d\n", err);
813762306a36Sopenharmony_ci		goto exit;
813862306a36Sopenharmony_ci	}
813962306a36Sopenharmony_ci
814062306a36Sopenharmony_ci	/* Get noise value */
814162306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise);
814262306a36Sopenharmony_ci	if (err) {
814362306a36Sopenharmony_ci		brcmf_err("Get Phy Noise failed, use dummy value\n");
814462306a36Sopenharmony_ci		noise = CHAN_NOISE_DUMMY;
814562306a36Sopenharmony_ci	}
814662306a36Sopenharmony_ci
814762306a36Sopenharmony_ci	/* Start Measurement for obss stats on current channel */
814862306a36Sopenharmony_ci	req.msrmnt_query = 0;
814962306a36Sopenharmony_ci	req.time_req = ACS_MSRMNT_DELAY;
815062306a36Sopenharmony_ci	err = brcmf_dump_obss(ifp, req, &survey);
815162306a36Sopenharmony_ci	if (err)
815262306a36Sopenharmony_ci		goto exit;
815362306a36Sopenharmony_ci
815462306a36Sopenharmony_ci	/* Add 10 ms for IOVAR completion */
815562306a36Sopenharmony_ci	msleep(ACS_MSRMNT_DELAY + 10);
815662306a36Sopenharmony_ci
815762306a36Sopenharmony_ci	/* Issue IOVAR to collect measurement results */
815862306a36Sopenharmony_ci	req.msrmnt_query = 1;
815962306a36Sopenharmony_ci	err = brcmf_dump_obss(ifp, req, &survey);
816062306a36Sopenharmony_ci	if (err)
816162306a36Sopenharmony_ci		goto exit;
816262306a36Sopenharmony_ci
816362306a36Sopenharmony_ci	info->noise = noise;
816462306a36Sopenharmony_ci	info->time = ACS_MSRMNT_DELAY;
816562306a36Sopenharmony_ci	info->time_busy = ACS_MSRMNT_DELAY - survey.idle;
816662306a36Sopenharmony_ci	info->time_rx = survey.obss + survey.ibss + survey.no_ctg +
816762306a36Sopenharmony_ci		survey.no_pckt;
816862306a36Sopenharmony_ci	info->time_tx = survey.tx;
816962306a36Sopenharmony_ci	info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
817062306a36Sopenharmony_ci		SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX |
817162306a36Sopenharmony_ci		SURVEY_INFO_TIME_TX;
817262306a36Sopenharmony_ci
817362306a36Sopenharmony_ci	brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n",
817462306a36Sopenharmony_ci		  ieee80211_frequency_to_channel(info->channel->center_freq),
817562306a36Sopenharmony_ci		  ACS_MSRMNT_DELAY);
817662306a36Sopenharmony_ci	brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n",
817762306a36Sopenharmony_ci		  info->noise, info->time_busy, info->time_rx, info->time_tx);
817862306a36Sopenharmony_ci
817962306a36Sopenharmony_ciexit:
818062306a36Sopenharmony_ci	if (!brcmf_is_apmode(ifp->vif))
818162306a36Sopenharmony_ci		brcmf_set_mpc(ifp, 1);
818262306a36Sopenharmony_ci	return err;
818362306a36Sopenharmony_ci}
818462306a36Sopenharmony_ci
818562306a36Sopenharmony_cistatic void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
818662306a36Sopenharmony_ci					struct regulatory_request *req)
818762306a36Sopenharmony_ci{
818862306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
818962306a36Sopenharmony_ci	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
819062306a36Sopenharmony_ci	struct brcmf_pub *drvr = cfg->pub;
819162306a36Sopenharmony_ci	struct brcmf_fil_country_le ccreq;
819262306a36Sopenharmony_ci	s32 err;
819362306a36Sopenharmony_ci	int i;
819462306a36Sopenharmony_ci
819562306a36Sopenharmony_ci	/* The country code gets set to "00" by default at boot, ignore */
819662306a36Sopenharmony_ci	if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
819762306a36Sopenharmony_ci		return;
819862306a36Sopenharmony_ci
819962306a36Sopenharmony_ci	/* ignore non-ISO3166 country codes */
820062306a36Sopenharmony_ci	for (i = 0; i < 2; i++)
820162306a36Sopenharmony_ci		if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
820262306a36Sopenharmony_ci			bphy_err(drvr, "not an ISO3166 code (0x%02x 0x%02x)\n",
820362306a36Sopenharmony_ci				 req->alpha2[0], req->alpha2[1]);
820462306a36Sopenharmony_ci			return;
820562306a36Sopenharmony_ci		}
820662306a36Sopenharmony_ci
820762306a36Sopenharmony_ci	brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
820862306a36Sopenharmony_ci		  req->alpha2[0], req->alpha2[1]);
820962306a36Sopenharmony_ci
821062306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
821162306a36Sopenharmony_ci	if (err) {
821262306a36Sopenharmony_ci		bphy_err(drvr, "Country code iovar returned err = %d\n", err);
821362306a36Sopenharmony_ci		return;
821462306a36Sopenharmony_ci	}
821562306a36Sopenharmony_ci
821662306a36Sopenharmony_ci	err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
821762306a36Sopenharmony_ci	if (err)
821862306a36Sopenharmony_ci		return;
821962306a36Sopenharmony_ci
822062306a36Sopenharmony_ci	err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
822162306a36Sopenharmony_ci	if (err) {
822262306a36Sopenharmony_ci		bphy_err(drvr, "Firmware rejected country setting\n");
822362306a36Sopenharmony_ci		return;
822462306a36Sopenharmony_ci	}
822562306a36Sopenharmony_ci	brcmf_setup_wiphybands(cfg);
822662306a36Sopenharmony_ci}
822762306a36Sopenharmony_ci
822862306a36Sopenharmony_cistatic void brcmf_free_wiphy(struct wiphy *wiphy)
822962306a36Sopenharmony_ci{
823062306a36Sopenharmony_ci	int i;
823162306a36Sopenharmony_ci
823262306a36Sopenharmony_ci	if (!wiphy)
823362306a36Sopenharmony_ci		return;
823462306a36Sopenharmony_ci
823562306a36Sopenharmony_ci	if (wiphy->iface_combinations) {
823662306a36Sopenharmony_ci		for (i = 0; i < wiphy->n_iface_combinations; i++)
823762306a36Sopenharmony_ci			kfree(wiphy->iface_combinations[i].limits);
823862306a36Sopenharmony_ci	}
823962306a36Sopenharmony_ci	kfree(wiphy->iface_combinations);
824062306a36Sopenharmony_ci	if (wiphy->bands[NL80211_BAND_2GHZ]) {
824162306a36Sopenharmony_ci		kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
824262306a36Sopenharmony_ci		kfree(wiphy->bands[NL80211_BAND_2GHZ]);
824362306a36Sopenharmony_ci	}
824462306a36Sopenharmony_ci	if (wiphy->bands[NL80211_BAND_5GHZ]) {
824562306a36Sopenharmony_ci		kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
824662306a36Sopenharmony_ci		kfree(wiphy->bands[NL80211_BAND_5GHZ]);
824762306a36Sopenharmony_ci	}
824862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PM)
824962306a36Sopenharmony_ci	if (wiphy->wowlan != &brcmf_wowlan_support)
825062306a36Sopenharmony_ci		kfree(wiphy->wowlan);
825162306a36Sopenharmony_ci#endif
825262306a36Sopenharmony_ci}
825362306a36Sopenharmony_ci
825462306a36Sopenharmony_cistruct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
825562306a36Sopenharmony_ci						  struct cfg80211_ops *ops,
825662306a36Sopenharmony_ci						  bool p2pdev_forced)
825762306a36Sopenharmony_ci{
825862306a36Sopenharmony_ci	struct wiphy *wiphy = drvr->wiphy;
825962306a36Sopenharmony_ci	struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
826062306a36Sopenharmony_ci	struct brcmf_cfg80211_info *cfg;
826162306a36Sopenharmony_ci	struct brcmf_cfg80211_vif *vif;
826262306a36Sopenharmony_ci	struct brcmf_if *ifp;
826362306a36Sopenharmony_ci	s32 err = 0;
826462306a36Sopenharmony_ci	s32 io_type;
826562306a36Sopenharmony_ci	u16 *cap = NULL;
826662306a36Sopenharmony_ci
826762306a36Sopenharmony_ci	if (!ndev) {
826862306a36Sopenharmony_ci		bphy_err(drvr, "ndev is invalid\n");
826962306a36Sopenharmony_ci		return NULL;
827062306a36Sopenharmony_ci	}
827162306a36Sopenharmony_ci
827262306a36Sopenharmony_ci	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
827362306a36Sopenharmony_ci	if (!cfg) {
827462306a36Sopenharmony_ci		bphy_err(drvr, "Could not allocate wiphy device\n");
827562306a36Sopenharmony_ci		return NULL;
827662306a36Sopenharmony_ci	}
827762306a36Sopenharmony_ci
827862306a36Sopenharmony_ci	cfg->wiphy = wiphy;
827962306a36Sopenharmony_ci	cfg->pub = drvr;
828062306a36Sopenharmony_ci	init_vif_event(&cfg->vif_event);
828162306a36Sopenharmony_ci	INIT_LIST_HEAD(&cfg->vif_list);
828262306a36Sopenharmony_ci
828362306a36Sopenharmony_ci	vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);
828462306a36Sopenharmony_ci	if (IS_ERR(vif))
828562306a36Sopenharmony_ci		goto wiphy_out;
828662306a36Sopenharmony_ci
828762306a36Sopenharmony_ci	ifp = netdev_priv(ndev);
828862306a36Sopenharmony_ci	vif->ifp = ifp;
828962306a36Sopenharmony_ci	vif->wdev.netdev = ndev;
829062306a36Sopenharmony_ci	ndev->ieee80211_ptr = &vif->wdev;
829162306a36Sopenharmony_ci	SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
829262306a36Sopenharmony_ci
829362306a36Sopenharmony_ci	err = wl_init_priv(cfg);
829462306a36Sopenharmony_ci	if (err) {
829562306a36Sopenharmony_ci		bphy_err(drvr, "Failed to init iwm_priv (%d)\n", err);
829662306a36Sopenharmony_ci		brcmf_free_vif(vif);
829762306a36Sopenharmony_ci		goto wiphy_out;
829862306a36Sopenharmony_ci	}
829962306a36Sopenharmony_ci	ifp->vif = vif;
830062306a36Sopenharmony_ci
830162306a36Sopenharmony_ci	/* determine d11 io type before wiphy setup */
830262306a36Sopenharmony_ci	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
830362306a36Sopenharmony_ci	if (err) {
830462306a36Sopenharmony_ci		bphy_err(drvr, "Failed to get D11 version (%d)\n", err);
830562306a36Sopenharmony_ci		goto priv_out;
830662306a36Sopenharmony_ci	}
830762306a36Sopenharmony_ci	cfg->d11inf.io_type = (u8)io_type;
830862306a36Sopenharmony_ci	brcmu_d11_attach(&cfg->d11inf);
830962306a36Sopenharmony_ci
831062306a36Sopenharmony_ci	/* regulatory notifer below needs access to cfg so
831162306a36Sopenharmony_ci	 * assign it now.
831262306a36Sopenharmony_ci	 */
831362306a36Sopenharmony_ci	drvr->config = cfg;
831462306a36Sopenharmony_ci
831562306a36Sopenharmony_ci	err = brcmf_setup_wiphy(wiphy, ifp);
831662306a36Sopenharmony_ci	if (err < 0)
831762306a36Sopenharmony_ci		goto priv_out;
831862306a36Sopenharmony_ci
831962306a36Sopenharmony_ci	brcmf_dbg(INFO, "Registering custom regulatory\n");
832062306a36Sopenharmony_ci	wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
832162306a36Sopenharmony_ci	wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
832262306a36Sopenharmony_ci	wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
832362306a36Sopenharmony_ci
832462306a36Sopenharmony_ci	/* firmware defaults to 40MHz disabled in 2G band. We signal
832562306a36Sopenharmony_ci	 * cfg80211 here that we do and have it decide we can enable
832662306a36Sopenharmony_ci	 * it. But first check if device does support 2G operation.
832762306a36Sopenharmony_ci	 */
832862306a36Sopenharmony_ci	if (wiphy->bands[NL80211_BAND_2GHZ]) {
832962306a36Sopenharmony_ci		cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
833062306a36Sopenharmony_ci		*cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
833162306a36Sopenharmony_ci	}
833262306a36Sopenharmony_ci#ifdef CONFIG_PM
833362306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
833462306a36Sopenharmony_ci		ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
833562306a36Sopenharmony_ci#endif
833662306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS))
833762306a36Sopenharmony_ci		ops->dump_survey = brcmf_cfg80211_dump_survey;
833862306a36Sopenharmony_ci
833962306a36Sopenharmony_ci	err = wiphy_register(wiphy);
834062306a36Sopenharmony_ci	if (err < 0) {
834162306a36Sopenharmony_ci		bphy_err(drvr, "Could not register wiphy device (%d)\n", err);
834262306a36Sopenharmony_ci		goto priv_out;
834362306a36Sopenharmony_ci	}
834462306a36Sopenharmony_ci
834562306a36Sopenharmony_ci	err = brcmf_setup_wiphybands(cfg);
834662306a36Sopenharmony_ci	if (err) {
834762306a36Sopenharmony_ci		bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err);
834862306a36Sopenharmony_ci		goto wiphy_unreg_out;
834962306a36Sopenharmony_ci	}
835062306a36Sopenharmony_ci
835162306a36Sopenharmony_ci	/* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
835262306a36Sopenharmony_ci	 * setup 40MHz in 2GHz band and enable OBSS scanning.
835362306a36Sopenharmony_ci	 */
835462306a36Sopenharmony_ci	if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
835562306a36Sopenharmony_ci		err = brcmf_enable_bw40_2g(cfg);
835662306a36Sopenharmony_ci		if (!err)
835762306a36Sopenharmony_ci			err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
835862306a36Sopenharmony_ci						      BRCMF_OBSS_COEX_AUTO);
835962306a36Sopenharmony_ci		else
836062306a36Sopenharmony_ci			*cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
836162306a36Sopenharmony_ci	}
836262306a36Sopenharmony_ci
836362306a36Sopenharmony_ci	err = brcmf_fweh_activate_events(ifp);
836462306a36Sopenharmony_ci	if (err) {
836562306a36Sopenharmony_ci		bphy_err(drvr, "FWEH activation failed (%d)\n", err);
836662306a36Sopenharmony_ci		goto wiphy_unreg_out;
836762306a36Sopenharmony_ci	}
836862306a36Sopenharmony_ci
836962306a36Sopenharmony_ci	err = brcmf_p2p_attach(cfg, p2pdev_forced);
837062306a36Sopenharmony_ci	if (err) {
837162306a36Sopenharmony_ci		bphy_err(drvr, "P2P initialisation failed (%d)\n", err);
837262306a36Sopenharmony_ci		goto wiphy_unreg_out;
837362306a36Sopenharmony_ci	}
837462306a36Sopenharmony_ci	err = brcmf_btcoex_attach(cfg);
837562306a36Sopenharmony_ci	if (err) {
837662306a36Sopenharmony_ci		bphy_err(drvr, "BT-coex initialisation failed (%d)\n", err);
837762306a36Sopenharmony_ci		brcmf_p2p_detach(&cfg->p2p);
837862306a36Sopenharmony_ci		goto wiphy_unreg_out;
837962306a36Sopenharmony_ci	}
838062306a36Sopenharmony_ci	err = brcmf_pno_attach(cfg);
838162306a36Sopenharmony_ci	if (err) {
838262306a36Sopenharmony_ci		bphy_err(drvr, "PNO initialisation failed (%d)\n", err);
838362306a36Sopenharmony_ci		brcmf_btcoex_detach(cfg);
838462306a36Sopenharmony_ci		brcmf_p2p_detach(&cfg->p2p);
838562306a36Sopenharmony_ci		goto wiphy_unreg_out;
838662306a36Sopenharmony_ci	}
838762306a36Sopenharmony_ci
838862306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
838962306a36Sopenharmony_ci		err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
839062306a36Sopenharmony_ci		if (err) {
839162306a36Sopenharmony_ci			brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
839262306a36Sopenharmony_ci			wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
839362306a36Sopenharmony_ci		} else {
839462306a36Sopenharmony_ci			brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
839562306a36Sopenharmony_ci					    brcmf_notify_tdls_peer_event);
839662306a36Sopenharmony_ci		}
839762306a36Sopenharmony_ci	}
839862306a36Sopenharmony_ci
839962306a36Sopenharmony_ci	/* (re-) activate FWEH event handling */
840062306a36Sopenharmony_ci	err = brcmf_fweh_activate_events(ifp);
840162306a36Sopenharmony_ci	if (err) {
840262306a36Sopenharmony_ci		bphy_err(drvr, "FWEH activation failed (%d)\n", err);
840362306a36Sopenharmony_ci		goto detach;
840462306a36Sopenharmony_ci	}
840562306a36Sopenharmony_ci
840662306a36Sopenharmony_ci	/* Fill in some of the advertised nl80211 supported features */
840762306a36Sopenharmony_ci	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
840862306a36Sopenharmony_ci		wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
840962306a36Sopenharmony_ci#ifdef CONFIG_PM
841062306a36Sopenharmony_ci		if (wiphy->wowlan &&
841162306a36Sopenharmony_ci		    wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
841262306a36Sopenharmony_ci			wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
841362306a36Sopenharmony_ci#endif
841462306a36Sopenharmony_ci	}
841562306a36Sopenharmony_ci
841662306a36Sopenharmony_ci	return cfg;
841762306a36Sopenharmony_ci
841862306a36Sopenharmony_cidetach:
841962306a36Sopenharmony_ci	brcmf_pno_detach(cfg);
842062306a36Sopenharmony_ci	brcmf_btcoex_detach(cfg);
842162306a36Sopenharmony_ci	brcmf_p2p_detach(&cfg->p2p);
842262306a36Sopenharmony_ciwiphy_unreg_out:
842362306a36Sopenharmony_ci	wiphy_unregister(cfg->wiphy);
842462306a36Sopenharmony_cipriv_out:
842562306a36Sopenharmony_ci	wl_deinit_priv(cfg);
842662306a36Sopenharmony_ci	brcmf_free_vif(vif);
842762306a36Sopenharmony_ci	ifp->vif = NULL;
842862306a36Sopenharmony_ciwiphy_out:
842962306a36Sopenharmony_ci	brcmf_free_wiphy(wiphy);
843062306a36Sopenharmony_ci	kfree(cfg);
843162306a36Sopenharmony_ci	return NULL;
843262306a36Sopenharmony_ci}
843362306a36Sopenharmony_ci
843462306a36Sopenharmony_civoid brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
843562306a36Sopenharmony_ci{
843662306a36Sopenharmony_ci	if (!cfg)
843762306a36Sopenharmony_ci		return;
843862306a36Sopenharmony_ci
843962306a36Sopenharmony_ci	brcmf_pno_detach(cfg);
844062306a36Sopenharmony_ci	brcmf_btcoex_detach(cfg);
844162306a36Sopenharmony_ci	wiphy_unregister(cfg->wiphy);
844262306a36Sopenharmony_ci	wl_deinit_priv(cfg);
844362306a36Sopenharmony_ci	cancel_work_sync(&cfg->escan_timeout_work);
844462306a36Sopenharmony_ci	brcmf_free_wiphy(cfg->wiphy);
844562306a36Sopenharmony_ci	kfree(cfg);
844662306a36Sopenharmony_ci}
8447