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, ¶ms);
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, ¶ms);
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(¶ms_le->ssid_le, ¶ms_v2_le->ssid_le,
1121b4c3e9b5SBjoern A. Zeeb sizeof(params_le->ssid_le));
1122b4c3e9b5SBjoern A. Zeeb memcpy(¶ms_le->bssid, ¶ms_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(¶ms_le->channel_list[0],
1144b4c3e9b5SBjoern A. Zeeb ¶ms_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(¶ms_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, ¶ms_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 ¶ms_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(¶ms_v2_le, ¶ms_le);
1270b4c3e9b5SBjoern A. Zeeb err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
1271b4c3e9b5SBjoern A. Zeeb ¶ms_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, ¶ms->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(¶ms->params_v2_le, ¶ms_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 ¶ms->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(¶ms, 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, ¶ms);
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", >k_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