xref: /freebsd/sys/contrib/dev/broadcom/brcm80211/brcmfmac/cfg80211.c (revision 9375e11f079d725d42c5b431de33044088ad14f7)
1b4c3e9b5SBjoern A. Zeeb // SPDX-License-Identifier: ISC
2b4c3e9b5SBjoern A. Zeeb /*
3b4c3e9b5SBjoern A. Zeeb  * Copyright (c) 2010 Broadcom Corporation
4b4c3e9b5SBjoern A. Zeeb  */
5b4c3e9b5SBjoern A. Zeeb 
6b4c3e9b5SBjoern A. Zeeb /* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
7b4c3e9b5SBjoern A. Zeeb 
8902136e0SBjoern A. Zeeb #if defined(__FreeBSD__)
9902136e0SBjoern A. Zeeb #include <net/cfg80211.h>
10902136e0SBjoern A. Zeeb #endif
11b4c3e9b5SBjoern A. Zeeb #include <linux/kernel.h>
12b4c3e9b5SBjoern A. Zeeb #include <linux/etherdevice.h>
13b4c3e9b5SBjoern A. Zeeb #include <linux/module.h>
14b4c3e9b5SBjoern A. Zeeb #include <linux/vmalloc.h>
15902136e0SBjoern A. Zeeb #if defined(__linux__)
16b4c3e9b5SBjoern A. Zeeb #include <net/cfg80211.h>
17902136e0SBjoern A. Zeeb #endif
18b4c3e9b5SBjoern A. Zeeb #include <net/netlink.h>
19b4c3e9b5SBjoern A. Zeeb #include <uapi/linux/if_arp.h>
20902136e0SBjoern A. Zeeb #if defined(__FreeBSD__)
21902136e0SBjoern A. Zeeb #include <linux/delay.h>
22902136e0SBjoern A. Zeeb #endif
23b4c3e9b5SBjoern A. Zeeb 
24b4c3e9b5SBjoern A. Zeeb #include <brcmu_utils.h>
25b4c3e9b5SBjoern A. Zeeb #include <defs.h>
26b4c3e9b5SBjoern A. Zeeb #include <brcmu_wifi.h>
27b4c3e9b5SBjoern A. Zeeb #include <brcm_hw_ids.h>
28b4c3e9b5SBjoern A. Zeeb #include "core.h"
29b4c3e9b5SBjoern A. Zeeb #include "debug.h"
30b4c3e9b5SBjoern A. Zeeb #include "tracepoint.h"
31b4c3e9b5SBjoern A. Zeeb #include "fwil_types.h"
32b4c3e9b5SBjoern A. Zeeb #include "p2p.h"
33b4c3e9b5SBjoern A. Zeeb #include "btcoex.h"
34b4c3e9b5SBjoern A. Zeeb #include "pno.h"
35b4c3e9b5SBjoern A. Zeeb #include "fwsignal.h"
36b4c3e9b5SBjoern A. Zeeb #include "cfg80211.h"
37b4c3e9b5SBjoern A. Zeeb #include "feature.h"
38b4c3e9b5SBjoern A. Zeeb #include "fwil.h"
39b4c3e9b5SBjoern A. Zeeb #include "proto.h"
40b4c3e9b5SBjoern A. Zeeb #include "vendor.h"
41b4c3e9b5SBjoern A. Zeeb #include "bus.h"
42b4c3e9b5SBjoern A. Zeeb #include "common.h"
43b4c3e9b5SBjoern A. Zeeb #include "fwvid.h"
44b4c3e9b5SBjoern A. Zeeb 
45b4c3e9b5SBjoern A. Zeeb #define BRCMF_SCAN_IE_LEN_MAX		2048
46b4c3e9b5SBjoern A. Zeeb 
47902136e0SBjoern A. Zeeb #if defined(__FreeBSD__)
48902136e0SBjoern A. Zeeb #ifdef WPA_OUI
49902136e0SBjoern A. Zeeb #undef WPA_OUI
50902136e0SBjoern A. Zeeb #endif
51902136e0SBjoern A. Zeeb #ifdef WPA_OUI_TYPE
52902136e0SBjoern A. Zeeb #undef WPA_OUI_TYPE
53902136e0SBjoern A. Zeeb #endif
54902136e0SBjoern A. Zeeb #ifdef RSN_OUI
55902136e0SBjoern A. Zeeb #undef RSN_OUI
56902136e0SBjoern A. Zeeb #endif
57902136e0SBjoern A. Zeeb #ifdef WME_OUI_TYPE
58902136e0SBjoern A. Zeeb #undef WME_OUI_TYPE
59902136e0SBjoern A. Zeeb #endif
60902136e0SBjoern A. Zeeb #ifdef WPS_OUI_TYPE
61902136e0SBjoern A. Zeeb #undef WPS_OUI_TYPE
62902136e0SBjoern A. Zeeb #endif
63902136e0SBjoern A. Zeeb #endif
64902136e0SBjoern A. Zeeb 
65b4c3e9b5SBjoern A. Zeeb #define WPA_OUI				"\x00\x50\xF2"	/* WPA OUI */
66b4c3e9b5SBjoern A. Zeeb #define WPA_OUI_TYPE			1
67b4c3e9b5SBjoern A. Zeeb #define RSN_OUI				"\x00\x0F\xAC"	/* RSN OUI */
68b4c3e9b5SBjoern A. Zeeb #define	WME_OUI_TYPE			2
69b4c3e9b5SBjoern A. Zeeb #define WPS_OUI_TYPE			4
70b4c3e9b5SBjoern A. Zeeb 
71b4c3e9b5SBjoern A. Zeeb #define VS_IE_FIXED_HDR_LEN		6
72b4c3e9b5SBjoern A. Zeeb #define WPA_IE_VERSION_LEN		2
73b4c3e9b5SBjoern A. Zeeb #define WPA_IE_MIN_OUI_LEN		4
74b4c3e9b5SBjoern A. Zeeb #define WPA_IE_SUITE_COUNT_LEN		2
75b4c3e9b5SBjoern A. Zeeb 
76b4c3e9b5SBjoern A. Zeeb #define WPA_CIPHER_NONE			0	/* None */
77b4c3e9b5SBjoern A. Zeeb #define WPA_CIPHER_WEP_40		1	/* WEP (40-bit) */
78b4c3e9b5SBjoern A. Zeeb #define WPA_CIPHER_TKIP			2	/* TKIP: default for WPA */
79b4c3e9b5SBjoern A. Zeeb #define WPA_CIPHER_AES_CCM		4	/* AES (CCM) */
80b4c3e9b5SBjoern A. Zeeb #define WPA_CIPHER_WEP_104		5	/* WEP (104-bit) */
81b4c3e9b5SBjoern A. Zeeb 
82b4c3e9b5SBjoern A. Zeeb #define RSN_AKM_NONE			0	/* None (IBSS) */
83b4c3e9b5SBjoern A. Zeeb #define RSN_AKM_UNSPECIFIED		1	/* Over 802.1x */
84b4c3e9b5SBjoern A. Zeeb #define RSN_AKM_PSK			2	/* Pre-shared Key */
85b4c3e9b5SBjoern A. Zeeb #define RSN_AKM_SHA256_1X		5	/* SHA256, 802.1X */
86b4c3e9b5SBjoern A. Zeeb #define RSN_AKM_SHA256_PSK		6	/* SHA256, Pre-shared Key */
87b4c3e9b5SBjoern A. Zeeb #define RSN_AKM_SAE			8	/* SAE */
88b4c3e9b5SBjoern A. Zeeb #define RSN_CAP_LEN			2	/* Length of RSN capabilities */
89b4c3e9b5SBjoern A. Zeeb #define RSN_CAP_PTK_REPLAY_CNTR_MASK	(BIT(2) | BIT(3))
90b4c3e9b5SBjoern A. Zeeb #define RSN_CAP_MFPR_MASK		BIT(6)
91b4c3e9b5SBjoern A. Zeeb #define RSN_CAP_MFPC_MASK		BIT(7)
92b4c3e9b5SBjoern A. Zeeb #define RSN_PMKID_COUNT_LEN		2
93b4c3e9b5SBjoern A. Zeeb 
94b4c3e9b5SBjoern A. Zeeb #define VNDR_IE_CMD_LEN			4	/* length of the set command
95b4c3e9b5SBjoern A. Zeeb 						 * string :"add", "del" (+ NUL)
96b4c3e9b5SBjoern A. Zeeb 						 */
97b4c3e9b5SBjoern A. Zeeb #define VNDR_IE_COUNT_OFFSET		4
98b4c3e9b5SBjoern A. Zeeb #define VNDR_IE_PKTFLAG_OFFSET		8
99b4c3e9b5SBjoern A. Zeeb #define VNDR_IE_VSIE_OFFSET		12
100b4c3e9b5SBjoern A. Zeeb #define VNDR_IE_HDR_SIZE		12
101b4c3e9b5SBjoern A. Zeeb #define VNDR_IE_PARSE_LIMIT		5
102b4c3e9b5SBjoern A. Zeeb 
103b4c3e9b5SBjoern A. Zeeb #define	DOT11_BCN_PRB_FIXED_LEN		12	/* beacon/probe fixed length */
104b4c3e9b5SBjoern A. Zeeb 
105b4c3e9b5SBjoern A. Zeeb #define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS	320
106b4c3e9b5SBjoern A. Zeeb #define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS	400
107b4c3e9b5SBjoern A. Zeeb #define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS	20
108b4c3e9b5SBjoern A. Zeeb 
109b4c3e9b5SBjoern A. Zeeb #define BRCMF_SCAN_CHANNEL_TIME		40
110b4c3e9b5SBjoern A. Zeeb #define BRCMF_SCAN_UNASSOC_TIME		40
111b4c3e9b5SBjoern A. Zeeb #define BRCMF_SCAN_PASSIVE_TIME		120
112b4c3e9b5SBjoern A. Zeeb 
113b4c3e9b5SBjoern A. Zeeb #define BRCMF_ND_INFO_TIMEOUT		msecs_to_jiffies(2000)
114b4c3e9b5SBjoern A. Zeeb 
115b4c3e9b5SBjoern A. Zeeb #define BRCMF_PS_MAX_TIMEOUT_MS		2000
116b4c3e9b5SBjoern A. Zeeb 
117b4c3e9b5SBjoern A. Zeeb /* Dump obss definitions */
118b4c3e9b5SBjoern A. Zeeb #define ACS_MSRMNT_DELAY		80
119b4c3e9b5SBjoern A. Zeeb #define CHAN_NOISE_DUMMY		(-80)
120b4c3e9b5SBjoern A. Zeeb #define OBSS_TOKEN_IDX			15
121b4c3e9b5SBjoern A. Zeeb #define IBSS_TOKEN_IDX			15
122b4c3e9b5SBjoern A. Zeeb #define TX_TOKEN_IDX			14
123b4c3e9b5SBjoern A. Zeeb #define CTG_TOKEN_IDX			13
124b4c3e9b5SBjoern A. Zeeb #define PKT_TOKEN_IDX			15
125b4c3e9b5SBjoern A. Zeeb #define IDLE_TOKEN_IDX			12
126b4c3e9b5SBjoern A. Zeeb 
127b4c3e9b5SBjoern A. Zeeb #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
128b4c3e9b5SBjoern A. Zeeb 	(sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
129b4c3e9b5SBjoern A. Zeeb 
130b4c3e9b5SBjoern A. Zeeb #define BRCMF_MAX_CHANSPEC_LIST \
131b4c3e9b5SBjoern A. Zeeb 	(BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)
132b4c3e9b5SBjoern A. Zeeb 
133b4c3e9b5SBjoern A. Zeeb struct brcmf_dump_survey {
134b4c3e9b5SBjoern A. Zeeb 	u32 obss;
135b4c3e9b5SBjoern A. Zeeb 	u32 ibss;
136b4c3e9b5SBjoern A. Zeeb 	u32 no_ctg;
137b4c3e9b5SBjoern A. Zeeb 	u32 no_pckt;
138b4c3e9b5SBjoern A. Zeeb 	u32 tx;
139b4c3e9b5SBjoern A. Zeeb 	u32 idle;
140b4c3e9b5SBjoern A. Zeeb };
141b4c3e9b5SBjoern A. Zeeb 
142b4c3e9b5SBjoern A. Zeeb struct cca_stats_n_flags {
143b4c3e9b5SBjoern A. Zeeb 	u32 msrmnt_time; /* Time for Measurement (msec) */
144b4c3e9b5SBjoern A. Zeeb 	u32 msrmnt_done; /* flag set when measurement complete */
145b4c3e9b5SBjoern A. Zeeb 	char buf[1];
146b4c3e9b5SBjoern A. Zeeb };
147b4c3e9b5SBjoern A. Zeeb 
148b4c3e9b5SBjoern A. Zeeb struct cca_msrmnt_query {
149b4c3e9b5SBjoern A. Zeeb 	u32 msrmnt_query;
150b4c3e9b5SBjoern A. Zeeb 	u32 time_req;
151b4c3e9b5SBjoern A. Zeeb };
152b4c3e9b5SBjoern A. Zeeb 
check_vif_up(struct brcmf_cfg80211_vif * vif)153b4c3e9b5SBjoern A. Zeeb static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
154b4c3e9b5SBjoern A. Zeeb {
155b4c3e9b5SBjoern A. Zeeb 	if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
156b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
157b4c3e9b5SBjoern A. Zeeb 			  vif->sme_state);
158b4c3e9b5SBjoern A. Zeeb 		return false;
159b4c3e9b5SBjoern A. Zeeb 	}
160b4c3e9b5SBjoern A. Zeeb 	return true;
161b4c3e9b5SBjoern A. Zeeb }
162b4c3e9b5SBjoern A. Zeeb 
163b4c3e9b5SBjoern A. Zeeb #define RATE_TO_BASE100KBPS(rate)   (((rate) * 10) / 2)
164b4c3e9b5SBjoern A. Zeeb #define RATETAB_ENT(_rateid, _flags) \
165b4c3e9b5SBjoern A. Zeeb 	{                                                               \
166b4c3e9b5SBjoern A. Zeeb 		.bitrate        = RATE_TO_BASE100KBPS(_rateid),     \
167b4c3e9b5SBjoern A. Zeeb 		.hw_value       = (_rateid),                            \
168b4c3e9b5SBjoern A. Zeeb 		.flags          = (_flags),                             \
169b4c3e9b5SBjoern A. Zeeb 	}
170b4c3e9b5SBjoern A. Zeeb 
171b4c3e9b5SBjoern A. Zeeb static struct ieee80211_rate __wl_rates[] = {
172b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_1M, 0),
173b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
174b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
175b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
176b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_6M, 0),
177b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_9M, 0),
178b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_12M, 0),
179b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_18M, 0),
180b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_24M, 0),
181b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_36M, 0),
182b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_48M, 0),
183b4c3e9b5SBjoern A. Zeeb 	RATETAB_ENT(BRCM_RATE_54M, 0),
184b4c3e9b5SBjoern A. Zeeb };
185b4c3e9b5SBjoern A. Zeeb 
186b4c3e9b5SBjoern A. Zeeb #define wl_g_rates		(__wl_rates + 0)
187b4c3e9b5SBjoern A. Zeeb #define wl_g_rates_size		ARRAY_SIZE(__wl_rates)
188b4c3e9b5SBjoern A. Zeeb #define wl_a_rates		(__wl_rates + 4)
189b4c3e9b5SBjoern A. Zeeb #define wl_a_rates_size		(wl_g_rates_size - 4)
190b4c3e9b5SBjoern A. Zeeb 
191b4c3e9b5SBjoern A. Zeeb #define CHAN2G(_channel, _freq) {				\
192b4c3e9b5SBjoern A. Zeeb 	.band			= NL80211_BAND_2GHZ,		\
193b4c3e9b5SBjoern A. Zeeb 	.center_freq		= (_freq),			\
194b4c3e9b5SBjoern A. Zeeb 	.hw_value		= (_channel),			\
195b4c3e9b5SBjoern A. Zeeb 	.max_antenna_gain	= 0,				\
196b4c3e9b5SBjoern A. Zeeb 	.max_power		= 30,				\
197b4c3e9b5SBjoern A. Zeeb }
198b4c3e9b5SBjoern A. Zeeb 
199b4c3e9b5SBjoern A. Zeeb #define CHAN5G(_channel) {					\
200b4c3e9b5SBjoern A. Zeeb 	.band			= NL80211_BAND_5GHZ,		\
201b4c3e9b5SBjoern A. Zeeb 	.center_freq		= 5000 + (5 * (_channel)),	\
202b4c3e9b5SBjoern A. Zeeb 	.hw_value		= (_channel),			\
203b4c3e9b5SBjoern A. Zeeb 	.max_antenna_gain	= 0,				\
204b4c3e9b5SBjoern A. Zeeb 	.max_power		= 30,				\
205b4c3e9b5SBjoern A. Zeeb }
206b4c3e9b5SBjoern A. Zeeb 
207b4c3e9b5SBjoern A. Zeeb static struct ieee80211_channel __wl_2ghz_channels[] = {
208b4c3e9b5SBjoern A. Zeeb 	CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
209b4c3e9b5SBjoern A. Zeeb 	CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
210b4c3e9b5SBjoern A. Zeeb 	CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
211b4c3e9b5SBjoern A. Zeeb 	CHAN2G(13, 2472), CHAN2G(14, 2484)
212b4c3e9b5SBjoern A. Zeeb };
213b4c3e9b5SBjoern A. Zeeb 
214b4c3e9b5SBjoern A. Zeeb static struct ieee80211_channel __wl_5ghz_channels[] = {
215b4c3e9b5SBjoern A. Zeeb 	CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
216b4c3e9b5SBjoern A. Zeeb 	CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
217b4c3e9b5SBjoern A. Zeeb 	CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
218b4c3e9b5SBjoern A. Zeeb 	CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
219b4c3e9b5SBjoern A. Zeeb 	CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
220b4c3e9b5SBjoern A. Zeeb 	CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
221b4c3e9b5SBjoern A. Zeeb };
222b4c3e9b5SBjoern A. Zeeb 
223b4c3e9b5SBjoern A. Zeeb /* Band templates duplicated per wiphy. The channel info
224b4c3e9b5SBjoern A. Zeeb  * above is added to the band during setup.
225b4c3e9b5SBjoern A. Zeeb  */
226b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_supported_band __wl_band_2ghz = {
227b4c3e9b5SBjoern A. Zeeb 	.band = NL80211_BAND_2GHZ,
228b4c3e9b5SBjoern A. Zeeb 	.bitrates = wl_g_rates,
229b4c3e9b5SBjoern A. Zeeb 	.n_bitrates = wl_g_rates_size,
230b4c3e9b5SBjoern A. Zeeb };
231b4c3e9b5SBjoern A. Zeeb 
232b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_supported_band __wl_band_5ghz = {
233b4c3e9b5SBjoern A. Zeeb 	.band = NL80211_BAND_5GHZ,
234b4c3e9b5SBjoern A. Zeeb 	.bitrates = wl_a_rates,
235b4c3e9b5SBjoern A. Zeeb 	.n_bitrates = wl_a_rates_size,
236b4c3e9b5SBjoern A. Zeeb };
237b4c3e9b5SBjoern A. Zeeb 
238b4c3e9b5SBjoern A. Zeeb /* This is to override regulatory domains defined in cfg80211 module (reg.c)
239b4c3e9b5SBjoern A. Zeeb  * By default world regulatory domain defined in reg.c puts the flags
240b4c3e9b5SBjoern A. Zeeb  * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
241b4c3e9b5SBjoern A. Zeeb  * With respect to these flags, wpa_supplicant doesn't * start p2p
242b4c3e9b5SBjoern A. Zeeb  * operations on 5GHz channels. All the changes in world regulatory
243b4c3e9b5SBjoern A. Zeeb  * domain are to be done here.
244b4c3e9b5SBjoern A. Zeeb  */
245b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_regdomain brcmf_regdom = {
246b4c3e9b5SBjoern A. Zeeb 	.n_reg_rules = 4,
247b4c3e9b5SBjoern A. Zeeb 	.alpha2 =  "99",
248b4c3e9b5SBjoern A. Zeeb 	.reg_rules = {
249b4c3e9b5SBjoern A. Zeeb 		/* IEEE 802.11b/g, channels 1..11 */
250b4c3e9b5SBjoern A. Zeeb 		REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
251b4c3e9b5SBjoern A. Zeeb 		/* If any */
252b4c3e9b5SBjoern A. Zeeb 		/* IEEE 802.11 channel 14 - Only JP enables
253b4c3e9b5SBjoern A. Zeeb 		 * this and for 802.11b only
254b4c3e9b5SBjoern A. Zeeb 		 */
255b4c3e9b5SBjoern A. Zeeb 		REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
256b4c3e9b5SBjoern A. Zeeb 		/* IEEE 802.11a, channel 36..64 */
257b4c3e9b5SBjoern A. Zeeb 		REG_RULE(5150-10, 5350+10, 160, 6, 20, 0),
258b4c3e9b5SBjoern A. Zeeb 		/* IEEE 802.11a, channel 100..165 */
259b4c3e9b5SBjoern A. Zeeb 		REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), }
260b4c3e9b5SBjoern A. Zeeb };
261b4c3e9b5SBjoern A. Zeeb 
262b4c3e9b5SBjoern A. Zeeb /* Note: brcmf_cipher_suites is an array of int defining which cipher suites
263b4c3e9b5SBjoern A. Zeeb  * are supported. A pointer to this array and the number of entries is passed
264b4c3e9b5SBjoern A. Zeeb  * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
265b4c3e9b5SBjoern A. Zeeb  * So the cipher suite AES_CMAC has to be the last one in the array, and when
266b4c3e9b5SBjoern A. Zeeb  * device does not support MFP then the number of suites will be decreased by 1
267b4c3e9b5SBjoern A. Zeeb  */
268b4c3e9b5SBjoern A. Zeeb static const u32 brcmf_cipher_suites[] = {
269b4c3e9b5SBjoern A. Zeeb 	WLAN_CIPHER_SUITE_WEP40,
270b4c3e9b5SBjoern A. Zeeb 	WLAN_CIPHER_SUITE_WEP104,
271b4c3e9b5SBjoern A. Zeeb 	WLAN_CIPHER_SUITE_TKIP,
272b4c3e9b5SBjoern A. Zeeb 	WLAN_CIPHER_SUITE_CCMP,
273b4c3e9b5SBjoern A. Zeeb 	/* Keep as last entry: */
274b4c3e9b5SBjoern A. Zeeb 	WLAN_CIPHER_SUITE_AES_CMAC
275b4c3e9b5SBjoern A. Zeeb };
276b4c3e9b5SBjoern A. Zeeb 
277b4c3e9b5SBjoern A. Zeeb /* Vendor specific ie. id = 221, oui and type defines exact ie */
278b4c3e9b5SBjoern A. Zeeb struct brcmf_vs_tlv {
279b4c3e9b5SBjoern A. Zeeb 	u8 id;
280b4c3e9b5SBjoern A. Zeeb 	u8 len;
281b4c3e9b5SBjoern A. Zeeb 	u8 oui[3];
282b4c3e9b5SBjoern A. Zeeb 	u8 oui_type;
283b4c3e9b5SBjoern A. Zeeb };
284b4c3e9b5SBjoern A. Zeeb 
285b4c3e9b5SBjoern A. Zeeb struct parsed_vndr_ie_info {
286902136e0SBjoern A. Zeeb #if defined(__linux__)
287b4c3e9b5SBjoern A. Zeeb 	u8 *ie_ptr;
288902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
289902136e0SBjoern A. Zeeb 	const u8 *ie_ptr;
290902136e0SBjoern A. Zeeb #endif
291b4c3e9b5SBjoern A. Zeeb 	u32 ie_len;	/* total length including id & length field */
292b4c3e9b5SBjoern A. Zeeb 	struct brcmf_vs_tlv vndrie;
293b4c3e9b5SBjoern A. Zeeb };
294b4c3e9b5SBjoern A. Zeeb 
295b4c3e9b5SBjoern A. Zeeb struct parsed_vndr_ies {
296b4c3e9b5SBjoern A. Zeeb 	u32 count;
297b4c3e9b5SBjoern A. Zeeb 	struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
298b4c3e9b5SBjoern A. Zeeb };
299b4c3e9b5SBjoern A. Zeeb 
300b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_CREATE_VER_1		1
301b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_CREATE_VER_2		2
302b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_CREATE_VER_3		3
303b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_CREATE_VER_MAX		WL_INTERFACE_CREATE_VER_3
304b4c3e9b5SBjoern A. Zeeb 
305b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_MAC_DONT_USE	0x0
306b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_MAC_USE		0x2
307b4c3e9b5SBjoern A. Zeeb 
308b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_CREATE_STA		0x0
309b4c3e9b5SBjoern A. Zeeb #define WL_INTERFACE_CREATE_AP		0x1
310b4c3e9b5SBjoern A. Zeeb 
311b4c3e9b5SBjoern A. Zeeb struct wl_interface_create_v1 {
312b4c3e9b5SBjoern A. Zeeb 	u16	ver;			/* structure version */
313b4c3e9b5SBjoern A. Zeeb 	u32	flags;			/* flags for operation */
314b4c3e9b5SBjoern A. Zeeb 	u8	mac_addr[ETH_ALEN];	/* MAC address */
315b4c3e9b5SBjoern A. Zeeb 	u32	wlc_index;		/* optional for wlc index */
316b4c3e9b5SBjoern A. Zeeb };
317b4c3e9b5SBjoern A. Zeeb 
318b4c3e9b5SBjoern A. Zeeb struct wl_interface_create_v2 {
319b4c3e9b5SBjoern A. Zeeb 	u16	ver;			/* structure version */
320b4c3e9b5SBjoern A. Zeeb 	u8	pad1[2];
321b4c3e9b5SBjoern A. Zeeb 	u32	flags;			/* flags for operation */
322b4c3e9b5SBjoern A. Zeeb 	u8	mac_addr[ETH_ALEN];	/* MAC address */
323b4c3e9b5SBjoern A. Zeeb 	u8	iftype;			/* type of interface created */
324b4c3e9b5SBjoern A. Zeeb 	u8	pad2;
325b4c3e9b5SBjoern A. Zeeb 	u32	wlc_index;		/* optional for wlc index */
326b4c3e9b5SBjoern A. Zeeb };
327b4c3e9b5SBjoern A. Zeeb 
328b4c3e9b5SBjoern A. Zeeb struct wl_interface_create_v3 {
329b4c3e9b5SBjoern A. Zeeb 	u16 ver;			/* structure version */
330b4c3e9b5SBjoern A. Zeeb 	u16 len;			/* length of structure + data */
331b4c3e9b5SBjoern A. Zeeb 	u16 fixed_len;			/* length of structure */
332b4c3e9b5SBjoern A. Zeeb 	u8 iftype;			/* type of interface created */
333b4c3e9b5SBjoern A. Zeeb 	u8 wlc_index;			/* optional for wlc index */
334b4c3e9b5SBjoern A. Zeeb 	u32 flags;			/* flags for operation */
335b4c3e9b5SBjoern A. Zeeb 	u8 mac_addr[ETH_ALEN];		/* MAC address */
336b4c3e9b5SBjoern A. Zeeb 	u8 bssid[ETH_ALEN];		/* optional for BSSID */
337b4c3e9b5SBjoern A. Zeeb 	u8 if_index;			/* interface index request */
338b4c3e9b5SBjoern A. Zeeb 	u8 pad[3];
339b4c3e9b5SBjoern A. Zeeb 	u8 data[];			/* Optional for specific data */
340b4c3e9b5SBjoern A. Zeeb };
341b4c3e9b5SBjoern A. Zeeb 
nl80211_band_to_fwil(enum nl80211_band band)342b4c3e9b5SBjoern A. Zeeb static u8 nl80211_band_to_fwil(enum nl80211_band band)
343b4c3e9b5SBjoern A. Zeeb {
344b4c3e9b5SBjoern A. Zeeb 	switch (band) {
345b4c3e9b5SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
346b4c3e9b5SBjoern A. Zeeb 		return WLC_BAND_2G;
347b4c3e9b5SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
348b4c3e9b5SBjoern A. Zeeb 		return WLC_BAND_5G;
349b4c3e9b5SBjoern A. Zeeb 	default:
350b4c3e9b5SBjoern A. Zeeb 		WARN_ON(1);
351b4c3e9b5SBjoern A. Zeeb 		break;
352b4c3e9b5SBjoern A. Zeeb 	}
353b4c3e9b5SBjoern A. Zeeb 	return 0;
354b4c3e9b5SBjoern A. Zeeb }
355b4c3e9b5SBjoern A. Zeeb 
chandef_to_chanspec(struct brcmu_d11inf * d11inf,struct cfg80211_chan_def * ch)356b4c3e9b5SBjoern A. Zeeb static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
357b4c3e9b5SBjoern A. Zeeb 			       struct cfg80211_chan_def *ch)
358b4c3e9b5SBjoern A. Zeeb {
359b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch_inf;
360b4c3e9b5SBjoern A. Zeeb 	s32 primary_offset;
361b4c3e9b5SBjoern A. Zeeb 
362b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
363b4c3e9b5SBjoern A. Zeeb 		  ch->chan->center_freq, ch->center_freq1, ch->width);
364b4c3e9b5SBjoern A. Zeeb 	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
365b4c3e9b5SBjoern A. Zeeb 	primary_offset = ch->chan->center_freq - ch->center_freq1;
366b4c3e9b5SBjoern A. Zeeb 	switch (ch->width) {
367b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20:
368b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_20_NOHT:
369b4c3e9b5SBjoern A. Zeeb 		ch_inf.bw = BRCMU_CHAN_BW_20;
370b4c3e9b5SBjoern A. Zeeb 		WARN_ON(primary_offset != 0);
371b4c3e9b5SBjoern A. Zeeb 		break;
372b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_40:
373b4c3e9b5SBjoern A. Zeeb 		ch_inf.bw = BRCMU_CHAN_BW_40;
374b4c3e9b5SBjoern A. Zeeb 		if (primary_offset > 0)
375b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_U;
376b4c3e9b5SBjoern A. Zeeb 		else
377b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_L;
378b4c3e9b5SBjoern A. Zeeb 		break;
379b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_80:
380b4c3e9b5SBjoern A. Zeeb 		ch_inf.bw = BRCMU_CHAN_BW_80;
381b4c3e9b5SBjoern A. Zeeb 		if (primary_offset == -30)
382b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_LL;
383b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == -10)
384b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_LU;
385b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == 10)
386b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_UL;
387b4c3e9b5SBjoern A. Zeeb 		else
388b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_UU;
389b4c3e9b5SBjoern A. Zeeb 		break;
390b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_160:
391b4c3e9b5SBjoern A. Zeeb 		ch_inf.bw = BRCMU_CHAN_BW_160;
392b4c3e9b5SBjoern A. Zeeb 		if (primary_offset == -70)
393b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_LLL;
394b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == -50)
395b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_LLU;
396b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == -30)
397b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_LUL;
398b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == -10)
399b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_LUU;
400b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == 10)
401b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_ULL;
402b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == 30)
403b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_ULU;
404b4c3e9b5SBjoern A. Zeeb 		else if (primary_offset == 50)
405b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_UUL;
406b4c3e9b5SBjoern A. Zeeb 		else
407b4c3e9b5SBjoern A. Zeeb 			ch_inf.sb = BRCMU_CHAN_SB_UUU;
408b4c3e9b5SBjoern A. Zeeb 		break;
409b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_80P80:
410b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_5:
411b4c3e9b5SBjoern A. Zeeb 	case NL80211_CHAN_WIDTH_10:
412b4c3e9b5SBjoern A. Zeeb 	default:
413b4c3e9b5SBjoern A. Zeeb 		WARN_ON_ONCE(1);
414b4c3e9b5SBjoern A. Zeeb 	}
415b4c3e9b5SBjoern A. Zeeb 	switch (ch->chan->band) {
416b4c3e9b5SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
417b4c3e9b5SBjoern A. Zeeb 		ch_inf.band = BRCMU_CHAN_BAND_2G;
418b4c3e9b5SBjoern A. Zeeb 		break;
419b4c3e9b5SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
420b4c3e9b5SBjoern A. Zeeb 		ch_inf.band = BRCMU_CHAN_BAND_5G;
421b4c3e9b5SBjoern A. Zeeb 		break;
422b4c3e9b5SBjoern A. Zeeb 	case NL80211_BAND_60GHZ:
423b4c3e9b5SBjoern A. Zeeb 	default:
424b4c3e9b5SBjoern A. Zeeb 		WARN_ON_ONCE(1);
425b4c3e9b5SBjoern A. Zeeb 	}
426b4c3e9b5SBjoern A. Zeeb 	d11inf->encchspec(&ch_inf);
427b4c3e9b5SBjoern A. Zeeb 
428b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "chanspec: 0x%x\n", ch_inf.chspec);
429b4c3e9b5SBjoern A. Zeeb 	return ch_inf.chspec;
430b4c3e9b5SBjoern A. Zeeb }
431b4c3e9b5SBjoern A. Zeeb 
channel_to_chanspec(struct brcmu_d11inf * d11inf,struct ieee80211_channel * ch)432b4c3e9b5SBjoern A. Zeeb u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
433b4c3e9b5SBjoern A. Zeeb 			struct ieee80211_channel *ch)
434b4c3e9b5SBjoern A. Zeeb {
435b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch_inf;
436b4c3e9b5SBjoern A. Zeeb 
437b4c3e9b5SBjoern A. Zeeb 	ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
438b4c3e9b5SBjoern A. Zeeb 	ch_inf.bw = BRCMU_CHAN_BW_20;
439b4c3e9b5SBjoern A. Zeeb 	d11inf->encchspec(&ch_inf);
440b4c3e9b5SBjoern A. Zeeb 
441b4c3e9b5SBjoern A. Zeeb 	return ch_inf.chspec;
442b4c3e9b5SBjoern A. Zeeb }
443b4c3e9b5SBjoern A. Zeeb 
444b4c3e9b5SBjoern A. Zeeb /* Traverse a string of 1-byte tag/1-byte length/variable-length value
445b4c3e9b5SBjoern A. Zeeb  * triples, returning a pointer to the substring whose first element
446b4c3e9b5SBjoern A. Zeeb  * matches tag
447b4c3e9b5SBjoern A. Zeeb  */
448b4c3e9b5SBjoern A. Zeeb static const struct brcmf_tlv *
brcmf_parse_tlvs(const void * buf,int buflen,uint key)449b4c3e9b5SBjoern A. Zeeb brcmf_parse_tlvs(const void *buf, int buflen, uint key)
450b4c3e9b5SBjoern A. Zeeb {
451b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *elt = buf;
452b4c3e9b5SBjoern A. Zeeb 	int totlen = buflen;
453b4c3e9b5SBjoern A. Zeeb 
454b4c3e9b5SBjoern A. Zeeb 	/* find tagged parameter */
455b4c3e9b5SBjoern A. Zeeb 	while (totlen >= TLV_HDR_LEN) {
456b4c3e9b5SBjoern A. Zeeb 		int len = elt->len;
457b4c3e9b5SBjoern A. Zeeb 
458b4c3e9b5SBjoern A. Zeeb 		/* validate remaining totlen */
459b4c3e9b5SBjoern A. Zeeb 		if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
460b4c3e9b5SBjoern A. Zeeb 			return elt;
461b4c3e9b5SBjoern A. Zeeb 
462902136e0SBjoern A. Zeeb #if defined(__linux__)
463b4c3e9b5SBjoern A. Zeeb 		elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
464902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
465902136e0SBjoern A. Zeeb 		elt = (const struct brcmf_tlv *)((const u8 *)elt + (len + TLV_HDR_LEN));
466902136e0SBjoern A. Zeeb #endif
467b4c3e9b5SBjoern A. Zeeb 		totlen -= (len + TLV_HDR_LEN);
468b4c3e9b5SBjoern A. Zeeb 	}
469b4c3e9b5SBjoern A. Zeeb 
470b4c3e9b5SBjoern A. Zeeb 	return NULL;
471b4c3e9b5SBjoern A. Zeeb }
472b4c3e9b5SBjoern A. Zeeb 
473b4c3e9b5SBjoern A. Zeeb /* Is any of the tlvs the expected entry? If
474b4c3e9b5SBjoern A. Zeeb  * not update the tlvs buffer pointer/length.
475b4c3e9b5SBjoern A. Zeeb  */
476b4c3e9b5SBjoern A. Zeeb static bool
brcmf_tlv_has_ie(const u8 * ie,const u8 ** tlvs,u32 * tlvs_len,const u8 * oui,u32 oui_len,u8 type)477b4c3e9b5SBjoern A. Zeeb brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
478b4c3e9b5SBjoern A. Zeeb 		 const u8 *oui, u32 oui_len, u8 type)
479b4c3e9b5SBjoern A. Zeeb {
480b4c3e9b5SBjoern A. Zeeb 	/* If the contents match the OUI and the type */
481b4c3e9b5SBjoern A. Zeeb 	if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
482b4c3e9b5SBjoern A. Zeeb 	    !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
483b4c3e9b5SBjoern A. Zeeb 	    type == ie[TLV_BODY_OFF + oui_len]) {
484b4c3e9b5SBjoern A. Zeeb 		return true;
485b4c3e9b5SBjoern A. Zeeb 	}
486b4c3e9b5SBjoern A. Zeeb 
487b4c3e9b5SBjoern A. Zeeb 	if (tlvs == NULL)
488b4c3e9b5SBjoern A. Zeeb 		return false;
489b4c3e9b5SBjoern A. Zeeb 	/* point to the next ie */
490b4c3e9b5SBjoern A. Zeeb 	ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
491b4c3e9b5SBjoern A. Zeeb 	/* calculate the length of the rest of the buffer */
492b4c3e9b5SBjoern A. Zeeb 	*tlvs_len -= (int)(ie - *tlvs);
493b4c3e9b5SBjoern A. Zeeb 	/* update the pointer to the start of the buffer */
494b4c3e9b5SBjoern A. Zeeb 	*tlvs = ie;
495b4c3e9b5SBjoern A. Zeeb 
496b4c3e9b5SBjoern A. Zeeb 	return false;
497b4c3e9b5SBjoern A. Zeeb }
498b4c3e9b5SBjoern A. Zeeb 
499902136e0SBjoern A. Zeeb #if defined(__linux__)
500b4c3e9b5SBjoern A. Zeeb static struct brcmf_vs_tlv *
501902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
502902136e0SBjoern A. Zeeb static const struct brcmf_vs_tlv *
503902136e0SBjoern A. Zeeb #endif
brcmf_find_wpaie(const u8 * parse,u32 len)504b4c3e9b5SBjoern A. Zeeb brcmf_find_wpaie(const u8 *parse, u32 len)
505b4c3e9b5SBjoern A. Zeeb {
506b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *ie;
507b4c3e9b5SBjoern A. Zeeb 
508b4c3e9b5SBjoern A. Zeeb 	while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
509b4c3e9b5SBjoern A. Zeeb 		if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
510b4c3e9b5SBjoern A. Zeeb 				     WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
511902136e0SBjoern A. Zeeb #if defined(__linux__)
512b4c3e9b5SBjoern A. Zeeb 			return (struct brcmf_vs_tlv *)ie;
513902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
514902136e0SBjoern A. Zeeb 			return (const struct brcmf_vs_tlv *)ie;
515902136e0SBjoern A. Zeeb #endif
516b4c3e9b5SBjoern A. Zeeb 	}
517b4c3e9b5SBjoern A. Zeeb 	return NULL;
518b4c3e9b5SBjoern A. Zeeb }
519b4c3e9b5SBjoern A. Zeeb 
520902136e0SBjoern A. Zeeb #if defined(__linux__)
521b4c3e9b5SBjoern A. Zeeb static struct brcmf_vs_tlv *
522902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
523902136e0SBjoern A. Zeeb static const struct brcmf_vs_tlv *
524902136e0SBjoern A. Zeeb #endif
brcmf_find_wpsie(const u8 * parse,u32 len)525b4c3e9b5SBjoern A. Zeeb brcmf_find_wpsie(const u8 *parse, u32 len)
526b4c3e9b5SBjoern A. Zeeb {
527b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *ie;
528b4c3e9b5SBjoern A. Zeeb 
529b4c3e9b5SBjoern A. Zeeb 	while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
530902136e0SBjoern A. Zeeb #if defined(__linux__)
531b4c3e9b5SBjoern A. Zeeb 		if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
532b4c3e9b5SBjoern A. Zeeb 				     WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
533b4c3e9b5SBjoern A. Zeeb 			return (struct brcmf_vs_tlv *)ie;
534902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
535902136e0SBjoern A. Zeeb 		if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
536902136e0SBjoern A. Zeeb 				     WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
537902136e0SBjoern A. Zeeb 			return (const struct brcmf_vs_tlv *)ie;
538902136e0SBjoern A. Zeeb #endif
539b4c3e9b5SBjoern A. Zeeb 	}
540b4c3e9b5SBjoern A. Zeeb 	return NULL;
541b4c3e9b5SBjoern A. Zeeb }
542b4c3e9b5SBjoern A. Zeeb 
brcmf_vif_change_validate(struct brcmf_cfg80211_info * cfg,struct brcmf_cfg80211_vif * vif,enum nl80211_iftype new_type)543b4c3e9b5SBjoern A. Zeeb static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
544b4c3e9b5SBjoern A. Zeeb 				     struct brcmf_cfg80211_vif *vif,
545b4c3e9b5SBjoern A. Zeeb 				     enum nl80211_iftype new_type)
546b4c3e9b5SBjoern A. Zeeb {
547b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *pos;
548b4c3e9b5SBjoern A. Zeeb 	bool check_combos = false;
549b4c3e9b5SBjoern A. Zeeb 	int ret = 0;
550b4c3e9b5SBjoern A. Zeeb 	struct iface_combination_params params = {
551b4c3e9b5SBjoern A. Zeeb 		.num_different_channels = 1,
552b4c3e9b5SBjoern A. Zeeb 	};
553b4c3e9b5SBjoern A. Zeeb 
554b4c3e9b5SBjoern A. Zeeb 	list_for_each_entry(pos, &cfg->vif_list, list)
555b4c3e9b5SBjoern A. Zeeb 		if (pos == vif) {
556b4c3e9b5SBjoern A. Zeeb 			params.iftype_num[new_type]++;
557b4c3e9b5SBjoern A. Zeeb 		} else {
558b4c3e9b5SBjoern A. Zeeb 			/* concurrent interfaces so need check combinations */
559b4c3e9b5SBjoern A. Zeeb 			check_combos = true;
560b4c3e9b5SBjoern A. Zeeb 			params.iftype_num[pos->wdev.iftype]++;
561b4c3e9b5SBjoern A. Zeeb 		}
562b4c3e9b5SBjoern A. Zeeb 
563b4c3e9b5SBjoern A. Zeeb 	if (check_combos)
564b4c3e9b5SBjoern A. Zeeb 		ret = cfg80211_check_combinations(cfg->wiphy, &params);
565b4c3e9b5SBjoern A. Zeeb 
566b4c3e9b5SBjoern A. Zeeb 	return ret;
567b4c3e9b5SBjoern A. Zeeb }
568b4c3e9b5SBjoern A. Zeeb 
brcmf_vif_add_validate(struct brcmf_cfg80211_info * cfg,enum nl80211_iftype new_type)569b4c3e9b5SBjoern A. Zeeb static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
570b4c3e9b5SBjoern A. Zeeb 				  enum nl80211_iftype new_type)
571b4c3e9b5SBjoern A. Zeeb {
572b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *pos;
573b4c3e9b5SBjoern A. Zeeb 	struct iface_combination_params params = {
574b4c3e9b5SBjoern A. Zeeb 		.num_different_channels = 1,
575b4c3e9b5SBjoern A. Zeeb 	};
576b4c3e9b5SBjoern A. Zeeb 
577b4c3e9b5SBjoern A. Zeeb 	list_for_each_entry(pos, &cfg->vif_list, list)
578b4c3e9b5SBjoern A. Zeeb 		params.iftype_num[pos->wdev.iftype]++;
579b4c3e9b5SBjoern A. Zeeb 
580b4c3e9b5SBjoern A. Zeeb 	params.iftype_num[new_type]++;
581b4c3e9b5SBjoern A. Zeeb 	return cfg80211_check_combinations(cfg->wiphy, &params);
582b4c3e9b5SBjoern A. Zeeb }
583b4c3e9b5SBjoern A. Zeeb 
convert_key_from_CPU(struct brcmf_wsec_key * key,struct brcmf_wsec_key_le * key_le)584b4c3e9b5SBjoern A. Zeeb static void convert_key_from_CPU(struct brcmf_wsec_key *key,
585b4c3e9b5SBjoern A. Zeeb 				 struct brcmf_wsec_key_le *key_le)
586b4c3e9b5SBjoern A. Zeeb {
587b4c3e9b5SBjoern A. Zeeb 	key_le->index = cpu_to_le32(key->index);
588b4c3e9b5SBjoern A. Zeeb 	key_le->len = cpu_to_le32(key->len);
589b4c3e9b5SBjoern A. Zeeb 	key_le->algo = cpu_to_le32(key->algo);
590b4c3e9b5SBjoern A. Zeeb 	key_le->flags = cpu_to_le32(key->flags);
591b4c3e9b5SBjoern A. Zeeb 	key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
592b4c3e9b5SBjoern A. Zeeb 	key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
593b4c3e9b5SBjoern A. Zeeb 	key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
594b4c3e9b5SBjoern A. Zeeb 	memcpy(key_le->data, key->data, sizeof(key->data));
595b4c3e9b5SBjoern A. Zeeb 	memcpy(key_le->ea, key->ea, sizeof(key->ea));
596b4c3e9b5SBjoern A. Zeeb }
597b4c3e9b5SBjoern A. Zeeb 
598b4c3e9b5SBjoern A. Zeeb static int
send_key_to_dongle(struct brcmf_if * ifp,struct brcmf_wsec_key * key)599b4c3e9b5SBjoern A. Zeeb send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
600b4c3e9b5SBjoern A. Zeeb {
601b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
602b4c3e9b5SBjoern A. Zeeb 	int err;
603b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wsec_key_le key_le;
604b4c3e9b5SBjoern A. Zeeb 
605b4c3e9b5SBjoern A. Zeeb 	convert_key_from_CPU(key, &key_le);
606b4c3e9b5SBjoern A. Zeeb 
607b4c3e9b5SBjoern A. Zeeb 	brcmf_netdev_wait_pend8021x(ifp);
608b4c3e9b5SBjoern A. Zeeb 
609b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
610b4c3e9b5SBjoern A. Zeeb 					sizeof(key_le));
611b4c3e9b5SBjoern A. Zeeb 
612b4c3e9b5SBjoern A. Zeeb 	if (err)
613b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wsec_key error (%d)\n", err);
614b4c3e9b5SBjoern A. Zeeb 	return err;
615b4c3e9b5SBjoern A. Zeeb }
616b4c3e9b5SBjoern A. Zeeb 
617b4c3e9b5SBjoern A. Zeeb static void
brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev * wdev)618b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
619b4c3e9b5SBjoern A. Zeeb {
620b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
621b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
622b4c3e9b5SBjoern A. Zeeb 
623b4c3e9b5SBjoern A. Zeeb 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
624b4c3e9b5SBjoern A. Zeeb 	ifp = vif->ifp;
625b4c3e9b5SBjoern A. Zeeb 
626b4c3e9b5SBjoern A. Zeeb 	if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
627b4c3e9b5SBjoern A. Zeeb 	    (wdev->iftype == NL80211_IFTYPE_AP) ||
628b4c3e9b5SBjoern A. Zeeb 	    (wdev->iftype == NL80211_IFTYPE_P2P_GO))
629b4c3e9b5SBjoern A. Zeeb 		brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
630b4c3e9b5SBjoern A. Zeeb 						ADDR_DIRECT);
631b4c3e9b5SBjoern A. Zeeb 	else
632b4c3e9b5SBjoern A. Zeeb 		brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
633b4c3e9b5SBjoern A. Zeeb 						ADDR_INDIRECT);
634b4c3e9b5SBjoern A. Zeeb }
635b4c3e9b5SBjoern A. Zeeb 
brcmf_get_first_free_bsscfgidx(struct brcmf_pub * drvr)636b4c3e9b5SBjoern A. Zeeb static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
637b4c3e9b5SBjoern A. Zeeb {
638b4c3e9b5SBjoern A. Zeeb 	int bsscfgidx;
639b4c3e9b5SBjoern A. Zeeb 
640b4c3e9b5SBjoern A. Zeeb 	for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {
641b4c3e9b5SBjoern A. Zeeb 		/* bsscfgidx 1 is reserved for legacy P2P */
642b4c3e9b5SBjoern A. Zeeb 		if (bsscfgidx == 1)
643b4c3e9b5SBjoern A. Zeeb 			continue;
644b4c3e9b5SBjoern A. Zeeb 		if (!drvr->iflist[bsscfgidx])
645b4c3e9b5SBjoern A. Zeeb 			return bsscfgidx;
646b4c3e9b5SBjoern A. Zeeb 	}
647b4c3e9b5SBjoern A. Zeeb 
648b4c3e9b5SBjoern A. Zeeb 	return -ENOMEM;
649b4c3e9b5SBjoern A. Zeeb }
650b4c3e9b5SBjoern A. Zeeb 
brcmf_set_vif_sta_macaddr(struct brcmf_if * ifp,u8 * mac_addr)651b4c3e9b5SBjoern A. Zeeb static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr)
652b4c3e9b5SBjoern A. Zeeb {
653b4c3e9b5SBjoern A. Zeeb 	u8 mac_idx = ifp->drvr->sta_mac_idx;
654b4c3e9b5SBjoern A. Zeeb 
655b4c3e9b5SBjoern A. Zeeb 	/* set difference MAC address with locally administered bit */
656b4c3e9b5SBjoern A. Zeeb 	memcpy(mac_addr, ifp->mac_addr, ETH_ALEN);
657b4c3e9b5SBjoern A. Zeeb 	mac_addr[0] |= 0x02;
658b4c3e9b5SBjoern A. Zeeb 	mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0;
659b4c3e9b5SBjoern A. Zeeb 	mac_idx++;
660b4c3e9b5SBjoern A. Zeeb 	mac_idx = mac_idx % 2;
661b4c3e9b5SBjoern A. Zeeb 	ifp->drvr->sta_mac_idx = mac_idx;
662b4c3e9b5SBjoern A. Zeeb }
663b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_request_sta_if(struct brcmf_if * ifp,u8 * macaddr)664b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr)
665b4c3e9b5SBjoern A. Zeeb {
666b4c3e9b5SBjoern A. Zeeb 	struct wl_interface_create_v1 iface_v1;
667b4c3e9b5SBjoern A. Zeeb 	struct wl_interface_create_v2 iface_v2;
668b4c3e9b5SBjoern A. Zeeb 	struct wl_interface_create_v3 iface_v3;
669b4c3e9b5SBjoern A. Zeeb 	u32 iface_create_ver;
670b4c3e9b5SBjoern A. Zeeb 	int err;
671b4c3e9b5SBjoern A. Zeeb 
672b4c3e9b5SBjoern A. Zeeb 	/* interface_create version 1 */
673b4c3e9b5SBjoern A. Zeeb 	memset(&iface_v1, 0, sizeof(iface_v1));
674b4c3e9b5SBjoern A. Zeeb 	iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
675b4c3e9b5SBjoern A. Zeeb 	iface_v1.flags = WL_INTERFACE_CREATE_STA |
676b4c3e9b5SBjoern A. Zeeb 			 WL_INTERFACE_MAC_USE;
677b4c3e9b5SBjoern A. Zeeb 	if (!is_zero_ether_addr(macaddr))
678b4c3e9b5SBjoern A. Zeeb 		memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN);
679b4c3e9b5SBjoern A. Zeeb 	else
680b4c3e9b5SBjoern A. Zeeb 		brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
681b4c3e9b5SBjoern A. Zeeb 
682b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
683b4c3e9b5SBjoern A. Zeeb 				       &iface_v1,
684b4c3e9b5SBjoern A. Zeeb 				       sizeof(iface_v1));
685b4c3e9b5SBjoern A. Zeeb 	if (err) {
686b4c3e9b5SBjoern A. Zeeb 		brcmf_info("failed to create interface(v1), err=%d\n",
687b4c3e9b5SBjoern A. Zeeb 			   err);
688b4c3e9b5SBjoern A. Zeeb 	} else {
689b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "interface created(v1)\n");
690b4c3e9b5SBjoern A. Zeeb 		return 0;
691b4c3e9b5SBjoern A. Zeeb 	}
692b4c3e9b5SBjoern A. Zeeb 
693b4c3e9b5SBjoern A. Zeeb 	/* interface_create version 2 */
694b4c3e9b5SBjoern A. Zeeb 	memset(&iface_v2, 0, sizeof(iface_v2));
695b4c3e9b5SBjoern A. Zeeb 	iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
696b4c3e9b5SBjoern A. Zeeb 	iface_v2.flags = WL_INTERFACE_MAC_USE;
697b4c3e9b5SBjoern A. Zeeb 	iface_v2.iftype = WL_INTERFACE_CREATE_STA;
698b4c3e9b5SBjoern A. Zeeb 	if (!is_zero_ether_addr(macaddr))
699b4c3e9b5SBjoern A. Zeeb 		memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN);
700b4c3e9b5SBjoern A. Zeeb 	else
701b4c3e9b5SBjoern A. Zeeb 		brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
702b4c3e9b5SBjoern A. Zeeb 
703b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
704b4c3e9b5SBjoern A. Zeeb 				       &iface_v2,
705b4c3e9b5SBjoern A. Zeeb 				       sizeof(iface_v2));
706b4c3e9b5SBjoern A. Zeeb 	if (err) {
707b4c3e9b5SBjoern A. Zeeb 		brcmf_info("failed to create interface(v2), err=%d\n",
708b4c3e9b5SBjoern A. Zeeb 			   err);
709b4c3e9b5SBjoern A. Zeeb 	} else {
710b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "interface created(v2)\n");
711b4c3e9b5SBjoern A. Zeeb 		return 0;
712b4c3e9b5SBjoern A. Zeeb 	}
713b4c3e9b5SBjoern A. Zeeb 
714b4c3e9b5SBjoern A. Zeeb 	/* interface_create version 3+ */
715b4c3e9b5SBjoern A. Zeeb 	/* get supported version from firmware side */
716b4c3e9b5SBjoern A. Zeeb 	iface_create_ver = 0;
717b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",
718b4c3e9b5SBjoern A. Zeeb 					 &iface_create_ver);
719b4c3e9b5SBjoern A. Zeeb 	if (err) {
720b4c3e9b5SBjoern A. Zeeb 		brcmf_err("fail to get supported version, err=%d\n", err);
721b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
722b4c3e9b5SBjoern A. Zeeb 	}
723b4c3e9b5SBjoern A. Zeeb 
724b4c3e9b5SBjoern A. Zeeb 	switch (iface_create_ver) {
725b4c3e9b5SBjoern A. Zeeb 	case WL_INTERFACE_CREATE_VER_3:
726b4c3e9b5SBjoern A. Zeeb 		memset(&iface_v3, 0, sizeof(iface_v3));
727b4c3e9b5SBjoern A. Zeeb 		iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
728b4c3e9b5SBjoern A. Zeeb 		iface_v3.flags = WL_INTERFACE_MAC_USE;
729b4c3e9b5SBjoern A. Zeeb 		iface_v3.iftype = WL_INTERFACE_CREATE_STA;
730b4c3e9b5SBjoern A. Zeeb 		if (!is_zero_ether_addr(macaddr))
731b4c3e9b5SBjoern A. Zeeb 			memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN);
732b4c3e9b5SBjoern A. Zeeb 		else
733b4c3e9b5SBjoern A. Zeeb 			brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
734b4c3e9b5SBjoern A. Zeeb 
735b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_get(ifp, "interface_create",
736b4c3e9b5SBjoern A. Zeeb 					       &iface_v3,
737b4c3e9b5SBjoern A. Zeeb 					       sizeof(iface_v3));
738b4c3e9b5SBjoern A. Zeeb 
739b4c3e9b5SBjoern A. Zeeb 		if (!err)
740b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "interface created(v3)\n");
741b4c3e9b5SBjoern A. Zeeb 		break;
742b4c3e9b5SBjoern A. Zeeb 	default:
743b4c3e9b5SBjoern A. Zeeb 		brcmf_err("not support interface create(v%d)\n",
744b4c3e9b5SBjoern A. Zeeb 			  iface_create_ver);
745b4c3e9b5SBjoern A. Zeeb 		err = -EOPNOTSUPP;
746b4c3e9b5SBjoern A. Zeeb 		break;
747b4c3e9b5SBjoern A. Zeeb 	}
748b4c3e9b5SBjoern A. Zeeb 
749b4c3e9b5SBjoern A. Zeeb 	if (err) {
750b4c3e9b5SBjoern A. Zeeb 		brcmf_info("station interface creation failed (%d)\n",
751b4c3e9b5SBjoern A. Zeeb 			   err);
752b4c3e9b5SBjoern A. Zeeb 		return -EIO;
753b4c3e9b5SBjoern A. Zeeb 	}
754b4c3e9b5SBjoern A. Zeeb 
755b4c3e9b5SBjoern A. Zeeb 	return 0;
756b4c3e9b5SBjoern A. Zeeb }
757b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_request_ap_if(struct brcmf_if * ifp)758b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
759b4c3e9b5SBjoern A. Zeeb {
760b4c3e9b5SBjoern A. Zeeb 	struct wl_interface_create_v1 iface_v1;
761b4c3e9b5SBjoern A. Zeeb 	struct wl_interface_create_v2 iface_v2;
762b4c3e9b5SBjoern A. Zeeb 	struct wl_interface_create_v3 iface_v3;
763b4c3e9b5SBjoern A. Zeeb 	u32 iface_create_ver;
764b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
765b4c3e9b5SBjoern A. Zeeb 	struct brcmf_mbss_ssid_le mbss_ssid_le;
766b4c3e9b5SBjoern A. Zeeb 	int bsscfgidx;
767b4c3e9b5SBjoern A. Zeeb 	int err;
768b4c3e9b5SBjoern A. Zeeb 
769b4c3e9b5SBjoern A. Zeeb 	/* interface_create version 1 */
770b4c3e9b5SBjoern A. Zeeb 	memset(&iface_v1, 0, sizeof(iface_v1));
771b4c3e9b5SBjoern A. Zeeb 	iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
772b4c3e9b5SBjoern A. Zeeb 	iface_v1.flags = WL_INTERFACE_CREATE_AP |
773b4c3e9b5SBjoern A. Zeeb 			 WL_INTERFACE_MAC_USE;
774b4c3e9b5SBjoern A. Zeeb 
775b4c3e9b5SBjoern A. Zeeb 	brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
776b4c3e9b5SBjoern A. Zeeb 
777b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
778b4c3e9b5SBjoern A. Zeeb 				       &iface_v1,
779b4c3e9b5SBjoern A. Zeeb 				       sizeof(iface_v1));
780b4c3e9b5SBjoern A. Zeeb 	if (err) {
781b4c3e9b5SBjoern A. Zeeb 		brcmf_info("failed to create interface(v1), err=%d\n",
782b4c3e9b5SBjoern A. Zeeb 			   err);
783b4c3e9b5SBjoern A. Zeeb 	} else {
784b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "interface created(v1)\n");
785b4c3e9b5SBjoern A. Zeeb 		return 0;
786b4c3e9b5SBjoern A. Zeeb 	}
787b4c3e9b5SBjoern A. Zeeb 
788b4c3e9b5SBjoern A. Zeeb 	/* interface_create version 2 */
789b4c3e9b5SBjoern A. Zeeb 	memset(&iface_v2, 0, sizeof(iface_v2));
790b4c3e9b5SBjoern A. Zeeb 	iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
791b4c3e9b5SBjoern A. Zeeb 	iface_v2.flags = WL_INTERFACE_MAC_USE;
792b4c3e9b5SBjoern A. Zeeb 	iface_v2.iftype = WL_INTERFACE_CREATE_AP;
793b4c3e9b5SBjoern A. Zeeb 
794b4c3e9b5SBjoern A. Zeeb 	brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
795b4c3e9b5SBjoern A. Zeeb 
796b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "interface_create",
797b4c3e9b5SBjoern A. Zeeb 				       &iface_v2,
798b4c3e9b5SBjoern A. Zeeb 				       sizeof(iface_v2));
799b4c3e9b5SBjoern A. Zeeb 	if (err) {
800b4c3e9b5SBjoern A. Zeeb 		brcmf_info("failed to create interface(v2), err=%d\n",
801b4c3e9b5SBjoern A. Zeeb 			   err);
802b4c3e9b5SBjoern A. Zeeb 	} else {
803b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "interface created(v2)\n");
804b4c3e9b5SBjoern A. Zeeb 		return 0;
805b4c3e9b5SBjoern A. Zeeb 	}
806b4c3e9b5SBjoern A. Zeeb 
807b4c3e9b5SBjoern A. Zeeb 	/* interface_create version 3+ */
808b4c3e9b5SBjoern A. Zeeb 	/* get supported version from firmware side */
809b4c3e9b5SBjoern A. Zeeb 	iface_create_ver = 0;
810b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",
811b4c3e9b5SBjoern A. Zeeb 					 &iface_create_ver);
812b4c3e9b5SBjoern A. Zeeb 	if (err) {
813b4c3e9b5SBjoern A. Zeeb 		brcmf_err("fail to get supported version, err=%d\n", err);
814b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
815b4c3e9b5SBjoern A. Zeeb 	}
816b4c3e9b5SBjoern A. Zeeb 
817b4c3e9b5SBjoern A. Zeeb 	switch (iface_create_ver) {
818b4c3e9b5SBjoern A. Zeeb 	case WL_INTERFACE_CREATE_VER_3:
819b4c3e9b5SBjoern A. Zeeb 		memset(&iface_v3, 0, sizeof(iface_v3));
820b4c3e9b5SBjoern A. Zeeb 		iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
821b4c3e9b5SBjoern A. Zeeb 		iface_v3.flags = WL_INTERFACE_MAC_USE;
822b4c3e9b5SBjoern A. Zeeb 		iface_v3.iftype = WL_INTERFACE_CREATE_AP;
823b4c3e9b5SBjoern A. Zeeb 		brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
824b4c3e9b5SBjoern A. Zeeb 
825b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_get(ifp, "interface_create",
826b4c3e9b5SBjoern A. Zeeb 					       &iface_v3,
827b4c3e9b5SBjoern A. Zeeb 					       sizeof(iface_v3));
828b4c3e9b5SBjoern A. Zeeb 
829b4c3e9b5SBjoern A. Zeeb 		if (!err)
830b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "interface created(v3)\n");
831b4c3e9b5SBjoern A. Zeeb 		break;
832b4c3e9b5SBjoern A. Zeeb 	default:
833b4c3e9b5SBjoern A. Zeeb 		brcmf_err("not support interface create(v%d)\n",
834b4c3e9b5SBjoern A. Zeeb 			  iface_create_ver);
835b4c3e9b5SBjoern A. Zeeb 		err = -EOPNOTSUPP;
836b4c3e9b5SBjoern A. Zeeb 		break;
837b4c3e9b5SBjoern A. Zeeb 	}
838b4c3e9b5SBjoern A. Zeeb 
839b4c3e9b5SBjoern A. Zeeb 	if (err) {
840b4c3e9b5SBjoern A. Zeeb 		brcmf_info("Does not support interface_create (%d)\n",
841b4c3e9b5SBjoern A. Zeeb 			   err);
842b4c3e9b5SBjoern A. Zeeb 		memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
843b4c3e9b5SBjoern A. Zeeb 		bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);
844b4c3e9b5SBjoern A. Zeeb 		if (bsscfgidx < 0)
845b4c3e9b5SBjoern A. Zeeb 			return bsscfgidx;
846b4c3e9b5SBjoern A. Zeeb 
847b4c3e9b5SBjoern A. Zeeb 		mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
848b4c3e9b5SBjoern A. Zeeb 		mbss_ssid_le.SSID_len = cpu_to_le32(5);
849b4c3e9b5SBjoern A. Zeeb 		sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx);
850b4c3e9b5SBjoern A. Zeeb 
851b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
852b4c3e9b5SBjoern A. Zeeb 						sizeof(mbss_ssid_le));
853b4c3e9b5SBjoern A. Zeeb 
854b4c3e9b5SBjoern A. Zeeb 		if (err < 0)
855b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "setting ssid failed %d\n", err);
856b4c3e9b5SBjoern A. Zeeb 	}
857b4c3e9b5SBjoern A. Zeeb 
858b4c3e9b5SBjoern A. Zeeb 	return err;
859b4c3e9b5SBjoern A. Zeeb }
860b4c3e9b5SBjoern A. Zeeb 
861b4c3e9b5SBjoern A. Zeeb /**
862b4c3e9b5SBjoern A. Zeeb  * brcmf_apsta_add_vif() - create a new AP or STA virtual interface
863b4c3e9b5SBjoern A. Zeeb  *
864b4c3e9b5SBjoern A. Zeeb  * @wiphy: wiphy device of new interface.
865b4c3e9b5SBjoern A. Zeeb  * @name: name of the new interface.
866b4c3e9b5SBjoern A. Zeeb  * @params: contains mac address for AP or STA device.
867b4c3e9b5SBjoern A. Zeeb  * @type: interface type.
868b4c3e9b5SBjoern A. Zeeb  *
869b4c3e9b5SBjoern A. Zeeb  * Return: pointer to new vif on success, ERR_PTR(-errno) if not
870b4c3e9b5SBjoern A. Zeeb  */
871b4c3e9b5SBjoern A. Zeeb static
brcmf_apsta_add_vif(struct wiphy * wiphy,const char * name,struct vif_params * params,enum nl80211_iftype type)872b4c3e9b5SBjoern A. Zeeb struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name,
873b4c3e9b5SBjoern A. Zeeb 					 struct vif_params *params,
874b4c3e9b5SBjoern A. Zeeb 					 enum nl80211_iftype type)
875b4c3e9b5SBjoern A. Zeeb {
876b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
877b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
878b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
879b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
880b4c3e9b5SBjoern A. Zeeb 	int err;
881b4c3e9b5SBjoern A. Zeeb 
882b4c3e9b5SBjoern A. Zeeb 	if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP)
883b4c3e9b5SBjoern A. Zeeb 		return ERR_PTR(-EINVAL);
884b4c3e9b5SBjoern A. Zeeb 
885b4c3e9b5SBjoern A. Zeeb 	if (brcmf_cfg80211_vif_event_armed(cfg))
886b4c3e9b5SBjoern A. Zeeb 		return ERR_PTR(-EBUSY);
887b4c3e9b5SBjoern A. Zeeb 
888b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
889b4c3e9b5SBjoern A. Zeeb 
890b4c3e9b5SBjoern A. Zeeb 	vif = brcmf_alloc_vif(cfg, type);
891b4c3e9b5SBjoern A. Zeeb 	if (IS_ERR(vif))
892b4c3e9b5SBjoern A. Zeeb 		return (struct wireless_dev *)vif;
893b4c3e9b5SBjoern A. Zeeb 
894b4c3e9b5SBjoern A. Zeeb 	brcmf_cfg80211_arm_vif_event(cfg, vif);
895b4c3e9b5SBjoern A. Zeeb 
896b4c3e9b5SBjoern A. Zeeb 	if (type == NL80211_IFTYPE_STATION)
897b4c3e9b5SBjoern A. Zeeb 		err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr);
898b4c3e9b5SBjoern A. Zeeb 	else
899b4c3e9b5SBjoern A. Zeeb 		err = brcmf_cfg80211_request_ap_if(ifp);
900b4c3e9b5SBjoern A. Zeeb 	if (err) {
901b4c3e9b5SBjoern A. Zeeb 		brcmf_cfg80211_arm_vif_event(cfg, NULL);
902b4c3e9b5SBjoern A. Zeeb 		goto fail;
903b4c3e9b5SBjoern A. Zeeb 	}
904b4c3e9b5SBjoern A. Zeeb 
905b4c3e9b5SBjoern A. Zeeb 	/* wait for firmware event */
906b4c3e9b5SBjoern A. Zeeb 	err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
907b4c3e9b5SBjoern A. Zeeb 					    BRCMF_VIF_EVENT_TIMEOUT);
908b4c3e9b5SBjoern A. Zeeb 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
909b4c3e9b5SBjoern A. Zeeb 	if (!err) {
910b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "timeout occurred\n");
911b4c3e9b5SBjoern A. Zeeb 		err = -EIO;
912b4c3e9b5SBjoern A. Zeeb 		goto fail;
913b4c3e9b5SBjoern A. Zeeb 	}
914b4c3e9b5SBjoern A. Zeeb 
915b4c3e9b5SBjoern A. Zeeb 	/* interface created in firmware */
916b4c3e9b5SBjoern A. Zeeb 	ifp = vif->ifp;
917b4c3e9b5SBjoern A. Zeeb 	if (!ifp) {
918b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "no if pointer provided\n");
919b4c3e9b5SBjoern A. Zeeb 		err = -ENOENT;
920b4c3e9b5SBjoern A. Zeeb 		goto fail;
921b4c3e9b5SBjoern A. Zeeb 	}
922b4c3e9b5SBjoern A. Zeeb 
923b4c3e9b5SBjoern A. Zeeb 	strscpy(ifp->ndev->name, name, sizeof(ifp->ndev->name));
924b4c3e9b5SBjoern A. Zeeb 	err = brcmf_net_attach(ifp, true);
925b4c3e9b5SBjoern A. Zeeb 	if (err) {
926b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Registering netdevice failed\n");
927b4c3e9b5SBjoern A. Zeeb 		free_netdev(ifp->ndev);
928b4c3e9b5SBjoern A. Zeeb 		goto fail;
929b4c3e9b5SBjoern A. Zeeb 	}
930b4c3e9b5SBjoern A. Zeeb 
931b4c3e9b5SBjoern A. Zeeb 	return &ifp->vif->wdev;
932b4c3e9b5SBjoern A. Zeeb 
933b4c3e9b5SBjoern A. Zeeb fail:
934b4c3e9b5SBjoern A. Zeeb 	brcmf_free_vif(vif);
935b4c3e9b5SBjoern A. Zeeb 	return ERR_PTR(err);
936b4c3e9b5SBjoern A. Zeeb }
937b4c3e9b5SBjoern A. Zeeb 
brcmf_is_apmode(struct brcmf_cfg80211_vif * vif)938b4c3e9b5SBjoern A. Zeeb static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
939b4c3e9b5SBjoern A. Zeeb {
940b4c3e9b5SBjoern A. Zeeb 	enum nl80211_iftype iftype;
941b4c3e9b5SBjoern A. Zeeb 
942b4c3e9b5SBjoern A. Zeeb 	iftype = vif->wdev.iftype;
943b4c3e9b5SBjoern A. Zeeb 	return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
944b4c3e9b5SBjoern A. Zeeb }
945b4c3e9b5SBjoern A. Zeeb 
brcmf_is_ibssmode(struct brcmf_cfg80211_vif * vif)946b4c3e9b5SBjoern A. Zeeb static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
947b4c3e9b5SBjoern A. Zeeb {
948b4c3e9b5SBjoern A. Zeeb 	return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
949b4c3e9b5SBjoern A. Zeeb }
950b4c3e9b5SBjoern A. Zeeb 
951b4c3e9b5SBjoern A. Zeeb /**
952b4c3e9b5SBjoern A. Zeeb  * brcmf_mon_add_vif() - create monitor mode virtual interface
953b4c3e9b5SBjoern A. Zeeb  *
954b4c3e9b5SBjoern A. Zeeb  * @wiphy: wiphy device of new interface.
955b4c3e9b5SBjoern A. Zeeb  * @name: name of the new interface.
956b4c3e9b5SBjoern A. Zeeb  *
957b4c3e9b5SBjoern A. Zeeb  * Return: pointer to new vif on success, ERR_PTR(-errno) if not
958b4c3e9b5SBjoern A. Zeeb  */
brcmf_mon_add_vif(struct wiphy * wiphy,const char * name)959b4c3e9b5SBjoern A. Zeeb static struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy,
960b4c3e9b5SBjoern A. Zeeb 					      const char *name)
961b4c3e9b5SBjoern A. Zeeb {
962b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
963b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
964b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev;
965b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
966b4c3e9b5SBjoern A. Zeeb 	int err;
967b4c3e9b5SBjoern A. Zeeb 
968b4c3e9b5SBjoern A. Zeeb 	if (cfg->pub->mon_if) {
969b4c3e9b5SBjoern A. Zeeb 		err = -EEXIST;
970b4c3e9b5SBjoern A. Zeeb 		goto err_out;
971b4c3e9b5SBjoern A. Zeeb 	}
972b4c3e9b5SBjoern A. Zeeb 
973b4c3e9b5SBjoern A. Zeeb 	vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_MONITOR);
974b4c3e9b5SBjoern A. Zeeb 	if (IS_ERR(vif)) {
975b4c3e9b5SBjoern A. Zeeb 		err = PTR_ERR(vif);
976b4c3e9b5SBjoern A. Zeeb 		goto err_out;
977b4c3e9b5SBjoern A. Zeeb 	}
978b4c3e9b5SBjoern A. Zeeb 
979b4c3e9b5SBjoern A. Zeeb 	ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup);
980b4c3e9b5SBjoern A. Zeeb 	if (!ndev) {
981b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
982b4c3e9b5SBjoern A. Zeeb 		goto err_free_vif;
983b4c3e9b5SBjoern A. Zeeb 	}
984b4c3e9b5SBjoern A. Zeeb 	ndev->type = ARPHRD_IEEE80211_RADIOTAP;
985b4c3e9b5SBjoern A. Zeeb 	ndev->ieee80211_ptr = &vif->wdev;
986b4c3e9b5SBjoern A. Zeeb 	ndev->needs_free_netdev = true;
987b4c3e9b5SBjoern A. Zeeb 	ndev->priv_destructor = brcmf_cfg80211_free_netdev;
988b4c3e9b5SBjoern A. Zeeb 	SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
989b4c3e9b5SBjoern A. Zeeb 
990b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(ndev);
991b4c3e9b5SBjoern A. Zeeb 	ifp->vif = vif;
992b4c3e9b5SBjoern A. Zeeb 	ifp->ndev = ndev;
993b4c3e9b5SBjoern A. Zeeb 	ifp->drvr = cfg->pub;
994b4c3e9b5SBjoern A. Zeeb 
995b4c3e9b5SBjoern A. Zeeb 	vif->ifp = ifp;
996b4c3e9b5SBjoern A. Zeeb 	vif->wdev.netdev = ndev;
997b4c3e9b5SBjoern A. Zeeb 
998b4c3e9b5SBjoern A. Zeeb 	err = brcmf_net_mon_attach(ifp);
999b4c3e9b5SBjoern A. Zeeb 	if (err) {
1000b4c3e9b5SBjoern A. Zeeb 		brcmf_err("Failed to attach %s device\n", ndev->name);
1001b4c3e9b5SBjoern A. Zeeb 		free_netdev(ndev);
1002b4c3e9b5SBjoern A. Zeeb 		goto err_free_vif;
1003b4c3e9b5SBjoern A. Zeeb 	}
1004b4c3e9b5SBjoern A. Zeeb 
1005b4c3e9b5SBjoern A. Zeeb 	cfg->pub->mon_if = ifp;
1006b4c3e9b5SBjoern A. Zeeb 
1007b4c3e9b5SBjoern A. Zeeb 	return &vif->wdev;
1008b4c3e9b5SBjoern A. Zeeb 
1009b4c3e9b5SBjoern A. Zeeb err_free_vif:
1010b4c3e9b5SBjoern A. Zeeb 	brcmf_free_vif(vif);
1011b4c3e9b5SBjoern A. Zeeb err_out:
1012b4c3e9b5SBjoern A. Zeeb 	return ERR_PTR(err);
1013b4c3e9b5SBjoern A. Zeeb }
1014b4c3e9b5SBjoern A. Zeeb 
brcmf_mon_del_vif(struct wiphy * wiphy,struct wireless_dev * wdev)1015b4c3e9b5SBjoern A. Zeeb static int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
1016b4c3e9b5SBjoern A. Zeeb {
1017b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1018b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = wdev->netdev;
1019b4c3e9b5SBjoern A. Zeeb 
1020b4c3e9b5SBjoern A. Zeeb 	ndev->netdev_ops->ndo_stop(ndev);
1021b4c3e9b5SBjoern A. Zeeb 
1022b4c3e9b5SBjoern A. Zeeb 	brcmf_net_detach(ndev, true);
1023b4c3e9b5SBjoern A. Zeeb 
1024b4c3e9b5SBjoern A. Zeeb 	cfg->pub->mon_if = NULL;
1025b4c3e9b5SBjoern A. Zeeb 
1026b4c3e9b5SBjoern A. Zeeb 	return 0;
1027b4c3e9b5SBjoern A. Zeeb }
1028b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_add_iface(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,struct vif_params * params)1029b4c3e9b5SBjoern A. Zeeb static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
1030b4c3e9b5SBjoern A. Zeeb 						     const char *name,
1031b4c3e9b5SBjoern A. Zeeb 						     unsigned char name_assign_type,
1032b4c3e9b5SBjoern A. Zeeb 						     enum nl80211_iftype type,
1033b4c3e9b5SBjoern A. Zeeb 						     struct vif_params *params)
1034b4c3e9b5SBjoern A. Zeeb {
1035b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1036b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1037b4c3e9b5SBjoern A. Zeeb 	struct wireless_dev *wdev;
1038b4c3e9b5SBjoern A. Zeeb 	int err;
1039b4c3e9b5SBjoern A. Zeeb 
1040b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
1041b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
1042b4c3e9b5SBjoern A. Zeeb 	if (err) {
1043b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "iface validation failed: err=%d\n", err);
1044b4c3e9b5SBjoern A. Zeeb 		return ERR_PTR(err);
1045b4c3e9b5SBjoern A. Zeeb 	}
1046b4c3e9b5SBjoern A. Zeeb 	switch (type) {
1047b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_ADHOC:
1048b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_AP_VLAN:
1049b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_WDS:
1050b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_MESH_POINT:
1051b4c3e9b5SBjoern A. Zeeb 		return ERR_PTR(-EOPNOTSUPP);
1052b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_MONITOR:
1053b4c3e9b5SBjoern A. Zeeb 		return brcmf_mon_add_vif(wiphy, name);
1054b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_STATION:
1055b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_AP:
1056b4c3e9b5SBjoern A. Zeeb 		wdev = brcmf_apsta_add_vif(wiphy, name, params, type);
1057b4c3e9b5SBjoern A. Zeeb 		break;
1058b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_CLIENT:
1059b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_GO:
1060b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_DEVICE:
1061b4c3e9b5SBjoern A. Zeeb 		wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params);
1062b4c3e9b5SBjoern A. Zeeb 		break;
1063b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_UNSPECIFIED:
1064b4c3e9b5SBjoern A. Zeeb 	default:
1065b4c3e9b5SBjoern A. Zeeb 		return ERR_PTR(-EINVAL);
1066b4c3e9b5SBjoern A. Zeeb 	}
1067b4c3e9b5SBjoern A. Zeeb 
1068b4c3e9b5SBjoern A. Zeeb 	if (IS_ERR(wdev))
1069b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "add iface %s type %d failed: err=%d\n", name,
1070b4c3e9b5SBjoern A. Zeeb 			 type, (int)PTR_ERR(wdev));
1071b4c3e9b5SBjoern A. Zeeb 	else
1072b4c3e9b5SBjoern A. Zeeb 		brcmf_cfg80211_update_proto_addr_mode(wdev);
1073b4c3e9b5SBjoern A. Zeeb 
1074b4c3e9b5SBjoern A. Zeeb 	return wdev;
1075b4c3e9b5SBjoern A. Zeeb }
1076b4c3e9b5SBjoern A. Zeeb 
brcmf_scan_config_mpc(struct brcmf_if * ifp,int mpc)1077b4c3e9b5SBjoern A. Zeeb static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
1078b4c3e9b5SBjoern A. Zeeb {
1079b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
1080b4c3e9b5SBjoern A. Zeeb 		brcmf_set_mpc(ifp, mpc);
1081b4c3e9b5SBjoern A. Zeeb }
1082b4c3e9b5SBjoern A. Zeeb 
brcmf_set_mpc(struct brcmf_if * ifp,int mpc)1083b4c3e9b5SBjoern A. Zeeb void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
1084b4c3e9b5SBjoern A. Zeeb {
1085b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
1086b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1087b4c3e9b5SBjoern A. Zeeb 
1088b4c3e9b5SBjoern A. Zeeb 	if (check_vif_up(ifp->vif)) {
1089b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
1090b4c3e9b5SBjoern A. Zeeb 		if (err) {
1091b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "fail to set mpc\n");
1092b4c3e9b5SBjoern A. Zeeb 			return;
1093b4c3e9b5SBjoern A. Zeeb 		}
1094b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "MPC : %d\n", mpc);
1095b4c3e9b5SBjoern A. Zeeb 	}
1096b4c3e9b5SBjoern A. Zeeb }
1097b4c3e9b5SBjoern A. Zeeb 
brcmf_is_apmode_operating(struct wiphy * wiphy)1098b4c3e9b5SBjoern A. Zeeb bool brcmf_is_apmode_operating(struct wiphy *wiphy)
1099b4c3e9b5SBjoern A. Zeeb {
1100b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1101b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
1102b4c3e9b5SBjoern A. Zeeb 	bool ret = false;
1103b4c3e9b5SBjoern A. Zeeb 
1104b4c3e9b5SBjoern A. Zeeb 	list_for_each_entry(vif, &cfg->vif_list, list) {
1105b4c3e9b5SBjoern A. Zeeb 		if (brcmf_is_apmode(vif) &&
1106b4c3e9b5SBjoern A. Zeeb 		    test_bit(BRCMF_VIF_STATUS_AP_CREATED, &vif->sme_state))
1107b4c3e9b5SBjoern A. Zeeb 			ret = true;
1108b4c3e9b5SBjoern A. Zeeb 	}
1109b4c3e9b5SBjoern A. Zeeb 
1110b4c3e9b5SBjoern A. Zeeb 	return ret;
1111b4c3e9b5SBjoern A. Zeeb }
1112b4c3e9b5SBjoern A. Zeeb 
brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le * params_v2_le,struct brcmf_scan_params_le * params_le)1113b4c3e9b5SBjoern A. Zeeb static void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le,
1114b4c3e9b5SBjoern A. Zeeb 				       struct brcmf_scan_params_le *params_le)
1115b4c3e9b5SBjoern A. Zeeb {
1116b4c3e9b5SBjoern A. Zeeb 	size_t params_size;
1117b4c3e9b5SBjoern A. Zeeb 	u32 ch;
1118b4c3e9b5SBjoern A. Zeeb 	int n_channels, n_ssids;
1119b4c3e9b5SBjoern A. Zeeb 
1120b4c3e9b5SBjoern A. Zeeb 	memcpy(&params_le->ssid_le, &params_v2_le->ssid_le,
1121b4c3e9b5SBjoern A. Zeeb 	       sizeof(params_le->ssid_le));
1122b4c3e9b5SBjoern A. Zeeb 	memcpy(&params_le->bssid, &params_v2_le->bssid,
1123b4c3e9b5SBjoern A. Zeeb 	       sizeof(params_le->bssid));
1124b4c3e9b5SBjoern A. Zeeb 
1125b4c3e9b5SBjoern A. Zeeb 	params_le->bss_type = params_v2_le->bss_type;
1126b4c3e9b5SBjoern A. Zeeb 	params_le->scan_type = le32_to_cpu(params_v2_le->scan_type);
1127b4c3e9b5SBjoern A. Zeeb 	params_le->nprobes = params_v2_le->nprobes;
1128b4c3e9b5SBjoern A. Zeeb 	params_le->active_time = params_v2_le->active_time;
1129b4c3e9b5SBjoern A. Zeeb 	params_le->passive_time = params_v2_le->passive_time;
1130b4c3e9b5SBjoern A. Zeeb 	params_le->home_time = params_v2_le->home_time;
1131b4c3e9b5SBjoern A. Zeeb 	params_le->channel_num = params_v2_le->channel_num;
1132b4c3e9b5SBjoern A. Zeeb 
1133b4c3e9b5SBjoern A. Zeeb 	ch = le32_to_cpu(params_v2_le->channel_num);
1134b4c3e9b5SBjoern A. Zeeb 	n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK;
1135b4c3e9b5SBjoern A. Zeeb 	n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT;
1136b4c3e9b5SBjoern A. Zeeb 
1137b4c3e9b5SBjoern A. Zeeb 	params_size = sizeof(u16) * n_channels;
1138b4c3e9b5SBjoern A. Zeeb 	if (n_ssids > 0) {
1139b4c3e9b5SBjoern A. Zeeb 		params_size = roundup(params_size, sizeof(u32));
1140b4c3e9b5SBjoern A. Zeeb 		params_size += sizeof(struct brcmf_ssid_le) * n_ssids;
1141b4c3e9b5SBjoern A. Zeeb 	}
1142b4c3e9b5SBjoern A. Zeeb 
1143b4c3e9b5SBjoern A. Zeeb 	memcpy(&params_le->channel_list[0],
1144b4c3e9b5SBjoern A. Zeeb 	       &params_v2_le->channel_list[0], params_size);
1145b4c3e9b5SBjoern A. Zeeb }
1146b4c3e9b5SBjoern A. Zeeb 
brcmf_escan_prep(struct brcmf_cfg80211_info * cfg,struct brcmf_scan_params_v2_le * params_le,struct cfg80211_scan_request * request)1147b4c3e9b5SBjoern A. Zeeb static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
1148b4c3e9b5SBjoern A. Zeeb 			     struct brcmf_scan_params_v2_le *params_le,
1149b4c3e9b5SBjoern A. Zeeb 			     struct cfg80211_scan_request *request)
1150b4c3e9b5SBjoern A. Zeeb {
1151b4c3e9b5SBjoern A. Zeeb 	u32 n_ssids;
1152b4c3e9b5SBjoern A. Zeeb 	u32 n_channels;
1153b4c3e9b5SBjoern A. Zeeb 	s32 i;
1154b4c3e9b5SBjoern A. Zeeb 	s32 offset;
1155b4c3e9b5SBjoern A. Zeeb 	u16 chanspec;
1156b4c3e9b5SBjoern A. Zeeb 	char *ptr;
1157b4c3e9b5SBjoern A. Zeeb 	int length;
1158b4c3e9b5SBjoern A. Zeeb 	struct brcmf_ssid_le ssid_le;
1159b4c3e9b5SBjoern A. Zeeb 
1160b4c3e9b5SBjoern A. Zeeb 	eth_broadcast_addr(params_le->bssid);
1161b4c3e9b5SBjoern A. Zeeb 
1162b4c3e9b5SBjoern A. Zeeb 	length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
1163b4c3e9b5SBjoern A. Zeeb 
1164b4c3e9b5SBjoern A. Zeeb 	params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);
1165b4c3e9b5SBjoern A. Zeeb 	params_le->bss_type = DOT11_BSSTYPE_ANY;
1166b4c3e9b5SBjoern A. Zeeb 	params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE);
1167b4c3e9b5SBjoern A. Zeeb 	params_le->channel_num = 0;
1168b4c3e9b5SBjoern A. Zeeb 	params_le->nprobes = cpu_to_le32(-1);
1169b4c3e9b5SBjoern A. Zeeb 	params_le->active_time = cpu_to_le32(-1);
1170b4c3e9b5SBjoern A. Zeeb 	params_le->passive_time = cpu_to_le32(-1);
1171b4c3e9b5SBjoern A. Zeeb 	params_le->home_time = cpu_to_le32(-1);
1172b4c3e9b5SBjoern A. Zeeb 	memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
1173b4c3e9b5SBjoern A. Zeeb 
1174b4c3e9b5SBjoern A. Zeeb 	/* Scan abort */
1175b4c3e9b5SBjoern A. Zeeb 	if (!request) {
1176b4c3e9b5SBjoern A. Zeeb 		length += sizeof(u16);
1177b4c3e9b5SBjoern A. Zeeb 		params_le->channel_num = cpu_to_le32(1);
1178b4c3e9b5SBjoern A. Zeeb 		params_le->channel_list[0] = cpu_to_le16(-1);
1179b4c3e9b5SBjoern A. Zeeb 		params_le->length = cpu_to_le16(length);
1180b4c3e9b5SBjoern A. Zeeb 		return;
1181b4c3e9b5SBjoern A. Zeeb 	}
1182b4c3e9b5SBjoern A. Zeeb 
1183b4c3e9b5SBjoern A. Zeeb 	n_ssids = request->n_ssids;
1184b4c3e9b5SBjoern A. Zeeb 	n_channels = request->n_channels;
1185b4c3e9b5SBjoern A. Zeeb 
1186b4c3e9b5SBjoern A. Zeeb 	/* Copy channel array if applicable */
1187b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
1188b4c3e9b5SBjoern A. Zeeb 		  n_channels);
1189b4c3e9b5SBjoern A. Zeeb 	if (n_channels > 0) {
1190b4c3e9b5SBjoern A. Zeeb 		length += roundup(sizeof(u16) * n_channels, sizeof(u32));
1191b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < n_channels; i++) {
1192b4c3e9b5SBjoern A. Zeeb 			chanspec = channel_to_chanspec(&cfg->d11inf,
1193b4c3e9b5SBjoern A. Zeeb 						       request->channels[i]);
1194b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
1195b4c3e9b5SBjoern A. Zeeb 				  request->channels[i]->hw_value, chanspec);
1196b4c3e9b5SBjoern A. Zeeb 			params_le->channel_list[i] = cpu_to_le16(chanspec);
1197b4c3e9b5SBjoern A. Zeeb 		}
1198b4c3e9b5SBjoern A. Zeeb 	} else {
1199b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "Scanning all channels\n");
1200b4c3e9b5SBjoern A. Zeeb 	}
1201b4c3e9b5SBjoern A. Zeeb 
1202b4c3e9b5SBjoern A. Zeeb 	/* Copy ssid array if applicable */
1203b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
1204b4c3e9b5SBjoern A. Zeeb 	if (n_ssids > 0) {
1205b4c3e9b5SBjoern A. Zeeb 		offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) +
1206b4c3e9b5SBjoern A. Zeeb 				n_channels * sizeof(u16);
1207b4c3e9b5SBjoern A. Zeeb 		offset = roundup(offset, sizeof(u32));
1208b4c3e9b5SBjoern A. Zeeb 		length += sizeof(ssid_le) * n_ssids;
1209b4c3e9b5SBjoern A. Zeeb 		ptr = (char *)params_le + offset;
1210b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < n_ssids; i++) {
1211b4c3e9b5SBjoern A. Zeeb 			memset(&ssid_le, 0, sizeof(ssid_le));
1212b4c3e9b5SBjoern A. Zeeb 			ssid_le.SSID_len =
1213b4c3e9b5SBjoern A. Zeeb 					cpu_to_le32(request->ssids[i].ssid_len);
1214b4c3e9b5SBjoern A. Zeeb 			memcpy(ssid_le.SSID, request->ssids[i].ssid,
1215b4c3e9b5SBjoern A. Zeeb 			       request->ssids[i].ssid_len);
1216b4c3e9b5SBjoern A. Zeeb 			if (!ssid_le.SSID_len)
1217b4c3e9b5SBjoern A. Zeeb 				brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
1218b4c3e9b5SBjoern A. Zeeb 			else
1219b4c3e9b5SBjoern A. Zeeb 				brcmf_dbg(SCAN, "%d: scan for  %.32s size=%d\n",
1220b4c3e9b5SBjoern A. Zeeb 					  i, ssid_le.SSID, ssid_le.SSID_len);
1221b4c3e9b5SBjoern A. Zeeb 			memcpy(ptr, &ssid_le, sizeof(ssid_le));
1222b4c3e9b5SBjoern A. Zeeb 			ptr += sizeof(ssid_le);
1223b4c3e9b5SBjoern A. Zeeb 		}
1224b4c3e9b5SBjoern A. Zeeb 	} else {
1225b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "Performing passive scan\n");
1226b4c3e9b5SBjoern A. Zeeb 		params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE);
1227b4c3e9b5SBjoern A. Zeeb 	}
1228b4c3e9b5SBjoern A. Zeeb 	params_le->length = cpu_to_le16(length);
1229b4c3e9b5SBjoern A. Zeeb 	/* Adding mask to channel numbers */
1230b4c3e9b5SBjoern A. Zeeb 	params_le->channel_num =
1231b4c3e9b5SBjoern A. Zeeb 		cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
1232b4c3e9b5SBjoern A. Zeeb 			(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
1233b4c3e9b5SBjoern A. Zeeb }
1234b4c3e9b5SBjoern A. Zeeb 
brcmf_notify_escan_complete(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,bool aborted,bool fw_abort)1235b4c3e9b5SBjoern A. Zeeb s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
1236b4c3e9b5SBjoern A. Zeeb 				struct brcmf_if *ifp, bool aborted,
1237b4c3e9b5SBjoern A. Zeeb 				bool fw_abort)
1238b4c3e9b5SBjoern A. Zeeb {
1239b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1240b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scan_params_v2_le params_v2_le;
1241b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_scan_request *scan_request;
1242b4c3e9b5SBjoern A. Zeeb 	u64 reqid;
1243b4c3e9b5SBjoern A. Zeeb 	u32 bucket;
1244b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1245b4c3e9b5SBjoern A. Zeeb 
1246b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "Enter\n");
1247b4c3e9b5SBjoern A. Zeeb 
1248b4c3e9b5SBjoern A. Zeeb 	/* clear scan request, because the FW abort can cause a second call */
1249b4c3e9b5SBjoern A. Zeeb 	/* to this functon and might cause a double cfg80211_scan_done      */
1250b4c3e9b5SBjoern A. Zeeb 	scan_request = cfg->scan_request;
1251b4c3e9b5SBjoern A. Zeeb 	cfg->scan_request = NULL;
1252b4c3e9b5SBjoern A. Zeeb 
1253b4c3e9b5SBjoern A. Zeeb 	timer_delete_sync(&cfg->escan_timeout);
1254b4c3e9b5SBjoern A. Zeeb 
1255b4c3e9b5SBjoern A. Zeeb 	if (fw_abort) {
1256b4c3e9b5SBjoern A. Zeeb 		/* Do a scan abort to stop the driver's scan engine */
1257b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "ABORT scan in firmware\n");
1258b4c3e9b5SBjoern A. Zeeb 
1259b4c3e9b5SBjoern A. Zeeb 		brcmf_escan_prep(cfg, &params_v2_le, NULL);
1260b4c3e9b5SBjoern A. Zeeb 
1261b4c3e9b5SBjoern A. Zeeb 		/* E-Scan (or anyother type) can be aborted by SCAN */
1262b4c3e9b5SBjoern A. Zeeb 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
1263b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
1264b4c3e9b5SBjoern A. Zeeb 						     &params_v2_le,
1265b4c3e9b5SBjoern A. Zeeb 						     sizeof(params_v2_le));
1266b4c3e9b5SBjoern A. Zeeb 		} else {
1267b4c3e9b5SBjoern A. Zeeb 			struct brcmf_scan_params_le params_le;
1268b4c3e9b5SBjoern A. Zeeb 
1269b4c3e9b5SBjoern A. Zeeb 			brcmf_scan_params_v2_to_v1(&params_v2_le, &params_le);
1270b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
1271b4c3e9b5SBjoern A. Zeeb 						     &params_le,
1272b4c3e9b5SBjoern A. Zeeb 						     sizeof(params_le));
1273b4c3e9b5SBjoern A. Zeeb 		}
1274b4c3e9b5SBjoern A. Zeeb 
1275b4c3e9b5SBjoern A. Zeeb 		if (err)
1276b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Scan abort failed\n");
1277b4c3e9b5SBjoern A. Zeeb 	}
1278b4c3e9b5SBjoern A. Zeeb 
1279b4c3e9b5SBjoern A. Zeeb 	brcmf_scan_config_mpc(ifp, 1);
1280b4c3e9b5SBjoern A. Zeeb 
1281b4c3e9b5SBjoern A. Zeeb 	/*
1282b4c3e9b5SBjoern A. Zeeb 	 * e-scan can be initiated internally
1283b4c3e9b5SBjoern A. Zeeb 	 * which takes precedence.
1284b4c3e9b5SBjoern A. Zeeb 	 */
1285b4c3e9b5SBjoern A. Zeeb 	if (cfg->int_escan_map) {
1286b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "scheduled scan completed (%x)\n",
1287b4c3e9b5SBjoern A. Zeeb 			  cfg->int_escan_map);
1288b4c3e9b5SBjoern A. Zeeb 		while (cfg->int_escan_map) {
1289b4c3e9b5SBjoern A. Zeeb 			bucket = __ffs(cfg->int_escan_map);
1290b4c3e9b5SBjoern A. Zeeb 			cfg->int_escan_map &= ~BIT(bucket);
1291b4c3e9b5SBjoern A. Zeeb 			reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno,
1292b4c3e9b5SBjoern A. Zeeb 							       bucket);
1293b4c3e9b5SBjoern A. Zeeb 			if (!aborted) {
1294902136e0SBjoern A. Zeeb #if defined(__linux__)
1295b4c3e9b5SBjoern A. Zeeb 				brcmf_dbg(SCAN, "report results: reqid=%llu\n",
1296b4c3e9b5SBjoern A. Zeeb 					  reqid);
1297902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
1298902136e0SBjoern A. Zeeb 				brcmf_dbg(SCAN, "report results: reqid=%ju\n",
1299902136e0SBjoern A. Zeeb 					  (uintmax_t)reqid);
1300902136e0SBjoern A. Zeeb #endif
1301b4c3e9b5SBjoern A. Zeeb 				cfg80211_sched_scan_results(cfg_to_wiphy(cfg),
1302b4c3e9b5SBjoern A. Zeeb 							    reqid);
1303b4c3e9b5SBjoern A. Zeeb 			}
1304b4c3e9b5SBjoern A. Zeeb 		}
1305b4c3e9b5SBjoern A. Zeeb 	} else if (scan_request) {
1306b4c3e9b5SBjoern A. Zeeb 		struct cfg80211_scan_info info = {
1307b4c3e9b5SBjoern A. Zeeb 			.aborted = aborted,
1308b4c3e9b5SBjoern A. Zeeb 		};
1309b4c3e9b5SBjoern A. Zeeb 
1310b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
1311b4c3e9b5SBjoern A. Zeeb 			  aborted ? "Aborted" : "Done");
1312b4c3e9b5SBjoern A. Zeeb 		cfg80211_scan_done(scan_request, &info);
1313b4c3e9b5SBjoern A. Zeeb 	}
1314b4c3e9b5SBjoern A. Zeeb 	if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
1315b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
1316b4c3e9b5SBjoern A. Zeeb 
1317b4c3e9b5SBjoern A. Zeeb 	return err;
1318b4c3e9b5SBjoern A. Zeeb }
1319b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_del_apsta_iface(struct wiphy * wiphy,struct wireless_dev * wdev)1320b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy,
1321b4c3e9b5SBjoern A. Zeeb 					  struct wireless_dev *wdev)
1322b4c3e9b5SBjoern A. Zeeb {
1323b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1324b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = wdev->netdev;
1325b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1326b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1327b4c3e9b5SBjoern A. Zeeb 	int ret;
1328b4c3e9b5SBjoern A. Zeeb 	int err;
1329b4c3e9b5SBjoern A. Zeeb 
1330b4c3e9b5SBjoern A. Zeeb 	brcmf_cfg80211_arm_vif_event(cfg, ifp->vif);
1331b4c3e9b5SBjoern A. Zeeb 
1332b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0);
1333b4c3e9b5SBjoern A. Zeeb 	if (err) {
1334b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "interface_remove failed %d\n", err);
1335b4c3e9b5SBjoern A. Zeeb 		goto err_unarm;
1336b4c3e9b5SBjoern A. Zeeb 	}
1337b4c3e9b5SBjoern A. Zeeb 
1338b4c3e9b5SBjoern A. Zeeb 	/* wait for firmware event */
1339b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
1340b4c3e9b5SBjoern A. Zeeb 					    BRCMF_VIF_EVENT_TIMEOUT);
1341b4c3e9b5SBjoern A. Zeeb 	if (!ret) {
1342b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "timeout occurred\n");
1343b4c3e9b5SBjoern A. Zeeb 		err = -EIO;
1344b4c3e9b5SBjoern A. Zeeb 		goto err_unarm;
1345b4c3e9b5SBjoern A. Zeeb 	}
1346b4c3e9b5SBjoern A. Zeeb 
1347b4c3e9b5SBjoern A. Zeeb 	brcmf_remove_interface(ifp, true);
1348b4c3e9b5SBjoern A. Zeeb 
1349b4c3e9b5SBjoern A. Zeeb err_unarm:
1350b4c3e9b5SBjoern A. Zeeb 	brcmf_cfg80211_arm_vif_event(cfg, NULL);
1351b4c3e9b5SBjoern A. Zeeb 	return err;
1352b4c3e9b5SBjoern A. Zeeb }
1353b4c3e9b5SBjoern A. Zeeb 
1354b4c3e9b5SBjoern A. Zeeb static
brcmf_cfg80211_del_iface(struct wiphy * wiphy,struct wireless_dev * wdev)1355b4c3e9b5SBjoern A. Zeeb int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
1356b4c3e9b5SBjoern A. Zeeb {
1357b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1358b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = wdev->netdev;
1359b4c3e9b5SBjoern A. Zeeb 
1360b4c3e9b5SBjoern A. Zeeb 	if (ndev && ndev == cfg_to_ndev(cfg))
1361b4c3e9b5SBjoern A. Zeeb 		return -ENOTSUPP;
1362b4c3e9b5SBjoern A. Zeeb 
1363b4c3e9b5SBjoern A. Zeeb 	/* vif event pending in firmware */
1364b4c3e9b5SBjoern A. Zeeb 	if (brcmf_cfg80211_vif_event_armed(cfg))
1365b4c3e9b5SBjoern A. Zeeb 		return -EBUSY;
1366b4c3e9b5SBjoern A. Zeeb 
1367b4c3e9b5SBjoern A. Zeeb 	if (ndev) {
1368b4c3e9b5SBjoern A. Zeeb 		if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
1369b4c3e9b5SBjoern A. Zeeb 		    cfg->escan_info.ifp == netdev_priv(ndev))
1370b4c3e9b5SBjoern A. Zeeb 			brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
1371b4c3e9b5SBjoern A. Zeeb 						    true, true);
1372b4c3e9b5SBjoern A. Zeeb 
1373b4c3e9b5SBjoern A. Zeeb 		brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
1374b4c3e9b5SBjoern A. Zeeb 	}
1375b4c3e9b5SBjoern A. Zeeb 
1376b4c3e9b5SBjoern A. Zeeb 	switch (wdev->iftype) {
1377b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_ADHOC:
1378b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_AP_VLAN:
1379b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_WDS:
1380b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_MESH_POINT:
1381b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
1382b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_MONITOR:
1383b4c3e9b5SBjoern A. Zeeb 		return brcmf_mon_del_vif(wiphy, wdev);
1384b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_STATION:
1385b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_AP:
1386b4c3e9b5SBjoern A. Zeeb 		return brcmf_cfg80211_del_apsta_iface(wiphy, wdev);
1387b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_CLIENT:
1388b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_GO:
1389b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_DEVICE:
1390b4c3e9b5SBjoern A. Zeeb 		return brcmf_p2p_del_vif(wiphy, wdev);
1391b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_UNSPECIFIED:
1392b4c3e9b5SBjoern A. Zeeb 	default:
1393b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
1394b4c3e9b5SBjoern A. Zeeb 	}
1395b4c3e9b5SBjoern A. Zeeb 	return -EOPNOTSUPP;
1396b4c3e9b5SBjoern A. Zeeb }
1397b4c3e9b5SBjoern A. Zeeb 
1398b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_change_iface(struct wiphy * wiphy,struct net_device * ndev,enum nl80211_iftype type,struct vif_params * params)1399b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
1400b4c3e9b5SBjoern A. Zeeb 			 enum nl80211_iftype type,
1401b4c3e9b5SBjoern A. Zeeb 			 struct vif_params *params)
1402b4c3e9b5SBjoern A. Zeeb {
1403b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1404b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1405b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif = ifp->vif;
1406b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1407b4c3e9b5SBjoern A. Zeeb 	s32 infra = 0;
1408b4c3e9b5SBjoern A. Zeeb 	s32 ap = 0;
1409b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1410b4c3e9b5SBjoern A. Zeeb 
1411b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
1412b4c3e9b5SBjoern A. Zeeb 		  type);
1413b4c3e9b5SBjoern A. Zeeb 
1414b4c3e9b5SBjoern A. Zeeb 	/* WAR: There are a number of p2p interface related problems which
1415b4c3e9b5SBjoern A. Zeeb 	 * need to be handled initially (before doing the validate).
1416b4c3e9b5SBjoern A. Zeeb 	 * wpa_supplicant tends to do iface changes on p2p device/client/go
1417b4c3e9b5SBjoern A. Zeeb 	 * which are not always possible/allowed. However we need to return
1418b4c3e9b5SBjoern A. Zeeb 	 * OK otherwise the wpa_supplicant wont start. The situation differs
1419b4c3e9b5SBjoern A. Zeeb 	 * on configuration and setup (p2pon=1 module param). The first check
1420b4c3e9b5SBjoern A. Zeeb 	 * is to see if the request is a change to station for p2p iface.
1421b4c3e9b5SBjoern A. Zeeb 	 */
1422b4c3e9b5SBjoern A. Zeeb 	if ((type == NL80211_IFTYPE_STATION) &&
1423b4c3e9b5SBjoern A. Zeeb 	    ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
1424b4c3e9b5SBjoern A. Zeeb 	     (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
1425b4c3e9b5SBjoern A. Zeeb 	     (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
1426b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
1427b4c3e9b5SBjoern A. Zeeb 		/* Now depending on whether module param p2pon=1 was used the
1428b4c3e9b5SBjoern A. Zeeb 		 * response needs to be either 0 or EOPNOTSUPP. The reason is
1429b4c3e9b5SBjoern A. Zeeb 		 * that if p2pon=1 is used, but a newer supplicant is used then
1430b4c3e9b5SBjoern A. Zeeb 		 * we should return an error, as this combination wont work.
1431b4c3e9b5SBjoern A. Zeeb 		 * In other situations 0 is returned and supplicant will start
1432b4c3e9b5SBjoern A. Zeeb 		 * normally. It will give a trace in cfg80211, but it is the
1433b4c3e9b5SBjoern A. Zeeb 		 * only way to get it working. Unfortunately this will result
1434b4c3e9b5SBjoern A. Zeeb 		 * in situation where we wont support new supplicant in
1435b4c3e9b5SBjoern A. Zeeb 		 * combination with module param p2pon=1, but that is the way
1436b4c3e9b5SBjoern A. Zeeb 		 * it is. If the user tries this then unloading of driver might
1437b4c3e9b5SBjoern A. Zeeb 		 * fail/lock.
1438b4c3e9b5SBjoern A. Zeeb 		 */
1439b4c3e9b5SBjoern A. Zeeb 		if (cfg->p2p.p2pdev_dynamically)
1440b4c3e9b5SBjoern A. Zeeb 			return -EOPNOTSUPP;
1441b4c3e9b5SBjoern A. Zeeb 		else
1442b4c3e9b5SBjoern A. Zeeb 			return 0;
1443b4c3e9b5SBjoern A. Zeeb 	}
1444b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
1445b4c3e9b5SBjoern A. Zeeb 	if (err) {
1446b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "iface validation failed: err=%d\n", err);
1447b4c3e9b5SBjoern A. Zeeb 		return err;
1448b4c3e9b5SBjoern A. Zeeb 	}
1449b4c3e9b5SBjoern A. Zeeb 	switch (type) {
1450b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_MONITOR:
1451b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_WDS:
1452b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "type (%d) : currently we do not support this type\n",
1453b4c3e9b5SBjoern A. Zeeb 			 type);
1454b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
1455b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_ADHOC:
1456b4c3e9b5SBjoern A. Zeeb 		infra = 0;
1457b4c3e9b5SBjoern A. Zeeb 		break;
1458b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_STATION:
1459b4c3e9b5SBjoern A. Zeeb 		infra = 1;
1460b4c3e9b5SBjoern A. Zeeb 		break;
1461b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_AP:
1462b4c3e9b5SBjoern A. Zeeb 	case NL80211_IFTYPE_P2P_GO:
1463b4c3e9b5SBjoern A. Zeeb 		ap = 1;
1464b4c3e9b5SBjoern A. Zeeb 		break;
1465b4c3e9b5SBjoern A. Zeeb 	default:
1466b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
1467b4c3e9b5SBjoern A. Zeeb 		goto done;
1468b4c3e9b5SBjoern A. Zeeb 	}
1469b4c3e9b5SBjoern A. Zeeb 
1470b4c3e9b5SBjoern A. Zeeb 	if (ap) {
1471b4c3e9b5SBjoern A. Zeeb 		if (type == NL80211_IFTYPE_P2P_GO) {
1472b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "IF Type = P2P GO\n");
1473b4c3e9b5SBjoern A. Zeeb 			err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
1474b4c3e9b5SBjoern A. Zeeb 		}
1475b4c3e9b5SBjoern A. Zeeb 		if (!err) {
1476b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "IF Type = AP\n");
1477b4c3e9b5SBjoern A. Zeeb 		}
1478b4c3e9b5SBjoern A. Zeeb 	} else {
1479b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
1480b4c3e9b5SBjoern A. Zeeb 		if (err) {
1481b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "WLC_SET_INFRA error (%d)\n", err);
1482b4c3e9b5SBjoern A. Zeeb 			err = -EAGAIN;
1483b4c3e9b5SBjoern A. Zeeb 			goto done;
1484b4c3e9b5SBjoern A. Zeeb 		}
1485b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
1486b4c3e9b5SBjoern A. Zeeb 			  "Adhoc" : "Infra");
1487b4c3e9b5SBjoern A. Zeeb 	}
1488b4c3e9b5SBjoern A. Zeeb 	ndev->ieee80211_ptr->iftype = type;
1489b4c3e9b5SBjoern A. Zeeb 
1490b4c3e9b5SBjoern A. Zeeb 	brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
1491b4c3e9b5SBjoern A. Zeeb 
1492b4c3e9b5SBjoern A. Zeeb done:
1493b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
1494b4c3e9b5SBjoern A. Zeeb 
1495b4c3e9b5SBjoern A. Zeeb 	return err;
1496b4c3e9b5SBjoern A. Zeeb }
1497b4c3e9b5SBjoern A. Zeeb 
1498b4c3e9b5SBjoern A. Zeeb static s32
brcmf_run_escan(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,struct cfg80211_scan_request * request)1499b4c3e9b5SBjoern A. Zeeb brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
1500b4c3e9b5SBjoern A. Zeeb 		struct cfg80211_scan_request *request)
1501b4c3e9b5SBjoern A. Zeeb {
1502b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1503b4c3e9b5SBjoern A. Zeeb 	s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE +
1504b4c3e9b5SBjoern A. Zeeb 			  offsetof(struct brcmf_escan_params_le, params_v2_le);
1505b4c3e9b5SBjoern A. Zeeb 	struct brcmf_escan_params_le *params;
1506b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1507b4c3e9b5SBjoern A. Zeeb 
1508b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "E-SCAN START\n");
1509b4c3e9b5SBjoern A. Zeeb 
1510b4c3e9b5SBjoern A. Zeeb 	if (request != NULL) {
1511b4c3e9b5SBjoern A. Zeeb 		/* Allocate space for populating ssids in struct */
1512b4c3e9b5SBjoern A. Zeeb 		params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
1513b4c3e9b5SBjoern A. Zeeb 
1514b4c3e9b5SBjoern A. Zeeb 		/* Allocate space for populating ssids in struct */
1515b4c3e9b5SBjoern A. Zeeb 		params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
1516b4c3e9b5SBjoern A. Zeeb 	}
1517b4c3e9b5SBjoern A. Zeeb 
1518b4c3e9b5SBjoern A. Zeeb 	params = kzalloc(params_size, GFP_KERNEL);
1519b4c3e9b5SBjoern A. Zeeb 	if (!params) {
1520b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
1521b4c3e9b5SBjoern A. Zeeb 		goto exit;
1522b4c3e9b5SBjoern A. Zeeb 	}
1523b4c3e9b5SBjoern A. Zeeb 	BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
1524b4c3e9b5SBjoern A. Zeeb 	brcmf_escan_prep(cfg, &params->params_v2_le, request);
1525b4c3e9b5SBjoern A. Zeeb 
1526b4c3e9b5SBjoern A. Zeeb 	params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2);
1527b4c3e9b5SBjoern A. Zeeb 
1528b4c3e9b5SBjoern A. Zeeb 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
1529b4c3e9b5SBjoern A. Zeeb 		struct brcmf_escan_params_le *params_v1;
1530b4c3e9b5SBjoern A. Zeeb 
1531b4c3e9b5SBjoern A. Zeeb 		params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
1532b4c3e9b5SBjoern A. Zeeb 		params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE;
1533b4c3e9b5SBjoern A. Zeeb 		params_v1 = kzalloc(params_size, GFP_KERNEL);
1534b4c3e9b5SBjoern A. Zeeb 		if (!params_v1) {
1535b4c3e9b5SBjoern A. Zeeb 			err = -ENOMEM;
1536b4c3e9b5SBjoern A. Zeeb 			goto exit_params;
1537b4c3e9b5SBjoern A. Zeeb 		}
1538b4c3e9b5SBjoern A. Zeeb 		params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
1539b4c3e9b5SBjoern A. Zeeb 		brcmf_scan_params_v2_to_v1(&params->params_v2_le, &params_v1->params_le);
1540b4c3e9b5SBjoern A. Zeeb 		kfree(params);
1541b4c3e9b5SBjoern A. Zeeb 		params = params_v1;
1542b4c3e9b5SBjoern A. Zeeb 	}
1543b4c3e9b5SBjoern A. Zeeb 
1544b4c3e9b5SBjoern A. Zeeb 	params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
1545b4c3e9b5SBjoern A. Zeeb 	params->sync_id = cpu_to_le16(0x1234);
1546b4c3e9b5SBjoern A. Zeeb 
1547b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
1548b4c3e9b5SBjoern A. Zeeb 	if (err) {
1549b4c3e9b5SBjoern A. Zeeb 		if (err == -EBUSY)
1550b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "system busy : escan canceled\n");
1551b4c3e9b5SBjoern A. Zeeb 		else
1552b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "error (%d)\n", err);
1553b4c3e9b5SBjoern A. Zeeb 	}
1554b4c3e9b5SBjoern A. Zeeb 
1555b4c3e9b5SBjoern A. Zeeb exit_params:
1556b4c3e9b5SBjoern A. Zeeb 	kfree(params);
1557b4c3e9b5SBjoern A. Zeeb exit:
1558b4c3e9b5SBjoern A. Zeeb 	return err;
1559b4c3e9b5SBjoern A. Zeeb }
1560b4c3e9b5SBjoern A. Zeeb 
1561b4c3e9b5SBjoern A. Zeeb static s32
brcmf_do_escan(struct brcmf_if * ifp,struct cfg80211_scan_request * request)1562b4c3e9b5SBjoern A. Zeeb brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request)
1563b4c3e9b5SBjoern A. Zeeb {
1564b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
1565b4c3e9b5SBjoern A. Zeeb 	s32 err;
1566b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scan_results *results;
1567b4c3e9b5SBjoern A. Zeeb 	struct escan_info *escan = &cfg->escan_info;
1568b4c3e9b5SBjoern A. Zeeb 
1569b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "Enter\n");
1570b4c3e9b5SBjoern A. Zeeb 	escan->ifp = ifp;
1571b4c3e9b5SBjoern A. Zeeb 	escan->wiphy = cfg->wiphy;
1572b4c3e9b5SBjoern A. Zeeb 	escan->escan_state = WL_ESCAN_STATE_SCANNING;
1573b4c3e9b5SBjoern A. Zeeb 
1574b4c3e9b5SBjoern A. Zeeb 	brcmf_scan_config_mpc(ifp, 0);
1575b4c3e9b5SBjoern A. Zeeb 	results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
1576b4c3e9b5SBjoern A. Zeeb 	results->version = 0;
1577b4c3e9b5SBjoern A. Zeeb 	results->count = 0;
1578b4c3e9b5SBjoern A. Zeeb 	results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1579b4c3e9b5SBjoern A. Zeeb 
1580b4c3e9b5SBjoern A. Zeeb 	err = escan->run(cfg, ifp, request);
1581b4c3e9b5SBjoern A. Zeeb 	if (err)
1582b4c3e9b5SBjoern A. Zeeb 		brcmf_scan_config_mpc(ifp, 1);
1583b4c3e9b5SBjoern A. Zeeb 	return err;
1584b4c3e9b5SBjoern A. Zeeb }
1585b4c3e9b5SBjoern A. Zeeb 
1586b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_scan(struct wiphy * wiphy,struct cfg80211_scan_request * request)1587b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
1588b4c3e9b5SBjoern A. Zeeb {
1589b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1590b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1591b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
1592b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1593b4c3e9b5SBjoern A. Zeeb 
1594b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
1595b4c3e9b5SBjoern A. Zeeb 	vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1596b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(vif))
1597b4c3e9b5SBjoern A. Zeeb 		return -EIO;
1598b4c3e9b5SBjoern A. Zeeb 
1599b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
1600b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Scanning already: status (%lu)\n",
1601b4c3e9b5SBjoern A. Zeeb 			 cfg->scan_status);
1602b4c3e9b5SBjoern A. Zeeb 		return -EAGAIN;
1603b4c3e9b5SBjoern A. Zeeb 	}
1604b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
1605b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Scanning being aborted: status (%lu)\n",
1606b4c3e9b5SBjoern A. Zeeb 			 cfg->scan_status);
1607b4c3e9b5SBjoern A. Zeeb 		return -EAGAIN;
1608b4c3e9b5SBjoern A. Zeeb 	}
1609b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1610b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Scanning suppressed: status (%lu)\n",
1611b4c3e9b5SBjoern A. Zeeb 			 cfg->scan_status);
1612b4c3e9b5SBjoern A. Zeeb 		return -EAGAIN;
1613b4c3e9b5SBjoern A. Zeeb 	}
1614b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) {
1615b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Connecting: status (%lu)\n", vif->sme_state);
1616b4c3e9b5SBjoern A. Zeeb 		return -EAGAIN;
1617b4c3e9b5SBjoern A. Zeeb 	}
1618b4c3e9b5SBjoern A. Zeeb 
1619b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "START ESCAN\n");
1620b4c3e9b5SBjoern A. Zeeb 
1621b4c3e9b5SBjoern A. Zeeb 	cfg->scan_request = request;
1622b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
1623b4c3e9b5SBjoern A. Zeeb 
1624b4c3e9b5SBjoern A. Zeeb 	cfg->escan_info.run = brcmf_run_escan;
1625b4c3e9b5SBjoern A. Zeeb 	err = brcmf_p2p_scan_prep(wiphy, request, vif);
1626b4c3e9b5SBjoern A. Zeeb 	if (err)
1627b4c3e9b5SBjoern A. Zeeb 		goto scan_out;
1628b4c3e9b5SBjoern A. Zeeb 
1629b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
1630b4c3e9b5SBjoern A. Zeeb 				    request->ie, request->ie_len);
1631b4c3e9b5SBjoern A. Zeeb 	if (err)
1632b4c3e9b5SBjoern A. Zeeb 		goto scan_out;
1633b4c3e9b5SBjoern A. Zeeb 
1634b4c3e9b5SBjoern A. Zeeb 	/* If scan req comes for p2p0, send it over primary I/F */
1635b4c3e9b5SBjoern A. Zeeb 	if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1636b4c3e9b5SBjoern A. Zeeb 		vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
1637b4c3e9b5SBjoern A. Zeeb 
1638b4c3e9b5SBjoern A. Zeeb 	err = brcmf_do_escan(vif->ifp, request);
1639b4c3e9b5SBjoern A. Zeeb 	if (err)
1640b4c3e9b5SBjoern A. Zeeb 		goto scan_out;
1641b4c3e9b5SBjoern A. Zeeb 
1642b4c3e9b5SBjoern A. Zeeb 	/* Arm scan timeout timer */
1643b4c3e9b5SBjoern A. Zeeb 	mod_timer(&cfg->escan_timeout,
1644b4c3e9b5SBjoern A. Zeeb 		  jiffies + msecs_to_jiffies(BRCMF_ESCAN_TIMER_INTERVAL_MS));
1645b4c3e9b5SBjoern A. Zeeb 
1646b4c3e9b5SBjoern A. Zeeb 	return 0;
1647b4c3e9b5SBjoern A. Zeeb 
1648b4c3e9b5SBjoern A. Zeeb scan_out:
1649b4c3e9b5SBjoern A. Zeeb 	bphy_err(drvr, "scan error (%d)\n", err);
1650b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
1651b4c3e9b5SBjoern A. Zeeb 	cfg->scan_request = NULL;
1652b4c3e9b5SBjoern A. Zeeb 	return err;
1653b4c3e9b5SBjoern A. Zeeb }
1654b4c3e9b5SBjoern A. Zeeb 
brcmf_set_rts(struct net_device * ndev,u32 rts_threshold)1655b4c3e9b5SBjoern A. Zeeb static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1656b4c3e9b5SBjoern A. Zeeb {
1657b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1658b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
1659b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1660b4c3e9b5SBjoern A. Zeeb 
1661b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "rtsthresh", rts_threshold);
1662b4c3e9b5SBjoern A. Zeeb 	if (err)
1663b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Error (%d)\n", err);
1664b4c3e9b5SBjoern A. Zeeb 
1665b4c3e9b5SBjoern A. Zeeb 	return err;
1666b4c3e9b5SBjoern A. Zeeb }
1667b4c3e9b5SBjoern A. Zeeb 
brcmf_set_frag(struct net_device * ndev,u32 frag_threshold)1668b4c3e9b5SBjoern A. Zeeb static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1669b4c3e9b5SBjoern A. Zeeb {
1670b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1671b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
1672b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1673b4c3e9b5SBjoern A. Zeeb 
1674b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "fragthresh",
1675b4c3e9b5SBjoern A. Zeeb 				      frag_threshold);
1676b4c3e9b5SBjoern A. Zeeb 	if (err)
1677b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Error (%d)\n", err);
1678b4c3e9b5SBjoern A. Zeeb 
1679b4c3e9b5SBjoern A. Zeeb 	return err;
1680b4c3e9b5SBjoern A. Zeeb }
1681b4c3e9b5SBjoern A. Zeeb 
brcmf_set_retry(struct net_device * ndev,u32 retry,bool l)1682b4c3e9b5SBjoern A. Zeeb static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1683b4c3e9b5SBjoern A. Zeeb {
1684b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1685b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
1686b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1687b4c3e9b5SBjoern A. Zeeb 	u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
1688b4c3e9b5SBjoern A. Zeeb 
1689b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, cmd, retry);
1690b4c3e9b5SBjoern A. Zeeb 	if (err) {
1691b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "cmd (%d) , error (%d)\n", cmd, err);
1692b4c3e9b5SBjoern A. Zeeb 		return err;
1693b4c3e9b5SBjoern A. Zeeb 	}
1694b4c3e9b5SBjoern A. Zeeb 	return err;
1695b4c3e9b5SBjoern A. Zeeb }
1696b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_set_wiphy_params(struct wiphy * wiphy,int radio_idx,u32 changed)1697b4c3e9b5SBjoern A. Zeeb static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, int radio_idx,
1698b4c3e9b5SBjoern A. Zeeb 					   u32 changed)
1699b4c3e9b5SBjoern A. Zeeb {
1700b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1701b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = cfg_to_ndev(cfg);
1702b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1703b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1704b4c3e9b5SBjoern A. Zeeb 
1705b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
1706b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
1707b4c3e9b5SBjoern A. Zeeb 		return -EIO;
1708b4c3e9b5SBjoern A. Zeeb 
1709b4c3e9b5SBjoern A. Zeeb 	if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
1710b4c3e9b5SBjoern A. Zeeb 	    (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1711b4c3e9b5SBjoern A. Zeeb 		cfg->conf->rts_threshold = wiphy->rts_threshold;
1712b4c3e9b5SBjoern A. Zeeb 		err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
1713b4c3e9b5SBjoern A. Zeeb 		if (!err)
1714b4c3e9b5SBjoern A. Zeeb 			goto done;
1715b4c3e9b5SBjoern A. Zeeb 	}
1716b4c3e9b5SBjoern A. Zeeb 	if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
1717b4c3e9b5SBjoern A. Zeeb 	    (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1718b4c3e9b5SBjoern A. Zeeb 		cfg->conf->frag_threshold = wiphy->frag_threshold;
1719b4c3e9b5SBjoern A. Zeeb 		err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
1720b4c3e9b5SBjoern A. Zeeb 		if (!err)
1721b4c3e9b5SBjoern A. Zeeb 			goto done;
1722b4c3e9b5SBjoern A. Zeeb 	}
1723b4c3e9b5SBjoern A. Zeeb 	if (changed & WIPHY_PARAM_RETRY_LONG
1724b4c3e9b5SBjoern A. Zeeb 	    && (cfg->conf->retry_long != wiphy->retry_long)) {
1725b4c3e9b5SBjoern A. Zeeb 		cfg->conf->retry_long = wiphy->retry_long;
1726b4c3e9b5SBjoern A. Zeeb 		err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
1727b4c3e9b5SBjoern A. Zeeb 		if (!err)
1728b4c3e9b5SBjoern A. Zeeb 			goto done;
1729b4c3e9b5SBjoern A. Zeeb 	}
1730b4c3e9b5SBjoern A. Zeeb 	if (changed & WIPHY_PARAM_RETRY_SHORT
1731b4c3e9b5SBjoern A. Zeeb 	    && (cfg->conf->retry_short != wiphy->retry_short)) {
1732b4c3e9b5SBjoern A. Zeeb 		cfg->conf->retry_short = wiphy->retry_short;
1733b4c3e9b5SBjoern A. Zeeb 		err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
1734b4c3e9b5SBjoern A. Zeeb 		if (!err)
1735b4c3e9b5SBjoern A. Zeeb 			goto done;
1736b4c3e9b5SBjoern A. Zeeb 	}
1737b4c3e9b5SBjoern A. Zeeb 
1738b4c3e9b5SBjoern A. Zeeb done:
1739b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
1740b4c3e9b5SBjoern A. Zeeb 	return err;
1741b4c3e9b5SBjoern A. Zeeb }
1742b4c3e9b5SBjoern A. Zeeb 
brcmf_init_prof(struct brcmf_cfg80211_profile * prof)1743b4c3e9b5SBjoern A. Zeeb static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1744b4c3e9b5SBjoern A. Zeeb {
1745b4c3e9b5SBjoern A. Zeeb 	memset(prof, 0, sizeof(*prof));
1746b4c3e9b5SBjoern A. Zeeb }
1747b4c3e9b5SBjoern A. Zeeb 
brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg * e)1748b4c3e9b5SBjoern A. Zeeb static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1749b4c3e9b5SBjoern A. Zeeb {
1750b4c3e9b5SBjoern A. Zeeb 	u16 reason;
1751b4c3e9b5SBjoern A. Zeeb 
1752b4c3e9b5SBjoern A. Zeeb 	switch (e->event_code) {
1753b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_DEAUTH:
1754b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_DEAUTH_IND:
1755b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_DISASSOC_IND:
1756b4c3e9b5SBjoern A. Zeeb 		reason = e->reason;
1757b4c3e9b5SBjoern A. Zeeb 		break;
1758b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_LINK:
1759b4c3e9b5SBjoern A. Zeeb 	default:
1760b4c3e9b5SBjoern A. Zeeb 		reason = 0;
1761b4c3e9b5SBjoern A. Zeeb 		break;
1762b4c3e9b5SBjoern A. Zeeb 	}
1763b4c3e9b5SBjoern A. Zeeb 	return reason;
1764b4c3e9b5SBjoern A. Zeeb }
1765b4c3e9b5SBjoern A. Zeeb 
brcmf_set_wsec(struct brcmf_if * ifp,const u8 * key,u16 key_len,u16 flags)1766b4c3e9b5SBjoern A. Zeeb int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags)
1767b4c3e9b5SBjoern A. Zeeb {
1768b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
1769b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wsec_pmk_le pmk;
1770b4c3e9b5SBjoern A. Zeeb 	int err;
1771b4c3e9b5SBjoern A. Zeeb 
1772b4c3e9b5SBjoern A. Zeeb 	if (key_len > sizeof(pmk.key)) {
1773b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "key must be less than %zu bytes\n",
1774b4c3e9b5SBjoern A. Zeeb 			 sizeof(pmk.key));
1775b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
1776b4c3e9b5SBjoern A. Zeeb 	}
1777b4c3e9b5SBjoern A. Zeeb 
1778b4c3e9b5SBjoern A. Zeeb 	memset(&pmk, 0, sizeof(pmk));
1779b4c3e9b5SBjoern A. Zeeb 
1780b4c3e9b5SBjoern A. Zeeb 	/* pass key material directly */
1781b4c3e9b5SBjoern A. Zeeb 	pmk.key_len = cpu_to_le16(key_len);
1782b4c3e9b5SBjoern A. Zeeb 	pmk.flags = cpu_to_le16(flags);
1783b4c3e9b5SBjoern A. Zeeb 	memcpy(pmk.key, key, key_len);
1784b4c3e9b5SBjoern A. Zeeb 
1785b4c3e9b5SBjoern A. Zeeb 	/* store key material in firmware */
1786b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
1787b4c3e9b5SBjoern A. Zeeb 				     &pmk, sizeof(pmk));
1788b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
1789b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",
1790b4c3e9b5SBjoern A. Zeeb 			 key_len);
1791b4c3e9b5SBjoern A. Zeeb 
1792b4c3e9b5SBjoern A. Zeeb 	return err;
1793b4c3e9b5SBjoern A. Zeeb }
1794b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_set_wsec);
1795b4c3e9b5SBjoern A. Zeeb 
brcmf_set_pmk(struct brcmf_if * ifp,const u8 * pmk_data,u16 pmk_len)1796b4c3e9b5SBjoern A. Zeeb static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
1797b4c3e9b5SBjoern A. Zeeb {
1798b4c3e9b5SBjoern A. Zeeb 	return brcmf_set_wsec(ifp, pmk_data, pmk_len, 0);
1799b4c3e9b5SBjoern A. Zeeb }
1800b4c3e9b5SBjoern A. Zeeb 
brcmf_link_down(struct brcmf_cfg80211_vif * vif,u16 reason,bool locally_generated)1801b4c3e9b5SBjoern A. Zeeb static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,
1802b4c3e9b5SBjoern A. Zeeb 			    bool locally_generated)
1803b4c3e9b5SBjoern A. Zeeb {
1804b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
1805b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1806b4c3e9b5SBjoern A. Zeeb 	bool bus_up = drvr->bus_if->state == BRCMF_BUS_UP;
1807b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1808b4c3e9b5SBjoern A. Zeeb 
1809b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
1810b4c3e9b5SBjoern A. Zeeb 
1811b4c3e9b5SBjoern A. Zeeb 	if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
1812b4c3e9b5SBjoern A. Zeeb 		if (bus_up) {
1813b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n");
1814b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_data_set(vif->ifp,
1815b4c3e9b5SBjoern A. Zeeb 						     BRCMF_C_DISASSOC, NULL, 0);
1816b4c3e9b5SBjoern A. Zeeb 			if (err)
1817b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "WLC_DISASSOC failed (%d)\n",
1818b4c3e9b5SBjoern A. Zeeb 					 err);
1819b4c3e9b5SBjoern A. Zeeb 		}
1820b4c3e9b5SBjoern A. Zeeb 
1821b4c3e9b5SBjoern A. Zeeb 		if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1822b4c3e9b5SBjoern A. Zeeb 		    (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
1823b4c3e9b5SBjoern A. Zeeb 			cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
1824b4c3e9b5SBjoern A. Zeeb 					      locally_generated, GFP_KERNEL);
1825b4c3e9b5SBjoern A. Zeeb 	}
1826b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
1827b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
1828b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
1829b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1830b4c3e9b5SBjoern A. Zeeb 	brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
1831b4c3e9b5SBjoern A. Zeeb 	if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
1832b4c3e9b5SBjoern A. Zeeb 		if (bus_up)
1833b4c3e9b5SBjoern A. Zeeb 			brcmf_set_pmk(vif->ifp, NULL, 0);
1834b4c3e9b5SBjoern A. Zeeb 		vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
1835b4c3e9b5SBjoern A. Zeeb 	}
1836b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
1837b4c3e9b5SBjoern A. Zeeb }
1838b4c3e9b5SBjoern A. Zeeb 
1839b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_join_ibss(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ibss_params * params)1840b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1841b4c3e9b5SBjoern A. Zeeb 		      struct cfg80211_ibss_params *params)
1842b4c3e9b5SBjoern A. Zeeb {
1843b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1844b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1845b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
1846b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
1847b4c3e9b5SBjoern A. Zeeb 	struct brcmf_join_params join_params;
1848b4c3e9b5SBjoern A. Zeeb 	size_t join_params_size = 0;
1849b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
1850b4c3e9b5SBjoern A. Zeeb 	s32 wsec = 0;
1851b4c3e9b5SBjoern A. Zeeb 	s32 bcnprd;
1852b4c3e9b5SBjoern A. Zeeb 	u16 chanspec;
1853b4c3e9b5SBjoern A. Zeeb 	u32 ssid_len;
1854b4c3e9b5SBjoern A. Zeeb 
1855b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
1856b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
1857b4c3e9b5SBjoern A. Zeeb 		return -EIO;
1858b4c3e9b5SBjoern A. Zeeb 
1859b4c3e9b5SBjoern A. Zeeb 	if (params->ssid)
1860b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
1861b4c3e9b5SBjoern A. Zeeb 	else {
1862b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
1863b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
1864b4c3e9b5SBjoern A. Zeeb 	}
1865b4c3e9b5SBjoern A. Zeeb 
1866b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
1867b4c3e9b5SBjoern A. Zeeb 
1868b4c3e9b5SBjoern A. Zeeb 	if (params->bssid)
1869b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
1870b4c3e9b5SBjoern A. Zeeb 	else
1871b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "No BSSID specified\n");
1872b4c3e9b5SBjoern A. Zeeb 
1873b4c3e9b5SBjoern A. Zeeb 	if (params->chandef.chan)
1874b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "channel: %d\n",
1875b4c3e9b5SBjoern A. Zeeb 			  params->chandef.chan->center_freq);
1876b4c3e9b5SBjoern A. Zeeb 	else
1877b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "no channel specified\n");
1878b4c3e9b5SBjoern A. Zeeb 
1879b4c3e9b5SBjoern A. Zeeb 	if (params->channel_fixed)
1880b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "fixed channel required\n");
1881b4c3e9b5SBjoern A. Zeeb 	else
1882b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "no fixed channel required\n");
1883b4c3e9b5SBjoern A. Zeeb 
1884b4c3e9b5SBjoern A. Zeeb 	if (params->ie && params->ie_len)
1885b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
1886b4c3e9b5SBjoern A. Zeeb 	else
1887b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "no ie specified\n");
1888b4c3e9b5SBjoern A. Zeeb 
1889b4c3e9b5SBjoern A. Zeeb 	if (params->beacon_interval)
1890b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "beacon interval: %d\n",
1891b4c3e9b5SBjoern A. Zeeb 			  params->beacon_interval);
1892b4c3e9b5SBjoern A. Zeeb 	else
1893b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "no beacon interval specified\n");
1894b4c3e9b5SBjoern A. Zeeb 
1895b4c3e9b5SBjoern A. Zeeb 	if (params->basic_rates)
1896b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
1897b4c3e9b5SBjoern A. Zeeb 	else
1898b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "no basic rates specified\n");
1899b4c3e9b5SBjoern A. Zeeb 
1900b4c3e9b5SBjoern A. Zeeb 	if (params->privacy)
1901b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "privacy required\n");
1902b4c3e9b5SBjoern A. Zeeb 	else
1903b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "no privacy required\n");
1904b4c3e9b5SBjoern A. Zeeb 
1905b4c3e9b5SBjoern A. Zeeb 	/* Configure Privacy for starter */
1906b4c3e9b5SBjoern A. Zeeb 	if (params->privacy)
1907b4c3e9b5SBjoern A. Zeeb 		wsec |= WEP_ENABLED;
1908b4c3e9b5SBjoern A. Zeeb 
1909b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
1910b4c3e9b5SBjoern A. Zeeb 	if (err) {
1911b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wsec failed (%d)\n", err);
1912b4c3e9b5SBjoern A. Zeeb 		goto done;
1913b4c3e9b5SBjoern A. Zeeb 	}
1914b4c3e9b5SBjoern A. Zeeb 
1915b4c3e9b5SBjoern A. Zeeb 	/* Configure Beacon Interval for starter */
1916b4c3e9b5SBjoern A. Zeeb 	if (params->beacon_interval)
1917b4c3e9b5SBjoern A. Zeeb 		bcnprd = params->beacon_interval;
1918b4c3e9b5SBjoern A. Zeeb 	else
1919b4c3e9b5SBjoern A. Zeeb 		bcnprd = 100;
1920b4c3e9b5SBjoern A. Zeeb 
1921b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
1922b4c3e9b5SBjoern A. Zeeb 	if (err) {
1923b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_SET_BCNPRD failed (%d)\n", err);
1924b4c3e9b5SBjoern A. Zeeb 		goto done;
1925b4c3e9b5SBjoern A. Zeeb 	}
1926b4c3e9b5SBjoern A. Zeeb 
1927b4c3e9b5SBjoern A. Zeeb 	/* Configure required join parameter */
1928b4c3e9b5SBjoern A. Zeeb 	memset(&join_params, 0, sizeof(struct brcmf_join_params));
1929b4c3e9b5SBjoern A. Zeeb 
1930b4c3e9b5SBjoern A. Zeeb 	/* SSID */
1931b4c3e9b5SBjoern A. Zeeb 	ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1932b4c3e9b5SBjoern A. Zeeb 	memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1933b4c3e9b5SBjoern A. Zeeb 	join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
1934b4c3e9b5SBjoern A. Zeeb 	join_params_size = sizeof(join_params.ssid_le);
1935b4c3e9b5SBjoern A. Zeeb 
1936b4c3e9b5SBjoern A. Zeeb 	/* BSSID */
1937b4c3e9b5SBjoern A. Zeeb 	if (params->bssid) {
1938b4c3e9b5SBjoern A. Zeeb 		memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1939b4c3e9b5SBjoern A. Zeeb 		join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
1940b4c3e9b5SBjoern A. Zeeb 		memcpy(profile->bssid, params->bssid, ETH_ALEN);
1941b4c3e9b5SBjoern A. Zeeb 	} else {
1942b4c3e9b5SBjoern A. Zeeb 		eth_broadcast_addr(join_params.params_le.bssid);
1943b4c3e9b5SBjoern A. Zeeb 		eth_zero_addr(profile->bssid);
1944b4c3e9b5SBjoern A. Zeeb 	}
1945b4c3e9b5SBjoern A. Zeeb 
1946b4c3e9b5SBjoern A. Zeeb 	/* Channel */
1947b4c3e9b5SBjoern A. Zeeb 	if (params->chandef.chan) {
1948b4c3e9b5SBjoern A. Zeeb 		u32 target_channel;
1949b4c3e9b5SBjoern A. Zeeb 
1950b4c3e9b5SBjoern A. Zeeb 		cfg->channel =
1951b4c3e9b5SBjoern A. Zeeb 			ieee80211_frequency_to_channel(
1952b4c3e9b5SBjoern A. Zeeb 				params->chandef.chan->center_freq);
1953b4c3e9b5SBjoern A. Zeeb 		if (params->channel_fixed) {
1954b4c3e9b5SBjoern A. Zeeb 			/* adding chanspec */
1955b4c3e9b5SBjoern A. Zeeb 			chanspec = chandef_to_chanspec(&cfg->d11inf,
1956b4c3e9b5SBjoern A. Zeeb 						       &params->chandef);
1957b4c3e9b5SBjoern A. Zeeb 			join_params.params_le.chanspec_list[0] =
1958b4c3e9b5SBjoern A. Zeeb 				cpu_to_le16(chanspec);
1959b4c3e9b5SBjoern A. Zeeb 			join_params.params_le.chanspec_num = cpu_to_le32(1);
1960b4c3e9b5SBjoern A. Zeeb 			join_params_size += sizeof(join_params.params_le);
1961b4c3e9b5SBjoern A. Zeeb 		}
1962b4c3e9b5SBjoern A. Zeeb 
1963b4c3e9b5SBjoern A. Zeeb 		/* set channel for starter */
1964b4c3e9b5SBjoern A. Zeeb 		target_channel = cfg->channel;
1965b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
1966b4c3e9b5SBjoern A. Zeeb 					    target_channel);
1967b4c3e9b5SBjoern A. Zeeb 		if (err) {
1968b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "WLC_SET_CHANNEL failed (%d)\n", err);
1969b4c3e9b5SBjoern A. Zeeb 			goto done;
1970b4c3e9b5SBjoern A. Zeeb 		}
1971b4c3e9b5SBjoern A. Zeeb 	} else
1972b4c3e9b5SBjoern A. Zeeb 		cfg->channel = 0;
1973b4c3e9b5SBjoern A. Zeeb 
1974b4c3e9b5SBjoern A. Zeeb 	cfg->ibss_starter = false;
1975b4c3e9b5SBjoern A. Zeeb 
1976b4c3e9b5SBjoern A. Zeeb 
1977b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
1978b4c3e9b5SBjoern A. Zeeb 				     &join_params, join_params_size);
1979b4c3e9b5SBjoern A. Zeeb 	if (err) {
1980b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_SET_SSID failed (%d)\n", err);
1981b4c3e9b5SBjoern A. Zeeb 		goto done;
1982b4c3e9b5SBjoern A. Zeeb 	}
1983b4c3e9b5SBjoern A. Zeeb 
1984b4c3e9b5SBjoern A. Zeeb done:
1985b4c3e9b5SBjoern A. Zeeb 	if (err)
1986b4c3e9b5SBjoern A. Zeeb 		clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
1987b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
1988b4c3e9b5SBjoern A. Zeeb 	return err;
1989b4c3e9b5SBjoern A. Zeeb }
1990b4c3e9b5SBjoern A. Zeeb 
1991b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_leave_ibss(struct wiphy * wiphy,struct net_device * ndev)1992b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1993b4c3e9b5SBjoern A. Zeeb {
1994b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
1995b4c3e9b5SBjoern A. Zeeb 
1996b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
1997b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif)) {
1998b4c3e9b5SBjoern A. Zeeb 		/* When driver is being unloaded, it can end up here. If an
1999b4c3e9b5SBjoern A. Zeeb 		 * error is returned then later on a debug trace in the wireless
2000b4c3e9b5SBjoern A. Zeeb 		 * core module will be printed. To avoid this 0 is returned.
2001b4c3e9b5SBjoern A. Zeeb 		 */
2002b4c3e9b5SBjoern A. Zeeb 		return 0;
2003b4c3e9b5SBjoern A. Zeeb 	}
2004b4c3e9b5SBjoern A. Zeeb 
2005b4c3e9b5SBjoern A. Zeeb 	brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING, true);
2006b4c3e9b5SBjoern A. Zeeb 	brcmf_net_setcarrier(ifp, false);
2007b4c3e9b5SBjoern A. Zeeb 
2008b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
2009b4c3e9b5SBjoern A. Zeeb 
2010b4c3e9b5SBjoern A. Zeeb 	return 0;
2011b4c3e9b5SBjoern A. Zeeb }
2012b4c3e9b5SBjoern A. Zeeb 
brcmf_set_wpa_version(struct net_device * ndev,struct cfg80211_connect_params * sme)2013b4c3e9b5SBjoern A. Zeeb static s32 brcmf_set_wpa_version(struct net_device *ndev,
2014b4c3e9b5SBjoern A. Zeeb 				 struct cfg80211_connect_params *sme)
2015b4c3e9b5SBjoern A. Zeeb {
2016b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2017b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
2018b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2019b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_security *sec;
2020b4c3e9b5SBjoern A. Zeeb 	s32 val;
2021b4c3e9b5SBjoern A. Zeeb 	s32 err;
2022b4c3e9b5SBjoern A. Zeeb 
2023b4c3e9b5SBjoern A. Zeeb 	if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {
2024b4c3e9b5SBjoern A. Zeeb 		val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
2025b4c3e9b5SBjoern A. Zeeb 	} else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {
2026b4c3e9b5SBjoern A. Zeeb 		if (drvr->bus_if->fwvid == BRCMF_FWVENDOR_CYW &&
2027b4c3e9b5SBjoern A. Zeeb 		    sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE)
2028b4c3e9b5SBjoern A. Zeeb 			val = WPA3_AUTH_SAE_PSK;
2029b4c3e9b5SBjoern A. Zeeb 		else
2030b4c3e9b5SBjoern A. Zeeb 			val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
2031b4c3e9b5SBjoern A. Zeeb 	} else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) {
2032b4c3e9b5SBjoern A. Zeeb 		val = WPA3_AUTH_SAE_PSK;
2033b4c3e9b5SBjoern A. Zeeb 	} else {
2034b4c3e9b5SBjoern A. Zeeb 		val = WPA_AUTH_DISABLED;
2035b4c3e9b5SBjoern A. Zeeb 	}
2036b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
2037b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val);
2038b4c3e9b5SBjoern A. Zeeb 	if (err) {
2039b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "set wpa_auth failed (%d)\n", err);
2040b4c3e9b5SBjoern A. Zeeb 		return err;
2041b4c3e9b5SBjoern A. Zeeb 	}
2042b4c3e9b5SBjoern A. Zeeb 	sec = &profile->sec;
2043b4c3e9b5SBjoern A. Zeeb 	sec->wpa_versions = sme->crypto.wpa_versions;
2044b4c3e9b5SBjoern A. Zeeb 	return err;
2045b4c3e9b5SBjoern A. Zeeb }
2046b4c3e9b5SBjoern A. Zeeb 
brcmf_set_auth_type(struct net_device * ndev,struct cfg80211_connect_params * sme)2047b4c3e9b5SBjoern A. Zeeb static s32 brcmf_set_auth_type(struct net_device *ndev,
2048b4c3e9b5SBjoern A. Zeeb 			       struct cfg80211_connect_params *sme)
2049b4c3e9b5SBjoern A. Zeeb {
2050b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2051b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
2052b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2053b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_security *sec;
2054b4c3e9b5SBjoern A. Zeeb 	s32 val = 0;
2055b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
2056b4c3e9b5SBjoern A. Zeeb 
2057b4c3e9b5SBjoern A. Zeeb 	switch (sme->auth_type) {
2058b4c3e9b5SBjoern A. Zeeb 	case NL80211_AUTHTYPE_OPEN_SYSTEM:
2059b4c3e9b5SBjoern A. Zeeb 		val = 0;
2060b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "open system\n");
2061b4c3e9b5SBjoern A. Zeeb 		break;
2062b4c3e9b5SBjoern A. Zeeb 	case NL80211_AUTHTYPE_SHARED_KEY:
2063b4c3e9b5SBjoern A. Zeeb 		val = 1;
2064b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "shared key\n");
2065b4c3e9b5SBjoern A. Zeeb 		break;
2066b4c3e9b5SBjoern A. Zeeb 	case NL80211_AUTHTYPE_SAE:
2067b4c3e9b5SBjoern A. Zeeb 		val = 3;
2068b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "SAE authentication\n");
2069b4c3e9b5SBjoern A. Zeeb 		break;
2070b4c3e9b5SBjoern A. Zeeb 	default:
2071b4c3e9b5SBjoern A. Zeeb 		val = 2;
2072b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
2073b4c3e9b5SBjoern A. Zeeb 		break;
2074b4c3e9b5SBjoern A. Zeeb 	}
2075b4c3e9b5SBjoern A. Zeeb 
2076b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
2077b4c3e9b5SBjoern A. Zeeb 	if (err) {
2078b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "set auth failed (%d)\n", err);
2079b4c3e9b5SBjoern A. Zeeb 		return err;
2080b4c3e9b5SBjoern A. Zeeb 	}
2081b4c3e9b5SBjoern A. Zeeb 	sec = &profile->sec;
2082b4c3e9b5SBjoern A. Zeeb 	sec->auth_type = sme->auth_type;
2083b4c3e9b5SBjoern A. Zeeb 	return err;
2084b4c3e9b5SBjoern A. Zeeb }
2085b4c3e9b5SBjoern A. Zeeb 
2086b4c3e9b5SBjoern A. Zeeb static s32
brcmf_set_wsec_mode(struct net_device * ndev,struct cfg80211_connect_params * sme)2087b4c3e9b5SBjoern A. Zeeb brcmf_set_wsec_mode(struct net_device *ndev,
2088b4c3e9b5SBjoern A. Zeeb 		    struct cfg80211_connect_params *sme)
2089b4c3e9b5SBjoern A. Zeeb {
2090b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2091b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
2092b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2093b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_security *sec;
2094b4c3e9b5SBjoern A. Zeeb 	s32 pval = 0;
2095b4c3e9b5SBjoern A. Zeeb 	s32 gval = 0;
2096b4c3e9b5SBjoern A. Zeeb 	s32 wsec;
2097b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
2098b4c3e9b5SBjoern A. Zeeb 
2099b4c3e9b5SBjoern A. Zeeb 	if (sme->crypto.n_ciphers_pairwise) {
2100b4c3e9b5SBjoern A. Zeeb 		switch (sme->crypto.ciphers_pairwise[0]) {
2101b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_WEP40:
2102b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_WEP104:
2103b4c3e9b5SBjoern A. Zeeb 			pval = WEP_ENABLED;
2104b4c3e9b5SBjoern A. Zeeb 			break;
2105b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_TKIP:
2106b4c3e9b5SBjoern A. Zeeb 			pval = TKIP_ENABLED;
2107b4c3e9b5SBjoern A. Zeeb 			break;
2108b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_CCMP:
2109b4c3e9b5SBjoern A. Zeeb 			pval = AES_ENABLED;
2110b4c3e9b5SBjoern A. Zeeb 			break;
2111b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_AES_CMAC:
2112b4c3e9b5SBjoern A. Zeeb 			pval = AES_ENABLED;
2113b4c3e9b5SBjoern A. Zeeb 			break;
2114b4c3e9b5SBjoern A. Zeeb 		default:
2115b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid cipher pairwise (%d)\n",
2116b4c3e9b5SBjoern A. Zeeb 				 sme->crypto.ciphers_pairwise[0]);
2117b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
2118b4c3e9b5SBjoern A. Zeeb 		}
2119b4c3e9b5SBjoern A. Zeeb 	}
2120b4c3e9b5SBjoern A. Zeeb 	if (sme->crypto.cipher_group) {
2121b4c3e9b5SBjoern A. Zeeb 		switch (sme->crypto.cipher_group) {
2122b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_WEP40:
2123b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_WEP104:
2124b4c3e9b5SBjoern A. Zeeb 			gval = WEP_ENABLED;
2125b4c3e9b5SBjoern A. Zeeb 			break;
2126b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_TKIP:
2127b4c3e9b5SBjoern A. Zeeb 			gval = TKIP_ENABLED;
2128b4c3e9b5SBjoern A. Zeeb 			break;
2129b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_CCMP:
2130b4c3e9b5SBjoern A. Zeeb 			gval = AES_ENABLED;
2131b4c3e9b5SBjoern A. Zeeb 			break;
2132b4c3e9b5SBjoern A. Zeeb 		case WLAN_CIPHER_SUITE_AES_CMAC:
2133b4c3e9b5SBjoern A. Zeeb 			gval = AES_ENABLED;
2134b4c3e9b5SBjoern A. Zeeb 			break;
2135b4c3e9b5SBjoern A. Zeeb 		default:
2136b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid cipher group (%d)\n",
2137b4c3e9b5SBjoern A. Zeeb 				 sme->crypto.cipher_group);
2138b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
2139b4c3e9b5SBjoern A. Zeeb 		}
2140b4c3e9b5SBjoern A. Zeeb 	}
2141b4c3e9b5SBjoern A. Zeeb 
2142b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
2143b4c3e9b5SBjoern A. Zeeb 	/* In case of privacy, but no security and WPS then simulate */
2144b4c3e9b5SBjoern A. Zeeb 	/* setting AES. WPS-2.0 allows no security                   */
2145b4c3e9b5SBjoern A. Zeeb 	if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
2146b4c3e9b5SBjoern A. Zeeb 	    sme->privacy)
2147b4c3e9b5SBjoern A. Zeeb 		pval = AES_ENABLED;
2148b4c3e9b5SBjoern A. Zeeb 
2149b4c3e9b5SBjoern A. Zeeb 	wsec = pval | gval;
2150b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2151b4c3e9b5SBjoern A. Zeeb 	if (err) {
2152b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "error (%d)\n", err);
2153b4c3e9b5SBjoern A. Zeeb 		return err;
2154b4c3e9b5SBjoern A. Zeeb 	}
2155b4c3e9b5SBjoern A. Zeeb 
2156b4c3e9b5SBjoern A. Zeeb 	sec = &profile->sec;
2157b4c3e9b5SBjoern A. Zeeb 	sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
2158b4c3e9b5SBjoern A. Zeeb 	sec->cipher_group = sme->crypto.cipher_group;
2159b4c3e9b5SBjoern A. Zeeb 
2160b4c3e9b5SBjoern A. Zeeb 	return err;
2161b4c3e9b5SBjoern A. Zeeb }
2162b4c3e9b5SBjoern A. Zeeb 
2163b4c3e9b5SBjoern A. Zeeb static s32
brcmf_set_key_mgmt(struct net_device * ndev,struct cfg80211_connect_params * sme)2164b4c3e9b5SBjoern A. Zeeb brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
2165b4c3e9b5SBjoern A. Zeeb {
2166b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2167b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
2168b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2169b4c3e9b5SBjoern A. Zeeb 	s32 val;
2170b4c3e9b5SBjoern A. Zeeb 	s32 err;
2171b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *rsn_ie;
2172b4c3e9b5SBjoern A. Zeeb 	const u8 *ie;
2173b4c3e9b5SBjoern A. Zeeb 	u32 ie_len;
2174b4c3e9b5SBjoern A. Zeeb 	u32 offset;
2175b4c3e9b5SBjoern A. Zeeb 	u16 rsn_cap;
2176b4c3e9b5SBjoern A. Zeeb 	u32 mfp;
2177b4c3e9b5SBjoern A. Zeeb 	u16 count;
2178b4c3e9b5SBjoern A. Zeeb 
2179b4c3e9b5SBjoern A. Zeeb 	profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
2180b4c3e9b5SBjoern A. Zeeb 	profile->is_ft = false;
2181b4c3e9b5SBjoern A. Zeeb 
2182b4c3e9b5SBjoern A. Zeeb 	if (!sme->crypto.n_akm_suites)
2183b4c3e9b5SBjoern A. Zeeb 		return 0;
2184b4c3e9b5SBjoern A. Zeeb 
2185b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
2186b4c3e9b5SBjoern A. Zeeb 				       "wpa_auth", &val);
2187b4c3e9b5SBjoern A. Zeeb 	if (err) {
2188b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "could not get wpa_auth (%d)\n", err);
2189b4c3e9b5SBjoern A. Zeeb 		return err;
2190b4c3e9b5SBjoern A. Zeeb 	}
2191b4c3e9b5SBjoern A. Zeeb 	if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
2192b4c3e9b5SBjoern A. Zeeb 		switch (sme->crypto.akm_suites[0]) {
2193b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_8021X:
2194b4c3e9b5SBjoern A. Zeeb 			val = WPA_AUTH_UNSPECIFIED;
2195b4c3e9b5SBjoern A. Zeeb 			if (sme->want_1x)
2196b4c3e9b5SBjoern A. Zeeb 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
2197b4c3e9b5SBjoern A. Zeeb 			break;
2198b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_PSK:
2199b4c3e9b5SBjoern A. Zeeb 			val = WPA_AUTH_PSK;
2200b4c3e9b5SBjoern A. Zeeb 			break;
2201b4c3e9b5SBjoern A. Zeeb 		default:
2202b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid akm suite (%d)\n",
2203b4c3e9b5SBjoern A. Zeeb 				 sme->crypto.akm_suites[0]);
2204b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
2205b4c3e9b5SBjoern A. Zeeb 		}
2206b4c3e9b5SBjoern A. Zeeb 	} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
2207b4c3e9b5SBjoern A. Zeeb 		switch (sme->crypto.akm_suites[0]) {
2208b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_8021X:
2209b4c3e9b5SBjoern A. Zeeb 			val = WPA2_AUTH_UNSPECIFIED;
2210b4c3e9b5SBjoern A. Zeeb 			if (sme->want_1x)
2211b4c3e9b5SBjoern A. Zeeb 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
2212b4c3e9b5SBjoern A. Zeeb 			break;
2213b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_8021X_SHA256:
2214b4c3e9b5SBjoern A. Zeeb 			val = WPA2_AUTH_1X_SHA256;
2215b4c3e9b5SBjoern A. Zeeb 			if (sme->want_1x)
2216b4c3e9b5SBjoern A. Zeeb 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
2217b4c3e9b5SBjoern A. Zeeb 			break;
2218b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_PSK_SHA256:
2219b4c3e9b5SBjoern A. Zeeb 			val = WPA2_AUTH_PSK_SHA256;
2220b4c3e9b5SBjoern A. Zeeb 			break;
2221b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_PSK:
2222b4c3e9b5SBjoern A. Zeeb 			val = WPA2_AUTH_PSK;
2223b4c3e9b5SBjoern A. Zeeb 			break;
2224b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_FT_8021X:
2225b4c3e9b5SBjoern A. Zeeb 			val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;
2226b4c3e9b5SBjoern A. Zeeb 			profile->is_ft = true;
2227b4c3e9b5SBjoern A. Zeeb 			if (sme->want_1x)
2228b4c3e9b5SBjoern A. Zeeb 				profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
2229b4c3e9b5SBjoern A. Zeeb 			break;
2230b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_FT_PSK:
2231b4c3e9b5SBjoern A. Zeeb 			val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
2232b4c3e9b5SBjoern A. Zeeb 			profile->is_ft = true;
2233b4c3e9b5SBjoern A. Zeeb 			break;
2234b4c3e9b5SBjoern A. Zeeb 		default:
2235b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid akm suite (%d)\n",
2236b4c3e9b5SBjoern A. Zeeb 				 sme->crypto.akm_suites[0]);
2237b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
2238b4c3e9b5SBjoern A. Zeeb 		}
2239b4c3e9b5SBjoern A. Zeeb 	} else if (val & WPA3_AUTH_SAE_PSK) {
2240b4c3e9b5SBjoern A. Zeeb 		switch (sme->crypto.akm_suites[0]) {
2241b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_SAE:
2242b4c3e9b5SBjoern A. Zeeb 			val = WPA3_AUTH_SAE_PSK;
2243b4c3e9b5SBjoern A. Zeeb 			break;
2244b4c3e9b5SBjoern A. Zeeb 		case WLAN_AKM_SUITE_FT_OVER_SAE:
2245b4c3e9b5SBjoern A. Zeeb 			val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT;
2246b4c3e9b5SBjoern A. Zeeb 			profile->is_ft = true;
2247b4c3e9b5SBjoern A. Zeeb 			break;
2248b4c3e9b5SBjoern A. Zeeb 		default:
2249b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid akm suite (%d)\n",
2250b4c3e9b5SBjoern A. Zeeb 				 sme->crypto.akm_suites[0]);
2251b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
2252b4c3e9b5SBjoern A. Zeeb 		}
2253b4c3e9b5SBjoern A. Zeeb 		if (sme->crypto.sae_pwd) {
2254b4c3e9b5SBjoern A. Zeeb 			profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
2255b4c3e9b5SBjoern A. Zeeb 		}
2256b4c3e9b5SBjoern A. Zeeb 	}
2257b4c3e9b5SBjoern A. Zeeb 
2258b4c3e9b5SBjoern A. Zeeb 	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
2259b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "using 1X offload\n");
2260b4c3e9b5SBjoern A. Zeeb 	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE)
2261b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "using SAE offload\n");
2262b4c3e9b5SBjoern A. Zeeb 
2263b4c3e9b5SBjoern A. Zeeb 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
2264b4c3e9b5SBjoern A. Zeeb 		goto skip_mfp_config;
2265b4c3e9b5SBjoern A. Zeeb 	/* The MFP mode (1 or 2) needs to be determined, parse IEs. The
2266b4c3e9b5SBjoern A. Zeeb 	 * IE will not be verified, just a quick search for MFP config
2267b4c3e9b5SBjoern A. Zeeb 	 */
2268b4c3e9b5SBjoern A. Zeeb 	rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
2269b4c3e9b5SBjoern A. Zeeb 				  WLAN_EID_RSN);
2270b4c3e9b5SBjoern A. Zeeb 	if (!rsn_ie)
2271b4c3e9b5SBjoern A. Zeeb 		goto skip_mfp_config;
2272b4c3e9b5SBjoern A. Zeeb 	ie = (const u8 *)rsn_ie;
2273b4c3e9b5SBjoern A. Zeeb 	ie_len = rsn_ie->len + TLV_HDR_LEN;
2274b4c3e9b5SBjoern A. Zeeb 	/* Skip unicast suite */
2275b4c3e9b5SBjoern A. Zeeb 	offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
2276b4c3e9b5SBjoern A. Zeeb 	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
2277b4c3e9b5SBjoern A. Zeeb 		goto skip_mfp_config;
2278b4c3e9b5SBjoern A. Zeeb 	/* Skip multicast suite */
2279b4c3e9b5SBjoern A. Zeeb 	count = ie[offset] + (ie[offset + 1] << 8);
2280b4c3e9b5SBjoern A. Zeeb 	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
2281b4c3e9b5SBjoern A. Zeeb 	if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
2282b4c3e9b5SBjoern A. Zeeb 		goto skip_mfp_config;
2283b4c3e9b5SBjoern A. Zeeb 	/* Skip auth key management suite(s) */
2284b4c3e9b5SBjoern A. Zeeb 	count = ie[offset] + (ie[offset + 1] << 8);
2285b4c3e9b5SBjoern A. Zeeb 	offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
2286b4c3e9b5SBjoern A. Zeeb 	if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
2287b4c3e9b5SBjoern A. Zeeb 		goto skip_mfp_config;
2288b4c3e9b5SBjoern A. Zeeb 	/* Ready to read capabilities */
2289b4c3e9b5SBjoern A. Zeeb 	mfp = BRCMF_MFP_NONE;
2290b4c3e9b5SBjoern A. Zeeb 	rsn_cap = ie[offset] + (ie[offset + 1] << 8);
2291b4c3e9b5SBjoern A. Zeeb 	if (rsn_cap & RSN_CAP_MFPR_MASK)
2292b4c3e9b5SBjoern A. Zeeb 		mfp = BRCMF_MFP_REQUIRED;
2293b4c3e9b5SBjoern A. Zeeb 	else if (rsn_cap & RSN_CAP_MFPC_MASK)
2294b4c3e9b5SBjoern A. Zeeb 		mfp = BRCMF_MFP_CAPABLE;
2295b4c3e9b5SBjoern A. Zeeb 	brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
2296b4c3e9b5SBjoern A. Zeeb 
2297b4c3e9b5SBjoern A. Zeeb skip_mfp_config:
2298b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
2299b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
2300b4c3e9b5SBjoern A. Zeeb 	if (err) {
2301b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "could not set wpa_auth (%d)\n", err);
2302b4c3e9b5SBjoern A. Zeeb 		return err;
2303b4c3e9b5SBjoern A. Zeeb 	}
2304b4c3e9b5SBjoern A. Zeeb 
2305b4c3e9b5SBjoern A. Zeeb 	return err;
2306b4c3e9b5SBjoern A. Zeeb }
2307b4c3e9b5SBjoern A. Zeeb 
2308b4c3e9b5SBjoern A. Zeeb static s32
brcmf_set_sharedkey(struct net_device * ndev,struct cfg80211_connect_params * sme)2309b4c3e9b5SBjoern A. Zeeb brcmf_set_sharedkey(struct net_device *ndev,
2310b4c3e9b5SBjoern A. Zeeb 		    struct cfg80211_connect_params *sme)
2311b4c3e9b5SBjoern A. Zeeb {
2312b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2313b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2314b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
2315b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_security *sec;
2316b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wsec_key key;
2317b4c3e9b5SBjoern A. Zeeb 	s32 val;
2318b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
2319b4c3e9b5SBjoern A. Zeeb 
2320b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
2321b4c3e9b5SBjoern A. Zeeb 
2322b4c3e9b5SBjoern A. Zeeb 	if (sme->key_len == 0)
2323b4c3e9b5SBjoern A. Zeeb 		return 0;
2324b4c3e9b5SBjoern A. Zeeb 
2325b4c3e9b5SBjoern A. Zeeb 	sec = &profile->sec;
2326b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
2327b4c3e9b5SBjoern A. Zeeb 		  sec->wpa_versions, sec->cipher_pairwise);
2328b4c3e9b5SBjoern A. Zeeb 
2329b4c3e9b5SBjoern A. Zeeb 	if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |
2330b4c3e9b5SBjoern A. Zeeb 				 NL80211_WPA_VERSION_3))
2331b4c3e9b5SBjoern A. Zeeb 		return 0;
2332b4c3e9b5SBjoern A. Zeeb 
2333b4c3e9b5SBjoern A. Zeeb 	if (!(sec->cipher_pairwise &
2334b4c3e9b5SBjoern A. Zeeb 	    (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
2335b4c3e9b5SBjoern A. Zeeb 		return 0;
2336b4c3e9b5SBjoern A. Zeeb 
2337b4c3e9b5SBjoern A. Zeeb 	memset(&key, 0, sizeof(key));
2338b4c3e9b5SBjoern A. Zeeb 	key.len = (u32) sme->key_len;
2339b4c3e9b5SBjoern A. Zeeb 	key.index = (u32) sme->key_idx;
2340b4c3e9b5SBjoern A. Zeeb 	if (key.len > sizeof(key.data)) {
2341b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Too long key length (%u)\n", key.len);
2342b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
2343b4c3e9b5SBjoern A. Zeeb 	}
2344b4c3e9b5SBjoern A. Zeeb 	memcpy(key.data, sme->key, key.len);
2345b4c3e9b5SBjoern A. Zeeb 	key.flags = BRCMF_PRIMARY_KEY;
2346b4c3e9b5SBjoern A. Zeeb 	switch (sec->cipher_pairwise) {
2347b4c3e9b5SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP40:
2348b4c3e9b5SBjoern A. Zeeb 		key.algo = CRYPTO_ALGO_WEP1;
2349b4c3e9b5SBjoern A. Zeeb 		break;
2350b4c3e9b5SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP104:
2351b4c3e9b5SBjoern A. Zeeb 		key.algo = CRYPTO_ALGO_WEP128;
2352b4c3e9b5SBjoern A. Zeeb 		break;
2353b4c3e9b5SBjoern A. Zeeb 	default:
2354b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Invalid algorithm (%d)\n",
2355b4c3e9b5SBjoern A. Zeeb 			 sme->crypto.ciphers_pairwise[0]);
2356b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
2357b4c3e9b5SBjoern A. Zeeb 	}
2358b4c3e9b5SBjoern A. Zeeb 	/* Set the new key/index */
2359b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
2360b4c3e9b5SBjoern A. Zeeb 		  key.len, key.index, key.algo);
2361b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "key \"%s\"\n", key.data);
2362b4c3e9b5SBjoern A. Zeeb 	err = send_key_to_dongle(ifp, &key);
2363b4c3e9b5SBjoern A. Zeeb 	if (err)
2364b4c3e9b5SBjoern A. Zeeb 		return err;
2365b4c3e9b5SBjoern A. Zeeb 
2366b4c3e9b5SBjoern A. Zeeb 	if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
2367b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "set auth_type to shared key\n");
2368b4c3e9b5SBjoern A. Zeeb 		val = WL_AUTH_SHARED_KEY;	/* shared key */
2369b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
2370b4c3e9b5SBjoern A. Zeeb 		if (err)
2371b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "set auth failed (%d)\n", err);
2372b4c3e9b5SBjoern A. Zeeb 	}
2373b4c3e9b5SBjoern A. Zeeb 	return err;
2374b4c3e9b5SBjoern A. Zeeb }
2375b4c3e9b5SBjoern A. Zeeb 
2376b4c3e9b5SBjoern A. Zeeb static
brcmf_war_auth_type(struct brcmf_if * ifp,enum nl80211_auth_type type)2377b4c3e9b5SBjoern A. Zeeb enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
2378b4c3e9b5SBjoern A. Zeeb 					   enum nl80211_auth_type type)
2379b4c3e9b5SBjoern A. Zeeb {
2380b4c3e9b5SBjoern A. Zeeb 	if (type == NL80211_AUTHTYPE_AUTOMATIC &&
2381b4c3e9b5SBjoern A. Zeeb 	    brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
2382b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
2383b4c3e9b5SBjoern A. Zeeb 		type = NL80211_AUTHTYPE_OPEN_SYSTEM;
2384b4c3e9b5SBjoern A. Zeeb 	}
2385b4c3e9b5SBjoern A. Zeeb 	return type;
2386b4c3e9b5SBjoern A. Zeeb }
2387b4c3e9b5SBjoern A. Zeeb 
brcmf_set_join_pref(struct brcmf_if * ifp,struct cfg80211_bss_selection * bss_select)2388b4c3e9b5SBjoern A. Zeeb static void brcmf_set_join_pref(struct brcmf_if *ifp,
2389b4c3e9b5SBjoern A. Zeeb 				struct cfg80211_bss_selection *bss_select)
2390b4c3e9b5SBjoern A. Zeeb {
2391b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2392b4c3e9b5SBjoern A. Zeeb 	struct brcmf_join_pref_params join_pref_params[2];
2393b4c3e9b5SBjoern A. Zeeb 	enum nl80211_band band;
2394b4c3e9b5SBjoern A. Zeeb 	int err, i = 0;
2395b4c3e9b5SBjoern A. Zeeb 
2396b4c3e9b5SBjoern A. Zeeb 	join_pref_params[i].len = 2;
2397b4c3e9b5SBjoern A. Zeeb 	join_pref_params[i].rssi_gain = 0;
2398b4c3e9b5SBjoern A. Zeeb 
2399b4c3e9b5SBjoern A. Zeeb 	if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)
2400b4c3e9b5SBjoern A. Zeeb 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);
2401b4c3e9b5SBjoern A. Zeeb 
2402b4c3e9b5SBjoern A. Zeeb 	switch (bss_select->behaviour) {
2403b4c3e9b5SBjoern A. Zeeb 	case __NL80211_BSS_SELECT_ATTR_INVALID:
2404b4c3e9b5SBjoern A. Zeeb 		brcmf_c_set_joinpref_default(ifp);
2405b4c3e9b5SBjoern A. Zeeb 		return;
2406b4c3e9b5SBjoern A. Zeeb 	case NL80211_BSS_SELECT_ATTR_BAND_PREF:
2407b4c3e9b5SBjoern A. Zeeb 		join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;
2408b4c3e9b5SBjoern A. Zeeb 		band = bss_select->param.band_pref;
2409b4c3e9b5SBjoern A. Zeeb 		join_pref_params[i].band = nl80211_band_to_fwil(band);
2410b4c3e9b5SBjoern A. Zeeb 		i++;
2411b4c3e9b5SBjoern A. Zeeb 		break;
2412b4c3e9b5SBjoern A. Zeeb 	case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:
2413b4c3e9b5SBjoern A. Zeeb 		join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;
2414b4c3e9b5SBjoern A. Zeeb 		band = bss_select->param.adjust.band;
2415b4c3e9b5SBjoern A. Zeeb 		join_pref_params[i].band = nl80211_band_to_fwil(band);
2416b4c3e9b5SBjoern A. Zeeb 		join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;
2417b4c3e9b5SBjoern A. Zeeb 		i++;
2418b4c3e9b5SBjoern A. Zeeb 		break;
2419b4c3e9b5SBjoern A. Zeeb 	case NL80211_BSS_SELECT_ATTR_RSSI:
2420b4c3e9b5SBjoern A. Zeeb 	default:
2421b4c3e9b5SBjoern A. Zeeb 		break;
2422b4c3e9b5SBjoern A. Zeeb 	}
2423b4c3e9b5SBjoern A. Zeeb 	join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;
2424b4c3e9b5SBjoern A. Zeeb 	join_pref_params[i].len = 2;
2425b4c3e9b5SBjoern A. Zeeb 	join_pref_params[i].rssi_gain = 0;
2426b4c3e9b5SBjoern A. Zeeb 	join_pref_params[i].band = 0;
2427b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
2428b4c3e9b5SBjoern A. Zeeb 				       sizeof(join_pref_params));
2429b4c3e9b5SBjoern A. Zeeb 	if (err)
2430b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Set join_pref error (%d)\n", err);
2431b4c3e9b5SBjoern A. Zeeb }
2432b4c3e9b5SBjoern A. Zeeb 
2433b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_connect(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme)2434b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
2435b4c3e9b5SBjoern A. Zeeb 		       struct cfg80211_connect_params *sme)
2436b4c3e9b5SBjoern A. Zeeb {
2437b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2438b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2439b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
2440b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_channel *chan = sme->channel;
2441b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2442b4c3e9b5SBjoern A. Zeeb 	struct brcmf_join_params join_params;
2443b4c3e9b5SBjoern A. Zeeb 	size_t join_params_size;
2444b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *rsn_ie;
2445b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_vs_tlv *wpa_ie;
2446b4c3e9b5SBjoern A. Zeeb 	const void *ie;
2447b4c3e9b5SBjoern A. Zeeb 	u32 ie_len;
2448b4c3e9b5SBjoern A. Zeeb 	struct brcmf_ext_join_params_le *ext_join_params;
2449b4c3e9b5SBjoern A. Zeeb 	u16 chanspec;
2450b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
2451b4c3e9b5SBjoern A. Zeeb 	u32 ssid_len;
2452b4c3e9b5SBjoern A. Zeeb 
2453b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
2454b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
2455b4c3e9b5SBjoern A. Zeeb 		return -EIO;
2456b4c3e9b5SBjoern A. Zeeb 
2457b4c3e9b5SBjoern A. Zeeb 	if (!sme->ssid) {
2458b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Invalid ssid\n");
2459b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
2460b4c3e9b5SBjoern A. Zeeb 	}
2461b4c3e9b5SBjoern A. Zeeb 
2462b4c3e9b5SBjoern A. Zeeb 	if (sme->channel_hint)
2463b4c3e9b5SBjoern A. Zeeb 		chan = sme->channel_hint;
2464b4c3e9b5SBjoern A. Zeeb 
2465b4c3e9b5SBjoern A. Zeeb 	if (sme->bssid_hint)
2466b4c3e9b5SBjoern A. Zeeb 		sme->bssid = sme->bssid_hint;
2467b4c3e9b5SBjoern A. Zeeb 
2468b4c3e9b5SBjoern A. Zeeb 	if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
2469b4c3e9b5SBjoern A. Zeeb 		/* A normal (non P2P) connection request setup. */
2470b4c3e9b5SBjoern A. Zeeb 		ie = NULL;
2471b4c3e9b5SBjoern A. Zeeb 		ie_len = 0;
2472b4c3e9b5SBjoern A. Zeeb 		/* find the WPA_IE */
2473902136e0SBjoern A. Zeeb #if defined(__linux__)
2474b4c3e9b5SBjoern A. Zeeb 		wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
2475902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
2476902136e0SBjoern A. Zeeb 		wpa_ie = brcmf_find_wpaie(sme->ie, sme->ie_len);
2477902136e0SBjoern A. Zeeb #endif
2478b4c3e9b5SBjoern A. Zeeb 		if (wpa_ie) {
2479b4c3e9b5SBjoern A. Zeeb 			ie = wpa_ie;
2480b4c3e9b5SBjoern A. Zeeb 			ie_len = wpa_ie->len + TLV_HDR_LEN;
2481b4c3e9b5SBjoern A. Zeeb 		} else {
2482b4c3e9b5SBjoern A. Zeeb 			/* find the RSN_IE */
2483b4c3e9b5SBjoern A. Zeeb 			rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
2484b4c3e9b5SBjoern A. Zeeb 						  sme->ie_len,
2485b4c3e9b5SBjoern A. Zeeb 						  WLAN_EID_RSN);
2486b4c3e9b5SBjoern A. Zeeb 			if (rsn_ie) {
2487b4c3e9b5SBjoern A. Zeeb 				ie = rsn_ie;
2488b4c3e9b5SBjoern A. Zeeb 				ie_len = rsn_ie->len + TLV_HDR_LEN;
2489b4c3e9b5SBjoern A. Zeeb 			}
2490b4c3e9b5SBjoern A. Zeeb 		}
2491b4c3e9b5SBjoern A. Zeeb 		brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
2492b4c3e9b5SBjoern A. Zeeb 	}
2493b4c3e9b5SBjoern A. Zeeb 
2494b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
2495b4c3e9b5SBjoern A. Zeeb 				    sme->ie, sme->ie_len);
2496b4c3e9b5SBjoern A. Zeeb 	if (err)
2497b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Set Assoc REQ IE Failed\n");
2498b4c3e9b5SBjoern A. Zeeb 	else
2499b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
2500b4c3e9b5SBjoern A. Zeeb 
2501b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
2502b4c3e9b5SBjoern A. Zeeb 
2503b4c3e9b5SBjoern A. Zeeb 	if (chan) {
2504b4c3e9b5SBjoern A. Zeeb 		cfg->channel =
2505b4c3e9b5SBjoern A. Zeeb 			ieee80211_frequency_to_channel(chan->center_freq);
2506b4c3e9b5SBjoern A. Zeeb 		chanspec = channel_to_chanspec(&cfg->d11inf, chan);
2507b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
2508b4c3e9b5SBjoern A. Zeeb 			  cfg->channel, chan->center_freq, chanspec);
2509b4c3e9b5SBjoern A. Zeeb 	} else {
2510b4c3e9b5SBjoern A. Zeeb 		cfg->channel = 0;
2511b4c3e9b5SBjoern A. Zeeb 		chanspec = 0;
2512b4c3e9b5SBjoern A. Zeeb 	}
2513b4c3e9b5SBjoern A. Zeeb 
2514902136e0SBjoern A. Zeeb #if defined(__linux__)
2515b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
2516902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
2517902136e0SBjoern A. Zeeb 	brcmf_dbg(INFO, "ie (%p), ie_len (%u)\n", sme->ie, sme->ie_len);
2518902136e0SBjoern A. Zeeb #endif
2519b4c3e9b5SBjoern A. Zeeb 
2520b4c3e9b5SBjoern A. Zeeb 	err = brcmf_set_wpa_version(ndev, sme);
2521b4c3e9b5SBjoern A. Zeeb 	if (err) {
2522b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wl_set_wpa_version failed (%d)\n", err);
2523b4c3e9b5SBjoern A. Zeeb 		goto done;
2524b4c3e9b5SBjoern A. Zeeb 	}
2525b4c3e9b5SBjoern A. Zeeb 
2526b4c3e9b5SBjoern A. Zeeb 	sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
2527b4c3e9b5SBjoern A. Zeeb 	err = brcmf_set_auth_type(ndev, sme);
2528b4c3e9b5SBjoern A. Zeeb 	if (err) {
2529b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wl_set_auth_type failed (%d)\n", err);
2530b4c3e9b5SBjoern A. Zeeb 		goto done;
2531b4c3e9b5SBjoern A. Zeeb 	}
2532b4c3e9b5SBjoern A. Zeeb 
2533b4c3e9b5SBjoern A. Zeeb 	err = brcmf_set_wsec_mode(ndev, sme);
2534b4c3e9b5SBjoern A. Zeeb 	if (err) {
2535b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wl_set_set_cipher failed (%d)\n", err);
2536b4c3e9b5SBjoern A. Zeeb 		goto done;
2537b4c3e9b5SBjoern A. Zeeb 	}
2538b4c3e9b5SBjoern A. Zeeb 
2539b4c3e9b5SBjoern A. Zeeb 	err = brcmf_set_key_mgmt(ndev, sme);
2540b4c3e9b5SBjoern A. Zeeb 	if (err) {
2541b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wl_set_key_mgmt failed (%d)\n", err);
2542b4c3e9b5SBjoern A. Zeeb 		goto done;
2543b4c3e9b5SBjoern A. Zeeb 	}
2544b4c3e9b5SBjoern A. Zeeb 
2545b4c3e9b5SBjoern A. Zeeb 	err = brcmf_set_sharedkey(ndev, sme);
2546b4c3e9b5SBjoern A. Zeeb 	if (err) {
2547b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "brcmf_set_sharedkey failed (%d)\n", err);
2548b4c3e9b5SBjoern A. Zeeb 		goto done;
2549b4c3e9b5SBjoern A. Zeeb 	}
2550b4c3e9b5SBjoern A. Zeeb 
2551b4c3e9b5SBjoern A. Zeeb 	if (sme->crypto.psk &&
2552b4c3e9b5SBjoern A. Zeeb 	    profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
2553b4c3e9b5SBjoern A. Zeeb 		if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
2554b4c3e9b5SBjoern A. Zeeb 			err = -EINVAL;
2555b4c3e9b5SBjoern A. Zeeb 			goto done;
2556b4c3e9b5SBjoern A. Zeeb 		}
2557b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "using PSK offload\n");
2558b4c3e9b5SBjoern A. Zeeb 		profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
2559b4c3e9b5SBjoern A. Zeeb 	}
2560b4c3e9b5SBjoern A. Zeeb 
2561b4c3e9b5SBjoern A. Zeeb 	if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
2562b4c3e9b5SBjoern A. Zeeb 		/* enable firmware supplicant for this interface */
2563b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1);
2564b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
2565b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "failed to enable fw supplicant\n");
2566b4c3e9b5SBjoern A. Zeeb 			goto done;
2567b4c3e9b5SBjoern A. Zeeb 		}
2568b4c3e9b5SBjoern A. Zeeb 	}
2569b4c3e9b5SBjoern A. Zeeb 
2570b4c3e9b5SBjoern A. Zeeb 	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)
2571b4c3e9b5SBjoern A. Zeeb 		err = brcmf_set_pmk(ifp, sme->crypto.psk,
2572b4c3e9b5SBjoern A. Zeeb 				    BRCMF_WSEC_MAX_PSK_LEN);
2573b4c3e9b5SBjoern A. Zeeb 	else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {
2574b4c3e9b5SBjoern A. Zeeb 		/* clean up user-space RSNE */
2575b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0);
2576b4c3e9b5SBjoern A. Zeeb 		if (err) {
2577b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "failed to clean up user-space RSNE\n");
2578b4c3e9b5SBjoern A. Zeeb 			goto done;
2579b4c3e9b5SBjoern A. Zeeb 		}
2580b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fwvid_set_sae_password(ifp, &sme->crypto);
2581b4c3e9b5SBjoern A. Zeeb 		if (!err && sme->crypto.psk)
2582b4c3e9b5SBjoern A. Zeeb 			err = brcmf_set_pmk(ifp, sme->crypto.psk,
2583b4c3e9b5SBjoern A. Zeeb 					    BRCMF_WSEC_MAX_PSK_LEN);
2584b4c3e9b5SBjoern A. Zeeb 	}
2585b4c3e9b5SBjoern A. Zeeb 	if (err)
2586b4c3e9b5SBjoern A. Zeeb 		goto done;
2587b4c3e9b5SBjoern A. Zeeb 
2588b4c3e9b5SBjoern A. Zeeb 	/* Join with specific BSSID and cached SSID
2589b4c3e9b5SBjoern A. Zeeb 	 * If SSID is zero join based on BSSID only
2590b4c3e9b5SBjoern A. Zeeb 	 */
2591b4c3e9b5SBjoern A. Zeeb 	join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
2592b4c3e9b5SBjoern A. Zeeb 		offsetof(struct brcmf_assoc_params_le, chanspec_list);
2593b4c3e9b5SBjoern A. Zeeb 	if (cfg->channel)
2594b4c3e9b5SBjoern A. Zeeb 		join_params_size += sizeof(u16);
2595b4c3e9b5SBjoern A. Zeeb 	ext_join_params = kzalloc(sizeof(*ext_join_params), GFP_KERNEL);
2596b4c3e9b5SBjoern A. Zeeb 	if (ext_join_params == NULL) {
2597b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
2598b4c3e9b5SBjoern A. Zeeb 		goto done;
2599b4c3e9b5SBjoern A. Zeeb 	}
2600b4c3e9b5SBjoern A. Zeeb 	ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
2601b4c3e9b5SBjoern A. Zeeb 	ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
2602b4c3e9b5SBjoern A. Zeeb 	memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
2603b4c3e9b5SBjoern A. Zeeb 	if (ssid_len < IEEE80211_MAX_SSID_LEN)
2604b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
2605b4c3e9b5SBjoern A. Zeeb 			  ext_join_params->ssid_le.SSID, ssid_len);
2606b4c3e9b5SBjoern A. Zeeb 
2607b4c3e9b5SBjoern A. Zeeb 	/* Set up join scan parameters */
2608b4c3e9b5SBjoern A. Zeeb 	ext_join_params->scan_le.scan_type = -1;
2609b4c3e9b5SBjoern A. Zeeb 	ext_join_params->scan_le.home_time = cpu_to_le32(-1);
2610b4c3e9b5SBjoern A. Zeeb 
2611b4c3e9b5SBjoern A. Zeeb 	if (sme->bssid)
2612b4c3e9b5SBjoern A. Zeeb 		memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
2613b4c3e9b5SBjoern A. Zeeb 	else
2614b4c3e9b5SBjoern A. Zeeb 		eth_broadcast_addr(ext_join_params->assoc_le.bssid);
2615b4c3e9b5SBjoern A. Zeeb 
2616b4c3e9b5SBjoern A. Zeeb 	if (cfg->channel) {
2617b4c3e9b5SBjoern A. Zeeb 		ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
2618b4c3e9b5SBjoern A. Zeeb 
2619b4c3e9b5SBjoern A. Zeeb 		ext_join_params->assoc_le.chanspec_list[0] =
2620b4c3e9b5SBjoern A. Zeeb 			cpu_to_le16(chanspec);
2621b4c3e9b5SBjoern A. Zeeb 		/* Increase dwell time to receive probe response or detect
2622b4c3e9b5SBjoern A. Zeeb 		 * beacon from target AP at a noisy air only during connect
2623b4c3e9b5SBjoern A. Zeeb 		 * command.
2624b4c3e9b5SBjoern A. Zeeb 		 */
2625b4c3e9b5SBjoern A. Zeeb 		ext_join_params->scan_le.active_time =
2626b4c3e9b5SBjoern A. Zeeb 			cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
2627b4c3e9b5SBjoern A. Zeeb 		ext_join_params->scan_le.passive_time =
2628b4c3e9b5SBjoern A. Zeeb 			cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
2629b4c3e9b5SBjoern A. Zeeb 		/* To sync with presence period of VSDB GO send probe request
2630b4c3e9b5SBjoern A. Zeeb 		 * more frequently. Probe request will be stopped when it gets
2631b4c3e9b5SBjoern A. Zeeb 		 * probe response from target AP/GO.
2632b4c3e9b5SBjoern A. Zeeb 		 */
2633b4c3e9b5SBjoern A. Zeeb 		ext_join_params->scan_le.nprobes =
2634b4c3e9b5SBjoern A. Zeeb 			cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
2635b4c3e9b5SBjoern A. Zeeb 				    BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
2636b4c3e9b5SBjoern A. Zeeb 	} else {
2637b4c3e9b5SBjoern A. Zeeb 		ext_join_params->scan_le.active_time = cpu_to_le32(-1);
2638b4c3e9b5SBjoern A. Zeeb 		ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
2639b4c3e9b5SBjoern A. Zeeb 		ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
2640b4c3e9b5SBjoern A. Zeeb 	}
2641b4c3e9b5SBjoern A. Zeeb 
2642b4c3e9b5SBjoern A. Zeeb 	brcmf_set_join_pref(ifp, &sme->bss_select);
2643b4c3e9b5SBjoern A. Zeeb 
2644b4c3e9b5SBjoern A. Zeeb 	err  = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
2645b4c3e9b5SBjoern A. Zeeb 					 join_params_size);
2646b4c3e9b5SBjoern A. Zeeb 	kfree(ext_join_params);
2647b4c3e9b5SBjoern A. Zeeb 	if (!err)
2648b4c3e9b5SBjoern A. Zeeb 		/* This is it. join command worked, we are done */
2649b4c3e9b5SBjoern A. Zeeb 		goto done;
2650b4c3e9b5SBjoern A. Zeeb 
2651b4c3e9b5SBjoern A. Zeeb 	/* join command failed, fallback to set ssid */
2652b4c3e9b5SBjoern A. Zeeb 	memset(&join_params, 0, sizeof(join_params));
2653b4c3e9b5SBjoern A. Zeeb 	join_params_size = sizeof(join_params.ssid_le);
2654b4c3e9b5SBjoern A. Zeeb 
2655b4c3e9b5SBjoern A. Zeeb 	memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
2656b4c3e9b5SBjoern A. Zeeb 	join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
2657b4c3e9b5SBjoern A. Zeeb 
2658b4c3e9b5SBjoern A. Zeeb 	if (sme->bssid)
2659b4c3e9b5SBjoern A. Zeeb 		memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
2660b4c3e9b5SBjoern A. Zeeb 	else
2661b4c3e9b5SBjoern A. Zeeb 		eth_broadcast_addr(join_params.params_le.bssid);
2662b4c3e9b5SBjoern A. Zeeb 
2663b4c3e9b5SBjoern A. Zeeb 	if (cfg->channel) {
2664b4c3e9b5SBjoern A. Zeeb 		join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
2665b4c3e9b5SBjoern A. Zeeb 		join_params.params_le.chanspec_num = cpu_to_le32(1);
2666b4c3e9b5SBjoern A. Zeeb 		join_params_size += sizeof(join_params.params_le);
2667b4c3e9b5SBjoern A. Zeeb 	}
2668b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
2669b4c3e9b5SBjoern A. Zeeb 				     &join_params, join_params_size);
2670b4c3e9b5SBjoern A. Zeeb 	if (err)
2671b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "BRCMF_C_SET_SSID failed (%d)\n", err);
2672b4c3e9b5SBjoern A. Zeeb 
2673b4c3e9b5SBjoern A. Zeeb done:
2674b4c3e9b5SBjoern A. Zeeb 	if (err)
2675b4c3e9b5SBjoern A. Zeeb 		clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
2676b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
2677b4c3e9b5SBjoern A. Zeeb 	return err;
2678b4c3e9b5SBjoern A. Zeeb }
2679b4c3e9b5SBjoern A. Zeeb 
2680b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_disconnect(struct wiphy * wiphy,struct net_device * ndev,u16 reason_code)2681b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
2682b4c3e9b5SBjoern A. Zeeb 		       u16 reason_code)
2683b4c3e9b5SBjoern A. Zeeb {
2684b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2685b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2686b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
2687b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
2688b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scb_val_le scbval;
2689b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
2690b4c3e9b5SBjoern A. Zeeb 
2691b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
2692b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
2693b4c3e9b5SBjoern A. Zeeb 		return -EIO;
2694b4c3e9b5SBjoern A. Zeeb 
2695b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
2696b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
2697b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state);
2698b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state);
2699b4c3e9b5SBjoern A. Zeeb 	cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
2700b4c3e9b5SBjoern A. Zeeb 
2701b4c3e9b5SBjoern A. Zeeb 	memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
2702b4c3e9b5SBjoern A. Zeeb 	scbval.val = cpu_to_le32(reason_code);
2703b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
2704b4c3e9b5SBjoern A. Zeeb 				     &scbval, sizeof(scbval));
2705b4c3e9b5SBjoern A. Zeeb 	if (err)
2706b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "error (%d)\n", err);
2707b4c3e9b5SBjoern A. Zeeb 
2708b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
2709b4c3e9b5SBjoern A. Zeeb 	return err;
2710b4c3e9b5SBjoern A. Zeeb }
2711b4c3e9b5SBjoern A. Zeeb 
2712b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_set_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,int radio_idx,enum nl80211_tx_power_setting type,s32 mbm)2713b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2714b4c3e9b5SBjoern A. Zeeb 			    int radio_idx, enum nl80211_tx_power_setting type,
2715b4c3e9b5SBjoern A. Zeeb 			    s32 mbm)
2716b4c3e9b5SBjoern A. Zeeb {
2717b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2718b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = cfg_to_ndev(cfg);
2719b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2720b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
2721b4c3e9b5SBjoern A. Zeeb 	s32 err;
2722b4c3e9b5SBjoern A. Zeeb 	s32 disable;
2723b4c3e9b5SBjoern A. Zeeb 	u32 qdbm = 127;
2724b4c3e9b5SBjoern A. Zeeb 
2725b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
2726b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
2727b4c3e9b5SBjoern A. Zeeb 		return -EIO;
2728b4c3e9b5SBjoern A. Zeeb 
2729b4c3e9b5SBjoern A. Zeeb 	switch (type) {
2730b4c3e9b5SBjoern A. Zeeb 	case NL80211_TX_POWER_AUTOMATIC:
2731b4c3e9b5SBjoern A. Zeeb 		break;
2732b4c3e9b5SBjoern A. Zeeb 	case NL80211_TX_POWER_LIMITED:
2733b4c3e9b5SBjoern A. Zeeb 	case NL80211_TX_POWER_FIXED:
2734b4c3e9b5SBjoern A. Zeeb 		if (mbm < 0) {
2735b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "TX_POWER_FIXED - dbm is negative\n");
2736b4c3e9b5SBjoern A. Zeeb 			err = -EINVAL;
2737b4c3e9b5SBjoern A. Zeeb 			goto done;
2738b4c3e9b5SBjoern A. Zeeb 		}
2739b4c3e9b5SBjoern A. Zeeb 		qdbm =  MBM_TO_DBM(4 * mbm);
2740b4c3e9b5SBjoern A. Zeeb 		if (qdbm > 127)
2741b4c3e9b5SBjoern A. Zeeb 			qdbm = 127;
2742b4c3e9b5SBjoern A. Zeeb 		qdbm |= WL_TXPWR_OVERRIDE;
2743b4c3e9b5SBjoern A. Zeeb 		break;
2744b4c3e9b5SBjoern A. Zeeb 	default:
2745b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Unsupported type %d\n", type);
2746b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
2747b4c3e9b5SBjoern A. Zeeb 		goto done;
2748b4c3e9b5SBjoern A. Zeeb 	}
2749b4c3e9b5SBjoern A. Zeeb 	/* Make sure radio is off or on as far as software is concerned */
2750b4c3e9b5SBjoern A. Zeeb 	disable = WL_RADIO_SW_DISABLE << 16;
2751b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
2752b4c3e9b5SBjoern A. Zeeb 	if (err)
2753b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_SET_RADIO error (%d)\n", err);
2754b4c3e9b5SBjoern A. Zeeb 
2755b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
2756b4c3e9b5SBjoern A. Zeeb 	if (err)
2757b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "qtxpower error (%d)\n", err);
2758b4c3e9b5SBjoern A. Zeeb 
2759b4c3e9b5SBjoern A. Zeeb done:
2760b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
2761b4c3e9b5SBjoern A. Zeeb 	return err;
2762b4c3e9b5SBjoern A. Zeeb }
2763b4c3e9b5SBjoern A. Zeeb 
2764b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_get_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,int radio_idx,unsigned int link_id,s32 * dbm)2765b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2766b4c3e9b5SBjoern A. Zeeb 			    int radio_idx, unsigned int link_id, s32 *dbm)
2767b4c3e9b5SBjoern A. Zeeb {
2768b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2769b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif = wdev_to_vif(wdev);
2770b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
2771b4c3e9b5SBjoern A. Zeeb 	s32 qdbm;
2772b4c3e9b5SBjoern A. Zeeb 	s32 err;
2773b4c3e9b5SBjoern A. Zeeb 
2774b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
2775b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(vif))
2776b4c3e9b5SBjoern A. Zeeb 		return -EIO;
2777b4c3e9b5SBjoern A. Zeeb 
2778b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_get(vif->ifp, "qtxpower", &qdbm);
2779b4c3e9b5SBjoern A. Zeeb 	if (err) {
2780b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "error (%d)\n", err);
2781b4c3e9b5SBjoern A. Zeeb 		goto done;
2782b4c3e9b5SBjoern A. Zeeb 	}
2783b4c3e9b5SBjoern A. Zeeb 	*dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
2784b4c3e9b5SBjoern A. Zeeb 
2785b4c3e9b5SBjoern A. Zeeb done:
2786b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
2787b4c3e9b5SBjoern A. Zeeb 	return err;
2788b4c3e9b5SBjoern A. Zeeb }
2789b4c3e9b5SBjoern A. Zeeb 
2790b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_config_default_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool unicast,bool multicast)2791b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
2792b4c3e9b5SBjoern A. Zeeb 				  int link_id, u8 key_idx, bool unicast,
2793b4c3e9b5SBjoern A. Zeeb 				  bool multicast)
2794b4c3e9b5SBjoern A. Zeeb {
2795b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2796b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
2797b4c3e9b5SBjoern A. Zeeb 	u32 index;
2798b4c3e9b5SBjoern A. Zeeb 	u32 wsec;
2799b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
2800b4c3e9b5SBjoern A. Zeeb 
2801b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
2802b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
2803b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
2804b4c3e9b5SBjoern A. Zeeb 		return -EIO;
2805b4c3e9b5SBjoern A. Zeeb 
2806b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2807b4c3e9b5SBjoern A. Zeeb 	if (err) {
2808b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
2809b4c3e9b5SBjoern A. Zeeb 		goto done;
2810b4c3e9b5SBjoern A. Zeeb 	}
2811b4c3e9b5SBjoern A. Zeeb 
2812b4c3e9b5SBjoern A. Zeeb 	if (wsec & WEP_ENABLED) {
2813b4c3e9b5SBjoern A. Zeeb 		/* Just select a new current key */
2814b4c3e9b5SBjoern A. Zeeb 		index = key_idx;
2815b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp,
2816b4c3e9b5SBjoern A. Zeeb 					    BRCMF_C_SET_KEY_PRIMARY, index);
2817b4c3e9b5SBjoern A. Zeeb 		if (err)
2818b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "error (%d)\n", err);
2819b4c3e9b5SBjoern A. Zeeb 	}
2820b4c3e9b5SBjoern A. Zeeb done:
2821b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
2822b4c3e9b5SBjoern A. Zeeb 	return err;
2823b4c3e9b5SBjoern A. Zeeb }
2824b4c3e9b5SBjoern A. Zeeb 
2825b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_del_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr)2826b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2827b4c3e9b5SBjoern A. Zeeb 		       int link_id, u8 key_idx, bool pairwise,
2828b4c3e9b5SBjoern A. Zeeb 		       const u8 *mac_addr)
2829b4c3e9b5SBjoern A. Zeeb {
2830b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2831b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wsec_key *key;
2832b4c3e9b5SBjoern A. Zeeb 	s32 err;
2833b4c3e9b5SBjoern A. Zeeb 
2834b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
2835b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
2836b4c3e9b5SBjoern A. Zeeb 
2837b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
2838b4c3e9b5SBjoern A. Zeeb 		return -EIO;
2839b4c3e9b5SBjoern A. Zeeb 
2840b4c3e9b5SBjoern A. Zeeb 	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2841b4c3e9b5SBjoern A. Zeeb 		/* we ignore this key index in this case */
2842b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
2843b4c3e9b5SBjoern A. Zeeb 	}
2844b4c3e9b5SBjoern A. Zeeb 
2845b4c3e9b5SBjoern A. Zeeb 	key = &ifp->vif->profile.key[key_idx];
2846b4c3e9b5SBjoern A. Zeeb 
2847b4c3e9b5SBjoern A. Zeeb 	if (key->algo == CRYPTO_ALGO_OFF) {
2848b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
2849b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
2850b4c3e9b5SBjoern A. Zeeb 	}
2851b4c3e9b5SBjoern A. Zeeb 
2852b4c3e9b5SBjoern A. Zeeb 	memset(key, 0, sizeof(*key));
2853b4c3e9b5SBjoern A. Zeeb 	key->index = (u32)key_idx;
2854b4c3e9b5SBjoern A. Zeeb 	key->flags = BRCMF_PRIMARY_KEY;
2855b4c3e9b5SBjoern A. Zeeb 
2856b4c3e9b5SBjoern A. Zeeb 	/* Clear the key/index */
2857b4c3e9b5SBjoern A. Zeeb 	err = send_key_to_dongle(ifp, key);
2858b4c3e9b5SBjoern A. Zeeb 
2859b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
2860b4c3e9b5SBjoern A. Zeeb 	return err;
2861b4c3e9b5SBjoern A. Zeeb }
2862b4c3e9b5SBjoern A. Zeeb 
2863b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_add_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,struct key_params * params)2864b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2865b4c3e9b5SBjoern A. Zeeb 		       int link_id, u8 key_idx, bool pairwise,
2866b4c3e9b5SBjoern A. Zeeb 		       const u8 *mac_addr, struct key_params *params)
2867b4c3e9b5SBjoern A. Zeeb {
2868b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2869b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
2870b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
2871b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wsec_key *key;
2872b4c3e9b5SBjoern A. Zeeb 	s32 val;
2873b4c3e9b5SBjoern A. Zeeb 	s32 wsec;
2874b4c3e9b5SBjoern A. Zeeb 	s32 err;
2875b4c3e9b5SBjoern A. Zeeb 	u8 keybuf[8];
2876b4c3e9b5SBjoern A. Zeeb 	bool ext_key;
2877b4c3e9b5SBjoern A. Zeeb 
2878b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
2879b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
2880b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
2881b4c3e9b5SBjoern A. Zeeb 		return -EIO;
2882b4c3e9b5SBjoern A. Zeeb 
2883b4c3e9b5SBjoern A. Zeeb 	if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2884b4c3e9b5SBjoern A. Zeeb 		/* we ignore this key index in this case */
2885b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "invalid key index (%d)\n", key_idx);
2886b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
2887b4c3e9b5SBjoern A. Zeeb 	}
2888b4c3e9b5SBjoern A. Zeeb 
2889b4c3e9b5SBjoern A. Zeeb 	if (params->key_len == 0)
2890b4c3e9b5SBjoern A. Zeeb 		return brcmf_cfg80211_del_key(wiphy, ndev, -1, key_idx,
2891b4c3e9b5SBjoern A. Zeeb 					      pairwise, mac_addr);
2892b4c3e9b5SBjoern A. Zeeb 
2893b4c3e9b5SBjoern A. Zeeb 	if (params->key_len > sizeof(key->data)) {
2894b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Too long key length (%u)\n", params->key_len);
2895b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
2896b4c3e9b5SBjoern A. Zeeb 	}
2897b4c3e9b5SBjoern A. Zeeb 
2898b4c3e9b5SBjoern A. Zeeb 	ext_key = false;
2899b4c3e9b5SBjoern A. Zeeb 	if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2900b4c3e9b5SBjoern A. Zeeb 	    (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
2901b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
2902b4c3e9b5SBjoern A. Zeeb 		ext_key = true;
2903b4c3e9b5SBjoern A. Zeeb 	}
2904b4c3e9b5SBjoern A. Zeeb 
2905b4c3e9b5SBjoern A. Zeeb 	key = &ifp->vif->profile.key[key_idx];
2906b4c3e9b5SBjoern A. Zeeb 	memset(key, 0, sizeof(*key));
2907b4c3e9b5SBjoern A. Zeeb 	if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
2908902136e0SBjoern A. Zeeb #if defined(__linux__)
2909b4c3e9b5SBjoern A. Zeeb 		memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
2910902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
2911902136e0SBjoern A. Zeeb 		memcpy((char *)&key->ea, mac_addr, ETH_ALEN);
2912902136e0SBjoern A. Zeeb #endif
2913b4c3e9b5SBjoern A. Zeeb 	key->len = params->key_len;
2914b4c3e9b5SBjoern A. Zeeb 	key->index = key_idx;
2915b4c3e9b5SBjoern A. Zeeb 	memcpy(key->data, params->key, key->len);
2916b4c3e9b5SBjoern A. Zeeb 	if (!ext_key)
2917b4c3e9b5SBjoern A. Zeeb 		key->flags = BRCMF_PRIMARY_KEY;
2918b4c3e9b5SBjoern A. Zeeb 
2919b4c3e9b5SBjoern A. Zeeb 	if (params->seq && params->seq_len == 6) {
2920b4c3e9b5SBjoern A. Zeeb 		/* rx iv */
2921902136e0SBjoern A. Zeeb #if defined(__linux__)
2922b4c3e9b5SBjoern A. Zeeb 		u8 *ivptr;
2923b4c3e9b5SBjoern A. Zeeb 
2924b4c3e9b5SBjoern A. Zeeb 		ivptr = (u8 *)params->seq;
2925902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
2926902136e0SBjoern A. Zeeb 		const u8 *ivptr;
2927902136e0SBjoern A. Zeeb 
2928902136e0SBjoern A. Zeeb 		ivptr = params->seq;
2929902136e0SBjoern A. Zeeb #endif
2930b4c3e9b5SBjoern A. Zeeb 		key->rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2931b4c3e9b5SBjoern A. Zeeb 			(ivptr[3] << 8) | ivptr[2];
2932b4c3e9b5SBjoern A. Zeeb 		key->rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2933b4c3e9b5SBjoern A. Zeeb 		key->iv_initialized = true;
2934b4c3e9b5SBjoern A. Zeeb 	}
2935b4c3e9b5SBjoern A. Zeeb 
2936b4c3e9b5SBjoern A. Zeeb 	switch (params->cipher) {
2937b4c3e9b5SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP40:
2938b4c3e9b5SBjoern A. Zeeb 		key->algo = CRYPTO_ALGO_WEP1;
2939b4c3e9b5SBjoern A. Zeeb 		val = WEP_ENABLED;
2940b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
2941b4c3e9b5SBjoern A. Zeeb 		break;
2942b4c3e9b5SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP104:
2943b4c3e9b5SBjoern A. Zeeb 		key->algo = CRYPTO_ALGO_WEP128;
2944b4c3e9b5SBjoern A. Zeeb 		val = WEP_ENABLED;
2945b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
2946b4c3e9b5SBjoern A. Zeeb 		break;
2947b4c3e9b5SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_TKIP:
2948b4c3e9b5SBjoern A. Zeeb 		if (!brcmf_is_apmode(ifp->vif)) {
2949b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
2950b4c3e9b5SBjoern A. Zeeb 			memcpy(keybuf, &key->data[24], sizeof(keybuf));
2951b4c3e9b5SBjoern A. Zeeb 			memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2952b4c3e9b5SBjoern A. Zeeb 			memcpy(&key->data[16], keybuf, sizeof(keybuf));
2953b4c3e9b5SBjoern A. Zeeb 		}
2954b4c3e9b5SBjoern A. Zeeb 		key->algo = CRYPTO_ALGO_TKIP;
2955b4c3e9b5SBjoern A. Zeeb 		val = TKIP_ENABLED;
2956b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
2957b4c3e9b5SBjoern A. Zeeb 		break;
2958b4c3e9b5SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_AES_CMAC:
2959b4c3e9b5SBjoern A. Zeeb 		key->algo = CRYPTO_ALGO_AES_CCM;
2960b4c3e9b5SBjoern A. Zeeb 		val = AES_ENABLED;
2961b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
2962b4c3e9b5SBjoern A. Zeeb 		break;
2963b4c3e9b5SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP:
2964b4c3e9b5SBjoern A. Zeeb 		key->algo = CRYPTO_ALGO_AES_CCM;
2965b4c3e9b5SBjoern A. Zeeb 		val = AES_ENABLED;
2966b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
2967b4c3e9b5SBjoern A. Zeeb 		break;
2968b4c3e9b5SBjoern A. Zeeb 	default:
2969b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher);
2970b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
2971b4c3e9b5SBjoern A. Zeeb 		goto done;
2972b4c3e9b5SBjoern A. Zeeb 	}
2973b4c3e9b5SBjoern A. Zeeb 
2974b4c3e9b5SBjoern A. Zeeb 	err = send_key_to_dongle(ifp, key);
2975b4c3e9b5SBjoern A. Zeeb 	if (ext_key || err)
2976b4c3e9b5SBjoern A. Zeeb 		goto done;
2977b4c3e9b5SBjoern A. Zeeb 
2978b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2979b4c3e9b5SBjoern A. Zeeb 	if (err) {
2980b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "get wsec error (%d)\n", err);
2981b4c3e9b5SBjoern A. Zeeb 		goto done;
2982b4c3e9b5SBjoern A. Zeeb 	}
2983b4c3e9b5SBjoern A. Zeeb 	wsec |= val;
2984b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2985b4c3e9b5SBjoern A. Zeeb 	if (err) {
2986b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "set wsec error (%d)\n", err);
2987b4c3e9b5SBjoern A. Zeeb 		goto done;
2988b4c3e9b5SBjoern A. Zeeb 	}
2989b4c3e9b5SBjoern A. Zeeb 
2990b4c3e9b5SBjoern A. Zeeb done:
2991b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
2992b4c3e9b5SBjoern A. Zeeb 	return err;
2993b4c3e9b5SBjoern A. Zeeb }
2994b4c3e9b5SBjoern A. Zeeb 
2995b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_get_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,void * cookie,void (* callback)(void * cookie,struct key_params * params))2996b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2997b4c3e9b5SBjoern A. Zeeb 		       int link_id, u8 key_idx, bool pairwise,
2998b4c3e9b5SBjoern A. Zeeb 		       const u8 *mac_addr, void *cookie,
2999b4c3e9b5SBjoern A. Zeeb 		       void (*callback)(void *cookie,
3000b4c3e9b5SBjoern A. Zeeb 					struct key_params *params))
3001b4c3e9b5SBjoern A. Zeeb {
3002b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3003b4c3e9b5SBjoern A. Zeeb 	struct key_params params;
3004b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
3005b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
3006b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3007b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_security *sec;
3008b4c3e9b5SBjoern A. Zeeb 	s32 wsec;
3009b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
3010b4c3e9b5SBjoern A. Zeeb 
3011b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
3012b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "key index (%d)\n", key_idx);
3013b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
3014b4c3e9b5SBjoern A. Zeeb 		return -EIO;
3015b4c3e9b5SBjoern A. Zeeb 
3016b4c3e9b5SBjoern A. Zeeb 	memset(&params, 0, sizeof(params));
3017b4c3e9b5SBjoern A. Zeeb 
3018b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
3019b4c3e9b5SBjoern A. Zeeb 	if (err) {
3020b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
3021b4c3e9b5SBjoern A. Zeeb 		/* Ignore this error, may happen during DISASSOC */
3022b4c3e9b5SBjoern A. Zeeb 		err = -EAGAIN;
3023b4c3e9b5SBjoern A. Zeeb 		goto done;
3024b4c3e9b5SBjoern A. Zeeb 	}
3025b4c3e9b5SBjoern A. Zeeb 	if (wsec & WEP_ENABLED) {
3026b4c3e9b5SBjoern A. Zeeb 		sec = &profile->sec;
3027b4c3e9b5SBjoern A. Zeeb 		if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
3028b4c3e9b5SBjoern A. Zeeb 			params.cipher = WLAN_CIPHER_SUITE_WEP40;
3029b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
3030b4c3e9b5SBjoern A. Zeeb 		} else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
3031b4c3e9b5SBjoern A. Zeeb 			params.cipher = WLAN_CIPHER_SUITE_WEP104;
3032b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
3033b4c3e9b5SBjoern A. Zeeb 		}
3034b4c3e9b5SBjoern A. Zeeb 	} else if (wsec & TKIP_ENABLED) {
3035b4c3e9b5SBjoern A. Zeeb 		params.cipher = WLAN_CIPHER_SUITE_TKIP;
3036b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
3037b4c3e9b5SBjoern A. Zeeb 	} else if (wsec & AES_ENABLED) {
3038b4c3e9b5SBjoern A. Zeeb 		params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
3039b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
3040b4c3e9b5SBjoern A. Zeeb 	} else  {
3041b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Invalid algo (0x%x)\n", wsec);
3042b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
3043b4c3e9b5SBjoern A. Zeeb 		goto done;
3044b4c3e9b5SBjoern A. Zeeb 	}
3045b4c3e9b5SBjoern A. Zeeb 	callback(cookie, &params);
3046b4c3e9b5SBjoern A. Zeeb 
3047b4c3e9b5SBjoern A. Zeeb done:
3048b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
3049b4c3e9b5SBjoern A. Zeeb 	return err;
3050b4c3e9b5SBjoern A. Zeeb }
3051b4c3e9b5SBjoern A. Zeeb 
3052b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_config_default_mgmt_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx)3053b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
3054b4c3e9b5SBjoern A. Zeeb 				       struct net_device *ndev, int link_id,
3055b4c3e9b5SBjoern A. Zeeb 				       u8 key_idx)
3056b4c3e9b5SBjoern A. Zeeb {
3057b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
3058b4c3e9b5SBjoern A. Zeeb 
3059b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
3060b4c3e9b5SBjoern A. Zeeb 
3061b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
3062b4c3e9b5SBjoern A. Zeeb 		return 0;
3063b4c3e9b5SBjoern A. Zeeb 
3064b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "Not supported\n");
3065b4c3e9b5SBjoern A. Zeeb 
3066b4c3e9b5SBjoern A. Zeeb 	return -EOPNOTSUPP;
3067b4c3e9b5SBjoern A. Zeeb }
3068b4c3e9b5SBjoern A. Zeeb 
3069b4c3e9b5SBjoern A. Zeeb static void
brcmf_cfg80211_reconfigure_wep(struct brcmf_if * ifp)3070b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
3071b4c3e9b5SBjoern A. Zeeb {
3072b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
3073b4c3e9b5SBjoern A. Zeeb 	s32 err;
3074b4c3e9b5SBjoern A. Zeeb 	u8 key_idx;
3075b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wsec_key *key;
3076b4c3e9b5SBjoern A. Zeeb 	s32 wsec;
3077b4c3e9b5SBjoern A. Zeeb 
3078b4c3e9b5SBjoern A. Zeeb 	for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
3079b4c3e9b5SBjoern A. Zeeb 		key = &ifp->vif->profile.key[key_idx];
3080b4c3e9b5SBjoern A. Zeeb 		if ((key->algo == CRYPTO_ALGO_WEP1) ||
3081b4c3e9b5SBjoern A. Zeeb 		    (key->algo == CRYPTO_ALGO_WEP128))
3082b4c3e9b5SBjoern A. Zeeb 			break;
3083b4c3e9b5SBjoern A. Zeeb 	}
3084b4c3e9b5SBjoern A. Zeeb 	if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
3085b4c3e9b5SBjoern A. Zeeb 		return;
3086b4c3e9b5SBjoern A. Zeeb 
3087b4c3e9b5SBjoern A. Zeeb 	err = send_key_to_dongle(ifp, key);
3088b4c3e9b5SBjoern A. Zeeb 	if (err) {
3089b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Setting WEP key failed (%d)\n", err);
3090b4c3e9b5SBjoern A. Zeeb 		return;
3091b4c3e9b5SBjoern A. Zeeb 	}
3092b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
3093b4c3e9b5SBjoern A. Zeeb 	if (err) {
3094b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "get wsec error (%d)\n", err);
3095b4c3e9b5SBjoern A. Zeeb 		return;
3096b4c3e9b5SBjoern A. Zeeb 	}
3097b4c3e9b5SBjoern A. Zeeb 	wsec |= WEP_ENABLED;
3098b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
3099b4c3e9b5SBjoern A. Zeeb 	if (err)
3100b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "set wsec error (%d)\n", err);
3101b4c3e9b5SBjoern A. Zeeb }
3102b4c3e9b5SBjoern A. Zeeb 
brcmf_convert_sta_flags(u32 fw_sta_flags,struct station_info * si)3103b4c3e9b5SBjoern A. Zeeb static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
3104b4c3e9b5SBjoern A. Zeeb {
3105b4c3e9b5SBjoern A. Zeeb 	struct nl80211_sta_flag_update *sfu;
3106b4c3e9b5SBjoern A. Zeeb 
3107b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
3108b4c3e9b5SBjoern A. Zeeb 	si->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);
3109b4c3e9b5SBjoern A. Zeeb 	sfu = &si->sta_flags;
3110b4c3e9b5SBjoern A. Zeeb 	sfu->mask = BIT(NL80211_STA_FLAG_WME) |
3111b4c3e9b5SBjoern A. Zeeb 		    BIT(NL80211_STA_FLAG_AUTHENTICATED) |
3112b4c3e9b5SBjoern A. Zeeb 		    BIT(NL80211_STA_FLAG_ASSOCIATED) |
3113b4c3e9b5SBjoern A. Zeeb 		    BIT(NL80211_STA_FLAG_AUTHORIZED);
3114b4c3e9b5SBjoern A. Zeeb 	if (fw_sta_flags & BRCMF_STA_WME)
3115b4c3e9b5SBjoern A. Zeeb 		sfu->set |= BIT(NL80211_STA_FLAG_WME);
3116b4c3e9b5SBjoern A. Zeeb 	if (fw_sta_flags & BRCMF_STA_AUTHE)
3117b4c3e9b5SBjoern A. Zeeb 		sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
3118b4c3e9b5SBjoern A. Zeeb 	if (fw_sta_flags & BRCMF_STA_ASSOC)
3119b4c3e9b5SBjoern A. Zeeb 		sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
3120b4c3e9b5SBjoern A. Zeeb 	if (fw_sta_flags & BRCMF_STA_AUTHO)
3121b4c3e9b5SBjoern A. Zeeb 		sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
3122b4c3e9b5SBjoern A. Zeeb }
3123b4c3e9b5SBjoern A. Zeeb 
brcmf_fill_bss_param(struct brcmf_if * ifp,struct station_info * si)3124b4c3e9b5SBjoern A. Zeeb static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
3125b4c3e9b5SBjoern A. Zeeb {
3126b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
3127b4c3e9b5SBjoern A. Zeeb 	struct {
3128b4c3e9b5SBjoern A. Zeeb 		__le32 len;
3129b4c3e9b5SBjoern A. Zeeb 		struct brcmf_bss_info_le bss_le;
3130b4c3e9b5SBjoern A. Zeeb 	} *buf;
3131b4c3e9b5SBjoern A. Zeeb 	u16 capability;
3132b4c3e9b5SBjoern A. Zeeb 	int err;
3133b4c3e9b5SBjoern A. Zeeb 
3134b4c3e9b5SBjoern A. Zeeb 	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
3135b4c3e9b5SBjoern A. Zeeb 	if (!buf)
3136b4c3e9b5SBjoern A. Zeeb 		return;
3137b4c3e9b5SBjoern A. Zeeb 
3138b4c3e9b5SBjoern A. Zeeb 	buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
3139b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
3140b4c3e9b5SBjoern A. Zeeb 				     WL_BSS_INFO_MAX);
3141b4c3e9b5SBjoern A. Zeeb 	if (err) {
3142b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Failed to get bss info (%d)\n", err);
3143b4c3e9b5SBjoern A. Zeeb 		goto out_kfree;
3144b4c3e9b5SBjoern A. Zeeb 	}
3145b4c3e9b5SBjoern A. Zeeb 	si->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);
3146b4c3e9b5SBjoern A. Zeeb 	si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
3147b4c3e9b5SBjoern A. Zeeb 	si->bss_param.dtim_period = buf->bss_le.dtim_period;
3148b4c3e9b5SBjoern A. Zeeb 	capability = le16_to_cpu(buf->bss_le.capability);
3149b4c3e9b5SBjoern A. Zeeb 	if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
3150b4c3e9b5SBjoern A. Zeeb 		si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
3151b4c3e9b5SBjoern A. Zeeb 	if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
3152b4c3e9b5SBjoern A. Zeeb 		si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
3153b4c3e9b5SBjoern A. Zeeb 	if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
3154b4c3e9b5SBjoern A. Zeeb 		si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
3155b4c3e9b5SBjoern A. Zeeb 
3156b4c3e9b5SBjoern A. Zeeb out_kfree:
3157b4c3e9b5SBjoern A. Zeeb 	kfree(buf);
3158b4c3e9b5SBjoern A. Zeeb }
3159b4c3e9b5SBjoern A. Zeeb 
3160b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_get_station_ibss(struct brcmf_if * ifp,struct station_info * sinfo)3161b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
3162b4c3e9b5SBjoern A. Zeeb 				struct station_info *sinfo)
3163b4c3e9b5SBjoern A. Zeeb {
3164b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
3165b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scb_val_le scbval;
3166b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pktcnt_le pktcnt;
3167b4c3e9b5SBjoern A. Zeeb 	s32 err;
3168b4c3e9b5SBjoern A. Zeeb 	u32 rate;
3169b4c3e9b5SBjoern A. Zeeb 	u32 rssi;
3170b4c3e9b5SBjoern A. Zeeb 
3171b4c3e9b5SBjoern A. Zeeb 	/* Get the current tx rate */
3172b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
3173b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
3174b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "BRCMF_C_GET_RATE error (%d)\n", err);
3175b4c3e9b5SBjoern A. Zeeb 		return err;
3176b4c3e9b5SBjoern A. Zeeb 	}
3177b4c3e9b5SBjoern A. Zeeb 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
3178b4c3e9b5SBjoern A. Zeeb 	sinfo->txrate.legacy = rate * 5;
3179b4c3e9b5SBjoern A. Zeeb 
3180b4c3e9b5SBjoern A. Zeeb 	memset(&scbval, 0, sizeof(scbval));
3181b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
3182b4c3e9b5SBjoern A. Zeeb 				     sizeof(scbval));
3183b4c3e9b5SBjoern A. Zeeb 	if (err) {
3184b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "BRCMF_C_GET_RSSI error (%d)\n", err);
3185b4c3e9b5SBjoern A. Zeeb 		return err;
3186b4c3e9b5SBjoern A. Zeeb 	}
3187b4c3e9b5SBjoern A. Zeeb 	rssi = le32_to_cpu(scbval.val);
3188b4c3e9b5SBjoern A. Zeeb 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
3189b4c3e9b5SBjoern A. Zeeb 	sinfo->signal = rssi;
3190b4c3e9b5SBjoern A. Zeeb 
3191b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
3192b4c3e9b5SBjoern A. Zeeb 				     sizeof(pktcnt));
3193b4c3e9b5SBjoern A. Zeeb 	if (err) {
3194b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
3195b4c3e9b5SBjoern A. Zeeb 		return err;
3196b4c3e9b5SBjoern A. Zeeb 	}
3197b4c3e9b5SBjoern A. Zeeb 	sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
3198b4c3e9b5SBjoern A. Zeeb 			 BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
3199b4c3e9b5SBjoern A. Zeeb 			 BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
3200b4c3e9b5SBjoern A. Zeeb 			 BIT_ULL(NL80211_STA_INFO_TX_FAILED);
3201b4c3e9b5SBjoern A. Zeeb 	sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
3202b4c3e9b5SBjoern A. Zeeb 	sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
3203b4c3e9b5SBjoern A. Zeeb 	sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
3204b4c3e9b5SBjoern A. Zeeb 	sinfo->tx_failed  = le32_to_cpu(pktcnt.tx_bad_pkt);
3205b4c3e9b5SBjoern A. Zeeb 
3206b4c3e9b5SBjoern A. Zeeb 	return 0;
3207b4c3e9b5SBjoern A. Zeeb }
3208b4c3e9b5SBjoern A. Zeeb 
3209b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_get_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_info * sinfo)3210b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
3211b4c3e9b5SBjoern A. Zeeb 			   const u8 *mac, struct station_info *sinfo)
3212b4c3e9b5SBjoern A. Zeeb {
3213b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3214b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
3215b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3216b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scb_val_le scb_val;
3217b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
3218b4c3e9b5SBjoern A. Zeeb 	struct brcmf_sta_info_le sta_info_le;
3219b4c3e9b5SBjoern A. Zeeb 	u32 sta_flags;
3220b4c3e9b5SBjoern A. Zeeb 	u32 is_tdls_peer;
3221b4c3e9b5SBjoern A. Zeeb 	s32 total_rssi_avg = 0;
3222b4c3e9b5SBjoern A. Zeeb 	s32 total_rssi = 0;
3223b4c3e9b5SBjoern A. Zeeb 	s32 count_rssi = 0;
3224b4c3e9b5SBjoern A. Zeeb 	int rssi;
3225b4c3e9b5SBjoern A. Zeeb 	u32 i;
3226b4c3e9b5SBjoern A. Zeeb 
3227b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
3228b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
3229b4c3e9b5SBjoern A. Zeeb 		return -EIO;
3230b4c3e9b5SBjoern A. Zeeb 
3231b4c3e9b5SBjoern A. Zeeb 	if (brcmf_is_ibssmode(ifp->vif))
3232b4c3e9b5SBjoern A. Zeeb 		return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
3233b4c3e9b5SBjoern A. Zeeb 
3234b4c3e9b5SBjoern A. Zeeb 	memset(&sta_info_le, 0, sizeof(sta_info_le));
3235b4c3e9b5SBjoern A. Zeeb 	memcpy(&sta_info_le, mac, ETH_ALEN);
3236b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
3237b4c3e9b5SBjoern A. Zeeb 				       &sta_info_le,
3238b4c3e9b5SBjoern A. Zeeb 				       sizeof(sta_info_le));
3239b4c3e9b5SBjoern A. Zeeb 	is_tdls_peer = !err;
3240b4c3e9b5SBjoern A. Zeeb 	if (err) {
3241b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_get(ifp, "sta_info",
3242b4c3e9b5SBjoern A. Zeeb 					       &sta_info_le,
3243b4c3e9b5SBjoern A. Zeeb 					       sizeof(sta_info_le));
3244b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
3245b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "GET STA INFO failed, %d\n", err);
3246b4c3e9b5SBjoern A. Zeeb 			goto done;
3247b4c3e9b5SBjoern A. Zeeb 		}
3248b4c3e9b5SBjoern A. Zeeb 	}
3249b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
3250b4c3e9b5SBjoern A. Zeeb 	sinfo->filled = BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
3251b4c3e9b5SBjoern A. Zeeb 	sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
3252b4c3e9b5SBjoern A. Zeeb 	sta_flags = le32_to_cpu(sta_info_le.flags);
3253b4c3e9b5SBjoern A. Zeeb 	brcmf_convert_sta_flags(sta_flags, sinfo);
3254b4c3e9b5SBjoern A. Zeeb 	sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
3255b4c3e9b5SBjoern A. Zeeb 	if (is_tdls_peer)
3256b4c3e9b5SBjoern A. Zeeb 		sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
3257b4c3e9b5SBjoern A. Zeeb 	else
3258b4c3e9b5SBjoern A. Zeeb 		sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
3259b4c3e9b5SBjoern A. Zeeb 	if (sta_flags & BRCMF_STA_ASSOC) {
3260b4c3e9b5SBjoern A. Zeeb 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);
3261b4c3e9b5SBjoern A. Zeeb 		sinfo->connected_time = le32_to_cpu(sta_info_le.in);
3262b4c3e9b5SBjoern A. Zeeb 		brcmf_fill_bss_param(ifp, sinfo);
3263b4c3e9b5SBjoern A. Zeeb 	}
3264b4c3e9b5SBjoern A. Zeeb 	if (sta_flags & BRCMF_STA_SCBSTATS) {
3265b4c3e9b5SBjoern A. Zeeb 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
3266b4c3e9b5SBjoern A. Zeeb 		sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
3267b4c3e9b5SBjoern A. Zeeb 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
3268b4c3e9b5SBjoern A. Zeeb 		sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
3269b4c3e9b5SBjoern A. Zeeb 		sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
3270b4c3e9b5SBjoern A. Zeeb 		sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
3271b4c3e9b5SBjoern A. Zeeb 		sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
3272b4c3e9b5SBjoern A. Zeeb 		sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
3273b4c3e9b5SBjoern A. Zeeb 		if (sinfo->tx_packets) {
3274b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
3275b4c3e9b5SBjoern A. Zeeb 			sinfo->txrate.legacy =
3276b4c3e9b5SBjoern A. Zeeb 				le32_to_cpu(sta_info_le.tx_rate) / 100;
3277b4c3e9b5SBjoern A. Zeeb 		}
3278b4c3e9b5SBjoern A. Zeeb 		if (sinfo->rx_packets) {
3279b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
3280b4c3e9b5SBjoern A. Zeeb 			sinfo->rxrate.legacy =
3281b4c3e9b5SBjoern A. Zeeb 				le32_to_cpu(sta_info_le.rx_rate) / 100;
3282b4c3e9b5SBjoern A. Zeeb 		}
3283b4c3e9b5SBjoern A. Zeeb 		if (le16_to_cpu(sta_info_le.ver) >= 4) {
3284b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
3285b4c3e9b5SBjoern A. Zeeb 			sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
3286b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
3287b4c3e9b5SBjoern A. Zeeb 			sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
3288b4c3e9b5SBjoern A. Zeeb 		}
3289b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < BRCMF_ANT_MAX; i++) {
3290b4c3e9b5SBjoern A. Zeeb 			if (sta_info_le.rssi[i] == 0 ||
3291b4c3e9b5SBjoern A. Zeeb 			    sta_info_le.rx_lastpkt_rssi[i] == 0)
3292b4c3e9b5SBjoern A. Zeeb 				continue;
3293b4c3e9b5SBjoern A. Zeeb 			sinfo->chains |= BIT(count_rssi);
3294b4c3e9b5SBjoern A. Zeeb 			sinfo->chain_signal[count_rssi] =
3295b4c3e9b5SBjoern A. Zeeb 				sta_info_le.rx_lastpkt_rssi[i];
3296b4c3e9b5SBjoern A. Zeeb 			sinfo->chain_signal_avg[count_rssi] =
3297b4c3e9b5SBjoern A. Zeeb 				sta_info_le.rssi[i];
3298b4c3e9b5SBjoern A. Zeeb 			total_rssi += sta_info_le.rx_lastpkt_rssi[i];
3299b4c3e9b5SBjoern A. Zeeb 			total_rssi_avg += sta_info_le.rssi[i];
3300b4c3e9b5SBjoern A. Zeeb 			count_rssi++;
3301b4c3e9b5SBjoern A. Zeeb 		}
3302b4c3e9b5SBjoern A. Zeeb 		if (count_rssi) {
3303b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
3304b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
3305b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
3306b4c3e9b5SBjoern A. Zeeb 			sinfo->filled |=
3307b4c3e9b5SBjoern A. Zeeb 				BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
3308b4c3e9b5SBjoern A. Zeeb 			sinfo->signal = total_rssi / count_rssi;
3309b4c3e9b5SBjoern A. Zeeb 			sinfo->signal_avg = total_rssi_avg / count_rssi;
3310b4c3e9b5SBjoern A. Zeeb 		} else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
3311b4c3e9b5SBjoern A. Zeeb 			&ifp->vif->sme_state)) {
3312b4c3e9b5SBjoern A. Zeeb 			memset(&scb_val, 0, sizeof(scb_val));
3313b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
3314b4c3e9b5SBjoern A. Zeeb 						     &scb_val, sizeof(scb_val));
3315b4c3e9b5SBjoern A. Zeeb 			if (err) {
3316b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "Could not get rssi (%d)\n",
3317b4c3e9b5SBjoern A. Zeeb 					 err);
3318b4c3e9b5SBjoern A. Zeeb 				goto done;
3319b4c3e9b5SBjoern A. Zeeb 			} else {
3320b4c3e9b5SBjoern A. Zeeb 				rssi = le32_to_cpu(scb_val.val);
3321b4c3e9b5SBjoern A. Zeeb 				sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
3322b4c3e9b5SBjoern A. Zeeb 				sinfo->signal = rssi;
3323b4c3e9b5SBjoern A. Zeeb 				brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
3324b4c3e9b5SBjoern A. Zeeb 			}
3325b4c3e9b5SBjoern A. Zeeb 		}
3326b4c3e9b5SBjoern A. Zeeb 	}
3327b4c3e9b5SBjoern A. Zeeb done:
3328b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
3329b4c3e9b5SBjoern A. Zeeb 	return err;
3330b4c3e9b5SBjoern A. Zeeb }
3331b4c3e9b5SBjoern A. Zeeb 
3332b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_dump_station(struct wiphy * wiphy,struct net_device * ndev,int idx,u8 * mac,struct station_info * sinfo)3333b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
3334b4c3e9b5SBjoern A. Zeeb 			    int idx, u8 *mac, struct station_info *sinfo)
3335b4c3e9b5SBjoern A. Zeeb {
3336b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3337b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
3338b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3339b4c3e9b5SBjoern A. Zeeb 	s32 err;
3340b4c3e9b5SBjoern A. Zeeb 
3341b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
3342b4c3e9b5SBjoern A. Zeeb 
3343b4c3e9b5SBjoern A. Zeeb 	if (idx == 0) {
3344b4c3e9b5SBjoern A. Zeeb 		cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
3345b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
3346b4c3e9b5SBjoern A. Zeeb 					     &cfg->assoclist,
3347b4c3e9b5SBjoern A. Zeeb 					     sizeof(cfg->assoclist));
3348b4c3e9b5SBjoern A. Zeeb 		if (err) {
3349b4c3e9b5SBjoern A. Zeeb 			/* GET_ASSOCLIST unsupported by firmware of older chips */
3350b4c3e9b5SBjoern A. Zeeb 			if (err == -EBADE)
3351b4c3e9b5SBjoern A. Zeeb 				bphy_info_once(drvr, "BRCMF_C_GET_ASSOCLIST unsupported\n");
3352b4c3e9b5SBjoern A. Zeeb 			else
3353b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "BRCMF_C_GET_ASSOCLIST failed, err=%d\n",
3354b4c3e9b5SBjoern A. Zeeb 					 err);
3355b4c3e9b5SBjoern A. Zeeb 
3356b4c3e9b5SBjoern A. Zeeb 			cfg->assoclist.count = 0;
3357b4c3e9b5SBjoern A. Zeeb 			return -EOPNOTSUPP;
3358b4c3e9b5SBjoern A. Zeeb 		}
3359b4c3e9b5SBjoern A. Zeeb 	}
3360b4c3e9b5SBjoern A. Zeeb 	if (idx < le32_to_cpu(cfg->assoclist.count)) {
3361b4c3e9b5SBjoern A. Zeeb 		memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
3362b4c3e9b5SBjoern A. Zeeb 		return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
3363b4c3e9b5SBjoern A. Zeeb 	}
3364b4c3e9b5SBjoern A. Zeeb 	return -ENOENT;
3365b4c3e9b5SBjoern A. Zeeb }
3366b4c3e9b5SBjoern A. Zeeb 
3367b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_set_power_mgmt(struct wiphy * wiphy,struct net_device * ndev,bool enabled,s32 timeout)3368b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
3369b4c3e9b5SBjoern A. Zeeb 			   bool enabled, s32 timeout)
3370b4c3e9b5SBjoern A. Zeeb {
3371b4c3e9b5SBjoern A. Zeeb 	s32 pm;
3372b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
3373b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3374b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
3375b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3376b4c3e9b5SBjoern A. Zeeb 
3377b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
3378b4c3e9b5SBjoern A. Zeeb 
3379b4c3e9b5SBjoern A. Zeeb 	/*
3380b4c3e9b5SBjoern A. Zeeb 	 * Powersave enable/disable request is coming from the
3381b4c3e9b5SBjoern A. Zeeb 	 * cfg80211 even before the interface is up. In that
3382b4c3e9b5SBjoern A. Zeeb 	 * scenario, driver will be storing the power save
3383b4c3e9b5SBjoern A. Zeeb 	 * preference in cfg struct to apply this to
3384b4c3e9b5SBjoern A. Zeeb 	 * FW later while initializing the dongle
3385b4c3e9b5SBjoern A. Zeeb 	 */
3386b4c3e9b5SBjoern A. Zeeb 	cfg->pwr_save = enabled;
3387b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif)) {
3388b4c3e9b5SBjoern A. Zeeb 
3389b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
3390b4c3e9b5SBjoern A. Zeeb 		goto done;
3391b4c3e9b5SBjoern A. Zeeb 	}
3392b4c3e9b5SBjoern A. Zeeb 
3393b4c3e9b5SBjoern A. Zeeb 	pm = enabled ? PM_FAST : PM_OFF;
3394b4c3e9b5SBjoern A. Zeeb 	/* Do not enable the power save after assoc if it is a p2p interface */
3395b4c3e9b5SBjoern A. Zeeb 	if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
3396b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
3397b4c3e9b5SBjoern A. Zeeb 		pm = PM_OFF;
3398b4c3e9b5SBjoern A. Zeeb 	}
3399b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
3400b4c3e9b5SBjoern A. Zeeb 
3401b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
3402b4c3e9b5SBjoern A. Zeeb 	if (err) {
3403b4c3e9b5SBjoern A. Zeeb 		if (err == -ENODEV)
3404b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "net_device is not ready yet\n");
3405b4c3e9b5SBjoern A. Zeeb 		else
3406b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "error (%d)\n", err);
3407b4c3e9b5SBjoern A. Zeeb 	}
3408b4c3e9b5SBjoern A. Zeeb 
3409b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret",
3410b4c3e9b5SBjoern A. Zeeb 				min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS));
3411b4c3e9b5SBjoern A. Zeeb 	if (err)
3412b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err);
3413b4c3e9b5SBjoern A. Zeeb 
3414b4c3e9b5SBjoern A. Zeeb done:
3415b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
3416b4c3e9b5SBjoern A. Zeeb 	return err;
3417b4c3e9b5SBjoern A. Zeeb }
3418b4c3e9b5SBjoern A. Zeeb 
brcmf_inform_single_bss(struct brcmf_cfg80211_info * cfg,struct brcmf_bss_info_le * bi)3419b4c3e9b5SBjoern A. Zeeb static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
3420b4c3e9b5SBjoern A. Zeeb 				   struct brcmf_bss_info_le *bi)
3421b4c3e9b5SBjoern A. Zeeb {
3422b4c3e9b5SBjoern A. Zeeb 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
3423b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3424b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_bss *bss;
3425b4c3e9b5SBjoern A. Zeeb 	enum nl80211_band band;
3426b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch;
3427b4c3e9b5SBjoern A. Zeeb 	u16 channel;
3428b4c3e9b5SBjoern A. Zeeb 	u32 freq;
3429b4c3e9b5SBjoern A. Zeeb 	u16 notify_capability;
3430b4c3e9b5SBjoern A. Zeeb 	u16 notify_interval;
3431b4c3e9b5SBjoern A. Zeeb 	u8 *notify_ie;
3432b4c3e9b5SBjoern A. Zeeb 	size_t notify_ielen;
3433b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_inform_bss bss_data = {};
3434b4c3e9b5SBjoern A. Zeeb 
3435b4c3e9b5SBjoern A. Zeeb 	if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
3436b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Bss info is larger than buffer. Discarding\n");
3437b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
3438b4c3e9b5SBjoern A. Zeeb 	}
3439b4c3e9b5SBjoern A. Zeeb 
3440b4c3e9b5SBjoern A. Zeeb 	if (!bi->ctl_ch) {
3441b4c3e9b5SBjoern A. Zeeb 		ch.chspec = le16_to_cpu(bi->chanspec);
3442b4c3e9b5SBjoern A. Zeeb 		cfg->d11inf.decchspec(&ch);
3443b4c3e9b5SBjoern A. Zeeb 		bi->ctl_ch = ch.control_ch_num;
3444b4c3e9b5SBjoern A. Zeeb 	}
3445b4c3e9b5SBjoern A. Zeeb 	channel = bi->ctl_ch;
3446b4c3e9b5SBjoern A. Zeeb 
3447b4c3e9b5SBjoern A. Zeeb 	if (channel <= CH_MAX_2G_CHANNEL)
3448b4c3e9b5SBjoern A. Zeeb 		band = NL80211_BAND_2GHZ;
3449b4c3e9b5SBjoern A. Zeeb 	else
3450b4c3e9b5SBjoern A. Zeeb 		band = NL80211_BAND_5GHZ;
3451b4c3e9b5SBjoern A. Zeeb 
3452b4c3e9b5SBjoern A. Zeeb 	freq = ieee80211_channel_to_frequency(channel, band);
3453b4c3e9b5SBjoern A. Zeeb 	bss_data.chan = ieee80211_get_channel(wiphy, freq);
3454b4c3e9b5SBjoern A. Zeeb 	bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
3455b4c3e9b5SBjoern A. Zeeb 
3456b4c3e9b5SBjoern A. Zeeb 	notify_capability = le16_to_cpu(bi->capability);
3457b4c3e9b5SBjoern A. Zeeb 	notify_interval = le16_to_cpu(bi->beacon_period);
3458b4c3e9b5SBjoern A. Zeeb 	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
3459b4c3e9b5SBjoern A. Zeeb 	notify_ielen = le32_to_cpu(bi->ie_length);
3460b4c3e9b5SBjoern A. Zeeb 	bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100;
3461b4c3e9b5SBjoern A. Zeeb 
3462b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
3463b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
3464b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
3465b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
3466b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal);
3467b4c3e9b5SBjoern A. Zeeb 
3468b4c3e9b5SBjoern A. Zeeb 	bss = cfg80211_inform_bss_data(wiphy, &bss_data,
3469b4c3e9b5SBjoern A. Zeeb 				       CFG80211_BSS_FTYPE_UNKNOWN,
3470b4c3e9b5SBjoern A. Zeeb 				       (const u8 *)bi->BSSID,
3471b4c3e9b5SBjoern A. Zeeb 				       0, notify_capability,
3472b4c3e9b5SBjoern A. Zeeb 				       notify_interval, notify_ie,
3473b4c3e9b5SBjoern A. Zeeb 				       notify_ielen, GFP_KERNEL);
3474b4c3e9b5SBjoern A. Zeeb 
3475b4c3e9b5SBjoern A. Zeeb 	if (!bss)
3476b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
3477b4c3e9b5SBjoern A. Zeeb 
3478b4c3e9b5SBjoern A. Zeeb 	cfg80211_put_bss(wiphy, bss);
3479b4c3e9b5SBjoern A. Zeeb 
3480b4c3e9b5SBjoern A. Zeeb 	return 0;
3481b4c3e9b5SBjoern A. Zeeb }
3482b4c3e9b5SBjoern A. Zeeb 
3483b4c3e9b5SBjoern A. Zeeb static struct brcmf_bss_info_le *
next_bss_le(struct brcmf_scan_results * list,struct brcmf_bss_info_le * bss)3484b4c3e9b5SBjoern A. Zeeb next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
3485b4c3e9b5SBjoern A. Zeeb {
3486b4c3e9b5SBjoern A. Zeeb 	if (bss == NULL)
3487b4c3e9b5SBjoern A. Zeeb 		return list->bss_info_le;
3488b4c3e9b5SBjoern A. Zeeb 	return (struct brcmf_bss_info_le *)((unsigned long)bss +
3489b4c3e9b5SBjoern A. Zeeb 					    le32_to_cpu(bss->length));
3490b4c3e9b5SBjoern A. Zeeb }
3491b4c3e9b5SBjoern A. Zeeb 
brcmf_inform_bss(struct brcmf_cfg80211_info * cfg)3492b4c3e9b5SBjoern A. Zeeb static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
3493b4c3e9b5SBjoern A. Zeeb {
3494b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3495b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scan_results *bss_list;
3496b4c3e9b5SBjoern A. Zeeb 	struct brcmf_bss_info_le *bi = NULL;	/* must be initialized */
3497b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
3498b4c3e9b5SBjoern A. Zeeb 	int i;
3499b4c3e9b5SBjoern A. Zeeb 
3500b4c3e9b5SBjoern A. Zeeb 	bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
3501b4c3e9b5SBjoern A. Zeeb 	if (bss_list->count != 0 &&
3502b4c3e9b5SBjoern A. Zeeb 	    bss_list->version != BRCMF_BSS_INFO_VERSION) {
3503b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Version %d != WL_BSS_INFO_VERSION\n",
3504b4c3e9b5SBjoern A. Zeeb 			 bss_list->version);
3505b4c3e9b5SBjoern A. Zeeb 		return -EOPNOTSUPP;
3506b4c3e9b5SBjoern A. Zeeb 	}
3507b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
3508b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < bss_list->count; i++) {
3509b4c3e9b5SBjoern A. Zeeb 		bi = next_bss_le(bss_list, bi);
3510b4c3e9b5SBjoern A. Zeeb 		err = brcmf_inform_single_bss(cfg, bi);
3511b4c3e9b5SBjoern A. Zeeb 		if (err)
3512b4c3e9b5SBjoern A. Zeeb 			break;
3513b4c3e9b5SBjoern A. Zeeb 	}
3514b4c3e9b5SBjoern A. Zeeb 	return err;
3515b4c3e9b5SBjoern A. Zeeb }
3516b4c3e9b5SBjoern A. Zeeb 
brcmf_inform_ibss(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const u8 * bssid)3517b4c3e9b5SBjoern A. Zeeb static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
3518b4c3e9b5SBjoern A. Zeeb 			     struct net_device *ndev, const u8 *bssid)
3519b4c3e9b5SBjoern A. Zeeb {
3520b4c3e9b5SBjoern A. Zeeb 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
3521b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3522b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_channel *notify_channel;
3523b4c3e9b5SBjoern A. Zeeb 	struct brcmf_bss_info_le *bi = NULL;
3524b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
3525b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_bss *bss;
3526b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch;
3527b4c3e9b5SBjoern A. Zeeb 	u8 *buf = NULL;
3528b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
3529b4c3e9b5SBjoern A. Zeeb 	u32 freq;
3530b4c3e9b5SBjoern A. Zeeb 	u16 notify_capability;
3531b4c3e9b5SBjoern A. Zeeb 	u16 notify_interval;
3532b4c3e9b5SBjoern A. Zeeb 	u8 *notify_ie;
3533b4c3e9b5SBjoern A. Zeeb 	size_t notify_ielen;
3534b4c3e9b5SBjoern A. Zeeb 	s32 notify_signal;
3535b4c3e9b5SBjoern A. Zeeb 
3536b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
3537b4c3e9b5SBjoern A. Zeeb 
3538b4c3e9b5SBjoern A. Zeeb 	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
3539b4c3e9b5SBjoern A. Zeeb 	if (buf == NULL) {
3540b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
3541b4c3e9b5SBjoern A. Zeeb 		goto CleanUp;
3542b4c3e9b5SBjoern A. Zeeb 	}
3543b4c3e9b5SBjoern A. Zeeb 
3544b4c3e9b5SBjoern A. Zeeb 	*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
3545b4c3e9b5SBjoern A. Zeeb 
3546b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
3547b4c3e9b5SBjoern A. Zeeb 				     buf, WL_BSS_INFO_MAX);
3548b4c3e9b5SBjoern A. Zeeb 	if (err) {
3549b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_GET_BSS_INFO failed: %d\n", err);
3550b4c3e9b5SBjoern A. Zeeb 		goto CleanUp;
3551b4c3e9b5SBjoern A. Zeeb 	}
3552b4c3e9b5SBjoern A. Zeeb 
3553b4c3e9b5SBjoern A. Zeeb 	bi = (struct brcmf_bss_info_le *)(buf + 4);
3554b4c3e9b5SBjoern A. Zeeb 
3555b4c3e9b5SBjoern A. Zeeb 	ch.chspec = le16_to_cpu(bi->chanspec);
3556b4c3e9b5SBjoern A. Zeeb 	cfg->d11inf.decchspec(&ch);
3557b4c3e9b5SBjoern A. Zeeb 
3558b4c3e9b5SBjoern A. Zeeb 	if (ch.band == BRCMU_CHAN_BAND_2G)
3559b4c3e9b5SBjoern A. Zeeb 		band = wiphy->bands[NL80211_BAND_2GHZ];
3560b4c3e9b5SBjoern A. Zeeb 	else
3561b4c3e9b5SBjoern A. Zeeb 		band = wiphy->bands[NL80211_BAND_5GHZ];
3562b4c3e9b5SBjoern A. Zeeb 
3563b4c3e9b5SBjoern A. Zeeb 	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
3564b4c3e9b5SBjoern A. Zeeb 	cfg->channel = freq;
3565b4c3e9b5SBjoern A. Zeeb 	notify_channel = ieee80211_get_channel(wiphy, freq);
3566b4c3e9b5SBjoern A. Zeeb 
3567b4c3e9b5SBjoern A. Zeeb 	notify_capability = le16_to_cpu(bi->capability);
3568b4c3e9b5SBjoern A. Zeeb 	notify_interval = le16_to_cpu(bi->beacon_period);
3569b4c3e9b5SBjoern A. Zeeb 	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
3570b4c3e9b5SBjoern A. Zeeb 	notify_ielen = le32_to_cpu(bi->ie_length);
3571b4c3e9b5SBjoern A. Zeeb 	notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
3572b4c3e9b5SBjoern A. Zeeb 
3573b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq);
3574b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "capability: %X\n", notify_capability);
3575b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
3576b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "signal: %d\n", notify_signal);
3577b4c3e9b5SBjoern A. Zeeb 
3578b4c3e9b5SBjoern A. Zeeb 	bss = cfg80211_inform_bss(wiphy, notify_channel,
3579b4c3e9b5SBjoern A. Zeeb 				  CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
3580b4c3e9b5SBjoern A. Zeeb 				  notify_capability, notify_interval,
3581b4c3e9b5SBjoern A. Zeeb 				  notify_ie, notify_ielen, notify_signal,
3582b4c3e9b5SBjoern A. Zeeb 				  GFP_KERNEL);
3583b4c3e9b5SBjoern A. Zeeb 
3584b4c3e9b5SBjoern A. Zeeb 	if (!bss) {
3585b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
3586b4c3e9b5SBjoern A. Zeeb 		goto CleanUp;
3587b4c3e9b5SBjoern A. Zeeb 	}
3588b4c3e9b5SBjoern A. Zeeb 
3589b4c3e9b5SBjoern A. Zeeb 	cfg80211_put_bss(wiphy, bss);
3590b4c3e9b5SBjoern A. Zeeb 
3591b4c3e9b5SBjoern A. Zeeb CleanUp:
3592b4c3e9b5SBjoern A. Zeeb 
3593b4c3e9b5SBjoern A. Zeeb 	kfree(buf);
3594b4c3e9b5SBjoern A. Zeeb 
3595b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
3596b4c3e9b5SBjoern A. Zeeb 
3597b4c3e9b5SBjoern A. Zeeb 	return err;
3598b4c3e9b5SBjoern A. Zeeb }
3599b4c3e9b5SBjoern A. Zeeb 
brcmf_update_bss_info(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)3600b4c3e9b5SBjoern A. Zeeb static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
3601b4c3e9b5SBjoern A. Zeeb 				 struct brcmf_if *ifp)
3602b4c3e9b5SBjoern A. Zeeb {
3603b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3604b4c3e9b5SBjoern A. Zeeb 	struct brcmf_bss_info_le *bi = NULL;
3605b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
3606b4c3e9b5SBjoern A. Zeeb 
3607b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
3608b4c3e9b5SBjoern A. Zeeb 	if (brcmf_is_ibssmode(ifp->vif))
3609b4c3e9b5SBjoern A. Zeeb 		return err;
3610b4c3e9b5SBjoern A. Zeeb 
3611b4c3e9b5SBjoern A. Zeeb 	*(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
3612b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
3613b4c3e9b5SBjoern A. Zeeb 				     cfg->extra_buf, WL_EXTRA_BUF_MAX);
3614b4c3e9b5SBjoern A. Zeeb 	if (err) {
3615b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Could not get bss info %d\n", err);
3616b4c3e9b5SBjoern A. Zeeb 		goto update_bss_info_out;
3617b4c3e9b5SBjoern A. Zeeb 	}
3618b4c3e9b5SBjoern A. Zeeb 	bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
3619b4c3e9b5SBjoern A. Zeeb 	err = brcmf_inform_single_bss(cfg, bi);
3620b4c3e9b5SBjoern A. Zeeb 
3621b4c3e9b5SBjoern A. Zeeb update_bss_info_out:
3622b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit");
3623b4c3e9b5SBjoern A. Zeeb 	return err;
3624b4c3e9b5SBjoern A. Zeeb }
3625b4c3e9b5SBjoern A. Zeeb 
brcmf_abort_scanning(struct brcmf_cfg80211_info * cfg)3626b4c3e9b5SBjoern A. Zeeb void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
3627b4c3e9b5SBjoern A. Zeeb {
3628b4c3e9b5SBjoern A. Zeeb 	struct escan_info *escan = &cfg->escan_info;
3629b4c3e9b5SBjoern A. Zeeb 
3630b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
3631b4c3e9b5SBjoern A. Zeeb 	if (cfg->int_escan_map || cfg->scan_request) {
3632b4c3e9b5SBjoern A. Zeeb 		escan->escan_state = WL_ESCAN_STATE_IDLE;
3633b4c3e9b5SBjoern A. Zeeb 		brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
3634b4c3e9b5SBjoern A. Zeeb 	}
3635b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3636b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
3637b4c3e9b5SBjoern A. Zeeb }
3638b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_escan_timeout_worker(struct work_struct * work)3639b4c3e9b5SBjoern A. Zeeb static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
3640b4c3e9b5SBjoern A. Zeeb {
3641b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg =
3642b4c3e9b5SBjoern A. Zeeb 			container_of(work, struct brcmf_cfg80211_info,
3643b4c3e9b5SBjoern A. Zeeb 				     escan_timeout_work);
3644b4c3e9b5SBjoern A. Zeeb 
3645b4c3e9b5SBjoern A. Zeeb 	brcmf_inform_bss(cfg);
3646b4c3e9b5SBjoern A. Zeeb 	brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
3647b4c3e9b5SBjoern A. Zeeb }
3648b4c3e9b5SBjoern A. Zeeb 
brcmf_escan_timeout(struct timer_list * t)3649b4c3e9b5SBjoern A. Zeeb static void brcmf_escan_timeout(struct timer_list *t)
3650b4c3e9b5SBjoern A. Zeeb {
3651b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg =
3652b4c3e9b5SBjoern A. Zeeb 			timer_container_of(cfg, t, escan_timeout);
3653b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
3654b4c3e9b5SBjoern A. Zeeb 
3655b4c3e9b5SBjoern A. Zeeb 	if (cfg->int_escan_map || cfg->scan_request) {
3656b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "timer expired\n");
3657b4c3e9b5SBjoern A. Zeeb 		schedule_work(&cfg->escan_timeout_work);
3658b4c3e9b5SBjoern A. Zeeb 	}
3659b4c3e9b5SBjoern A. Zeeb }
3660b4c3e9b5SBjoern A. Zeeb 
3661b4c3e9b5SBjoern A. Zeeb static s32
brcmf_compare_update_same_bss(struct brcmf_cfg80211_info * cfg,struct brcmf_bss_info_le * bss,struct brcmf_bss_info_le * bss_info_le)3662b4c3e9b5SBjoern A. Zeeb brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
3663b4c3e9b5SBjoern A. Zeeb 			      struct brcmf_bss_info_le *bss,
3664b4c3e9b5SBjoern A. Zeeb 			      struct brcmf_bss_info_le *bss_info_le)
3665b4c3e9b5SBjoern A. Zeeb {
3666b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch_bss, ch_bss_info_le;
3667b4c3e9b5SBjoern A. Zeeb 
3668b4c3e9b5SBjoern A. Zeeb 	ch_bss.chspec = le16_to_cpu(bss->chanspec);
3669b4c3e9b5SBjoern A. Zeeb 	cfg->d11inf.decchspec(&ch_bss);
3670b4c3e9b5SBjoern A. Zeeb 	ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
3671b4c3e9b5SBjoern A. Zeeb 	cfg->d11inf.decchspec(&ch_bss_info_le);
3672b4c3e9b5SBjoern A. Zeeb 
3673b4c3e9b5SBjoern A. Zeeb 	if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
3674b4c3e9b5SBjoern A. Zeeb 		ch_bss.band == ch_bss_info_le.band &&
3675b4c3e9b5SBjoern A. Zeeb 		bss_info_le->SSID_len == bss->SSID_len &&
3676b4c3e9b5SBjoern A. Zeeb 		!memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
3677b4c3e9b5SBjoern A. Zeeb 		if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
3678b4c3e9b5SBjoern A. Zeeb 			(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
3679b4c3e9b5SBjoern A. Zeeb 			s16 bss_rssi = le16_to_cpu(bss->RSSI);
3680b4c3e9b5SBjoern A. Zeeb 			s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
3681b4c3e9b5SBjoern A. Zeeb 
3682b4c3e9b5SBjoern A. Zeeb 			/* preserve max RSSI if the measurements are
3683b4c3e9b5SBjoern A. Zeeb 			* both on-channel or both off-channel
3684b4c3e9b5SBjoern A. Zeeb 			*/
3685b4c3e9b5SBjoern A. Zeeb 			if (bss_info_rssi > bss_rssi)
3686b4c3e9b5SBjoern A. Zeeb 				bss->RSSI = bss_info_le->RSSI;
3687b4c3e9b5SBjoern A. Zeeb 		} else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
3688b4c3e9b5SBjoern A. Zeeb 			(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
3689b4c3e9b5SBjoern A. Zeeb 			/* preserve the on-channel rssi measurement
3690b4c3e9b5SBjoern A. Zeeb 			* if the new measurement is off channel
3691b4c3e9b5SBjoern A. Zeeb 			*/
3692b4c3e9b5SBjoern A. Zeeb 			bss->RSSI = bss_info_le->RSSI;
3693b4c3e9b5SBjoern A. Zeeb 			bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
3694b4c3e9b5SBjoern A. Zeeb 		}
3695b4c3e9b5SBjoern A. Zeeb 		return 1;
3696b4c3e9b5SBjoern A. Zeeb 	}
3697b4c3e9b5SBjoern A. Zeeb 	return 0;
3698b4c3e9b5SBjoern A. Zeeb }
3699b4c3e9b5SBjoern A. Zeeb 
3700b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_escan_handler(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)3701b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
3702b4c3e9b5SBjoern A. Zeeb 			     const struct brcmf_event_msg *e, void *data)
3703b4c3e9b5SBjoern A. Zeeb {
3704b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
3705b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = drvr->config;
3706b4c3e9b5SBjoern A. Zeeb 	s32 status;
3707b4c3e9b5SBjoern A. Zeeb 	struct brcmf_escan_result_le *escan_result_le;
3708b4c3e9b5SBjoern A. Zeeb 	u32 escan_buflen;
3709b4c3e9b5SBjoern A. Zeeb 	struct brcmf_bss_info_le *bss_info_le;
3710b4c3e9b5SBjoern A. Zeeb 	struct brcmf_bss_info_le *bss = NULL;
3711b4c3e9b5SBjoern A. Zeeb 	u32 bi_length;
3712b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scan_results *list;
3713b4c3e9b5SBjoern A. Zeeb 	u32 i;
3714b4c3e9b5SBjoern A. Zeeb 	bool aborted;
3715b4c3e9b5SBjoern A. Zeeb 
3716b4c3e9b5SBjoern A. Zeeb 	status = e->status;
3717b4c3e9b5SBjoern A. Zeeb 
3718b4c3e9b5SBjoern A. Zeeb 	if (status == BRCMF_E_STATUS_ABORT)
3719b4c3e9b5SBjoern A. Zeeb 		goto exit;
3720b4c3e9b5SBjoern A. Zeeb 
3721b4c3e9b5SBjoern A. Zeeb 	if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3722b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "scan not ready, bsscfgidx=%d\n",
3723b4c3e9b5SBjoern A. Zeeb 			 ifp->bsscfgidx);
3724b4c3e9b5SBjoern A. Zeeb 		return -EPERM;
3725b4c3e9b5SBjoern A. Zeeb 	}
3726b4c3e9b5SBjoern A. Zeeb 
3727b4c3e9b5SBjoern A. Zeeb 	if (status == BRCMF_E_STATUS_PARTIAL) {
3728b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "ESCAN Partial result\n");
3729b4c3e9b5SBjoern A. Zeeb 		if (e->datalen < sizeof(*escan_result_le)) {
3730b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid event data length\n");
3731b4c3e9b5SBjoern A. Zeeb 			goto exit;
3732b4c3e9b5SBjoern A. Zeeb 		}
3733b4c3e9b5SBjoern A. Zeeb 		escan_result_le = (struct brcmf_escan_result_le *) data;
3734b4c3e9b5SBjoern A. Zeeb 		if (!escan_result_le) {
3735b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Invalid escan result (NULL pointer)\n");
3736b4c3e9b5SBjoern A. Zeeb 			goto exit;
3737b4c3e9b5SBjoern A. Zeeb 		}
3738b4c3e9b5SBjoern A. Zeeb 		escan_buflen = le32_to_cpu(escan_result_le->buflen);
3739b4c3e9b5SBjoern A. Zeeb 		if (escan_buflen > BRCMF_ESCAN_BUF_SIZE ||
3740b4c3e9b5SBjoern A. Zeeb 		    escan_buflen > e->datalen ||
3741b4c3e9b5SBjoern A. Zeeb 		    escan_buflen < sizeof(*escan_result_le)) {
3742b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Invalid escan buffer length: %d\n",
3743b4c3e9b5SBjoern A. Zeeb 				 escan_buflen);
3744b4c3e9b5SBjoern A. Zeeb 			goto exit;
3745b4c3e9b5SBjoern A. Zeeb 		}
3746b4c3e9b5SBjoern A. Zeeb 		if (le16_to_cpu(escan_result_le->bss_count) != 1) {
3747b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Invalid bss_count %d: ignoring\n",
3748b4c3e9b5SBjoern A. Zeeb 				 escan_result_le->bss_count);
3749b4c3e9b5SBjoern A. Zeeb 			goto exit;
3750b4c3e9b5SBjoern A. Zeeb 		}
3751b4c3e9b5SBjoern A. Zeeb 		bss_info_le = &escan_result_le->bss_info_le;
3752b4c3e9b5SBjoern A. Zeeb 
3753b4c3e9b5SBjoern A. Zeeb 		if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
3754b4c3e9b5SBjoern A. Zeeb 			goto exit;
3755b4c3e9b5SBjoern A. Zeeb 
3756b4c3e9b5SBjoern A. Zeeb 		if (!cfg->int_escan_map && !cfg->scan_request) {
3757b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(SCAN, "result without cfg80211 request\n");
3758b4c3e9b5SBjoern A. Zeeb 			goto exit;
3759b4c3e9b5SBjoern A. Zeeb 		}
3760b4c3e9b5SBjoern A. Zeeb 
3761b4c3e9b5SBjoern A. Zeeb 		bi_length = le32_to_cpu(bss_info_le->length);
3762b4c3e9b5SBjoern A. Zeeb 		if (bi_length != escan_buflen -	WL_ESCAN_RESULTS_FIXED_SIZE) {
3763b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Ignoring invalid bss_info length: %d\n",
3764b4c3e9b5SBjoern A. Zeeb 				 bi_length);
3765b4c3e9b5SBjoern A. Zeeb 			goto exit;
3766b4c3e9b5SBjoern A. Zeeb 		}
3767b4c3e9b5SBjoern A. Zeeb 
3768b4c3e9b5SBjoern A. Zeeb 		if (!(cfg_to_wiphy(cfg)->interface_modes &
3769b4c3e9b5SBjoern A. Zeeb 					BIT(NL80211_IFTYPE_ADHOC))) {
3770b4c3e9b5SBjoern A. Zeeb 			if (le16_to_cpu(bss_info_le->capability) &
3771b4c3e9b5SBjoern A. Zeeb 						WLAN_CAPABILITY_IBSS) {
3772b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "Ignoring IBSS result\n");
3773b4c3e9b5SBjoern A. Zeeb 				goto exit;
3774b4c3e9b5SBjoern A. Zeeb 			}
3775b4c3e9b5SBjoern A. Zeeb 		}
3776b4c3e9b5SBjoern A. Zeeb 
3777b4c3e9b5SBjoern A. Zeeb 		list = (struct brcmf_scan_results *)
3778b4c3e9b5SBjoern A. Zeeb 				cfg->escan_info.escan_buf;
3779b4c3e9b5SBjoern A. Zeeb 		if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
3780b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Buffer is too small: ignoring\n");
3781b4c3e9b5SBjoern A. Zeeb 			goto exit;
3782b4c3e9b5SBjoern A. Zeeb 		}
3783b4c3e9b5SBjoern A. Zeeb 
3784b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < list->count; i++) {
3785b4c3e9b5SBjoern A. Zeeb 			bss = bss ? (struct brcmf_bss_info_le *)
3786b4c3e9b5SBjoern A. Zeeb 				((unsigned char *)bss +
3787b4c3e9b5SBjoern A. Zeeb 				le32_to_cpu(bss->length)) : list->bss_info_le;
3788b4c3e9b5SBjoern A. Zeeb 			if (brcmf_compare_update_same_bss(cfg, bss,
3789b4c3e9b5SBjoern A. Zeeb 							  bss_info_le))
3790b4c3e9b5SBjoern A. Zeeb 				goto exit;
3791b4c3e9b5SBjoern A. Zeeb 		}
3792b4c3e9b5SBjoern A. Zeeb 		memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
3793b4c3e9b5SBjoern A. Zeeb 		       bi_length);
3794b4c3e9b5SBjoern A. Zeeb 		list->version = le32_to_cpu(bss_info_le->version);
3795b4c3e9b5SBjoern A. Zeeb 		list->buflen += bi_length;
3796b4c3e9b5SBjoern A. Zeeb 		list->count++;
3797b4c3e9b5SBjoern A. Zeeb 	} else {
3798b4c3e9b5SBjoern A. Zeeb 		cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3799b4c3e9b5SBjoern A. Zeeb 		if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
3800b4c3e9b5SBjoern A. Zeeb 			goto exit;
3801b4c3e9b5SBjoern A. Zeeb 		if (cfg->int_escan_map || cfg->scan_request) {
3802b4c3e9b5SBjoern A. Zeeb 			brcmf_inform_bss(cfg);
3803b4c3e9b5SBjoern A. Zeeb 			aborted = status != BRCMF_E_STATUS_SUCCESS;
3804b4c3e9b5SBjoern A. Zeeb 			brcmf_notify_escan_complete(cfg, ifp, aborted, false);
3805b4c3e9b5SBjoern A. Zeeb 		} else
3806b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
3807b4c3e9b5SBjoern A. Zeeb 				  status);
3808b4c3e9b5SBjoern A. Zeeb 	}
3809b4c3e9b5SBjoern A. Zeeb exit:
3810b4c3e9b5SBjoern A. Zeeb 	return 0;
3811b4c3e9b5SBjoern A. Zeeb }
3812b4c3e9b5SBjoern A. Zeeb 
brcmf_init_escan(struct brcmf_cfg80211_info * cfg)3813b4c3e9b5SBjoern A. Zeeb static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
3814b4c3e9b5SBjoern A. Zeeb {
3815b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3816b4c3e9b5SBjoern A. Zeeb 			    brcmf_cfg80211_escan_handler);
3817b4c3e9b5SBjoern A. Zeeb 	cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3818b4c3e9b5SBjoern A. Zeeb 	/* Init scan_timeout timer */
3819b4c3e9b5SBjoern A. Zeeb 	timer_setup(&cfg->escan_timeout, brcmf_escan_timeout, 0);
3820b4c3e9b5SBjoern A. Zeeb 	INIT_WORK(&cfg->escan_timeout_work,
3821b4c3e9b5SBjoern A. Zeeb 		  brcmf_cfg80211_escan_timeout_worker);
3822b4c3e9b5SBjoern A. Zeeb }
3823b4c3e9b5SBjoern A. Zeeb 
3824b4c3e9b5SBjoern A. Zeeb static struct cfg80211_scan_request *
brcmf_alloc_internal_escan_request(struct wiphy * wiphy,u32 n_netinfo)3825b4c3e9b5SBjoern A. Zeeb brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {
3826b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_scan_request *req;
3827b4c3e9b5SBjoern A. Zeeb 	size_t req_size;
3828b4c3e9b5SBjoern A. Zeeb 
3829b4c3e9b5SBjoern A. Zeeb 	req_size = sizeof(*req) +
3830b4c3e9b5SBjoern A. Zeeb 		   n_netinfo * sizeof(req->channels[0]) +
3831b4c3e9b5SBjoern A. Zeeb 		   n_netinfo * sizeof(*req->ssids);
3832b4c3e9b5SBjoern A. Zeeb 
3833b4c3e9b5SBjoern A. Zeeb 	req = kzalloc(req_size, GFP_KERNEL);
3834b4c3e9b5SBjoern A. Zeeb 	if (req) {
3835b4c3e9b5SBjoern A. Zeeb 		req->wiphy = wiphy;
3836902136e0SBjoern A. Zeeb #if defined(__linux__)
3837b4c3e9b5SBjoern A. Zeeb 		req->ssids = (void *)(&req->channels[0]) +
3838b4c3e9b5SBjoern A. Zeeb 			     n_netinfo * sizeof(req->channels[0]);
3839902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
3840902136e0SBjoern A. Zeeb 		req->ssids = (void *)((&req->channels[0]) +
3841902136e0SBjoern A. Zeeb 			     n_netinfo * sizeof(req->channels[0]));
3842902136e0SBjoern A. Zeeb #endif
3843b4c3e9b5SBjoern A. Zeeb 	}
3844b4c3e9b5SBjoern A. Zeeb 	return req;
3845b4c3e9b5SBjoern A. Zeeb }
3846b4c3e9b5SBjoern A. Zeeb 
brcmf_internal_escan_add_info(struct cfg80211_scan_request * req,u8 * ssid,u8 ssid_len,u8 channel)3847b4c3e9b5SBjoern A. Zeeb static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
3848b4c3e9b5SBjoern A. Zeeb 					 u8 *ssid, u8 ssid_len, u8 channel)
3849b4c3e9b5SBjoern A. Zeeb {
3850b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_channel *chan;
3851b4c3e9b5SBjoern A. Zeeb 	enum nl80211_band band;
3852b4c3e9b5SBjoern A. Zeeb 	int freq, i;
3853b4c3e9b5SBjoern A. Zeeb 
3854b4c3e9b5SBjoern A. Zeeb 	if (channel <= CH_MAX_2G_CHANNEL)
3855b4c3e9b5SBjoern A. Zeeb 		band = NL80211_BAND_2GHZ;
3856b4c3e9b5SBjoern A. Zeeb 	else
3857b4c3e9b5SBjoern A. Zeeb 		band = NL80211_BAND_5GHZ;
3858b4c3e9b5SBjoern A. Zeeb 
3859b4c3e9b5SBjoern A. Zeeb 	freq = ieee80211_channel_to_frequency(channel, band);
3860b4c3e9b5SBjoern A. Zeeb 	if (!freq)
3861b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
3862b4c3e9b5SBjoern A. Zeeb 
3863b4c3e9b5SBjoern A. Zeeb 	chan = ieee80211_get_channel(req->wiphy, freq);
3864b4c3e9b5SBjoern A. Zeeb 	if (!chan)
3865b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
3866b4c3e9b5SBjoern A. Zeeb 
3867b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < req->n_channels; i++) {
3868b4c3e9b5SBjoern A. Zeeb 		if (req->channels[i] == chan)
3869b4c3e9b5SBjoern A. Zeeb 			break;
3870b4c3e9b5SBjoern A. Zeeb 	}
3871b4c3e9b5SBjoern A. Zeeb 	if (i == req->n_channels) {
3872b4c3e9b5SBjoern A. Zeeb 		req->n_channels++;
3873b4c3e9b5SBjoern A. Zeeb 		req->channels[i] = chan;
3874b4c3e9b5SBjoern A. Zeeb 	}
3875b4c3e9b5SBjoern A. Zeeb 
3876b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < req->n_ssids; i++) {
3877b4c3e9b5SBjoern A. Zeeb 		if (req->ssids[i].ssid_len == ssid_len &&
3878b4c3e9b5SBjoern A. Zeeb 		    !memcmp(req->ssids[i].ssid, ssid, ssid_len))
3879b4c3e9b5SBjoern A. Zeeb 			break;
3880b4c3e9b5SBjoern A. Zeeb 	}
3881b4c3e9b5SBjoern A. Zeeb 	if (i == req->n_ssids) {
3882b4c3e9b5SBjoern A. Zeeb 		memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
3883b4c3e9b5SBjoern A. Zeeb 		req->ssids[req->n_ssids++].ssid_len = ssid_len;
3884b4c3e9b5SBjoern A. Zeeb 	}
3885b4c3e9b5SBjoern A. Zeeb 	return 0;
3886b4c3e9b5SBjoern A. Zeeb }
3887b4c3e9b5SBjoern A. Zeeb 
brcmf_start_internal_escan(struct brcmf_if * ifp,u32 fwmap,struct cfg80211_scan_request * request)3888b4c3e9b5SBjoern A. Zeeb static int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap,
3889b4c3e9b5SBjoern A. Zeeb 				      struct cfg80211_scan_request *request)
3890b4c3e9b5SBjoern A. Zeeb {
3891b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3892b4c3e9b5SBjoern A. Zeeb 	int err;
3893b4c3e9b5SBjoern A. Zeeb 
3894b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3895b4c3e9b5SBjoern A. Zeeb 		if (cfg->int_escan_map)
3896b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(SCAN, "aborting internal scan: map=%u\n",
3897b4c3e9b5SBjoern A. Zeeb 				  cfg->int_escan_map);
3898b4c3e9b5SBjoern A. Zeeb 		/* Abort any on-going scan */
3899b4c3e9b5SBjoern A. Zeeb 		brcmf_abort_scanning(cfg);
3900b4c3e9b5SBjoern A. Zeeb 	}
3901b4c3e9b5SBjoern A. Zeeb 
3902b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap);
3903b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3904b4c3e9b5SBjoern A. Zeeb 	cfg->escan_info.run = brcmf_run_escan;
3905b4c3e9b5SBjoern A. Zeeb 	err = brcmf_do_escan(ifp, request);
3906b4c3e9b5SBjoern A. Zeeb 	if (err) {
3907b4c3e9b5SBjoern A. Zeeb 		clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3908b4c3e9b5SBjoern A. Zeeb 		return err;
3909b4c3e9b5SBjoern A. Zeeb 	}
3910b4c3e9b5SBjoern A. Zeeb 	cfg->int_escan_map = fwmap;
3911b4c3e9b5SBjoern A. Zeeb 	return 0;
3912b4c3e9b5SBjoern A. Zeeb }
3913b4c3e9b5SBjoern A. Zeeb 
3914b4c3e9b5SBjoern A. Zeeb static struct brcmf_pno_net_info_le *
brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le * pfn_v1)3915b4c3e9b5SBjoern A. Zeeb brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)
3916b4c3e9b5SBjoern A. Zeeb {
3917b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_scanresults_v2_le *pfn_v2;
3918b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_net_info_le *netinfo;
3919b4c3e9b5SBjoern A. Zeeb 
3920b4c3e9b5SBjoern A. Zeeb 	switch (pfn_v1->version) {
3921b4c3e9b5SBjoern A. Zeeb 	default:
3922b4c3e9b5SBjoern A. Zeeb 		WARN_ON(1);
3923b4c3e9b5SBjoern A. Zeeb 		fallthrough;
3924b4c3e9b5SBjoern A. Zeeb 	case cpu_to_le32(1):
3925b4c3e9b5SBjoern A. Zeeb 		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);
3926b4c3e9b5SBjoern A. Zeeb 		break;
3927b4c3e9b5SBjoern A. Zeeb 	case cpu_to_le32(2):
3928b4c3e9b5SBjoern A. Zeeb 		pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;
3929b4c3e9b5SBjoern A. Zeeb 		netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);
3930b4c3e9b5SBjoern A. Zeeb 		break;
3931b4c3e9b5SBjoern A. Zeeb 	}
3932b4c3e9b5SBjoern A. Zeeb 
3933b4c3e9b5SBjoern A. Zeeb 	return netinfo;
3934b4c3e9b5SBjoern A. Zeeb }
3935b4c3e9b5SBjoern A. Zeeb 
3936b4c3e9b5SBjoern A. Zeeb /* PFN result doesn't have all the info which are required by the supplicant
3937b4c3e9b5SBjoern A. Zeeb  * (For e.g IEs) Do a target Escan so that sched scan results are reported
3938b4c3e9b5SBjoern A. Zeeb  * via wl_inform_single_bss in the required format. Escan does require the
3939b4c3e9b5SBjoern A. Zeeb  * scan request in the form of cfg80211_scan_request. For timebeing, create
3940b4c3e9b5SBjoern A. Zeeb  * cfg80211_scan_request one out of the received PNO event.
3941b4c3e9b5SBjoern A. Zeeb  */
3942b4c3e9b5SBjoern A. Zeeb static s32
brcmf_notify_sched_scan_results(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)3943b4c3e9b5SBjoern A. Zeeb brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
3944b4c3e9b5SBjoern A. Zeeb 				const struct brcmf_event_msg *e, void *data)
3945b4c3e9b5SBjoern A. Zeeb {
3946b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
3947b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = drvr->config;
3948b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3949b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_scan_request *request = NULL;
3950b4c3e9b5SBjoern A. Zeeb 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
3951b4c3e9b5SBjoern A. Zeeb 	int i, err = 0;
3952b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_scanresults_le *pfn_result;
3953b4c3e9b5SBjoern A. Zeeb 	u32 bucket_map;
3954b4c3e9b5SBjoern A. Zeeb 	u32 result_count;
3955b4c3e9b5SBjoern A. Zeeb 	u32 status;
3956b4c3e9b5SBjoern A. Zeeb 	u32 datalen;
3957b4c3e9b5SBjoern A. Zeeb 
3958b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "Enter\n");
3959b4c3e9b5SBjoern A. Zeeb 
3960b4c3e9b5SBjoern A. Zeeb 	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
3961b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "Event data too small. Ignore\n");
3962b4c3e9b5SBjoern A. Zeeb 		return 0;
3963b4c3e9b5SBjoern A. Zeeb 	}
3964b4c3e9b5SBjoern A. Zeeb 
3965b4c3e9b5SBjoern A. Zeeb 	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
3966b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
3967b4c3e9b5SBjoern A. Zeeb 		return 0;
3968b4c3e9b5SBjoern A. Zeeb 	}
3969b4c3e9b5SBjoern A. Zeeb 
3970b4c3e9b5SBjoern A. Zeeb 	pfn_result = (struct brcmf_pno_scanresults_le *)data;
3971b4c3e9b5SBjoern A. Zeeb 	result_count = le32_to_cpu(pfn_result->count);
3972b4c3e9b5SBjoern A. Zeeb 	status = le32_to_cpu(pfn_result->status);
3973b4c3e9b5SBjoern A. Zeeb 
3974b4c3e9b5SBjoern A. Zeeb 	/* PFN event is limited to fit 512 bytes so we may get
3975b4c3e9b5SBjoern A. Zeeb 	 * multiple NET_FOUND events. For now place a warning here.
3976b4c3e9b5SBjoern A. Zeeb 	 */
3977b4c3e9b5SBjoern A. Zeeb 	WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3978b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
3979b4c3e9b5SBjoern A. Zeeb 	if (!result_count) {
3980b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "FALSE PNO Event. (pfn_count == 0)\n");
3981b4c3e9b5SBjoern A. Zeeb 		goto out_err;
3982b4c3e9b5SBjoern A. Zeeb 	}
3983b4c3e9b5SBjoern A. Zeeb 
3984b4c3e9b5SBjoern A. Zeeb 	netinfo_start = brcmf_get_netinfo_array(pfn_result);
3985902136e0SBjoern A. Zeeb #if defined(__linux__)
3986b4c3e9b5SBjoern A. Zeeb 	datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
3987902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
3988902136e0SBjoern A. Zeeb 	datalen = e->datalen - ((u8 *)netinfo_start - (u8 *)pfn_result);
3989902136e0SBjoern A. Zeeb #endif
3990b4c3e9b5SBjoern A. Zeeb 	if (datalen < result_count * sizeof(*netinfo)) {
3991b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "insufficient event data\n");
3992b4c3e9b5SBjoern A. Zeeb 		goto out_err;
3993b4c3e9b5SBjoern A. Zeeb 	}
3994b4c3e9b5SBjoern A. Zeeb 
3995b4c3e9b5SBjoern A. Zeeb 	request = brcmf_alloc_internal_escan_request(wiphy,
3996b4c3e9b5SBjoern A. Zeeb 						     result_count);
3997b4c3e9b5SBjoern A. Zeeb 	if (!request) {
3998b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
3999b4c3e9b5SBjoern A. Zeeb 		goto out_err;
4000b4c3e9b5SBjoern A. Zeeb 	}
4001b4c3e9b5SBjoern A. Zeeb 
4002b4c3e9b5SBjoern A. Zeeb 	bucket_map = 0;
4003b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < result_count; i++) {
4004b4c3e9b5SBjoern A. Zeeb 		netinfo = &netinfo_start[i];
4005b4c3e9b5SBjoern A. Zeeb 
4006b4c3e9b5SBjoern A. Zeeb 		if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
4007b4c3e9b5SBjoern A. Zeeb 			netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
4008b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
4009b4c3e9b5SBjoern A. Zeeb 			  netinfo->SSID, netinfo->channel);
4010b4c3e9b5SBjoern A. Zeeb 		bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo);
4011b4c3e9b5SBjoern A. Zeeb 		err = brcmf_internal_escan_add_info(request,
4012b4c3e9b5SBjoern A. Zeeb 						    netinfo->SSID,
4013b4c3e9b5SBjoern A. Zeeb 						    netinfo->SSID_len,
4014b4c3e9b5SBjoern A. Zeeb 						    netinfo->channel);
4015b4c3e9b5SBjoern A. Zeeb 		if (err)
4016b4c3e9b5SBjoern A. Zeeb 			goto out_err;
4017b4c3e9b5SBjoern A. Zeeb 	}
4018b4c3e9b5SBjoern A. Zeeb 
4019b4c3e9b5SBjoern A. Zeeb 	if (!bucket_map)
4020b4c3e9b5SBjoern A. Zeeb 		goto free_req;
4021b4c3e9b5SBjoern A. Zeeb 
4022b4c3e9b5SBjoern A. Zeeb 	err = brcmf_start_internal_escan(ifp, bucket_map, request);
4023b4c3e9b5SBjoern A. Zeeb 	if (!err)
4024b4c3e9b5SBjoern A. Zeeb 		goto free_req;
4025b4c3e9b5SBjoern A. Zeeb 
4026b4c3e9b5SBjoern A. Zeeb out_err:
4027b4c3e9b5SBjoern A. Zeeb 	cfg80211_sched_scan_stopped(wiphy, 0);
4028b4c3e9b5SBjoern A. Zeeb free_req:
4029b4c3e9b5SBjoern A. Zeeb 	kfree(request);
4030b4c3e9b5SBjoern A. Zeeb 	return err;
4031b4c3e9b5SBjoern A. Zeeb }
4032b4c3e9b5SBjoern A. Zeeb 
4033b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_sched_scan_start(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_sched_scan_request * req)4034b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
4035b4c3e9b5SBjoern A. Zeeb 				struct net_device *ndev,
4036b4c3e9b5SBjoern A. Zeeb 				struct cfg80211_sched_scan_request *req)
4037b4c3e9b5SBjoern A. Zeeb {
4038b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4039b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
4040b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
4041b4c3e9b5SBjoern A. Zeeb 
4042b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n",
4043b4c3e9b5SBjoern A. Zeeb 		  req->n_match_sets, req->n_ssids);
4044b4c3e9b5SBjoern A. Zeeb 
4045b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
4046b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Scanning suppressed: status=%lu\n",
4047b4c3e9b5SBjoern A. Zeeb 			 cfg->scan_status);
4048b4c3e9b5SBjoern A. Zeeb 		return -EAGAIN;
4049b4c3e9b5SBjoern A. Zeeb 	}
4050b4c3e9b5SBjoern A. Zeeb 
4051b4c3e9b5SBjoern A. Zeeb 	if (req->n_match_sets <= 0) {
4052b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n",
4053b4c3e9b5SBjoern A. Zeeb 			  req->n_match_sets);
4054b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
4055b4c3e9b5SBjoern A. Zeeb 	}
4056b4c3e9b5SBjoern A. Zeeb 
4057b4c3e9b5SBjoern A. Zeeb 	return brcmf_pno_start_sched_scan(ifp, req);
4058b4c3e9b5SBjoern A. Zeeb }
4059b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_sched_scan_stop(struct wiphy * wiphy,struct net_device * ndev,u64 reqid)4060b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
4061b4c3e9b5SBjoern A. Zeeb 					  struct net_device *ndev, u64 reqid)
4062b4c3e9b5SBjoern A. Zeeb {
4063b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4064b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
4065b4c3e9b5SBjoern A. Zeeb 
4066b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "enter\n");
4067b4c3e9b5SBjoern A. Zeeb 	brcmf_pno_stop_sched_scan(ifp, reqid);
4068b4c3e9b5SBjoern A. Zeeb 	if (cfg->int_escan_map)
4069b4c3e9b5SBjoern A. Zeeb 		brcmf_notify_escan_complete(cfg, ifp, true, true);
4070b4c3e9b5SBjoern A. Zeeb 	return 0;
4071b4c3e9b5SBjoern A. Zeeb }
4072b4c3e9b5SBjoern A. Zeeb 
brcmf_delay(u32 ms)4073b4c3e9b5SBjoern A. Zeeb static __always_inline void brcmf_delay(u32 ms)
4074b4c3e9b5SBjoern A. Zeeb {
4075b4c3e9b5SBjoern A. Zeeb 	if (ms < 1000 / HZ) {
4076b4c3e9b5SBjoern A. Zeeb 		cond_resched();
4077b4c3e9b5SBjoern A. Zeeb 		mdelay(ms);
4078b4c3e9b5SBjoern A. Zeeb 	} else {
4079902136e0SBjoern A. Zeeb #if defined(__linux__)
4080b4c3e9b5SBjoern A. Zeeb 		msleep(ms);
4081902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4082902136e0SBjoern A. Zeeb 		linux_msleep(ms);
4083902136e0SBjoern A. Zeeb #endif
4084b4c3e9b5SBjoern A. Zeeb 	}
4085b4c3e9b5SBjoern A. Zeeb }
4086b4c3e9b5SBjoern A. Zeeb 
brcmf_config_wowl_pattern(struct brcmf_if * ifp,u8 cmd[4],u8 * pattern,u32 patternsize,u8 * mask,u32 packet_offset)4087b4c3e9b5SBjoern A. Zeeb static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
4088b4c3e9b5SBjoern A. Zeeb 				     u8 *pattern, u32 patternsize, u8 *mask,
4089b4c3e9b5SBjoern A. Zeeb 				     u32 packet_offset)
4090b4c3e9b5SBjoern A. Zeeb {
4091b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fil_wowl_pattern_le *filter;
4092b4c3e9b5SBjoern A. Zeeb 	u32 masksize;
4093b4c3e9b5SBjoern A. Zeeb 	u32 patternoffset;
4094b4c3e9b5SBjoern A. Zeeb 	u8 *buf;
4095b4c3e9b5SBjoern A. Zeeb 	u32 bufsize;
4096b4c3e9b5SBjoern A. Zeeb 	s32 ret;
4097b4c3e9b5SBjoern A. Zeeb 
4098b4c3e9b5SBjoern A. Zeeb 	masksize = (patternsize + 7) / 8;
4099b4c3e9b5SBjoern A. Zeeb 	patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
4100b4c3e9b5SBjoern A. Zeeb 
4101b4c3e9b5SBjoern A. Zeeb 	bufsize = sizeof(*filter) + patternsize + masksize;
4102b4c3e9b5SBjoern A. Zeeb 	buf = kzalloc(bufsize, GFP_KERNEL);
4103b4c3e9b5SBjoern A. Zeeb 	if (!buf)
4104b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
4105b4c3e9b5SBjoern A. Zeeb 	filter = (struct brcmf_fil_wowl_pattern_le *)buf;
4106b4c3e9b5SBjoern A. Zeeb 
4107b4c3e9b5SBjoern A. Zeeb 	memcpy(filter->cmd, cmd, 4);
4108b4c3e9b5SBjoern A. Zeeb 	filter->masksize = cpu_to_le32(masksize);
4109b4c3e9b5SBjoern A. Zeeb 	filter->offset = cpu_to_le32(packet_offset);
4110b4c3e9b5SBjoern A. Zeeb 	filter->patternoffset = cpu_to_le32(patternoffset);
4111b4c3e9b5SBjoern A. Zeeb 	filter->patternsize = cpu_to_le32(patternsize);
4112b4c3e9b5SBjoern A. Zeeb 	filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
4113b4c3e9b5SBjoern A. Zeeb 
4114b4c3e9b5SBjoern A. Zeeb 	if ((mask) && (masksize))
4115b4c3e9b5SBjoern A. Zeeb 		memcpy(buf + sizeof(*filter), mask, masksize);
4116b4c3e9b5SBjoern A. Zeeb 	if ((pattern) && (patternsize))
4117b4c3e9b5SBjoern A. Zeeb 		memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
4118b4c3e9b5SBjoern A. Zeeb 
4119b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
4120b4c3e9b5SBjoern A. Zeeb 
4121b4c3e9b5SBjoern A. Zeeb 	kfree(buf);
4122b4c3e9b5SBjoern A. Zeeb 	return ret;
4123b4c3e9b5SBjoern A. Zeeb }
4124b4c3e9b5SBjoern A. Zeeb 
4125b4c3e9b5SBjoern A. Zeeb static s32
brcmf_wowl_nd_results(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)4126b4c3e9b5SBjoern A. Zeeb brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
4127b4c3e9b5SBjoern A. Zeeb 		      void *data)
4128b4c3e9b5SBjoern A. Zeeb {
4129b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
4130b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = drvr->config;
4131b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_scanresults_le *pfn_result;
4132b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pno_net_info_le *netinfo;
4133b4c3e9b5SBjoern A. Zeeb 
4134b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(SCAN, "Enter\n");
4135b4c3e9b5SBjoern A. Zeeb 
4136b4c3e9b5SBjoern A. Zeeb 	if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
4137b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "Event data too small. Ignore\n");
4138b4c3e9b5SBjoern A. Zeeb 		return 0;
4139b4c3e9b5SBjoern A. Zeeb 	}
4140b4c3e9b5SBjoern A. Zeeb 
4141b4c3e9b5SBjoern A. Zeeb 	pfn_result = (struct brcmf_pno_scanresults_le *)data;
4142b4c3e9b5SBjoern A. Zeeb 
4143b4c3e9b5SBjoern A. Zeeb 	if (e->event_code == BRCMF_E_PFN_NET_LOST) {
4144b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
4145b4c3e9b5SBjoern A. Zeeb 		return 0;
4146b4c3e9b5SBjoern A. Zeeb 	}
4147b4c3e9b5SBjoern A. Zeeb 
4148b4c3e9b5SBjoern A. Zeeb 	if (le32_to_cpu(pfn_result->count) < 1) {
4149b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Invalid result count, expected 1 (%d)\n",
4150b4c3e9b5SBjoern A. Zeeb 			 le32_to_cpu(pfn_result->count));
4151b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
4152b4c3e9b5SBjoern A. Zeeb 	}
4153b4c3e9b5SBjoern A. Zeeb 
4154b4c3e9b5SBjoern A. Zeeb 	netinfo = brcmf_get_netinfo_array(pfn_result);
4155b4c3e9b5SBjoern A. Zeeb 	if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
4156b4c3e9b5SBjoern A. Zeeb 		netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
4157b4c3e9b5SBjoern A. Zeeb 	memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
4158b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
4159b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd->n_channels = 1;
4160b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd->channels[0] =
4161b4c3e9b5SBjoern A. Zeeb 		ieee80211_channel_to_frequency(netinfo->channel,
4162b4c3e9b5SBjoern A. Zeeb 			netinfo->channel <= CH_MAX_2G_CHANNEL ?
4163b4c3e9b5SBjoern A. Zeeb 					NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
4164b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd_info->n_matches = 1;
4165b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
4166b4c3e9b5SBjoern A. Zeeb 
4167b4c3e9b5SBjoern A. Zeeb 	/* Inform (the resume task) that the net detect information was recvd */
4168b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd_data_completed = true;
4169b4c3e9b5SBjoern A. Zeeb 	wake_up(&cfg->wowl.nd_data_wait);
4170b4c3e9b5SBjoern A. Zeeb 
4171b4c3e9b5SBjoern A. Zeeb 	return 0;
4172b4c3e9b5SBjoern A. Zeeb }
4173b4c3e9b5SBjoern A. Zeeb 
4174b4c3e9b5SBjoern A. Zeeb #ifdef CONFIG_PM
4175b4c3e9b5SBjoern A. Zeeb 
brcmf_report_wowl_wakeind(struct wiphy * wiphy,struct brcmf_if * ifp)4176b4c3e9b5SBjoern A. Zeeb static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
4177b4c3e9b5SBjoern A. Zeeb {
4178b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4179b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
4180b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wowl_wakeind_le wake_ind_le;
4181b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_wowlan_wakeup wakeup_data;
4182b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_wowlan_wakeup *wakeup;
4183b4c3e9b5SBjoern A. Zeeb 	u32 wakeind;
4184b4c3e9b5SBjoern A. Zeeb 	s32 err;
4185b4c3e9b5SBjoern A. Zeeb 	long time_left;
4186b4c3e9b5SBjoern A. Zeeb 
4187b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
4188b4c3e9b5SBjoern A. Zeeb 				       sizeof(wake_ind_le));
4189b4c3e9b5SBjoern A. Zeeb 	if (err) {
4190b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Get wowl_wakeind failed, err = %d\n", err);
4191b4c3e9b5SBjoern A. Zeeb 		return;
4192b4c3e9b5SBjoern A. Zeeb 	}
4193b4c3e9b5SBjoern A. Zeeb 
4194b4c3e9b5SBjoern A. Zeeb 	wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
4195b4c3e9b5SBjoern A. Zeeb 	if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
4196b4c3e9b5SBjoern A. Zeeb 		       BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
4197b4c3e9b5SBjoern A. Zeeb 		       BRCMF_WOWL_PFN_FOUND)) {
4198b4c3e9b5SBjoern A. Zeeb 		wakeup = &wakeup_data;
4199b4c3e9b5SBjoern A. Zeeb 		memset(&wakeup_data, 0, sizeof(wakeup_data));
4200b4c3e9b5SBjoern A. Zeeb 		wakeup_data.pattern_idx = -1;
4201b4c3e9b5SBjoern A. Zeeb 
4202b4c3e9b5SBjoern A. Zeeb 		if (wakeind & BRCMF_WOWL_MAGIC) {
4203b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
4204b4c3e9b5SBjoern A. Zeeb 			wakeup_data.magic_pkt = true;
4205b4c3e9b5SBjoern A. Zeeb 		}
4206b4c3e9b5SBjoern A. Zeeb 		if (wakeind & BRCMF_WOWL_DIS) {
4207b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
4208b4c3e9b5SBjoern A. Zeeb 			wakeup_data.disconnect = true;
4209b4c3e9b5SBjoern A. Zeeb 		}
4210b4c3e9b5SBjoern A. Zeeb 		if (wakeind & BRCMF_WOWL_BCN) {
4211b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
4212b4c3e9b5SBjoern A. Zeeb 			wakeup_data.disconnect = true;
4213b4c3e9b5SBjoern A. Zeeb 		}
4214b4c3e9b5SBjoern A. Zeeb 		if (wakeind & BRCMF_WOWL_RETR) {
4215b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
4216b4c3e9b5SBjoern A. Zeeb 			wakeup_data.disconnect = true;
4217b4c3e9b5SBjoern A. Zeeb 		}
4218b4c3e9b5SBjoern A. Zeeb 		if (wakeind & BRCMF_WOWL_NET) {
4219b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
4220b4c3e9b5SBjoern A. Zeeb 			/* For now always map to pattern 0, no API to get
4221b4c3e9b5SBjoern A. Zeeb 			 * correct information available at the moment.
4222b4c3e9b5SBjoern A. Zeeb 			 */
4223b4c3e9b5SBjoern A. Zeeb 			wakeup_data.pattern_idx = 0;
4224b4c3e9b5SBjoern A. Zeeb 		}
4225b4c3e9b5SBjoern A. Zeeb 		if (wakeind & BRCMF_WOWL_PFN_FOUND) {
4226b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
4227b4c3e9b5SBjoern A. Zeeb 			time_left = wait_event_timeout(cfg->wowl.nd_data_wait,
4228b4c3e9b5SBjoern A. Zeeb 						       cfg->wowl.nd_data_completed,
4229b4c3e9b5SBjoern A. Zeeb 						       BRCMF_ND_INFO_TIMEOUT);
4230b4c3e9b5SBjoern A. Zeeb 			if (!time_left)
4231b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "No result for wowl net detect\n");
4232b4c3e9b5SBjoern A. Zeeb 			else
4233b4c3e9b5SBjoern A. Zeeb 				wakeup_data.net_detect = cfg->wowl.nd_info;
4234b4c3e9b5SBjoern A. Zeeb 		}
4235b4c3e9b5SBjoern A. Zeeb 		if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
4236b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
4237b4c3e9b5SBjoern A. Zeeb 			wakeup_data.gtk_rekey_failure = true;
4238b4c3e9b5SBjoern A. Zeeb 		}
4239b4c3e9b5SBjoern A. Zeeb 	} else {
4240b4c3e9b5SBjoern A. Zeeb 		wakeup = NULL;
4241b4c3e9b5SBjoern A. Zeeb 	}
4242b4c3e9b5SBjoern A. Zeeb 	cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
4243b4c3e9b5SBjoern A. Zeeb }
4244b4c3e9b5SBjoern A. Zeeb 
4245b4c3e9b5SBjoern A. Zeeb #else
4246b4c3e9b5SBjoern A. Zeeb 
brcmf_report_wowl_wakeind(struct wiphy * wiphy,struct brcmf_if * ifp)4247b4c3e9b5SBjoern A. Zeeb static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
4248b4c3e9b5SBjoern A. Zeeb {
4249b4c3e9b5SBjoern A. Zeeb }
4250b4c3e9b5SBjoern A. Zeeb 
4251b4c3e9b5SBjoern A. Zeeb #endif /* CONFIG_PM */
4252b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_resume(struct wiphy * wiphy)4253b4c3e9b5SBjoern A. Zeeb static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
4254b4c3e9b5SBjoern A. Zeeb {
4255b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4256b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = cfg_to_ndev(cfg);
4257b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
4258b4c3e9b5SBjoern A. Zeeb 
4259b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
4260b4c3e9b5SBjoern A. Zeeb 
4261b4c3e9b5SBjoern A. Zeeb 	if (cfg->wowl.active) {
4262b4c3e9b5SBjoern A. Zeeb 		brcmf_report_wowl_wakeind(wiphy, ifp);
4263b4c3e9b5SBjoern A. Zeeb 		brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
4264b4c3e9b5SBjoern A. Zeeb 		brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
4265b4c3e9b5SBjoern A. Zeeb 		if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
4266b4c3e9b5SBjoern A. Zeeb 			brcmf_configure_arp_nd_offload(ifp, true);
4267b4c3e9b5SBjoern A. Zeeb 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
4268b4c3e9b5SBjoern A. Zeeb 				      cfg->wowl.pre_pmmode);
4269b4c3e9b5SBjoern A. Zeeb 		cfg->wowl.active = false;
4270b4c3e9b5SBjoern A. Zeeb 		if (cfg->wowl.nd_enabled) {
4271b4c3e9b5SBjoern A. Zeeb 			brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0);
4272b4c3e9b5SBjoern A. Zeeb 			brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
4273b4c3e9b5SBjoern A. Zeeb 			brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
4274b4c3e9b5SBjoern A. Zeeb 					    brcmf_notify_sched_scan_results);
4275b4c3e9b5SBjoern A. Zeeb 			cfg->wowl.nd_enabled = false;
4276b4c3e9b5SBjoern A. Zeeb 		}
4277b4c3e9b5SBjoern A. Zeeb 	}
4278b4c3e9b5SBjoern A. Zeeb 	return 0;
4279b4c3e9b5SBjoern A. Zeeb }
4280b4c3e9b5SBjoern A. Zeeb 
brcmf_configure_wowl(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,struct cfg80211_wowlan * wowl)4281b4c3e9b5SBjoern A. Zeeb static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
4282b4c3e9b5SBjoern A. Zeeb 				 struct brcmf_if *ifp,
4283b4c3e9b5SBjoern A. Zeeb 				 struct cfg80211_wowlan *wowl)
4284b4c3e9b5SBjoern A. Zeeb {
4285b4c3e9b5SBjoern A. Zeeb 	u32 wowl_config;
4286b4c3e9b5SBjoern A. Zeeb 	struct brcmf_wowl_wakeind_le wowl_wakeind;
4287b4c3e9b5SBjoern A. Zeeb 	u32 i;
4288b4c3e9b5SBjoern A. Zeeb 
4289b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Suspend, wowl config.\n");
4290b4c3e9b5SBjoern A. Zeeb 
4291b4c3e9b5SBjoern A. Zeeb 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
4292b4c3e9b5SBjoern A. Zeeb 		brcmf_configure_arp_nd_offload(ifp, false);
4293b4c3e9b5SBjoern A. Zeeb 	brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
4294b4c3e9b5SBjoern A. Zeeb 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
4295b4c3e9b5SBjoern A. Zeeb 
4296b4c3e9b5SBjoern A. Zeeb 	wowl_config = 0;
4297b4c3e9b5SBjoern A. Zeeb 	if (wowl->disconnect)
4298b4c3e9b5SBjoern A. Zeeb 		wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
4299b4c3e9b5SBjoern A. Zeeb 	if (wowl->magic_pkt)
4300b4c3e9b5SBjoern A. Zeeb 		wowl_config |= BRCMF_WOWL_MAGIC;
4301b4c3e9b5SBjoern A. Zeeb 	if ((wowl->patterns) && (wowl->n_patterns)) {
4302b4c3e9b5SBjoern A. Zeeb 		wowl_config |= BRCMF_WOWL_NET;
4303b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < wowl->n_patterns; i++) {
4304b4c3e9b5SBjoern A. Zeeb 			brcmf_config_wowl_pattern(ifp, "add",
4305b4c3e9b5SBjoern A. Zeeb 				(u8 *)wowl->patterns[i].pattern,
4306b4c3e9b5SBjoern A. Zeeb 				wowl->patterns[i].pattern_len,
4307b4c3e9b5SBjoern A. Zeeb 				(u8 *)wowl->patterns[i].mask,
4308b4c3e9b5SBjoern A. Zeeb 				wowl->patterns[i].pkt_offset);
4309b4c3e9b5SBjoern A. Zeeb 		}
4310b4c3e9b5SBjoern A. Zeeb 	}
4311b4c3e9b5SBjoern A. Zeeb 	if (wowl->nd_config) {
4312b4c3e9b5SBjoern A. Zeeb 		brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
4313b4c3e9b5SBjoern A. Zeeb 						wowl->nd_config);
4314b4c3e9b5SBjoern A. Zeeb 		wowl_config |= BRCMF_WOWL_PFN_FOUND;
4315b4c3e9b5SBjoern A. Zeeb 
4316b4c3e9b5SBjoern A. Zeeb 		cfg->wowl.nd_data_completed = false;
4317b4c3e9b5SBjoern A. Zeeb 		cfg->wowl.nd_enabled = true;
4318b4c3e9b5SBjoern A. Zeeb 		/* Now reroute the event for PFN to the wowl function. */
4319b4c3e9b5SBjoern A. Zeeb 		brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
4320b4c3e9b5SBjoern A. Zeeb 		brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
4321b4c3e9b5SBjoern A. Zeeb 				    brcmf_wowl_nd_results);
4322b4c3e9b5SBjoern A. Zeeb 	}
4323b4c3e9b5SBjoern A. Zeeb 	if (wowl->gtk_rekey_failure)
4324b4c3e9b5SBjoern A. Zeeb 		wowl_config |= BRCMF_WOWL_GTK_FAILURE;
4325b4c3e9b5SBjoern A. Zeeb 	if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
4326b4c3e9b5SBjoern A. Zeeb 		wowl_config |= BRCMF_WOWL_UNASSOC;
4327b4c3e9b5SBjoern A. Zeeb 
4328b4c3e9b5SBjoern A. Zeeb 	memcpy(&wowl_wakeind, "clear", 6);
4329b4c3e9b5SBjoern A. Zeeb 	brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind,
4330b4c3e9b5SBjoern A. Zeeb 				 sizeof(wowl_wakeind));
4331b4c3e9b5SBjoern A. Zeeb 	brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
4332b4c3e9b5SBjoern A. Zeeb 	brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
4333b4c3e9b5SBjoern A. Zeeb 	brcmf_bus_wowl_config(cfg->pub->bus_if, true);
4334b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.active = true;
4335b4c3e9b5SBjoern A. Zeeb }
4336b4c3e9b5SBjoern A. Zeeb 
brcmf_keepalive_start(struct brcmf_if * ifp,unsigned int interval)4337b4c3e9b5SBjoern A. Zeeb static int brcmf_keepalive_start(struct brcmf_if *ifp, unsigned int interval)
4338b4c3e9b5SBjoern A. Zeeb {
4339b4c3e9b5SBjoern A. Zeeb 	struct brcmf_mkeep_alive_pkt_le kalive = {0};
4340b4c3e9b5SBjoern A. Zeeb 	int ret = 0;
4341b4c3e9b5SBjoern A. Zeeb 
4342b4c3e9b5SBjoern A. Zeeb 	/* Configure Null function/data keepalive */
4343b4c3e9b5SBjoern A. Zeeb 	kalive.version = cpu_to_le16(1);
4344b4c3e9b5SBjoern A. Zeeb 	kalive.period_msec = cpu_to_le32(interval * MSEC_PER_SEC);
4345b4c3e9b5SBjoern A. Zeeb 	kalive.len_bytes = cpu_to_le16(0);
4346b4c3e9b5SBjoern A. Zeeb 	kalive.keep_alive_id = 0;
4347b4c3e9b5SBjoern A. Zeeb 
4348b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &kalive, sizeof(kalive));
4349b4c3e9b5SBjoern A. Zeeb 	if (ret)
4350b4c3e9b5SBjoern A. Zeeb 		brcmf_err("keep-alive packet config failed, ret=%d\n", ret);
4351b4c3e9b5SBjoern A. Zeeb 
4352b4c3e9b5SBjoern A. Zeeb 	return ret;
4353b4c3e9b5SBjoern A. Zeeb }
4354b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_suspend(struct wiphy * wiphy,struct cfg80211_wowlan * wowl)4355b4c3e9b5SBjoern A. Zeeb static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
4356b4c3e9b5SBjoern A. Zeeb 				  struct cfg80211_wowlan *wowl)
4357b4c3e9b5SBjoern A. Zeeb {
4358b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4359b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = cfg_to_ndev(cfg);
4360b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
4361b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
4362b4c3e9b5SBjoern A. Zeeb 
4363b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
4364b4c3e9b5SBjoern A. Zeeb 
4365b4c3e9b5SBjoern A. Zeeb 	/* if the primary net_device is not READY there is nothing
4366b4c3e9b5SBjoern A. Zeeb 	 * we can do but pray resume goes smoothly.
4367b4c3e9b5SBjoern A. Zeeb 	 */
4368b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
4369b4c3e9b5SBjoern A. Zeeb 		goto exit;
4370b4c3e9b5SBjoern A. Zeeb 
4371b4c3e9b5SBjoern A. Zeeb 	/* Stop scheduled scan */
4372b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
4373b4c3e9b5SBjoern A. Zeeb 		brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0);
4374b4c3e9b5SBjoern A. Zeeb 
4375b4c3e9b5SBjoern A. Zeeb 	/* end any scanning */
4376b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
4377b4c3e9b5SBjoern A. Zeeb 		brcmf_abort_scanning(cfg);
4378b4c3e9b5SBjoern A. Zeeb 
4379b4c3e9b5SBjoern A. Zeeb 	if (wowl == NULL) {
4380b4c3e9b5SBjoern A. Zeeb 		brcmf_bus_wowl_config(cfg->pub->bus_if, false);
4381b4c3e9b5SBjoern A. Zeeb 		list_for_each_entry(vif, &cfg->vif_list, list) {
4382b4c3e9b5SBjoern A. Zeeb 			if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
4383b4c3e9b5SBjoern A. Zeeb 				continue;
4384b4c3e9b5SBjoern A. Zeeb 			/* While going to suspend if associated with AP
4385b4c3e9b5SBjoern A. Zeeb 			 * disassociate from AP to save power while system is
4386b4c3e9b5SBjoern A. Zeeb 			 * in suspended state
4387b4c3e9b5SBjoern A. Zeeb 			 */
4388b4c3e9b5SBjoern A. Zeeb 			brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED, true);
4389b4c3e9b5SBjoern A. Zeeb 			/* Make sure WPA_Supplicant receives all the event
4390b4c3e9b5SBjoern A. Zeeb 			 * generated due to DISASSOC call to the fw to keep
4391b4c3e9b5SBjoern A. Zeeb 			 * the state fw and WPA_Supplicant state consistent
4392b4c3e9b5SBjoern A. Zeeb 			 */
4393b4c3e9b5SBjoern A. Zeeb 			brcmf_delay(500);
4394b4c3e9b5SBjoern A. Zeeb 		}
4395b4c3e9b5SBjoern A. Zeeb 		/* Configure MPC */
4396b4c3e9b5SBjoern A. Zeeb 		brcmf_set_mpc(ifp, 1);
4397b4c3e9b5SBjoern A. Zeeb 
4398b4c3e9b5SBjoern A. Zeeb 	} else {
4399b4c3e9b5SBjoern A. Zeeb 		/* Configure WOWL parameters */
4400b4c3e9b5SBjoern A. Zeeb 		brcmf_configure_wowl(cfg, ifp, wowl);
4401b4c3e9b5SBjoern A. Zeeb 
4402b4c3e9b5SBjoern A. Zeeb 		/* Prevent disassociation due to inactivity with keep-alive */
4403b4c3e9b5SBjoern A. Zeeb 		brcmf_keepalive_start(ifp, 30);
4404b4c3e9b5SBjoern A. Zeeb 	}
4405b4c3e9b5SBjoern A. Zeeb 
4406b4c3e9b5SBjoern A. Zeeb exit:
4407b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
4408b4c3e9b5SBjoern A. Zeeb 	/* clear any scanning activity */
4409b4c3e9b5SBjoern A. Zeeb 	cfg->scan_status = 0;
4410b4c3e9b5SBjoern A. Zeeb 	return 0;
4411b4c3e9b5SBjoern A. Zeeb }
4412b4c3e9b5SBjoern A. Zeeb 
4413b4c3e9b5SBjoern A. Zeeb static s32
brcmf_pmksa_v3_op(struct brcmf_if * ifp,struct cfg80211_pmksa * pmksa,bool alive)4414b4c3e9b5SBjoern A. Zeeb brcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa,
4415b4c3e9b5SBjoern A. Zeeb 		  bool alive)
4416b4c3e9b5SBjoern A. Zeeb {
4417b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pmk_op_v3_le *pmk_op;
4418b4c3e9b5SBjoern A. Zeeb 	int length = offsetof(struct brcmf_pmk_op_v3_le, pmk);
4419b4c3e9b5SBjoern A. Zeeb 	int ret;
4420b4c3e9b5SBjoern A. Zeeb 
4421b4c3e9b5SBjoern A. Zeeb 	pmk_op = kzalloc(sizeof(*pmk_op), GFP_KERNEL);
4422b4c3e9b5SBjoern A. Zeeb 	if (!pmk_op)
4423b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
4424b4c3e9b5SBjoern A. Zeeb 
4425b4c3e9b5SBjoern A. Zeeb 	pmk_op->version = cpu_to_le16(BRCMF_PMKSA_VER_3);
4426b4c3e9b5SBjoern A. Zeeb 
4427b4c3e9b5SBjoern A. Zeeb 	if (!pmksa) {
4428b4c3e9b5SBjoern A. Zeeb 		/* Flush operation, operate on entire list */
4429b4c3e9b5SBjoern A. Zeeb 		pmk_op->count = cpu_to_le16(0);
4430b4c3e9b5SBjoern A. Zeeb 	} else {
4431b4c3e9b5SBjoern A. Zeeb 		/* Single PMK operation */
4432b4c3e9b5SBjoern A. Zeeb 		pmk_op->count = cpu_to_le16(1);
4433b4c3e9b5SBjoern A. Zeeb 		length += sizeof(struct brcmf_pmksa_v3);
4434b4c3e9b5SBjoern A. Zeeb 		if (pmksa->bssid)
4435b4c3e9b5SBjoern A. Zeeb 			memcpy(pmk_op->pmk[0].bssid, pmksa->bssid, ETH_ALEN);
4436b4c3e9b5SBjoern A. Zeeb 		if (pmksa->pmkid) {
4437b4c3e9b5SBjoern A. Zeeb 			memcpy(pmk_op->pmk[0].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
4438b4c3e9b5SBjoern A. Zeeb 			pmk_op->pmk[0].pmkid_len = WLAN_PMKID_LEN;
4439b4c3e9b5SBjoern A. Zeeb 		}
4440b4c3e9b5SBjoern A. Zeeb 		if (pmksa->ssid && pmksa->ssid_len) {
4441b4c3e9b5SBjoern A. Zeeb 			memcpy(pmk_op->pmk[0].ssid.SSID, pmksa->ssid, pmksa->ssid_len);
4442b4c3e9b5SBjoern A. Zeeb 			pmk_op->pmk[0].ssid.SSID_len = pmksa->ssid_len;
4443b4c3e9b5SBjoern A. Zeeb 		}
4444b4c3e9b5SBjoern A. Zeeb 		pmk_op->pmk[0].time_left = cpu_to_le32(alive ? BRCMF_PMKSA_NO_EXPIRY : 0);
4445b4c3e9b5SBjoern A. Zeeb 	}
4446b4c3e9b5SBjoern A. Zeeb 
4447b4c3e9b5SBjoern A. Zeeb 	pmk_op->length = cpu_to_le16(length);
4448b4c3e9b5SBjoern A. Zeeb 
4449b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_op, sizeof(*pmk_op));
4450b4c3e9b5SBjoern A. Zeeb 	kfree(pmk_op);
4451b4c3e9b5SBjoern A. Zeeb 	return ret;
4452b4c3e9b5SBjoern A. Zeeb }
4453b4c3e9b5SBjoern A. Zeeb 
4454b4c3e9b5SBjoern A. Zeeb static __used s32
brcmf_update_pmklist(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)4455b4c3e9b5SBjoern A. Zeeb brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
4456b4c3e9b5SBjoern A. Zeeb {
4457b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pmk_list_le *pmk_list;
4458b4c3e9b5SBjoern A. Zeeb 	int i;
4459b4c3e9b5SBjoern A. Zeeb 	u32 npmk;
4460b4c3e9b5SBjoern A. Zeeb 
4461b4c3e9b5SBjoern A. Zeeb 	pmk_list = &cfg->pmk_list;
4462b4c3e9b5SBjoern A. Zeeb 	npmk = le32_to_cpu(pmk_list->npmk);
4463b4c3e9b5SBjoern A. Zeeb 
4464b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "No of elements %d\n", npmk);
4465b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < npmk; i++)
4466b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
4467b4c3e9b5SBjoern A. Zeeb 
4468b4c3e9b5SBjoern A. Zeeb 	return brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
4469b4c3e9b5SBjoern A. Zeeb 			sizeof(*pmk_list));
4470b4c3e9b5SBjoern A. Zeeb }
4471b4c3e9b5SBjoern A. Zeeb 
4472b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_set_pmksa(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_pmksa * pmksa)4473b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
4474b4c3e9b5SBjoern A. Zeeb 			 struct cfg80211_pmksa *pmksa)
4475b4c3e9b5SBjoern A. Zeeb {
4476b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4477b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
4478b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
4479b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
4480b4c3e9b5SBjoern A. Zeeb 	s32 err;
4481b4c3e9b5SBjoern A. Zeeb 	u32 npmk, i;
4482b4c3e9b5SBjoern A. Zeeb 
4483b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
4484b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
4485b4c3e9b5SBjoern A. Zeeb 		return -EIO;
4486b4c3e9b5SBjoern A. Zeeb 
4487b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmksa->bssid);
4488b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmksa->pmkid);
4489b4c3e9b5SBjoern A. Zeeb 
4490b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4491b4c3e9b5SBjoern A. Zeeb 		return brcmf_pmksa_v3_op(ifp, pmksa, true);
4492b4c3e9b5SBjoern A. Zeeb 
4493b4c3e9b5SBjoern A. Zeeb 	/* TODO: implement PMKID_V2 */
4494b4c3e9b5SBjoern A. Zeeb 
4495b4c3e9b5SBjoern A. Zeeb 	npmk = le32_to_cpu(cfg->pmk_list.npmk);
4496b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < npmk; i++)
4497b4c3e9b5SBjoern A. Zeeb 		if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
4498b4c3e9b5SBjoern A. Zeeb 			break;
4499b4c3e9b5SBjoern A. Zeeb 	if (i < BRCMF_MAXPMKID) {
4500b4c3e9b5SBjoern A. Zeeb 		memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
4501b4c3e9b5SBjoern A. Zeeb 		memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
4502b4c3e9b5SBjoern A. Zeeb 		if (i == npmk) {
4503b4c3e9b5SBjoern A. Zeeb 			npmk++;
4504b4c3e9b5SBjoern A. Zeeb 			cfg->pmk_list.npmk = cpu_to_le32(npmk);
4505b4c3e9b5SBjoern A. Zeeb 		}
4506b4c3e9b5SBjoern A. Zeeb 	} else {
4507b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Too many PMKSA entries cached %d\n", npmk);
4508b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
4509b4c3e9b5SBjoern A. Zeeb 	}
4510b4c3e9b5SBjoern A. Zeeb 
4511b4c3e9b5SBjoern A. Zeeb 	err = brcmf_update_pmklist(cfg, ifp);
4512b4c3e9b5SBjoern A. Zeeb 
4513b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
4514b4c3e9b5SBjoern A. Zeeb 	return err;
4515b4c3e9b5SBjoern A. Zeeb }
4516b4c3e9b5SBjoern A. Zeeb 
4517b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_del_pmksa(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_pmksa * pmksa)4518b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
4519b4c3e9b5SBjoern A. Zeeb 			 struct cfg80211_pmksa *pmksa)
4520b4c3e9b5SBjoern A. Zeeb {
4521b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4522b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
4523b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
4524b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
4525b4c3e9b5SBjoern A. Zeeb 	s32 err;
4526b4c3e9b5SBjoern A. Zeeb 	u32 npmk, i;
4527b4c3e9b5SBjoern A. Zeeb 
4528b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
4529b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
4530b4c3e9b5SBjoern A. Zeeb 		return -EIO;
4531b4c3e9b5SBjoern A. Zeeb 
4532b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid);
4533b4c3e9b5SBjoern A. Zeeb 
4534b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4535b4c3e9b5SBjoern A. Zeeb 		return brcmf_pmksa_v3_op(ifp, pmksa, false);
4536b4c3e9b5SBjoern A. Zeeb 
4537b4c3e9b5SBjoern A. Zeeb 	/* TODO: implement PMKID_V2 */
4538b4c3e9b5SBjoern A. Zeeb 
4539b4c3e9b5SBjoern A. Zeeb 	npmk = le32_to_cpu(cfg->pmk_list.npmk);
4540b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < npmk; i++)
4541b4c3e9b5SBjoern A. Zeeb 		if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
4542b4c3e9b5SBjoern A. Zeeb 			break;
4543b4c3e9b5SBjoern A. Zeeb 
4544b4c3e9b5SBjoern A. Zeeb 	if ((npmk > 0) && (i < npmk)) {
4545b4c3e9b5SBjoern A. Zeeb 		for (; i < (npmk - 1); i++) {
4546b4c3e9b5SBjoern A. Zeeb 			memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
4547b4c3e9b5SBjoern A. Zeeb 			memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
4548b4c3e9b5SBjoern A. Zeeb 			       WLAN_PMKID_LEN);
4549b4c3e9b5SBjoern A. Zeeb 		}
4550b4c3e9b5SBjoern A. Zeeb 		memset(&pmk[i], 0, sizeof(*pmk));
4551b4c3e9b5SBjoern A. Zeeb 		cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
4552b4c3e9b5SBjoern A. Zeeb 	} else {
4553b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Cache entry not found\n");
4554b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
4555b4c3e9b5SBjoern A. Zeeb 	}
4556b4c3e9b5SBjoern A. Zeeb 
4557b4c3e9b5SBjoern A. Zeeb 	err = brcmf_update_pmklist(cfg, ifp);
4558b4c3e9b5SBjoern A. Zeeb 
4559b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
4560b4c3e9b5SBjoern A. Zeeb 	return err;
4561b4c3e9b5SBjoern A. Zeeb 
4562b4c3e9b5SBjoern A. Zeeb }
4563b4c3e9b5SBjoern A. Zeeb 
4564b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_flush_pmksa(struct wiphy * wiphy,struct net_device * ndev)4565b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
4566b4c3e9b5SBjoern A. Zeeb {
4567b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4568b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
4569b4c3e9b5SBjoern A. Zeeb 	s32 err;
4570b4c3e9b5SBjoern A. Zeeb 
4571b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
4572b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
4573b4c3e9b5SBjoern A. Zeeb 		return -EIO;
4574b4c3e9b5SBjoern A. Zeeb 
4575b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4576b4c3e9b5SBjoern A. Zeeb 		return brcmf_pmksa_v3_op(ifp, NULL, false);
4577b4c3e9b5SBjoern A. Zeeb 
4578b4c3e9b5SBjoern A. Zeeb 	/* TODO: implement PMKID_V2 */
4579b4c3e9b5SBjoern A. Zeeb 
4580b4c3e9b5SBjoern A. Zeeb 	memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
4581b4c3e9b5SBjoern A. Zeeb 	err = brcmf_update_pmklist(cfg, ifp);
4582b4c3e9b5SBjoern A. Zeeb 
4583b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
4584b4c3e9b5SBjoern A. Zeeb 	return err;
4585b4c3e9b5SBjoern A. Zeeb 
4586b4c3e9b5SBjoern A. Zeeb }
4587b4c3e9b5SBjoern A. Zeeb 
brcmf_configure_opensecurity(struct brcmf_if * ifp)4588b4c3e9b5SBjoern A. Zeeb static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
4589b4c3e9b5SBjoern A. Zeeb {
4590b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
4591b4c3e9b5SBjoern A. Zeeb 	s32 err;
4592b4c3e9b5SBjoern A. Zeeb 	s32 wpa_val;
4593b4c3e9b5SBjoern A. Zeeb 
4594b4c3e9b5SBjoern A. Zeeb 	/* set auth */
4595b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
4596b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
4597b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "auth error %d\n", err);
4598b4c3e9b5SBjoern A. Zeeb 		return err;
4599b4c3e9b5SBjoern A. Zeeb 	}
4600b4c3e9b5SBjoern A. Zeeb 	/* set wsec */
4601b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
4602b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
4603b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wsec error %d\n", err);
4604b4c3e9b5SBjoern A. Zeeb 		return err;
4605b4c3e9b5SBjoern A. Zeeb 	}
4606b4c3e9b5SBjoern A. Zeeb 	/* set upper-layer auth */
4607b4c3e9b5SBjoern A. Zeeb 	if (brcmf_is_ibssmode(ifp->vif))
4608b4c3e9b5SBjoern A. Zeeb 		wpa_val = WPA_AUTH_NONE;
4609b4c3e9b5SBjoern A. Zeeb 	else
4610b4c3e9b5SBjoern A. Zeeb 		wpa_val = WPA_AUTH_DISABLED;
4611b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val);
4612b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
4613b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wpa_auth error %d\n", err);
4614b4c3e9b5SBjoern A. Zeeb 		return err;
4615b4c3e9b5SBjoern A. Zeeb 	}
4616b4c3e9b5SBjoern A. Zeeb 
4617b4c3e9b5SBjoern A. Zeeb 	return 0;
4618b4c3e9b5SBjoern A. Zeeb }
4619b4c3e9b5SBjoern A. Zeeb 
4620902136e0SBjoern A. Zeeb #if defined(__linux__)
brcmf_valid_wpa_oui(u8 * oui,bool is_rsn_ie)4621b4c3e9b5SBjoern A. Zeeb static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
4622902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4623902136e0SBjoern A. Zeeb static bool brcmf_valid_wpa_oui(const u8 *oui, bool is_rsn_ie)
4624902136e0SBjoern A. Zeeb #endif
4625b4c3e9b5SBjoern A. Zeeb {
4626b4c3e9b5SBjoern A. Zeeb 	if (is_rsn_ie)
4627b4c3e9b5SBjoern A. Zeeb 		return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
4628b4c3e9b5SBjoern A. Zeeb 
4629b4c3e9b5SBjoern A. Zeeb 	return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
4630b4c3e9b5SBjoern A. Zeeb }
4631b4c3e9b5SBjoern A. Zeeb 
4632b4c3e9b5SBjoern A. Zeeb static s32
brcmf_configure_wpaie(struct brcmf_if * ifp,const struct brcmf_vs_tlv * wpa_ie,bool is_rsn_ie)4633b4c3e9b5SBjoern A. Zeeb brcmf_configure_wpaie(struct brcmf_if *ifp,
4634b4c3e9b5SBjoern A. Zeeb 		      const struct brcmf_vs_tlv *wpa_ie,
4635b4c3e9b5SBjoern A. Zeeb 		      bool is_rsn_ie)
4636b4c3e9b5SBjoern A. Zeeb {
4637b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
4638b4c3e9b5SBjoern A. Zeeb 	u32 auth = 0; /* d11 open authentication */
4639b4c3e9b5SBjoern A. Zeeb 	u16 count;
4640b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
4641b4c3e9b5SBjoern A. Zeeb 	s32 len;
4642b4c3e9b5SBjoern A. Zeeb 	u32 i;
4643b4c3e9b5SBjoern A. Zeeb 	u32 wsec;
4644b4c3e9b5SBjoern A. Zeeb 	u32 pval = 0;
4645b4c3e9b5SBjoern A. Zeeb 	u32 gval = 0;
4646b4c3e9b5SBjoern A. Zeeb 	u32 wpa_auth = 0;
4647b4c3e9b5SBjoern A. Zeeb 	u32 offset;
4648902136e0SBjoern A. Zeeb #if defined(__linux__)
4649b4c3e9b5SBjoern A. Zeeb 	u8 *data;
4650902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4651902136e0SBjoern A. Zeeb 	const u8 *data;
4652902136e0SBjoern A. Zeeb #endif
4653b4c3e9b5SBjoern A. Zeeb 	u16 rsn_cap;
4654b4c3e9b5SBjoern A. Zeeb 	u32 wme_bss_disable;
4655b4c3e9b5SBjoern A. Zeeb 	u32 mfp;
4656b4c3e9b5SBjoern A. Zeeb 
4657b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
4658b4c3e9b5SBjoern A. Zeeb 	if (wpa_ie == NULL)
4659b4c3e9b5SBjoern A. Zeeb 		goto exit;
4660b4c3e9b5SBjoern A. Zeeb 
4661b4c3e9b5SBjoern A. Zeeb 	len = wpa_ie->len + TLV_HDR_LEN;
4662902136e0SBjoern A. Zeeb #if defined(__linux__)
4663b4c3e9b5SBjoern A. Zeeb 	data = (u8 *)wpa_ie;
4664902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4665902136e0SBjoern A. Zeeb 	data = (const u8 *)wpa_ie;
4666902136e0SBjoern A. Zeeb #endif
4667b4c3e9b5SBjoern A. Zeeb 	offset = TLV_HDR_LEN;
4668b4c3e9b5SBjoern A. Zeeb 	if (!is_rsn_ie)
4669b4c3e9b5SBjoern A. Zeeb 		offset += VS_IE_FIXED_HDR_LEN;
4670b4c3e9b5SBjoern A. Zeeb 	else
4671b4c3e9b5SBjoern A. Zeeb 		offset += WPA_IE_VERSION_LEN;
4672b4c3e9b5SBjoern A. Zeeb 
4673b4c3e9b5SBjoern A. Zeeb 	/* check for multicast cipher suite */
4674b4c3e9b5SBjoern A. Zeeb 	if (offset + WPA_IE_MIN_OUI_LEN > len) {
4675b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
4676b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "no multicast cipher suite\n");
4677b4c3e9b5SBjoern A. Zeeb 		goto exit;
4678b4c3e9b5SBjoern A. Zeeb 	}
4679b4c3e9b5SBjoern A. Zeeb 
4680b4c3e9b5SBjoern A. Zeeb 	if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
4681b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
4682b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "invalid OUI\n");
4683b4c3e9b5SBjoern A. Zeeb 		goto exit;
4684b4c3e9b5SBjoern A. Zeeb 	}
4685b4c3e9b5SBjoern A. Zeeb 	offset += TLV_OUI_LEN;
4686b4c3e9b5SBjoern A. Zeeb 
4687b4c3e9b5SBjoern A. Zeeb 	/* pick up multicast cipher */
4688b4c3e9b5SBjoern A. Zeeb 	switch (data[offset]) {
4689b4c3e9b5SBjoern A. Zeeb 	case WPA_CIPHER_NONE:
4690b4c3e9b5SBjoern A. Zeeb 		gval = 0;
4691b4c3e9b5SBjoern A. Zeeb 		break;
4692b4c3e9b5SBjoern A. Zeeb 	case WPA_CIPHER_WEP_40:
4693b4c3e9b5SBjoern A. Zeeb 	case WPA_CIPHER_WEP_104:
4694b4c3e9b5SBjoern A. Zeeb 		gval = WEP_ENABLED;
4695b4c3e9b5SBjoern A. Zeeb 		break;
4696b4c3e9b5SBjoern A. Zeeb 	case WPA_CIPHER_TKIP:
4697b4c3e9b5SBjoern A. Zeeb 		gval = TKIP_ENABLED;
4698b4c3e9b5SBjoern A. Zeeb 		break;
4699b4c3e9b5SBjoern A. Zeeb 	case WPA_CIPHER_AES_CCM:
4700b4c3e9b5SBjoern A. Zeeb 		gval = AES_ENABLED;
4701b4c3e9b5SBjoern A. Zeeb 		break;
4702b4c3e9b5SBjoern A. Zeeb 	default:
4703b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
4704b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Invalid multi cast cipher info\n");
4705b4c3e9b5SBjoern A. Zeeb 		goto exit;
4706b4c3e9b5SBjoern A. Zeeb 	}
4707b4c3e9b5SBjoern A. Zeeb 
4708b4c3e9b5SBjoern A. Zeeb 	offset++;
4709b4c3e9b5SBjoern A. Zeeb 	/* walk thru unicast cipher list and pick up what we recognize */
4710b4c3e9b5SBjoern A. Zeeb 	count = data[offset] + (data[offset + 1] << 8);
4711b4c3e9b5SBjoern A. Zeeb 	offset += WPA_IE_SUITE_COUNT_LEN;
4712b4c3e9b5SBjoern A. Zeeb 	/* Check for unicast suite(s) */
4713b4c3e9b5SBjoern A. Zeeb 	if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
4714b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
4715b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "no unicast cipher suite\n");
4716b4c3e9b5SBjoern A. Zeeb 		goto exit;
4717b4c3e9b5SBjoern A. Zeeb 	}
4718b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < count; i++) {
4719b4c3e9b5SBjoern A. Zeeb 		if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
4720b4c3e9b5SBjoern A. Zeeb 			err = -EINVAL;
4721b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid OUI\n");
4722b4c3e9b5SBjoern A. Zeeb 			goto exit;
4723b4c3e9b5SBjoern A. Zeeb 		}
4724b4c3e9b5SBjoern A. Zeeb 		offset += TLV_OUI_LEN;
4725b4c3e9b5SBjoern A. Zeeb 		switch (data[offset]) {
4726b4c3e9b5SBjoern A. Zeeb 		case WPA_CIPHER_NONE:
4727b4c3e9b5SBjoern A. Zeeb 			break;
4728b4c3e9b5SBjoern A. Zeeb 		case WPA_CIPHER_WEP_40:
4729b4c3e9b5SBjoern A. Zeeb 		case WPA_CIPHER_WEP_104:
4730b4c3e9b5SBjoern A. Zeeb 			pval |= WEP_ENABLED;
4731b4c3e9b5SBjoern A. Zeeb 			break;
4732b4c3e9b5SBjoern A. Zeeb 		case WPA_CIPHER_TKIP:
4733b4c3e9b5SBjoern A. Zeeb 			pval |= TKIP_ENABLED;
4734b4c3e9b5SBjoern A. Zeeb 			break;
4735b4c3e9b5SBjoern A. Zeeb 		case WPA_CIPHER_AES_CCM:
4736b4c3e9b5SBjoern A. Zeeb 			pval |= AES_ENABLED;
4737b4c3e9b5SBjoern A. Zeeb 			break;
4738b4c3e9b5SBjoern A. Zeeb 		default:
4739b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Invalid unicast security info\n");
4740b4c3e9b5SBjoern A. Zeeb 		}
4741b4c3e9b5SBjoern A. Zeeb 		offset++;
4742b4c3e9b5SBjoern A. Zeeb 	}
4743b4c3e9b5SBjoern A. Zeeb 	/* walk thru auth management suite list and pick up what we recognize */
4744b4c3e9b5SBjoern A. Zeeb 	count = data[offset] + (data[offset + 1] << 8);
4745b4c3e9b5SBjoern A. Zeeb 	offset += WPA_IE_SUITE_COUNT_LEN;
4746b4c3e9b5SBjoern A. Zeeb 	/* Check for auth key management suite(s) */
4747b4c3e9b5SBjoern A. Zeeb 	if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
4748b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
4749b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "no auth key mgmt suite\n");
4750b4c3e9b5SBjoern A. Zeeb 		goto exit;
4751b4c3e9b5SBjoern A. Zeeb 	}
4752b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < count; i++) {
4753b4c3e9b5SBjoern A. Zeeb 		if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
4754b4c3e9b5SBjoern A. Zeeb 			err = -EINVAL;
4755b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid OUI\n");
4756b4c3e9b5SBjoern A. Zeeb 			goto exit;
4757b4c3e9b5SBjoern A. Zeeb 		}
4758b4c3e9b5SBjoern A. Zeeb 		offset += TLV_OUI_LEN;
4759b4c3e9b5SBjoern A. Zeeb 		switch (data[offset]) {
4760b4c3e9b5SBjoern A. Zeeb 		case RSN_AKM_NONE:
4761b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
4762b4c3e9b5SBjoern A. Zeeb 			wpa_auth |= WPA_AUTH_NONE;
4763b4c3e9b5SBjoern A. Zeeb 			break;
4764b4c3e9b5SBjoern A. Zeeb 		case RSN_AKM_UNSPECIFIED:
4765b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
4766b4c3e9b5SBjoern A. Zeeb 			is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
4767b4c3e9b5SBjoern A. Zeeb 				    (wpa_auth |= WPA_AUTH_UNSPECIFIED);
4768b4c3e9b5SBjoern A. Zeeb 			break;
4769b4c3e9b5SBjoern A. Zeeb 		case RSN_AKM_PSK:
4770b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
4771b4c3e9b5SBjoern A. Zeeb 			is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
4772b4c3e9b5SBjoern A. Zeeb 				    (wpa_auth |= WPA_AUTH_PSK);
4773b4c3e9b5SBjoern A. Zeeb 			break;
4774b4c3e9b5SBjoern A. Zeeb 		case RSN_AKM_SHA256_PSK:
4775b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
4776b4c3e9b5SBjoern A. Zeeb 			wpa_auth |= WPA2_AUTH_PSK_SHA256;
4777b4c3e9b5SBjoern A. Zeeb 			break;
4778b4c3e9b5SBjoern A. Zeeb 		case RSN_AKM_SHA256_1X:
4779b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
4780b4c3e9b5SBjoern A. Zeeb 			wpa_auth |= WPA2_AUTH_1X_SHA256;
4781b4c3e9b5SBjoern A. Zeeb 			break;
4782b4c3e9b5SBjoern A. Zeeb 		case RSN_AKM_SAE:
4783b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "RSN_AKM_SAE\n");
4784b4c3e9b5SBjoern A. Zeeb 			wpa_auth |= WPA3_AUTH_SAE_PSK;
4785b4c3e9b5SBjoern A. Zeeb 			break;
4786b4c3e9b5SBjoern A. Zeeb 		default:
4787b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Invalid key mgmt info\n");
4788b4c3e9b5SBjoern A. Zeeb 		}
4789b4c3e9b5SBjoern A. Zeeb 		offset++;
4790b4c3e9b5SBjoern A. Zeeb 	}
4791b4c3e9b5SBjoern A. Zeeb 
4792b4c3e9b5SBjoern A. Zeeb 	mfp = BRCMF_MFP_NONE;
4793b4c3e9b5SBjoern A. Zeeb 	if (is_rsn_ie) {
4794b4c3e9b5SBjoern A. Zeeb 		wme_bss_disable = 1;
4795b4c3e9b5SBjoern A. Zeeb 		if ((offset + RSN_CAP_LEN) <= len) {
4796b4c3e9b5SBjoern A. Zeeb 			rsn_cap = data[offset] + (data[offset + 1] << 8);
4797b4c3e9b5SBjoern A. Zeeb 			if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
4798b4c3e9b5SBjoern A. Zeeb 				wme_bss_disable = 0;
4799b4c3e9b5SBjoern A. Zeeb 			if (rsn_cap & RSN_CAP_MFPR_MASK) {
4800b4c3e9b5SBjoern A. Zeeb 				brcmf_dbg(TRACE, "MFP Required\n");
4801b4c3e9b5SBjoern A. Zeeb 				mfp = BRCMF_MFP_REQUIRED;
4802b4c3e9b5SBjoern A. Zeeb 				/* Firmware only supports mfp required in
4803b4c3e9b5SBjoern A. Zeeb 				 * combination with WPA2_AUTH_PSK_SHA256,
4804b4c3e9b5SBjoern A. Zeeb 				 * WPA2_AUTH_1X_SHA256, or WPA3_AUTH_SAE_PSK.
4805b4c3e9b5SBjoern A. Zeeb 				 */
4806b4c3e9b5SBjoern A. Zeeb 				if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
4807b4c3e9b5SBjoern A. Zeeb 						  WPA2_AUTH_1X_SHA256 |
4808b4c3e9b5SBjoern A. Zeeb 						  WPA3_AUTH_SAE_PSK))) {
4809b4c3e9b5SBjoern A. Zeeb 					err = -EINVAL;
4810b4c3e9b5SBjoern A. Zeeb 					goto exit;
4811b4c3e9b5SBjoern A. Zeeb 				}
4812b4c3e9b5SBjoern A. Zeeb 				/* Firmware has requirement that WPA2_AUTH_PSK/
4813b4c3e9b5SBjoern A. Zeeb 				 * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
4814b4c3e9b5SBjoern A. Zeeb 				 * is to be included in the rsn ie.
4815b4c3e9b5SBjoern A. Zeeb 				 */
4816b4c3e9b5SBjoern A. Zeeb 				if (wpa_auth & WPA2_AUTH_PSK_SHA256)
4817b4c3e9b5SBjoern A. Zeeb 					wpa_auth |= WPA2_AUTH_PSK;
4818b4c3e9b5SBjoern A. Zeeb 				else if (wpa_auth & WPA2_AUTH_1X_SHA256)
4819b4c3e9b5SBjoern A. Zeeb 					wpa_auth |= WPA2_AUTH_UNSPECIFIED;
4820b4c3e9b5SBjoern A. Zeeb 			} else if (rsn_cap & RSN_CAP_MFPC_MASK) {
4821b4c3e9b5SBjoern A. Zeeb 				brcmf_dbg(TRACE, "MFP Capable\n");
4822b4c3e9b5SBjoern A. Zeeb 				mfp = BRCMF_MFP_CAPABLE;
4823b4c3e9b5SBjoern A. Zeeb 			}
4824b4c3e9b5SBjoern A. Zeeb 		}
4825b4c3e9b5SBjoern A. Zeeb 		offset += RSN_CAP_LEN;
4826b4c3e9b5SBjoern A. Zeeb 		/* set wme_bss_disable to sync RSN Capabilities */
4827b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
4828b4c3e9b5SBjoern A. Zeeb 					       wme_bss_disable);
4829b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
4830b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "wme_bss_disable error %d\n", err);
4831b4c3e9b5SBjoern A. Zeeb 			goto exit;
4832b4c3e9b5SBjoern A. Zeeb 		}
4833b4c3e9b5SBjoern A. Zeeb 
4834b4c3e9b5SBjoern A. Zeeb 		/* Skip PMKID cnt as it is know to be 0 for AP. */
4835b4c3e9b5SBjoern A. Zeeb 		offset += RSN_PMKID_COUNT_LEN;
4836b4c3e9b5SBjoern A. Zeeb 
4837b4c3e9b5SBjoern A. Zeeb 		/* See if there is BIP wpa suite left for MFP */
4838b4c3e9b5SBjoern A. Zeeb 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
4839b4c3e9b5SBjoern A. Zeeb 		    ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
4840b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_bsscfg_data_set(ifp, "bip",
4841b4c3e9b5SBjoern A. Zeeb 							&data[offset],
4842b4c3e9b5SBjoern A. Zeeb 							WPA_IE_MIN_OUI_LEN);
4843b4c3e9b5SBjoern A. Zeeb 			if (err < 0) {
4844b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "bip error %d\n", err);
4845b4c3e9b5SBjoern A. Zeeb 				goto exit;
4846b4c3e9b5SBjoern A. Zeeb 			}
4847b4c3e9b5SBjoern A. Zeeb 		}
4848b4c3e9b5SBjoern A. Zeeb 	}
4849b4c3e9b5SBjoern A. Zeeb 	/* FOR WPS , set SES_OW_ENABLED */
4850b4c3e9b5SBjoern A. Zeeb 	wsec = (pval | gval | SES_OW_ENABLED);
4851b4c3e9b5SBjoern A. Zeeb 
4852b4c3e9b5SBjoern A. Zeeb 	/* set auth */
4853b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
4854b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
4855b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "auth error %d\n", err);
4856b4c3e9b5SBjoern A. Zeeb 		goto exit;
4857b4c3e9b5SBjoern A. Zeeb 	}
4858b4c3e9b5SBjoern A. Zeeb 	/* set wsec */
4859b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
4860b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
4861b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wsec error %d\n", err);
4862b4c3e9b5SBjoern A. Zeeb 		goto exit;
4863b4c3e9b5SBjoern A. Zeeb 	}
4864b4c3e9b5SBjoern A. Zeeb 	/* Configure MFP, this needs to go after wsec otherwise the wsec command
4865b4c3e9b5SBjoern A. Zeeb 	 * will overwrite the values set by MFP
4866b4c3e9b5SBjoern A. Zeeb 	 */
4867b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
4868b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
4869b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
4870b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "mfp error %d\n", err);
4871b4c3e9b5SBjoern A. Zeeb 			goto exit;
4872b4c3e9b5SBjoern A. Zeeb 		}
4873b4c3e9b5SBjoern A. Zeeb 	}
4874b4c3e9b5SBjoern A. Zeeb 	/* set upper-layer auth */
4875b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
4876b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
4877b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "wpa_auth error %d\n", err);
4878b4c3e9b5SBjoern A. Zeeb 		goto exit;
4879b4c3e9b5SBjoern A. Zeeb 	}
4880b4c3e9b5SBjoern A. Zeeb 
4881b4c3e9b5SBjoern A. Zeeb exit:
4882b4c3e9b5SBjoern A. Zeeb 	return err;
4883b4c3e9b5SBjoern A. Zeeb }
4884b4c3e9b5SBjoern A. Zeeb 
4885b4c3e9b5SBjoern A. Zeeb static s32
brcmf_parse_vndr_ies(const u8 * vndr_ie_buf,u32 vndr_ie_len,struct parsed_vndr_ies * vndr_ies)4886b4c3e9b5SBjoern A. Zeeb brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
4887b4c3e9b5SBjoern A. Zeeb 		     struct parsed_vndr_ies *vndr_ies)
4888b4c3e9b5SBjoern A. Zeeb {
4889902136e0SBjoern A. Zeeb #if defined(__linux__)
4890b4c3e9b5SBjoern A. Zeeb 	struct brcmf_vs_tlv *vndrie;
4891b4c3e9b5SBjoern A. Zeeb 	struct brcmf_tlv *ie;
4892902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4893902136e0SBjoern A. Zeeb 	const struct brcmf_vs_tlv *vndrie;
4894902136e0SBjoern A. Zeeb 	const struct brcmf_tlv *ie;
4895902136e0SBjoern A. Zeeb #endif
4896b4c3e9b5SBjoern A. Zeeb 	struct parsed_vndr_ie_info *parsed_info;
4897b4c3e9b5SBjoern A. Zeeb 	s32 remaining_len;
4898b4c3e9b5SBjoern A. Zeeb 
4899b4c3e9b5SBjoern A. Zeeb 	remaining_len = (s32)vndr_ie_len;
4900b4c3e9b5SBjoern A. Zeeb 	memset(vndr_ies, 0, sizeof(*vndr_ies));
4901b4c3e9b5SBjoern A. Zeeb 
4902902136e0SBjoern A. Zeeb #if defined(__linux__)
4903b4c3e9b5SBjoern A. Zeeb 	ie = (struct brcmf_tlv *)vndr_ie_buf;
4904902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4905902136e0SBjoern A. Zeeb 	ie = (const struct brcmf_tlv *)vndr_ie_buf;
4906902136e0SBjoern A. Zeeb #endif
4907b4c3e9b5SBjoern A. Zeeb 	while (ie) {
4908b4c3e9b5SBjoern A. Zeeb 		if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
4909b4c3e9b5SBjoern A. Zeeb 			goto next;
4910902136e0SBjoern A. Zeeb #if defined(__linux__)
4911b4c3e9b5SBjoern A. Zeeb 		vndrie = (struct brcmf_vs_tlv *)ie;
4912902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4913902136e0SBjoern A. Zeeb 		vndrie = (const struct brcmf_vs_tlv *)ie;
4914902136e0SBjoern A. Zeeb #endif
4915b4c3e9b5SBjoern A. Zeeb 		/* len should be bigger than OUI length + one */
4916b4c3e9b5SBjoern A. Zeeb 		if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
4917b4c3e9b5SBjoern A. Zeeb 			brcmf_err("invalid vndr ie. length is too small %d\n",
4918b4c3e9b5SBjoern A. Zeeb 				  vndrie->len);
4919b4c3e9b5SBjoern A. Zeeb 			goto next;
4920b4c3e9b5SBjoern A. Zeeb 		}
4921b4c3e9b5SBjoern A. Zeeb 		/* if wpa or wme ie, do not add ie */
4922b4c3e9b5SBjoern A. Zeeb 		if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
4923b4c3e9b5SBjoern A. Zeeb 		    ((vndrie->oui_type == WPA_OUI_TYPE) ||
4924b4c3e9b5SBjoern A. Zeeb 		    (vndrie->oui_type == WME_OUI_TYPE))) {
4925b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
4926b4c3e9b5SBjoern A. Zeeb 			goto next;
4927b4c3e9b5SBjoern A. Zeeb 		}
4928b4c3e9b5SBjoern A. Zeeb 
4929b4c3e9b5SBjoern A. Zeeb 		parsed_info = &vndr_ies->ie_info[vndr_ies->count];
4930b4c3e9b5SBjoern A. Zeeb 
4931b4c3e9b5SBjoern A. Zeeb 		/* save vndr ie information */
4932902136e0SBjoern A. Zeeb #if defined(__linux__)
4933b4c3e9b5SBjoern A. Zeeb 		parsed_info->ie_ptr = (char *)vndrie;
4934902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4935902136e0SBjoern A. Zeeb 		parsed_info->ie_ptr = (const char *)vndrie;
4936902136e0SBjoern A. Zeeb #endif
4937b4c3e9b5SBjoern A. Zeeb 		parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
4938b4c3e9b5SBjoern A. Zeeb 		memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
4939b4c3e9b5SBjoern A. Zeeb 
4940b4c3e9b5SBjoern A. Zeeb 		vndr_ies->count++;
4941b4c3e9b5SBjoern A. Zeeb 
4942b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "** OUI %3ph, type 0x%02x\n",
4943b4c3e9b5SBjoern A. Zeeb 			  parsed_info->vndrie.oui,
4944b4c3e9b5SBjoern A. Zeeb 			  parsed_info->vndrie.oui_type);
4945b4c3e9b5SBjoern A. Zeeb 
4946b4c3e9b5SBjoern A. Zeeb 		if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
4947b4c3e9b5SBjoern A. Zeeb 			break;
4948b4c3e9b5SBjoern A. Zeeb next:
4949b4c3e9b5SBjoern A. Zeeb 		remaining_len -= (ie->len + TLV_HDR_LEN);
4950b4c3e9b5SBjoern A. Zeeb 		if (remaining_len <= TLV_HDR_LEN)
4951b4c3e9b5SBjoern A. Zeeb 			ie = NULL;
4952b4c3e9b5SBjoern A. Zeeb 		else
4953902136e0SBjoern A. Zeeb #if defined(__linux__)
4954b4c3e9b5SBjoern A. Zeeb 			ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
4955902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4956902136e0SBjoern A. Zeeb 			ie = (const struct brcmf_tlv *)(((const u8 *)ie) + ie->len +
4957902136e0SBjoern A. Zeeb #endif
4958b4c3e9b5SBjoern A. Zeeb 				TLV_HDR_LEN);
4959b4c3e9b5SBjoern A. Zeeb 	}
4960b4c3e9b5SBjoern A. Zeeb 	return 0;
4961b4c3e9b5SBjoern A. Zeeb }
4962b4c3e9b5SBjoern A. Zeeb 
4963b4c3e9b5SBjoern A. Zeeb static u32
4964902136e0SBjoern A. Zeeb #if defined(__linux__)
brcmf_vndr_ie(u8 * iebuf,s32 pktflag,u8 * ie_ptr,u32 ie_len,s8 * add_del_cmd)4965b4c3e9b5SBjoern A. Zeeb brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
4966902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
4967902136e0SBjoern A. Zeeb brcmf_vndr_ie(u8 *iebuf, s32 pktflag, const u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
4968902136e0SBjoern A. Zeeb #endif
4969b4c3e9b5SBjoern A. Zeeb {
4970b4c3e9b5SBjoern A. Zeeb 	strscpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN);
4971b4c3e9b5SBjoern A. Zeeb 
4972b4c3e9b5SBjoern A. Zeeb 	put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
4973b4c3e9b5SBjoern A. Zeeb 
4974b4c3e9b5SBjoern A. Zeeb 	put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
4975b4c3e9b5SBjoern A. Zeeb 
4976b4c3e9b5SBjoern A. Zeeb 	memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
4977b4c3e9b5SBjoern A. Zeeb 
4978b4c3e9b5SBjoern A. Zeeb 	return ie_len + VNDR_IE_HDR_SIZE;
4979b4c3e9b5SBjoern A. Zeeb }
4980b4c3e9b5SBjoern A. Zeeb 
brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif * vif,s32 pktflag,const u8 * vndr_ie_buf,u32 vndr_ie_len)4981b4c3e9b5SBjoern A. Zeeb s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
4982b4c3e9b5SBjoern A. Zeeb 			  const u8 *vndr_ie_buf, u32 vndr_ie_len)
4983b4c3e9b5SBjoern A. Zeeb {
4984b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr;
4985b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
4986b4c3e9b5SBjoern A. Zeeb 	struct vif_saved_ie *saved_ie;
4987b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
4988b4c3e9b5SBjoern A. Zeeb 	u8  *iovar_ie_buf;
4989b4c3e9b5SBjoern A. Zeeb 	u8  *curr_ie_buf;
4990b4c3e9b5SBjoern A. Zeeb 	u8  *mgmt_ie_buf = NULL;
4991b4c3e9b5SBjoern A. Zeeb 	int mgmt_ie_buf_len;
4992b4c3e9b5SBjoern A. Zeeb 	u32 *mgmt_ie_len;
4993b4c3e9b5SBjoern A. Zeeb 	u32 del_add_ie_buf_len = 0;
4994b4c3e9b5SBjoern A. Zeeb 	u32 total_ie_buf_len = 0;
4995b4c3e9b5SBjoern A. Zeeb 	u32 parsed_ie_buf_len = 0;
4996b4c3e9b5SBjoern A. Zeeb 	struct parsed_vndr_ies old_vndr_ies;
4997b4c3e9b5SBjoern A. Zeeb 	struct parsed_vndr_ies new_vndr_ies;
4998b4c3e9b5SBjoern A. Zeeb 	struct parsed_vndr_ie_info *vndrie_info;
4999b4c3e9b5SBjoern A. Zeeb 	s32 i;
5000b4c3e9b5SBjoern A. Zeeb 	u8 *ptr;
5001b4c3e9b5SBjoern A. Zeeb 	int remained_buf_len;
5002b4c3e9b5SBjoern A. Zeeb 
5003b4c3e9b5SBjoern A. Zeeb 	if (!vif)
5004b4c3e9b5SBjoern A. Zeeb 		return -ENODEV;
5005b4c3e9b5SBjoern A. Zeeb 	ifp = vif->ifp;
5006b4c3e9b5SBjoern A. Zeeb 	drvr = ifp->drvr;
5007b4c3e9b5SBjoern A. Zeeb 	saved_ie = &vif->saved_ie;
5008b4c3e9b5SBjoern A. Zeeb 
5009b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
5010b4c3e9b5SBjoern A. Zeeb 		  pktflag);
5011b4c3e9b5SBjoern A. Zeeb 	iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5012b4c3e9b5SBjoern A. Zeeb 	if (!iovar_ie_buf)
5013b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
5014b4c3e9b5SBjoern A. Zeeb 	curr_ie_buf = iovar_ie_buf;
5015b4c3e9b5SBjoern A. Zeeb 	switch (pktflag) {
5016b4c3e9b5SBjoern A. Zeeb 	case BRCMF_VNDR_IE_PRBREQ_FLAG:
5017b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf = saved_ie->probe_req_ie;
5018b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_len = &saved_ie->probe_req_ie_len;
5019b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
5020b4c3e9b5SBjoern A. Zeeb 		break;
5021b4c3e9b5SBjoern A. Zeeb 	case BRCMF_VNDR_IE_PRBRSP_FLAG:
5022b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf = saved_ie->probe_res_ie;
5023b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_len = &saved_ie->probe_res_ie_len;
5024b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
5025b4c3e9b5SBjoern A. Zeeb 		break;
5026b4c3e9b5SBjoern A. Zeeb 	case BRCMF_VNDR_IE_BEACON_FLAG:
5027b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf = saved_ie->beacon_ie;
5028b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_len = &saved_ie->beacon_ie_len;
5029b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
5030b4c3e9b5SBjoern A. Zeeb 		break;
5031b4c3e9b5SBjoern A. Zeeb 	case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
5032b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf = saved_ie->assoc_req_ie;
5033b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_len = &saved_ie->assoc_req_ie_len;
5034b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
5035b4c3e9b5SBjoern A. Zeeb 		break;
5036b4c3e9b5SBjoern A. Zeeb 	case BRCMF_VNDR_IE_ASSOCRSP_FLAG:
5037b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf = saved_ie->assoc_res_ie;
5038b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_len = &saved_ie->assoc_res_ie_len;
5039b4c3e9b5SBjoern A. Zeeb 		mgmt_ie_buf_len = sizeof(saved_ie->assoc_res_ie);
5040b4c3e9b5SBjoern A. Zeeb 		break;
5041b4c3e9b5SBjoern A. Zeeb 	default:
5042b4c3e9b5SBjoern A. Zeeb 		err = -EPERM;
5043b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "not suitable type\n");
5044b4c3e9b5SBjoern A. Zeeb 		goto exit;
5045b4c3e9b5SBjoern A. Zeeb 	}
5046b4c3e9b5SBjoern A. Zeeb 
5047b4c3e9b5SBjoern A. Zeeb 	if (vndr_ie_len > mgmt_ie_buf_len) {
5048b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
5049b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "extra IE size too big\n");
5050b4c3e9b5SBjoern A. Zeeb 		goto exit;
5051b4c3e9b5SBjoern A. Zeeb 	}
5052b4c3e9b5SBjoern A. Zeeb 
5053b4c3e9b5SBjoern A. Zeeb 	/* parse and save new vndr_ie in curr_ie_buff before comparing it */
5054b4c3e9b5SBjoern A. Zeeb 	if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
5055b4c3e9b5SBjoern A. Zeeb 		ptr = curr_ie_buf;
5056b4c3e9b5SBjoern A. Zeeb 		brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
5057b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < new_vndr_ies.count; i++) {
5058b4c3e9b5SBjoern A. Zeeb 			vndrie_info = &new_vndr_ies.ie_info[i];
5059b4c3e9b5SBjoern A. Zeeb 			memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
5060b4c3e9b5SBjoern A. Zeeb 			       vndrie_info->ie_len);
5061b4c3e9b5SBjoern A. Zeeb 			parsed_ie_buf_len += vndrie_info->ie_len;
5062b4c3e9b5SBjoern A. Zeeb 		}
5063b4c3e9b5SBjoern A. Zeeb 	}
5064b4c3e9b5SBjoern A. Zeeb 
5065b4c3e9b5SBjoern A. Zeeb 	if (mgmt_ie_buf && *mgmt_ie_len) {
5066b4c3e9b5SBjoern A. Zeeb 		if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
5067b4c3e9b5SBjoern A. Zeeb 		    (memcmp(mgmt_ie_buf, curr_ie_buf,
5068b4c3e9b5SBjoern A. Zeeb 			    parsed_ie_buf_len) == 0)) {
5069b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
5070b4c3e9b5SBjoern A. Zeeb 			goto exit;
5071b4c3e9b5SBjoern A. Zeeb 		}
5072b4c3e9b5SBjoern A. Zeeb 
5073b4c3e9b5SBjoern A. Zeeb 		/* parse old vndr_ie */
5074b4c3e9b5SBjoern A. Zeeb 		brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
5075b4c3e9b5SBjoern A. Zeeb 
5076b4c3e9b5SBjoern A. Zeeb 		/* make a command to delete old ie */
5077b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < old_vndr_ies.count; i++) {
5078b4c3e9b5SBjoern A. Zeeb 			vndrie_info = &old_vndr_ies.ie_info[i];
5079b4c3e9b5SBjoern A. Zeeb 
5080b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%3ph\n",
5081b4c3e9b5SBjoern A. Zeeb 				  vndrie_info->vndrie.id,
5082b4c3e9b5SBjoern A. Zeeb 				  vndrie_info->vndrie.len,
5083b4c3e9b5SBjoern A. Zeeb 				  vndrie_info->vndrie.oui);
5084b4c3e9b5SBjoern A. Zeeb 
5085b4c3e9b5SBjoern A. Zeeb 			del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
5086b4c3e9b5SBjoern A. Zeeb 							   vndrie_info->ie_ptr,
5087b4c3e9b5SBjoern A. Zeeb 							   vndrie_info->ie_len,
5088b4c3e9b5SBjoern A. Zeeb 							   "del");
5089b4c3e9b5SBjoern A. Zeeb 			curr_ie_buf += del_add_ie_buf_len;
5090b4c3e9b5SBjoern A. Zeeb 			total_ie_buf_len += del_add_ie_buf_len;
5091b4c3e9b5SBjoern A. Zeeb 		}
5092b4c3e9b5SBjoern A. Zeeb 	}
5093b4c3e9b5SBjoern A. Zeeb 
5094b4c3e9b5SBjoern A. Zeeb 	*mgmt_ie_len = 0;
5095b4c3e9b5SBjoern A. Zeeb 	/* Add if there is any extra IE */
5096b4c3e9b5SBjoern A. Zeeb 	if (mgmt_ie_buf && parsed_ie_buf_len) {
5097b4c3e9b5SBjoern A. Zeeb 		ptr = mgmt_ie_buf;
5098b4c3e9b5SBjoern A. Zeeb 
5099b4c3e9b5SBjoern A. Zeeb 		remained_buf_len = mgmt_ie_buf_len;
5100b4c3e9b5SBjoern A. Zeeb 
5101b4c3e9b5SBjoern A. Zeeb 		/* make a command to add new ie */
5102b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < new_vndr_ies.count; i++) {
5103b4c3e9b5SBjoern A. Zeeb 			vndrie_info = &new_vndr_ies.ie_info[i];
5104b4c3e9b5SBjoern A. Zeeb 
5105b4c3e9b5SBjoern A. Zeeb 			/* verify remained buf size before copy data */
5106b4c3e9b5SBjoern A. Zeeb 			if (remained_buf_len < (vndrie_info->vndrie.len +
5107b4c3e9b5SBjoern A. Zeeb 							VNDR_IE_VSIE_OFFSET)) {
5108b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "no space in mgmt_ie_buf: len left %d",
5109b4c3e9b5SBjoern A. Zeeb 					 remained_buf_len);
5110b4c3e9b5SBjoern A. Zeeb 				break;
5111b4c3e9b5SBjoern A. Zeeb 			}
5112b4c3e9b5SBjoern A. Zeeb 			remained_buf_len -= (vndrie_info->ie_len +
5113b4c3e9b5SBjoern A. Zeeb 					     VNDR_IE_VSIE_OFFSET);
5114b4c3e9b5SBjoern A. Zeeb 
5115b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%3ph\n",
5116b4c3e9b5SBjoern A. Zeeb 				  vndrie_info->vndrie.id,
5117b4c3e9b5SBjoern A. Zeeb 				  vndrie_info->vndrie.len,
5118b4c3e9b5SBjoern A. Zeeb 				  vndrie_info->vndrie.oui);
5119b4c3e9b5SBjoern A. Zeeb 
5120b4c3e9b5SBjoern A. Zeeb 			del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
5121b4c3e9b5SBjoern A. Zeeb 							   vndrie_info->ie_ptr,
5122b4c3e9b5SBjoern A. Zeeb 							   vndrie_info->ie_len,
5123b4c3e9b5SBjoern A. Zeeb 							   "add");
5124b4c3e9b5SBjoern A. Zeeb 
5125b4c3e9b5SBjoern A. Zeeb 			/* save the parsed IE in wl struct */
5126b4c3e9b5SBjoern A. Zeeb 			memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
5127b4c3e9b5SBjoern A. Zeeb 			       vndrie_info->ie_len);
5128b4c3e9b5SBjoern A. Zeeb 			*mgmt_ie_len += vndrie_info->ie_len;
5129b4c3e9b5SBjoern A. Zeeb 
5130b4c3e9b5SBjoern A. Zeeb 			curr_ie_buf += del_add_ie_buf_len;
5131b4c3e9b5SBjoern A. Zeeb 			total_ie_buf_len += del_add_ie_buf_len;
5132b4c3e9b5SBjoern A. Zeeb 		}
5133b4c3e9b5SBjoern A. Zeeb 	}
5134b4c3e9b5SBjoern A. Zeeb 	if (total_ie_buf_len) {
5135b4c3e9b5SBjoern A. Zeeb 		err  = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
5136b4c3e9b5SBjoern A. Zeeb 						 total_ie_buf_len);
5137b4c3e9b5SBjoern A. Zeeb 		if (err)
5138b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "vndr ie set error : %d\n", err);
5139b4c3e9b5SBjoern A. Zeeb 	}
5140b4c3e9b5SBjoern A. Zeeb 
5141b4c3e9b5SBjoern A. Zeeb exit:
5142b4c3e9b5SBjoern A. Zeeb 	kfree(iovar_ie_buf);
5143b4c3e9b5SBjoern A. Zeeb 	return err;
5144b4c3e9b5SBjoern A. Zeeb }
5145b4c3e9b5SBjoern A. Zeeb 
brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif * vif)5146b4c3e9b5SBjoern A. Zeeb s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
5147b4c3e9b5SBjoern A. Zeeb {
5148b4c3e9b5SBjoern A. Zeeb 	static const s32 pktflags[] = {
5149b4c3e9b5SBjoern A. Zeeb 		BRCMF_VNDR_IE_PRBRSP_FLAG,
5150b4c3e9b5SBjoern A. Zeeb 		BRCMF_VNDR_IE_BEACON_FLAG
5151b4c3e9b5SBjoern A. Zeeb 	};
5152b4c3e9b5SBjoern A. Zeeb 	int i;
5153b4c3e9b5SBjoern A. Zeeb 
5154b4c3e9b5SBjoern A. Zeeb 	if (vif->wdev.iftype == NL80211_IFTYPE_AP)
5155b4c3e9b5SBjoern A. Zeeb 		brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG, NULL, 0);
5156b4c3e9b5SBjoern A. Zeeb 	else
5157b4c3e9b5SBjoern A. Zeeb 		brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, NULL, 0);
5158b4c3e9b5SBjoern A. Zeeb 
5159b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(pktflags); i++)
5160b4c3e9b5SBjoern A. Zeeb 		brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
5161b4c3e9b5SBjoern A. Zeeb 
5162b4c3e9b5SBjoern A. Zeeb 	memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
5163b4c3e9b5SBjoern A. Zeeb 	return 0;
5164b4c3e9b5SBjoern A. Zeeb }
5165b4c3e9b5SBjoern A. Zeeb 
5166b4c3e9b5SBjoern A. Zeeb static s32
brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif * vif,struct cfg80211_beacon_data * beacon)5167b4c3e9b5SBjoern A. Zeeb brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
5168b4c3e9b5SBjoern A. Zeeb 			struct cfg80211_beacon_data *beacon)
5169b4c3e9b5SBjoern A. Zeeb {
5170b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = vif->ifp->drvr;
5171b4c3e9b5SBjoern A. Zeeb 	s32 err;
5172b4c3e9b5SBjoern A. Zeeb 
5173b4c3e9b5SBjoern A. Zeeb 	/* Set Beacon IEs to FW */
5174b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
5175b4c3e9b5SBjoern A. Zeeb 				    beacon->tail, beacon->tail_len);
5176b4c3e9b5SBjoern A. Zeeb 	if (err) {
5177b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Set Beacon IE Failed\n");
5178b4c3e9b5SBjoern A. Zeeb 		return err;
5179b4c3e9b5SBjoern A. Zeeb 	}
5180b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
5181b4c3e9b5SBjoern A. Zeeb 
5182b4c3e9b5SBjoern A. Zeeb 	/* Set Probe Response IEs to FW */
5183b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
5184b4c3e9b5SBjoern A. Zeeb 				    beacon->proberesp_ies,
5185b4c3e9b5SBjoern A. Zeeb 				    beacon->proberesp_ies_len);
5186b4c3e9b5SBjoern A. Zeeb 	if (err)
5187b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Set Probe Resp IE Failed\n");
5188b4c3e9b5SBjoern A. Zeeb 	else
5189b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
5190b4c3e9b5SBjoern A. Zeeb 
5191b4c3e9b5SBjoern A. Zeeb 	/* Set Assoc Response IEs to FW */
5192b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG,
5193b4c3e9b5SBjoern A. Zeeb 				    beacon->assocresp_ies,
5194b4c3e9b5SBjoern A. Zeeb 				    beacon->assocresp_ies_len);
5195b4c3e9b5SBjoern A. Zeeb 	if (err)
5196b4c3e9b5SBjoern A. Zeeb 		brcmf_err("Set Assoc Resp IE Failed\n");
5197b4c3e9b5SBjoern A. Zeeb 	else
5198b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc Resp\n");
5199b4c3e9b5SBjoern A. Zeeb 
5200b4c3e9b5SBjoern A. Zeeb 	return err;
5201b4c3e9b5SBjoern A. Zeeb }
5202b4c3e9b5SBjoern A. Zeeb 
5203b4c3e9b5SBjoern A. Zeeb static s32
brcmf_parse_configure_security(struct brcmf_if * ifp,struct cfg80211_ap_settings * settings,enum nl80211_iftype dev_role)5204b4c3e9b5SBjoern A. Zeeb brcmf_parse_configure_security(struct brcmf_if *ifp,
5205b4c3e9b5SBjoern A. Zeeb 			       struct cfg80211_ap_settings *settings,
5206b4c3e9b5SBjoern A. Zeeb 			       enum nl80211_iftype dev_role)
5207b4c3e9b5SBjoern A. Zeeb {
5208b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *rsn_ie;
5209b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_vs_tlv *wpa_ie;
5210b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
5211b4c3e9b5SBjoern A. Zeeb 
5212b4c3e9b5SBjoern A. Zeeb 	/* find the RSN_IE */
5213902136e0SBjoern A. Zeeb #if defined(__linux__)
5214b4c3e9b5SBjoern A. Zeeb 	rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
5215902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5216902136e0SBjoern A. Zeeb 	rsn_ie = brcmf_parse_tlvs(settings->beacon.tail,
5217902136e0SBjoern A. Zeeb #endif
5218b4c3e9b5SBjoern A. Zeeb 				  settings->beacon.tail_len, WLAN_EID_RSN);
5219b4c3e9b5SBjoern A. Zeeb 
5220b4c3e9b5SBjoern A. Zeeb 	/* find the WPA_IE */
5221902136e0SBjoern A. Zeeb #if defined(__linux__)
5222b4c3e9b5SBjoern A. Zeeb 	wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
5223902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5224902136e0SBjoern A. Zeeb 	wpa_ie = brcmf_find_wpaie(settings->beacon.tail,
5225902136e0SBjoern A. Zeeb #endif
5226b4c3e9b5SBjoern A. Zeeb 				  settings->beacon.tail_len);
5227b4c3e9b5SBjoern A. Zeeb 
5228b4c3e9b5SBjoern A. Zeeb 	if (wpa_ie || rsn_ie) {
5229b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "WPA(2) IE is found\n");
5230b4c3e9b5SBjoern A. Zeeb 		if (wpa_ie) {
5231b4c3e9b5SBjoern A. Zeeb 			/* WPA IE */
5232b4c3e9b5SBjoern A. Zeeb 			err = brcmf_configure_wpaie(ifp, wpa_ie, false);
5233b4c3e9b5SBjoern A. Zeeb 			if (err < 0)
5234b4c3e9b5SBjoern A. Zeeb 				return err;
5235b4c3e9b5SBjoern A. Zeeb 		} else {
5236902136e0SBjoern A. Zeeb #if defined(__linux__)
5237b4c3e9b5SBjoern A. Zeeb 			struct brcmf_vs_tlv *tmp_ie;
5238b4c3e9b5SBjoern A. Zeeb 
5239b4c3e9b5SBjoern A. Zeeb 			tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
5240902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5241902136e0SBjoern A. Zeeb 			const struct brcmf_vs_tlv *tmp_ie;
5242902136e0SBjoern A. Zeeb 
5243902136e0SBjoern A. Zeeb 			tmp_ie = (const struct brcmf_vs_tlv *)rsn_ie;
5244902136e0SBjoern A. Zeeb #endif
5245902136e0SBjoern A. Zeeb 
5246b4c3e9b5SBjoern A. Zeeb 
5247b4c3e9b5SBjoern A. Zeeb 			/* RSN IE */
5248b4c3e9b5SBjoern A. Zeeb 			err = brcmf_configure_wpaie(ifp, tmp_ie, true);
5249b4c3e9b5SBjoern A. Zeeb 			if (err < 0)
5250b4c3e9b5SBjoern A. Zeeb 				return err;
5251b4c3e9b5SBjoern A. Zeeb 		}
5252b4c3e9b5SBjoern A. Zeeb 	} else {
5253b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
5254b4c3e9b5SBjoern A. Zeeb 		brcmf_configure_opensecurity(ifp);
5255b4c3e9b5SBjoern A. Zeeb 	}
5256b4c3e9b5SBjoern A. Zeeb 
5257b4c3e9b5SBjoern A. Zeeb 	return err;
5258b4c3e9b5SBjoern A. Zeeb }
5259b4c3e9b5SBjoern A. Zeeb 
5260b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ap_settings * settings)5261b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
5262b4c3e9b5SBjoern A. Zeeb 			struct cfg80211_ap_settings *settings)
5263b4c3e9b5SBjoern A. Zeeb {
5264b4c3e9b5SBjoern A. Zeeb 	s32 ie_offset;
5265b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5266b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
5267b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
5268b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
5269b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_crypto_settings *crypto = &settings->crypto;
5270b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *ssid_ie;
5271b4c3e9b5SBjoern A. Zeeb 	const struct brcmf_tlv *country_ie;
5272b4c3e9b5SBjoern A. Zeeb 	struct brcmf_ssid_le ssid_le;
5273b4c3e9b5SBjoern A. Zeeb 	s32 err = -EPERM;
5274b4c3e9b5SBjoern A. Zeeb 	struct brcmf_join_params join_params;
5275b4c3e9b5SBjoern A. Zeeb 	enum nl80211_iftype dev_role;
5276b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fil_bss_enable_le bss_enable;
5277b4c3e9b5SBjoern A. Zeeb 	u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
5278b4c3e9b5SBjoern A. Zeeb 	bool mbss;
5279b4c3e9b5SBjoern A. Zeeb 	int is_11d;
5280b4c3e9b5SBjoern A. Zeeb 	bool supports_11d;
5281b4c3e9b5SBjoern A. Zeeb 	bool closednet;
5282b4c3e9b5SBjoern A. Zeeb 
5283b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
5284b4c3e9b5SBjoern A. Zeeb 		  settings->chandef.chan->hw_value,
5285b4c3e9b5SBjoern A. Zeeb 		  settings->chandef.center_freq1, settings->chandef.width,
5286b4c3e9b5SBjoern A. Zeeb 		  settings->beacon_interval, settings->dtim_period);
5287b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
5288b4c3e9b5SBjoern A. Zeeb 		  settings->ssid, settings->ssid_len, settings->auth_type,
5289b4c3e9b5SBjoern A. Zeeb 		  settings->inactivity_timeout);
5290b4c3e9b5SBjoern A. Zeeb 	dev_role = ifp->vif->wdev.iftype;
5291b4c3e9b5SBjoern A. Zeeb 	mbss = ifp->vif->mbss;
5292b4c3e9b5SBjoern A. Zeeb 
5293b4c3e9b5SBjoern A. Zeeb 	/* store current 11d setting */
5294b4c3e9b5SBjoern A. Zeeb 	if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY,
5295b4c3e9b5SBjoern A. Zeeb 				  &ifp->vif->is_11d)) {
5296b4c3e9b5SBjoern A. Zeeb 		is_11d = supports_11d = false;
5297b4c3e9b5SBjoern A. Zeeb 	} else {
5298902136e0SBjoern A. Zeeb #if defined(__linux__)
5299b4c3e9b5SBjoern A. Zeeb 		country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
5300902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5301902136e0SBjoern A. Zeeb 		country_ie = brcmf_parse_tlvs(settings->beacon.tail,
5302902136e0SBjoern A. Zeeb #endif
5303b4c3e9b5SBjoern A. Zeeb 					      settings->beacon.tail_len,
5304b4c3e9b5SBjoern A. Zeeb 					      WLAN_EID_COUNTRY);
5305b4c3e9b5SBjoern A. Zeeb 		is_11d = country_ie ? 1 : 0;
5306b4c3e9b5SBjoern A. Zeeb 		supports_11d = true;
5307b4c3e9b5SBjoern A. Zeeb 	}
5308b4c3e9b5SBjoern A. Zeeb 
5309b4c3e9b5SBjoern A. Zeeb 	memset(&ssid_le, 0, sizeof(ssid_le));
5310b4c3e9b5SBjoern A. Zeeb 	if (settings->ssid == NULL || settings->ssid_len == 0) {
5311b4c3e9b5SBjoern A. Zeeb 		ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
5312b4c3e9b5SBjoern A. Zeeb 		ssid_ie = brcmf_parse_tlvs(
5313902136e0SBjoern A. Zeeb #if defined(__linux__)
5314b4c3e9b5SBjoern A. Zeeb 				(u8 *)&settings->beacon.head[ie_offset],
5315902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5316902136e0SBjoern A. Zeeb 				&settings->beacon.head[ie_offset],
5317902136e0SBjoern A. Zeeb #endif
5318b4c3e9b5SBjoern A. Zeeb 				settings->beacon.head_len - ie_offset,
5319b4c3e9b5SBjoern A. Zeeb 				WLAN_EID_SSID);
5320b4c3e9b5SBjoern A. Zeeb 		if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
5321b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
5322b4c3e9b5SBjoern A. Zeeb 
5323b4c3e9b5SBjoern A. Zeeb 		memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
5324b4c3e9b5SBjoern A. Zeeb 		ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
5325b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
5326b4c3e9b5SBjoern A. Zeeb 	} else {
5327b4c3e9b5SBjoern A. Zeeb 		memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
5328b4c3e9b5SBjoern A. Zeeb 		ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
5329b4c3e9b5SBjoern A. Zeeb 	}
5330b4c3e9b5SBjoern A. Zeeb 
5331b4c3e9b5SBjoern A. Zeeb 	if (!mbss) {
5332b4c3e9b5SBjoern A. Zeeb 		brcmf_set_mpc(ifp, 0);
5333b4c3e9b5SBjoern A. Zeeb 		brcmf_configure_arp_nd_offload(ifp, false);
5334b4c3e9b5SBjoern A. Zeeb 	}
5335b4c3e9b5SBjoern A. Zeeb 
5336b4c3e9b5SBjoern A. Zeeb 	/* Parameters shared by all radio interfaces */
5337b4c3e9b5SBjoern A. Zeeb 	if (!mbss) {
5338b4c3e9b5SBjoern A. Zeeb 		if ((supports_11d) && (is_11d != ifp->vif->is_11d)) {
5339b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
5340b4c3e9b5SBjoern A. Zeeb 						    is_11d);
5341b4c3e9b5SBjoern A. Zeeb 			if (err < 0) {
5342b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "Regulatory Set Error, %d\n",
5343b4c3e9b5SBjoern A. Zeeb 					 err);
5344b4c3e9b5SBjoern A. Zeeb 				goto exit;
5345b4c3e9b5SBjoern A. Zeeb 			}
5346b4c3e9b5SBjoern A. Zeeb 		}
5347b4c3e9b5SBjoern A. Zeeb 		if (settings->beacon_interval) {
5348b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
5349b4c3e9b5SBjoern A. Zeeb 						    settings->beacon_interval);
5350b4c3e9b5SBjoern A. Zeeb 			if (err < 0) {
5351b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "Beacon Interval Set Error, %d\n",
5352b4c3e9b5SBjoern A. Zeeb 					 err);
5353b4c3e9b5SBjoern A. Zeeb 				goto exit;
5354b4c3e9b5SBjoern A. Zeeb 			}
5355b4c3e9b5SBjoern A. Zeeb 		}
5356b4c3e9b5SBjoern A. Zeeb 		if (settings->dtim_period) {
5357b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
5358b4c3e9b5SBjoern A. Zeeb 						    settings->dtim_period);
5359b4c3e9b5SBjoern A. Zeeb 			if (err < 0) {
5360b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "DTIM Interval Set Error, %d\n",
5361b4c3e9b5SBjoern A. Zeeb 					 err);
5362b4c3e9b5SBjoern A. Zeeb 				goto exit;
5363b4c3e9b5SBjoern A. Zeeb 			}
5364b4c3e9b5SBjoern A. Zeeb 		}
5365b4c3e9b5SBjoern A. Zeeb 
5366b4c3e9b5SBjoern A. Zeeb 		if ((dev_role == NL80211_IFTYPE_AP) &&
5367b4c3e9b5SBjoern A. Zeeb 		    ((ifp->ifidx == 0) ||
5368b4c3e9b5SBjoern A. Zeeb 		     (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB) &&
5369b4c3e9b5SBjoern A. Zeeb 		      !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)))) {
5370b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
5371b4c3e9b5SBjoern A. Zeeb 			if (err < 0) {
5372b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "BRCMF_C_DOWN error %d\n",
5373b4c3e9b5SBjoern A. Zeeb 					 err);
5374b4c3e9b5SBjoern A. Zeeb 				goto exit;
5375b4c3e9b5SBjoern A. Zeeb 			}
5376b4c3e9b5SBjoern A. Zeeb 			brcmf_fil_iovar_int_set(ifp, "apsta", 0);
5377b4c3e9b5SBjoern A. Zeeb 		}
5378b4c3e9b5SBjoern A. Zeeb 
5379b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
5380b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5381b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "SET INFRA error %d\n", err);
5382b4c3e9b5SBjoern A. Zeeb 			goto exit;
5383b4c3e9b5SBjoern A. Zeeb 		}
5384b4c3e9b5SBjoern A. Zeeb 	} else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) {
5385b4c3e9b5SBjoern A. Zeeb 		/* Multiple-BSS should use same 11d configuration */
5386b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
5387b4c3e9b5SBjoern A. Zeeb 		goto exit;
5388b4c3e9b5SBjoern A. Zeeb 	}
5389b4c3e9b5SBjoern A. Zeeb 
5390b4c3e9b5SBjoern A. Zeeb 	/* Interface specific setup */
5391b4c3e9b5SBjoern A. Zeeb 	if (dev_role == NL80211_IFTYPE_AP) {
5392b4c3e9b5SBjoern A. Zeeb 		if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
5393b4c3e9b5SBjoern A. Zeeb 			brcmf_fil_iovar_int_set(ifp, "mbss", 1);
5394b4c3e9b5SBjoern A. Zeeb 
5395b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
5396b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5397b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "setting AP mode failed %d\n",
5398b4c3e9b5SBjoern A. Zeeb 				 err);
5399b4c3e9b5SBjoern A. Zeeb 			goto exit;
5400b4c3e9b5SBjoern A. Zeeb 		}
5401b4c3e9b5SBjoern A. Zeeb 		if (!mbss) {
5402b4c3e9b5SBjoern A. Zeeb 			/* Firmware 10.x requires setting channel after enabling
5403b4c3e9b5SBjoern A. Zeeb 			 * AP and before bringing interface up.
5404b4c3e9b5SBjoern A. Zeeb 			 */
5405b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
5406b4c3e9b5SBjoern A. Zeeb 			if (err < 0) {
5407b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
5408b4c3e9b5SBjoern A. Zeeb 					 chanspec, err);
5409b4c3e9b5SBjoern A. Zeeb 				goto exit;
5410b4c3e9b5SBjoern A. Zeeb 			}
5411b4c3e9b5SBjoern A. Zeeb 		}
5412b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
5413b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5414b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err);
5415b4c3e9b5SBjoern A. Zeeb 			goto exit;
5416b4c3e9b5SBjoern A. Zeeb 		}
5417b4c3e9b5SBjoern A. Zeeb 
5418b4c3e9b5SBjoern A. Zeeb 		if (crypto->psk) {
5419b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "using PSK offload\n");
5420b4c3e9b5SBjoern A. Zeeb 			profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_PSK);
5421b4c3e9b5SBjoern A. Zeeb 			err = brcmf_set_pmk(ifp, crypto->psk,
5422b4c3e9b5SBjoern A. Zeeb 					    BRCMF_WSEC_MAX_PSK_LEN);
5423b4c3e9b5SBjoern A. Zeeb 			if (err < 0)
5424b4c3e9b5SBjoern A. Zeeb 				goto exit;
5425b4c3e9b5SBjoern A. Zeeb 		}
5426b4c3e9b5SBjoern A. Zeeb 		if (crypto->sae_pwd) {
5427b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "using SAE offload\n");
5428b4c3e9b5SBjoern A. Zeeb 			profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE);
5429b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fwvid_set_sae_password(ifp, crypto);
5430b4c3e9b5SBjoern A. Zeeb 			if (err < 0)
5431b4c3e9b5SBjoern A. Zeeb 				goto exit;
5432b4c3e9b5SBjoern A. Zeeb 		}
5433b4c3e9b5SBjoern A. Zeeb 		if (profile->use_fwauth == 0)
5434b4c3e9b5SBjoern A. Zeeb 			profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
5435b4c3e9b5SBjoern A. Zeeb 
5436b4c3e9b5SBjoern A. Zeeb 		err = brcmf_parse_configure_security(ifp, settings,
5437b4c3e9b5SBjoern A. Zeeb 						     NL80211_IFTYPE_AP);
5438b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5439b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "brcmf_parse_configure_security error\n");
5440b4c3e9b5SBjoern A. Zeeb 			goto exit;
5441b4c3e9b5SBjoern A. Zeeb 		}
5442b4c3e9b5SBjoern A. Zeeb 
5443b4c3e9b5SBjoern A. Zeeb 		/* On DOWN the firmware removes the WEP keys, reconfigure
5444b4c3e9b5SBjoern A. Zeeb 		 * them if they were set.
5445b4c3e9b5SBjoern A. Zeeb 		 */
5446b4c3e9b5SBjoern A. Zeeb 		brcmf_cfg80211_reconfigure_wep(ifp);
5447b4c3e9b5SBjoern A. Zeeb 
5448b4c3e9b5SBjoern A. Zeeb 		memset(&join_params, 0, sizeof(join_params));
5449b4c3e9b5SBjoern A. Zeeb 		/* join parameters starts with ssid */
5450b4c3e9b5SBjoern A. Zeeb 		memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
5451b4c3e9b5SBjoern A. Zeeb 		/* create softap */
5452b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
5453b4c3e9b5SBjoern A. Zeeb 					     &join_params, sizeof(join_params));
5454b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5455b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "SET SSID error (%d)\n", err);
5456b4c3e9b5SBjoern A. Zeeb 			goto exit;
5457b4c3e9b5SBjoern A. Zeeb 		}
5458b4c3e9b5SBjoern A. Zeeb 
5459b4c3e9b5SBjoern A. Zeeb 		closednet =
5460b4c3e9b5SBjoern A. Zeeb 			(settings->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);
5461b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "closednet",	closednet);
5462b4c3e9b5SBjoern A. Zeeb 		if (err) {
5463b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "%s closednet error (%d)\n",
5464b4c3e9b5SBjoern A. Zeeb 				 (closednet ? "enabled" : "disabled"),
5465b4c3e9b5SBjoern A. Zeeb 				 err);
5466b4c3e9b5SBjoern A. Zeeb 			goto exit;
5467b4c3e9b5SBjoern A. Zeeb 		}
5468b4c3e9b5SBjoern A. Zeeb 
5469b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "AP mode configuration complete\n");
5470b4c3e9b5SBjoern A. Zeeb 	} else if (dev_role == NL80211_IFTYPE_P2P_GO) {
5471b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
5472b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5473b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
5474b4c3e9b5SBjoern A. Zeeb 				 chanspec, err);
5475b4c3e9b5SBjoern A. Zeeb 			goto exit;
5476b4c3e9b5SBjoern A. Zeeb 		}
5477b4c3e9b5SBjoern A. Zeeb 
5478b4c3e9b5SBjoern A. Zeeb 		err = brcmf_parse_configure_security(ifp, settings,
5479b4c3e9b5SBjoern A. Zeeb 						     NL80211_IFTYPE_P2P_GO);
5480b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5481b4c3e9b5SBjoern A. Zeeb 			brcmf_err("brcmf_parse_configure_security error\n");
5482b4c3e9b5SBjoern A. Zeeb 			goto exit;
5483b4c3e9b5SBjoern A. Zeeb 		}
5484b4c3e9b5SBjoern A. Zeeb 
5485b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
5486b4c3e9b5SBjoern A. Zeeb 						sizeof(ssid_le));
5487b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5488b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "setting ssid failed %d\n", err);
5489b4c3e9b5SBjoern A. Zeeb 			goto exit;
5490b4c3e9b5SBjoern A. Zeeb 		}
5491b4c3e9b5SBjoern A. Zeeb 		bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
5492b4c3e9b5SBjoern A. Zeeb 		bss_enable.enable = cpu_to_le32(1);
5493b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
5494b4c3e9b5SBjoern A. Zeeb 					       sizeof(bss_enable));
5495b4c3e9b5SBjoern A. Zeeb 		if (err < 0) {
5496b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "bss_enable config failed %d\n", err);
5497b4c3e9b5SBjoern A. Zeeb 			goto exit;
5498b4c3e9b5SBjoern A. Zeeb 		}
5499b4c3e9b5SBjoern A. Zeeb 
5500b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "GO mode configuration complete\n");
5501b4c3e9b5SBjoern A. Zeeb 	} else {
5502b4c3e9b5SBjoern A. Zeeb 		WARN_ON(1);
5503b4c3e9b5SBjoern A. Zeeb 	}
5504b4c3e9b5SBjoern A. Zeeb 
5505b4c3e9b5SBjoern A. Zeeb 	brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
5506b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
5507b4c3e9b5SBjoern A. Zeeb 	brcmf_net_setcarrier(ifp, true);
5508b4c3e9b5SBjoern A. Zeeb 
5509b4c3e9b5SBjoern A. Zeeb exit:
5510b4c3e9b5SBjoern A. Zeeb 	if ((err) && (!mbss)) {
5511b4c3e9b5SBjoern A. Zeeb 		brcmf_set_mpc(ifp, 1);
5512b4c3e9b5SBjoern A. Zeeb 		brcmf_configure_arp_nd_offload(ifp, true);
5513b4c3e9b5SBjoern A. Zeeb 	}
5514b4c3e9b5SBjoern A. Zeeb 	return err;
5515b4c3e9b5SBjoern A. Zeeb }
5516b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_stop_ap(struct wiphy * wiphy,struct net_device * ndev,unsigned int link_id)5517b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev,
5518b4c3e9b5SBjoern A. Zeeb 				  unsigned int link_id)
5519b4c3e9b5SBjoern A. Zeeb {
5520b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5521b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
5522b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
5523b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
5524b4c3e9b5SBjoern A. Zeeb 	s32 err;
5525b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fil_bss_enable_le bss_enable;
5526b4c3e9b5SBjoern A. Zeeb 	struct brcmf_join_params join_params;
5527b4c3e9b5SBjoern A. Zeeb 
5528b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
5529b4c3e9b5SBjoern A. Zeeb 
5530b4c3e9b5SBjoern A. Zeeb 	if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
5531b4c3e9b5SBjoern A. Zeeb 		/* Due to most likely deauths outstanding we sleep */
5532b4c3e9b5SBjoern A. Zeeb 		/* first to make sure they get processed by fw. */
5533902136e0SBjoern A. Zeeb #if defined(__linux__)
5534b4c3e9b5SBjoern A. Zeeb 		msleep(400);
5535902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5536902136e0SBjoern A. Zeeb 		linux_msleep(400);
5537902136e0SBjoern A. Zeeb #endif
5538b4c3e9b5SBjoern A. Zeeb 
5539b4c3e9b5SBjoern A. Zeeb 		if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) {
5540b4c3e9b5SBjoern A. Zeeb 			struct cfg80211_crypto_settings crypto = {};
5541b4c3e9b5SBjoern A. Zeeb 
5542b4c3e9b5SBjoern A. Zeeb 			if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK))
5543b4c3e9b5SBjoern A. Zeeb 				brcmf_set_pmk(ifp, NULL, 0);
5544b4c3e9b5SBjoern A. Zeeb 			if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE))
5545b4c3e9b5SBjoern A. Zeeb 				brcmf_fwvid_set_sae_password(ifp, &crypto);
5546b4c3e9b5SBjoern A. Zeeb 			profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
5547b4c3e9b5SBjoern A. Zeeb 		}
5548b4c3e9b5SBjoern A. Zeeb 
5549b4c3e9b5SBjoern A. Zeeb 		if (ifp->vif->mbss) {
5550b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
5551b4c3e9b5SBjoern A. Zeeb 			return err;
5552b4c3e9b5SBjoern A. Zeeb 		}
5553b4c3e9b5SBjoern A. Zeeb 
5554b4c3e9b5SBjoern A. Zeeb 		/* First BSS doesn't get a full reset */
5555b4c3e9b5SBjoern A. Zeeb 		if (ifp->bsscfgidx == 0)
5556b4c3e9b5SBjoern A. Zeeb 			brcmf_fil_iovar_int_set(ifp, "closednet", 0);
5557b4c3e9b5SBjoern A. Zeeb 
5558b4c3e9b5SBjoern A. Zeeb 		memset(&join_params, 0, sizeof(join_params));
5559b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
5560b4c3e9b5SBjoern A. Zeeb 					     &join_params, sizeof(join_params));
5561b4c3e9b5SBjoern A. Zeeb 		if (err < 0)
5562b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "SET SSID error (%d)\n", err);
5563b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
5564b4c3e9b5SBjoern A. Zeeb 		if (err < 0)
5565b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err);
5566b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
5567b4c3e9b5SBjoern A. Zeeb 		if (err < 0)
5568b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "setting AP mode failed %d\n", err);
5569b4c3e9b5SBjoern A. Zeeb 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
5570b4c3e9b5SBjoern A. Zeeb 			brcmf_fil_iovar_int_set(ifp, "mbss", 0);
5571b4c3e9b5SBjoern A. Zeeb 		brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
5572b4c3e9b5SBjoern A. Zeeb 				      ifp->vif->is_11d);
5573b4c3e9b5SBjoern A. Zeeb 		/* Bring device back up so it can be used again */
5574b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
5575b4c3e9b5SBjoern A. Zeeb 		if (err < 0)
5576b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "BRCMF_C_UP error %d\n", err);
5577b4c3e9b5SBjoern A. Zeeb 
5578b4c3e9b5SBjoern A. Zeeb 		brcmf_vif_clear_mgmt_ies(ifp->vif);
5579b4c3e9b5SBjoern A. Zeeb 	} else {
5580b4c3e9b5SBjoern A. Zeeb 		bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
5581b4c3e9b5SBjoern A. Zeeb 		bss_enable.enable = cpu_to_le32(0);
5582b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
5583b4c3e9b5SBjoern A. Zeeb 					       sizeof(bss_enable));
5584b4c3e9b5SBjoern A. Zeeb 		if (err < 0)
5585b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "bss_enable config failed %d\n", err);
5586b4c3e9b5SBjoern A. Zeeb 	}
5587b4c3e9b5SBjoern A. Zeeb 	brcmf_set_mpc(ifp, 1);
5588b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
5589b4c3e9b5SBjoern A. Zeeb 	brcmf_configure_arp_nd_offload(ifp, true);
5590b4c3e9b5SBjoern A. Zeeb 	brcmf_net_setcarrier(ifp, false);
5591b4c3e9b5SBjoern A. Zeeb 
5592b4c3e9b5SBjoern A. Zeeb 	return err;
5593b4c3e9b5SBjoern A. Zeeb }
5594b4c3e9b5SBjoern A. Zeeb 
5595b4c3e9b5SBjoern A. Zeeb static s32
brcmf_cfg80211_change_beacon(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ap_update * info)5596b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
5597b4c3e9b5SBjoern A. Zeeb 			     struct cfg80211_ap_update *info)
5598b4c3e9b5SBjoern A. Zeeb {
5599b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
5600b4c3e9b5SBjoern A. Zeeb 
5601b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
5602b4c3e9b5SBjoern A. Zeeb 
5603b4c3e9b5SBjoern A. Zeeb 	return brcmf_config_ap_mgmt_ie(ifp->vif, &info->beacon);
5604b4c3e9b5SBjoern A. Zeeb }
5605b4c3e9b5SBjoern A. Zeeb 
5606b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_del_station(struct wiphy * wiphy,struct net_device * ndev,struct station_del_parameters * params)5607b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
5608b4c3e9b5SBjoern A. Zeeb 			   struct station_del_parameters *params)
5609b4c3e9b5SBjoern A. Zeeb {
5610b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5611b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
5612b4c3e9b5SBjoern A. Zeeb 	struct brcmf_scb_val_le scbval;
5613b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
5614b4c3e9b5SBjoern A. Zeeb 	s32 err;
5615b4c3e9b5SBjoern A. Zeeb 
5616b4c3e9b5SBjoern A. Zeeb 	if (!params->mac)
5617b4c3e9b5SBjoern A. Zeeb 		return -EFAULT;
5618b4c3e9b5SBjoern A. Zeeb 
5619b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
5620b4c3e9b5SBjoern A. Zeeb 
5621b4c3e9b5SBjoern A. Zeeb 	if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
5622b4c3e9b5SBjoern A. Zeeb 		ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
5623b4c3e9b5SBjoern A. Zeeb 	if (!check_vif_up(ifp->vif))
5624b4c3e9b5SBjoern A. Zeeb 		return -EIO;
5625b4c3e9b5SBjoern A. Zeeb 
5626b4c3e9b5SBjoern A. Zeeb 	memcpy(&scbval.ea, params->mac, ETH_ALEN);
5627b4c3e9b5SBjoern A. Zeeb 	scbval.val = cpu_to_le32(params->reason_code);
5628b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
5629b4c3e9b5SBjoern A. Zeeb 				     &scbval, sizeof(scbval));
5630b4c3e9b5SBjoern A. Zeeb 	if (err)
5631b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "SCB_DEAUTHENTICATE_FOR_REASON failed %d\n",
5632b4c3e9b5SBjoern A. Zeeb 			 err);
5633b4c3e9b5SBjoern A. Zeeb 
5634b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
5635b4c3e9b5SBjoern A. Zeeb 	return err;
5636b4c3e9b5SBjoern A. Zeeb }
5637b4c3e9b5SBjoern A. Zeeb 
5638b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_change_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_parameters * params)5639b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
5640b4c3e9b5SBjoern A. Zeeb 			      const u8 *mac, struct station_parameters *params)
5641b4c3e9b5SBjoern A. Zeeb {
5642b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5643b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
5644b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
5645b4c3e9b5SBjoern A. Zeeb 	s32 err;
5646b4c3e9b5SBjoern A. Zeeb 
5647b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
5648b4c3e9b5SBjoern A. Zeeb 		  params->sta_flags_mask, params->sta_flags_set);
5649b4c3e9b5SBjoern A. Zeeb 
5650b4c3e9b5SBjoern A. Zeeb 	/* Ignore all 00 MAC */
5651b4c3e9b5SBjoern A. Zeeb 	if (is_zero_ether_addr(mac))
5652b4c3e9b5SBjoern A. Zeeb 		return 0;
5653b4c3e9b5SBjoern A. Zeeb 
5654b4c3e9b5SBjoern A. Zeeb 	if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
5655b4c3e9b5SBjoern A. Zeeb 		return 0;
5656b4c3e9b5SBjoern A. Zeeb 
5657b4c3e9b5SBjoern A. Zeeb 	if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
5658b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
5659902136e0SBjoern A. Zeeb #if defined(__linux__)
5660b4c3e9b5SBjoern A. Zeeb 					     (void *)mac, ETH_ALEN);
5661902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5662902136e0SBjoern A. Zeeb 					     __DECONST(u8 *, mac), ETH_ALEN);
5663902136e0SBjoern A. Zeeb #endif
5664b4c3e9b5SBjoern A. Zeeb 	else
5665b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
5666902136e0SBjoern A. Zeeb #if defined(__linux__)
5667b4c3e9b5SBjoern A. Zeeb 					     (void *)mac, ETH_ALEN);
5668902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5669902136e0SBjoern A. Zeeb 					     __DECONST(u8 *, mac), ETH_ALEN);
5670902136e0SBjoern A. Zeeb #endif
5671b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
5672b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Setting SCB (de-)authorize failed, %d\n", err);
5673b4c3e9b5SBjoern A. Zeeb 
5674b4c3e9b5SBjoern A. Zeeb 	return err;
5675b4c3e9b5SBjoern A. Zeeb }
5676b4c3e9b5SBjoern A. Zeeb 
5677b4c3e9b5SBjoern A. Zeeb static void
brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy * wiphy,struct wireless_dev * wdev,struct mgmt_frame_regs * upd)5678b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
5679b4c3e9b5SBjoern A. Zeeb 					       struct wireless_dev *wdev,
5680b4c3e9b5SBjoern A. Zeeb 					       struct mgmt_frame_regs *upd)
5681b4c3e9b5SBjoern A. Zeeb {
5682b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
5683b4c3e9b5SBjoern A. Zeeb 
5684b4c3e9b5SBjoern A. Zeeb 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5685b4c3e9b5SBjoern A. Zeeb 
5686b4c3e9b5SBjoern A. Zeeb 	vif->mgmt_rx_reg = upd->interface_stypes;
5687b4c3e9b5SBjoern A. Zeeb }
5688b4c3e9b5SBjoern A. Zeeb 
5689b4c3e9b5SBjoern A. Zeeb 
5690b4c3e9b5SBjoern A. Zeeb int
brcmf_cfg80211_mgmt_tx(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_mgmt_tx_params * params,u64 * cookie)5691b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
5692b4c3e9b5SBjoern A. Zeeb 		       struct cfg80211_mgmt_tx_params *params, u64 *cookie)
5693b4c3e9b5SBjoern A. Zeeb {
5694b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5695b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_channel *chan = params->chan;
5696b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
5697b4c3e9b5SBjoern A. Zeeb 	const u8 *buf = params->buf;
5698b4c3e9b5SBjoern A. Zeeb 	size_t len = params->len;
5699b4c3e9b5SBjoern A. Zeeb 	const struct ieee80211_mgmt *mgmt;
5700b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
5701b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
5702b4c3e9b5SBjoern A. Zeeb 	s32 ie_offset;
5703b4c3e9b5SBjoern A. Zeeb 	s32 ie_len;
5704b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fil_action_frame_le *action_frame;
5705b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fil_af_params_le *af_params;
5706b4c3e9b5SBjoern A. Zeeb 	bool ack;
5707b4c3e9b5SBjoern A. Zeeb 	__le32 hw_ch;
5708b4c3e9b5SBjoern A. Zeeb 
5709b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
5710b4c3e9b5SBjoern A. Zeeb 
5711b4c3e9b5SBjoern A. Zeeb 	*cookie = 0;
5712b4c3e9b5SBjoern A. Zeeb 
5713b4c3e9b5SBjoern A. Zeeb 	mgmt = (const struct ieee80211_mgmt *)buf;
5714b4c3e9b5SBjoern A. Zeeb 
5715b4c3e9b5SBjoern A. Zeeb 	if (!ieee80211_is_mgmt(mgmt->frame_control)) {
5716b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Driver only allows MGMT packet type\n");
5717b4c3e9b5SBjoern A. Zeeb 		return -EPERM;
5718b4c3e9b5SBjoern A. Zeeb 	}
5719b4c3e9b5SBjoern A. Zeeb 
5720b4c3e9b5SBjoern A. Zeeb 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5721b4c3e9b5SBjoern A. Zeeb 
5722b4c3e9b5SBjoern A. Zeeb 	if (ieee80211_is_probe_resp(mgmt->frame_control)) {
5723b4c3e9b5SBjoern A. Zeeb 		/* Right now the only reason to get a probe response */
5724b4c3e9b5SBjoern A. Zeeb 		/* is for p2p listen response or for p2p GO from     */
5725b4c3e9b5SBjoern A. Zeeb 		/* wpa_supplicant. Unfortunately the probe is send   */
5726b4c3e9b5SBjoern A. Zeeb 		/* on primary ndev, while dongle wants it on the p2p */
5727b4c3e9b5SBjoern A. Zeeb 		/* vif. Since this is only reason for a probe        */
5728b4c3e9b5SBjoern A. Zeeb 		/* response to be sent, the vif is taken from cfg.   */
5729b4c3e9b5SBjoern A. Zeeb 		/* If ever desired to send proberesp for non p2p     */
5730b4c3e9b5SBjoern A. Zeeb 		/* response then data should be checked for          */
5731b4c3e9b5SBjoern A. Zeeb 		/* "DIRECT-". Note in future supplicant will take    */
5732b4c3e9b5SBjoern A. Zeeb 		/* dedicated p2p wdev to do this and then this 'hack'*/
5733b4c3e9b5SBjoern A. Zeeb 		/* is not needed anymore.                            */
5734b4c3e9b5SBjoern A. Zeeb 		ie_offset =  DOT11_MGMT_HDR_LEN +
5735b4c3e9b5SBjoern A. Zeeb 			     DOT11_BCN_PRB_FIXED_LEN;
5736b4c3e9b5SBjoern A. Zeeb 		ie_len = len - ie_offset;
5737b4c3e9b5SBjoern A. Zeeb 		if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
5738b4c3e9b5SBjoern A. Zeeb 			vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
5739b4c3e9b5SBjoern A. Zeeb 		err = brcmf_vif_set_mgmt_ie(vif,
5740b4c3e9b5SBjoern A. Zeeb 					    BRCMF_VNDR_IE_PRBRSP_FLAG,
5741b4c3e9b5SBjoern A. Zeeb 					    &buf[ie_offset],
5742b4c3e9b5SBjoern A. Zeeb 					    ie_len);
5743b4c3e9b5SBjoern A. Zeeb 		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
5744b4c3e9b5SBjoern A. Zeeb 					GFP_KERNEL);
5745b4c3e9b5SBjoern A. Zeeb 	} else if (ieee80211_is_action(mgmt->frame_control)) {
5746b4c3e9b5SBjoern A. Zeeb 		if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) {
5747b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "invalid action frame length\n");
5748b4c3e9b5SBjoern A. Zeeb 			err = -EINVAL;
5749b4c3e9b5SBjoern A. Zeeb 			goto exit;
5750b4c3e9b5SBjoern A. Zeeb 		}
5751b4c3e9b5SBjoern A. Zeeb 		af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
5752b4c3e9b5SBjoern A. Zeeb 		if (af_params == NULL) {
5753b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "unable to allocate frame\n");
5754b4c3e9b5SBjoern A. Zeeb 			err = -ENOMEM;
5755b4c3e9b5SBjoern A. Zeeb 			goto exit;
5756b4c3e9b5SBjoern A. Zeeb 		}
5757b4c3e9b5SBjoern A. Zeeb 		action_frame = &af_params->action_frame;
5758b4c3e9b5SBjoern A. Zeeb 		/* Add the packet Id */
5759b4c3e9b5SBjoern A. Zeeb 		action_frame->packet_id = cpu_to_le32(*cookie);
5760b4c3e9b5SBjoern A. Zeeb 		/* Add BSSID */
5761b4c3e9b5SBjoern A. Zeeb 		memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
5762b4c3e9b5SBjoern A. Zeeb 		memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
5763b4c3e9b5SBjoern A. Zeeb 		/* Add the length exepted for 802.11 header  */
5764b4c3e9b5SBjoern A. Zeeb 		action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
5765b4c3e9b5SBjoern A. Zeeb 		/* Add the channel. Use the one specified as parameter if any or
5766b4c3e9b5SBjoern A. Zeeb 		 * the current one (got from the firmware) otherwise
5767b4c3e9b5SBjoern A. Zeeb 		 */
5768b4c3e9b5SBjoern A. Zeeb 		if (chan) {
5769b4c3e9b5SBjoern A. Zeeb 			hw_ch = cpu_to_le32(chan->hw_value);
5770b4c3e9b5SBjoern A. Zeeb 		} else {
5771b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_cmd_data_get(vif->ifp,
5772b4c3e9b5SBjoern A. Zeeb 						     BRCMF_C_GET_CHANNEL,
5773b4c3e9b5SBjoern A. Zeeb 						     &hw_ch, sizeof(hw_ch));
5774b4c3e9b5SBjoern A. Zeeb 			if (err) {
5775b4c3e9b5SBjoern A. Zeeb 				bphy_err(drvr,
5776b4c3e9b5SBjoern A. Zeeb 					 "unable to get current hw channel\n");
5777b4c3e9b5SBjoern A. Zeeb 				goto free;
5778b4c3e9b5SBjoern A. Zeeb 			}
5779b4c3e9b5SBjoern A. Zeeb 		}
5780b4c3e9b5SBjoern A. Zeeb 		af_params->channel = hw_ch;
5781b4c3e9b5SBjoern A. Zeeb 
5782b4c3e9b5SBjoern A. Zeeb 		af_params->dwell_time = cpu_to_le32(params->wait);
5783b4c3e9b5SBjoern A. Zeeb 		memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
5784b4c3e9b5SBjoern A. Zeeb 		       le16_to_cpu(action_frame->len));
5785b4c3e9b5SBjoern A. Zeeb 
5786902136e0SBjoern A. Zeeb #if defined(__linux__)
5787b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, channel=%d\n",
5788b4c3e9b5SBjoern A. Zeeb 			  *cookie, le16_to_cpu(action_frame->len),
5789902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5790902136e0SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Action frame, cookie=%ju, len=%d, channel=%d\n",
5791902136e0SBjoern A. Zeeb 			  (uintmax_t)*cookie, le16_to_cpu(action_frame->len),
5792902136e0SBjoern A. Zeeb #endif
5793b4c3e9b5SBjoern A. Zeeb 			  le32_to_cpu(af_params->channel));
5794b4c3e9b5SBjoern A. Zeeb 
5795*9375e11fSBjoern A. Zeeb 		ack = brcmf_p2p_send_action_frame(vif->ifp, af_params);
5796b4c3e9b5SBjoern A. Zeeb 
5797b4c3e9b5SBjoern A. Zeeb 		cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
5798b4c3e9b5SBjoern A. Zeeb 					GFP_KERNEL);
5799b4c3e9b5SBjoern A. Zeeb free:
5800b4c3e9b5SBjoern A. Zeeb 		kfree(af_params);
5801b4c3e9b5SBjoern A. Zeeb 	} else {
5802b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
5803b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len);
5804b4c3e9b5SBjoern A. Zeeb 	}
5805b4c3e9b5SBjoern A. Zeeb 
5806b4c3e9b5SBjoern A. Zeeb exit:
5807b4c3e9b5SBjoern A. Zeeb 	return err;
5808b4c3e9b5SBjoern A. Zeeb }
5809b4c3e9b5SBjoern A. Zeeb BRCMF_EXPORT_SYMBOL_GPL(brcmf_cfg80211_mgmt_tx);
5810b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy * wiphy,struct net_device * ndev,s32 rssi_low,s32 rssi_high)5811b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
5812b4c3e9b5SBjoern A. Zeeb 						    struct net_device *ndev,
5813b4c3e9b5SBjoern A. Zeeb 						    s32 rssi_low, s32 rssi_high)
5814b4c3e9b5SBjoern A. Zeeb {
5815b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
5816b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
5817b4c3e9b5SBjoern A. Zeeb 	int err = 0;
5818b4c3e9b5SBjoern A. Zeeb 
5819b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "low=%d high=%d", rssi_low, rssi_high);
5820b4c3e9b5SBjoern A. Zeeb 
5821b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(ndev);
5822b4c3e9b5SBjoern A. Zeeb 	vif = ifp->vif;
5823b4c3e9b5SBjoern A. Zeeb 
5824b4c3e9b5SBjoern A. Zeeb 	if (rssi_low != vif->cqm_rssi_low || rssi_high != vif->cqm_rssi_high) {
5825b4c3e9b5SBjoern A. Zeeb 		/* The firmware will send an event when the RSSI is less than or
5826b4c3e9b5SBjoern A. Zeeb 		 * equal to a configured level and the previous RSSI event was
5827b4c3e9b5SBjoern A. Zeeb 		 * less than or equal to a different level. Set a third level
5828b4c3e9b5SBjoern A. Zeeb 		 * so that we also detect the transition from rssi <= rssi_high
5829b4c3e9b5SBjoern A. Zeeb 		 * to rssi > rssi_high.
5830b4c3e9b5SBjoern A. Zeeb 		 */
5831b4c3e9b5SBjoern A. Zeeb 		struct brcmf_rssi_event_le config = {
5832b4c3e9b5SBjoern A. Zeeb 			.rate_limit_msec = cpu_to_le32(0),
5833b4c3e9b5SBjoern A. Zeeb 			.rssi_level_num = 3,
5834b4c3e9b5SBjoern A. Zeeb 			.rssi_levels = {
5835b4c3e9b5SBjoern A. Zeeb 				clamp_val(rssi_low, S8_MIN, S8_MAX - 2),
5836b4c3e9b5SBjoern A. Zeeb 				clamp_val(rssi_high, S8_MIN + 1, S8_MAX - 1),
5837b4c3e9b5SBjoern A. Zeeb 				S8_MAX,
5838b4c3e9b5SBjoern A. Zeeb 			},
5839b4c3e9b5SBjoern A. Zeeb 		};
5840b4c3e9b5SBjoern A. Zeeb 
5841b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &config,
5842b4c3e9b5SBjoern A. Zeeb 					       sizeof(config));
5843b4c3e9b5SBjoern A. Zeeb 		if (err) {
5844b4c3e9b5SBjoern A. Zeeb 			err = -EINVAL;
5845b4c3e9b5SBjoern A. Zeeb 		} else {
5846b4c3e9b5SBjoern A. Zeeb 			vif->cqm_rssi_low = rssi_low;
5847b4c3e9b5SBjoern A. Zeeb 			vif->cqm_rssi_high = rssi_high;
5848b4c3e9b5SBjoern A. Zeeb 		}
5849b4c3e9b5SBjoern A. Zeeb 	}
5850b4c3e9b5SBjoern A. Zeeb 
5851b4c3e9b5SBjoern A. Zeeb 	return err;
5852b4c3e9b5SBjoern A. Zeeb }
5853b4c3e9b5SBjoern A. Zeeb 
5854b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_cancel_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,u64 cookie)5855b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
5856b4c3e9b5SBjoern A. Zeeb 					struct wireless_dev *wdev,
5857b4c3e9b5SBjoern A. Zeeb 					u64 cookie)
5858b4c3e9b5SBjoern A. Zeeb {
5859b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5860b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
5861b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
5862b4c3e9b5SBjoern A. Zeeb 	int err = 0;
5863b4c3e9b5SBjoern A. Zeeb 
5864b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
5865b4c3e9b5SBjoern A. Zeeb 
5866b4c3e9b5SBjoern A. Zeeb 	vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
5867b4c3e9b5SBjoern A. Zeeb 	if (vif == NULL) {
5868b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "No p2p device available for probe response\n");
5869b4c3e9b5SBjoern A. Zeeb 		err = -ENODEV;
5870b4c3e9b5SBjoern A. Zeeb 		goto exit;
5871b4c3e9b5SBjoern A. Zeeb 	}
5872b4c3e9b5SBjoern A. Zeeb 	brcmf_p2p_cancel_remain_on_channel(vif->ifp);
5873b4c3e9b5SBjoern A. Zeeb exit:
5874b4c3e9b5SBjoern A. Zeeb 	return err;
5875b4c3e9b5SBjoern A. Zeeb }
5876b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_get_channel(struct wiphy * wiphy,struct wireless_dev * wdev,unsigned int link_id,struct cfg80211_chan_def * chandef)5877b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
5878b4c3e9b5SBjoern A. Zeeb 				      struct wireless_dev *wdev,
5879b4c3e9b5SBjoern A. Zeeb 				      unsigned int link_id,
5880b4c3e9b5SBjoern A. Zeeb 				      struct cfg80211_chan_def *chandef)
5881b4c3e9b5SBjoern A. Zeeb {
5882b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5883b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = wdev->netdev;
5884b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
5885b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch;
5886b4c3e9b5SBjoern A. Zeeb 	enum nl80211_band band = 0;
5887b4c3e9b5SBjoern A. Zeeb 	enum nl80211_chan_width width = 0;
5888b4c3e9b5SBjoern A. Zeeb 	u32 chanspec;
5889b4c3e9b5SBjoern A. Zeeb 	int freq, err;
5890b4c3e9b5SBjoern A. Zeeb 
5891b4c3e9b5SBjoern A. Zeeb 	if (!ndev || drvr->bus_if->state != BRCMF_BUS_UP)
5892b4c3e9b5SBjoern A. Zeeb 		return -ENODEV;
5893b4c3e9b5SBjoern A. Zeeb 
5894b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_get(netdev_priv(ndev), "chanspec", &chanspec);
5895b4c3e9b5SBjoern A. Zeeb 	if (err) {
5896b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "chanspec failed (%d)\n", err);
5897b4c3e9b5SBjoern A. Zeeb 		return err;
5898b4c3e9b5SBjoern A. Zeeb 	}
5899b4c3e9b5SBjoern A. Zeeb 
5900b4c3e9b5SBjoern A. Zeeb 	ch.chspec = chanspec;
5901b4c3e9b5SBjoern A. Zeeb 	cfg->d11inf.decchspec(&ch);
5902b4c3e9b5SBjoern A. Zeeb 
5903b4c3e9b5SBjoern A. Zeeb 	switch (ch.band) {
5904b4c3e9b5SBjoern A. Zeeb 	case BRCMU_CHAN_BAND_2G:
5905b4c3e9b5SBjoern A. Zeeb 		band = NL80211_BAND_2GHZ;
5906b4c3e9b5SBjoern A. Zeeb 		break;
5907b4c3e9b5SBjoern A. Zeeb 	case BRCMU_CHAN_BAND_5G:
5908b4c3e9b5SBjoern A. Zeeb 		band = NL80211_BAND_5GHZ;
5909b4c3e9b5SBjoern A. Zeeb 		break;
5910b4c3e9b5SBjoern A. Zeeb 	}
5911b4c3e9b5SBjoern A. Zeeb 
5912b4c3e9b5SBjoern A. Zeeb 	switch (ch.bw) {
5913b4c3e9b5SBjoern A. Zeeb 	case BRCMU_CHAN_BW_80:
5914b4c3e9b5SBjoern A. Zeeb 		width = NL80211_CHAN_WIDTH_80;
5915b4c3e9b5SBjoern A. Zeeb 		break;
5916b4c3e9b5SBjoern A. Zeeb 	case BRCMU_CHAN_BW_40:
5917b4c3e9b5SBjoern A. Zeeb 		width = NL80211_CHAN_WIDTH_40;
5918b4c3e9b5SBjoern A. Zeeb 		break;
5919b4c3e9b5SBjoern A. Zeeb 	case BRCMU_CHAN_BW_20:
5920b4c3e9b5SBjoern A. Zeeb 		width = NL80211_CHAN_WIDTH_20;
5921b4c3e9b5SBjoern A. Zeeb 		break;
5922b4c3e9b5SBjoern A. Zeeb 	case BRCMU_CHAN_BW_80P80:
5923b4c3e9b5SBjoern A. Zeeb 		width = NL80211_CHAN_WIDTH_80P80;
5924b4c3e9b5SBjoern A. Zeeb 		break;
5925b4c3e9b5SBjoern A. Zeeb 	case BRCMU_CHAN_BW_160:
5926b4c3e9b5SBjoern A. Zeeb 		width = NL80211_CHAN_WIDTH_160;
5927b4c3e9b5SBjoern A. Zeeb 		break;
5928b4c3e9b5SBjoern A. Zeeb 	}
5929b4c3e9b5SBjoern A. Zeeb 
5930b4c3e9b5SBjoern A. Zeeb 	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
5931b4c3e9b5SBjoern A. Zeeb 	chandef->chan = ieee80211_get_channel(wiphy, freq);
5932b4c3e9b5SBjoern A. Zeeb 	chandef->width = width;
5933b4c3e9b5SBjoern A. Zeeb 	chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);
5934b4c3e9b5SBjoern A. Zeeb 	chandef->center_freq2 = 0;
5935b4c3e9b5SBjoern A. Zeeb 
5936b4c3e9b5SBjoern A. Zeeb 	return 0;
5937b4c3e9b5SBjoern A. Zeeb }
5938b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_crit_proto_start(struct wiphy * wiphy,struct wireless_dev * wdev,enum nl80211_crit_proto_id proto,u16 duration)5939b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
5940b4c3e9b5SBjoern A. Zeeb 					   struct wireless_dev *wdev,
5941b4c3e9b5SBjoern A. Zeeb 					   enum nl80211_crit_proto_id proto,
5942b4c3e9b5SBjoern A. Zeeb 					   u16 duration)
5943b4c3e9b5SBjoern A. Zeeb {
5944b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5945b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
5946b4c3e9b5SBjoern A. Zeeb 
5947b4c3e9b5SBjoern A. Zeeb 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5948b4c3e9b5SBjoern A. Zeeb 
5949b4c3e9b5SBjoern A. Zeeb 	/* only DHCP support for now */
5950b4c3e9b5SBjoern A. Zeeb 	if (proto != NL80211_CRIT_PROTO_DHCP)
5951b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
5952b4c3e9b5SBjoern A. Zeeb 
5953b4c3e9b5SBjoern A. Zeeb 	/* suppress and abort scanning */
5954b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
5955b4c3e9b5SBjoern A. Zeeb 	brcmf_abort_scanning(cfg);
5956b4c3e9b5SBjoern A. Zeeb 
5957b4c3e9b5SBjoern A. Zeeb 	return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
5958b4c3e9b5SBjoern A. Zeeb }
5959b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_crit_proto_stop(struct wiphy * wiphy,struct wireless_dev * wdev)5960b4c3e9b5SBjoern A. Zeeb static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
5961b4c3e9b5SBjoern A. Zeeb 					   struct wireless_dev *wdev)
5962b4c3e9b5SBjoern A. Zeeb {
5963b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5964b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
5965b4c3e9b5SBjoern A. Zeeb 
5966b4c3e9b5SBjoern A. Zeeb 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5967b4c3e9b5SBjoern A. Zeeb 
5968b4c3e9b5SBjoern A. Zeeb 	brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
5969b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
5970b4c3e9b5SBjoern A. Zeeb }
5971b4c3e9b5SBjoern A. Zeeb 
5972b4c3e9b5SBjoern A. Zeeb static s32
brcmf_notify_tdls_peer_event(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)5973b4c3e9b5SBjoern A. Zeeb brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
5974b4c3e9b5SBjoern A. Zeeb 			     const struct brcmf_event_msg *e, void *data)
5975b4c3e9b5SBjoern A. Zeeb {
5976b4c3e9b5SBjoern A. Zeeb 	switch (e->reason) {
5977b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
5978b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
5979b4c3e9b5SBjoern A. Zeeb 		break;
5980b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
5981b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "TDLS Peer Connected\n");
5982902136e0SBjoern A. Zeeb #if defined(__linux__)
5983b4c3e9b5SBjoern A. Zeeb 		brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5984902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5985902136e0SBjoern A. Zeeb 		brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, e->addr);
5986902136e0SBjoern A. Zeeb #endif
5987b4c3e9b5SBjoern A. Zeeb 		break;
5988b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
5989b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
5990902136e0SBjoern A. Zeeb #if defined(__linux__)
5991b4c3e9b5SBjoern A. Zeeb 		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5992902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
5993902136e0SBjoern A. Zeeb 		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, e->addr);
5994902136e0SBjoern A. Zeeb #endif
5995b4c3e9b5SBjoern A. Zeeb 		break;
5996b4c3e9b5SBjoern A. Zeeb 	}
5997b4c3e9b5SBjoern A. Zeeb 
5998b4c3e9b5SBjoern A. Zeeb 	return 0;
5999b4c3e9b5SBjoern A. Zeeb }
6000b4c3e9b5SBjoern A. Zeeb 
brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)6001b4c3e9b5SBjoern A. Zeeb static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
6002b4c3e9b5SBjoern A. Zeeb {
6003b4c3e9b5SBjoern A. Zeeb 	int ret;
6004b4c3e9b5SBjoern A. Zeeb 
6005b4c3e9b5SBjoern A. Zeeb 	switch (oper) {
6006b4c3e9b5SBjoern A. Zeeb 	case NL80211_TDLS_DISCOVERY_REQ:
6007b4c3e9b5SBjoern A. Zeeb 		ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
6008b4c3e9b5SBjoern A. Zeeb 		break;
6009b4c3e9b5SBjoern A. Zeeb 	case NL80211_TDLS_SETUP:
6010b4c3e9b5SBjoern A. Zeeb 		ret = BRCMF_TDLS_MANUAL_EP_CREATE;
6011b4c3e9b5SBjoern A. Zeeb 		break;
6012b4c3e9b5SBjoern A. Zeeb 	case NL80211_TDLS_TEARDOWN:
6013b4c3e9b5SBjoern A. Zeeb 		ret = BRCMF_TDLS_MANUAL_EP_DELETE;
6014b4c3e9b5SBjoern A. Zeeb 		break;
6015b4c3e9b5SBjoern A. Zeeb 	default:
6016b4c3e9b5SBjoern A. Zeeb 		brcmf_err("unsupported operation: %d\n", oper);
6017b4c3e9b5SBjoern A. Zeeb 		ret = -EOPNOTSUPP;
6018b4c3e9b5SBjoern A. Zeeb 	}
6019b4c3e9b5SBjoern A. Zeeb 	return ret;
6020b4c3e9b5SBjoern A. Zeeb }
6021b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_tdls_oper(struct wiphy * wiphy,struct net_device * ndev,const u8 * peer,enum nl80211_tdls_operation oper)6022b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
6023b4c3e9b5SBjoern A. Zeeb 				    struct net_device *ndev, const u8 *peer,
6024b4c3e9b5SBjoern A. Zeeb 				    enum nl80211_tdls_operation oper)
6025b4c3e9b5SBjoern A. Zeeb {
6026b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
6027b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
6028b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
6029b4c3e9b5SBjoern A. Zeeb 	struct brcmf_tdls_iovar_le info;
6030b4c3e9b5SBjoern A. Zeeb 	int ret = 0;
6031b4c3e9b5SBjoern A. Zeeb 
6032b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_convert_nl80211_tdls_oper(oper);
6033b4c3e9b5SBjoern A. Zeeb 	if (ret < 0)
6034b4c3e9b5SBjoern A. Zeeb 		return ret;
6035b4c3e9b5SBjoern A. Zeeb 
6036b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(ndev);
6037b4c3e9b5SBjoern A. Zeeb 	memset(&info, 0, sizeof(info));
6038b4c3e9b5SBjoern A. Zeeb 	info.mode = (u8)ret;
6039b4c3e9b5SBjoern A. Zeeb 	if (peer)
6040b4c3e9b5SBjoern A. Zeeb 		memcpy(info.ea, peer, ETH_ALEN);
6041b4c3e9b5SBjoern A. Zeeb 
6042b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
6043b4c3e9b5SBjoern A. Zeeb 				       &info, sizeof(info));
6044b4c3e9b5SBjoern A. Zeeb 	if (ret < 0)
6045b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "tdls_endpoint iovar failed: ret=%d\n", ret);
6046b4c3e9b5SBjoern A. Zeeb 
6047b4c3e9b5SBjoern A. Zeeb 	return ret;
6048b4c3e9b5SBjoern A. Zeeb }
6049b4c3e9b5SBjoern A. Zeeb 
6050b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_update_conn_params(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme,u32 changed)6051b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_update_conn_params(struct wiphy *wiphy,
6052b4c3e9b5SBjoern A. Zeeb 				  struct net_device *ndev,
6053b4c3e9b5SBjoern A. Zeeb 				  struct cfg80211_connect_params *sme,
6054b4c3e9b5SBjoern A. Zeeb 				  u32 changed)
6055b4c3e9b5SBjoern A. Zeeb {
6056b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
6057b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
6058b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
6059b4c3e9b5SBjoern A. Zeeb 	int err;
6060b4c3e9b5SBjoern A. Zeeb 
6061b4c3e9b5SBjoern A. Zeeb 	if (!(changed & UPDATE_ASSOC_IES))
6062b4c3e9b5SBjoern A. Zeeb 		return 0;
6063b4c3e9b5SBjoern A. Zeeb 
6064b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(ndev);
6065b4c3e9b5SBjoern A. Zeeb 	err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
6066b4c3e9b5SBjoern A. Zeeb 				    sme->ie, sme->ie_len);
6067b4c3e9b5SBjoern A. Zeeb 	if (err)
6068b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Set Assoc REQ IE Failed\n");
6069b4c3e9b5SBjoern A. Zeeb 	else
6070b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
6071b4c3e9b5SBjoern A. Zeeb 
6072b4c3e9b5SBjoern A. Zeeb 	return err;
6073b4c3e9b5SBjoern A. Zeeb }
6074b4c3e9b5SBjoern A. Zeeb 
6075b4c3e9b5SBjoern A. Zeeb #ifdef CONFIG_PM
6076b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_set_rekey_data(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_gtk_rekey_data * gtk)6077b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
6078b4c3e9b5SBjoern A. Zeeb 			      struct cfg80211_gtk_rekey_data *gtk)
6079b4c3e9b5SBjoern A. Zeeb {
6080b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
6081b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
6082b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
6083b4c3e9b5SBjoern A. Zeeb 	struct brcmf_gtk_keyinfo_le gtk_le;
6084b4c3e9b5SBjoern A. Zeeb 	int ret;
6085b4c3e9b5SBjoern A. Zeeb 
6086b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
6087b4c3e9b5SBjoern A. Zeeb 
6088b4c3e9b5SBjoern A. Zeeb 	memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
6089b4c3e9b5SBjoern A. Zeeb 	memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
6090b4c3e9b5SBjoern A. Zeeb 	memcpy(gtk_le.replay_counter, gtk->replay_ctr,
6091b4c3e9b5SBjoern A. Zeeb 	       sizeof(gtk_le.replay_counter));
6092b4c3e9b5SBjoern A. Zeeb 
6093b4c3e9b5SBjoern A. Zeeb 	ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", &gtk_le,
6094b4c3e9b5SBjoern A. Zeeb 				       sizeof(gtk_le));
6095b4c3e9b5SBjoern A. Zeeb 	if (ret < 0)
6096b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "gtk_key_info iovar failed: ret=%d\n", ret);
6097b4c3e9b5SBjoern A. Zeeb 
6098b4c3e9b5SBjoern A. Zeeb 	return ret;
6099b4c3e9b5SBjoern A. Zeeb }
6100b4c3e9b5SBjoern A. Zeeb #endif
6101b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_set_pmk(struct wiphy * wiphy,struct net_device * dev,const struct cfg80211_pmk_conf * conf)6102b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,
6103b4c3e9b5SBjoern A. Zeeb 				  const struct cfg80211_pmk_conf *conf)
6104b4c3e9b5SBjoern A. Zeeb {
6105b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
6106b4c3e9b5SBjoern A. Zeeb 
6107b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "enter\n");
6108b4c3e9b5SBjoern A. Zeeb 
6109b4c3e9b5SBjoern A. Zeeb 	/* expect using firmware supplicant for 1X */
6110b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(dev);
6111b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
6112b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
6113b4c3e9b5SBjoern A. Zeeb 
6114b4c3e9b5SBjoern A. Zeeb 	if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN)
6115b4c3e9b5SBjoern A. Zeeb 		return -ERANGE;
6116b4c3e9b5SBjoern A. Zeeb 
6117b4c3e9b5SBjoern A. Zeeb 	return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);
6118b4c3e9b5SBjoern A. Zeeb }
6119b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_del_pmk(struct wiphy * wiphy,struct net_device * dev,const u8 * aa)6120b4c3e9b5SBjoern A. Zeeb static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,
6121b4c3e9b5SBjoern A. Zeeb 				  const u8 *aa)
6122b4c3e9b5SBjoern A. Zeeb {
6123b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
6124b4c3e9b5SBjoern A. Zeeb 
6125b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "enter\n");
6126b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(dev);
6127b4c3e9b5SBjoern A. Zeeb 	if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
6128b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
6129b4c3e9b5SBjoern A. Zeeb 
6130b4c3e9b5SBjoern A. Zeeb 	return brcmf_set_pmk(ifp, NULL, 0);
6131b4c3e9b5SBjoern A. Zeeb }
6132b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_change_bss(struct wiphy * wiphy,struct net_device * dev,struct bss_parameters * params)6133*9375e11fSBjoern A. Zeeb static int brcmf_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,
6134*9375e11fSBjoern A. Zeeb 				     struct bss_parameters *params)
6135*9375e11fSBjoern A. Zeeb {
6136*9375e11fSBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(dev);
6137*9375e11fSBjoern A. Zeeb 	int ret = 0;
6138*9375e11fSBjoern A. Zeeb 
6139*9375e11fSBjoern A. Zeeb 	/* In AP mode, the "ap_isolate" value represents
6140*9375e11fSBjoern A. Zeeb 	 *  0 = allow low-level bridging of frames between associated stations
6141*9375e11fSBjoern A. Zeeb 	 *  1 = restrict low-level bridging of frames to isolate associated stations
6142*9375e11fSBjoern A. Zeeb 	 * -1 = do not change existing setting
6143*9375e11fSBjoern A. Zeeb 	 */
6144*9375e11fSBjoern A. Zeeb 	if (params->ap_isolate >= 0) {
6145*9375e11fSBjoern A. Zeeb 		ret = brcmf_fil_iovar_int_set(ifp, "ap_isolate", params->ap_isolate);
6146*9375e11fSBjoern A. Zeeb 		if (ret < 0)
6147*9375e11fSBjoern A. Zeeb 			brcmf_err("ap_isolate iovar failed: ret=%d\n", ret);
6148*9375e11fSBjoern A. Zeeb 	}
6149*9375e11fSBjoern A. Zeeb 
6150*9375e11fSBjoern A. Zeeb 	return ret;
6151*9375e11fSBjoern A. Zeeb }
6152*9375e11fSBjoern A. Zeeb 
6153b4c3e9b5SBjoern A. Zeeb static struct cfg80211_ops brcmf_cfg80211_ops = {
6154b4c3e9b5SBjoern A. Zeeb 	.add_virtual_intf = brcmf_cfg80211_add_iface,
6155b4c3e9b5SBjoern A. Zeeb 	.del_virtual_intf = brcmf_cfg80211_del_iface,
6156b4c3e9b5SBjoern A. Zeeb 	.change_virtual_intf = brcmf_cfg80211_change_iface,
6157b4c3e9b5SBjoern A. Zeeb 	.scan = brcmf_cfg80211_scan,
6158b4c3e9b5SBjoern A. Zeeb 	.set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
6159b4c3e9b5SBjoern A. Zeeb 	.join_ibss = brcmf_cfg80211_join_ibss,
6160b4c3e9b5SBjoern A. Zeeb 	.leave_ibss = brcmf_cfg80211_leave_ibss,
6161b4c3e9b5SBjoern A. Zeeb 	.get_station = brcmf_cfg80211_get_station,
6162b4c3e9b5SBjoern A. Zeeb 	.dump_station = brcmf_cfg80211_dump_station,
6163b4c3e9b5SBjoern A. Zeeb 	.set_tx_power = brcmf_cfg80211_set_tx_power,
6164b4c3e9b5SBjoern A. Zeeb 	.get_tx_power = brcmf_cfg80211_get_tx_power,
6165b4c3e9b5SBjoern A. Zeeb 	.add_key = brcmf_cfg80211_add_key,
6166b4c3e9b5SBjoern A. Zeeb 	.del_key = brcmf_cfg80211_del_key,
6167b4c3e9b5SBjoern A. Zeeb 	.get_key = brcmf_cfg80211_get_key,
6168b4c3e9b5SBjoern A. Zeeb 	.set_default_key = brcmf_cfg80211_config_default_key,
6169b4c3e9b5SBjoern A. Zeeb 	.set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
6170b4c3e9b5SBjoern A. Zeeb 	.set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
6171b4c3e9b5SBjoern A. Zeeb 	.connect = brcmf_cfg80211_connect,
6172b4c3e9b5SBjoern A. Zeeb 	.disconnect = brcmf_cfg80211_disconnect,
6173b4c3e9b5SBjoern A. Zeeb 	.suspend = brcmf_cfg80211_suspend,
6174b4c3e9b5SBjoern A. Zeeb 	.resume = brcmf_cfg80211_resume,
6175b4c3e9b5SBjoern A. Zeeb 	.set_pmksa = brcmf_cfg80211_set_pmksa,
6176b4c3e9b5SBjoern A. Zeeb 	.del_pmksa = brcmf_cfg80211_del_pmksa,
6177b4c3e9b5SBjoern A. Zeeb 	.flush_pmksa = brcmf_cfg80211_flush_pmksa,
6178b4c3e9b5SBjoern A. Zeeb 	.start_ap = brcmf_cfg80211_start_ap,
6179b4c3e9b5SBjoern A. Zeeb 	.stop_ap = brcmf_cfg80211_stop_ap,
6180b4c3e9b5SBjoern A. Zeeb 	.change_beacon = brcmf_cfg80211_change_beacon,
6181b4c3e9b5SBjoern A. Zeeb 	.del_station = brcmf_cfg80211_del_station,
6182b4c3e9b5SBjoern A. Zeeb 	.change_station = brcmf_cfg80211_change_station,
6183b4c3e9b5SBjoern A. Zeeb 	.sched_scan_start = brcmf_cfg80211_sched_scan_start,
6184b4c3e9b5SBjoern A. Zeeb 	.sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
6185b4c3e9b5SBjoern A. Zeeb 	.update_mgmt_frame_registrations =
6186b4c3e9b5SBjoern A. Zeeb 		brcmf_cfg80211_update_mgmt_frame_registrations,
6187b4c3e9b5SBjoern A. Zeeb 	.mgmt_tx = brcmf_cfg80211_mgmt_tx,
6188b4c3e9b5SBjoern A. Zeeb 	.set_cqm_rssi_range_config = brcmf_cfg80211_set_cqm_rssi_range_config,
6189b4c3e9b5SBjoern A. Zeeb 	.remain_on_channel = brcmf_p2p_remain_on_channel,
6190b4c3e9b5SBjoern A. Zeeb 	.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
6191b4c3e9b5SBjoern A. Zeeb 	.get_channel = brcmf_cfg80211_get_channel,
6192b4c3e9b5SBjoern A. Zeeb 	.start_p2p_device = brcmf_p2p_start_device,
6193b4c3e9b5SBjoern A. Zeeb 	.stop_p2p_device = brcmf_p2p_stop_device,
6194b4c3e9b5SBjoern A. Zeeb 	.crit_proto_start = brcmf_cfg80211_crit_proto_start,
6195b4c3e9b5SBjoern A. Zeeb 	.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
6196b4c3e9b5SBjoern A. Zeeb 	.tdls_oper = brcmf_cfg80211_tdls_oper,
6197b4c3e9b5SBjoern A. Zeeb 	.update_connect_params = brcmf_cfg80211_update_conn_params,
6198b4c3e9b5SBjoern A. Zeeb 	.set_pmk = brcmf_cfg80211_set_pmk,
6199b4c3e9b5SBjoern A. Zeeb 	.del_pmk = brcmf_cfg80211_del_pmk,
6200*9375e11fSBjoern A. Zeeb 	.change_bss = brcmf_cfg80211_change_bss,
6201b4c3e9b5SBjoern A. Zeeb };
6202b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_get_ops(struct brcmf_mp_device * settings)6203b4c3e9b5SBjoern A. Zeeb struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings)
6204b4c3e9b5SBjoern A. Zeeb {
6205b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_ops *ops;
6206b4c3e9b5SBjoern A. Zeeb 
6207b4c3e9b5SBjoern A. Zeeb 	ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),
6208b4c3e9b5SBjoern A. Zeeb 		       GFP_KERNEL);
6209b4c3e9b5SBjoern A. Zeeb 
6210b4c3e9b5SBjoern A. Zeeb 	if (ops && settings->roamoff)
6211b4c3e9b5SBjoern A. Zeeb 		ops->update_connect_params = NULL;
6212b4c3e9b5SBjoern A. Zeeb 
6213b4c3e9b5SBjoern A. Zeeb 	return ops;
6214b4c3e9b5SBjoern A. Zeeb }
6215b4c3e9b5SBjoern A. Zeeb 
brcmf_alloc_vif(struct brcmf_cfg80211_info * cfg,enum nl80211_iftype type)6216b4c3e9b5SBjoern A. Zeeb struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
6217b4c3e9b5SBjoern A. Zeeb 					   enum nl80211_iftype type)
6218b4c3e9b5SBjoern A. Zeeb {
6219b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif_walk;
6220b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
6221b4c3e9b5SBjoern A. Zeeb 	bool mbss;
6222b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
6223b4c3e9b5SBjoern A. Zeeb 
6224b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
6225b4c3e9b5SBjoern A. Zeeb 		  sizeof(*vif));
6226b4c3e9b5SBjoern A. Zeeb 	vif = kzalloc(sizeof(*vif), GFP_KERNEL);
6227b4c3e9b5SBjoern A. Zeeb 	if (!vif)
6228b4c3e9b5SBjoern A. Zeeb 		return ERR_PTR(-ENOMEM);
6229b4c3e9b5SBjoern A. Zeeb 
6230b4c3e9b5SBjoern A. Zeeb 	vif->wdev.wiphy = cfg->wiphy;
6231b4c3e9b5SBjoern A. Zeeb 	vif->wdev.iftype = type;
6232b4c3e9b5SBjoern A. Zeeb 	init_completion(&vif->mgmt_tx);
6233b4c3e9b5SBjoern A. Zeeb 
6234b4c3e9b5SBjoern A. Zeeb 	brcmf_init_prof(&vif->profile);
6235b4c3e9b5SBjoern A. Zeeb 
6236b4c3e9b5SBjoern A. Zeeb 	if (type == NL80211_IFTYPE_AP &&
6237b4c3e9b5SBjoern A. Zeeb 	    brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
6238b4c3e9b5SBjoern A. Zeeb 		mbss = false;
6239b4c3e9b5SBjoern A. Zeeb 		list_for_each_entry(vif_walk, &cfg->vif_list, list) {
6240b4c3e9b5SBjoern A. Zeeb 			if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
6241b4c3e9b5SBjoern A. Zeeb 				mbss = true;
6242b4c3e9b5SBjoern A. Zeeb 				break;
6243b4c3e9b5SBjoern A. Zeeb 			}
6244b4c3e9b5SBjoern A. Zeeb 		}
6245b4c3e9b5SBjoern A. Zeeb 		vif->mbss = mbss;
6246b4c3e9b5SBjoern A. Zeeb 	}
6247b4c3e9b5SBjoern A. Zeeb 
6248b4c3e9b5SBjoern A. Zeeb 	list_add_tail(&vif->list, &cfg->vif_list);
6249b4c3e9b5SBjoern A. Zeeb 	return vif;
6250b4c3e9b5SBjoern A. Zeeb }
6251b4c3e9b5SBjoern A. Zeeb 
brcmf_free_vif(struct brcmf_cfg80211_vif * vif)6252b4c3e9b5SBjoern A. Zeeb void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
6253b4c3e9b5SBjoern A. Zeeb {
6254b4c3e9b5SBjoern A. Zeeb 	list_del(&vif->list);
6255b4c3e9b5SBjoern A. Zeeb 	kfree(vif);
6256b4c3e9b5SBjoern A. Zeeb }
6257b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_free_netdev(struct net_device * ndev)6258b4c3e9b5SBjoern A. Zeeb void brcmf_cfg80211_free_netdev(struct net_device *ndev)
6259b4c3e9b5SBjoern A. Zeeb {
6260b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
6261b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
6262b4c3e9b5SBjoern A. Zeeb 
6263b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(ndev);
6264b4c3e9b5SBjoern A. Zeeb 	vif = ifp->vif;
6265b4c3e9b5SBjoern A. Zeeb 
6266b4c3e9b5SBjoern A. Zeeb 	if (vif)
6267b4c3e9b5SBjoern A. Zeeb 		brcmf_free_vif(vif);
6268b4c3e9b5SBjoern A. Zeeb }
6269b4c3e9b5SBjoern A. Zeeb 
brcmf_is_linkup(struct brcmf_cfg80211_vif * vif,const struct brcmf_event_msg * e)6270b4c3e9b5SBjoern A. Zeeb static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
6271b4c3e9b5SBjoern A. Zeeb 			    const struct brcmf_event_msg *e)
6272b4c3e9b5SBjoern A. Zeeb {
6273b4c3e9b5SBjoern A. Zeeb 	u32 event = e->event_code;
6274b4c3e9b5SBjoern A. Zeeb 	u32 status = e->status;
6275b4c3e9b5SBjoern A. Zeeb 
6276b4c3e9b5SBjoern A. Zeeb 	if ((vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK ||
6277b4c3e9b5SBjoern A. Zeeb 	     vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_SAE) &&
6278b4c3e9b5SBjoern A. Zeeb 	    event == BRCMF_E_PSK_SUP &&
6279b4c3e9b5SBjoern A. Zeeb 	    status == BRCMF_E_STATUS_FWSUP_COMPLETED)
6280b4c3e9b5SBjoern A. Zeeb 		set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
6281b4c3e9b5SBjoern A. Zeeb 	if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
6282b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Processing set ssid\n");
6283b4c3e9b5SBjoern A. Zeeb 		memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
6284b4c3e9b5SBjoern A. Zeeb 		if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&
6285b4c3e9b5SBjoern A. Zeeb 		    vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)
6286b4c3e9b5SBjoern A. Zeeb 			return true;
6287b4c3e9b5SBjoern A. Zeeb 
6288b4c3e9b5SBjoern A. Zeeb 		set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
6289b4c3e9b5SBjoern A. Zeeb 	}
6290b4c3e9b5SBjoern A. Zeeb 
6291b4c3e9b5SBjoern A. Zeeb 	if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) &&
6292b4c3e9b5SBjoern A. Zeeb 	    test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) {
6293b4c3e9b5SBjoern A. Zeeb 		clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
6294b4c3e9b5SBjoern A. Zeeb 		clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
6295b4c3e9b5SBjoern A. Zeeb 		return true;
6296b4c3e9b5SBjoern A. Zeeb 	}
6297b4c3e9b5SBjoern A. Zeeb 	return false;
6298b4c3e9b5SBjoern A. Zeeb }
6299b4c3e9b5SBjoern A. Zeeb 
brcmf_is_linkdown(struct brcmf_cfg80211_vif * vif,const struct brcmf_event_msg * e)6300b4c3e9b5SBjoern A. Zeeb static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif,
6301b4c3e9b5SBjoern A. Zeeb 			    const struct brcmf_event_msg *e)
6302b4c3e9b5SBjoern A. Zeeb {
6303b4c3e9b5SBjoern A. Zeeb 	u32 event = e->event_code;
6304b4c3e9b5SBjoern A. Zeeb 	u16 flags = e->flags;
6305b4c3e9b5SBjoern A. Zeeb 
6306b4c3e9b5SBjoern A. Zeeb 	if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
6307b4c3e9b5SBjoern A. Zeeb 	    (event == BRCMF_E_DISASSOC_IND) ||
6308b4c3e9b5SBjoern A. Zeeb 	    ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
6309b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Processing link down\n");
6310b4c3e9b5SBjoern A. Zeeb 		clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
6311b4c3e9b5SBjoern A. Zeeb 		clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
6312b4c3e9b5SBjoern A. Zeeb 		return true;
6313b4c3e9b5SBjoern A. Zeeb 	}
6314b4c3e9b5SBjoern A. Zeeb 	return false;
6315b4c3e9b5SBjoern A. Zeeb }
6316b4c3e9b5SBjoern A. Zeeb 
brcmf_is_nonetwork(struct brcmf_cfg80211_info * cfg,const struct brcmf_event_msg * e)6317b4c3e9b5SBjoern A. Zeeb static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
6318b4c3e9b5SBjoern A. Zeeb 			       const struct brcmf_event_msg *e)
6319b4c3e9b5SBjoern A. Zeeb {
6320b4c3e9b5SBjoern A. Zeeb 	u32 event = e->event_code;
6321b4c3e9b5SBjoern A. Zeeb 	u32 status = e->status;
6322b4c3e9b5SBjoern A. Zeeb 
6323b4c3e9b5SBjoern A. Zeeb 	if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
6324b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Processing Link %s & no network found\n",
6325b4c3e9b5SBjoern A. Zeeb 			  e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
6326b4c3e9b5SBjoern A. Zeeb 		return true;
6327b4c3e9b5SBjoern A. Zeeb 	}
6328b4c3e9b5SBjoern A. Zeeb 
6329b4c3e9b5SBjoern A. Zeeb 	if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
6330b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Processing connecting & no network found\n");
6331b4c3e9b5SBjoern A. Zeeb 		return true;
6332b4c3e9b5SBjoern A. Zeeb 	}
6333b4c3e9b5SBjoern A. Zeeb 
6334b4c3e9b5SBjoern A. Zeeb 	if (event == BRCMF_E_PSK_SUP &&
6335b4c3e9b5SBjoern A. Zeeb 	    status != BRCMF_E_STATUS_FWSUP_COMPLETED) {
6336b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Processing failed supplicant state: %u\n",
6337b4c3e9b5SBjoern A. Zeeb 			  status);
6338b4c3e9b5SBjoern A. Zeeb 		return true;
6339b4c3e9b5SBjoern A. Zeeb 	}
6340b4c3e9b5SBjoern A. Zeeb 
6341b4c3e9b5SBjoern A. Zeeb 	return false;
6342b4c3e9b5SBjoern A. Zeeb }
6343b4c3e9b5SBjoern A. Zeeb 
brcmf_clear_assoc_ies(struct brcmf_cfg80211_info * cfg)6344b4c3e9b5SBjoern A. Zeeb static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
6345b4c3e9b5SBjoern A. Zeeb {
6346b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
6347b4c3e9b5SBjoern A. Zeeb 
6348b4c3e9b5SBjoern A. Zeeb 	kfree(conn_info->req_ie);
6349b4c3e9b5SBjoern A. Zeeb 	conn_info->req_ie = NULL;
6350b4c3e9b5SBjoern A. Zeeb 	conn_info->req_ie_len = 0;
6351b4c3e9b5SBjoern A. Zeeb 	kfree(conn_info->resp_ie);
6352b4c3e9b5SBjoern A. Zeeb 	conn_info->resp_ie = NULL;
6353b4c3e9b5SBjoern A. Zeeb 	conn_info->resp_ie_len = 0;
6354b4c3e9b5SBjoern A. Zeeb }
6355b4c3e9b5SBjoern A. Zeeb 
brcmf_map_prio_to_prec(void * config,u8 prio)6356b4c3e9b5SBjoern A. Zeeb u8 brcmf_map_prio_to_prec(void *config, u8 prio)
6357b4c3e9b5SBjoern A. Zeeb {
6358b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
6359b4c3e9b5SBjoern A. Zeeb 
6360b4c3e9b5SBjoern A. Zeeb 	if (!cfg)
6361b4c3e9b5SBjoern A. Zeeb 		return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
6362b4c3e9b5SBjoern A. Zeeb 		       (prio ^ 2) : prio;
6363b4c3e9b5SBjoern A. Zeeb 
6364b4c3e9b5SBjoern A. Zeeb 	/* For those AC(s) with ACM flag set to 1, convert its 4-level priority
6365b4c3e9b5SBjoern A. Zeeb 	 * to an 8-level precedence which is the same as BE's
6366b4c3e9b5SBjoern A. Zeeb 	 */
6367b4c3e9b5SBjoern A. Zeeb 	if (prio > PRIO_8021D_EE &&
6368b4c3e9b5SBjoern A. Zeeb 	    cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE])
6369b4c3e9b5SBjoern A. Zeeb 		return cfg->ac_priority[prio] * 2;
6370b4c3e9b5SBjoern A. Zeeb 
6371b4c3e9b5SBjoern A. Zeeb 	/* Conversion of 4-level priority to 8-level precedence */
6372b4c3e9b5SBjoern A. Zeeb 	if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK ||
6373b4c3e9b5SBjoern A. Zeeb 	    prio == PRIO_8021D_CL || prio == PRIO_8021D_VO)
6374b4c3e9b5SBjoern A. Zeeb 		return cfg->ac_priority[prio] * 2;
6375b4c3e9b5SBjoern A. Zeeb 	else
6376b4c3e9b5SBjoern A. Zeeb 		return cfg->ac_priority[prio] * 2 + 1;
6377b4c3e9b5SBjoern A. Zeeb }
6378b4c3e9b5SBjoern A. Zeeb 
brcmf_map_prio_to_aci(void * config,u8 prio)6379b4c3e9b5SBjoern A. Zeeb u8 brcmf_map_prio_to_aci(void *config, u8 prio)
6380b4c3e9b5SBjoern A. Zeeb {
6381b4c3e9b5SBjoern A. Zeeb 	/* Prio here refers to the 802.1d priority in range of 0 to 7.
6382b4c3e9b5SBjoern A. Zeeb 	 * ACI here refers to the WLAN AC Index in range of 0 to 3.
6383b4c3e9b5SBjoern A. Zeeb 	 * This function will return ACI corresponding to input prio.
6384b4c3e9b5SBjoern A. Zeeb 	 */
6385b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
6386b4c3e9b5SBjoern A. Zeeb 
6387b4c3e9b5SBjoern A. Zeeb 	if (cfg)
6388b4c3e9b5SBjoern A. Zeeb 		return cfg->ac_priority[prio];
6389b4c3e9b5SBjoern A. Zeeb 
6390b4c3e9b5SBjoern A. Zeeb 	return prio;
6391b4c3e9b5SBjoern A. Zeeb }
6392b4c3e9b5SBjoern A. Zeeb 
brcmf_init_wmm_prio(u8 * priority)6393b4c3e9b5SBjoern A. Zeeb static void brcmf_init_wmm_prio(u8 *priority)
6394b4c3e9b5SBjoern A. Zeeb {
6395b4c3e9b5SBjoern A. Zeeb 	/* Initialize AC priority array to default
6396b4c3e9b5SBjoern A. Zeeb 	 * 802.1d priority as per following table:
6397b4c3e9b5SBjoern A. Zeeb 	 * 802.1d prio 0,3 maps to BE
6398b4c3e9b5SBjoern A. Zeeb 	 * 802.1d prio 1,2 maps to BK
6399b4c3e9b5SBjoern A. Zeeb 	 * 802.1d prio 4,5 maps to VI
6400b4c3e9b5SBjoern A. Zeeb 	 * 802.1d prio 6,7 maps to VO
6401b4c3e9b5SBjoern A. Zeeb 	 */
6402b4c3e9b5SBjoern A. Zeeb 	priority[0] = BRCMF_FWS_FIFO_AC_BE;
6403b4c3e9b5SBjoern A. Zeeb 	priority[3] = BRCMF_FWS_FIFO_AC_BE;
6404b4c3e9b5SBjoern A. Zeeb 	priority[1] = BRCMF_FWS_FIFO_AC_BK;
6405b4c3e9b5SBjoern A. Zeeb 	priority[2] = BRCMF_FWS_FIFO_AC_BK;
6406b4c3e9b5SBjoern A. Zeeb 	priority[4] = BRCMF_FWS_FIFO_AC_VI;
6407b4c3e9b5SBjoern A. Zeeb 	priority[5] = BRCMF_FWS_FIFO_AC_VI;
6408b4c3e9b5SBjoern A. Zeeb 	priority[6] = BRCMF_FWS_FIFO_AC_VO;
6409b4c3e9b5SBjoern A. Zeeb 	priority[7] = BRCMF_FWS_FIFO_AC_VO;
6410b4c3e9b5SBjoern A. Zeeb }
6411b4c3e9b5SBjoern A. Zeeb 
brcmf_wifi_prioritize_acparams(const struct brcmf_cfg80211_edcf_acparam * acp,u8 * priority)6412b4c3e9b5SBjoern A. Zeeb static void brcmf_wifi_prioritize_acparams(const
6413b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)
6414b4c3e9b5SBjoern A. Zeeb {
6415b4c3e9b5SBjoern A. Zeeb 	u8 aci;
6416b4c3e9b5SBjoern A. Zeeb 	u8 aifsn;
6417b4c3e9b5SBjoern A. Zeeb 	u8 ecwmin;
6418b4c3e9b5SBjoern A. Zeeb 	u8 ecwmax;
6419b4c3e9b5SBjoern A. Zeeb 	u8 acm;
6420b4c3e9b5SBjoern A. Zeeb 	u8 ranking_basis[EDCF_AC_COUNT];
6421b4c3e9b5SBjoern A. Zeeb 	u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */
6422b4c3e9b5SBjoern A. Zeeb 	u8 index;
6423b4c3e9b5SBjoern A. Zeeb 
6424b4c3e9b5SBjoern A. Zeeb 	for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) {
6425b4c3e9b5SBjoern A. Zeeb 		aifsn  = acp->ACI & EDCF_AIFSN_MASK;
6426b4c3e9b5SBjoern A. Zeeb 		acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;
6427b4c3e9b5SBjoern A. Zeeb 		ecwmin = acp->ECW & EDCF_ECWMIN_MASK;
6428b4c3e9b5SBjoern A. Zeeb 		ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
6429b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n",
6430b4c3e9b5SBjoern A. Zeeb 			  aci, aifsn, acm, ecwmin, ecwmax);
6431b4c3e9b5SBjoern A. Zeeb 		/* Default AC_VO will be the lowest ranking value */
6432b4c3e9b5SBjoern A. Zeeb 		ranking_basis[aci] = aifsn + ecwmin + ecwmax;
6433b4c3e9b5SBjoern A. Zeeb 		/* Initialise priority starting at 0 (AC_BE) */
6434b4c3e9b5SBjoern A. Zeeb 		aci_prio[aci] = 0;
6435b4c3e9b5SBjoern A. Zeeb 
6436b4c3e9b5SBjoern A. Zeeb 		/* If ACM is set, STA can't use this AC as per 802.11.
6437b4c3e9b5SBjoern A. Zeeb 		 * Change the ranking to BE
6438b4c3e9b5SBjoern A. Zeeb 		 */
6439b4c3e9b5SBjoern A. Zeeb 		if (aci != AC_BE && aci != AC_BK && acm == 1)
6440b4c3e9b5SBjoern A. Zeeb 			ranking_basis[aci] = ranking_basis[AC_BE];
6441b4c3e9b5SBjoern A. Zeeb 	}
6442b4c3e9b5SBjoern A. Zeeb 
6443b4c3e9b5SBjoern A. Zeeb 	/* Ranking method which works for AC priority
6444b4c3e9b5SBjoern A. Zeeb 	 * swapping when values for cwmin, cwmax and aifsn are varied
6445b4c3e9b5SBjoern A. Zeeb 	 * Compare each aci_prio against each other aci_prio
6446b4c3e9b5SBjoern A. Zeeb 	 */
6447b4c3e9b5SBjoern A. Zeeb 	for (aci = 0; aci < EDCF_AC_COUNT; aci++) {
6448b4c3e9b5SBjoern A. Zeeb 		for (index = 0; index < EDCF_AC_COUNT; index++) {
6449b4c3e9b5SBjoern A. Zeeb 			if (index != aci) {
6450b4c3e9b5SBjoern A. Zeeb 				/* Smaller ranking value has higher priority,
6451b4c3e9b5SBjoern A. Zeeb 				 * so increment priority for each ACI which has
6452b4c3e9b5SBjoern A. Zeeb 				 * a higher ranking value
6453b4c3e9b5SBjoern A. Zeeb 				 */
6454b4c3e9b5SBjoern A. Zeeb 				if (ranking_basis[aci] < ranking_basis[index])
6455b4c3e9b5SBjoern A. Zeeb 					aci_prio[aci]++;
6456b4c3e9b5SBjoern A. Zeeb 			}
6457b4c3e9b5SBjoern A. Zeeb 		}
6458b4c3e9b5SBjoern A. Zeeb 	}
6459b4c3e9b5SBjoern A. Zeeb 
6460b4c3e9b5SBjoern A. Zeeb 	/* By now, aci_prio[] will be in range of 0 to 3.
6461b4c3e9b5SBjoern A. Zeeb 	 * Use ACI prio to get the new priority value for
6462b4c3e9b5SBjoern A. Zeeb 	 * each 802.1d traffic type, in this range.
6463b4c3e9b5SBjoern A. Zeeb 	 */
6464b4c3e9b5SBjoern A. Zeeb 	if (!(aci_prio[AC_BE] == aci_prio[AC_BK] &&
6465b4c3e9b5SBjoern A. Zeeb 	      aci_prio[AC_BK] == aci_prio[AC_VI] &&
6466b4c3e9b5SBjoern A. Zeeb 	      aci_prio[AC_VI] == aci_prio[AC_VO])) {
6467b4c3e9b5SBjoern A. Zeeb 		/* 802.1d 0,3 maps to BE */
6468b4c3e9b5SBjoern A. Zeeb 		priority[0] = aci_prio[AC_BE];
6469b4c3e9b5SBjoern A. Zeeb 		priority[3] = aci_prio[AC_BE];
6470b4c3e9b5SBjoern A. Zeeb 
6471b4c3e9b5SBjoern A. Zeeb 		/* 802.1d 1,2 maps to BK */
6472b4c3e9b5SBjoern A. Zeeb 		priority[1] = aci_prio[AC_BK];
6473b4c3e9b5SBjoern A. Zeeb 		priority[2] = aci_prio[AC_BK];
6474b4c3e9b5SBjoern A. Zeeb 
6475b4c3e9b5SBjoern A. Zeeb 		/* 802.1d 4,5 maps to VO */
6476b4c3e9b5SBjoern A. Zeeb 		priority[4] = aci_prio[AC_VI];
6477b4c3e9b5SBjoern A. Zeeb 		priority[5] = aci_prio[AC_VI];
6478b4c3e9b5SBjoern A. Zeeb 
6479b4c3e9b5SBjoern A. Zeeb 		/* 802.1d 6,7 maps to VO */
6480b4c3e9b5SBjoern A. Zeeb 		priority[6] = aci_prio[AC_VO];
6481b4c3e9b5SBjoern A. Zeeb 		priority[7] = aci_prio[AC_VO];
6482b4c3e9b5SBjoern A. Zeeb 	} else {
6483b4c3e9b5SBjoern A. Zeeb 		/* Initialize to default priority */
6484b4c3e9b5SBjoern A. Zeeb 		brcmf_init_wmm_prio(priority);
6485b4c3e9b5SBjoern A. Zeeb 	}
6486b4c3e9b5SBjoern A. Zeeb 
6487b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",
6488b4c3e9b5SBjoern A. Zeeb 		  priority[0], priority[1], priority[2], priority[3]);
6489b4c3e9b5SBjoern A. Zeeb 
6490b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n",
6491b4c3e9b5SBjoern A. Zeeb 		  priority[4], priority[5], priority[6], priority[7]);
6492b4c3e9b5SBjoern A. Zeeb }
6493b4c3e9b5SBjoern A. Zeeb 
brcmf_get_assoc_ies(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)6494b4c3e9b5SBjoern A. Zeeb static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
6495b4c3e9b5SBjoern A. Zeeb 			       struct brcmf_if *ifp)
6496b4c3e9b5SBjoern A. Zeeb {
6497b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
6498b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
6499b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
6500b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT];
6501b4c3e9b5SBjoern A. Zeeb 	u32 req_len;
6502b4c3e9b5SBjoern A. Zeeb 	u32 resp_len;
6503b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
6504b4c3e9b5SBjoern A. Zeeb 
6505b4c3e9b5SBjoern A. Zeeb 	brcmf_clear_assoc_ies(cfg);
6506b4c3e9b5SBjoern A. Zeeb 
6507b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
6508b4c3e9b5SBjoern A. Zeeb 				       cfg->extra_buf, WL_ASSOC_INFO_MAX);
6509b4c3e9b5SBjoern A. Zeeb 	if (err) {
6510b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "could not get assoc info (%d)\n", err);
6511b4c3e9b5SBjoern A. Zeeb 		return err;
6512b4c3e9b5SBjoern A. Zeeb 	}
6513b4c3e9b5SBjoern A. Zeeb 	assoc_info =
6514b4c3e9b5SBjoern A. Zeeb 		(struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
6515b4c3e9b5SBjoern A. Zeeb 	req_len = le32_to_cpu(assoc_info->req_len);
6516b4c3e9b5SBjoern A. Zeeb 	resp_len = le32_to_cpu(assoc_info->resp_len);
6517b4c3e9b5SBjoern A. Zeeb 	if (req_len > WL_EXTRA_BUF_MAX || resp_len > WL_EXTRA_BUF_MAX) {
6518b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "invalid lengths in assoc info: req %u resp %u\n",
6519b4c3e9b5SBjoern A. Zeeb 			 req_len, resp_len);
6520b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
6521b4c3e9b5SBjoern A. Zeeb 	}
6522b4c3e9b5SBjoern A. Zeeb 	if (req_len) {
6523b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
6524b4c3e9b5SBjoern A. Zeeb 					       cfg->extra_buf,
6525b4c3e9b5SBjoern A. Zeeb 					       WL_ASSOC_INFO_MAX);
6526b4c3e9b5SBjoern A. Zeeb 		if (err) {
6527b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "could not get assoc req (%d)\n", err);
6528b4c3e9b5SBjoern A. Zeeb 			return err;
6529b4c3e9b5SBjoern A. Zeeb 		}
6530b4c3e9b5SBjoern A. Zeeb 		conn_info->req_ie_len = req_len;
6531b4c3e9b5SBjoern A. Zeeb 		conn_info->req_ie =
6532b4c3e9b5SBjoern A. Zeeb 		    kmemdup(cfg->extra_buf, conn_info->req_ie_len,
6533b4c3e9b5SBjoern A. Zeeb 			    GFP_KERNEL);
6534b4c3e9b5SBjoern A. Zeeb 		if (!conn_info->req_ie)
6535b4c3e9b5SBjoern A. Zeeb 			conn_info->req_ie_len = 0;
6536b4c3e9b5SBjoern A. Zeeb 	} else {
6537b4c3e9b5SBjoern A. Zeeb 		conn_info->req_ie_len = 0;
6538b4c3e9b5SBjoern A. Zeeb 		conn_info->req_ie = NULL;
6539b4c3e9b5SBjoern A. Zeeb 	}
6540b4c3e9b5SBjoern A. Zeeb 	if (resp_len) {
6541b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
6542b4c3e9b5SBjoern A. Zeeb 					       cfg->extra_buf,
6543b4c3e9b5SBjoern A. Zeeb 					       WL_ASSOC_INFO_MAX);
6544b4c3e9b5SBjoern A. Zeeb 		if (err) {
6545b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "could not get assoc resp (%d)\n", err);
6546b4c3e9b5SBjoern A. Zeeb 			return err;
6547b4c3e9b5SBjoern A. Zeeb 		}
6548b4c3e9b5SBjoern A. Zeeb 		conn_info->resp_ie_len = resp_len;
6549b4c3e9b5SBjoern A. Zeeb 		conn_info->resp_ie =
6550b4c3e9b5SBjoern A. Zeeb 		    kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
6551b4c3e9b5SBjoern A. Zeeb 			    GFP_KERNEL);
6552b4c3e9b5SBjoern A. Zeeb 		if (!conn_info->resp_ie)
6553b4c3e9b5SBjoern A. Zeeb 			conn_info->resp_ie_len = 0;
6554b4c3e9b5SBjoern A. Zeeb 
6555b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta",
6556b4c3e9b5SBjoern A. Zeeb 					       edcf_acparam_info,
6557b4c3e9b5SBjoern A. Zeeb 					       sizeof(edcf_acparam_info));
6558b4c3e9b5SBjoern A. Zeeb 		if (err) {
6559b4c3e9b5SBjoern A. Zeeb 			brcmf_err("could not get wme_ac_sta (%d)\n", err);
6560b4c3e9b5SBjoern A. Zeeb 			return err;
6561b4c3e9b5SBjoern A. Zeeb 		}
6562b4c3e9b5SBjoern A. Zeeb 
6563b4c3e9b5SBjoern A. Zeeb 		brcmf_wifi_prioritize_acparams(edcf_acparam_info,
6564b4c3e9b5SBjoern A. Zeeb 					       cfg->ac_priority);
6565b4c3e9b5SBjoern A. Zeeb 	} else {
6566b4c3e9b5SBjoern A. Zeeb 		conn_info->resp_ie_len = 0;
6567b4c3e9b5SBjoern A. Zeeb 		conn_info->resp_ie = NULL;
6568b4c3e9b5SBjoern A. Zeeb 	}
6569b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
6570b4c3e9b5SBjoern A. Zeeb 		  conn_info->req_ie_len, conn_info->resp_ie_len);
6571b4c3e9b5SBjoern A. Zeeb 
6572b4c3e9b5SBjoern A. Zeeb 	return err;
6573b4c3e9b5SBjoern A. Zeeb }
6574b4c3e9b5SBjoern A. Zeeb 
6575b4c3e9b5SBjoern A. Zeeb static s32
brcmf_bss_roaming_done(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e)6576b4c3e9b5SBjoern A. Zeeb brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
6577b4c3e9b5SBjoern A. Zeeb 		       struct net_device *ndev,
6578b4c3e9b5SBjoern A. Zeeb 		       const struct brcmf_event_msg *e)
6579b4c3e9b5SBjoern A. Zeeb {
6580b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
6581b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
6582b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
6583b4c3e9b5SBjoern A. Zeeb 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
6584b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_channel *notify_channel = NULL;
6585b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
6586b4c3e9b5SBjoern A. Zeeb 	struct brcmf_bss_info_le *bi;
6587b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch;
6588b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_roam_info roam_info = {};
6589b4c3e9b5SBjoern A. Zeeb 	u32 freq;
6590b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
6591b4c3e9b5SBjoern A. Zeeb 	u8 *buf;
6592b4c3e9b5SBjoern A. Zeeb 
6593b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
6594b4c3e9b5SBjoern A. Zeeb 
6595b4c3e9b5SBjoern A. Zeeb 	brcmf_get_assoc_ies(cfg, ifp);
6596b4c3e9b5SBjoern A. Zeeb 	memcpy(profile->bssid, e->addr, ETH_ALEN);
6597b4c3e9b5SBjoern A. Zeeb 	brcmf_update_bss_info(cfg, ifp);
6598b4c3e9b5SBjoern A. Zeeb 
6599b4c3e9b5SBjoern A. Zeeb 	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
6600b4c3e9b5SBjoern A. Zeeb 	if (buf == NULL) {
6601b4c3e9b5SBjoern A. Zeeb 		err = -ENOMEM;
6602b4c3e9b5SBjoern A. Zeeb 		goto done;
6603b4c3e9b5SBjoern A. Zeeb 	}
6604b4c3e9b5SBjoern A. Zeeb 
6605b4c3e9b5SBjoern A. Zeeb 	/* data sent to dongle has to be little endian */
6606b4c3e9b5SBjoern A. Zeeb 	*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
6607b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
6608b4c3e9b5SBjoern A. Zeeb 				     buf, WL_BSS_INFO_MAX);
6609b4c3e9b5SBjoern A. Zeeb 
6610b4c3e9b5SBjoern A. Zeeb 	if (err)
6611b4c3e9b5SBjoern A. Zeeb 		goto done;
6612b4c3e9b5SBjoern A. Zeeb 
6613b4c3e9b5SBjoern A. Zeeb 	bi = (struct brcmf_bss_info_le *)(buf + 4);
6614b4c3e9b5SBjoern A. Zeeb 	ch.chspec = le16_to_cpu(bi->chanspec);
6615b4c3e9b5SBjoern A. Zeeb 	cfg->d11inf.decchspec(&ch);
6616b4c3e9b5SBjoern A. Zeeb 
6617b4c3e9b5SBjoern A. Zeeb 	if (ch.band == BRCMU_CHAN_BAND_2G)
6618b4c3e9b5SBjoern A. Zeeb 		band = wiphy->bands[NL80211_BAND_2GHZ];
6619b4c3e9b5SBjoern A. Zeeb 	else
6620b4c3e9b5SBjoern A. Zeeb 		band = wiphy->bands[NL80211_BAND_5GHZ];
6621b4c3e9b5SBjoern A. Zeeb 
6622b4c3e9b5SBjoern A. Zeeb 	freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
6623b4c3e9b5SBjoern A. Zeeb 	notify_channel = ieee80211_get_channel(wiphy, freq);
6624b4c3e9b5SBjoern A. Zeeb 
6625b4c3e9b5SBjoern A. Zeeb done:
6626b4c3e9b5SBjoern A. Zeeb 	kfree(buf);
6627b4c3e9b5SBjoern A. Zeeb 
6628b4c3e9b5SBjoern A. Zeeb 	roam_info.links[0].channel = notify_channel;
6629b4c3e9b5SBjoern A. Zeeb 	roam_info.links[0].bssid = profile->bssid;
6630b4c3e9b5SBjoern A. Zeeb 	roam_info.req_ie = conn_info->req_ie;
6631b4c3e9b5SBjoern A. Zeeb 	roam_info.req_ie_len = conn_info->req_ie_len;
6632b4c3e9b5SBjoern A. Zeeb 	roam_info.resp_ie = conn_info->resp_ie;
6633b4c3e9b5SBjoern A. Zeeb 	roam_info.resp_ie_len = conn_info->resp_ie_len;
6634b4c3e9b5SBjoern A. Zeeb 
6635b4c3e9b5SBjoern A. Zeeb 	cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
6636b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "Report roaming result\n");
6637b4c3e9b5SBjoern A. Zeeb 
6638b4c3e9b5SBjoern A. Zeeb 	if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) {
6639b4c3e9b5SBjoern A. Zeeb 		cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL);
6640b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Report port authorized\n");
6641b4c3e9b5SBjoern A. Zeeb 	}
6642b4c3e9b5SBjoern A. Zeeb 
6643b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
6644b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
6645b4c3e9b5SBjoern A. Zeeb 	return err;
6646b4c3e9b5SBjoern A. Zeeb }
6647b4c3e9b5SBjoern A. Zeeb 
6648b4c3e9b5SBjoern A. Zeeb static s32
brcmf_bss_connect_done(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e,bool completed)6649b4c3e9b5SBjoern A. Zeeb brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
6650b4c3e9b5SBjoern A. Zeeb 		       struct net_device *ndev, const struct brcmf_event_msg *e,
6651b4c3e9b5SBjoern A. Zeeb 		       bool completed)
6652b4c3e9b5SBjoern A. Zeeb {
6653b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
6654b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
6655b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
6656b4c3e9b5SBjoern A. Zeeb 	struct cfg80211_connect_resp_params conn_params;
6657b4c3e9b5SBjoern A. Zeeb 
6658b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter\n");
6659b4c3e9b5SBjoern A. Zeeb 
6660b4c3e9b5SBjoern A. Zeeb 	if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
6661b4c3e9b5SBjoern A. Zeeb 			       &ifp->vif->sme_state)) {
6662b4c3e9b5SBjoern A. Zeeb 		memset(&conn_params, 0, sizeof(conn_params));
6663b4c3e9b5SBjoern A. Zeeb 		if (completed) {
6664b4c3e9b5SBjoern A. Zeeb 			brcmf_get_assoc_ies(cfg, ifp);
6665b4c3e9b5SBjoern A. Zeeb 			brcmf_update_bss_info(cfg, ifp);
6666b4c3e9b5SBjoern A. Zeeb 			set_bit(BRCMF_VIF_STATUS_CONNECTED,
6667b4c3e9b5SBjoern A. Zeeb 				&ifp->vif->sme_state);
6668b4c3e9b5SBjoern A. Zeeb 			conn_params.status = WLAN_STATUS_SUCCESS;
6669b4c3e9b5SBjoern A. Zeeb 		} else {
6670b4c3e9b5SBjoern A. Zeeb 			clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS,
6671b4c3e9b5SBjoern A. Zeeb 				  &ifp->vif->sme_state);
6672b4c3e9b5SBjoern A. Zeeb 			clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS,
6673b4c3e9b5SBjoern A. Zeeb 				  &ifp->vif->sme_state);
6674b4c3e9b5SBjoern A. Zeeb 			conn_params.status = WLAN_STATUS_AUTH_TIMEOUT;
6675b4c3e9b5SBjoern A. Zeeb 		}
6676b4c3e9b5SBjoern A. Zeeb 		conn_params.links[0].bssid = profile->bssid;
6677b4c3e9b5SBjoern A. Zeeb 		conn_params.req_ie = conn_info->req_ie;
6678b4c3e9b5SBjoern A. Zeeb 		conn_params.req_ie_len = conn_info->req_ie_len;
6679b4c3e9b5SBjoern A. Zeeb 		conn_params.resp_ie = conn_info->resp_ie;
6680b4c3e9b5SBjoern A. Zeeb 		conn_params.resp_ie_len = conn_info->resp_ie_len;
6681b4c3e9b5SBjoern A. Zeeb 		cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL);
6682b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Report connect result - connection %s\n",
6683b4c3e9b5SBjoern A. Zeeb 			  completed ? "succeeded" : "failed");
6684b4c3e9b5SBjoern A. Zeeb 	}
6685b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Exit\n");
6686b4c3e9b5SBjoern A. Zeeb 	return 0;
6687b4c3e9b5SBjoern A. Zeeb }
6688b4c3e9b5SBjoern A. Zeeb 
6689b4c3e9b5SBjoern A. Zeeb static s32
brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e,void * data)6690b4c3e9b5SBjoern A. Zeeb brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
6691b4c3e9b5SBjoern A. Zeeb 			       struct net_device *ndev,
6692b4c3e9b5SBjoern A. Zeeb 			       const struct brcmf_event_msg *e, void *data)
6693b4c3e9b5SBjoern A. Zeeb {
6694b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
6695b4c3e9b5SBjoern A. Zeeb 	static int generation;
6696b4c3e9b5SBjoern A. Zeeb 	u32 event = e->event_code;
6697b4c3e9b5SBjoern A. Zeeb 	u32 reason = e->reason;
6698b4c3e9b5SBjoern A. Zeeb 	struct station_info *sinfo;
6699b4c3e9b5SBjoern A. Zeeb 
6700b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(CONN, "event %s (%u), reason %d\n",
6701b4c3e9b5SBjoern A. Zeeb 		  brcmf_fweh_event_name(event), event, reason);
6702b4c3e9b5SBjoern A. Zeeb 	if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
6703b4c3e9b5SBjoern A. Zeeb 	    ndev != cfg_to_ndev(cfg)) {
6704b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "AP mode link down\n");
6705b4c3e9b5SBjoern A. Zeeb 		complete(&cfg->vif_disabled);
6706b4c3e9b5SBjoern A. Zeeb 		return 0;
6707b4c3e9b5SBjoern A. Zeeb 	}
6708b4c3e9b5SBjoern A. Zeeb 
6709b4c3e9b5SBjoern A. Zeeb 	if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
6710b4c3e9b5SBjoern A. Zeeb 	    (reason == BRCMF_E_STATUS_SUCCESS)) {
6711b4c3e9b5SBjoern A. Zeeb 		if (!data) {
6712b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "No IEs present in ASSOC/REASSOC_IND\n");
6713b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
6714b4c3e9b5SBjoern A. Zeeb 		}
6715b4c3e9b5SBjoern A. Zeeb 
6716b4c3e9b5SBjoern A. Zeeb 		sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
6717b4c3e9b5SBjoern A. Zeeb 		if (!sinfo)
6718b4c3e9b5SBjoern A. Zeeb 			return -ENOMEM;
6719b4c3e9b5SBjoern A. Zeeb 
6720b4c3e9b5SBjoern A. Zeeb 		sinfo->assoc_req_ies = data;
6721b4c3e9b5SBjoern A. Zeeb 		sinfo->assoc_req_ies_len = e->datalen;
6722b4c3e9b5SBjoern A. Zeeb 		generation++;
6723b4c3e9b5SBjoern A. Zeeb 		sinfo->generation = generation;
6724b4c3e9b5SBjoern A. Zeeb 		cfg80211_new_sta(ndev, e->addr, sinfo, GFP_KERNEL);
6725b4c3e9b5SBjoern A. Zeeb 
6726b4c3e9b5SBjoern A. Zeeb 		kfree(sinfo);
6727b4c3e9b5SBjoern A. Zeeb 	} else if ((event == BRCMF_E_DISASSOC_IND) ||
6728b4c3e9b5SBjoern A. Zeeb 		   (event == BRCMF_E_DEAUTH_IND) ||
6729b4c3e9b5SBjoern A. Zeeb 		   (event == BRCMF_E_DEAUTH)) {
6730b4c3e9b5SBjoern A. Zeeb 		cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
6731b4c3e9b5SBjoern A. Zeeb 	}
6732b4c3e9b5SBjoern A. Zeeb 	return 0;
6733b4c3e9b5SBjoern A. Zeeb }
6734b4c3e9b5SBjoern A. Zeeb 
6735b4c3e9b5SBjoern A. Zeeb static s32
brcmf_notify_connect_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)6736b4c3e9b5SBjoern A. Zeeb brcmf_notify_connect_status(struct brcmf_if *ifp,
6737b4c3e9b5SBjoern A. Zeeb 			    const struct brcmf_event_msg *e, void *data)
6738b4c3e9b5SBjoern A. Zeeb {
6739b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
6740b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = ifp->ndev;
6741b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
6742b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_channel *chan;
6743b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
6744b4c3e9b5SBjoern A. Zeeb 
6745b4c3e9b5SBjoern A. Zeeb 	if ((e->event_code == BRCMF_E_DEAUTH) ||
6746b4c3e9b5SBjoern A. Zeeb 	    (e->event_code == BRCMF_E_DEAUTH_IND) ||
6747b4c3e9b5SBjoern A. Zeeb 	    (e->event_code == BRCMF_E_DISASSOC_IND) ||
6748b4c3e9b5SBjoern A. Zeeb 	    ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
6749902136e0SBjoern A. Zeeb #if defined(__linux__)
6750b4c3e9b5SBjoern A. Zeeb 		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
6751902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
6752902136e0SBjoern A. Zeeb 		brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, e->addr);
6753902136e0SBjoern A. Zeeb #endif
6754b4c3e9b5SBjoern A. Zeeb 	}
6755b4c3e9b5SBjoern A. Zeeb 
6756b4c3e9b5SBjoern A. Zeeb 	if (brcmf_is_apmode(ifp->vif)) {
6757b4c3e9b5SBjoern A. Zeeb 		err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
6758b4c3e9b5SBjoern A. Zeeb 	} else if (brcmf_is_linkup(ifp->vif, e)) {
6759b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Linkup\n");
6760b4c3e9b5SBjoern A. Zeeb 		if (brcmf_is_ibssmode(ifp->vif)) {
6761b4c3e9b5SBjoern A. Zeeb 			brcmf_inform_ibss(cfg, ndev, e->addr);
6762b4c3e9b5SBjoern A. Zeeb 			chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
6763b4c3e9b5SBjoern A. Zeeb 			memcpy(profile->bssid, e->addr, ETH_ALEN);
6764b4c3e9b5SBjoern A. Zeeb 			cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
6765b4c3e9b5SBjoern A. Zeeb 			clear_bit(BRCMF_VIF_STATUS_CONNECTING,
6766b4c3e9b5SBjoern A. Zeeb 				  &ifp->vif->sme_state);
6767b4c3e9b5SBjoern A. Zeeb 			set_bit(BRCMF_VIF_STATUS_CONNECTED,
6768b4c3e9b5SBjoern A. Zeeb 				&ifp->vif->sme_state);
6769b4c3e9b5SBjoern A. Zeeb 		} else
6770b4c3e9b5SBjoern A. Zeeb 			brcmf_bss_connect_done(cfg, ndev, e, true);
6771b4c3e9b5SBjoern A. Zeeb 		brcmf_net_setcarrier(ifp, true);
6772b4c3e9b5SBjoern A. Zeeb 	} else if (brcmf_is_linkdown(ifp->vif, e)) {
6773b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(CONN, "Linkdown\n");
6774b4c3e9b5SBjoern A. Zeeb 		if (!brcmf_is_ibssmode(ifp->vif) &&
6775b4c3e9b5SBjoern A. Zeeb 		    (test_bit(BRCMF_VIF_STATUS_CONNECTED,
6776b4c3e9b5SBjoern A. Zeeb 			      &ifp->vif->sme_state) ||
6777b4c3e9b5SBjoern A. Zeeb 		     test_bit(BRCMF_VIF_STATUS_CONNECTING,
6778b4c3e9b5SBjoern A. Zeeb 			      &ifp->vif->sme_state))) {
6779b4c3e9b5SBjoern A. Zeeb 			if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
6780b4c3e9b5SBjoern A. Zeeb 				     &ifp->vif->sme_state) &&
6781b4c3e9b5SBjoern A. Zeeb 			    memcmp(profile->bssid, e->addr, ETH_ALEN))
6782b4c3e9b5SBjoern A. Zeeb 				return err;
6783b4c3e9b5SBjoern A. Zeeb 
6784b4c3e9b5SBjoern A. Zeeb 			brcmf_bss_connect_done(cfg, ndev, e, false);
6785b4c3e9b5SBjoern A. Zeeb 			brcmf_link_down(ifp->vif,
6786b4c3e9b5SBjoern A. Zeeb 					brcmf_map_fw_linkdown_reason(e),
6787b4c3e9b5SBjoern A. Zeeb 					e->event_code &
6788b4c3e9b5SBjoern A. Zeeb 					(BRCMF_E_DEAUTH_IND |
6789b4c3e9b5SBjoern A. Zeeb 					BRCMF_E_DISASSOC_IND)
6790b4c3e9b5SBjoern A. Zeeb 					? false : true);
6791b4c3e9b5SBjoern A. Zeeb 			brcmf_init_prof(ndev_to_prof(ndev));
6792b4c3e9b5SBjoern A. Zeeb 			if (ndev != cfg_to_ndev(cfg))
6793b4c3e9b5SBjoern A. Zeeb 				complete(&cfg->vif_disabled);
6794b4c3e9b5SBjoern A. Zeeb 			brcmf_net_setcarrier(ifp, false);
6795b4c3e9b5SBjoern A. Zeeb 		}
6796b4c3e9b5SBjoern A. Zeeb 	} else if (brcmf_is_nonetwork(cfg, e)) {
6797b4c3e9b5SBjoern A. Zeeb 		if (brcmf_is_ibssmode(ifp->vif))
6798b4c3e9b5SBjoern A. Zeeb 			clear_bit(BRCMF_VIF_STATUS_CONNECTING,
6799b4c3e9b5SBjoern A. Zeeb 				  &ifp->vif->sme_state);
6800b4c3e9b5SBjoern A. Zeeb 		else
6801b4c3e9b5SBjoern A. Zeeb 			brcmf_bss_connect_done(cfg, ndev, e, false);
6802b4c3e9b5SBjoern A. Zeeb 	}
6803b4c3e9b5SBjoern A. Zeeb 
6804b4c3e9b5SBjoern A. Zeeb 	return err;
6805b4c3e9b5SBjoern A. Zeeb }
6806b4c3e9b5SBjoern A. Zeeb 
6807b4c3e9b5SBjoern A. Zeeb static s32
brcmf_notify_roaming_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)6808b4c3e9b5SBjoern A. Zeeb brcmf_notify_roaming_status(struct brcmf_if *ifp,
6809b4c3e9b5SBjoern A. Zeeb 			    const struct brcmf_event_msg *e, void *data)
6810b4c3e9b5SBjoern A. Zeeb {
6811b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
6812b4c3e9b5SBjoern A. Zeeb 	u32 event = e->event_code;
6813b4c3e9b5SBjoern A. Zeeb 	u32 status = e->status;
6814b4c3e9b5SBjoern A. Zeeb 
6815b4c3e9b5SBjoern A. Zeeb 	if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
6816b4c3e9b5SBjoern A. Zeeb 		if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
6817b4c3e9b5SBjoern A. Zeeb 			     &ifp->vif->sme_state)) {
6818b4c3e9b5SBjoern A. Zeeb 			brcmf_bss_roaming_done(cfg, ifp->ndev, e);
6819b4c3e9b5SBjoern A. Zeeb 		} else {
6820b4c3e9b5SBjoern A. Zeeb 			brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
6821b4c3e9b5SBjoern A. Zeeb 			brcmf_net_setcarrier(ifp, true);
6822b4c3e9b5SBjoern A. Zeeb 		}
6823b4c3e9b5SBjoern A. Zeeb 	}
6824b4c3e9b5SBjoern A. Zeeb 
6825b4c3e9b5SBjoern A. Zeeb 	return 0;
6826b4c3e9b5SBjoern A. Zeeb }
6827b4c3e9b5SBjoern A. Zeeb 
6828b4c3e9b5SBjoern A. Zeeb static s32
brcmf_notify_mic_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)6829b4c3e9b5SBjoern A. Zeeb brcmf_notify_mic_status(struct brcmf_if *ifp,
6830b4c3e9b5SBjoern A. Zeeb 			const struct brcmf_event_msg *e, void *data)
6831b4c3e9b5SBjoern A. Zeeb {
6832b4c3e9b5SBjoern A. Zeeb 	u16 flags = e->flags;
6833b4c3e9b5SBjoern A. Zeeb 	enum nl80211_key_type key_type;
6834b4c3e9b5SBjoern A. Zeeb 
6835b4c3e9b5SBjoern A. Zeeb 	if (flags & BRCMF_EVENT_MSG_GROUP)
6836b4c3e9b5SBjoern A. Zeeb 		key_type = NL80211_KEYTYPE_GROUP;
6837b4c3e9b5SBjoern A. Zeeb 	else
6838b4c3e9b5SBjoern A. Zeeb 		key_type = NL80211_KEYTYPE_PAIRWISE;
6839b4c3e9b5SBjoern A. Zeeb 
6840902136e0SBjoern A. Zeeb #if defined(__linux__)
6841b4c3e9b5SBjoern A. Zeeb 	cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
6842902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
6843902136e0SBjoern A. Zeeb 	cfg80211_michael_mic_failure(ifp->ndev, e->addr, key_type, -1,
6844902136e0SBjoern A. Zeeb #endif
6845b4c3e9b5SBjoern A. Zeeb 				     NULL, GFP_KERNEL);
6846b4c3e9b5SBjoern A. Zeeb 
6847b4c3e9b5SBjoern A. Zeeb 	return 0;
6848b4c3e9b5SBjoern A. Zeeb }
6849b4c3e9b5SBjoern A. Zeeb 
brcmf_notify_rssi(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)6850b4c3e9b5SBjoern A. Zeeb static s32 brcmf_notify_rssi(struct brcmf_if *ifp,
6851b4c3e9b5SBjoern A. Zeeb 			     const struct brcmf_event_msg *e, void *data)
6852b4c3e9b5SBjoern A. Zeeb {
6853b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif = ifp->vif;
6854b4c3e9b5SBjoern A. Zeeb 	struct brcmf_rssi_be *info = data;
6855b4c3e9b5SBjoern A. Zeeb 	s32 rssi, snr = 0, noise = 0;
6856b4c3e9b5SBjoern A. Zeeb 	s32 low, high, last;
6857b4c3e9b5SBjoern A. Zeeb 
6858b4c3e9b5SBjoern A. Zeeb 	if (e->datalen >= sizeof(*info)) {
6859b4c3e9b5SBjoern A. Zeeb 		rssi = be32_to_cpu(info->rssi);
6860b4c3e9b5SBjoern A. Zeeb 		snr = be32_to_cpu(info->snr);
6861b4c3e9b5SBjoern A. Zeeb 		noise = be32_to_cpu(info->noise);
6862b4c3e9b5SBjoern A. Zeeb 	} else if (e->datalen >= sizeof(rssi)) {
6863b4c3e9b5SBjoern A. Zeeb 		rssi = be32_to_cpu(*(__be32 *)data);
6864b4c3e9b5SBjoern A. Zeeb 	} else {
6865b4c3e9b5SBjoern A. Zeeb 		brcmf_err("insufficient RSSI event data\n");
6866b4c3e9b5SBjoern A. Zeeb 		return 0;
6867b4c3e9b5SBjoern A. Zeeb 	}
6868b4c3e9b5SBjoern A. Zeeb 
6869b4c3e9b5SBjoern A. Zeeb 	low = vif->cqm_rssi_low;
6870b4c3e9b5SBjoern A. Zeeb 	high = vif->cqm_rssi_high;
6871b4c3e9b5SBjoern A. Zeeb 	last = vif->cqm_rssi_last;
6872b4c3e9b5SBjoern A. Zeeb 
6873b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "rssi=%d snr=%d noise=%d low=%d high=%d last=%d\n",
6874b4c3e9b5SBjoern A. Zeeb 		  rssi, snr, noise, low, high, last);
6875b4c3e9b5SBjoern A. Zeeb 
6876b4c3e9b5SBjoern A. Zeeb 	vif->cqm_rssi_last = rssi;
6877b4c3e9b5SBjoern A. Zeeb 
6878b4c3e9b5SBjoern A. Zeeb 	if (rssi <= low || rssi == 0) {
6879b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "LOW rssi=%d\n", rssi);
6880b4c3e9b5SBjoern A. Zeeb 		cfg80211_cqm_rssi_notify(ifp->ndev,
6881b4c3e9b5SBjoern A. Zeeb 					 NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
6882b4c3e9b5SBjoern A. Zeeb 					 rssi, GFP_KERNEL);
6883b4c3e9b5SBjoern A. Zeeb 	} else if (rssi > high) {
6884b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "HIGH rssi=%d\n", rssi);
6885b4c3e9b5SBjoern A. Zeeb 		cfg80211_cqm_rssi_notify(ifp->ndev,
6886b4c3e9b5SBjoern A. Zeeb 					 NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
6887b4c3e9b5SBjoern A. Zeeb 					 rssi, GFP_KERNEL);
6888b4c3e9b5SBjoern A. Zeeb 	}
6889b4c3e9b5SBjoern A. Zeeb 
6890b4c3e9b5SBjoern A. Zeeb 	return 0;
6891b4c3e9b5SBjoern A. Zeeb }
6892b4c3e9b5SBjoern A. Zeeb 
brcmf_notify_vif_event(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)6893b4c3e9b5SBjoern A. Zeeb static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
6894b4c3e9b5SBjoern A. Zeeb 				  const struct brcmf_event_msg *e, void *data)
6895b4c3e9b5SBjoern A. Zeeb {
6896b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
6897b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
6898b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6899b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
6900b4c3e9b5SBjoern A. Zeeb 
6901b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
6902b4c3e9b5SBjoern A. Zeeb 		  ifevent->action, ifevent->flags, ifevent->ifidx,
6903b4c3e9b5SBjoern A. Zeeb 		  ifevent->bsscfgidx);
6904b4c3e9b5SBjoern A. Zeeb 
6905b4c3e9b5SBjoern A. Zeeb 	spin_lock(&event->vif_event_lock);
6906b4c3e9b5SBjoern A. Zeeb 	event->action = ifevent->action;
6907b4c3e9b5SBjoern A. Zeeb 	vif = event->vif;
6908b4c3e9b5SBjoern A. Zeeb 
6909b4c3e9b5SBjoern A. Zeeb 	switch (ifevent->action) {
6910b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_IF_ADD:
6911b4c3e9b5SBjoern A. Zeeb 		/* waiting process may have timed out */
6912b4c3e9b5SBjoern A. Zeeb 		if (!cfg->vif_event.vif) {
6913b4c3e9b5SBjoern A. Zeeb 			spin_unlock(&event->vif_event_lock);
6914b4c3e9b5SBjoern A. Zeeb 			return -EBADF;
6915b4c3e9b5SBjoern A. Zeeb 		}
6916b4c3e9b5SBjoern A. Zeeb 
6917b4c3e9b5SBjoern A. Zeeb 		ifp->vif = vif;
6918b4c3e9b5SBjoern A. Zeeb 		vif->ifp = ifp;
6919b4c3e9b5SBjoern A. Zeeb 		if (ifp->ndev) {
6920b4c3e9b5SBjoern A. Zeeb 			vif->wdev.netdev = ifp->ndev;
6921b4c3e9b5SBjoern A. Zeeb 			ifp->ndev->ieee80211_ptr = &vif->wdev;
6922b4c3e9b5SBjoern A. Zeeb 			SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
6923b4c3e9b5SBjoern A. Zeeb 		}
6924b4c3e9b5SBjoern A. Zeeb 		spin_unlock(&event->vif_event_lock);
6925b4c3e9b5SBjoern A. Zeeb 		wake_up(&event->vif_wq);
6926b4c3e9b5SBjoern A. Zeeb 		return 0;
6927b4c3e9b5SBjoern A. Zeeb 
6928b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_IF_DEL:
6929b4c3e9b5SBjoern A. Zeeb 		spin_unlock(&event->vif_event_lock);
6930b4c3e9b5SBjoern A. Zeeb 		/* event may not be upon user request */
6931b4c3e9b5SBjoern A. Zeeb 		if (brcmf_cfg80211_vif_event_armed(cfg))
6932b4c3e9b5SBjoern A. Zeeb 			wake_up(&event->vif_wq);
6933b4c3e9b5SBjoern A. Zeeb 		return 0;
6934b4c3e9b5SBjoern A. Zeeb 
6935b4c3e9b5SBjoern A. Zeeb 	case BRCMF_E_IF_CHANGE:
6936b4c3e9b5SBjoern A. Zeeb 		spin_unlock(&event->vif_event_lock);
6937b4c3e9b5SBjoern A. Zeeb 		wake_up(&event->vif_wq);
6938b4c3e9b5SBjoern A. Zeeb 		return 0;
6939b4c3e9b5SBjoern A. Zeeb 
6940b4c3e9b5SBjoern A. Zeeb 	default:
6941b4c3e9b5SBjoern A. Zeeb 		spin_unlock(&event->vif_event_lock);
6942b4c3e9b5SBjoern A. Zeeb 		break;
6943b4c3e9b5SBjoern A. Zeeb 	}
6944b4c3e9b5SBjoern A. Zeeb 	return -EINVAL;
6945b4c3e9b5SBjoern A. Zeeb }
6946b4c3e9b5SBjoern A. Zeeb 
brcmf_init_conf(struct brcmf_cfg80211_conf * conf)6947b4c3e9b5SBjoern A. Zeeb static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
6948b4c3e9b5SBjoern A. Zeeb {
6949b4c3e9b5SBjoern A. Zeeb 	conf->frag_threshold = (u32)-1;
6950b4c3e9b5SBjoern A. Zeeb 	conf->rts_threshold = (u32)-1;
6951b4c3e9b5SBjoern A. Zeeb 	conf->retry_short = (u32)-1;
6952b4c3e9b5SBjoern A. Zeeb 	conf->retry_long = (u32)-1;
6953b4c3e9b5SBjoern A. Zeeb }
6954b4c3e9b5SBjoern A. Zeeb 
brcmf_register_event_handlers(struct brcmf_cfg80211_info * cfg)6955b4c3e9b5SBjoern A. Zeeb static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
6956b4c3e9b5SBjoern A. Zeeb {
6957b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
6958b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6959b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
6960b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6961b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
6962b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6963b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
6964b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6965b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
6966b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6967b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
6968b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6969b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
6970b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_roaming_status);
6971b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
6972b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_mic_status);
6973b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
6974b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6975b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
6976b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_sched_scan_results);
6977b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
6978b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_vif_event);
6979b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
6980b4c3e9b5SBjoern A. Zeeb 			    brcmf_p2p_notify_rx_mgmt_p2p_probereq);
6981b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
6982b4c3e9b5SBjoern A. Zeeb 			    brcmf_p2p_notify_listen_complete);
6983b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
6984b4c3e9b5SBjoern A. Zeeb 			    brcmf_p2p_notify_action_frame_rx);
6985b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
6986b4c3e9b5SBjoern A. Zeeb 			    brcmf_p2p_notify_action_tx_complete);
6987b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
6988b4c3e9b5SBjoern A. Zeeb 			    brcmf_p2p_notify_action_tx_complete);
6989b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
6990b4c3e9b5SBjoern A. Zeeb 			    brcmf_notify_connect_status);
6991b4c3e9b5SBjoern A. Zeeb 	brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi);
6992b4c3e9b5SBjoern A. Zeeb 
6993b4c3e9b5SBjoern A. Zeeb 	brcmf_fwvid_register_event_handlers(cfg->pub);
6994b4c3e9b5SBjoern A. Zeeb }
6995b4c3e9b5SBjoern A. Zeeb 
brcmf_deinit_priv_mem(struct brcmf_cfg80211_info * cfg)6996b4c3e9b5SBjoern A. Zeeb static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
6997b4c3e9b5SBjoern A. Zeeb {
6998b4c3e9b5SBjoern A. Zeeb 	kfree(cfg->conf);
6999b4c3e9b5SBjoern A. Zeeb 	cfg->conf = NULL;
7000b4c3e9b5SBjoern A. Zeeb 	kfree(cfg->extra_buf);
7001b4c3e9b5SBjoern A. Zeeb 	cfg->extra_buf = NULL;
7002b4c3e9b5SBjoern A. Zeeb 	kfree(cfg->wowl.nd);
7003b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd = NULL;
7004b4c3e9b5SBjoern A. Zeeb 	kfree(cfg->wowl.nd_info);
7005b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd_info = NULL;
7006b4c3e9b5SBjoern A. Zeeb 	kfree(cfg->escan_info.escan_buf);
7007b4c3e9b5SBjoern A. Zeeb 	cfg->escan_info.escan_buf = NULL;
7008b4c3e9b5SBjoern A. Zeeb }
7009b4c3e9b5SBjoern A. Zeeb 
brcmf_init_priv_mem(struct brcmf_cfg80211_info * cfg)7010b4c3e9b5SBjoern A. Zeeb static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
7011b4c3e9b5SBjoern A. Zeeb {
7012b4c3e9b5SBjoern A. Zeeb 	cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
7013b4c3e9b5SBjoern A. Zeeb 	if (!cfg->conf)
7014b4c3e9b5SBjoern A. Zeeb 		goto init_priv_mem_out;
7015b4c3e9b5SBjoern A. Zeeb 	cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
7016b4c3e9b5SBjoern A. Zeeb 	if (!cfg->extra_buf)
7017b4c3e9b5SBjoern A. Zeeb 		goto init_priv_mem_out;
7018b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
7019b4c3e9b5SBjoern A. Zeeb 	if (!cfg->wowl.nd)
7020b4c3e9b5SBjoern A. Zeeb 		goto init_priv_mem_out;
7021b4c3e9b5SBjoern A. Zeeb 	cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
7022b4c3e9b5SBjoern A. Zeeb 				    sizeof(struct cfg80211_wowlan_nd_match *),
7023b4c3e9b5SBjoern A. Zeeb 				    GFP_KERNEL);
7024b4c3e9b5SBjoern A. Zeeb 	if (!cfg->wowl.nd_info)
7025b4c3e9b5SBjoern A. Zeeb 		goto init_priv_mem_out;
7026b4c3e9b5SBjoern A. Zeeb 	cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
7027b4c3e9b5SBjoern A. Zeeb 	if (!cfg->escan_info.escan_buf)
7028b4c3e9b5SBjoern A. Zeeb 		goto init_priv_mem_out;
7029b4c3e9b5SBjoern A. Zeeb 
7030b4c3e9b5SBjoern A. Zeeb 	return 0;
7031b4c3e9b5SBjoern A. Zeeb 
7032b4c3e9b5SBjoern A. Zeeb init_priv_mem_out:
7033b4c3e9b5SBjoern A. Zeeb 	brcmf_deinit_priv_mem(cfg);
7034b4c3e9b5SBjoern A. Zeeb 
7035b4c3e9b5SBjoern A. Zeeb 	return -ENOMEM;
7036b4c3e9b5SBjoern A. Zeeb }
7037b4c3e9b5SBjoern A. Zeeb 
wl_init_priv(struct brcmf_cfg80211_info * cfg)7038b4c3e9b5SBjoern A. Zeeb static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
7039b4c3e9b5SBjoern A. Zeeb {
7040b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
7041b4c3e9b5SBjoern A. Zeeb 
7042b4c3e9b5SBjoern A. Zeeb 	cfg->scan_request = NULL;
7043b4c3e9b5SBjoern A. Zeeb 	cfg->pwr_save = true;
7044b4c3e9b5SBjoern A. Zeeb 	cfg->dongle_up = false;		/* dongle is not up yet */
7045b4c3e9b5SBjoern A. Zeeb 	err = brcmf_init_priv_mem(cfg);
7046b4c3e9b5SBjoern A. Zeeb 	if (err)
7047b4c3e9b5SBjoern A. Zeeb 		return err;
7048b4c3e9b5SBjoern A. Zeeb 	brcmf_register_event_handlers(cfg);
7049b4c3e9b5SBjoern A. Zeeb 	mutex_init(&cfg->usr_sync);
7050b4c3e9b5SBjoern A. Zeeb 	brcmf_init_escan(cfg);
7051b4c3e9b5SBjoern A. Zeeb 	brcmf_init_conf(cfg->conf);
7052b4c3e9b5SBjoern A. Zeeb 	brcmf_init_wmm_prio(cfg->ac_priority);
7053b4c3e9b5SBjoern A. Zeeb 	init_completion(&cfg->vif_disabled);
7054b4c3e9b5SBjoern A. Zeeb 	return err;
7055b4c3e9b5SBjoern A. Zeeb }
7056b4c3e9b5SBjoern A. Zeeb 
wl_deinit_priv(struct brcmf_cfg80211_info * cfg)7057b4c3e9b5SBjoern A. Zeeb static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
7058b4c3e9b5SBjoern A. Zeeb {
7059b4c3e9b5SBjoern A. Zeeb 	cfg->dongle_up = false;	/* dongle down */
7060b4c3e9b5SBjoern A. Zeeb 	brcmf_abort_scanning(cfg);
7061b4c3e9b5SBjoern A. Zeeb 	brcmf_deinit_priv_mem(cfg);
7062b4c3e9b5SBjoern A. Zeeb 	brcmf_clear_assoc_ies(cfg);
7063b4c3e9b5SBjoern A. Zeeb }
7064b4c3e9b5SBjoern A. Zeeb 
init_vif_event(struct brcmf_cfg80211_vif_event * event)7065b4c3e9b5SBjoern A. Zeeb static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
7066b4c3e9b5SBjoern A. Zeeb {
7067b4c3e9b5SBjoern A. Zeeb 	init_waitqueue_head(&event->vif_wq);
7068b4c3e9b5SBjoern A. Zeeb 	spin_lock_init(&event->vif_event_lock);
7069b4c3e9b5SBjoern A. Zeeb }
7070b4c3e9b5SBjoern A. Zeeb 
brcmf_dongle_roam(struct brcmf_if * ifp)7071b4c3e9b5SBjoern A. Zeeb static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
7072b4c3e9b5SBjoern A. Zeeb {
7073b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
7074b4c3e9b5SBjoern A. Zeeb 	s32 err;
7075b4c3e9b5SBjoern A. Zeeb 	u32 bcn_timeout;
7076b4c3e9b5SBjoern A. Zeeb 	__le32 roamtrigger[2];
7077b4c3e9b5SBjoern A. Zeeb 	__le32 roam_delta[2];
7078b4c3e9b5SBjoern A. Zeeb 
7079b4c3e9b5SBjoern A. Zeeb 	/* Configure beacon timeout value based upon roaming setting */
7080b4c3e9b5SBjoern A. Zeeb 	if (ifp->drvr->settings->roamoff)
7081b4c3e9b5SBjoern A. Zeeb 		bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
7082b4c3e9b5SBjoern A. Zeeb 	else
7083b4c3e9b5SBjoern A. Zeeb 		bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
7084b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
7085b4c3e9b5SBjoern A. Zeeb 	if (err) {
7086b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "bcn_timeout error (%d)\n", err);
7087b4c3e9b5SBjoern A. Zeeb 		goto roam_setup_done;
7088b4c3e9b5SBjoern A. Zeeb 	}
7089b4c3e9b5SBjoern A. Zeeb 
7090b4c3e9b5SBjoern A. Zeeb 	/* Enable/Disable built-in roaming to allow supplicant to take care of
7091b4c3e9b5SBjoern A. Zeeb 	 * roaming.
7092b4c3e9b5SBjoern A. Zeeb 	 */
7093b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "Internal Roaming = %s\n",
7094b4c3e9b5SBjoern A. Zeeb 		  ifp->drvr->settings->roamoff ? "Off" : "On");
7095b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_set(ifp, "roam_off",
7096b4c3e9b5SBjoern A. Zeeb 				      ifp->drvr->settings->roamoff);
7097b4c3e9b5SBjoern A. Zeeb 	if (err) {
7098b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "roam_off error (%d)\n", err);
7099b4c3e9b5SBjoern A. Zeeb 		goto roam_setup_done;
7100b4c3e9b5SBjoern A. Zeeb 	}
7101b4c3e9b5SBjoern A. Zeeb 
7102b4c3e9b5SBjoern A. Zeeb 	roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
7103b4c3e9b5SBjoern A. Zeeb 	roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
7104b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
7105b4c3e9b5SBjoern A. Zeeb 				     (void *)roamtrigger, sizeof(roamtrigger));
7106b4c3e9b5SBjoern A. Zeeb 	if (err)
7107b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err);
7108b4c3e9b5SBjoern A. Zeeb 
7109b4c3e9b5SBjoern A. Zeeb 	roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
7110b4c3e9b5SBjoern A. Zeeb 	roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
7111b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
7112b4c3e9b5SBjoern A. Zeeb 				     (void *)roam_delta, sizeof(roam_delta));
7113b4c3e9b5SBjoern A. Zeeb 	if (err)
7114b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err);
7115b4c3e9b5SBjoern A. Zeeb 
7116b4c3e9b5SBjoern A. Zeeb 	return 0;
7117b4c3e9b5SBjoern A. Zeeb 
7118b4c3e9b5SBjoern A. Zeeb roam_setup_done:
7119b4c3e9b5SBjoern A. Zeeb 	return err;
7120b4c3e9b5SBjoern A. Zeeb }
7121b4c3e9b5SBjoern A. Zeeb 
7122b4c3e9b5SBjoern A. Zeeb static s32
brcmf_dongle_scantime(struct brcmf_if * ifp)7123b4c3e9b5SBjoern A. Zeeb brcmf_dongle_scantime(struct brcmf_if *ifp)
7124b4c3e9b5SBjoern A. Zeeb {
7125b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
7126b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
7127b4c3e9b5SBjoern A. Zeeb 
7128b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
7129b4c3e9b5SBjoern A. Zeeb 				    BRCMF_SCAN_CHANNEL_TIME);
7130b4c3e9b5SBjoern A. Zeeb 	if (err) {
7131b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Scan assoc time error (%d)\n", err);
7132b4c3e9b5SBjoern A. Zeeb 		goto dongle_scantime_out;
7133b4c3e9b5SBjoern A. Zeeb 	}
7134b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
7135b4c3e9b5SBjoern A. Zeeb 				    BRCMF_SCAN_UNASSOC_TIME);
7136b4c3e9b5SBjoern A. Zeeb 	if (err) {
7137b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Scan unassoc time error (%d)\n", err);
7138b4c3e9b5SBjoern A. Zeeb 		goto dongle_scantime_out;
7139b4c3e9b5SBjoern A. Zeeb 	}
7140b4c3e9b5SBjoern A. Zeeb 
7141b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
7142b4c3e9b5SBjoern A. Zeeb 				    BRCMF_SCAN_PASSIVE_TIME);
7143b4c3e9b5SBjoern A. Zeeb 	if (err) {
7144b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Scan passive time error (%d)\n", err);
7145b4c3e9b5SBjoern A. Zeeb 		goto dongle_scantime_out;
7146b4c3e9b5SBjoern A. Zeeb 	}
7147b4c3e9b5SBjoern A. Zeeb 
7148b4c3e9b5SBjoern A. Zeeb dongle_scantime_out:
7149b4c3e9b5SBjoern A. Zeeb 	return err;
7150b4c3e9b5SBjoern A. Zeeb }
7151b4c3e9b5SBjoern A. Zeeb 
brcmf_update_bw40_channel_flag(struct ieee80211_channel * channel,struct brcmu_chan * ch)7152b4c3e9b5SBjoern A. Zeeb static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
7153b4c3e9b5SBjoern A. Zeeb 					   struct brcmu_chan *ch)
7154b4c3e9b5SBjoern A. Zeeb {
7155b4c3e9b5SBjoern A. Zeeb 	u32 ht40_flag;
7156b4c3e9b5SBjoern A. Zeeb 
7157b4c3e9b5SBjoern A. Zeeb 	ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
7158b4c3e9b5SBjoern A. Zeeb 	if (ch->sb == BRCMU_CHAN_SB_U) {
7159b4c3e9b5SBjoern A. Zeeb 		if (ht40_flag == IEEE80211_CHAN_NO_HT40)
7160b4c3e9b5SBjoern A. Zeeb 			channel->flags &= ~IEEE80211_CHAN_NO_HT40;
7161b4c3e9b5SBjoern A. Zeeb 		channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
7162b4c3e9b5SBjoern A. Zeeb 	} else {
7163b4c3e9b5SBjoern A. Zeeb 		/* It should be one of
7164b4c3e9b5SBjoern A. Zeeb 		 * IEEE80211_CHAN_NO_HT40 or
7165b4c3e9b5SBjoern A. Zeeb 		 * IEEE80211_CHAN_NO_HT40PLUS
7166b4c3e9b5SBjoern A. Zeeb 		 */
7167b4c3e9b5SBjoern A. Zeeb 		channel->flags &= ~IEEE80211_CHAN_NO_HT40;
7168b4c3e9b5SBjoern A. Zeeb 		if (ht40_flag == IEEE80211_CHAN_NO_HT40)
7169b4c3e9b5SBjoern A. Zeeb 			channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
7170b4c3e9b5SBjoern A. Zeeb 	}
7171b4c3e9b5SBjoern A. Zeeb }
7172b4c3e9b5SBjoern A. Zeeb 
brcmf_construct_chaninfo(struct brcmf_cfg80211_info * cfg,u32 bw_cap[])7173b4c3e9b5SBjoern A. Zeeb static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
7174b4c3e9b5SBjoern A. Zeeb 				    u32 bw_cap[])
7175b4c3e9b5SBjoern A. Zeeb {
7176b4c3e9b5SBjoern A. Zeeb 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
7177b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
7178b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
7179b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
7180b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_channel *channel;
7181b4c3e9b5SBjoern A. Zeeb 	struct brcmf_chanspec_list *list;
7182b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch;
7183b4c3e9b5SBjoern A. Zeeb 	int err;
7184b4c3e9b5SBjoern A. Zeeb 	u8 *pbuf;
7185b4c3e9b5SBjoern A. Zeeb 	u32 i, j;
7186b4c3e9b5SBjoern A. Zeeb 	u32 total;
7187b4c3e9b5SBjoern A. Zeeb 	u32 chaninfo;
7188b4c3e9b5SBjoern A. Zeeb 
7189b4c3e9b5SBjoern A. Zeeb 	pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
7190b4c3e9b5SBjoern A. Zeeb 
7191b4c3e9b5SBjoern A. Zeeb 	if (pbuf == NULL)
7192b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
7193b4c3e9b5SBjoern A. Zeeb 
7194b4c3e9b5SBjoern A. Zeeb 	list = (struct brcmf_chanspec_list *)pbuf;
7195b4c3e9b5SBjoern A. Zeeb 
7196b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
7197b4c3e9b5SBjoern A. Zeeb 				       BRCMF_DCMD_MEDLEN);
7198b4c3e9b5SBjoern A. Zeeb 	if (err) {
7199b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "get chanspecs error (%d)\n", err);
7200b4c3e9b5SBjoern A. Zeeb 		goto fail_pbuf;
7201b4c3e9b5SBjoern A. Zeeb 	}
7202b4c3e9b5SBjoern A. Zeeb 
7203b4c3e9b5SBjoern A. Zeeb 	band = wiphy->bands[NL80211_BAND_2GHZ];
7204b4c3e9b5SBjoern A. Zeeb 	if (band)
7205b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < band->n_channels; i++)
7206b4c3e9b5SBjoern A. Zeeb 			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
7207b4c3e9b5SBjoern A. Zeeb 	band = wiphy->bands[NL80211_BAND_5GHZ];
7208b4c3e9b5SBjoern A. Zeeb 	if (band)
7209b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < band->n_channels; i++)
7210b4c3e9b5SBjoern A. Zeeb 			band->channels[i].flags = IEEE80211_CHAN_DISABLED;
7211b4c3e9b5SBjoern A. Zeeb 
7212b4c3e9b5SBjoern A. Zeeb 	total = le32_to_cpu(list->count);
7213b4c3e9b5SBjoern A. Zeeb 	if (total > BRCMF_MAX_CHANSPEC_LIST) {
7214b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
7215b4c3e9b5SBjoern A. Zeeb 			 total);
7216b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
7217b4c3e9b5SBjoern A. Zeeb 		goto fail_pbuf;
7218b4c3e9b5SBjoern A. Zeeb 	}
7219b4c3e9b5SBjoern A. Zeeb 
7220b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < total; i++) {
7221b4c3e9b5SBjoern A. Zeeb 		ch.chspec = (u16)le32_to_cpu(list->element[i]);
7222b4c3e9b5SBjoern A. Zeeb 		cfg->d11inf.decchspec(&ch);
7223b4c3e9b5SBjoern A. Zeeb 
7224b4c3e9b5SBjoern A. Zeeb 		if (ch.band == BRCMU_CHAN_BAND_2G) {
7225b4c3e9b5SBjoern A. Zeeb 			band = wiphy->bands[NL80211_BAND_2GHZ];
7226b4c3e9b5SBjoern A. Zeeb 		} else if (ch.band == BRCMU_CHAN_BAND_5G) {
7227b4c3e9b5SBjoern A. Zeeb 			band = wiphy->bands[NL80211_BAND_5GHZ];
7228b4c3e9b5SBjoern A. Zeeb 		} else {
7229b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Invalid channel Spec. 0x%x.\n",
7230b4c3e9b5SBjoern A. Zeeb 				 ch.chspec);
7231b4c3e9b5SBjoern A. Zeeb 			continue;
7232b4c3e9b5SBjoern A. Zeeb 		}
7233b4c3e9b5SBjoern A. Zeeb 		if (!band)
7234b4c3e9b5SBjoern A. Zeeb 			continue;
7235b4c3e9b5SBjoern A. Zeeb 		if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
7236b4c3e9b5SBjoern A. Zeeb 		    ch.bw == BRCMU_CHAN_BW_40)
7237b4c3e9b5SBjoern A. Zeeb 			continue;
7238b4c3e9b5SBjoern A. Zeeb 		if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
7239b4c3e9b5SBjoern A. Zeeb 		    ch.bw == BRCMU_CHAN_BW_80)
7240b4c3e9b5SBjoern A. Zeeb 			continue;
7241b4c3e9b5SBjoern A. Zeeb 
7242b4c3e9b5SBjoern A. Zeeb 		channel = NULL;
7243b4c3e9b5SBjoern A. Zeeb 		for (j = 0; j < band->n_channels; j++) {
7244b4c3e9b5SBjoern A. Zeeb 			if (band->channels[j].hw_value == ch.control_ch_num) {
7245b4c3e9b5SBjoern A. Zeeb 				channel = &band->channels[j];
7246b4c3e9b5SBjoern A. Zeeb 				break;
7247b4c3e9b5SBjoern A. Zeeb 			}
7248b4c3e9b5SBjoern A. Zeeb 		}
7249b4c3e9b5SBjoern A. Zeeb 		if (!channel) {
7250b4c3e9b5SBjoern A. Zeeb 			/* It seems firmware supports some channel we never
7251b4c3e9b5SBjoern A. Zeeb 			 * considered. Something new in IEEE standard?
7252b4c3e9b5SBjoern A. Zeeb 			 */
7253b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Ignoring unexpected firmware channel %d\n",
7254b4c3e9b5SBjoern A. Zeeb 				 ch.control_ch_num);
7255b4c3e9b5SBjoern A. Zeeb 			continue;
7256b4c3e9b5SBjoern A. Zeeb 		}
7257b4c3e9b5SBjoern A. Zeeb 
7258b4c3e9b5SBjoern A. Zeeb 		if (channel->orig_flags & IEEE80211_CHAN_DISABLED)
7259b4c3e9b5SBjoern A. Zeeb 			continue;
7260b4c3e9b5SBjoern A. Zeeb 
7261b4c3e9b5SBjoern A. Zeeb 		/* assuming the chanspecs order is HT20,
7262b4c3e9b5SBjoern A. Zeeb 		 * HT40 upper, HT40 lower, and VHT80.
7263b4c3e9b5SBjoern A. Zeeb 		 */
7264b4c3e9b5SBjoern A. Zeeb 		switch (ch.bw) {
7265b4c3e9b5SBjoern A. Zeeb 		case BRCMU_CHAN_BW_160:
7266b4c3e9b5SBjoern A. Zeeb 			channel->flags &= ~IEEE80211_CHAN_NO_160MHZ;
7267b4c3e9b5SBjoern A. Zeeb 			break;
7268b4c3e9b5SBjoern A. Zeeb 		case BRCMU_CHAN_BW_80:
7269b4c3e9b5SBjoern A. Zeeb 			channel->flags &= ~IEEE80211_CHAN_NO_80MHZ;
7270b4c3e9b5SBjoern A. Zeeb 			break;
7271b4c3e9b5SBjoern A. Zeeb 		case BRCMU_CHAN_BW_40:
7272b4c3e9b5SBjoern A. Zeeb 			brcmf_update_bw40_channel_flag(channel, &ch);
7273b4c3e9b5SBjoern A. Zeeb 			break;
7274b4c3e9b5SBjoern A. Zeeb 		default:
7275b4c3e9b5SBjoern A. Zeeb 			wiphy_warn(wiphy, "Firmware reported unsupported bandwidth %d\n",
7276b4c3e9b5SBjoern A. Zeeb 				   ch.bw);
7277b4c3e9b5SBjoern A. Zeeb 			fallthrough;
7278b4c3e9b5SBjoern A. Zeeb 		case BRCMU_CHAN_BW_20:
7279b4c3e9b5SBjoern A. Zeeb 			/* enable the channel and disable other bandwidths
7280b4c3e9b5SBjoern A. Zeeb 			 * for now as mentioned order assure they are enabled
7281b4c3e9b5SBjoern A. Zeeb 			 * for subsequent chanspecs.
7282b4c3e9b5SBjoern A. Zeeb 			 */
7283b4c3e9b5SBjoern A. Zeeb 			channel->flags = IEEE80211_CHAN_NO_HT40 |
7284b4c3e9b5SBjoern A. Zeeb 					 IEEE80211_CHAN_NO_80MHZ |
7285b4c3e9b5SBjoern A. Zeeb 					 IEEE80211_CHAN_NO_160MHZ;
7286b4c3e9b5SBjoern A. Zeeb 			ch.bw = BRCMU_CHAN_BW_20;
7287b4c3e9b5SBjoern A. Zeeb 			cfg->d11inf.encchspec(&ch);
7288b4c3e9b5SBjoern A. Zeeb 			chaninfo = ch.chspec;
7289b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_bsscfg_int_query(ifp, "per_chan_info",
7290b4c3e9b5SBjoern A. Zeeb 							 &chaninfo);
7291b4c3e9b5SBjoern A. Zeeb 			if (!err) {
7292b4c3e9b5SBjoern A. Zeeb 				if (chaninfo & WL_CHAN_RADAR)
7293b4c3e9b5SBjoern A. Zeeb 					channel->flags |=
7294b4c3e9b5SBjoern A. Zeeb 						(IEEE80211_CHAN_RADAR |
7295b4c3e9b5SBjoern A. Zeeb 						 IEEE80211_CHAN_NO_IR);
7296b4c3e9b5SBjoern A. Zeeb 				if (chaninfo & WL_CHAN_PASSIVE)
7297b4c3e9b5SBjoern A. Zeeb 					channel->flags |=
7298b4c3e9b5SBjoern A. Zeeb 						IEEE80211_CHAN_NO_IR;
7299b4c3e9b5SBjoern A. Zeeb 			}
7300b4c3e9b5SBjoern A. Zeeb 		}
7301b4c3e9b5SBjoern A. Zeeb 	}
7302b4c3e9b5SBjoern A. Zeeb 
7303b4c3e9b5SBjoern A. Zeeb fail_pbuf:
7304b4c3e9b5SBjoern A. Zeeb 	kfree(pbuf);
7305b4c3e9b5SBjoern A. Zeeb 	return err;
7306b4c3e9b5SBjoern A. Zeeb }
7307b4c3e9b5SBjoern A. Zeeb 
brcmf_enable_bw40_2g(struct brcmf_cfg80211_info * cfg)7308b4c3e9b5SBjoern A. Zeeb static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
7309b4c3e9b5SBjoern A. Zeeb {
7310b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
7311b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
7312b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
7313b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fil_bwcap_le band_bwcap;
7314b4c3e9b5SBjoern A. Zeeb 	struct brcmf_chanspec_list *list;
7315b4c3e9b5SBjoern A. Zeeb 	u8 *pbuf;
7316b4c3e9b5SBjoern A. Zeeb 	u32 val;
7317b4c3e9b5SBjoern A. Zeeb 	int err;
7318b4c3e9b5SBjoern A. Zeeb 	struct brcmu_chan ch;
7319b4c3e9b5SBjoern A. Zeeb 	u32 num_chan;
7320b4c3e9b5SBjoern A. Zeeb 	int i, j;
7321b4c3e9b5SBjoern A. Zeeb 
7322b4c3e9b5SBjoern A. Zeeb 	/* verify support for bw_cap command */
7323b4c3e9b5SBjoern A. Zeeb 	val = WLC_BAND_5G;
7324b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &val);
7325b4c3e9b5SBjoern A. Zeeb 
7326b4c3e9b5SBjoern A. Zeeb 	if (!err) {
7327b4c3e9b5SBjoern A. Zeeb 		/* only set 2G bandwidth using bw_cap command */
7328b4c3e9b5SBjoern A. Zeeb 		band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
7329b4c3e9b5SBjoern A. Zeeb 		band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
7330b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
7331b4c3e9b5SBjoern A. Zeeb 					       sizeof(band_bwcap));
7332b4c3e9b5SBjoern A. Zeeb 	} else {
7333b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
7334b4c3e9b5SBjoern A. Zeeb 		val = WLC_N_BW_40ALL;
7335b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
7336b4c3e9b5SBjoern A. Zeeb 	}
7337b4c3e9b5SBjoern A. Zeeb 
7338b4c3e9b5SBjoern A. Zeeb 	if (!err) {
7339b4c3e9b5SBjoern A. Zeeb 		/* update channel info in 2G band */
7340b4c3e9b5SBjoern A. Zeeb 		pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
7341b4c3e9b5SBjoern A. Zeeb 
7342b4c3e9b5SBjoern A. Zeeb 		if (pbuf == NULL)
7343b4c3e9b5SBjoern A. Zeeb 			return -ENOMEM;
7344b4c3e9b5SBjoern A. Zeeb 
7345b4c3e9b5SBjoern A. Zeeb 		ch.band = BRCMU_CHAN_BAND_2G;
7346b4c3e9b5SBjoern A. Zeeb 		ch.bw = BRCMU_CHAN_BW_40;
7347b4c3e9b5SBjoern A. Zeeb 		ch.sb = BRCMU_CHAN_SB_NONE;
7348b4c3e9b5SBjoern A. Zeeb 		ch.chnum = 0;
7349b4c3e9b5SBjoern A. Zeeb 		cfg->d11inf.encchspec(&ch);
7350b4c3e9b5SBjoern A. Zeeb 
7351b4c3e9b5SBjoern A. Zeeb 		/* pass encoded chanspec in query */
7352b4c3e9b5SBjoern A. Zeeb 		*(__le16 *)pbuf = cpu_to_le16(ch.chspec);
7353b4c3e9b5SBjoern A. Zeeb 
7354b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
7355b4c3e9b5SBjoern A. Zeeb 					       BRCMF_DCMD_MEDLEN);
7356b4c3e9b5SBjoern A. Zeeb 		if (err) {
7357b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "get chanspecs error (%d)\n", err);
7358b4c3e9b5SBjoern A. Zeeb 			kfree(pbuf);
7359b4c3e9b5SBjoern A. Zeeb 			return err;
7360b4c3e9b5SBjoern A. Zeeb 		}
7361b4c3e9b5SBjoern A. Zeeb 
7362b4c3e9b5SBjoern A. Zeeb 		band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
7363b4c3e9b5SBjoern A. Zeeb 		list = (struct brcmf_chanspec_list *)pbuf;
7364b4c3e9b5SBjoern A. Zeeb 		num_chan = le32_to_cpu(list->count);
7365b4c3e9b5SBjoern A. Zeeb 		if (num_chan > BRCMF_MAX_CHANSPEC_LIST) {
7366b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
7367b4c3e9b5SBjoern A. Zeeb 				 num_chan);
7368b4c3e9b5SBjoern A. Zeeb 			kfree(pbuf);
7369b4c3e9b5SBjoern A. Zeeb 			return -EINVAL;
7370b4c3e9b5SBjoern A. Zeeb 		}
7371b4c3e9b5SBjoern A. Zeeb 
7372b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < num_chan; i++) {
7373b4c3e9b5SBjoern A. Zeeb 			ch.chspec = (u16)le32_to_cpu(list->element[i]);
7374b4c3e9b5SBjoern A. Zeeb 			cfg->d11inf.decchspec(&ch);
7375b4c3e9b5SBjoern A. Zeeb 			if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
7376b4c3e9b5SBjoern A. Zeeb 				continue;
7377b4c3e9b5SBjoern A. Zeeb 			if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
7378b4c3e9b5SBjoern A. Zeeb 				continue;
7379b4c3e9b5SBjoern A. Zeeb 			for (j = 0; j < band->n_channels; j++) {
7380b4c3e9b5SBjoern A. Zeeb 				if (band->channels[j].hw_value == ch.control_ch_num)
7381b4c3e9b5SBjoern A. Zeeb 					break;
7382b4c3e9b5SBjoern A. Zeeb 			}
7383b4c3e9b5SBjoern A. Zeeb 			if (WARN_ON(j == band->n_channels))
7384b4c3e9b5SBjoern A. Zeeb 				continue;
7385b4c3e9b5SBjoern A. Zeeb 
7386b4c3e9b5SBjoern A. Zeeb 			brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
7387b4c3e9b5SBjoern A. Zeeb 		}
7388b4c3e9b5SBjoern A. Zeeb 		kfree(pbuf);
7389b4c3e9b5SBjoern A. Zeeb 	}
7390b4c3e9b5SBjoern A. Zeeb 	return err;
7391b4c3e9b5SBjoern A. Zeeb }
7392b4c3e9b5SBjoern A. Zeeb 
brcmf_get_bwcap(struct brcmf_if * ifp,u32 bw_cap[])7393b4c3e9b5SBjoern A. Zeeb static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
7394b4c3e9b5SBjoern A. Zeeb {
7395b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
7396b4c3e9b5SBjoern A. Zeeb 	u32 band, mimo_bwcap;
7397b4c3e9b5SBjoern A. Zeeb 	int err;
7398b4c3e9b5SBjoern A. Zeeb 
7399b4c3e9b5SBjoern A. Zeeb 	band = WLC_BAND_2G;
7400b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
7401b4c3e9b5SBjoern A. Zeeb 	if (!err) {
7402b4c3e9b5SBjoern A. Zeeb 		bw_cap[NL80211_BAND_2GHZ] = band;
7403b4c3e9b5SBjoern A. Zeeb 		band = WLC_BAND_5G;
7404b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
7405b4c3e9b5SBjoern A. Zeeb 		if (!err) {
7406b4c3e9b5SBjoern A. Zeeb 			bw_cap[NL80211_BAND_5GHZ] = band;
7407b4c3e9b5SBjoern A. Zeeb 			return;
7408b4c3e9b5SBjoern A. Zeeb 		}
7409b4c3e9b5SBjoern A. Zeeb 		WARN_ON(1);
7410b4c3e9b5SBjoern A. Zeeb 		return;
7411b4c3e9b5SBjoern A. Zeeb 	}
7412b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
7413b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
7414b4c3e9b5SBjoern A. Zeeb 	if (err)
7415b4c3e9b5SBjoern A. Zeeb 		/* assume 20MHz if firmware does not give a clue */
7416b4c3e9b5SBjoern A. Zeeb 		mimo_bwcap = WLC_N_BW_20ALL;
7417b4c3e9b5SBjoern A. Zeeb 
7418b4c3e9b5SBjoern A. Zeeb 	switch (mimo_bwcap) {
7419b4c3e9b5SBjoern A. Zeeb 	case WLC_N_BW_40ALL:
7420b4c3e9b5SBjoern A. Zeeb 		bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
7421b4c3e9b5SBjoern A. Zeeb 		fallthrough;
7422b4c3e9b5SBjoern A. Zeeb 	case WLC_N_BW_20IN2G_40IN5G:
7423b4c3e9b5SBjoern A. Zeeb 		bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
7424b4c3e9b5SBjoern A. Zeeb 		fallthrough;
7425b4c3e9b5SBjoern A. Zeeb 	case WLC_N_BW_20ALL:
7426b4c3e9b5SBjoern A. Zeeb 		bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
7427b4c3e9b5SBjoern A. Zeeb 		bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
7428b4c3e9b5SBjoern A. Zeeb 		break;
7429b4c3e9b5SBjoern A. Zeeb 	default:
7430b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "invalid mimo_bw_cap value\n");
7431b4c3e9b5SBjoern A. Zeeb 	}
7432b4c3e9b5SBjoern A. Zeeb }
7433b4c3e9b5SBjoern A. Zeeb 
brcmf_update_ht_cap(struct ieee80211_supported_band * band,u32 bw_cap[2],u32 nchain)7434b4c3e9b5SBjoern A. Zeeb static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
7435b4c3e9b5SBjoern A. Zeeb 				u32 bw_cap[2], u32 nchain)
7436b4c3e9b5SBjoern A. Zeeb {
7437b4c3e9b5SBjoern A. Zeeb 	band->ht_cap.ht_supported = true;
7438b4c3e9b5SBjoern A. Zeeb 	if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
7439b4c3e9b5SBjoern A. Zeeb 		band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
7440b4c3e9b5SBjoern A. Zeeb 		band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
7441b4c3e9b5SBjoern A. Zeeb 	}
7442b4c3e9b5SBjoern A. Zeeb 	band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
7443b4c3e9b5SBjoern A. Zeeb 	band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
7444b4c3e9b5SBjoern A. Zeeb 	band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
7445b4c3e9b5SBjoern A. Zeeb 	band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
7446b4c3e9b5SBjoern A. Zeeb 	memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
7447b4c3e9b5SBjoern A. Zeeb 	band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
7448b4c3e9b5SBjoern A. Zeeb }
7449b4c3e9b5SBjoern A. Zeeb 
brcmf_get_mcs_map(u32 nchain,enum ieee80211_vht_mcs_support supp)7450b4c3e9b5SBjoern A. Zeeb static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
7451b4c3e9b5SBjoern A. Zeeb {
7452b4c3e9b5SBjoern A. Zeeb 	u16 mcs_map;
7453b4c3e9b5SBjoern A. Zeeb 	int i;
7454b4c3e9b5SBjoern A. Zeeb 
7455b4c3e9b5SBjoern A. Zeeb 	for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
7456b4c3e9b5SBjoern A. Zeeb 		mcs_map = (mcs_map << 2) | supp;
7457b4c3e9b5SBjoern A. Zeeb 
7458b4c3e9b5SBjoern A. Zeeb 	return cpu_to_le16(mcs_map);
7459b4c3e9b5SBjoern A. Zeeb }
7460b4c3e9b5SBjoern A. Zeeb 
brcmf_update_vht_cap(struct ieee80211_supported_band * band,u32 bw_cap[2],u32 nchain,u32 txstreams,u32 txbf_bfe_cap,u32 txbf_bfr_cap)7461b4c3e9b5SBjoern A. Zeeb static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
7462b4c3e9b5SBjoern A. Zeeb 				 u32 bw_cap[2], u32 nchain, u32 txstreams,
7463b4c3e9b5SBjoern A. Zeeb 				 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
7464b4c3e9b5SBjoern A. Zeeb {
7465b4c3e9b5SBjoern A. Zeeb 	__le16 mcs_map;
7466b4c3e9b5SBjoern A. Zeeb 
7467b4c3e9b5SBjoern A. Zeeb 	/* not allowed in 2.4G band */
7468b4c3e9b5SBjoern A. Zeeb 	if (band->band == NL80211_BAND_2GHZ)
7469b4c3e9b5SBjoern A. Zeeb 		return;
7470b4c3e9b5SBjoern A. Zeeb 
7471b4c3e9b5SBjoern A. Zeeb 	band->vht_cap.vht_supported = true;
7472b4c3e9b5SBjoern A. Zeeb 	/* 80MHz is mandatory */
7473b4c3e9b5SBjoern A. Zeeb 	band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
7474b4c3e9b5SBjoern A. Zeeb 	if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
7475b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
7476b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
7477b4c3e9b5SBjoern A. Zeeb 	}
7478b4c3e9b5SBjoern A. Zeeb 	/* all support 256-QAM */
7479b4c3e9b5SBjoern A. Zeeb 	mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
7480b4c3e9b5SBjoern A. Zeeb 	band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
7481b4c3e9b5SBjoern A. Zeeb 	band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
7482b4c3e9b5SBjoern A. Zeeb 
7483b4c3e9b5SBjoern A. Zeeb 	/* Beamforming support information */
7484b4c3e9b5SBjoern A. Zeeb 	if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
7485b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
7486b4c3e9b5SBjoern A. Zeeb 	if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
7487b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
7488b4c3e9b5SBjoern A. Zeeb 	if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
7489b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
7490b4c3e9b5SBjoern A. Zeeb 	if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
7491b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
7492b4c3e9b5SBjoern A. Zeeb 
7493b4c3e9b5SBjoern A. Zeeb 	if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
7494b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |=
7495b4c3e9b5SBjoern A. Zeeb 			(2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
7496b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |= ((txstreams - 1) <<
7497b4c3e9b5SBjoern A. Zeeb 				IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
7498b4c3e9b5SBjoern A. Zeeb 		band->vht_cap.cap |=
7499b4c3e9b5SBjoern A. Zeeb 			IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
7500b4c3e9b5SBjoern A. Zeeb 	}
7501b4c3e9b5SBjoern A. Zeeb }
7502b4c3e9b5SBjoern A. Zeeb 
brcmf_setup_wiphybands(struct brcmf_cfg80211_info * cfg)7503b4c3e9b5SBjoern A. Zeeb static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
7504b4c3e9b5SBjoern A. Zeeb {
7505b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
7506b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
7507b4c3e9b5SBjoern A. Zeeb 	struct wiphy *wiphy = cfg_to_wiphy(cfg);
7508b4c3e9b5SBjoern A. Zeeb 	u32 nmode;
7509b4c3e9b5SBjoern A. Zeeb 	u32 vhtmode = 0;
7510b4c3e9b5SBjoern A. Zeeb 	u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
7511b4c3e9b5SBjoern A. Zeeb 	u32 rxchain;
7512b4c3e9b5SBjoern A. Zeeb 	u32 nchain;
7513b4c3e9b5SBjoern A. Zeeb 	int err;
7514b4c3e9b5SBjoern A. Zeeb 	s32 i;
7515b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
7516b4c3e9b5SBjoern A. Zeeb 	u32 txstreams = 0;
7517b4c3e9b5SBjoern A. Zeeb 	u32 txbf_bfe_cap = 0;
7518b4c3e9b5SBjoern A. Zeeb 	u32 txbf_bfr_cap = 0;
7519b4c3e9b5SBjoern A. Zeeb 
7520b4c3e9b5SBjoern A. Zeeb 	(void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
7521b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
7522b4c3e9b5SBjoern A. Zeeb 	if (err) {
7523b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "nmode error (%d)\n", err);
7524b4c3e9b5SBjoern A. Zeeb 	} else {
7525b4c3e9b5SBjoern A. Zeeb 		brcmf_get_bwcap(ifp, bw_cap);
7526b4c3e9b5SBjoern A. Zeeb 	}
7527b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
7528b4c3e9b5SBjoern A. Zeeb 		  nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
7529b4c3e9b5SBjoern A. Zeeb 		  bw_cap[NL80211_BAND_5GHZ]);
7530b4c3e9b5SBjoern A. Zeeb 
7531b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
7532b4c3e9b5SBjoern A. Zeeb 	if (err) {
7533b4c3e9b5SBjoern A. Zeeb 		/* rxchain unsupported by firmware of older chips */
7534b4c3e9b5SBjoern A. Zeeb 		if (err == -EBADE)
7535b4c3e9b5SBjoern A. Zeeb 			bphy_info_once(drvr, "rxchain unsupported\n");
7536b4c3e9b5SBjoern A. Zeeb 		else
7537b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "rxchain error (%d)\n", err);
7538b4c3e9b5SBjoern A. Zeeb 
7539b4c3e9b5SBjoern A. Zeeb 		nchain = 1;
7540b4c3e9b5SBjoern A. Zeeb 	} else {
7541b4c3e9b5SBjoern A. Zeeb 		for (nchain = 0; rxchain; nchain++)
7542b4c3e9b5SBjoern A. Zeeb 			rxchain = rxchain & (rxchain - 1);
7543b4c3e9b5SBjoern A. Zeeb 	}
7544b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "nchain=%d\n", nchain);
7545b4c3e9b5SBjoern A. Zeeb 
7546b4c3e9b5SBjoern A. Zeeb 	err = brcmf_construct_chaninfo(cfg, bw_cap);
7547b4c3e9b5SBjoern A. Zeeb 	if (err) {
7548b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "brcmf_construct_chaninfo failed (%d)\n", err);
7549b4c3e9b5SBjoern A. Zeeb 		return err;
7550b4c3e9b5SBjoern A. Zeeb 	}
7551b4c3e9b5SBjoern A. Zeeb 
7552b4c3e9b5SBjoern A. Zeeb 	if (vhtmode) {
7553b4c3e9b5SBjoern A. Zeeb 		(void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
7554b4c3e9b5SBjoern A. Zeeb 		(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
7555b4c3e9b5SBjoern A. Zeeb 					      &txbf_bfe_cap);
7556b4c3e9b5SBjoern A. Zeeb 		(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
7557b4c3e9b5SBjoern A. Zeeb 					      &txbf_bfr_cap);
7558b4c3e9b5SBjoern A. Zeeb 	}
7559b4c3e9b5SBjoern A. Zeeb 
7560b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
7561b4c3e9b5SBjoern A. Zeeb 		band = wiphy->bands[i];
7562b4c3e9b5SBjoern A. Zeeb 		if (band == NULL)
7563b4c3e9b5SBjoern A. Zeeb 			continue;
7564b4c3e9b5SBjoern A. Zeeb 
7565b4c3e9b5SBjoern A. Zeeb 		if (nmode)
7566b4c3e9b5SBjoern A. Zeeb 			brcmf_update_ht_cap(band, bw_cap, nchain);
7567b4c3e9b5SBjoern A. Zeeb 		if (vhtmode)
7568b4c3e9b5SBjoern A. Zeeb 			brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
7569b4c3e9b5SBjoern A. Zeeb 					     txbf_bfe_cap, txbf_bfr_cap);
7570b4c3e9b5SBjoern A. Zeeb 	}
7571b4c3e9b5SBjoern A. Zeeb 
7572b4c3e9b5SBjoern A. Zeeb 	return 0;
7573b4c3e9b5SBjoern A. Zeeb }
7574b4c3e9b5SBjoern A. Zeeb 
7575b4c3e9b5SBjoern A. Zeeb static const struct ieee80211_txrx_stypes
7576b4c3e9b5SBjoern A. Zeeb brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
7577b4c3e9b5SBjoern A. Zeeb 	[NL80211_IFTYPE_STATION] = {
7578b4c3e9b5SBjoern A. Zeeb 		.tx = 0xffff,
7579b4c3e9b5SBjoern A. Zeeb 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
7580b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_AUTH >> 4) |
7581b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
7582b4c3e9b5SBjoern A. Zeeb 	},
7583b4c3e9b5SBjoern A. Zeeb 	[NL80211_IFTYPE_P2P_CLIENT] = {
7584b4c3e9b5SBjoern A. Zeeb 		.tx = 0xffff,
7585b4c3e9b5SBjoern A. Zeeb 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
7586b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
7587b4c3e9b5SBjoern A. Zeeb 	},
7588b4c3e9b5SBjoern A. Zeeb 	[NL80211_IFTYPE_P2P_GO] = {
7589b4c3e9b5SBjoern A. Zeeb 		.tx = 0xffff,
7590b4c3e9b5SBjoern A. Zeeb 		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
7591b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
7592b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
7593b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_DISASSOC >> 4) |
7594b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_AUTH >> 4) |
7595b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_DEAUTH >> 4) |
7596b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_ACTION >> 4)
7597b4c3e9b5SBjoern A. Zeeb 	},
7598b4c3e9b5SBjoern A. Zeeb 	[NL80211_IFTYPE_P2P_DEVICE] = {
7599b4c3e9b5SBjoern A. Zeeb 		.tx = 0xffff,
7600b4c3e9b5SBjoern A. Zeeb 		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
7601b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
7602b4c3e9b5SBjoern A. Zeeb 	},
7603b4c3e9b5SBjoern A. Zeeb 	[NL80211_IFTYPE_AP] = {
7604b4c3e9b5SBjoern A. Zeeb 		.tx = 0xffff,
7605b4c3e9b5SBjoern A. Zeeb 		.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
7606b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
7607b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
7608b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_DISASSOC >> 4) |
7609b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_AUTH >> 4) |
7610b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_DEAUTH >> 4) |
7611b4c3e9b5SBjoern A. Zeeb 		      BIT(IEEE80211_STYPE_ACTION >> 4)
7612b4c3e9b5SBjoern A. Zeeb 	}
7613b4c3e9b5SBjoern A. Zeeb };
7614b4c3e9b5SBjoern A. Zeeb 
7615b4c3e9b5SBjoern A. Zeeb /**
7616b4c3e9b5SBjoern A. Zeeb  * brcmf_setup_ifmodes() - determine interface modes and combinations.
7617b4c3e9b5SBjoern A. Zeeb  *
7618b4c3e9b5SBjoern A. Zeeb  * @wiphy: wiphy object.
7619b4c3e9b5SBjoern A. Zeeb  * @ifp: interface object needed for feat module api.
7620b4c3e9b5SBjoern A. Zeeb  *
7621b4c3e9b5SBjoern A. Zeeb  * The interface modes and combinations are determined dynamically here
7622b4c3e9b5SBjoern A. Zeeb  * based on firmware functionality.
7623b4c3e9b5SBjoern A. Zeeb  *
7624b4c3e9b5SBjoern A. Zeeb  * no p2p and no mbss:
7625b4c3e9b5SBjoern A. Zeeb  *
7626b4c3e9b5SBjoern A. Zeeb  *	#STA <= 1, #AP <= 1, channels = 1, 2 total
7627b4c3e9b5SBjoern A. Zeeb  *
7628b4c3e9b5SBjoern A. Zeeb  * no p2p and mbss:
7629b4c3e9b5SBjoern A. Zeeb  *
7630b4c3e9b5SBjoern A. Zeeb  *	#STA <= 1, #AP <= 1, channels = 1, 2 total
7631b4c3e9b5SBjoern A. Zeeb  *	#AP <= 4, matching BI, channels = 1, 4 total
7632b4c3e9b5SBjoern A. Zeeb  *
7633b4c3e9b5SBjoern A. Zeeb  * no p2p and rsdb:
7634b4c3e9b5SBjoern A. Zeeb  *	#STA <= 1, #AP <= 2, channels = 2, 4 total
7635b4c3e9b5SBjoern A. Zeeb  *
7636b4c3e9b5SBjoern A. Zeeb  * p2p, no mchan, and mbss:
7637b4c3e9b5SBjoern A. Zeeb  *
7638b4c3e9b5SBjoern A. Zeeb  *	#STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
7639b4c3e9b5SBjoern A. Zeeb  *	#STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
7640b4c3e9b5SBjoern A. Zeeb  *	#AP <= 4, matching BI, channels = 1, 4 total
7641b4c3e9b5SBjoern A. Zeeb  *
7642b4c3e9b5SBjoern A. Zeeb  * p2p, mchan, and mbss:
7643b4c3e9b5SBjoern A. Zeeb  *
7644b4c3e9b5SBjoern A. Zeeb  *	#STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
7645b4c3e9b5SBjoern A. Zeeb  *	#STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
7646b4c3e9b5SBjoern A. Zeeb  *	#AP <= 4, matching BI, channels = 1, 4 total
7647b4c3e9b5SBjoern A. Zeeb  *
7648b4c3e9b5SBjoern A. Zeeb  * p2p, rsdb, and no mbss:
7649b4c3e9b5SBjoern A. Zeeb  *	#STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,
7650b4c3e9b5SBjoern A. Zeeb  *	 channels = 2, 4 total
7651b4c3e9b5SBjoern A. Zeeb  *
7652b4c3e9b5SBjoern A. Zeeb  * Return: 0 on success, negative errno on failure
7653b4c3e9b5SBjoern A. Zeeb  */
brcmf_setup_ifmodes(struct wiphy * wiphy,struct brcmf_if * ifp)7654b4c3e9b5SBjoern A. Zeeb static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
7655b4c3e9b5SBjoern A. Zeeb {
7656b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_iface_combination *combo = NULL;
7657b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_iface_limit *c0_limits = NULL;
7658b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_iface_limit *p2p_limits = NULL;
7659b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_iface_limit *mbss_limits = NULL;
7660b4c3e9b5SBjoern A. Zeeb 	bool mon_flag, mbss, p2p, rsdb, mchan;
7661b4c3e9b5SBjoern A. Zeeb 	int i, c, n_combos, n_limits;
7662b4c3e9b5SBjoern A. Zeeb 
7663b4c3e9b5SBjoern A. Zeeb 	mon_flag = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FLAG);
7664b4c3e9b5SBjoern A. Zeeb 	mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
7665b4c3e9b5SBjoern A. Zeeb 	p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
7666b4c3e9b5SBjoern A. Zeeb 	rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB);
7667b4c3e9b5SBjoern A. Zeeb 	mchan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN);
7668b4c3e9b5SBjoern A. Zeeb 
7669b4c3e9b5SBjoern A. Zeeb 	n_combos = 1 + !!(p2p && !rsdb) + !!mbss;
7670b4c3e9b5SBjoern A. Zeeb 	combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
7671b4c3e9b5SBjoern A. Zeeb 	if (!combo)
7672b4c3e9b5SBjoern A. Zeeb 		goto err;
7673b4c3e9b5SBjoern A. Zeeb 
7674b4c3e9b5SBjoern A. Zeeb 	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
7675b4c3e9b5SBjoern A. Zeeb 				 BIT(NL80211_IFTYPE_ADHOC) |
7676b4c3e9b5SBjoern A. Zeeb 				 BIT(NL80211_IFTYPE_AP);
7677b4c3e9b5SBjoern A. Zeeb 	if (mon_flag)
7678b4c3e9b5SBjoern A. Zeeb 		wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
7679b4c3e9b5SBjoern A. Zeeb 	if (p2p)
7680b4c3e9b5SBjoern A. Zeeb 		wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
7681b4c3e9b5SBjoern A. Zeeb 					  BIT(NL80211_IFTYPE_P2P_GO) |
7682b4c3e9b5SBjoern A. Zeeb 					  BIT(NL80211_IFTYPE_P2P_DEVICE);
7683b4c3e9b5SBjoern A. Zeeb 
7684b4c3e9b5SBjoern A. Zeeb 	c = 0;
7685b4c3e9b5SBjoern A. Zeeb 	i = 0;
7686b4c3e9b5SBjoern A. Zeeb 	n_limits = 1 + mon_flag + (p2p ? 2 : 0) + (rsdb || !p2p);
7687b4c3e9b5SBjoern A. Zeeb 	c0_limits = kcalloc(n_limits, sizeof(*c0_limits), GFP_KERNEL);
7688b4c3e9b5SBjoern A. Zeeb 	if (!c0_limits)
7689b4c3e9b5SBjoern A. Zeeb 		goto err;
7690b4c3e9b5SBjoern A. Zeeb 
7691b4c3e9b5SBjoern A. Zeeb 	combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan));
7692b4c3e9b5SBjoern A. Zeeb 	c0_limits[i].max = 1 + (p2p && mchan);
7693b4c3e9b5SBjoern A. Zeeb 	c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
7694b4c3e9b5SBjoern A. Zeeb 	if (mon_flag) {
7695b4c3e9b5SBjoern A. Zeeb 		c0_limits[i].max = 1;
7696b4c3e9b5SBjoern A. Zeeb 		c0_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
7697b4c3e9b5SBjoern A. Zeeb 	}
7698b4c3e9b5SBjoern A. Zeeb 	if (p2p) {
7699b4c3e9b5SBjoern A. Zeeb 		c0_limits[i].max = 1;
7700b4c3e9b5SBjoern A. Zeeb 		c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
7701b4c3e9b5SBjoern A. Zeeb 		c0_limits[i].max = 1 + rsdb;
7702b4c3e9b5SBjoern A. Zeeb 		c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
7703b4c3e9b5SBjoern A. Zeeb 				       BIT(NL80211_IFTYPE_P2P_GO);
7704b4c3e9b5SBjoern A. Zeeb 	}
7705b4c3e9b5SBjoern A. Zeeb 	if (p2p && rsdb) {
7706b4c3e9b5SBjoern A. Zeeb 		c0_limits[i].max = 2;
7707b4c3e9b5SBjoern A. Zeeb 		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7708b4c3e9b5SBjoern A. Zeeb 		combo[c].max_interfaces = 4;
7709b4c3e9b5SBjoern A. Zeeb 	} else if (p2p) {
7710b4c3e9b5SBjoern A. Zeeb 		combo[c].max_interfaces = i;
7711b4c3e9b5SBjoern A. Zeeb 	} else if (rsdb) {
7712b4c3e9b5SBjoern A. Zeeb 		c0_limits[i].max = 2;
7713b4c3e9b5SBjoern A. Zeeb 		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7714b4c3e9b5SBjoern A. Zeeb 		combo[c].max_interfaces = 3;
7715b4c3e9b5SBjoern A. Zeeb 	} else {
7716b4c3e9b5SBjoern A. Zeeb 		c0_limits[i].max = 1;
7717b4c3e9b5SBjoern A. Zeeb 		c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7718b4c3e9b5SBjoern A. Zeeb 		combo[c].max_interfaces = i;
7719b4c3e9b5SBjoern A. Zeeb 	}
7720b4c3e9b5SBjoern A. Zeeb 	combo[c].n_limits = i;
7721b4c3e9b5SBjoern A. Zeeb 	combo[c].limits = c0_limits;
7722b4c3e9b5SBjoern A. Zeeb 
7723b4c3e9b5SBjoern A. Zeeb 	if (p2p && !rsdb) {
7724b4c3e9b5SBjoern A. Zeeb 		c++;
7725b4c3e9b5SBjoern A. Zeeb 		i = 0;
7726b4c3e9b5SBjoern A. Zeeb 		p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
7727b4c3e9b5SBjoern A. Zeeb 		if (!p2p_limits)
7728b4c3e9b5SBjoern A. Zeeb 			goto err;
7729b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i].max = 1;
7730b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
7731b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i].max = 1;
7732b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7733b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i].max = 1;
7734b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
7735b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i].max = 1;
7736b4c3e9b5SBjoern A. Zeeb 		p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
7737b4c3e9b5SBjoern A. Zeeb 		combo[c].num_different_channels = 1;
7738b4c3e9b5SBjoern A. Zeeb 		combo[c].max_interfaces = i;
7739b4c3e9b5SBjoern A. Zeeb 		combo[c].n_limits = i;
7740b4c3e9b5SBjoern A. Zeeb 		combo[c].limits = p2p_limits;
7741b4c3e9b5SBjoern A. Zeeb 	}
7742b4c3e9b5SBjoern A. Zeeb 
7743b4c3e9b5SBjoern A. Zeeb 	if (mbss) {
7744b4c3e9b5SBjoern A. Zeeb 		c++;
7745b4c3e9b5SBjoern A. Zeeb 		i = 0;
7746b4c3e9b5SBjoern A. Zeeb 		n_limits = 1 + mon_flag;
7747b4c3e9b5SBjoern A. Zeeb 		mbss_limits = kcalloc(n_limits, sizeof(*mbss_limits),
7748b4c3e9b5SBjoern A. Zeeb 				      GFP_KERNEL);
7749b4c3e9b5SBjoern A. Zeeb 		if (!mbss_limits)
7750b4c3e9b5SBjoern A. Zeeb 			goto err;
7751b4c3e9b5SBjoern A. Zeeb 		mbss_limits[i].max = 4;
7752b4c3e9b5SBjoern A. Zeeb 		mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7753b4c3e9b5SBjoern A. Zeeb 		if (mon_flag) {
7754b4c3e9b5SBjoern A. Zeeb 			mbss_limits[i].max = 1;
7755b4c3e9b5SBjoern A. Zeeb 			mbss_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
7756b4c3e9b5SBjoern A. Zeeb 		}
7757b4c3e9b5SBjoern A. Zeeb 		combo[c].beacon_int_infra_match = true;
7758b4c3e9b5SBjoern A. Zeeb 		combo[c].num_different_channels = 1;
7759b4c3e9b5SBjoern A. Zeeb 		combo[c].max_interfaces = 4 + mon_flag;
7760b4c3e9b5SBjoern A. Zeeb 		combo[c].n_limits = i;
7761b4c3e9b5SBjoern A. Zeeb 		combo[c].limits = mbss_limits;
7762b4c3e9b5SBjoern A. Zeeb 	}
7763b4c3e9b5SBjoern A. Zeeb 
7764b4c3e9b5SBjoern A. Zeeb 	wiphy->n_iface_combinations = n_combos;
7765b4c3e9b5SBjoern A. Zeeb 	wiphy->iface_combinations = combo;
7766b4c3e9b5SBjoern A. Zeeb 	return 0;
7767b4c3e9b5SBjoern A. Zeeb 
7768b4c3e9b5SBjoern A. Zeeb err:
7769b4c3e9b5SBjoern A. Zeeb 	kfree(c0_limits);
7770b4c3e9b5SBjoern A. Zeeb 	kfree(p2p_limits);
7771b4c3e9b5SBjoern A. Zeeb 	kfree(mbss_limits);
7772b4c3e9b5SBjoern A. Zeeb 	kfree(combo);
7773b4c3e9b5SBjoern A. Zeeb 	return -ENOMEM;
7774b4c3e9b5SBjoern A. Zeeb }
7775b4c3e9b5SBjoern A. Zeeb 
7776b4c3e9b5SBjoern A. Zeeb #ifdef CONFIG_PM
7777b4c3e9b5SBjoern A. Zeeb static const struct wiphy_wowlan_support brcmf_wowlan_support = {
7778b4c3e9b5SBjoern A. Zeeb 	.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
7779b4c3e9b5SBjoern A. Zeeb 	.n_patterns = BRCMF_WOWL_MAXPATTERNS,
7780b4c3e9b5SBjoern A. Zeeb 	.pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
7781b4c3e9b5SBjoern A. Zeeb 	.pattern_min_len = 1,
7782b4c3e9b5SBjoern A. Zeeb 	.max_pkt_offset = 1500,
7783b4c3e9b5SBjoern A. Zeeb };
7784b4c3e9b5SBjoern A. Zeeb #endif
7785b4c3e9b5SBjoern A. Zeeb 
brcmf_wiphy_wowl_params(struct wiphy * wiphy,struct brcmf_if * ifp)7786b4c3e9b5SBjoern A. Zeeb static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
7787b4c3e9b5SBjoern A. Zeeb {
7788b4c3e9b5SBjoern A. Zeeb #ifdef CONFIG_PM
7789b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
7790b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
7791b4c3e9b5SBjoern A. Zeeb 	struct wiphy_wowlan_support *wowl;
7792b4c3e9b5SBjoern A. Zeeb 
7793b4c3e9b5SBjoern A. Zeeb 	wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
7794b4c3e9b5SBjoern A. Zeeb 		       GFP_KERNEL);
7795b4c3e9b5SBjoern A. Zeeb 	if (!wowl) {
7796b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "only support basic wowlan features\n");
7797b4c3e9b5SBjoern A. Zeeb 		wiphy->wowlan = &brcmf_wowlan_support;
7798b4c3e9b5SBjoern A. Zeeb 		return;
7799b4c3e9b5SBjoern A. Zeeb 	}
7800b4c3e9b5SBjoern A. Zeeb 
7801b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
7802b4c3e9b5SBjoern A. Zeeb 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
7803b4c3e9b5SBjoern A. Zeeb 			wowl->flags |= WIPHY_WOWLAN_NET_DETECT;
7804b4c3e9b5SBjoern A. Zeeb 			wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
7805b4c3e9b5SBjoern A. Zeeb 			init_waitqueue_head(&cfg->wowl.nd_data_wait);
7806b4c3e9b5SBjoern A. Zeeb 		}
7807b4c3e9b5SBjoern A. Zeeb 	}
7808b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
7809b4c3e9b5SBjoern A. Zeeb 		wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
7810b4c3e9b5SBjoern A. Zeeb 		wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
7811b4c3e9b5SBjoern A. Zeeb 	}
7812b4c3e9b5SBjoern A. Zeeb 
7813b4c3e9b5SBjoern A. Zeeb 	wiphy->wowlan = wowl;
7814b4c3e9b5SBjoern A. Zeeb #endif
7815b4c3e9b5SBjoern A. Zeeb }
7816b4c3e9b5SBjoern A. Zeeb 
brcmf_setup_wiphy(struct wiphy * wiphy,struct brcmf_if * ifp)7817b4c3e9b5SBjoern A. Zeeb static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
7818b4c3e9b5SBjoern A. Zeeb {
7819b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = ifp->drvr;
7820b4c3e9b5SBjoern A. Zeeb 	const struct ieee80211_iface_combination *combo;
7821b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
7822b4c3e9b5SBjoern A. Zeeb 	u16 max_interfaces = 0;
7823b4c3e9b5SBjoern A. Zeeb 	bool gscan;
7824b4c3e9b5SBjoern A. Zeeb 	__le32 bandlist[3];
7825b4c3e9b5SBjoern A. Zeeb 	u32 n_bands;
7826b4c3e9b5SBjoern A. Zeeb 	int err, i;
7827b4c3e9b5SBjoern A. Zeeb 
7828b4c3e9b5SBjoern A. Zeeb 	wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
7829b4c3e9b5SBjoern A. Zeeb 	wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
7830b4c3e9b5SBjoern A. Zeeb 	wiphy->max_num_pmkids = BRCMF_MAXPMKID;
7831b4c3e9b5SBjoern A. Zeeb 
7832b4c3e9b5SBjoern A. Zeeb 	err = brcmf_setup_ifmodes(wiphy, ifp);
7833b4c3e9b5SBjoern A. Zeeb 	if (err)
7834b4c3e9b5SBjoern A. Zeeb 		return err;
7835b4c3e9b5SBjoern A. Zeeb 
7836b4c3e9b5SBjoern A. Zeeb 	for (i = 0, combo = wiphy->iface_combinations;
7837b4c3e9b5SBjoern A. Zeeb 	     i < wiphy->n_iface_combinations; i++, combo++) {
7838b4c3e9b5SBjoern A. Zeeb 		max_interfaces = max(max_interfaces, combo->max_interfaces);
7839b4c3e9b5SBjoern A. Zeeb 	}
7840b4c3e9b5SBjoern A. Zeeb 
7841b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
7842b4c3e9b5SBjoern A. Zeeb 	     i++) {
7843b4c3e9b5SBjoern A. Zeeb 		u8 *addr = drvr->addresses[i].addr;
7844b4c3e9b5SBjoern A. Zeeb 
7845b4c3e9b5SBjoern A. Zeeb 		memcpy(addr, drvr->mac, ETH_ALEN);
7846b4c3e9b5SBjoern A. Zeeb 		if (i) {
7847b4c3e9b5SBjoern A. Zeeb 			addr[0] |= BIT(1);
7848b4c3e9b5SBjoern A. Zeeb 			addr[ETH_ALEN - 1] ^= i;
7849b4c3e9b5SBjoern A. Zeeb 		}
7850b4c3e9b5SBjoern A. Zeeb 	}
7851b4c3e9b5SBjoern A. Zeeb 	wiphy->addresses = drvr->addresses;
7852b4c3e9b5SBjoern A. Zeeb 	wiphy->n_addresses = i;
7853b4c3e9b5SBjoern A. Zeeb 
7854b4c3e9b5SBjoern A. Zeeb 	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
7855b4c3e9b5SBjoern A. Zeeb 	wiphy->cipher_suites = brcmf_cipher_suites;
7856b4c3e9b5SBjoern A. Zeeb 	wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
7857b4c3e9b5SBjoern A. Zeeb 	if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
7858b4c3e9b5SBjoern A. Zeeb 		wiphy->n_cipher_suites--;
7859b4c3e9b5SBjoern A. Zeeb 	wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |
7860b4c3e9b5SBjoern A. Zeeb 				    BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
7861b4c3e9b5SBjoern A. Zeeb 				    BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
7862b4c3e9b5SBjoern A. Zeeb 
7863*9375e11fSBjoern A. Zeeb 	wiphy->bss_param_support = WIPHY_BSS_PARAM_AP_ISOLATE;
7864*9375e11fSBjoern A. Zeeb 
7865b4c3e9b5SBjoern A. Zeeb 	wiphy->flags |= WIPHY_FLAG_NETNS_OK |
7866b4c3e9b5SBjoern A. Zeeb 			WIPHY_FLAG_PS_ON_BY_DEFAULT |
7867b4c3e9b5SBjoern A. Zeeb 			WIPHY_FLAG_HAVE_AP_SME |
7868b4c3e9b5SBjoern A. Zeeb 			WIPHY_FLAG_OFFCHAN_TX |
7869b4c3e9b5SBjoern A. Zeeb 			WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
7870b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
7871b4c3e9b5SBjoern A. Zeeb 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
7872b4c3e9b5SBjoern A. Zeeb 	if (!ifp->drvr->settings->roamoff)
7873b4c3e9b5SBjoern A. Zeeb 		wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
7874b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) {
7875b4c3e9b5SBjoern A. Zeeb 		wiphy_ext_feature_set(wiphy,
7876b4c3e9b5SBjoern A. Zeeb 				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
7877b4c3e9b5SBjoern A. Zeeb 		wiphy_ext_feature_set(wiphy,
7878b4c3e9b5SBjoern A. Zeeb 				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
7879b4c3e9b5SBjoern A. Zeeb 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
7880b4c3e9b5SBjoern A. Zeeb 			wiphy_ext_feature_set(wiphy,
7881b4c3e9b5SBjoern A. Zeeb 					      NL80211_EXT_FEATURE_SAE_OFFLOAD);
7882b4c3e9b5SBjoern A. Zeeb 	}
7883b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWAUTH)) {
7884b4c3e9b5SBjoern A. Zeeb 		wiphy_ext_feature_set(wiphy,
7885b4c3e9b5SBjoern A. Zeeb 				      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK);
7886b4c3e9b5SBjoern A. Zeeb 		if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
7887b4c3e9b5SBjoern A. Zeeb 			wiphy_ext_feature_set(wiphy,
7888b4c3e9b5SBjoern A. Zeeb 					      NL80211_EXT_FEATURE_SAE_OFFLOAD_AP);
7889b4c3e9b5SBjoern A. Zeeb 	}
7890b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT))
7891b4c3e9b5SBjoern A. Zeeb 		wiphy->features |= NL80211_FEATURE_SAE;
7892b4c3e9b5SBjoern A. Zeeb 	wiphy->mgmt_stypes = brcmf_txrx_stypes;
7893b4c3e9b5SBjoern A. Zeeb 	wiphy->max_remain_on_channel_duration = 5000;
7894b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
7895b4c3e9b5SBjoern A. Zeeb 		gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN);
7896b4c3e9b5SBjoern A. Zeeb 		brcmf_pno_wiphy_params(wiphy, gscan);
7897b4c3e9b5SBjoern A. Zeeb 	}
7898b4c3e9b5SBjoern A. Zeeb 	/* vendor commands/events support */
7899b4c3e9b5SBjoern A. Zeeb 	wiphy->vendor_commands = brcmf_vendor_cmds;
7900b4c3e9b5SBjoern A. Zeeb 	wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
7901b4c3e9b5SBjoern A. Zeeb 
7902b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
7903b4c3e9b5SBjoern A. Zeeb 		brcmf_wiphy_wowl_params(wiphy, ifp);
7904b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
7905b4c3e9b5SBjoern A. Zeeb 				     sizeof(bandlist));
7906b4c3e9b5SBjoern A. Zeeb 	if (err) {
7907b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "could not obtain band info: err=%d\n", err);
7908b4c3e9b5SBjoern A. Zeeb 		return err;
7909b4c3e9b5SBjoern A. Zeeb 	}
7910b4c3e9b5SBjoern A. Zeeb 	/* first entry in bandlist is number of bands */
7911b4c3e9b5SBjoern A. Zeeb 	n_bands = le32_to_cpu(bandlist[0]);
7912b4c3e9b5SBjoern A. Zeeb 	for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
7913b4c3e9b5SBjoern A. Zeeb 		if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
7914b4c3e9b5SBjoern A. Zeeb 			band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
7915b4c3e9b5SBjoern A. Zeeb 				       GFP_KERNEL);
7916b4c3e9b5SBjoern A. Zeeb 			if (!band)
7917b4c3e9b5SBjoern A. Zeeb 				return -ENOMEM;
7918b4c3e9b5SBjoern A. Zeeb 
7919b4c3e9b5SBjoern A. Zeeb 			band->channels = kmemdup(&__wl_2ghz_channels,
7920b4c3e9b5SBjoern A. Zeeb 						 sizeof(__wl_2ghz_channels),
7921b4c3e9b5SBjoern A. Zeeb 						 GFP_KERNEL);
7922b4c3e9b5SBjoern A. Zeeb 			if (!band->channels) {
7923b4c3e9b5SBjoern A. Zeeb 				kfree(band);
7924b4c3e9b5SBjoern A. Zeeb 				return -ENOMEM;
7925b4c3e9b5SBjoern A. Zeeb 			}
7926b4c3e9b5SBjoern A. Zeeb 
7927b4c3e9b5SBjoern A. Zeeb 			band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
7928b4c3e9b5SBjoern A. Zeeb 			wiphy->bands[NL80211_BAND_2GHZ] = band;
7929b4c3e9b5SBjoern A. Zeeb 		}
7930b4c3e9b5SBjoern A. Zeeb 		if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
7931b4c3e9b5SBjoern A. Zeeb 			band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
7932b4c3e9b5SBjoern A. Zeeb 				       GFP_KERNEL);
7933b4c3e9b5SBjoern A. Zeeb 			if (!band)
7934b4c3e9b5SBjoern A. Zeeb 				return -ENOMEM;
7935b4c3e9b5SBjoern A. Zeeb 
7936b4c3e9b5SBjoern A. Zeeb 			band->channels = kmemdup(&__wl_5ghz_channels,
7937b4c3e9b5SBjoern A. Zeeb 						 sizeof(__wl_5ghz_channels),
7938b4c3e9b5SBjoern A. Zeeb 						 GFP_KERNEL);
7939b4c3e9b5SBjoern A. Zeeb 			if (!band->channels) {
7940b4c3e9b5SBjoern A. Zeeb 				kfree(band);
7941b4c3e9b5SBjoern A. Zeeb 				return -ENOMEM;
7942b4c3e9b5SBjoern A. Zeeb 			}
7943b4c3e9b5SBjoern A. Zeeb 
7944b4c3e9b5SBjoern A. Zeeb 			band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
7945b4c3e9b5SBjoern A. Zeeb 			wiphy->bands[NL80211_BAND_5GHZ] = band;
7946b4c3e9b5SBjoern A. Zeeb 		}
7947b4c3e9b5SBjoern A. Zeeb 	}
7948b4c3e9b5SBjoern A. Zeeb 
7949b4c3e9b5SBjoern A. Zeeb 	if (wiphy->bands[NL80211_BAND_5GHZ] &&
7950b4c3e9b5SBjoern A. Zeeb 	    brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DOT11H))
7951b4c3e9b5SBjoern A. Zeeb 		wiphy_ext_feature_set(wiphy,
7952b4c3e9b5SBjoern A. Zeeb 				      NL80211_EXT_FEATURE_DFS_OFFLOAD);
7953b4c3e9b5SBjoern A. Zeeb 
7954b4c3e9b5SBjoern A. Zeeb 	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
7955b4c3e9b5SBjoern A. Zeeb 
7956b4c3e9b5SBjoern A. Zeeb 	wiphy_read_of_freq_limits(wiphy);
7957b4c3e9b5SBjoern A. Zeeb 
7958b4c3e9b5SBjoern A. Zeeb 	return 0;
7959b4c3e9b5SBjoern A. Zeeb }
7960b4c3e9b5SBjoern A. Zeeb 
brcmf_config_dongle(struct brcmf_cfg80211_info * cfg)7961b4c3e9b5SBjoern A. Zeeb static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
7962b4c3e9b5SBjoern A. Zeeb {
7963b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
7964b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev;
7965b4c3e9b5SBjoern A. Zeeb 	struct wireless_dev *wdev;
7966b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
7967b4c3e9b5SBjoern A. Zeeb 	s32 power_mode;
7968b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
7969b4c3e9b5SBjoern A. Zeeb 
7970b4c3e9b5SBjoern A. Zeeb 	if (cfg->dongle_up)
7971b4c3e9b5SBjoern A. Zeeb 		return err;
7972b4c3e9b5SBjoern A. Zeeb 
7973b4c3e9b5SBjoern A. Zeeb 	ndev = cfg_to_ndev(cfg);
7974b4c3e9b5SBjoern A. Zeeb 	wdev = ndev->ieee80211_ptr;
7975b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(ndev);
7976b4c3e9b5SBjoern A. Zeeb 
7977b4c3e9b5SBjoern A. Zeeb 	/* make sure RF is ready for work */
7978b4c3e9b5SBjoern A. Zeeb 	brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
7979b4c3e9b5SBjoern A. Zeeb 
7980b4c3e9b5SBjoern A. Zeeb 	brcmf_dongle_scantime(ifp);
7981b4c3e9b5SBjoern A. Zeeb 
7982b4c3e9b5SBjoern A. Zeeb 	power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
7983b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
7984b4c3e9b5SBjoern A. Zeeb 	if (err)
7985b4c3e9b5SBjoern A. Zeeb 		goto default_conf_out;
7986b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "power save set to %s\n",
7987b4c3e9b5SBjoern A. Zeeb 		  (power_mode ? "enabled" : "disabled"));
7988b4c3e9b5SBjoern A. Zeeb 
7989b4c3e9b5SBjoern A. Zeeb 	err = brcmf_dongle_roam(ifp);
7990b4c3e9b5SBjoern A. Zeeb 	if (err)
7991b4c3e9b5SBjoern A. Zeeb 		goto default_conf_out;
7992b4c3e9b5SBjoern A. Zeeb 	err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
7993b4c3e9b5SBjoern A. Zeeb 					  NULL);
7994b4c3e9b5SBjoern A. Zeeb 	if (err)
7995b4c3e9b5SBjoern A. Zeeb 		goto default_conf_out;
7996b4c3e9b5SBjoern A. Zeeb 
7997b4c3e9b5SBjoern A. Zeeb 	brcmf_configure_arp_nd_offload(ifp, true);
7998b4c3e9b5SBjoern A. Zeeb 
7999b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1);
8000b4c3e9b5SBjoern A. Zeeb 	if (err) {
8001b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "failed to set frameburst mode\n");
8002b4c3e9b5SBjoern A. Zeeb 		goto default_conf_out;
8003b4c3e9b5SBjoern A. Zeeb 	}
8004b4c3e9b5SBjoern A. Zeeb 
8005b4c3e9b5SBjoern A. Zeeb 	cfg->dongle_up = true;
8006b4c3e9b5SBjoern A. Zeeb default_conf_out:
8007b4c3e9b5SBjoern A. Zeeb 
8008b4c3e9b5SBjoern A. Zeeb 	return err;
8009b4c3e9b5SBjoern A. Zeeb 
8010b4c3e9b5SBjoern A. Zeeb }
8011b4c3e9b5SBjoern A. Zeeb 
__brcmf_cfg80211_up(struct brcmf_if * ifp)8012b4c3e9b5SBjoern A. Zeeb static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
8013b4c3e9b5SBjoern A. Zeeb {
8014b4c3e9b5SBjoern A. Zeeb 	set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
8015b4c3e9b5SBjoern A. Zeeb 
8016b4c3e9b5SBjoern A. Zeeb 	return brcmf_config_dongle(ifp->drvr->config);
8017b4c3e9b5SBjoern A. Zeeb }
8018b4c3e9b5SBjoern A. Zeeb 
__brcmf_cfg80211_down(struct brcmf_if * ifp)8019b4c3e9b5SBjoern A. Zeeb static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
8020b4c3e9b5SBjoern A. Zeeb {
8021b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
8022b4c3e9b5SBjoern A. Zeeb 
8023b4c3e9b5SBjoern A. Zeeb 	/*
8024b4c3e9b5SBjoern A. Zeeb 	 * While going down, if associated with AP disassociate
8025b4c3e9b5SBjoern A. Zeeb 	 * from AP to save power
8026b4c3e9b5SBjoern A. Zeeb 	 */
8027b4c3e9b5SBjoern A. Zeeb 	if (check_vif_up(ifp->vif)) {
8028b4c3e9b5SBjoern A. Zeeb 		brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED, true);
8029b4c3e9b5SBjoern A. Zeeb 
8030b4c3e9b5SBjoern A. Zeeb 		/* Make sure WPA_Supplicant receives all the event
8031b4c3e9b5SBjoern A. Zeeb 		   generated due to DISASSOC call to the fw to keep
8032b4c3e9b5SBjoern A. Zeeb 		   the state fw and WPA_Supplicant state consistent
8033b4c3e9b5SBjoern A. Zeeb 		 */
8034b4c3e9b5SBjoern A. Zeeb 		brcmf_delay(500);
8035b4c3e9b5SBjoern A. Zeeb 	}
8036b4c3e9b5SBjoern A. Zeeb 
8037b4c3e9b5SBjoern A. Zeeb 	brcmf_abort_scanning(cfg);
8038b4c3e9b5SBjoern A. Zeeb 	clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
8039b4c3e9b5SBjoern A. Zeeb 
8040b4c3e9b5SBjoern A. Zeeb 	return 0;
8041b4c3e9b5SBjoern A. Zeeb }
8042b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_up(struct net_device * ndev)8043b4c3e9b5SBjoern A. Zeeb s32 brcmf_cfg80211_up(struct net_device *ndev)
8044b4c3e9b5SBjoern A. Zeeb {
8045b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
8046b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
8047b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
8048b4c3e9b5SBjoern A. Zeeb 
8049b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&cfg->usr_sync);
8050b4c3e9b5SBjoern A. Zeeb 	err = __brcmf_cfg80211_up(ifp);
8051b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&cfg->usr_sync);
8052b4c3e9b5SBjoern A. Zeeb 
8053b4c3e9b5SBjoern A. Zeeb 	return err;
8054b4c3e9b5SBjoern A. Zeeb }
8055b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_down(struct net_device * ndev)8056b4c3e9b5SBjoern A. Zeeb s32 brcmf_cfg80211_down(struct net_device *ndev)
8057b4c3e9b5SBjoern A. Zeeb {
8058b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(ndev);
8059b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
8060b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
8061b4c3e9b5SBjoern A. Zeeb 
8062b4c3e9b5SBjoern A. Zeeb 	mutex_lock(&cfg->usr_sync);
8063b4c3e9b5SBjoern A. Zeeb 	err = __brcmf_cfg80211_down(ifp);
8064b4c3e9b5SBjoern A. Zeeb 	mutex_unlock(&cfg->usr_sync);
8065b4c3e9b5SBjoern A. Zeeb 
8066b4c3e9b5SBjoern A. Zeeb 	return err;
8067b4c3e9b5SBjoern A. Zeeb }
8068b4c3e9b5SBjoern A. Zeeb 
brcmf_get_vif_state_any(struct brcmf_cfg80211_info * cfg,unsigned long state)8069b4c3e9b5SBjoern A. Zeeb bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
8070b4c3e9b5SBjoern A. Zeeb 			     unsigned long state)
8071b4c3e9b5SBjoern A. Zeeb {
8072b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
8073b4c3e9b5SBjoern A. Zeeb 
8074b4c3e9b5SBjoern A. Zeeb 	list_for_each_entry(vif, &cfg->vif_list, list) {
8075b4c3e9b5SBjoern A. Zeeb 		if (test_bit(state, &vif->sme_state))
8076b4c3e9b5SBjoern A. Zeeb 			return true;
8077b4c3e9b5SBjoern A. Zeeb 	}
8078b4c3e9b5SBjoern A. Zeeb 	return false;
8079b4c3e9b5SBjoern A. Zeeb }
8080b4c3e9b5SBjoern A. Zeeb 
vif_event_equals(struct brcmf_cfg80211_vif_event * event,u8 action)8081b4c3e9b5SBjoern A. Zeeb static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
8082b4c3e9b5SBjoern A. Zeeb 				    u8 action)
8083b4c3e9b5SBjoern A. Zeeb {
8084b4c3e9b5SBjoern A. Zeeb 	u8 evt_action;
8085b4c3e9b5SBjoern A. Zeeb 
8086b4c3e9b5SBjoern A. Zeeb 	spin_lock(&event->vif_event_lock);
8087b4c3e9b5SBjoern A. Zeeb 	evt_action = event->action;
8088b4c3e9b5SBjoern A. Zeeb 	spin_unlock(&event->vif_event_lock);
8089b4c3e9b5SBjoern A. Zeeb 	return evt_action == action;
8090b4c3e9b5SBjoern A. Zeeb }
8091b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info * cfg,struct brcmf_cfg80211_vif * vif)8092b4c3e9b5SBjoern A. Zeeb void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
8093b4c3e9b5SBjoern A. Zeeb 				  struct brcmf_cfg80211_vif *vif)
8094b4c3e9b5SBjoern A. Zeeb {
8095b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
8096b4c3e9b5SBjoern A. Zeeb 
8097b4c3e9b5SBjoern A. Zeeb 	spin_lock(&event->vif_event_lock);
8098b4c3e9b5SBjoern A. Zeeb 	event->vif = vif;
8099b4c3e9b5SBjoern A. Zeeb 	event->action = 0;
8100b4c3e9b5SBjoern A. Zeeb 	spin_unlock(&event->vif_event_lock);
8101b4c3e9b5SBjoern A. Zeeb }
8102b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info * cfg)8103b4c3e9b5SBjoern A. Zeeb bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
8104b4c3e9b5SBjoern A. Zeeb {
8105b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
8106b4c3e9b5SBjoern A. Zeeb 	bool armed;
8107b4c3e9b5SBjoern A. Zeeb 
8108b4c3e9b5SBjoern A. Zeeb 	spin_lock(&event->vif_event_lock);
8109b4c3e9b5SBjoern A. Zeeb 	armed = event->vif != NULL;
8110b4c3e9b5SBjoern A. Zeeb 	spin_unlock(&event->vif_event_lock);
8111b4c3e9b5SBjoern A. Zeeb 
8112b4c3e9b5SBjoern A. Zeeb 	return armed;
8113b4c3e9b5SBjoern A. Zeeb }
8114b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info * cfg,u8 action,ulong timeout)8115b4c3e9b5SBjoern A. Zeeb int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
8116b4c3e9b5SBjoern A. Zeeb 				  u8 action, ulong timeout)
8117b4c3e9b5SBjoern A. Zeeb {
8118b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
8119b4c3e9b5SBjoern A. Zeeb 
8120b4c3e9b5SBjoern A. Zeeb 	return wait_event_timeout(event->vif_wq,
8121b4c3e9b5SBjoern A. Zeeb 				  vif_event_equals(event, action), timeout);
8122b4c3e9b5SBjoern A. Zeeb }
8123b4c3e9b5SBjoern A. Zeeb 
brmcf_use_iso3166_ccode_fallback(struct brcmf_pub * drvr)8124b4c3e9b5SBjoern A. Zeeb static bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr)
8125b4c3e9b5SBjoern A. Zeeb {
8126b4c3e9b5SBjoern A. Zeeb 	if (drvr->settings->trivial_ccode_map)
8127b4c3e9b5SBjoern A. Zeeb 		return true;
8128b4c3e9b5SBjoern A. Zeeb 
8129b4c3e9b5SBjoern A. Zeeb 	switch (drvr->bus_if->chip) {
8130b4c3e9b5SBjoern A. Zeeb 	case BRCM_CC_43430_CHIP_ID:
8131b4c3e9b5SBjoern A. Zeeb 	case BRCM_CC_4345_CHIP_ID:
8132b4c3e9b5SBjoern A. Zeeb 	case BRCM_CC_4356_CHIP_ID:
8133b4c3e9b5SBjoern A. Zeeb 	case BRCM_CC_43602_CHIP_ID:
8134b4c3e9b5SBjoern A. Zeeb 		return true;
8135b4c3e9b5SBjoern A. Zeeb 	default:
8136b4c3e9b5SBjoern A. Zeeb 		return false;
8137b4c3e9b5SBjoern A. Zeeb 	}
8138b4c3e9b5SBjoern A. Zeeb }
8139b4c3e9b5SBjoern A. Zeeb 
brcmf_translate_country_code(struct brcmf_pub * drvr,char alpha2[2],struct brcmf_fil_country_le * ccreq)8140b4c3e9b5SBjoern A. Zeeb static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
8141b4c3e9b5SBjoern A. Zeeb 					struct brcmf_fil_country_le *ccreq)
8142b4c3e9b5SBjoern A. Zeeb {
8143b4c3e9b5SBjoern A. Zeeb 	struct brcmfmac_pd_cc *country_codes;
8144b4c3e9b5SBjoern A. Zeeb 	struct brcmfmac_pd_cc_entry *cc;
8145b4c3e9b5SBjoern A. Zeeb 	s32 found_index;
8146b4c3e9b5SBjoern A. Zeeb 	int i;
8147b4c3e9b5SBjoern A. Zeeb 
8148b4c3e9b5SBjoern A. Zeeb 	if ((alpha2[0] == ccreq->country_abbrev[0]) &&
8149b4c3e9b5SBjoern A. Zeeb 	    (alpha2[1] == ccreq->country_abbrev[1])) {
8150b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "Country code already set\n");
8151b4c3e9b5SBjoern A. Zeeb 		return -EAGAIN;
8152b4c3e9b5SBjoern A. Zeeb 	}
8153b4c3e9b5SBjoern A. Zeeb 
8154b4c3e9b5SBjoern A. Zeeb 	country_codes = drvr->settings->country_codes;
8155b4c3e9b5SBjoern A. Zeeb 	if (!country_codes) {
8156b4c3e9b5SBjoern A. Zeeb 		if (brmcf_use_iso3166_ccode_fallback(drvr)) {
8157b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(TRACE, "No country codes configured for device, using ISO3166 code and 0 rev\n");
8158b4c3e9b5SBjoern A. Zeeb 			memset(ccreq, 0, sizeof(*ccreq));
8159b4c3e9b5SBjoern A. Zeeb 			ccreq->country_abbrev[0] = alpha2[0];
8160b4c3e9b5SBjoern A. Zeeb 			ccreq->country_abbrev[1] = alpha2[1];
8161b4c3e9b5SBjoern A. Zeeb 			ccreq->ccode[0] = alpha2[0];
8162b4c3e9b5SBjoern A. Zeeb 			ccreq->ccode[1] = alpha2[1];
8163b4c3e9b5SBjoern A. Zeeb 			return 0;
8164b4c3e9b5SBjoern A. Zeeb 		}
8165b4c3e9b5SBjoern A. Zeeb 
8166b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "No country codes configured for device\n");
8167b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
8168b4c3e9b5SBjoern A. Zeeb 	}
8169b4c3e9b5SBjoern A. Zeeb 
8170b4c3e9b5SBjoern A. Zeeb 	found_index = -1;
8171b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < country_codes->table_size; i++) {
8172b4c3e9b5SBjoern A. Zeeb 		cc = &country_codes->table[i];
8173b4c3e9b5SBjoern A. Zeeb 		if ((cc->iso3166[0] == '\0') && (found_index == -1))
8174b4c3e9b5SBjoern A. Zeeb 			found_index = i;
8175b4c3e9b5SBjoern A. Zeeb 		if ((cc->iso3166[0] == alpha2[0]) &&
8176b4c3e9b5SBjoern A. Zeeb 		    (cc->iso3166[1] == alpha2[1])) {
8177b4c3e9b5SBjoern A. Zeeb 			found_index = i;
8178b4c3e9b5SBjoern A. Zeeb 			break;
8179b4c3e9b5SBjoern A. Zeeb 		}
8180b4c3e9b5SBjoern A. Zeeb 	}
8181b4c3e9b5SBjoern A. Zeeb 	if (found_index == -1) {
8182b4c3e9b5SBjoern A. Zeeb 		brcmf_dbg(TRACE, "No country code match found\n");
8183b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
8184b4c3e9b5SBjoern A. Zeeb 	}
8185b4c3e9b5SBjoern A. Zeeb 	memset(ccreq, 0, sizeof(*ccreq));
8186b4c3e9b5SBjoern A. Zeeb 	ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
8187b4c3e9b5SBjoern A. Zeeb 	memcpy(ccreq->ccode, country_codes->table[found_index].cc,
8188b4c3e9b5SBjoern A. Zeeb 	       BRCMF_COUNTRY_BUF_SZ);
8189b4c3e9b5SBjoern A. Zeeb 	ccreq->country_abbrev[0] = alpha2[0];
8190b4c3e9b5SBjoern A. Zeeb 	ccreq->country_abbrev[1] = alpha2[1];
8191b4c3e9b5SBjoern A. Zeeb 	ccreq->country_abbrev[2] = 0;
8192b4c3e9b5SBjoern A. Zeeb 
8193b4c3e9b5SBjoern A. Zeeb 	return 0;
8194b4c3e9b5SBjoern A. Zeeb }
8195b4c3e9b5SBjoern A. Zeeb 
8196b4c3e9b5SBjoern A. Zeeb static int
brcmf_parse_dump_obss(char * buf,struct brcmf_dump_survey * survey)8197b4c3e9b5SBjoern A. Zeeb brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey)
8198b4c3e9b5SBjoern A. Zeeb {
8199b4c3e9b5SBjoern A. Zeeb 	int i;
8200b4c3e9b5SBjoern A. Zeeb 	char *token;
8201b4c3e9b5SBjoern A. Zeeb 	char delim[] = "\n ";
8202b4c3e9b5SBjoern A. Zeeb 	unsigned long val;
8203b4c3e9b5SBjoern A. Zeeb 	int err = 0;
8204b4c3e9b5SBjoern A. Zeeb 
8205b4c3e9b5SBjoern A. Zeeb 	token = strsep(&buf, delim);
8206b4c3e9b5SBjoern A. Zeeb 	while (token) {
8207b4c3e9b5SBjoern A. Zeeb 		if (!strcmp(token, "OBSS")) {
8208b4c3e9b5SBjoern A. Zeeb 			for (i = 0; i < OBSS_TOKEN_IDX; i++)
8209b4c3e9b5SBjoern A. Zeeb 				token = strsep(&buf, delim);
8210b4c3e9b5SBjoern A. Zeeb 			err = kstrtoul(token, 10, &val);
8211b4c3e9b5SBjoern A. Zeeb 			if (err)
8212b4c3e9b5SBjoern A. Zeeb 				break;
8213b4c3e9b5SBjoern A. Zeeb 			survey->obss = val;
8214b4c3e9b5SBjoern A. Zeeb 		}
8215b4c3e9b5SBjoern A. Zeeb 
8216b4c3e9b5SBjoern A. Zeeb 		if (!strcmp(token, "IBSS")) {
8217b4c3e9b5SBjoern A. Zeeb 			for (i = 0; i < IBSS_TOKEN_IDX; i++)
8218b4c3e9b5SBjoern A. Zeeb 				token = strsep(&buf, delim);
8219b4c3e9b5SBjoern A. Zeeb 			err = kstrtoul(token, 10, &val);
8220b4c3e9b5SBjoern A. Zeeb 			if (err)
8221b4c3e9b5SBjoern A. Zeeb 				break;
8222b4c3e9b5SBjoern A. Zeeb 			survey->ibss = val;
8223b4c3e9b5SBjoern A. Zeeb 		}
8224b4c3e9b5SBjoern A. Zeeb 
8225b4c3e9b5SBjoern A. Zeeb 		if (!strcmp(token, "TXDur")) {
8226b4c3e9b5SBjoern A. Zeeb 			for (i = 0; i < TX_TOKEN_IDX; i++)
8227b4c3e9b5SBjoern A. Zeeb 				token = strsep(&buf, delim);
8228b4c3e9b5SBjoern A. Zeeb 			err = kstrtoul(token, 10, &val);
8229b4c3e9b5SBjoern A. Zeeb 			if (err)
8230b4c3e9b5SBjoern A. Zeeb 				break;
8231b4c3e9b5SBjoern A. Zeeb 			survey->tx = val;
8232b4c3e9b5SBjoern A. Zeeb 		}
8233b4c3e9b5SBjoern A. Zeeb 
8234b4c3e9b5SBjoern A. Zeeb 		if (!strcmp(token, "Category")) {
8235b4c3e9b5SBjoern A. Zeeb 			for (i = 0; i < CTG_TOKEN_IDX; i++)
8236b4c3e9b5SBjoern A. Zeeb 				token = strsep(&buf, delim);
8237b4c3e9b5SBjoern A. Zeeb 			err = kstrtoul(token, 10, &val);
8238b4c3e9b5SBjoern A. Zeeb 			if (err)
8239b4c3e9b5SBjoern A. Zeeb 				break;
8240b4c3e9b5SBjoern A. Zeeb 			survey->no_ctg = val;
8241b4c3e9b5SBjoern A. Zeeb 		}
8242b4c3e9b5SBjoern A. Zeeb 
8243b4c3e9b5SBjoern A. Zeeb 		if (!strcmp(token, "Packet")) {
8244b4c3e9b5SBjoern A. Zeeb 			for (i = 0; i < PKT_TOKEN_IDX; i++)
8245b4c3e9b5SBjoern A. Zeeb 				token = strsep(&buf, delim);
8246b4c3e9b5SBjoern A. Zeeb 			err = kstrtoul(token, 10, &val);
8247b4c3e9b5SBjoern A. Zeeb 			if (err)
8248b4c3e9b5SBjoern A. Zeeb 				break;
8249b4c3e9b5SBjoern A. Zeeb 			survey->no_pckt = val;
8250b4c3e9b5SBjoern A. Zeeb 		}
8251b4c3e9b5SBjoern A. Zeeb 
8252b4c3e9b5SBjoern A. Zeeb 		if (!strcmp(token, "Opp(time):")) {
8253b4c3e9b5SBjoern A. Zeeb 			for (i = 0; i < IDLE_TOKEN_IDX; i++)
8254b4c3e9b5SBjoern A. Zeeb 				token = strsep(&buf, delim);
8255b4c3e9b5SBjoern A. Zeeb 			err = kstrtoul(token, 10, &val);
8256b4c3e9b5SBjoern A. Zeeb 			if (err)
8257b4c3e9b5SBjoern A. Zeeb 				break;
8258b4c3e9b5SBjoern A. Zeeb 			survey->idle = val;
8259b4c3e9b5SBjoern A. Zeeb 		}
8260b4c3e9b5SBjoern A. Zeeb 
8261b4c3e9b5SBjoern A. Zeeb 		token = strsep(&buf, delim);
8262b4c3e9b5SBjoern A. Zeeb 	}
8263b4c3e9b5SBjoern A. Zeeb 
8264b4c3e9b5SBjoern A. Zeeb 	return err;
8265b4c3e9b5SBjoern A. Zeeb }
8266b4c3e9b5SBjoern A. Zeeb 
8267b4c3e9b5SBjoern A. Zeeb static int
brcmf_dump_obss(struct brcmf_if * ifp,struct cca_msrmnt_query req,struct brcmf_dump_survey * survey)8268b4c3e9b5SBjoern A. Zeeb brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req,
8269b4c3e9b5SBjoern A. Zeeb 		struct brcmf_dump_survey *survey)
8270b4c3e9b5SBjoern A. Zeeb {
8271b4c3e9b5SBjoern A. Zeeb 	struct cca_stats_n_flags *results;
8272b4c3e9b5SBjoern A. Zeeb 	char *buf;
8273b4c3e9b5SBjoern A. Zeeb 	int err;
8274b4c3e9b5SBjoern A. Zeeb 
8275b4c3e9b5SBjoern A. Zeeb 	buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL);
8276b4c3e9b5SBjoern A. Zeeb 	if (!buf)
8277b4c3e9b5SBjoern A. Zeeb 		return -ENOMEM;
8278b4c3e9b5SBjoern A. Zeeb 
8279b4c3e9b5SBjoern A. Zeeb 	memcpy(buf, &req, sizeof(struct cca_msrmnt_query));
8280b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "dump_obss",
8281b4c3e9b5SBjoern A. Zeeb 				       buf, BRCMF_DCMD_MEDLEN);
8282b4c3e9b5SBjoern A. Zeeb 	if (err) {
8283b4c3e9b5SBjoern A. Zeeb 		brcmf_err("dump_obss error (%d)\n", err);
8284b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
8285b4c3e9b5SBjoern A. Zeeb 		goto exit;
8286b4c3e9b5SBjoern A. Zeeb 	}
8287b4c3e9b5SBjoern A. Zeeb 	results = (struct cca_stats_n_flags *)(buf);
8288b4c3e9b5SBjoern A. Zeeb 
8289b4c3e9b5SBjoern A. Zeeb 	if (req.msrmnt_query)
8290b4c3e9b5SBjoern A. Zeeb 		brcmf_parse_dump_obss(results->buf, survey);
8291b4c3e9b5SBjoern A. Zeeb 
8292b4c3e9b5SBjoern A. Zeeb exit:
8293b4c3e9b5SBjoern A. Zeeb 	kfree(buf);
8294b4c3e9b5SBjoern A. Zeeb 	return err;
8295b4c3e9b5SBjoern A. Zeeb }
8296b4c3e9b5SBjoern A. Zeeb 
8297b4c3e9b5SBjoern A. Zeeb static s32
brcmf_set_channel(struct brcmf_cfg80211_info * cfg,struct ieee80211_channel * chan)8298b4c3e9b5SBjoern A. Zeeb brcmf_set_channel(struct brcmf_cfg80211_info *cfg, struct ieee80211_channel *chan)
8299b4c3e9b5SBjoern A. Zeeb {
8300b4c3e9b5SBjoern A. Zeeb 	u16 chspec = 0;
8301b4c3e9b5SBjoern A. Zeeb 	int err = 0;
8302b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
8303b4c3e9b5SBjoern A. Zeeb 
8304b4c3e9b5SBjoern A. Zeeb 	if (chan->flags & IEEE80211_CHAN_DISABLED)
8305b4c3e9b5SBjoern A. Zeeb 		return -EINVAL;
8306b4c3e9b5SBjoern A. Zeeb 
8307b4c3e9b5SBjoern A. Zeeb 	/* set_channel */
8308b4c3e9b5SBjoern A. Zeeb 	chspec = channel_to_chanspec(&cfg->d11inf, chan);
8309b4c3e9b5SBjoern A. Zeeb 	if (chspec != INVCHANSPEC) {
8310b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec);
8311b4c3e9b5SBjoern A. Zeeb 		if (err) {
8312b4c3e9b5SBjoern A. Zeeb 			brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err);
8313b4c3e9b5SBjoern A. Zeeb 			err = -EINVAL;
8314b4c3e9b5SBjoern A. Zeeb 		}
8315b4c3e9b5SBjoern A. Zeeb 	} else {
8316b4c3e9b5SBjoern A. Zeeb 		brcmf_err("failed to convert host chanspec to fw chanspec\n");
8317b4c3e9b5SBjoern A. Zeeb 		err = -EINVAL;
8318b4c3e9b5SBjoern A. Zeeb 	}
8319b4c3e9b5SBjoern A. Zeeb 
8320b4c3e9b5SBjoern A. Zeeb 	return err;
8321b4c3e9b5SBjoern A. Zeeb }
8322b4c3e9b5SBjoern A. Zeeb 
8323b4c3e9b5SBjoern A. Zeeb static int
brcmf_cfg80211_dump_survey(struct wiphy * wiphy,struct net_device * ndev,int idx,struct survey_info * info)8324b4c3e9b5SBjoern A. Zeeb brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
8325b4c3e9b5SBjoern A. Zeeb 			   int idx, struct survey_info *info)
8326b4c3e9b5SBjoern A. Zeeb {
8327b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
8328b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
8329b4c3e9b5SBjoern A. Zeeb 	struct brcmf_dump_survey survey = {};
8330b4c3e9b5SBjoern A. Zeeb 	struct ieee80211_supported_band *band;
8331b4c3e9b5SBjoern A. Zeeb 	enum nl80211_band band_id;
8332b4c3e9b5SBjoern A. Zeeb 	struct cca_msrmnt_query req;
8333b4c3e9b5SBjoern A. Zeeb 	u32 noise;
8334b4c3e9b5SBjoern A. Zeeb 	int err;
8335b4c3e9b5SBjoern A. Zeeb 
8336b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx);
8337b4c3e9b5SBjoern A. Zeeb 
8338b4c3e9b5SBjoern A. Zeeb 	/* Do not run survey when VIF in CONNECTING / CONNECTED states */
8339b4c3e9b5SBjoern A. Zeeb 	if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) ||
8340b4c3e9b5SBjoern A. Zeeb 	    (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) {
8341b4c3e9b5SBjoern A. Zeeb 		return -EBUSY;
8342b4c3e9b5SBjoern A. Zeeb 	}
8343b4c3e9b5SBjoern A. Zeeb 
8344b4c3e9b5SBjoern A. Zeeb 	for (band_id = 0; band_id < NUM_NL80211_BANDS; band_id++) {
8345b4c3e9b5SBjoern A. Zeeb 		band = wiphy->bands[band_id];
8346b4c3e9b5SBjoern A. Zeeb 		if (!band)
8347b4c3e9b5SBjoern A. Zeeb 			continue;
8348b4c3e9b5SBjoern A. Zeeb 		if (idx >= band->n_channels) {
8349b4c3e9b5SBjoern A. Zeeb 			idx -= band->n_channels;
8350b4c3e9b5SBjoern A. Zeeb 			continue;
8351b4c3e9b5SBjoern A. Zeeb 		}
8352b4c3e9b5SBjoern A. Zeeb 
8353b4c3e9b5SBjoern A. Zeeb 		info->channel = &band->channels[idx];
8354b4c3e9b5SBjoern A. Zeeb 		break;
8355b4c3e9b5SBjoern A. Zeeb 	}
8356b4c3e9b5SBjoern A. Zeeb 	if (band_id == NUM_NL80211_BANDS)
8357b4c3e9b5SBjoern A. Zeeb 		return -ENOENT;
8358b4c3e9b5SBjoern A. Zeeb 
8359b4c3e9b5SBjoern A. Zeeb 	/* Setting current channel to the requested channel */
8360b4c3e9b5SBjoern A. Zeeb 	info->filled = 0;
8361b4c3e9b5SBjoern A. Zeeb 	if (brcmf_set_channel(cfg, info->channel))
8362b4c3e9b5SBjoern A. Zeeb 		return 0;
8363b4c3e9b5SBjoern A. Zeeb 
8364b4c3e9b5SBjoern A. Zeeb 	/* Disable mpc */
8365b4c3e9b5SBjoern A. Zeeb 	brcmf_set_mpc(ifp, 0);
8366b4c3e9b5SBjoern A. Zeeb 
8367b4c3e9b5SBjoern A. Zeeb 	/* Set interface up, explicitly. */
8368b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
8369b4c3e9b5SBjoern A. Zeeb 	if (err) {
8370b4c3e9b5SBjoern A. Zeeb 		brcmf_err("set interface up failed, err = %d\n", err);
8371b4c3e9b5SBjoern A. Zeeb 		goto exit;
8372b4c3e9b5SBjoern A. Zeeb 	}
8373b4c3e9b5SBjoern A. Zeeb 
8374b4c3e9b5SBjoern A. Zeeb 	/* Get noise value */
8375b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise);
8376b4c3e9b5SBjoern A. Zeeb 	if (err) {
8377b4c3e9b5SBjoern A. Zeeb 		brcmf_err("Get Phy Noise failed, use dummy value\n");
8378b4c3e9b5SBjoern A. Zeeb 		noise = CHAN_NOISE_DUMMY;
8379b4c3e9b5SBjoern A. Zeeb 	}
8380b4c3e9b5SBjoern A. Zeeb 
8381b4c3e9b5SBjoern A. Zeeb 	/* Start Measurement for obss stats on current channel */
8382b4c3e9b5SBjoern A. Zeeb 	req.msrmnt_query = 0;
8383b4c3e9b5SBjoern A. Zeeb 	req.time_req = ACS_MSRMNT_DELAY;
8384b4c3e9b5SBjoern A. Zeeb 	err = brcmf_dump_obss(ifp, req, &survey);
8385b4c3e9b5SBjoern A. Zeeb 	if (err)
8386b4c3e9b5SBjoern A. Zeeb 		goto exit;
8387b4c3e9b5SBjoern A. Zeeb 
8388b4c3e9b5SBjoern A. Zeeb 	/* Add 10 ms for IOVAR completion */
8389902136e0SBjoern A. Zeeb #if defined(__linux__)
8390b4c3e9b5SBjoern A. Zeeb 	msleep(ACS_MSRMNT_DELAY + 10);
8391902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
8392902136e0SBjoern A. Zeeb 	linux_msleep(ACS_MSRMNT_DELAY + 10);
8393902136e0SBjoern A. Zeeb #endif
8394b4c3e9b5SBjoern A. Zeeb 
8395b4c3e9b5SBjoern A. Zeeb 	/* Issue IOVAR to collect measurement results */
8396b4c3e9b5SBjoern A. Zeeb 	req.msrmnt_query = 1;
8397b4c3e9b5SBjoern A. Zeeb 	err = brcmf_dump_obss(ifp, req, &survey);
8398b4c3e9b5SBjoern A. Zeeb 	if (err)
8399b4c3e9b5SBjoern A. Zeeb 		goto exit;
8400b4c3e9b5SBjoern A. Zeeb 
8401b4c3e9b5SBjoern A. Zeeb 	info->noise = noise;
8402b4c3e9b5SBjoern A. Zeeb 	info->time = ACS_MSRMNT_DELAY;
8403b4c3e9b5SBjoern A. Zeeb 	info->time_busy = ACS_MSRMNT_DELAY - survey.idle;
8404b4c3e9b5SBjoern A. Zeeb 	info->time_rx = survey.obss + survey.ibss + survey.no_ctg +
8405b4c3e9b5SBjoern A. Zeeb 		survey.no_pckt;
8406b4c3e9b5SBjoern A. Zeeb 	info->time_tx = survey.tx;
8407b4c3e9b5SBjoern A. Zeeb 	info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
8408b4c3e9b5SBjoern A. Zeeb 		SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX |
8409b4c3e9b5SBjoern A. Zeeb 		SURVEY_INFO_TIME_TX;
8410b4c3e9b5SBjoern A. Zeeb 
8411b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n",
8412b4c3e9b5SBjoern A. Zeeb 		  ieee80211_frequency_to_channel(info->channel->center_freq),
8413b4c3e9b5SBjoern A. Zeeb 		  ACS_MSRMNT_DELAY);
8414902136e0SBjoern A. Zeeb #if defined(__linux__)
8415b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n",
8416b4c3e9b5SBjoern A. Zeeb 		  info->noise, info->time_busy, info->time_rx, info->time_tx);
8417902136e0SBjoern A. Zeeb #elif defined(__FreeBSD__)
8418902136e0SBjoern A. Zeeb 	brcmf_dbg(INFO, "noise(%d) busy(%ju) rx(%ju) tx(%ju)\n",
8419902136e0SBjoern A. Zeeb 		  info->noise, (uintmax_t)info->time_busy, (uintmax_t)info->time_rx, (uintmax_t)info->time_tx);
8420902136e0SBjoern A. Zeeb #endif
8421b4c3e9b5SBjoern A. Zeeb 
8422b4c3e9b5SBjoern A. Zeeb exit:
8423b4c3e9b5SBjoern A. Zeeb 	if (!brcmf_is_apmode(ifp->vif))
8424b4c3e9b5SBjoern A. Zeeb 		brcmf_set_mpc(ifp, 1);
8425b4c3e9b5SBjoern A. Zeeb 	return err;
8426b4c3e9b5SBjoern A. Zeeb }
8427b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_reg_notifier(struct wiphy * wiphy,struct regulatory_request * req)8428b4c3e9b5SBjoern A. Zeeb static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
8429b4c3e9b5SBjoern A. Zeeb 					struct regulatory_request *req)
8430b4c3e9b5SBjoern A. Zeeb {
8431b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
8432b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
8433b4c3e9b5SBjoern A. Zeeb 	struct brcmf_pub *drvr = cfg->pub;
8434b4c3e9b5SBjoern A. Zeeb 	struct brcmf_fil_country_le ccreq;
8435b4c3e9b5SBjoern A. Zeeb 	s32 err;
8436b4c3e9b5SBjoern A. Zeeb 	int i;
8437b4c3e9b5SBjoern A. Zeeb 
8438b4c3e9b5SBjoern A. Zeeb 	/* The country code gets set to "00" by default at boot, ignore */
8439b4c3e9b5SBjoern A. Zeeb 	if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
8440b4c3e9b5SBjoern A. Zeeb 		return;
8441b4c3e9b5SBjoern A. Zeeb 
8442b4c3e9b5SBjoern A. Zeeb 	/* ignore non-ISO3166 country codes */
8443b4c3e9b5SBjoern A. Zeeb 	for (i = 0; i < 2; i++)
8444b4c3e9b5SBjoern A. Zeeb 		if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
8445b4c3e9b5SBjoern A. Zeeb 			bphy_err(drvr, "not an ISO3166 code (0x%02x 0x%02x)\n",
8446b4c3e9b5SBjoern A. Zeeb 				 req->alpha2[0], req->alpha2[1]);
8447b4c3e9b5SBjoern A. Zeeb 			return;
8448b4c3e9b5SBjoern A. Zeeb 		}
8449b4c3e9b5SBjoern A. Zeeb 
8450b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
8451b4c3e9b5SBjoern A. Zeeb 		  req->alpha2[0], req->alpha2[1]);
8452b4c3e9b5SBjoern A. Zeeb 
8453b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
8454b4c3e9b5SBjoern A. Zeeb 	if (err) {
8455b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Country code iovar returned err = %d\n", err);
8456b4c3e9b5SBjoern A. Zeeb 		return;
8457b4c3e9b5SBjoern A. Zeeb 	}
8458b4c3e9b5SBjoern A. Zeeb 
8459b4c3e9b5SBjoern A. Zeeb 	err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
8460b4c3e9b5SBjoern A. Zeeb 	if (err)
8461b4c3e9b5SBjoern A. Zeeb 		return;
8462b4c3e9b5SBjoern A. Zeeb 
8463b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
8464b4c3e9b5SBjoern A. Zeeb 	if (err) {
8465b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Firmware rejected country setting\n");
8466b4c3e9b5SBjoern A. Zeeb 		return;
8467b4c3e9b5SBjoern A. Zeeb 	}
8468b4c3e9b5SBjoern A. Zeeb 	brcmf_setup_wiphybands(cfg);
8469b4c3e9b5SBjoern A. Zeeb }
8470b4c3e9b5SBjoern A. Zeeb 
brcmf_free_wiphy(struct wiphy * wiphy)8471b4c3e9b5SBjoern A. Zeeb static void brcmf_free_wiphy(struct wiphy *wiphy)
8472b4c3e9b5SBjoern A. Zeeb {
8473b4c3e9b5SBjoern A. Zeeb 	int i;
8474b4c3e9b5SBjoern A. Zeeb 
8475b4c3e9b5SBjoern A. Zeeb 	if (!wiphy)
8476b4c3e9b5SBjoern A. Zeeb 		return;
8477b4c3e9b5SBjoern A. Zeeb 
8478b4c3e9b5SBjoern A. Zeeb 	if (wiphy->iface_combinations) {
8479b4c3e9b5SBjoern A. Zeeb 		for (i = 0; i < wiphy->n_iface_combinations; i++)
8480b4c3e9b5SBjoern A. Zeeb 			kfree(wiphy->iface_combinations[i].limits);
8481b4c3e9b5SBjoern A. Zeeb 	}
8482b4c3e9b5SBjoern A. Zeeb 	kfree(wiphy->iface_combinations);
8483b4c3e9b5SBjoern A. Zeeb 	if (wiphy->bands[NL80211_BAND_2GHZ]) {
8484b4c3e9b5SBjoern A. Zeeb 		kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
8485b4c3e9b5SBjoern A. Zeeb 		kfree(wiphy->bands[NL80211_BAND_2GHZ]);
8486b4c3e9b5SBjoern A. Zeeb 	}
8487b4c3e9b5SBjoern A. Zeeb 	if (wiphy->bands[NL80211_BAND_5GHZ]) {
8488b4c3e9b5SBjoern A. Zeeb 		kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
8489b4c3e9b5SBjoern A. Zeeb 		kfree(wiphy->bands[NL80211_BAND_5GHZ]);
8490b4c3e9b5SBjoern A. Zeeb 	}
8491b4c3e9b5SBjoern A. Zeeb #if IS_ENABLED(CONFIG_PM)
8492b4c3e9b5SBjoern A. Zeeb 	if (wiphy->wowlan != &brcmf_wowlan_support)
8493b4c3e9b5SBjoern A. Zeeb 		kfree(wiphy->wowlan);
8494b4c3e9b5SBjoern A. Zeeb #endif
8495b4c3e9b5SBjoern A. Zeeb }
8496b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_attach(struct brcmf_pub * drvr,struct cfg80211_ops * ops,bool p2pdev_forced)8497b4c3e9b5SBjoern A. Zeeb struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
8498b4c3e9b5SBjoern A. Zeeb 						  struct cfg80211_ops *ops,
8499b4c3e9b5SBjoern A. Zeeb 						  bool p2pdev_forced)
8500b4c3e9b5SBjoern A. Zeeb {
8501b4c3e9b5SBjoern A. Zeeb 	struct wiphy *wiphy = drvr->wiphy;
8502b4c3e9b5SBjoern A. Zeeb 	struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
8503b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_info *cfg;
8504b4c3e9b5SBjoern A. Zeeb 	struct brcmf_cfg80211_vif *vif;
8505b4c3e9b5SBjoern A. Zeeb 	struct brcmf_if *ifp;
8506b4c3e9b5SBjoern A. Zeeb 	s32 err = 0;
8507b4c3e9b5SBjoern A. Zeeb 	s32 io_type;
8508b4c3e9b5SBjoern A. Zeeb 	u16 *cap = NULL;
8509b4c3e9b5SBjoern A. Zeeb 
8510b4c3e9b5SBjoern A. Zeeb 	if (!ndev) {
8511b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "ndev is invalid\n");
8512b4c3e9b5SBjoern A. Zeeb 		return NULL;
8513b4c3e9b5SBjoern A. Zeeb 	}
8514b4c3e9b5SBjoern A. Zeeb 
8515b4c3e9b5SBjoern A. Zeeb 	cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
8516b4c3e9b5SBjoern A. Zeeb 	if (!cfg) {
8517b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Could not allocate wiphy device\n");
8518b4c3e9b5SBjoern A. Zeeb 		return NULL;
8519b4c3e9b5SBjoern A. Zeeb 	}
8520b4c3e9b5SBjoern A. Zeeb 
8521b4c3e9b5SBjoern A. Zeeb 	cfg->wiphy = wiphy;
8522b4c3e9b5SBjoern A. Zeeb 	cfg->pub = drvr;
8523b4c3e9b5SBjoern A. Zeeb 	init_vif_event(&cfg->vif_event);
8524b4c3e9b5SBjoern A. Zeeb 	INIT_LIST_HEAD(&cfg->vif_list);
8525b4c3e9b5SBjoern A. Zeeb 
8526b4c3e9b5SBjoern A. Zeeb 	vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);
8527b4c3e9b5SBjoern A. Zeeb 	if (IS_ERR(vif))
8528b4c3e9b5SBjoern A. Zeeb 		goto wiphy_out;
8529b4c3e9b5SBjoern A. Zeeb 
8530b4c3e9b5SBjoern A. Zeeb 	ifp = netdev_priv(ndev);
8531b4c3e9b5SBjoern A. Zeeb 	vif->ifp = ifp;
8532b4c3e9b5SBjoern A. Zeeb 	vif->wdev.netdev = ndev;
8533b4c3e9b5SBjoern A. Zeeb 	ndev->ieee80211_ptr = &vif->wdev;
8534b4c3e9b5SBjoern A. Zeeb 	SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
8535b4c3e9b5SBjoern A. Zeeb 
8536b4c3e9b5SBjoern A. Zeeb 	err = wl_init_priv(cfg);
8537b4c3e9b5SBjoern A. Zeeb 	if (err) {
8538b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Failed to init iwm_priv (%d)\n", err);
8539b4c3e9b5SBjoern A. Zeeb 		brcmf_free_vif(vif);
8540b4c3e9b5SBjoern A. Zeeb 		goto wiphy_out;
8541b4c3e9b5SBjoern A. Zeeb 	}
8542b4c3e9b5SBjoern A. Zeeb 	ifp->vif = vif;
8543b4c3e9b5SBjoern A. Zeeb 
8544b4c3e9b5SBjoern A. Zeeb 	/* determine d11 io type before wiphy setup */
8545b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
8546b4c3e9b5SBjoern A. Zeeb 	if (err) {
8547b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Failed to get D11 version (%d)\n", err);
8548b4c3e9b5SBjoern A. Zeeb 		goto priv_out;
8549b4c3e9b5SBjoern A. Zeeb 	}
8550b4c3e9b5SBjoern A. Zeeb 	cfg->d11inf.io_type = (u8)io_type;
8551b4c3e9b5SBjoern A. Zeeb 	brcmu_d11_attach(&cfg->d11inf);
8552b4c3e9b5SBjoern A. Zeeb 
8553b4c3e9b5SBjoern A. Zeeb 	/* regulatory notifier below needs access to cfg so
8554b4c3e9b5SBjoern A. Zeeb 	 * assign it now.
8555b4c3e9b5SBjoern A. Zeeb 	 */
8556b4c3e9b5SBjoern A. Zeeb 	drvr->config = cfg;
8557b4c3e9b5SBjoern A. Zeeb 
8558b4c3e9b5SBjoern A. Zeeb 	err = brcmf_setup_wiphy(wiphy, ifp);
8559b4c3e9b5SBjoern A. Zeeb 	if (err < 0)
8560b4c3e9b5SBjoern A. Zeeb 		goto priv_out;
8561b4c3e9b5SBjoern A. Zeeb 
8562b4c3e9b5SBjoern A. Zeeb 	brcmf_dbg(INFO, "Registering custom regulatory\n");
8563b4c3e9b5SBjoern A. Zeeb 	wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
8564b4c3e9b5SBjoern A. Zeeb 	wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
8565b4c3e9b5SBjoern A. Zeeb 	wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
8566b4c3e9b5SBjoern A. Zeeb 
8567b4c3e9b5SBjoern A. Zeeb 	/* firmware defaults to 40MHz disabled in 2G band. We signal
8568b4c3e9b5SBjoern A. Zeeb 	 * cfg80211 here that we do and have it decide we can enable
8569b4c3e9b5SBjoern A. Zeeb 	 * it. But first check if device does support 2G operation.
8570b4c3e9b5SBjoern A. Zeeb 	 */
8571b4c3e9b5SBjoern A. Zeeb 	if (wiphy->bands[NL80211_BAND_2GHZ]) {
8572b4c3e9b5SBjoern A. Zeeb 		cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
8573b4c3e9b5SBjoern A. Zeeb 		*cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
8574b4c3e9b5SBjoern A. Zeeb 	}
8575b4c3e9b5SBjoern A. Zeeb #ifdef CONFIG_PM
8576b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
8577b4c3e9b5SBjoern A. Zeeb 		ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
8578b4c3e9b5SBjoern A. Zeeb #endif
8579b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS))
8580b4c3e9b5SBjoern A. Zeeb 		ops->dump_survey = brcmf_cfg80211_dump_survey;
8581b4c3e9b5SBjoern A. Zeeb 
8582b4c3e9b5SBjoern A. Zeeb 	err = wiphy_register(wiphy);
8583b4c3e9b5SBjoern A. Zeeb 	if (err < 0) {
8584b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Could not register wiphy device (%d)\n", err);
8585b4c3e9b5SBjoern A. Zeeb 		goto priv_out;
8586b4c3e9b5SBjoern A. Zeeb 	}
8587b4c3e9b5SBjoern A. Zeeb 
8588b4c3e9b5SBjoern A. Zeeb 	err = brcmf_setup_wiphybands(cfg);
8589b4c3e9b5SBjoern A. Zeeb 	if (err) {
8590b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err);
8591b4c3e9b5SBjoern A. Zeeb 		goto wiphy_unreg_out;
8592b4c3e9b5SBjoern A. Zeeb 	}
8593b4c3e9b5SBjoern A. Zeeb 
8594b4c3e9b5SBjoern A. Zeeb 	/* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
8595b4c3e9b5SBjoern A. Zeeb 	 * setup 40MHz in 2GHz band and enable OBSS scanning.
8596b4c3e9b5SBjoern A. Zeeb 	 */
8597b4c3e9b5SBjoern A. Zeeb 	if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
8598b4c3e9b5SBjoern A. Zeeb 		err = brcmf_enable_bw40_2g(cfg);
8599b4c3e9b5SBjoern A. Zeeb 		if (!err)
8600b4c3e9b5SBjoern A. Zeeb 			err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
8601b4c3e9b5SBjoern A. Zeeb 						      BRCMF_OBSS_COEX_AUTO);
8602b4c3e9b5SBjoern A. Zeeb 		else
8603b4c3e9b5SBjoern A. Zeeb 			*cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
8604b4c3e9b5SBjoern A. Zeeb 	}
8605b4c3e9b5SBjoern A. Zeeb 
8606b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fweh_activate_events(ifp);
8607b4c3e9b5SBjoern A. Zeeb 	if (err) {
8608b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "FWEH activation failed (%d)\n", err);
8609b4c3e9b5SBjoern A. Zeeb 		goto wiphy_unreg_out;
8610b4c3e9b5SBjoern A. Zeeb 	}
8611b4c3e9b5SBjoern A. Zeeb 
8612b4c3e9b5SBjoern A. Zeeb 	err = brcmf_p2p_attach(cfg, p2pdev_forced);
8613b4c3e9b5SBjoern A. Zeeb 	if (err) {
8614b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "P2P initialisation failed (%d)\n", err);
8615b4c3e9b5SBjoern A. Zeeb 		goto wiphy_unreg_out;
8616b4c3e9b5SBjoern A. Zeeb 	}
8617b4c3e9b5SBjoern A. Zeeb 	err = brcmf_btcoex_attach(cfg);
8618b4c3e9b5SBjoern A. Zeeb 	if (err) {
8619b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "BT-coex initialisation failed (%d)\n", err);
8620b4c3e9b5SBjoern A. Zeeb 		brcmf_p2p_detach(&cfg->p2p);
8621b4c3e9b5SBjoern A. Zeeb 		goto wiphy_unreg_out;
8622b4c3e9b5SBjoern A. Zeeb 	}
8623b4c3e9b5SBjoern A. Zeeb 	err = brcmf_pno_attach(cfg);
8624b4c3e9b5SBjoern A. Zeeb 	if (err) {
8625b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "PNO initialisation failed (%d)\n", err);
8626b4c3e9b5SBjoern A. Zeeb 		brcmf_btcoex_detach(cfg);
8627b4c3e9b5SBjoern A. Zeeb 		brcmf_p2p_detach(&cfg->p2p);
8628b4c3e9b5SBjoern A. Zeeb 		goto wiphy_unreg_out;
8629b4c3e9b5SBjoern A. Zeeb 	}
8630b4c3e9b5SBjoern A. Zeeb 
8631b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
8632b4c3e9b5SBjoern A. Zeeb 		err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
8633b4c3e9b5SBjoern A. Zeeb 		if (err) {
8634b4c3e9b5SBjoern A. Zeeb 			brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
8635b4c3e9b5SBjoern A. Zeeb 			wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
8636b4c3e9b5SBjoern A. Zeeb 		} else {
8637b4c3e9b5SBjoern A. Zeeb 			brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
8638b4c3e9b5SBjoern A. Zeeb 					    brcmf_notify_tdls_peer_event);
8639b4c3e9b5SBjoern A. Zeeb 		}
8640b4c3e9b5SBjoern A. Zeeb 	}
8641b4c3e9b5SBjoern A. Zeeb 
8642b4c3e9b5SBjoern A. Zeeb 	/* (re-) activate FWEH event handling */
8643b4c3e9b5SBjoern A. Zeeb 	err = brcmf_fweh_activate_events(ifp);
8644b4c3e9b5SBjoern A. Zeeb 	if (err) {
8645b4c3e9b5SBjoern A. Zeeb 		bphy_err(drvr, "FWEH activation failed (%d)\n", err);
8646b4c3e9b5SBjoern A. Zeeb 		goto detach;
8647b4c3e9b5SBjoern A. Zeeb 	}
8648b4c3e9b5SBjoern A. Zeeb 
8649b4c3e9b5SBjoern A. Zeeb 	/* Fill in some of the advertised nl80211 supported features */
8650b4c3e9b5SBjoern A. Zeeb 	if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
8651b4c3e9b5SBjoern A. Zeeb 		wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
8652b4c3e9b5SBjoern A. Zeeb #ifdef CONFIG_PM
8653b4c3e9b5SBjoern A. Zeeb 		if (wiphy->wowlan &&
8654b4c3e9b5SBjoern A. Zeeb 		    wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
8655b4c3e9b5SBjoern A. Zeeb 			wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
8656b4c3e9b5SBjoern A. Zeeb #endif
8657b4c3e9b5SBjoern A. Zeeb 	}
8658b4c3e9b5SBjoern A. Zeeb 
8659b4c3e9b5SBjoern A. Zeeb 	return cfg;
8660b4c3e9b5SBjoern A. Zeeb 
8661b4c3e9b5SBjoern A. Zeeb detach:
8662b4c3e9b5SBjoern A. Zeeb 	brcmf_pno_detach(cfg);
8663b4c3e9b5SBjoern A. Zeeb 	brcmf_btcoex_detach(cfg);
8664b4c3e9b5SBjoern A. Zeeb 	brcmf_p2p_detach(&cfg->p2p);
8665b4c3e9b5SBjoern A. Zeeb wiphy_unreg_out:
8666b4c3e9b5SBjoern A. Zeeb 	wiphy_unregister(cfg->wiphy);
8667b4c3e9b5SBjoern A. Zeeb priv_out:
8668b4c3e9b5SBjoern A. Zeeb 	wl_deinit_priv(cfg);
8669b4c3e9b5SBjoern A. Zeeb 	brcmf_free_vif(vif);
8670b4c3e9b5SBjoern A. Zeeb 	ifp->vif = NULL;
8671b4c3e9b5SBjoern A. Zeeb wiphy_out:
8672b4c3e9b5SBjoern A. Zeeb 	brcmf_free_wiphy(wiphy);
8673b4c3e9b5SBjoern A. Zeeb 	kfree(cfg);
8674b4c3e9b5SBjoern A. Zeeb 	return NULL;
8675b4c3e9b5SBjoern A. Zeeb }
8676b4c3e9b5SBjoern A. Zeeb 
brcmf_cfg80211_detach(struct brcmf_cfg80211_info * cfg)8677b4c3e9b5SBjoern A. Zeeb void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
8678b4c3e9b5SBjoern A. Zeeb {
8679b4c3e9b5SBjoern A. Zeeb 	if (!cfg)
8680b4c3e9b5SBjoern A. Zeeb 		return;
8681b4c3e9b5SBjoern A. Zeeb 
8682b4c3e9b5SBjoern A. Zeeb 	brcmf_pno_detach(cfg);
8683b4c3e9b5SBjoern A. Zeeb 	brcmf_btcoex_detach(cfg);
8684b4c3e9b5SBjoern A. Zeeb 	wiphy_unregister(cfg->wiphy);
8685b4c3e9b5SBjoern A. Zeeb 	wl_deinit_priv(cfg);
8686b4c3e9b5SBjoern A. Zeeb 	cancel_work_sync(&cfg->escan_timeout_work);
8687b4c3e9b5SBjoern A. Zeeb 	brcmf_free_wiphy(cfg->wiphy);
8688b4c3e9b5SBjoern A. Zeeb 	kfree(cfg);
8689b4c3e9b5SBjoern A. Zeeb }
8690