blob: a5db9531de5d1e47fb7fd1471e122834729d1c43 [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
18
19#include <linux/kernel.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <linux/etherdevice.h>
Hante Meuleman68ca3952014-02-25 20:30:26 +010021#include <linux/module.h>
Franky Lin1bacb042014-06-21 12:11:16 +020022#include <linux/vmalloc.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020024#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020025
26#include <brcmu_utils.h>
27#include <defs.h>
28#include <brcmu_wifi.h>
Hante Meuleman122d3d02014-10-28 14:56:18 +010029#include "core.h"
Hante Meulemana8e8ed32014-10-28 14:56:13 +010030#include "debug.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020031#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010032#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010033#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020034#include "btcoex.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010035#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020036#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070037#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020038#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020039#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010040#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010041#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020042
Arend van Spriele5806072012-09-19 22:21:08 +020043#define BRCMF_SCAN_IE_LEN_MAX 2048
44#define BRCMF_PNO_VERSION 2
45#define BRCMF_PNO_TIME 30
46#define BRCMF_PNO_REPEAT 4
47#define BRCMF_PNO_FREQ_EXPO_MAX 3
48#define BRCMF_PNO_MAX_PFN_COUNT 16
49#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
50#define BRCMF_PNO_HIDDEN_BIT 2
51#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
52#define BRCMF_PNO_SCAN_COMPLETE 1
53#define BRCMF_PNO_SCAN_INCOMPLETE 0
54
Hante Meuleman1a873342012-09-27 14:17:54 +020055#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010059#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020060
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
Hante Meuleman240d61a2016-02-17 11:27:10 +010075#define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */
76#define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */
Hante Meuleman1a873342012-09-27 14:17:54 +020077#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
Hante Meuleman240d61a2016-02-17 11:27:10 +010078#define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3))
79#define RSN_CAP_MFPR_MASK BIT(6)
80#define RSN_CAP_MFPC_MASK BIT(7)
81#define RSN_PMKID_COUNT_LEN 2
Hante Meuleman1a873342012-09-27 14:17:54 +020082
83#define VNDR_IE_CMD_LEN 4 /* length of the set command
84 * string :"add", "del" (+ NUL)
85 */
86#define VNDR_IE_COUNT_OFFSET 4
87#define VNDR_IE_PKTFLAG_OFFSET 8
88#define VNDR_IE_VSIE_OFFSET 12
89#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010090#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020091
92#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
93#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020094
Hante Meuleman89286dc2013-02-08 15:53:46 +010095#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
96#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
97#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
98
Hante Meuleman1678ba82015-12-10 13:43:00 +010099#define BRCMF_SCAN_CHANNEL_TIME 40
100#define BRCMF_SCAN_UNASSOC_TIME 40
101#define BRCMF_SCAN_PASSIVE_TIME 120
102
Hante Meuleman3021ad92016-01-05 11:05:45 +0100103#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)
104
Arend van Spriel5b435de2011-10-05 13:19:03 +0200105#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
106 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
107
Arend van Sprielce81e312012-10-22 13:55:37 -0700108static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200109{
Arend van Sprielc1179032012-10-22 13:55:33 -0700110 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100111 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
112 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200113 return false;
114 }
115 return true;
116}
117
Arend van Spriel5b435de2011-10-05 13:19:03 +0200118#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
119#define RATETAB_ENT(_rateid, _flags) \
120 { \
121 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
122 .hw_value = (_rateid), \
123 .flags = (_flags), \
124 }
125
126static struct ieee80211_rate __wl_rates[] = {
127 RATETAB_ENT(BRCM_RATE_1M, 0),
128 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
129 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
130 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
131 RATETAB_ENT(BRCM_RATE_6M, 0),
132 RATETAB_ENT(BRCM_RATE_9M, 0),
133 RATETAB_ENT(BRCM_RATE_12M, 0),
134 RATETAB_ENT(BRCM_RATE_18M, 0),
135 RATETAB_ENT(BRCM_RATE_24M, 0),
136 RATETAB_ENT(BRCM_RATE_36M, 0),
137 RATETAB_ENT(BRCM_RATE_48M, 0),
138 RATETAB_ENT(BRCM_RATE_54M, 0),
139};
140
Arend van Spriel5b435de2011-10-05 13:19:03 +0200141#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200142#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
143#define wl_a_rates (__wl_rates + 4)
144#define wl_a_rates_size (wl_g_rates_size - 4)
145
146#define CHAN2G(_channel, _freq) { \
Johannes Berg57fbcce2016-04-12 15:56:15 +0200147 .band = NL80211_BAND_2GHZ, \
Arend van Spriel58de92d2015-04-14 20:10:24 +0200148 .center_freq = (_freq), \
149 .hw_value = (_channel), \
150 .flags = IEEE80211_CHAN_DISABLED, \
151 .max_antenna_gain = 0, \
152 .max_power = 30, \
153}
154
155#define CHAN5G(_channel) { \
Johannes Berg57fbcce2016-04-12 15:56:15 +0200156 .band = NL80211_BAND_5GHZ, \
Arend van Spriel58de92d2015-04-14 20:10:24 +0200157 .center_freq = 5000 + (5 * (_channel)), \
158 .hw_value = (_channel), \
159 .flags = IEEE80211_CHAN_DISABLED, \
160 .max_antenna_gain = 0, \
161 .max_power = 30, \
162}
163
164static struct ieee80211_channel __wl_2ghz_channels[] = {
165 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
166 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
167 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
168 CHAN2G(13, 2472), CHAN2G(14, 2484)
169};
170
171static struct ieee80211_channel __wl_5ghz_channels[] = {
172 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
173 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
174 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
175 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
176 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
177 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
178};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200179
Arend van Sprielb48d8912014-07-12 08:49:41 +0200180/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200181 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200182 */
183static const struct ieee80211_supported_band __wl_band_2ghz = {
Johannes Berg57fbcce2016-04-12 15:56:15 +0200184 .band = NL80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200185 .bitrates = wl_g_rates,
186 .n_bitrates = wl_g_rates_size,
187};
188
Arend van Spriel58de92d2015-04-14 20:10:24 +0200189static const struct ieee80211_supported_band __wl_band_5ghz = {
Johannes Berg57fbcce2016-04-12 15:56:15 +0200190 .band = NL80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200191 .bitrates = wl_a_rates,
192 .n_bitrates = wl_a_rates_size,
193};
194
Hante Meulemand48200b2013-04-03 12:40:29 +0200195/* This is to override regulatory domains defined in cfg80211 module (reg.c)
196 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200197 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
198 * With respect to these flags, wpa_supplicant doesn't * start p2p
199 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200200 * domain are to be done here.
201 */
202static const struct ieee80211_regdomain brcmf_regdom = {
203 .n_reg_rules = 4,
204 .alpha2 = "99",
205 .reg_rules = {
206 /* IEEE 802.11b/g, channels 1..11 */
207 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
208 /* If any */
209 /* IEEE 802.11 channel 14 - Only JP enables
210 * this and for 802.11b only
211 */
212 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
213 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200214 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200215 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200216 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200217};
218
Hante Meuleman240d61a2016-02-17 11:27:10 +0100219/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
220 * are supported. A pointer to this array and the number of entries is passed
221 * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
222 * So the cipher suite AES_CMAC has to be the last one in the array, and when
223 * device does not support MFP then the number of suites will be decreased by 1
224 */
225static const u32 brcmf_cipher_suites[] = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200226 WLAN_CIPHER_SUITE_WEP40,
227 WLAN_CIPHER_SUITE_WEP104,
228 WLAN_CIPHER_SUITE_TKIP,
229 WLAN_CIPHER_SUITE_CCMP,
Hante Meuleman240d61a2016-02-17 11:27:10 +0100230 /* Keep as last entry: */
231 WLAN_CIPHER_SUITE_AES_CMAC
Arend van Spriel5b435de2011-10-05 13:19:03 +0200232};
233
Hante Meuleman1a873342012-09-27 14:17:54 +0200234/* Vendor specific ie. id = 221, oui and type defines exact ie */
235struct brcmf_vs_tlv {
236 u8 id;
237 u8 len;
238 u8 oui[3];
239 u8 oui_type;
240};
241
242struct parsed_vndr_ie_info {
243 u8 *ie_ptr;
244 u32 ie_len; /* total length including id & length field */
245 struct brcmf_vs_tlv vndrie;
246};
247
248struct parsed_vndr_ies {
249 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100250 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200251};
252
Arend van Spriel7705ba62016-04-17 16:44:58 +0200253static u8 nl80211_band_to_fwil(enum nl80211_band band)
254{
255 switch (band) {
256 case NL80211_BAND_2GHZ:
257 return WLC_BAND_2G;
258 case NL80211_BAND_5GHZ:
259 return WLC_BAND_5G;
260 default:
261 WARN_ON(1);
262 break;
263 }
264 return 0;
265}
266
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200267static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
268 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200269{
270 struct brcmu_chan ch_inf;
271 s32 primary_offset;
272
273 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
274 ch->chan->center_freq, ch->center_freq1, ch->width);
275 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
Rafał Miłecki36e80722016-01-20 16:46:04 +0100276 primary_offset = ch->chan->center_freq - ch->center_freq1;
Arend van Spriel600a8972014-05-12 10:47:39 +0200277 switch (ch->width) {
278 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100279 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200280 ch_inf.bw = BRCMU_CHAN_BW_20;
281 WARN_ON(primary_offset != 0);
282 break;
283 case NL80211_CHAN_WIDTH_40:
284 ch_inf.bw = BRCMU_CHAN_BW_40;
Rafał Miłecki36e80722016-01-20 16:46:04 +0100285 if (primary_offset > 0)
Arend van Spriel600a8972014-05-12 10:47:39 +0200286 ch_inf.sb = BRCMU_CHAN_SB_U;
287 else
288 ch_inf.sb = BRCMU_CHAN_SB_L;
289 break;
290 case NL80211_CHAN_WIDTH_80:
291 ch_inf.bw = BRCMU_CHAN_BW_80;
Rafał Miłecki36e80722016-01-20 16:46:04 +0100292 if (primary_offset == -30)
293 ch_inf.sb = BRCMU_CHAN_SB_LL;
294 else if (primary_offset == -10)
295 ch_inf.sb = BRCMU_CHAN_SB_LU;
296 else if (primary_offset == 10)
297 ch_inf.sb = BRCMU_CHAN_SB_UL;
298 else
299 ch_inf.sb = BRCMU_CHAN_SB_UU;
Arend van Spriel600a8972014-05-12 10:47:39 +0200300 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100301 case NL80211_CHAN_WIDTH_80P80:
302 case NL80211_CHAN_WIDTH_160:
303 case NL80211_CHAN_WIDTH_5:
304 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200305 default:
306 WARN_ON_ONCE(1);
307 }
308 switch (ch->chan->band) {
Johannes Berg57fbcce2016-04-12 15:56:15 +0200309 case NL80211_BAND_2GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200310 ch_inf.band = BRCMU_CHAN_BAND_2G;
311 break;
Johannes Berg57fbcce2016-04-12 15:56:15 +0200312 case NL80211_BAND_5GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200313 ch_inf.band = BRCMU_CHAN_BAND_5G;
314 break;
Johannes Berg57fbcce2016-04-12 15:56:15 +0200315 case NL80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200316 default:
317 WARN_ON_ONCE(1);
318 }
319 d11inf->encchspec(&ch_inf);
320
321 return ch_inf.chspec;
322}
323
Franky Lin83cf17a2013-04-11 13:28:50 +0200324u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
325 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700326{
Franky Lin83cf17a2013-04-11 13:28:50 +0200327 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700328
Franky Lin83cf17a2013-04-11 13:28:50 +0200329 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
330 ch_inf.bw = BRCMU_CHAN_BW_20;
331 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700332
Franky Lin83cf17a2013-04-11 13:28:50 +0200333 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700334}
335
Hante Meuleman89286dc2013-02-08 15:53:46 +0100336/* Traverse a string of 1-byte tag/1-byte length/variable-length value
337 * triples, returning a pointer to the substring whose first element
338 * matches tag
339 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100340const struct brcmf_tlv *
341brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100342{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100343 const struct brcmf_tlv *elt = buf;
344 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100345
346 /* find tagged parameter */
347 while (totlen >= TLV_HDR_LEN) {
348 int len = elt->len;
349
350 /* validate remaining totlen */
351 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
352 return elt;
353
354 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
355 totlen -= (len + TLV_HDR_LEN);
356 }
357
358 return NULL;
359}
360
361/* Is any of the tlvs the expected entry? If
362 * not update the tlvs buffer pointer/length.
363 */
364static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100365brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
366 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100367{
368 /* If the contents match the OUI and the type */
369 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
370 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
371 type == ie[TLV_BODY_OFF + oui_len]) {
372 return true;
373 }
374
375 if (tlvs == NULL)
376 return false;
377 /* point to the next ie */
378 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
379 /* calculate the length of the rest of the buffer */
380 *tlvs_len -= (int)(ie - *tlvs);
381 /* update the pointer to the start of the buffer */
382 *tlvs = ie;
383
384 return false;
385}
386
387static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100388brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100389{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100390 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100391
392 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100393 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100394 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
395 return (struct brcmf_vs_tlv *)ie;
396 }
397 return NULL;
398}
399
400static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100401brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100402{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100403 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100404
405 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
406 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
407 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
408 return (struct brcmf_vs_tlv *)ie;
409 }
410 return NULL;
411}
412
Arend van Spriel39504a22015-08-20 22:06:05 +0200413static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
414 struct brcmf_cfg80211_vif *vif,
415 enum nl80211_iftype new_type)
416{
417 int iftype_num[NUM_NL80211_IFTYPES];
418 struct brcmf_cfg80211_vif *pos;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100419 bool check_combos = false;
420 int ret = 0;
Arend van Spriel39504a22015-08-20 22:06:05 +0200421
422 memset(&iftype_num[0], 0, sizeof(iftype_num));
423 list_for_each_entry(pos, &cfg->vif_list, list)
Arend van Spriel353c46a2015-12-10 13:43:06 +0100424 if (pos == vif) {
Arend van Spriel39504a22015-08-20 22:06:05 +0200425 iftype_num[new_type]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100426 } else {
427 /* concurrent interfaces so need check combinations */
428 check_combos = true;
Arend van Spriel39504a22015-08-20 22:06:05 +0200429 iftype_num[pos->wdev.iftype]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100430 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200431
Arend van Spriel353c46a2015-12-10 13:43:06 +0100432 if (check_combos)
433 ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
434
435 return ret;
Arend van Spriel39504a22015-08-20 22:06:05 +0200436}
437
438static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
439 enum nl80211_iftype new_type)
440{
441 int iftype_num[NUM_NL80211_IFTYPES];
442 struct brcmf_cfg80211_vif *pos;
443
444 memset(&iftype_num[0], 0, sizeof(iftype_num));
445 list_for_each_entry(pos, &cfg->vif_list, list)
446 iftype_num[pos->wdev.iftype]++;
447
448 iftype_num[new_type]++;
449 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
450}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100451
Arend van Spriel5b435de2011-10-05 13:19:03 +0200452static void convert_key_from_CPU(struct brcmf_wsec_key *key,
453 struct brcmf_wsec_key_le *key_le)
454{
455 key_le->index = cpu_to_le32(key->index);
456 key_le->len = cpu_to_le32(key->len);
457 key_le->algo = cpu_to_le32(key->algo);
458 key_le->flags = cpu_to_le32(key->flags);
459 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
460 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
461 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
462 memcpy(key_le->data, key->data, sizeof(key->data));
463 memcpy(key_le->ea, key->ea, sizeof(key->ea));
464}
465
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200466static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100467send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200468{
469 int err;
470 struct brcmf_wsec_key_le key_le;
471
472 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200473
Hante Meuleman118eb302014-12-21 12:43:49 +0100474 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700475
Hante Meuleman118eb302014-12-21 12:43:49 +0100476 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700477 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200478
Arend van Spriel5b435de2011-10-05 13:19:03 +0200479 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100480 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200481 return err;
482}
483
Hante Meulemanb3657452013-05-27 21:09:53 +0200484static s32
Franky Lin52f22fb2016-02-17 11:26:55 +0100485brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
Hante Meulemanb3657452013-05-27 21:09:53 +0200486{
487 s32 err;
488 u32 mode;
489
490 if (enable)
491 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
492 else
493 mode = 0;
494
495 /* Try to set and enable ARP offload feature, this may fail, then it */
496 /* is simply not supported and err 0 will be returned */
497 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
498 if (err) {
499 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
500 mode, err);
501 err = 0;
502 } else {
503 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
504 if (err) {
505 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
506 enable, err);
507 err = 0;
508 } else
509 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
510 enable, mode);
511 }
512
Franky Lin52f22fb2016-02-17 11:26:55 +0100513 err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
514 if (err) {
515 brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
516 enable, err);
517 err = 0;
518 } else
519 brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
520 enable, mode);
521
Hante Meulemanb3657452013-05-27 21:09:53 +0200522 return err;
523}
524
Hante Meuleman8851cce2014-07-30 13:20:02 +0200525static void
526brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
527{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200528 struct brcmf_cfg80211_vif *vif;
529 struct brcmf_if *ifp;
530
531 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
532 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200533
534 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
535 (wdev->iftype == NL80211_IFTYPE_AP) ||
536 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
537 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
538 ADDR_DIRECT);
539 else
540 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
541 ADDR_INDIRECT);
542}
543
Rafał Miłeckid02fb8f2016-05-26 01:44:27 +0200544static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
545{
546 int bsscfgidx;
547
548 for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {
549 /* bsscfgidx 1 is reserved for legacy P2P */
550 if (bsscfgidx == 1)
551 continue;
552 if (!drvr->iflist[bsscfgidx])
553 return bsscfgidx;
554 }
555
556 return -ENOMEM;
557}
558
Hante Meulemana44aa402014-12-03 21:05:33 +0100559static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
560{
561 struct brcmf_mbss_ssid_le mbss_ssid_le;
562 int bsscfgidx;
563 int err;
564
565 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
Rafał Miłeckid02fb8f2016-05-26 01:44:27 +0200566 bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);
Hante Meulemana44aa402014-12-03 21:05:33 +0100567 if (bsscfgidx < 0)
568 return bsscfgidx;
569
570 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
571 mbss_ssid_le.SSID_len = cpu_to_le32(5);
572 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
573
574 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
575 sizeof(mbss_ssid_le));
576 if (err < 0)
577 brcmf_err("setting ssid failed %d\n", err);
578
579 return err;
580}
581
582/**
583 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
584 *
585 * @wiphy: wiphy device of new interface.
586 * @name: name of the new interface.
587 * @flags: not used.
588 * @params: contains mac address for AP device.
589 */
590static
591struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
592 u32 *flags, struct vif_params *params)
593{
594 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
595 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
596 struct brcmf_cfg80211_vif *vif;
597 int err;
598
599 if (brcmf_cfg80211_vif_event_armed(cfg))
600 return ERR_PTR(-EBUSY);
601
602 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
603
Rafał Miłecki26072332016-06-06 23:03:55 +0200604 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100605 if (IS_ERR(vif))
606 return (struct wireless_dev *)vif;
607
608 brcmf_cfg80211_arm_vif_event(cfg, vif);
609
610 err = brcmf_cfg80211_request_ap_if(ifp);
611 if (err) {
612 brcmf_cfg80211_arm_vif_event(cfg, NULL);
613 goto fail;
614 }
615
616 /* wait for firmware event */
Arend van Spriela9eb0c42016-02-17 11:26:50 +0100617 err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
618 BRCMF_VIF_EVENT_TIMEOUT);
Hante Meulemana44aa402014-12-03 21:05:33 +0100619 brcmf_cfg80211_arm_vif_event(cfg, NULL);
620 if (!err) {
621 brcmf_err("timeout occurred\n");
622 err = -EIO;
623 goto fail;
624 }
625
626 /* interface created in firmware */
627 ifp = vif->ifp;
628 if (!ifp) {
629 brcmf_err("no if pointer provided\n");
630 err = -ENOENT;
631 goto fail;
632 }
633
634 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
635 err = brcmf_net_attach(ifp, true);
636 if (err) {
637 brcmf_err("Registering netdevice failed\n");
638 goto fail;
639 }
640
641 return &ifp->vif->wdev;
642
643fail:
644 brcmf_free_vif(vif);
645 return ERR_PTR(err);
646}
647
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100648static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
649{
650 enum nl80211_iftype iftype;
651
652 iftype = vif->wdev.iftype;
653 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
654}
655
656static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
657{
658 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
659}
660
Arend van Spriel9f440b72013-02-08 15:53:36 +0100661static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
662 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100663 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100664 enum nl80211_iftype type,
665 u32 *flags,
666 struct vif_params *params)
667{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200668 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200669 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200670
Arend van Spriel9f440b72013-02-08 15:53:36 +0100671 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200672 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
673 if (err) {
674 brcmf_err("iface validation failed: err=%d\n", err);
675 return ERR_PTR(err);
676 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100677 switch (type) {
678 case NL80211_IFTYPE_ADHOC:
679 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100680 case NL80211_IFTYPE_AP_VLAN:
681 case NL80211_IFTYPE_WDS:
682 case NL80211_IFTYPE_MONITOR:
683 case NL80211_IFTYPE_MESH_POINT:
684 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100685 case NL80211_IFTYPE_AP:
686 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
Rafał Miłecki0cd33c22016-05-27 10:54:28 +0200687 break;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100688 case NL80211_IFTYPE_P2P_CLIENT:
689 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200690 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100691 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Rafał Miłecki0cd33c22016-05-27 10:54:28 +0200692 break;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100693 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100694 default:
695 return ERR_PTR(-EINVAL);
696 }
Rafał Miłecki0cd33c22016-05-27 10:54:28 +0200697
698 if (IS_ERR(wdev))
699 brcmf_err("add iface %s type %d failed: err=%d\n",
700 name, type, (int)PTR_ERR(wdev));
701 else
702 brcmf_cfg80211_update_proto_addr_mode(wdev);
703
704 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100705}
706
Daniel Kim5e787f72014-06-21 12:11:18 +0200707static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
708{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200709 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200710 brcmf_set_mpc(ifp, mpc);
711}
712
Arend van Sprielf96aa072013-04-05 10:57:48 +0200713void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100714{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100715 s32 err = 0;
716
717 if (check_vif_up(ifp->vif)) {
718 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
719 if (err) {
720 brcmf_err("fail to set mpc\n");
721 return;
722 }
723 brcmf_dbg(INFO, "MPC : %d\n", mpc);
724 }
725}
726
Arend van Spriela0f472a2013-04-05 10:57:49 +0200727s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
728 struct brcmf_if *ifp, bool aborted,
729 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100730{
731 struct brcmf_scan_params_le params_le;
732 struct cfg80211_scan_request *scan_request;
733 s32 err = 0;
734
735 brcmf_dbg(SCAN, "Enter\n");
736
737 /* clear scan request, because the FW abort can cause a second call */
738 /* to this functon and might cause a double cfg80211_scan_done */
739 scan_request = cfg->scan_request;
740 cfg->scan_request = NULL;
741
742 if (timer_pending(&cfg->escan_timeout))
743 del_timer_sync(&cfg->escan_timeout);
744
745 if (fw_abort) {
746 /* Do a scan abort to stop the driver's scan engine */
747 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
748 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800749 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100750 params_le.bss_type = DOT11_BSSTYPE_ANY;
751 params_le.scan_type = 0;
752 params_le.channel_num = cpu_to_le32(1);
753 params_le.nprobes = cpu_to_le32(1);
754 params_le.active_time = cpu_to_le32(-1);
755 params_le.passive_time = cpu_to_le32(-1);
756 params_le.home_time = cpu_to_le32(-1);
757 /* Scan is aborted by setting channel_list[0] to -1 */
758 params_le.channel_list[0] = cpu_to_le16(-1);
759 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200760 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100761 &params_le, sizeof(params_le));
762 if (err)
763 brcmf_err("Scan abort failed\n");
764 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200765
Daniel Kim5e787f72014-06-21 12:11:18 +0200766 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200767
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100768 /*
769 * e-scan can be initiated by scheduled scan
770 * which takes precedence.
771 */
772 if (cfg->sched_escan) {
773 brcmf_dbg(SCAN, "scheduled scan completed\n");
774 cfg->sched_escan = false;
775 if (!aborted)
776 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100777 } else if (scan_request) {
778 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
779 aborted ? "Aborted" : "Done");
780 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100781 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100782 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
783 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100784
785 return err;
786}
787
Arend van Spriel9f440b72013-02-08 15:53:36 +0100788static
789int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
790{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100791 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
792 struct net_device *ndev = wdev->netdev;
793
794 /* vif event pending in firmware */
795 if (brcmf_cfg80211_vif_event_armed(cfg))
796 return -EBUSY;
797
798 if (ndev) {
799 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200800 cfg->escan_info.ifp == netdev_priv(ndev))
801 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
802 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100803
804 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
805 }
806
Arend van Spriel9f440b72013-02-08 15:53:36 +0100807 switch (wdev->iftype) {
808 case NL80211_IFTYPE_ADHOC:
809 case NL80211_IFTYPE_STATION:
810 case NL80211_IFTYPE_AP:
811 case NL80211_IFTYPE_AP_VLAN:
812 case NL80211_IFTYPE_WDS:
813 case NL80211_IFTYPE_MONITOR:
814 case NL80211_IFTYPE_MESH_POINT:
815 return -EOPNOTSUPP;
816 case NL80211_IFTYPE_P2P_CLIENT:
817 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200818 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100819 return brcmf_p2p_del_vif(wiphy, wdev);
820 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100821 default:
822 return -EINVAL;
823 }
824 return -EOPNOTSUPP;
825}
826
Arend van Spriel5b435de2011-10-05 13:19:03 +0200827static s32
828brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
829 enum nl80211_iftype type, u32 *flags,
830 struct vif_params *params)
831{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100832 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700833 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100834 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200835 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200836 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200837 s32 err = 0;
838
Hante Meuleman37a869e2015-10-29 20:33:17 +0100839 brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
840 type);
Hante Meuleman178e9ef2015-09-18 22:08:11 +0200841
842 /* WAR: There are a number of p2p interface related problems which
843 * need to be handled initially (before doing the validate).
844 * wpa_supplicant tends to do iface changes on p2p device/client/go
845 * which are not always possible/allowed. However we need to return
846 * OK otherwise the wpa_supplicant wont start. The situation differs
847 * on configuration and setup (p2pon=1 module param). The first check
848 * is to see if the request is a change to station for p2p iface.
849 */
850 if ((type == NL80211_IFTYPE_STATION) &&
851 ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
852 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
853 (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
854 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
855 /* Now depending on whether module param p2pon=1 was used the
856 * response needs to be either 0 or EOPNOTSUPP. The reason is
857 * that if p2pon=1 is used, but a newer supplicant is used then
858 * we should return an error, as this combination wont work.
859 * In other situations 0 is returned and supplicant will start
860 * normally. It will give a trace in cfg80211, but it is the
861 * only way to get it working. Unfortunately this will result
862 * in situation where we wont support new supplicant in
863 * combination with module param p2pon=1, but that is the way
864 * it is. If the user tries this then unloading of driver might
865 * fail/lock.
866 */
867 if (cfg->p2p.p2pdev_dynamically)
868 return -EOPNOTSUPP;
869 else
870 return 0;
871 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200872 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
873 if (err) {
874 brcmf_err("iface validation failed: err=%d\n", err);
875 return err;
876 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200877 switch (type) {
878 case NL80211_IFTYPE_MONITOR:
879 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100880 brcmf_err("type (%d) : currently we do not support this type\n",
881 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200882 return -EOPNOTSUPP;
883 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200884 infra = 0;
885 break;
886 case NL80211_IFTYPE_STATION:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200887 infra = 1;
888 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200889 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100890 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200891 ap = 1;
892 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200893 default:
894 err = -EINVAL;
895 goto done;
896 }
897
Hante Meuleman1a873342012-09-27 14:17:54 +0200898 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100899 if (type == NL80211_IFTYPE_P2P_GO) {
900 brcmf_dbg(INFO, "IF Type = P2P GO\n");
901 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
902 }
903 if (!err) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100904 brcmf_dbg(INFO, "IF Type = AP\n");
905 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200906 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100907 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200908 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100909 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200910 err = -EAGAIN;
911 goto done;
912 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100913 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100914 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200915 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200916 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200917
Hante Meuleman8851cce2014-07-30 13:20:02 +0200918 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
919
Arend van Spriel5b435de2011-10-05 13:19:03 +0200920done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100921 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200922
923 return err;
924}
925
Franky Lin83cf17a2013-04-11 13:28:50 +0200926static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
927 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200928 struct cfg80211_scan_request *request)
929{
930 u32 n_ssids;
931 u32 n_channels;
932 s32 i;
933 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200934 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200935 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200936 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200937
Joe Perches93803b32015-03-02 19:54:49 -0800938 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200939 params_le->bss_type = DOT11_BSSTYPE_ANY;
940 params_le->scan_type = 0;
941 params_le->channel_num = 0;
942 params_le->nprobes = cpu_to_le32(-1);
943 params_le->active_time = cpu_to_le32(-1);
944 params_le->passive_time = cpu_to_le32(-1);
945 params_le->home_time = cpu_to_le32(-1);
946 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
947
948 /* if request is null exit so it will be all channel broadcast scan */
949 if (!request)
950 return;
951
952 n_ssids = request->n_ssids;
953 n_channels = request->n_channels;
954 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100955 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
956 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200957 if (n_channels > 0) {
958 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200959 chanspec = channel_to_chanspec(&cfg->d11inf,
960 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100961 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
962 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200963 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200964 }
965 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100966 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200967 }
968 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100969 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200970 if (n_ssids > 0) {
971 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
972 n_channels * sizeof(u16);
973 offset = roundup(offset, sizeof(u32));
974 ptr = (char *)params_le + offset;
975 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200976 memset(&ssid_le, 0, sizeof(ssid_le));
977 ssid_le.SSID_len =
978 cpu_to_le32(request->ssids[i].ssid_len);
979 memcpy(ssid_le.SSID, request->ssids[i].ssid,
980 request->ssids[i].ssid_len);
981 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100982 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200983 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100984 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
985 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200986 memcpy(ptr, &ssid_le, sizeof(ssid_le));
987 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200988 }
989 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100990 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200991 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100992 brcmf_dbg(SCAN, "SSID %s len=%d\n",
993 params_le->ssid_le.SSID,
994 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200995 params_le->ssid_le.SSID_len =
996 cpu_to_le32(request->ssids->ssid_len);
997 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
998 request->ssids->ssid_len);
999 }
1000 }
1001 /* Adding mask to channel numbers */
1002 params_le->channel_num =
1003 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
1004 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
1005}
1006
1007static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001008brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemanc4958102015-11-25 11:32:41 +01001009 struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001010{
1011 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
1012 offsetof(struct brcmf_escan_params_le, params_le);
1013 struct brcmf_escan_params_le *params;
1014 s32 err = 0;
1015
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001016 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001017
1018 if (request != NULL) {
1019 /* Allocate space for populating ssids in struct */
1020 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
1021
1022 /* Allocate space for populating ssids in struct */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001023 params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
Hante Meulemane756af52012-09-11 21:18:52 +02001024 }
1025
1026 params = kzalloc(params_size, GFP_KERNEL);
1027 if (!params) {
1028 err = -ENOMEM;
1029 goto exit;
1030 }
1031 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +02001032 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001033 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
Hante Meulemanc4958102015-11-25 11:32:41 +01001034 params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001035 params->sync_id = cpu_to_le16(0x1234);
1036
Arend van Spriela0f472a2013-04-05 10:57:49 +02001037 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +02001038 if (err) {
1039 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001040 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001041 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001042 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001043 }
1044
1045 kfree(params);
1046exit:
1047 return err;
1048}
1049
1050static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001051brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +02001052 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001053{
1054 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001055 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001056 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001057 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001058
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001059 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001060 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001061 escan->wiphy = wiphy;
1062 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001063 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001064 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001065 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001066 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001067 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001068 return err;
1069 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001070 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001071 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001072 results->version = 0;
1073 results->count = 0;
1074 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1075
Hante Meulemanc4958102015-11-25 11:32:41 +01001076 err = escan->run(cfg, ifp, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001077 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001078 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001079 return err;
1080}
1081
1082static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001083brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001084 struct cfg80211_scan_request *request,
1085 struct cfg80211_ssid *this_ssid)
1086{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001087 struct brcmf_if *ifp = vif->ifp;
1088 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001089 struct cfg80211_ssid *ssids;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001090 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001091 bool escan_req;
1092 bool spec_scan;
1093 s32 err;
Hante Meuleman675f5d82015-12-10 13:43:01 +01001094 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +02001095 u32 SSID_len;
1096
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001097 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001098
Arend van Sprielc1179032012-10-22 13:55:33 -07001099 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001100 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001101 return -EAGAIN;
1102 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001103 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001104 brcmf_err("Scanning being aborted: status (%lu)\n",
1105 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001106 return -EAGAIN;
1107 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001108 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1109 brcmf_err("Scanning suppressed: status (%lu)\n",
1110 cfg->scan_status);
1111 return -EAGAIN;
1112 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001113 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001114 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001115 return -EAGAIN;
1116 }
1117
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001118 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001119 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1120 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001121
Hante Meulemane756af52012-09-11 21:18:52 +02001122 escan_req = false;
1123 if (request) {
1124 /* scan bss */
1125 ssids = request->ssids;
1126 escan_req = true;
1127 } else {
1128 /* scan in ibss */
1129 /* we don't do escan in ibss */
1130 ssids = this_ssid;
1131 }
1132
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001133 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001134 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001135 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001136 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001137 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001138 if (err)
1139 goto scan_out;
1140
Arend van Spriela0f472a2013-04-05 10:57:49 +02001141 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001142 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001143 goto scan_out;
1144 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001145 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1146 ssids->ssid, ssids->ssid_len);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001147 memset(&ssid_le, 0, sizeof(ssid_le));
1148 SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
1149 ssid_le.SSID_len = cpu_to_le32(0);
Hante Meulemane756af52012-09-11 21:18:52 +02001150 spec_scan = false;
1151 if (SSID_len) {
Hante Meuleman675f5d82015-12-10 13:43:01 +01001152 memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
1153 ssid_le.SSID_len = cpu_to_le32(SSID_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001154 spec_scan = true;
1155 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001156 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001157
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001158 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001159 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001160 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001161 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001162 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001163 goto scan_out;
1164 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001165 brcmf_scan_config_mpc(ifp, 0);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001166 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
1167 sizeof(ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001168 if (err) {
1169 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001170 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
Hante Meuleman675f5d82015-12-10 13:43:01 +01001171 ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001172 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001173 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001174
Daniel Kim5e787f72014-06-21 12:11:18 +02001175 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001176 goto scan_out;
1177 }
1178 }
1179
Hante Meuleman661fa952015-02-06 18:36:47 +01001180 /* Arm scan timeout timer */
1181 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemand5367332016-02-17 11:26:51 +01001182 BRCMF_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
Hante Meuleman661fa952015-02-06 18:36:47 +01001183
Hante Meulemane756af52012-09-11 21:18:52 +02001184 return 0;
1185
1186scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001187 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001188 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001189 return err;
1190}
1191
Arend van Spriel5b435de2011-10-05 13:19:03 +02001192static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001193brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001194{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001195 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001196 s32 err = 0;
1197
Arend van Sprield96b8012012-12-05 15:26:02 +01001198 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001199 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1200 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001201 return -EIO;
1202
Arend van Spriela0f472a2013-04-05 10:57:49 +02001203 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001204
Arend van Spriel5b435de2011-10-05 13:19:03 +02001205 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001206 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001207
Arend van Sprield96b8012012-12-05 15:26:02 +01001208 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001209 return err;
1210}
1211
1212static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1213{
1214 s32 err = 0;
1215
Arend van Sprielac24be62012-10-22 10:36:23 -07001216 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1217 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001218 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001219 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001220
1221 return err;
1222}
1223
1224static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1225{
1226 s32 err = 0;
1227
Arend van Sprielac24be62012-10-22 10:36:23 -07001228 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1229 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001230 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001231 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001232
1233 return err;
1234}
1235
1236static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1237{
1238 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001239 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001240
Arend van Sprielac24be62012-10-22 10:36:23 -07001241 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001242 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001243 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001244 return err;
1245 }
1246 return err;
1247}
1248
1249static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1250{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001251 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1252 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001253 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001254 s32 err = 0;
1255
Arend van Sprield96b8012012-12-05 15:26:02 +01001256 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001257 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001258 return -EIO;
1259
1260 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001261 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1262 cfg->conf->rts_threshold = wiphy->rts_threshold;
1263 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001264 if (!err)
1265 goto done;
1266 }
1267 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001268 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1269 cfg->conf->frag_threshold = wiphy->frag_threshold;
1270 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001271 if (!err)
1272 goto done;
1273 }
1274 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001275 && (cfg->conf->retry_long != wiphy->retry_long)) {
1276 cfg->conf->retry_long = wiphy->retry_long;
1277 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001278 if (!err)
1279 goto done;
1280 }
1281 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001282 && (cfg->conf->retry_short != wiphy->retry_short)) {
1283 cfg->conf->retry_short = wiphy->retry_short;
1284 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001285 if (!err)
1286 goto done;
1287 }
1288
1289done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001290 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001291 return err;
1292}
1293
Arend van Spriel5b435de2011-10-05 13:19:03 +02001294static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1295{
1296 memset(prof, 0, sizeof(*prof));
1297}
1298
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001299static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1300{
1301 u16 reason;
1302
1303 switch (e->event_code) {
1304 case BRCMF_E_DEAUTH:
1305 case BRCMF_E_DEAUTH_IND:
1306 case BRCMF_E_DISASSOC_IND:
1307 reason = e->reason;
1308 break;
1309 case BRCMF_E_LINK:
1310 default:
1311 reason = 0;
1312 break;
1313 }
1314 return reason;
1315}
1316
1317static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001318{
Piotr Haber61730d42013-04-23 12:53:12 +02001319 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001320 s32 err = 0;
1321
Arend van Sprield96b8012012-12-05 15:26:02 +01001322 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001323
Hante Meulemanb0a79082015-12-10 13:43:07 +01001324 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001325 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001326 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001327 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001328 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001329 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001330 }
Hante Meulemanb0a79082015-12-10 13:43:07 +01001331 if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1332 (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
1333 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
1334 true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001335 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001336 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001337 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1338 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001339 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001340}
1341
1342static s32
1343brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1344 struct cfg80211_ibss_params *params)
1345{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001346 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001347 struct brcmf_if *ifp = netdev_priv(ndev);
1348 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001349 struct brcmf_join_params join_params;
1350 size_t join_params_size = 0;
1351 s32 err = 0;
1352 s32 wsec = 0;
1353 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001354 u16 chanspec;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001355 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001356
Arend van Sprield96b8012012-12-05 15:26:02 +01001357 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001358 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001359 return -EIO;
1360
1361 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001362 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001363 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001364 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001365 return -EOPNOTSUPP;
1366 }
1367
Arend van Sprielc1179032012-10-22 13:55:33 -07001368 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001369
1370 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001371 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001372 else
Arend van Spriel16886732012-12-05 15:26:04 +01001373 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001374
Johannes Berg683b6d32012-11-08 21:25:48 +01001375 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001376 brcmf_dbg(CONN, "channel: %d\n",
1377 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001378 else
Arend van Spriel16886732012-12-05 15:26:04 +01001379 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001380
1381 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001382 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001383 else
Arend van Spriel16886732012-12-05 15:26:04 +01001384 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001385
1386 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001387 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001388 else
Arend van Spriel16886732012-12-05 15:26:04 +01001389 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001390
1391 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001392 brcmf_dbg(CONN, "beacon interval: %d\n",
1393 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001394 else
Arend van Spriel16886732012-12-05 15:26:04 +01001395 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001396
1397 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001398 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001399 else
Arend van Spriel16886732012-12-05 15:26:04 +01001400 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001401
1402 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001403 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001404 else
Arend van Spriel16886732012-12-05 15:26:04 +01001405 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001406
1407 /* Configure Privacy for starter */
1408 if (params->privacy)
1409 wsec |= WEP_ENABLED;
1410
Arend van Sprielc1179032012-10-22 13:55:33 -07001411 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001412 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001413 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001414 goto done;
1415 }
1416
1417 /* Configure Beacon Interval for starter */
1418 if (params->beacon_interval)
1419 bcnprd = params->beacon_interval;
1420 else
1421 bcnprd = 100;
1422
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001423 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001424 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001425 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001426 goto done;
1427 }
1428
1429 /* Configure required join parameter */
1430 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1431
1432 /* SSID */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001433 ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1434 memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1435 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001436 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001437
1438 /* BSSID */
1439 if (params->bssid) {
1440 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001441 join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001442 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001443 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001444 eth_broadcast_addr(join_params.params_le.bssid);
1445 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001446 }
1447
Arend van Spriel5b435de2011-10-05 13:19:03 +02001448 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001449 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001450 u32 target_channel;
1451
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001452 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001453 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001454 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001455 if (params->channel_fixed) {
1456 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001457 chanspec = chandef_to_chanspec(&cfg->d11inf,
1458 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001459 join_params.params_le.chanspec_list[0] =
1460 cpu_to_le16(chanspec);
1461 join_params.params_le.chanspec_num = cpu_to_le32(1);
1462 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001463 }
1464
1465 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001466 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001467 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001468 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001469 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001470 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001471 goto done;
1472 }
1473 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001474 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001475
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001476 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001477
1478
Arend van Sprielc1179032012-10-22 13:55:33 -07001479 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001480 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001481 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001482 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001483 goto done;
1484 }
1485
1486done:
1487 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001488 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001489 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001490 return err;
1491}
1492
1493static s32
1494brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1495{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001496 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001497
Arend van Sprield96b8012012-12-05 15:26:02 +01001498 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman6a98d642016-01-02 09:41:40 +01001499 if (!check_vif_up(ifp->vif)) {
1500 /* When driver is being unloaded, it can end up here. If an
1501 * error is returned then later on a debug trace in the wireless
1502 * core module will be printed. To avoid this 0 is returned.
1503 */
1504 return 0;
1505 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001506
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001507 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Hante Meuleman42e0ed02016-01-05 11:05:50 +01001508 brcmf_net_setcarrier(ifp, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001509
Arend van Sprield96b8012012-12-05 15:26:02 +01001510 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001511
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001512 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001513}
1514
1515static s32 brcmf_set_wpa_version(struct net_device *ndev,
1516 struct cfg80211_connect_params *sme)
1517{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001518 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001519 struct brcmf_cfg80211_security *sec;
1520 s32 val = 0;
1521 s32 err = 0;
1522
1523 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1524 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1525 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1526 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1527 else
1528 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001529 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001530 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001531 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001532 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001533 return err;
1534 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001535 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001536 sec->wpa_versions = sme->crypto.wpa_versions;
1537 return err;
1538}
1539
1540static s32 brcmf_set_auth_type(struct net_device *ndev,
1541 struct cfg80211_connect_params *sme)
1542{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001543 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001544 struct brcmf_cfg80211_security *sec;
1545 s32 val = 0;
1546 s32 err = 0;
1547
1548 switch (sme->auth_type) {
1549 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1550 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001551 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001552 break;
1553 case NL80211_AUTHTYPE_SHARED_KEY:
1554 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001555 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001556 break;
1557 case NL80211_AUTHTYPE_AUTOMATIC:
1558 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001559 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001560 break;
1561 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001562 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001563 default:
1564 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001565 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001566 break;
1567 }
1568
Hante Meuleman89286dc2013-02-08 15:53:46 +01001569 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001570 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001571 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001572 return err;
1573 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001574 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001575 sec->auth_type = sme->auth_type;
1576 return err;
1577}
1578
1579static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001580brcmf_set_wsec_mode(struct net_device *ndev,
Hante Meuleman240d61a2016-02-17 11:27:10 +01001581 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001582{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001583 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001584 struct brcmf_cfg80211_security *sec;
1585 s32 pval = 0;
1586 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001587 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001588 s32 err = 0;
1589
1590 if (sme->crypto.n_ciphers_pairwise) {
1591 switch (sme->crypto.ciphers_pairwise[0]) {
1592 case WLAN_CIPHER_SUITE_WEP40:
1593 case WLAN_CIPHER_SUITE_WEP104:
1594 pval = WEP_ENABLED;
1595 break;
1596 case WLAN_CIPHER_SUITE_TKIP:
1597 pval = TKIP_ENABLED;
1598 break;
1599 case WLAN_CIPHER_SUITE_CCMP:
1600 pval = AES_ENABLED;
1601 break;
1602 case WLAN_CIPHER_SUITE_AES_CMAC:
1603 pval = AES_ENABLED;
1604 break;
1605 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001606 brcmf_err("invalid cipher pairwise (%d)\n",
1607 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001608 return -EINVAL;
1609 }
1610 }
1611 if (sme->crypto.cipher_group) {
1612 switch (sme->crypto.cipher_group) {
1613 case WLAN_CIPHER_SUITE_WEP40:
1614 case WLAN_CIPHER_SUITE_WEP104:
1615 gval = WEP_ENABLED;
1616 break;
1617 case WLAN_CIPHER_SUITE_TKIP:
1618 gval = TKIP_ENABLED;
1619 break;
1620 case WLAN_CIPHER_SUITE_CCMP:
1621 gval = AES_ENABLED;
1622 break;
1623 case WLAN_CIPHER_SUITE_AES_CMAC:
1624 gval = AES_ENABLED;
1625 break;
1626 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001627 brcmf_err("invalid cipher group (%d)\n",
1628 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001629 return -EINVAL;
1630 }
1631 }
1632
Arend van Spriel16886732012-12-05 15:26:04 +01001633 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001634 /* In case of privacy, but no security and WPS then simulate */
1635 /* setting AES. WPS-2.0 allows no security */
1636 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1637 sme->privacy)
1638 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001639
Hante Meuleman240d61a2016-02-17 11:27:10 +01001640 wsec = pval | gval;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001641 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001642 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001643 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001644 return err;
1645 }
1646
Arend van Spriel06bb1232012-09-27 14:17:56 +02001647 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001648 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1649 sec->cipher_group = sme->crypto.cipher_group;
1650
1651 return err;
1652}
1653
1654static s32
1655brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1656{
Hante Meuleman240d61a2016-02-17 11:27:10 +01001657 struct brcmf_if *ifp = netdev_priv(ndev);
1658 s32 val;
1659 s32 err;
1660 const struct brcmf_tlv *rsn_ie;
1661 const u8 *ie;
1662 u32 ie_len;
1663 u32 offset;
1664 u16 rsn_cap;
1665 u32 mfp;
1666 u16 count;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001667
Hante Meuleman240d61a2016-02-17 11:27:10 +01001668 if (!sme->crypto.n_akm_suites)
1669 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001670
Hante Meuleman240d61a2016-02-17 11:27:10 +01001671 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val);
1672 if (err) {
1673 brcmf_err("could not get wpa_auth (%d)\n", err);
1674 return err;
1675 }
1676 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1677 switch (sme->crypto.akm_suites[0]) {
1678 case WLAN_AKM_SUITE_8021X:
1679 val = WPA_AUTH_UNSPECIFIED;
1680 break;
1681 case WLAN_AKM_SUITE_PSK:
1682 val = WPA_AUTH_PSK;
1683 break;
1684 default:
1685 brcmf_err("invalid cipher group (%d)\n",
1686 sme->crypto.cipher_group);
1687 return -EINVAL;
1688 }
1689 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1690 switch (sme->crypto.akm_suites[0]) {
1691 case WLAN_AKM_SUITE_8021X:
1692 val = WPA2_AUTH_UNSPECIFIED;
1693 break;
1694 case WLAN_AKM_SUITE_8021X_SHA256:
1695 val = WPA2_AUTH_1X_SHA256;
1696 break;
1697 case WLAN_AKM_SUITE_PSK_SHA256:
1698 val = WPA2_AUTH_PSK_SHA256;
1699 break;
1700 case WLAN_AKM_SUITE_PSK:
1701 val = WPA2_AUTH_PSK;
1702 break;
1703 default:
1704 brcmf_err("invalid cipher group (%d)\n",
1705 sme->crypto.cipher_group);
1706 return -EINVAL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001707 }
1708 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01001709
1710 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
1711 goto skip_mfp_config;
1712 /* The MFP mode (1 or 2) needs to be determined, parse IEs. The
1713 * IE will not be verified, just a quick search for MFP config
1714 */
1715 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
1716 WLAN_EID_RSN);
1717 if (!rsn_ie)
1718 goto skip_mfp_config;
1719 ie = (const u8 *)rsn_ie;
1720 ie_len = rsn_ie->len + TLV_HDR_LEN;
1721 /* Skip unicast suite */
1722 offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
1723 if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
1724 goto skip_mfp_config;
1725 /* Skip multicast suite */
1726 count = ie[offset] + (ie[offset + 1] << 8);
1727 offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
1728 if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
1729 goto skip_mfp_config;
1730 /* Skip auth key management suite(s) */
1731 count = ie[offset] + (ie[offset + 1] << 8);
1732 offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
1733 if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
1734 goto skip_mfp_config;
1735 /* Ready to read capabilities */
1736 mfp = BRCMF_MFP_NONE;
1737 rsn_cap = ie[offset] + (ie[offset + 1] << 8);
1738 if (rsn_cap & RSN_CAP_MFPR_MASK)
1739 mfp = BRCMF_MFP_REQUIRED;
1740 else if (rsn_cap & RSN_CAP_MFPC_MASK)
1741 mfp = BRCMF_MFP_CAPABLE;
1742 brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
1743
1744skip_mfp_config:
1745 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
1746 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
1747 if (err) {
1748 brcmf_err("could not set wpa_auth (%d)\n", err);
1749 return err;
1750 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001751
1752 return err;
1753}
1754
1755static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001756brcmf_set_sharedkey(struct net_device *ndev,
1757 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001758{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001759 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001760 struct brcmf_cfg80211_security *sec;
1761 struct brcmf_wsec_key key;
1762 s32 val;
1763 s32 err = 0;
1764
Arend van Spriel16886732012-12-05 15:26:04 +01001765 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001766
Roland Vossena718e2f2011-10-12 20:51:24 +02001767 if (sme->key_len == 0)
1768 return 0;
1769
Arend van Spriel06bb1232012-09-27 14:17:56 +02001770 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001771 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1772 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001773
1774 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1775 return 0;
1776
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001777 if (!(sec->cipher_pairwise &
1778 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1779 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001780
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001781 memset(&key, 0, sizeof(key));
1782 key.len = (u32) sme->key_len;
1783 key.index = (u32) sme->key_idx;
1784 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001785 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001786 return -EINVAL;
1787 }
1788 memcpy(key.data, sme->key, key.len);
1789 key.flags = BRCMF_PRIMARY_KEY;
1790 switch (sec->cipher_pairwise) {
1791 case WLAN_CIPHER_SUITE_WEP40:
1792 key.algo = CRYPTO_ALGO_WEP1;
1793 break;
1794 case WLAN_CIPHER_SUITE_WEP104:
1795 key.algo = CRYPTO_ALGO_WEP128;
1796 break;
1797 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001798 brcmf_err("Invalid algorithm (%d)\n",
1799 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001800 return -EINVAL;
1801 }
1802 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001803 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1804 key.len, key.index, key.algo);
1805 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001806 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001807 if (err)
1808 return err;
1809
1810 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001811 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001812 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001813 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001814 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001815 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001816 }
1817 return err;
1818}
1819
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001820static
1821enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1822 enum nl80211_auth_type type)
1823{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001824 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1825 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1826 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1827 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001828 }
1829 return type;
1830}
1831
Arend van Spriel7705ba62016-04-17 16:44:58 +02001832static void brcmf_set_join_pref(struct brcmf_if *ifp,
1833 struct cfg80211_bss_selection *bss_select)
1834{
1835 struct brcmf_join_pref_params join_pref_params[2];
1836 enum nl80211_band band;
1837 int err, i = 0;
1838
1839 join_pref_params[i].len = 2;
1840 join_pref_params[i].rssi_gain = 0;
1841
1842 if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)
1843 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);
1844
1845 switch (bss_select->behaviour) {
1846 case __NL80211_BSS_SELECT_ATTR_INVALID:
1847 brcmf_c_set_joinpref_default(ifp);
1848 return;
1849 case NL80211_BSS_SELECT_ATTR_BAND_PREF:
1850 join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;
1851 band = bss_select->param.band_pref;
1852 join_pref_params[i].band = nl80211_band_to_fwil(band);
1853 i++;
1854 break;
1855 case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:
1856 join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;
1857 band = bss_select->param.adjust.band;
1858 join_pref_params[i].band = nl80211_band_to_fwil(band);
1859 join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;
1860 i++;
1861 break;
1862 case NL80211_BSS_SELECT_ATTR_RSSI:
1863 default:
1864 break;
1865 }
1866 join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;
1867 join_pref_params[i].len = 2;
1868 join_pref_params[i].rssi_gain = 0;
1869 join_pref_params[i].band = 0;
1870 err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
1871 sizeof(join_pref_params));
1872 if (err)
1873 brcmf_err("Set join_pref error (%d)\n", err);
1874}
1875
Arend van Spriel5b435de2011-10-05 13:19:03 +02001876static s32
1877brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001878 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001879{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001880 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001881 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001882 struct ieee80211_channel *chan = sme->channel;
1883 struct brcmf_join_params join_params;
1884 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001885 const struct brcmf_tlv *rsn_ie;
1886 const struct brcmf_vs_tlv *wpa_ie;
1887 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001888 u32 ie_len;
1889 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001890 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001891 s32 err = 0;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001892 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001893
Arend van Sprield96b8012012-12-05 15:26:02 +01001894 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001895 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001896 return -EIO;
1897
1898 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001899 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001900 return -EOPNOTSUPP;
1901 }
1902
Hante Meuleman89286dc2013-02-08 15:53:46 +01001903 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1904 /* A normal (non P2P) connection request setup. */
1905 ie = NULL;
1906 ie_len = 0;
1907 /* find the WPA_IE */
1908 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1909 if (wpa_ie) {
1910 ie = wpa_ie;
1911 ie_len = wpa_ie->len + TLV_HDR_LEN;
1912 } else {
1913 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001914 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1915 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001916 WLAN_EID_RSN);
1917 if (rsn_ie) {
1918 ie = rsn_ie;
1919 ie_len = rsn_ie->len + TLV_HDR_LEN;
1920 }
1921 }
1922 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1923 }
1924
1925 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1926 sme->ie, sme->ie_len);
1927 if (err)
1928 brcmf_err("Set Assoc REQ IE Failed\n");
1929 else
1930 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1931
Arend van Sprielc1179032012-10-22 13:55:33 -07001932 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001933
1934 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001935 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001936 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001937 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001938 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1939 cfg->channel, chan->center_freq, chanspec);
1940 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001941 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001942 chanspec = 0;
1943 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001944
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001945 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001946
1947 err = brcmf_set_wpa_version(ndev, sme);
1948 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001949 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001950 goto done;
1951 }
1952
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001953 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001954 err = brcmf_set_auth_type(ndev, sme);
1955 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001956 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001957 goto done;
1958 }
1959
Hante Meuleman240d61a2016-02-17 11:27:10 +01001960 err = brcmf_set_wsec_mode(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001961 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001962 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001963 goto done;
1964 }
1965
1966 err = brcmf_set_key_mgmt(ndev, sme);
1967 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001968 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001969 goto done;
1970 }
1971
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001972 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001973 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001974 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001975 goto done;
1976 }
1977
Hante Meuleman89286dc2013-02-08 15:53:46 +01001978 /* Join with specific BSSID and cached SSID
1979 * If SSID is zero join based on BSSID only
1980 */
1981 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1982 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1983 if (cfg->channel)
1984 join_params_size += sizeof(u16);
1985 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1986 if (ext_join_params == NULL) {
1987 err = -ENOMEM;
1988 goto done;
1989 }
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001990 ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
1991 ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
1992 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
1993 if (ssid_len < IEEE80211_MAX_SSID_LEN)
1994 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
1995 ext_join_params->ssid_le.SSID, ssid_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001996
Hante Meuleman89286dc2013-02-08 15:53:46 +01001997 /* Set up join scan parameters */
1998 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001999 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
2000
2001 if (sme->bssid)
2002 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
2003 else
Joe Perches93803b32015-03-02 19:54:49 -08002004 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01002005
2006 if (cfg->channel) {
2007 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
2008
2009 ext_join_params->assoc_le.chanspec_list[0] =
2010 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01002011 /* Increase dwell time to receive probe response or detect
2012 * beacon from target AP at a noisy air only during connect
2013 * command.
2014 */
2015 ext_join_params->scan_le.active_time =
2016 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
2017 ext_join_params->scan_le.passive_time =
2018 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
2019 /* To sync with presence period of VSDB GO send probe request
2020 * more frequently. Probe request will be stopped when it gets
2021 * probe response from target AP/GO.
2022 */
2023 ext_join_params->scan_le.nprobes =
2024 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
2025 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
2026 } else {
2027 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
2028 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
2029 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01002030 }
2031
Arend van Spriel7705ba62016-04-17 16:44:58 +02002032 brcmf_set_join_pref(ifp, &sme->bss_select);
2033
Hante Meuleman89286dc2013-02-08 15:53:46 +01002034 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
2035 join_params_size);
2036 kfree(ext_join_params);
2037 if (!err)
2038 /* This is it. join command worked, we are done */
2039 goto done;
2040
2041 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002042 memset(&join_params, 0, sizeof(join_params));
2043 join_params_size = sizeof(join_params.ssid_le);
2044
Hante Meulemane9a6ca82015-11-25 11:32:37 +01002045 memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
2046 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002047
Hante Meuleman89286dc2013-02-08 15:53:46 +01002048 if (sme->bssid)
2049 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
2050 else
Joe Perches93803b32015-03-02 19:54:49 -08002051 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002052
Hante Meuleman17012612013-02-06 18:40:44 +01002053 if (cfg->channel) {
2054 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
2055 join_params.params_le.chanspec_num = cpu_to_le32(1);
2056 join_params_size += sizeof(join_params.params_le);
2057 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002058 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002059 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002060 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01002061 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002062
2063done:
2064 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07002065 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01002066 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002067 return err;
2068}
2069
2070static s32
2071brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
2072 u16 reason_code)
2073{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002074 struct brcmf_if *ifp = netdev_priv(ndev);
2075 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002076 struct brcmf_scb_val_le scbval;
2077 s32 err = 0;
2078
Arend van Sprield96b8012012-12-05 15:26:02 +01002079 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07002080 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002081 return -EIO;
2082
Arend van Sprielc1179032012-10-22 13:55:33 -07002083 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01002084 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02002085 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002086
Arend van Spriel06bb1232012-09-27 14:17:56 +02002087 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002088 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07002089 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07002090 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002091 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002092 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002093
Arend van Sprield96b8012012-12-05 15:26:02 +01002094 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002095 return err;
2096}
2097
2098static s32
Johannes Bergc8442112012-10-24 10:17:18 +02002099brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05002100 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002101{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002102 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002103 struct net_device *ndev = cfg_to_ndev(cfg);
2104 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002105 s32 err;
2106 s32 disable;
2107 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002108
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002109 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07002110 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002111 return -EIO;
2112
2113 switch (type) {
2114 case NL80211_TX_POWER_AUTOMATIC:
2115 break;
2116 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02002117 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002118 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002119 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002120 err = -EINVAL;
2121 goto done;
2122 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002123 qdbm = MBM_TO_DBM(4 * mbm);
2124 if (qdbm > 127)
2125 qdbm = 127;
2126 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002127 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002128 default:
2129 brcmf_err("Unsupported type %d\n", type);
2130 err = -EINVAL;
2131 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002132 }
2133 /* Make sure radio is off or on as far as software is concerned */
2134 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07002135 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002136 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002137 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002138
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002139 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002140 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002141 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002142
2143done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002144 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002145 return err;
2146}
2147
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002148static s32
2149brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2150 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002151{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002152 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002153 struct net_device *ndev = cfg_to_ndev(cfg);
2154 struct brcmf_if *ifp = netdev_priv(ndev);
2155 s32 qdbm = 0;
2156 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002157
Arend van Sprield96b8012012-12-05 15:26:02 +01002158 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002159 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002160 return -EIO;
2161
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002162 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002163 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002164 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002165 goto done;
2166 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002167 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002168
2169done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002170 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002171 return err;
2172}
2173
2174static s32
2175brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002176 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002177{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002178 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002179 u32 index;
2180 u32 wsec;
2181 s32 err = 0;
2182
Arend van Sprield96b8012012-12-05 15:26:02 +01002183 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002184 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002185 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002186 return -EIO;
2187
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002188 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002189 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002190 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002191 goto done;
2192 }
2193
2194 if (wsec & WEP_ENABLED) {
2195 /* Just select a new current key */
2196 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002197 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002198 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002199 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002200 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002201 }
2202done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002203 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002204 return err;
2205}
2206
2207static s32
Hante Meuleman219e0f72016-02-17 11:27:09 +01002208brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2209 u8 key_idx, bool pairwise, const u8 *mac_addr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002210{
Hante Meuleman992f6062013-04-02 21:06:17 +02002211 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman240d61a2016-02-17 11:27:10 +01002212 struct brcmf_wsec_key *key;
2213 s32 err;
Hante Meuleman219e0f72016-02-17 11:27:09 +01002214
2215 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman240d61a2016-02-17 11:27:10 +01002216 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
2217
Hante Meuleman219e0f72016-02-17 11:27:09 +01002218 if (!check_vif_up(ifp->vif))
2219 return -EIO;
2220
2221 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2222 /* we ignore this key index in this case */
2223 return -EINVAL;
2224 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002225
Hante Meuleman240d61a2016-02-17 11:27:10 +01002226 key = &ifp->vif->profile.key[key_idx];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002227
Hante Meuleman240d61a2016-02-17 11:27:10 +01002228 if (key->algo == CRYPTO_ALGO_OFF) {
2229 brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
2230 return -EINVAL;
2231 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002232
Hante Meuleman240d61a2016-02-17 11:27:10 +01002233 memset(key, 0, sizeof(*key));
2234 key->index = (u32)key_idx;
2235 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236
Hante Meuleman240d61a2016-02-17 11:27:10 +01002237 /* Clear the key/index */
2238 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002239
Hante Meuleman219e0f72016-02-17 11:27:09 +01002240 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002241 return err;
2242}
2243
2244static s32
2245brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman240d61a2016-02-17 11:27:10 +01002246 u8 key_idx, bool pairwise, const u8 *mac_addr,
2247 struct key_params *params)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002248{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002249 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002250 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002251 s32 val;
2252 s32 wsec;
Hante Meuleman219e0f72016-02-17 11:27:09 +01002253 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002254 u8 keybuf[8];
Hante Meuleman219e0f72016-02-17 11:27:09 +01002255 bool ext_key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002256
Arend van Sprield96b8012012-12-05 15:26:02 +01002257 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002258 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002259 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002260 return -EIO;
2261
Hante Meuleman118eb302014-12-21 12:43:49 +01002262 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2263 /* we ignore this key index in this case */
2264 brcmf_err("invalid key index (%d)\n", key_idx);
2265 return -EINVAL;
2266 }
2267
Hante Meuleman219e0f72016-02-17 11:27:09 +01002268 if (params->key_len == 0)
2269 return brcmf_cfg80211_del_key(wiphy, ndev, key_idx, pairwise,
2270 mac_addr);
2271
2272 if (params->key_len > sizeof(key->data)) {
2273 brcmf_err("Too long key length (%u)\n", params->key_len);
2274 return -EINVAL;
2275 }
2276
2277 ext_key = false;
2278 if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2279 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
2280 brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
2281 ext_key = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002282 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002283
Hante Meuleman118eb302014-12-21 12:43:49 +01002284 key = &ifp->vif->profile.key[key_idx];
2285 memset(key, 0, sizeof(*key));
Hante Meuleman219e0f72016-02-17 11:27:09 +01002286 if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
2287 memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
Hante Meuleman118eb302014-12-21 12:43:49 +01002288 key->len = params->key_len;
2289 key->index = key_idx;
Hante Meuleman118eb302014-12-21 12:43:49 +01002290 memcpy(key->data, params->key, key->len);
Hante Meuleman219e0f72016-02-17 11:27:09 +01002291 if (!ext_key)
2292 key->flags = BRCMF_PRIMARY_KEY;
Hante Meuleman118eb302014-12-21 12:43:49 +01002293
Arend van Spriel5b435de2011-10-05 13:19:03 +02002294 switch (params->cipher) {
2295 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002296 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002297 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002298 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002299 break;
2300 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002301 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002302 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002303 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002304 break;
2305 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002306 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002307 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002308 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2309 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2310 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002311 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002312 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002313 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002314 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002315 break;
2316 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002317 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002318 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002319 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002320 break;
2321 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002322 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002323 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002324 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002325 break;
2326 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002327 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002328 err = -EINVAL;
2329 goto done;
2330 }
2331
Hante Meuleman118eb302014-12-21 12:43:49 +01002332 err = send_key_to_dongle(ifp, key);
Hante Meuleman219e0f72016-02-17 11:27:09 +01002333 if (ext_key || err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002334 goto done;
2335
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002336 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002337 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002338 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002339 goto done;
2340 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002341 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002342 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002343 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002344 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002345 goto done;
2346 }
2347
Arend van Spriel5b435de2011-10-05 13:19:03 +02002348done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002349 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002350 return err;
2351}
2352
2353static s32
Hante Meuleman240d61a2016-02-17 11:27:10 +01002354brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx,
2355 bool pairwise, const u8 *mac_addr, void *cookie,
2356 void (*callback)(void *cookie,
2357 struct key_params *params))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002358{
2359 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002360 struct brcmf_if *ifp = netdev_priv(ndev);
2361 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002362 struct brcmf_cfg80211_security *sec;
2363 s32 wsec;
2364 s32 err = 0;
2365
Arend van Sprield96b8012012-12-05 15:26:02 +01002366 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002367 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002368 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002369 return -EIO;
2370
2371 memset(&params, 0, sizeof(params));
2372
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002373 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002374 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002375 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002376 /* Ignore this error, may happen during DISASSOC */
2377 err = -EAGAIN;
2378 goto done;
2379 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002380 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002381 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002382 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2383 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002384 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002385 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2386 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002387 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002388 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002389 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002390 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002391 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002392 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002393 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002394 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002395 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002396 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002397 err = -EINVAL;
2398 goto done;
2399 }
2400 callback(cookie, &params);
2401
2402done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002403 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002404 return err;
2405}
2406
2407static s32
2408brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
Hante Meuleman240d61a2016-02-17 11:27:10 +01002409 struct net_device *ndev, u8 key_idx)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002410{
Hante Meuleman240d61a2016-02-17 11:27:10 +01002411 struct brcmf_if *ifp = netdev_priv(ndev);
2412
2413 brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
2414
2415 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
2416 return 0;
2417
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002418 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002419
2420 return -EOPNOTSUPP;
2421}
2422
Hante Meuleman118eb302014-12-21 12:43:49 +01002423static void
2424brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2425{
2426 s32 err;
2427 u8 key_idx;
2428 struct brcmf_wsec_key *key;
2429 s32 wsec;
2430
2431 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2432 key = &ifp->vif->profile.key[key_idx];
2433 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2434 (key->algo == CRYPTO_ALGO_WEP128))
2435 break;
2436 }
2437 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2438 return;
2439
2440 err = send_key_to_dongle(ifp, key);
2441 if (err) {
2442 brcmf_err("Setting WEP key failed (%d)\n", err);
2443 return;
2444 }
2445 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2446 if (err) {
2447 brcmf_err("get wsec error (%d)\n", err);
2448 return;
2449 }
2450 wsec |= WEP_ENABLED;
2451 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2452 if (err)
2453 brcmf_err("set wsec error (%d)\n", err);
2454}
2455
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002456static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2457{
2458 struct nl80211_sta_flag_update *sfu;
2459
2460 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2461 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2462 sfu = &si->sta_flags;
2463 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2464 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2465 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2466 BIT(NL80211_STA_FLAG_AUTHORIZED);
2467 if (fw_sta_flags & BRCMF_STA_WME)
2468 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2469 if (fw_sta_flags & BRCMF_STA_AUTHE)
2470 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2471 if (fw_sta_flags & BRCMF_STA_ASSOC)
2472 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2473 if (fw_sta_flags & BRCMF_STA_AUTHO)
2474 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2475}
2476
2477static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2478{
2479 struct {
2480 __le32 len;
2481 struct brcmf_bss_info_le bss_le;
2482 } *buf;
2483 u16 capability;
2484 int err;
2485
2486 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2487 if (!buf)
2488 return;
2489
2490 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2491 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2492 WL_BSS_INFO_MAX);
2493 if (err) {
2494 brcmf_err("Failed to get bss info (%d)\n", err);
2495 return;
2496 }
2497 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2498 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2499 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2500 capability = le16_to_cpu(buf->bss_le.capability);
2501 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2502 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2503 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2504 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2505 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2506 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2507}
2508
Arend van Spriel5b435de2011-10-05 13:19:03 +02002509static s32
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002510brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
2511 struct station_info *sinfo)
2512{
2513 struct brcmf_scb_val_le scbval;
2514 struct brcmf_pktcnt_le pktcnt;
2515 s32 err;
2516 u32 rate;
2517 u32 rssi;
2518
2519 /* Get the current tx rate */
2520 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
2521 if (err < 0) {
2522 brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
2523 return err;
2524 }
2525 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
2526 sinfo->txrate.legacy = rate * 5;
2527
2528 memset(&scbval, 0, sizeof(scbval));
2529 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
2530 sizeof(scbval));
2531 if (err) {
2532 brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
2533 return err;
2534 }
2535 rssi = le32_to_cpu(scbval.val);
2536 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2537 sinfo->signal = rssi;
2538
2539 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
2540 sizeof(pktcnt));
2541 if (err) {
2542 brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
2543 return err;
2544 }
2545 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
2546 BIT(NL80211_STA_INFO_RX_DROP_MISC) |
2547 BIT(NL80211_STA_INFO_TX_PACKETS) |
2548 BIT(NL80211_STA_INFO_TX_FAILED);
2549 sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
2550 sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
2551 sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
2552 sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
2553
2554 return 0;
2555}
2556
2557static s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002558brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002559 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002560{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002561 struct brcmf_if *ifp = netdev_priv(ndev);
Jaap Jan Meijer94abd772016-05-12 18:25:08 +02002562 struct brcmf_scb_val_le scb_val;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002563 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002564 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002565 u32 sta_flags;
2566 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002567 s32 total_rssi;
2568 s32 count_rssi;
Jaap Jan Meijer94abd772016-05-12 18:25:08 +02002569 int rssi;
Hante Meulemancae355d2015-10-08 20:33:17 +02002570 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002571
Arend van Sprield96b8012012-12-05 15:26:02 +01002572 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002573 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002574 return -EIO;
2575
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002576 if (brcmf_is_ibssmode(ifp->vif))
2577 return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
2578
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002579 memset(&sta_info_le, 0, sizeof(sta_info_le));
2580 memcpy(&sta_info_le, mac, ETH_ALEN);
2581 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2582 &sta_info_le,
2583 sizeof(sta_info_le));
2584 is_tdls_peer = !err;
2585 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002586 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002587 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002588 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002589 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002590 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002591 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002592 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002593 }
2594 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2595 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2596 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2597 sta_flags = le32_to_cpu(sta_info_le.flags);
2598 brcmf_convert_sta_flags(sta_flags, sinfo);
2599 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2600 if (is_tdls_peer)
2601 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2602 else
2603 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2604 if (sta_flags & BRCMF_STA_ASSOC) {
2605 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2606 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2607 brcmf_fill_bss_param(ifp, sinfo);
2608 }
2609 if (sta_flags & BRCMF_STA_SCBSTATS) {
2610 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2611 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2612 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2613 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2614 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2615 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2616 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2617 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2618 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002619 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002620 sinfo->txrate.legacy =
2621 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002622 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002623 if (sinfo->rx_packets) {
2624 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002625 sinfo->rxrate.legacy =
2626 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002627 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002628 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2629 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2630 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2631 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2632 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2633 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002634 total_rssi = 0;
2635 count_rssi = 0;
2636 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2637 if (sta_info_le.rssi[i]) {
2638 sinfo->chain_signal_avg[count_rssi] =
2639 sta_info_le.rssi[i];
2640 sinfo->chain_signal[count_rssi] =
2641 sta_info_le.rssi[i];
2642 total_rssi += sta_info_le.rssi[i];
2643 count_rssi++;
2644 }
2645 }
2646 if (count_rssi) {
2647 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2648 sinfo->chains = count_rssi;
2649
2650 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2651 total_rssi /= count_rssi;
2652 sinfo->signal = total_rssi;
Jaap Jan Meijer94abd772016-05-12 18:25:08 +02002653 } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
2654 &ifp->vif->sme_state)) {
2655 memset(&scb_val, 0, sizeof(scb_val));
2656 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
2657 &scb_val, sizeof(scb_val));
2658 if (err) {
2659 brcmf_err("Could not get rssi (%d)\n", err);
2660 goto done;
2661 } else {
2662 rssi = le32_to_cpu(scb_val.val);
2663 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2664 sinfo->signal = rssi;
2665 brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
2666 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002667 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002668 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002669done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002670 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002671 return err;
2672}
2673
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002674static int
2675brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2676 int idx, u8 *mac, struct station_info *sinfo)
2677{
2678 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2679 struct brcmf_if *ifp = netdev_priv(ndev);
2680 s32 err;
2681
2682 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2683
2684 if (idx == 0) {
2685 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2686 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2687 &cfg->assoclist,
2688 sizeof(cfg->assoclist));
2689 if (err) {
2690 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2691 err);
2692 cfg->assoclist.count = 0;
2693 return -EOPNOTSUPP;
2694 }
2695 }
2696 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2697 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2698 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2699 }
2700 return -ENOENT;
2701}
2702
Arend van Spriel5b435de2011-10-05 13:19:03 +02002703static s32
2704brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2705 bool enabled, s32 timeout)
2706{
2707 s32 pm;
2708 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002709 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002710 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002711
Arend van Sprield96b8012012-12-05 15:26:02 +01002712 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002713
2714 /*
2715 * Powersave enable/disable request is coming from the
2716 * cfg80211 even before the interface is up. In that
2717 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002718 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002719 * FW later while initializing the dongle
2720 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002721 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002722 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002723
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002724 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002725 goto done;
2726 }
2727
2728 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002729 /* Do not enable the power save after assoc if it is a p2p interface */
2730 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2731 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2732 pm = PM_OFF;
2733 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002734 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002735
Arend van Sprielc1179032012-10-22 13:55:33 -07002736 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002737 if (err) {
2738 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002739 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002740 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002741 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002742 }
2743done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002744 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002745 return err;
2746}
2747
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002748static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002749 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002750{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002751 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002752 struct ieee80211_channel *notify_channel;
2753 struct cfg80211_bss *bss;
2754 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002755 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002756 u16 channel;
2757 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002758 u16 notify_capability;
2759 u16 notify_interval;
2760 u8 *notify_ie;
2761 size_t notify_ielen;
2762 s32 notify_signal;
2763
2764 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002765 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002766 return 0;
2767 }
2768
Franky Lin83cf17a2013-04-11 13:28:50 +02002769 if (!bi->ctl_ch) {
2770 ch.chspec = le16_to_cpu(bi->chanspec);
2771 cfg->d11inf.decchspec(&ch);
Rafał Miłecki4712d882016-05-20 13:38:57 +02002772 bi->ctl_ch = ch.control_ch_num;
Franky Lin83cf17a2013-04-11 13:28:50 +02002773 }
2774 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002775
2776 if (channel <= CH_MAX_2G_CHANNEL)
Johannes Berg57fbcce2016-04-12 15:56:15 +02002777 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002778 else
Johannes Berg57fbcce2016-04-12 15:56:15 +02002779 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002780
2781 freq = ieee80211_channel_to_frequency(channel, band->band);
2782 notify_channel = ieee80211_get_channel(wiphy, freq);
2783
Arend van Spriel5b435de2011-10-05 13:19:03 +02002784 notify_capability = le16_to_cpu(bi->capability);
2785 notify_interval = le16_to_cpu(bi->beacon_period);
2786 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2787 notify_ielen = le32_to_cpu(bi->ie_length);
2788 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2789
Arend van Spriel16886732012-12-05 15:26:04 +01002790 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2791 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2792 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2793 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2794 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002795
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002796 bss = cfg80211_inform_bss(wiphy, notify_channel,
2797 CFG80211_BSS_FTYPE_UNKNOWN,
2798 (const u8 *)bi->BSSID,
2799 0, notify_capability,
2800 notify_interval, notify_ie,
2801 notify_ielen, notify_signal,
2802 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002803
Franky Line78946e2011-11-10 20:30:34 +01002804 if (!bss)
2805 return -ENOMEM;
2806
Johannes Berg5b112d32013-02-01 01:49:58 +01002807 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002808
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002809 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002810}
2811
Roland Vossen6f09be02011-10-18 14:03:02 +02002812static struct brcmf_bss_info_le *
2813next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2814{
2815 if (bss == NULL)
2816 return list->bss_info_le;
2817 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2818 le32_to_cpu(bss->length));
2819}
2820
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002821static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002822{
2823 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002824 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002825 s32 err = 0;
2826 int i;
2827
Hante Meulemanef8596e2014-09-30 10:23:13 +02002828 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002829 if (bss_list->count != 0 &&
2830 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002831 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2832 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002833 return -EOPNOTSUPP;
2834 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002835 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002836 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002837 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002838 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002839 if (err)
2840 break;
2841 }
2842 return err;
2843}
2844
Hante Meulemanb0a79082015-12-10 13:43:07 +01002845static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
2846 struct net_device *ndev, const u8 *bssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002847{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002848 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002849 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002850 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002851 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002852 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002853 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002854 u8 *buf = NULL;
2855 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002856 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002857 u16 notify_capability;
2858 u16 notify_interval;
2859 u8 *notify_ie;
2860 size_t notify_ielen;
2861 s32 notify_signal;
2862
Arend van Sprield96b8012012-12-05 15:26:02 +01002863 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002864
2865 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2866 if (buf == NULL) {
2867 err = -ENOMEM;
2868 goto CleanUp;
2869 }
2870
2871 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2872
Arend van Sprielac24be62012-10-22 10:36:23 -07002873 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2874 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002875 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002876 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002877 goto CleanUp;
2878 }
2879
Roland Vossend34bf642011-10-18 14:03:01 +02002880 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002881
Franky Lin83cf17a2013-04-11 13:28:50 +02002882 ch.chspec = le16_to_cpu(bi->chanspec);
2883 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002884
Franky Lin83cf17a2013-04-11 13:28:50 +02002885 if (ch.band == BRCMU_CHAN_BAND_2G)
Johannes Berg57fbcce2016-04-12 15:56:15 +02002886 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002887 else
Johannes Berg57fbcce2016-04-12 15:56:15 +02002888 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002889
Rafał Miłecki4712d882016-05-20 13:38:57 +02002890 freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
Hante Meulemanb0a79082015-12-10 13:43:07 +01002891 cfg->channel = freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002892 notify_channel = ieee80211_get_channel(wiphy, freq);
2893
Arend van Spriel5b435de2011-10-05 13:19:03 +02002894 notify_capability = le16_to_cpu(bi->capability);
2895 notify_interval = le16_to_cpu(bi->beacon_period);
2896 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2897 notify_ielen = le32_to_cpu(bi->ie_length);
2898 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2899
Rafał Miłecki4712d882016-05-20 13:38:57 +02002900 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002901 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2902 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2903 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002904
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002905 bss = cfg80211_inform_bss(wiphy, notify_channel,
2906 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2907 notify_capability, notify_interval,
2908 notify_ie, notify_ielen, notify_signal,
2909 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002910
Franky Line78946e2011-11-10 20:30:34 +01002911 if (!bss) {
2912 err = -ENOMEM;
2913 goto CleanUp;
2914 }
2915
Johannes Berg5b112d32013-02-01 01:49:58 +01002916 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002917
Arend van Spriel5b435de2011-10-05 13:19:03 +02002918CleanUp:
2919
2920 kfree(buf);
2921
Arend van Sprield96b8012012-12-05 15:26:02 +01002922 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002923
2924 return err;
2925}
2926
Hante Meuleman89286dc2013-02-08 15:53:46 +01002927static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2928 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002929{
Roland Vossend34bf642011-10-18 14:03:01 +02002930 struct brcmf_bss_info_le *bi;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002931 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002932 u16 beacon_interval;
2933 u8 dtim_period;
2934 size_t ie_len;
2935 u8 *ie;
2936 s32 err = 0;
2937
Arend van Sprield96b8012012-12-05 15:26:02 +01002938 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002939 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002940 return err;
2941
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002942 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002943 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002944 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002945 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002946 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002947 goto update_bss_info_out;
2948 }
2949
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002950 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2951 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002952 if (err)
2953 goto update_bss_info_out;
2954
2955 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2956 ie_len = le32_to_cpu(bi->ie_length);
2957 beacon_interval = le16_to_cpu(bi->beacon_period);
2958
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002959 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002960 if (tim)
2961 dtim_period = tim->data[1];
2962 else {
2963 /*
2964 * active scan was done so we could not get dtim
2965 * information out of probe response.
2966 * so we speficially query dtim information to dongle.
2967 */
2968 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002969 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002970 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002971 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002972 goto update_bss_info_out;
2973 }
2974 dtim_period = (u8)var;
2975 }
2976
Arend van Spriel5b435de2011-10-05 13:19:03 +02002977update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002978 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002979 return err;
2980}
2981
Hante Meuleman18e2f612013-02-08 15:53:49 +01002982void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002983{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002984 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002985
Arend van Sprielc1179032012-10-22 13:55:33 -07002986 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002987 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002988 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002989 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002990 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002991 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2992 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002993}
2994
Hante Meulemane756af52012-09-11 21:18:52 +02002995static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2996{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002997 struct brcmf_cfg80211_info *cfg =
2998 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002999 escan_timeout_work);
3000
Hante Meulemanef8596e2014-09-30 10:23:13 +02003001 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02003002 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02003003}
3004
3005static void brcmf_escan_timeout(unsigned long data)
3006{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003007 struct brcmf_cfg80211_info *cfg =
3008 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02003009
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003010 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003011 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08003012 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02003013 }
3014}
3015
3016static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02003017brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
3018 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02003019 struct brcmf_bss_info_le *bss_info_le)
3020{
Franky Lin83cf17a2013-04-11 13:28:50 +02003021 struct brcmu_chan ch_bss, ch_bss_info_le;
3022
3023 ch_bss.chspec = le16_to_cpu(bss->chanspec);
3024 cfg->d11inf.decchspec(&ch_bss);
3025 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
3026 cfg->d11inf.decchspec(&ch_bss_info_le);
3027
Hante Meulemane756af52012-09-11 21:18:52 +02003028 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02003029 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02003030 bss_info_le->SSID_len == bss->SSID_len &&
3031 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01003032 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
3033 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02003034 s16 bss_rssi = le16_to_cpu(bss->RSSI);
3035 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
3036
Hante Meulemane756af52012-09-11 21:18:52 +02003037 /* preserve max RSSI if the measurements are
3038 * both on-channel or both off-channel
3039 */
Arend van Spriel029591f2012-09-19 22:21:06 +02003040 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02003041 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01003042 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
3043 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02003044 /* preserve the on-channel rssi measurement
3045 * if the new measurement is off channel
3046 */
3047 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01003048 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02003049 }
3050 return 1;
3051 }
3052 return 0;
3053}
3054
3055static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003056brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02003057 const struct brcmf_event_msg *e, void *data)
3058{
Arend van Spriel19937322012-11-05 16:22:32 -08003059 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02003060 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02003061 struct brcmf_escan_result_le *escan_result_le;
3062 struct brcmf_bss_info_le *bss_info_le;
3063 struct brcmf_bss_info_le *bss = NULL;
3064 u32 bi_length;
3065 struct brcmf_scan_results *list;
3066 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003067 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02003068
Arend van Spriel5c36b992012-11-14 18:46:05 -08003069 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02003070
Arend van Spriela0f472a2013-04-05 10:57:49 +02003071 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meuleman37a869e2015-10-29 20:33:17 +01003072 brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
Hante Meulemane756af52012-09-11 21:18:52 +02003073 return -EPERM;
3074 }
3075
3076 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003077 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003078 escan_result_le = (struct brcmf_escan_result_le *) data;
3079 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003080 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003081 goto exit;
3082 }
Hante Meulemane756af52012-09-11 21:18:52 +02003083 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003084 brcmf_err("Invalid bss_count %d: ignoring\n",
3085 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02003086 goto exit;
3087 }
3088 bss_info_le = &escan_result_le->bss_info_le;
3089
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003090 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
3091 goto exit;
3092
3093 if (!cfg->scan_request) {
3094 brcmf_dbg(SCAN, "result without cfg80211 request\n");
3095 goto exit;
3096 }
3097
Hante Meulemane756af52012-09-11 21:18:52 +02003098 bi_length = le32_to_cpu(bss_info_le->length);
3099 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
3100 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003101 brcmf_err("Invalid bss_info length %d: ignoring\n",
3102 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02003103 goto exit;
3104 }
3105
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003106 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02003107 BIT(NL80211_IFTYPE_ADHOC))) {
3108 if (le16_to_cpu(bss_info_le->capability) &
3109 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003110 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003111 goto exit;
3112 }
3113 }
3114
3115 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003116 cfg->escan_info.escan_buf;
Hante Meulemand5367332016-02-17 11:26:51 +01003117 if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003118 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003119 goto exit;
3120 }
3121
3122 for (i = 0; i < list->count; i++) {
3123 bss = bss ? (struct brcmf_bss_info_le *)
3124 ((unsigned char *)bss +
3125 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02003126 if (brcmf_compare_update_same_bss(cfg, bss,
3127 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02003128 goto exit;
3129 }
Hante Meulemand5367332016-02-17 11:26:51 +01003130 memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
3131 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02003132 list->version = le32_to_cpu(bss_info_le->version);
3133 list->buflen += bi_length;
3134 list->count++;
3135 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003136 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003137 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
3138 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003139 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003140 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003141 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02003142 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02003143 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003144 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
3145 status);
Hante Meulemane756af52012-09-11 21:18:52 +02003146 }
3147exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003148 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02003149}
3150
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003151static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003152{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003153 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3154 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003155 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3156 /* Init scan_timeout timer */
3157 init_timer(&cfg->escan_timeout);
3158 cfg->escan_timeout.data = (unsigned long) cfg;
3159 cfg->escan_timeout.function = brcmf_escan_timeout;
3160 INIT_WORK(&cfg->escan_timeout_work,
3161 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003162}
3163
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003164/* PFN result doesn't have all the info which are required by the supplicant
3165 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3166 * via wl_inform_single_bss in the required format. Escan does require the
3167 * scan request in the form of cfg80211_scan_request. For timebeing, create
3168 * cfg80211_scan_request one out of the received PNO event.
3169 */
3170static s32
3171brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
3172 const struct brcmf_event_msg *e, void *data)
3173{
3174 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3175 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3176 struct cfg80211_scan_request *request = NULL;
3177 struct cfg80211_ssid *ssid = NULL;
3178 struct ieee80211_channel *channel = NULL;
3179 struct wiphy *wiphy = cfg_to_wiphy(cfg);
3180 int err = 0;
3181 int channel_req = 0;
3182 int band = 0;
3183 struct brcmf_pno_scanresults_le *pfn_result;
3184 u32 result_count;
3185 u32 status;
3186
3187 brcmf_dbg(SCAN, "Enter\n");
3188
Hante Meuleman0aedbca2016-02-17 11:26:54 +01003189 if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
3190 brcmf_dbg(SCAN, "Event data to small. Ignore\n");
3191 return 0;
3192 }
3193
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003194 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
3195 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
3196 return 0;
3197 }
3198
3199 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3200 result_count = le32_to_cpu(pfn_result->count);
3201 status = le32_to_cpu(pfn_result->status);
3202
3203 /* PFN event is limited to fit 512 bytes so we may get
3204 * multiple NET_FOUND events. For now place a warning here.
3205 */
3206 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3207 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
3208 if (result_count > 0) {
3209 int i;
3210
3211 request = kzalloc(sizeof(*request), GFP_KERNEL);
3212 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3213 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
3214 if (!request || !ssid || !channel) {
3215 err = -ENOMEM;
3216 goto out_err;
3217 }
3218
3219 request->wiphy = wiphy;
3220 data += sizeof(struct brcmf_pno_scanresults_le);
3221 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3222
3223 for (i = 0; i < result_count; i++) {
3224 netinfo = &netinfo_start[i];
3225 if (!netinfo) {
3226 brcmf_err("Invalid netinfo ptr. index: %d\n",
3227 i);
3228 err = -EINVAL;
3229 goto out_err;
3230 }
3231
3232 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3233 netinfo->SSID, netinfo->channel);
3234 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3235 ssid[i].ssid_len = netinfo->SSID_len;
3236 request->n_ssids++;
3237
3238 channel_req = netinfo->channel;
3239 if (channel_req <= CH_MAX_2G_CHANNEL)
3240 band = NL80211_BAND_2GHZ;
3241 else
3242 band = NL80211_BAND_5GHZ;
3243 channel[i].center_freq =
3244 ieee80211_channel_to_frequency(channel_req,
3245 band);
3246 channel[i].band = band;
3247 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3248 request->channels[i] = &channel[i];
3249 request->n_channels++;
3250 }
3251
3252 /* assign parsed ssid array */
3253 if (request->n_ssids)
3254 request->ssids = &ssid[0];
3255
3256 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3257 /* Abort any on-going scan */
3258 brcmf_abort_scanning(cfg);
3259 }
3260
3261 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3262 cfg->escan_info.run = brcmf_run_escan;
3263 err = brcmf_do_escan(cfg, wiphy, ifp, request);
3264 if (err) {
3265 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3266 goto out_err;
3267 }
3268 cfg->sched_escan = true;
3269 cfg->scan_request = request;
3270 } else {
3271 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
3272 goto out_err;
3273 }
3274
3275 kfree(ssid);
3276 kfree(channel);
3277 kfree(request);
3278 return 0;
3279
3280out_err:
3281 kfree(ssid);
3282 kfree(channel);
3283 kfree(request);
3284 cfg80211_sched_scan_stopped(wiphy);
3285 return err;
3286}
3287
3288static int brcmf_dev_pno_clean(struct net_device *ndev)
3289{
3290 int ret;
3291
3292 /* Disable pfn */
3293 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
3294 if (ret == 0) {
3295 /* clear pfn */
3296 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3297 NULL, 0);
3298 }
3299 if (ret < 0)
3300 brcmf_err("failed code %d\n", ret);
3301
3302 return ret;
3303}
3304
3305static int brcmf_dev_pno_config(struct brcmf_if *ifp,
3306 struct cfg80211_sched_scan_request *request)
3307{
3308 struct brcmf_pno_param_le pfn_param;
3309 struct brcmf_pno_macaddr_le pfn_mac;
3310 s32 err;
3311 u8 *mac_mask;
3312 int i;
3313
3314 memset(&pfn_param, 0, sizeof(pfn_param));
3315 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3316
3317 /* set extra pno params */
3318 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3319 pfn_param.repeat = BRCMF_PNO_REPEAT;
3320 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3321
3322 /* set up pno scan fr */
3323 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3324
3325 err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
3326 sizeof(pfn_param));
3327 if (err) {
3328 brcmf_err("pfn_set failed, err=%d\n", err);
3329 return err;
3330 }
3331
3332 /* Find out if mac randomization should be turned on */
3333 if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
3334 return 0;
3335
3336 pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
3337 pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
3338
3339 memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN);
3340 mac_mask = request->mac_addr_mask;
3341 for (i = 0; i < ETH_ALEN; i++) {
3342 pfn_mac.mac[i] &= mac_mask[i];
3343 pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
3344 }
3345 /* Clear multi bit */
3346 pfn_mac.mac[0] &= 0xFE;
3347 /* Set locally administered */
3348 pfn_mac.mac[0] |= 0x02;
3349
3350 err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
3351 sizeof(pfn_mac));
3352 if (err)
3353 brcmf_err("pfn_macaddr failed, err=%d\n", err);
3354
3355 return err;
3356}
3357
3358static int
3359brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3360 struct net_device *ndev,
3361 struct cfg80211_sched_scan_request *request)
3362{
3363 struct brcmf_if *ifp = netdev_priv(ndev);
3364 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
3365 struct brcmf_pno_net_param_le pfn;
3366 int i;
3367 int ret = 0;
3368
3369 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
3370 request->n_match_sets, request->n_ssids);
3371 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3372 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
3373 return -EAGAIN;
3374 }
3375 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3376 brcmf_err("Scanning suppressed: status (%lu)\n",
3377 cfg->scan_status);
3378 return -EAGAIN;
3379 }
3380
3381 if (!request->n_ssids || !request->n_match_sets) {
3382 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
3383 request->n_ssids);
3384 return -EINVAL;
3385 }
3386
3387 if (request->n_ssids > 0) {
3388 for (i = 0; i < request->n_ssids; i++) {
3389 /* Active scan req for ssids */
3390 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3391 request->ssids[i].ssid);
3392
3393 /* match_set ssids is a supert set of n_ssid list,
3394 * so we need not add these set separately.
3395 */
3396 }
3397 }
3398
3399 if (request->n_match_sets > 0) {
3400 /* clean up everything */
3401 ret = brcmf_dev_pno_clean(ndev);
3402 if (ret < 0) {
3403 brcmf_err("failed error=%d\n", ret);
3404 return ret;
3405 }
3406
3407 /* configure pno */
3408 if (brcmf_dev_pno_config(ifp, request))
3409 return -EINVAL;
3410
3411 /* configure each match set */
3412 for (i = 0; i < request->n_match_sets; i++) {
3413 struct cfg80211_ssid *ssid;
3414 u32 ssid_len;
3415
3416 ssid = &request->match_sets[i].ssid;
3417 ssid_len = ssid->ssid_len;
3418
3419 if (!ssid_len) {
3420 brcmf_err("skip broadcast ssid\n");
3421 continue;
3422 }
3423 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3424 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3425 pfn.wsec = cpu_to_le32(0);
3426 pfn.infra = cpu_to_le32(1);
3427 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3428 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3429 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
3430 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
3431 sizeof(pfn));
3432 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3433 ret == 0 ? "set" : "failed", ssid->ssid);
3434 }
3435 /* Enable the PNO */
3436 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
3437 brcmf_err("PNO enable failed!! ret=%d\n", ret);
3438 return -EINVAL;
3439 }
3440 } else {
3441 return -EINVAL;
3442 }
3443
3444 return 0;
3445}
3446
3447static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3448 struct net_device *ndev)
3449{
3450 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3451
3452 brcmf_dbg(SCAN, "enter\n");
3453 brcmf_dev_pno_clean(ndev);
3454 if (cfg->sched_escan)
3455 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
3456 return 0;
3457}
3458
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003459static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003460{
3461 if (ms < 1000 / HZ) {
3462 cond_resched();
3463 mdelay(ms);
3464 } else {
3465 msleep(ms);
3466 }
3467}
3468
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003469static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3470 u8 *pattern, u32 patternsize, u8 *mask,
3471 u32 packet_offset)
3472{
3473 struct brcmf_fil_wowl_pattern_le *filter;
3474 u32 masksize;
3475 u32 patternoffset;
3476 u8 *buf;
3477 u32 bufsize;
3478 s32 ret;
3479
3480 masksize = (patternsize + 7) / 8;
3481 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3482
3483 bufsize = sizeof(*filter) + patternsize + masksize;
3484 buf = kzalloc(bufsize, GFP_KERNEL);
3485 if (!buf)
3486 return -ENOMEM;
3487 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3488
3489 memcpy(filter->cmd, cmd, 4);
3490 filter->masksize = cpu_to_le32(masksize);
3491 filter->offset = cpu_to_le32(packet_offset);
3492 filter->patternoffset = cpu_to_le32(patternoffset);
3493 filter->patternsize = cpu_to_le32(patternsize);
3494 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3495
3496 if ((mask) && (masksize))
3497 memcpy(buf + sizeof(*filter), mask, masksize);
3498 if ((pattern) && (patternsize))
3499 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3500
3501 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3502
3503 kfree(buf);
3504 return ret;
3505}
3506
Hante Meuleman3021ad92016-01-05 11:05:45 +01003507static s32
3508brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
3509 void *data)
3510{
3511 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3512 struct brcmf_pno_scanresults_le *pfn_result;
3513 struct brcmf_pno_net_info_le *netinfo;
3514
3515 brcmf_dbg(SCAN, "Enter\n");
3516
Hante Meuleman0aedbca2016-02-17 11:26:54 +01003517 if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
3518 brcmf_dbg(SCAN, "Event data to small. Ignore\n");
3519 return 0;
3520 }
3521
Hante Meuleman3021ad92016-01-05 11:05:45 +01003522 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3523
3524 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
3525 brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
3526 return 0;
3527 }
3528
3529 if (le32_to_cpu(pfn_result->count) < 1) {
3530 brcmf_err("Invalid result count, expected 1 (%d)\n",
3531 le32_to_cpu(pfn_result->count));
3532 return -EINVAL;
3533 }
3534
3535 data += sizeof(struct brcmf_pno_scanresults_le);
3536 netinfo = (struct brcmf_pno_net_info_le *)data;
3537 memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
3538 cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
3539 cfg->wowl.nd->n_channels = 1;
3540 cfg->wowl.nd->channels[0] =
3541 ieee80211_channel_to_frequency(netinfo->channel,
3542 netinfo->channel <= CH_MAX_2G_CHANNEL ?
3543 NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
3544 cfg->wowl.nd_info->n_matches = 1;
3545 cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
3546
3547 /* Inform (the resume task) that the net detect information was recvd */
3548 cfg->wowl.nd_data_completed = true;
3549 wake_up(&cfg->wowl.nd_data_wait);
3550
3551 return 0;
3552}
3553
Hante Meulemanaeb64222015-10-29 20:33:19 +01003554#ifdef CONFIG_PM
3555
3556static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3557{
Hante Meuleman3021ad92016-01-05 11:05:45 +01003558 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemanaeb64222015-10-29 20:33:19 +01003559 struct brcmf_wowl_wakeind_le wake_ind_le;
3560 struct cfg80211_wowlan_wakeup wakeup_data;
3561 struct cfg80211_wowlan_wakeup *wakeup;
3562 u32 wakeind;
3563 s32 err;
Hante Meuleman3021ad92016-01-05 11:05:45 +01003564 int timeout;
Hante Meulemanaeb64222015-10-29 20:33:19 +01003565
3566 err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
3567 sizeof(wake_ind_le));
Hante Meuleman3021ad92016-01-05 11:05:45 +01003568 if (err) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003569 brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
3570 return;
3571 }
3572
3573 wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
3574 if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
Hante Meuleman3021ad92016-01-05 11:05:45 +01003575 BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
3576 BRCMF_WOWL_PFN_FOUND)) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003577 wakeup = &wakeup_data;
3578 memset(&wakeup_data, 0, sizeof(wakeup_data));
3579 wakeup_data.pattern_idx = -1;
3580
3581 if (wakeind & BRCMF_WOWL_MAGIC) {
3582 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
3583 wakeup_data.magic_pkt = true;
3584 }
3585 if (wakeind & BRCMF_WOWL_DIS) {
3586 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
3587 wakeup_data.disconnect = true;
3588 }
3589 if (wakeind & BRCMF_WOWL_BCN) {
3590 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
3591 wakeup_data.disconnect = true;
3592 }
3593 if (wakeind & BRCMF_WOWL_RETR) {
3594 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
3595 wakeup_data.disconnect = true;
3596 }
3597 if (wakeind & BRCMF_WOWL_NET) {
3598 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
3599 /* For now always map to pattern 0, no API to get
3600 * correct information available at the moment.
3601 */
3602 wakeup_data.pattern_idx = 0;
3603 }
Hante Meuleman3021ad92016-01-05 11:05:45 +01003604 if (wakeind & BRCMF_WOWL_PFN_FOUND) {
3605 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
3606 timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
3607 cfg->wowl.nd_data_completed,
3608 BRCMF_ND_INFO_TIMEOUT);
3609 if (!timeout)
3610 brcmf_err("No result for wowl net detect\n");
3611 else
3612 wakeup_data.net_detect = cfg->wowl.nd_info;
3613 }
Hante Meuleman5c22fb82016-02-17 11:27:03 +01003614 if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
3615 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
3616 wakeup_data.gtk_rekey_failure = true;
3617 }
Hante Meulemanaeb64222015-10-29 20:33:19 +01003618 } else {
3619 wakeup = NULL;
3620 }
3621 cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
3622}
3623
3624#else
3625
3626static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3627{
3628}
3629
3630#endif /* CONFIG_PM */
3631
Arend van Spriel5b435de2011-10-05 13:19:03 +02003632static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3633{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003634 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3635 struct net_device *ndev = cfg_to_ndev(cfg);
3636 struct brcmf_if *ifp = netdev_priv(ndev);
3637
Arend van Sprield96b8012012-12-05 15:26:02 +01003638 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003639
Hante Meuleman3021ad92016-01-05 11:05:45 +01003640 if (cfg->wowl.active) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003641 brcmf_report_wowl_wakeind(wiphy, ifp);
3642 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
3643 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman73ef9e62016-02-17 11:27:05 +01003644 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
3645 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003646 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
Hante Meuleman3021ad92016-01-05 11:05:45 +01003647 cfg->wowl.pre_pmmode);
3648 cfg->wowl.active = false;
3649 if (cfg->wowl.nd_enabled) {
3650 brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev);
3651 brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
3652 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
3653 brcmf_notify_sched_scan_results);
3654 cfg->wowl.nd_enabled = false;
3655 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003656 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003657 return 0;
3658}
3659
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003660static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3661 struct brcmf_if *ifp,
3662 struct cfg80211_wowlan *wowl)
3663{
3664 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003665 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003666
3667 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3668
Hante Meuleman73ef9e62016-02-17 11:27:05 +01003669 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
3670 brcmf_configure_arp_nd_offload(ifp, false);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003671 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003672 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3673
3674 wowl_config = 0;
3675 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003676 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003677 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003678 wowl_config |= BRCMF_WOWL_MAGIC;
3679 if ((wowl->patterns) && (wowl->n_patterns)) {
3680 wowl_config |= BRCMF_WOWL_NET;
3681 for (i = 0; i < wowl->n_patterns; i++) {
3682 brcmf_config_wowl_pattern(ifp, "add",
3683 (u8 *)wowl->patterns[i].pattern,
3684 wowl->patterns[i].pattern_len,
3685 (u8 *)wowl->patterns[i].mask,
3686 wowl->patterns[i].pkt_offset);
3687 }
3688 }
Hante Meuleman3021ad92016-01-05 11:05:45 +01003689 if (wowl->nd_config) {
3690 brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
3691 wowl->nd_config);
3692 wowl_config |= BRCMF_WOWL_PFN_FOUND;
3693
3694 cfg->wowl.nd_data_completed = false;
3695 cfg->wowl.nd_enabled = true;
3696 /* Now reroute the event for PFN to the wowl function. */
3697 brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
3698 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
3699 brcmf_wowl_nd_results);
3700 }
Hante Meuleman5c22fb82016-02-17 11:27:03 +01003701 if (wowl->gtk_rekey_failure)
3702 wowl_config |= BRCMF_WOWL_GTK_FAILURE;
Hante Meuleman3021ad92016-01-05 11:05:45 +01003703 if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
3704 wowl_config |= BRCMF_WOWL_UNASSOC;
3705
Hante Meuleman28b285a2016-04-11 11:35:22 +02003706 brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear",
3707 sizeof(struct brcmf_wowl_wakeind_le));
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003708 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3709 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3710 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003711 cfg->wowl.active = true;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003712}
3713
Arend van Spriel5b435de2011-10-05 13:19:03 +02003714static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003715 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003716{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003717 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3718 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003719 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003720 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003721
Arend van Sprield96b8012012-12-05 15:26:02 +01003722 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003723
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003724 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003725 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003726 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003727 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003728 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003729
Hante Meuleman3021ad92016-01-05 11:05:45 +01003730 /* Stop scheduled scan */
3731 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
3732 brcmf_cfg80211_sched_scan_stop(wiphy, ndev);
3733
Arend van Spriel7d641072012-10-22 13:55:39 -07003734 /* end any scanning */
3735 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003736 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003737
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003738 if (wowl == NULL) {
3739 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3740 list_for_each_entry(vif, &cfg->vif_list, list) {
3741 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3742 continue;
3743 /* While going to suspend if associated with AP
3744 * disassociate from AP to save power while system is
3745 * in suspended state
3746 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003747 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003748 /* Make sure WPA_Supplicant receives all the event
3749 * generated due to DISASSOC call to the fw to keep
3750 * the state fw and WPA_Supplicant state consistent
3751 */
3752 brcmf_delay(500);
3753 }
3754 /* Configure MPC */
3755 brcmf_set_mpc(ifp, 1);
3756
3757 } else {
3758 /* Configure WOWL paramaters */
3759 brcmf_configure_wowl(cfg, ifp, wowl);
3760 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003761
Arend van Spriel7d641072012-10-22 13:55:39 -07003762exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003763 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003764 /* clear any scanning activity */
3765 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003766 return 0;
3767}
3768
3769static __used s32
Hante Meuleman6c404f32015-12-10 13:43:03 +01003770brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003771{
Hante Meuleman6c404f32015-12-10 13:43:03 +01003772 struct brcmf_pmk_list_le *pmk_list;
3773 int i;
3774 u32 npmk;
3775 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003776
Hante Meuleman6c404f32015-12-10 13:43:03 +01003777 pmk_list = &cfg->pmk_list;
3778 npmk = le32_to_cpu(pmk_list->npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003779
Hante Meuleman6c404f32015-12-10 13:43:03 +01003780 brcmf_dbg(CONN, "No of elements %d\n", npmk);
3781 for (i = 0; i < npmk; i++)
3782 brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003783
Hante Meuleman6c404f32015-12-10 13:43:03 +01003784 err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
3785 sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003786
3787 return err;
3788}
3789
3790static s32
3791brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3792 struct cfg80211_pmksa *pmksa)
3793{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003794 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003795 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003796 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3797 s32 err;
3798 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003799
Arend van Sprield96b8012012-12-05 15:26:02 +01003800 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003801 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003802 return -EIO;
3803
Hante Meuleman6c404f32015-12-10 13:43:03 +01003804 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3805 for (i = 0; i < npmk; i++)
3806 if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003807 break;
Hante Meuleman6c404f32015-12-10 13:43:03 +01003808 if (i < BRCMF_MAXPMKID) {
3809 memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
3810 memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
3811 if (i == npmk) {
3812 npmk++;
3813 cfg->pmk_list.npmk = cpu_to_le32(npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003814 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003815 } else {
3816 brcmf_err("Too many PMKSA entries cached %d\n", npmk);
3817 return -EINVAL;
3818 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003819
Hante Meuleman6c404f32015-12-10 13:43:03 +01003820 brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
3821 for (i = 0; i < WLAN_PMKID_LEN; i += 4)
3822 brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
3823 pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
3824 pmk[npmk].pmkid[i + 3]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003825
Hante Meuleman6c404f32015-12-10 13:43:03 +01003826 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003827
Arend van Sprield96b8012012-12-05 15:26:02 +01003828 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003829 return err;
3830}
3831
3832static s32
3833brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman6c404f32015-12-10 13:43:03 +01003834 struct cfg80211_pmksa *pmksa)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003835{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003836 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003837 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003838 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3839 s32 err;
3840 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003841
Arend van Sprield96b8012012-12-05 15:26:02 +01003842 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003843 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003844 return -EIO;
3845
Hante Meuleman6c404f32015-12-10 13:43:03 +01003846 brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003847
Hante Meuleman6c404f32015-12-10 13:43:03 +01003848 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3849 for (i = 0; i < npmk; i++)
3850 if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003851 break;
3852
Hante Meuleman6c404f32015-12-10 13:43:03 +01003853 if ((npmk > 0) && (i < npmk)) {
3854 for (; i < (npmk - 1); i++) {
3855 memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
3856 memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003857 WLAN_PMKID_LEN);
3858 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003859 memset(&pmk[i], 0, sizeof(*pmk));
3860 cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
3861 } else {
3862 brcmf_err("Cache entry not found\n");
3863 return -EINVAL;
3864 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003865
Hante Meuleman6c404f32015-12-10 13:43:03 +01003866 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003867
Arend van Sprield96b8012012-12-05 15:26:02 +01003868 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003869 return err;
3870
3871}
3872
3873static s32
3874brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3875{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003876 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003877 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003878 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003879
Arend van Sprield96b8012012-12-05 15:26:02 +01003880 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003881 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003882 return -EIO;
3883
Hante Meuleman6c404f32015-12-10 13:43:03 +01003884 memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
3885 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003886
Arend van Sprield96b8012012-12-05 15:26:02 +01003887 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003888 return err;
3889
3890}
3891
Hante Meuleman1f170112013-02-06 18:40:38 +01003892static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003893{
3894 s32 err;
3895
3896 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003897 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003898 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003899 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003900 return err;
3901 }
3902 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003903 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003904 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003905 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003906 return err;
3907 }
3908 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003909 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003910 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003911 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003912 return err;
3913 }
3914
3915 return 0;
3916}
3917
3918static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3919{
3920 if (is_rsn_ie)
3921 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3922
3923 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3924}
3925
3926static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003927brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003928 const struct brcmf_vs_tlv *wpa_ie,
3929 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003930{
3931 u32 auth = 0; /* d11 open authentication */
3932 u16 count;
3933 s32 err = 0;
Hante Meuleman240d61a2016-02-17 11:27:10 +01003934 s32 len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003935 u32 i;
3936 u32 wsec;
3937 u32 pval = 0;
3938 u32 gval = 0;
3939 u32 wpa_auth = 0;
3940 u32 offset;
3941 u8 *data;
3942 u16 rsn_cap;
3943 u32 wme_bss_disable;
Hante Meuleman240d61a2016-02-17 11:27:10 +01003944 u32 mfp;
Hante Meuleman1a873342012-09-27 14:17:54 +02003945
Arend van Sprield96b8012012-12-05 15:26:02 +01003946 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003947 if (wpa_ie == NULL)
3948 goto exit;
3949
3950 len = wpa_ie->len + TLV_HDR_LEN;
3951 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003952 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003953 if (!is_rsn_ie)
3954 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003955 else
3956 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003957
3958 /* check for multicast cipher suite */
3959 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3960 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003961 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003962 goto exit;
3963 }
3964
3965 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3966 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003967 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003968 goto exit;
3969 }
3970 offset += TLV_OUI_LEN;
3971
3972 /* pick up multicast cipher */
3973 switch (data[offset]) {
3974 case WPA_CIPHER_NONE:
3975 gval = 0;
3976 break;
3977 case WPA_CIPHER_WEP_40:
3978 case WPA_CIPHER_WEP_104:
3979 gval = WEP_ENABLED;
3980 break;
3981 case WPA_CIPHER_TKIP:
3982 gval = TKIP_ENABLED;
3983 break;
3984 case WPA_CIPHER_AES_CCM:
3985 gval = AES_ENABLED;
3986 break;
3987 default:
3988 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003989 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003990 goto exit;
3991 }
3992
3993 offset++;
3994 /* walk thru unicast cipher list and pick up what we recognize */
3995 count = data[offset] + (data[offset + 1] << 8);
3996 offset += WPA_IE_SUITE_COUNT_LEN;
3997 /* Check for unicast suite(s) */
3998 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3999 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004000 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004001 goto exit;
4002 }
4003 for (i = 0; i < count; i++) {
4004 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
4005 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004006 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004007 goto exit;
4008 }
4009 offset += TLV_OUI_LEN;
4010 switch (data[offset]) {
4011 case WPA_CIPHER_NONE:
4012 break;
4013 case WPA_CIPHER_WEP_40:
4014 case WPA_CIPHER_WEP_104:
4015 pval |= WEP_ENABLED;
4016 break;
4017 case WPA_CIPHER_TKIP:
4018 pval |= TKIP_ENABLED;
4019 break;
4020 case WPA_CIPHER_AES_CCM:
4021 pval |= AES_ENABLED;
4022 break;
4023 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01004024 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004025 }
4026 offset++;
4027 }
4028 /* walk thru auth management suite list and pick up what we recognize */
4029 count = data[offset] + (data[offset + 1] << 8);
4030 offset += WPA_IE_SUITE_COUNT_LEN;
4031 /* Check for auth key management suite(s) */
4032 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
4033 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004034 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004035 goto exit;
4036 }
4037 for (i = 0; i < count; i++) {
4038 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
4039 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004040 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004041 goto exit;
4042 }
4043 offset += TLV_OUI_LEN;
4044 switch (data[offset]) {
4045 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01004046 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004047 wpa_auth |= WPA_AUTH_NONE;
4048 break;
4049 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01004050 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004051 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
4052 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
4053 break;
4054 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01004055 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004056 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
4057 (wpa_auth |= WPA_AUTH_PSK);
4058 break;
Hante Meuleman240d61a2016-02-17 11:27:10 +01004059 case RSN_AKM_SHA256_PSK:
4060 brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
4061 wpa_auth |= WPA2_AUTH_PSK_SHA256;
4062 break;
4063 case RSN_AKM_SHA256_1X:
4064 brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
4065 wpa_auth |= WPA2_AUTH_1X_SHA256;
4066 break;
Hante Meuleman1a873342012-09-27 14:17:54 +02004067 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01004068 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004069 }
4070 offset++;
4071 }
4072
Hante Meuleman240d61a2016-02-17 11:27:10 +01004073 mfp = BRCMF_MFP_NONE;
Hante Meuleman1a873342012-09-27 14:17:54 +02004074 if (is_rsn_ie) {
4075 wme_bss_disable = 1;
4076 if ((offset + RSN_CAP_LEN) <= len) {
4077 rsn_cap = data[offset] + (data[offset + 1] << 8);
4078 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
4079 wme_bss_disable = 0;
Hante Meuleman240d61a2016-02-17 11:27:10 +01004080 if (rsn_cap & RSN_CAP_MFPR_MASK) {
4081 brcmf_dbg(TRACE, "MFP Required\n");
4082 mfp = BRCMF_MFP_REQUIRED;
4083 /* Firmware only supports mfp required in
4084 * combination with WPA2_AUTH_PSK_SHA256 or
4085 * WPA2_AUTH_1X_SHA256.
4086 */
4087 if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
4088 WPA2_AUTH_1X_SHA256))) {
4089 err = -EINVAL;
4090 goto exit;
4091 }
4092 /* Firmware has requirement that WPA2_AUTH_PSK/
4093 * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
4094 * is to be included in the rsn ie.
4095 */
4096 if (wpa_auth & WPA2_AUTH_PSK_SHA256)
4097 wpa_auth |= WPA2_AUTH_PSK;
4098 else if (wpa_auth & WPA2_AUTH_1X_SHA256)
4099 wpa_auth |= WPA2_AUTH_UNSPECIFIED;
4100 } else if (rsn_cap & RSN_CAP_MFPC_MASK) {
4101 brcmf_dbg(TRACE, "MFP Capable\n");
4102 mfp = BRCMF_MFP_CAPABLE;
4103 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004104 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01004105 offset += RSN_CAP_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02004106 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07004107 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004108 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02004109 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004110 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004111 goto exit;
4112 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01004113
4114 /* Skip PMKID cnt as it is know to be 0 for AP. */
4115 offset += RSN_PMKID_COUNT_LEN;
4116
4117 /* See if there is BIP wpa suite left for MFP */
4118 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
4119 ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
4120 err = brcmf_fil_bsscfg_data_set(ifp, "bip",
4121 &data[offset],
4122 WPA_IE_MIN_OUI_LEN);
4123 if (err < 0) {
4124 brcmf_err("bip error %d\n", err);
4125 goto exit;
4126 }
4127 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004128 }
4129 /* FOR WPS , set SES_OW_ENABLED */
4130 wsec = (pval | gval | SES_OW_ENABLED);
4131
4132 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07004133 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02004134 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004135 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004136 goto exit;
4137 }
4138 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07004139 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004140 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004141 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004142 goto exit;
4143 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01004144 /* Configure MFP, this needs to go after wsec otherwise the wsec command
4145 * will overwrite the values set by MFP
4146 */
4147 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
4148 err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
4149 if (err < 0) {
4150 brcmf_err("mfp error %d\n", err);
4151 goto exit;
4152 }
4153 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004154 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07004155 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02004156 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004157 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004158 goto exit;
4159 }
4160
4161exit:
4162 return err;
4163}
4164
4165static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08004166brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02004167 struct parsed_vndr_ies *vndr_ies)
4168{
Hante Meuleman1a873342012-09-27 14:17:54 +02004169 struct brcmf_vs_tlv *vndrie;
4170 struct brcmf_tlv *ie;
4171 struct parsed_vndr_ie_info *parsed_info;
4172 s32 remaining_len;
4173
4174 remaining_len = (s32)vndr_ie_len;
4175 memset(vndr_ies, 0, sizeof(*vndr_ies));
4176
4177 ie = (struct brcmf_tlv *)vndr_ie_buf;
4178 while (ie) {
4179 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
4180 goto next;
4181 vndrie = (struct brcmf_vs_tlv *)ie;
4182 /* len should be bigger than OUI length + one */
4183 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004184 brcmf_err("invalid vndr ie. length is too small %d\n",
4185 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004186 goto next;
4187 }
4188 /* if wpa or wme ie, do not add ie */
4189 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
4190 ((vndrie->oui_type == WPA_OUI_TYPE) ||
4191 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004192 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004193 goto next;
4194 }
4195
4196 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
4197
4198 /* save vndr ie information */
4199 parsed_info->ie_ptr = (char *)vndrie;
4200 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
4201 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
4202
4203 vndr_ies->count++;
4204
Arend van Sprield96b8012012-12-05 15:26:02 +01004205 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
4206 parsed_info->vndrie.oui[0],
4207 parsed_info->vndrie.oui[1],
4208 parsed_info->vndrie.oui[2],
4209 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02004210
Arend van Spriel9f440b72013-02-08 15:53:36 +01004211 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02004212 break;
4213next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004214 remaining_len -= (ie->len + TLV_HDR_LEN);
4215 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02004216 ie = NULL;
4217 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004218 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
4219 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02004220 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004221 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004222}
4223
4224static u32
4225brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
4226{
4227
Hante Meuleman1a873342012-09-27 14:17:54 +02004228 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
4229 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
4230
Vaishali Thakkar362126c2015-01-16 21:36:14 +05304231 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004232
Vaishali Thakkar362126c2015-01-16 21:36:14 +05304233 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004234
4235 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
4236
4237 return ie_len + VNDR_IE_HDR_SIZE;
4238}
4239
Arend van Spriel1332e262012-11-05 16:22:18 -08004240s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
4241 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02004242{
Arend van Spriel1332e262012-11-05 16:22:18 -08004243 struct brcmf_if *ifp;
4244 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004245 s32 err = 0;
4246 u8 *iovar_ie_buf;
4247 u8 *curr_ie_buf;
4248 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004249 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07004250 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004251 u32 del_add_ie_buf_len = 0;
4252 u32 total_ie_buf_len = 0;
4253 u32 parsed_ie_buf_len = 0;
4254 struct parsed_vndr_ies old_vndr_ies;
4255 struct parsed_vndr_ies new_vndr_ies;
4256 struct parsed_vndr_ie_info *vndrie_info;
4257 s32 i;
4258 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004259 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004260
Arend van Spriel1332e262012-11-05 16:22:18 -08004261 if (!vif)
4262 return -ENODEV;
4263 ifp = vif->ifp;
4264 saved_ie = &vif->saved_ie;
4265
Hante Meuleman37a869e2015-10-29 20:33:17 +01004266 brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
4267 pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02004268 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4269 if (!iovar_ie_buf)
4270 return -ENOMEM;
4271 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01004272 switch (pktflag) {
4273 case BRCMF_VNDR_IE_PRBREQ_FLAG:
4274 mgmt_ie_buf = saved_ie->probe_req_ie;
4275 mgmt_ie_len = &saved_ie->probe_req_ie_len;
4276 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
4277 break;
4278 case BRCMF_VNDR_IE_PRBRSP_FLAG:
4279 mgmt_ie_buf = saved_ie->probe_res_ie;
4280 mgmt_ie_len = &saved_ie->probe_res_ie_len;
4281 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
4282 break;
4283 case BRCMF_VNDR_IE_BEACON_FLAG:
4284 mgmt_ie_buf = saved_ie->beacon_ie;
4285 mgmt_ie_len = &saved_ie->beacon_ie_len;
4286 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
4287 break;
4288 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
4289 mgmt_ie_buf = saved_ie->assoc_req_ie;
4290 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
4291 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
4292 break;
4293 default:
4294 err = -EPERM;
4295 brcmf_err("not suitable type\n");
4296 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004297 }
4298
4299 if (vndr_ie_len > mgmt_ie_buf_len) {
4300 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004301 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004302 goto exit;
4303 }
4304
4305 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
4306 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
4307 ptr = curr_ie_buf;
4308 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
4309 for (i = 0; i < new_vndr_ies.count; i++) {
4310 vndrie_info = &new_vndr_ies.ie_info[i];
4311 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
4312 vndrie_info->ie_len);
4313 parsed_ie_buf_len += vndrie_info->ie_len;
4314 }
4315 }
4316
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004317 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004318 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
4319 (memcmp(mgmt_ie_buf, curr_ie_buf,
4320 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004321 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004322 goto exit;
4323 }
4324
4325 /* parse old vndr_ie */
4326 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
4327
4328 /* make a command to delete old ie */
4329 for (i = 0; i < old_vndr_ies.count; i++) {
4330 vndrie_info = &old_vndr_ies.ie_info[i];
4331
Arend van Sprield96b8012012-12-05 15:26:02 +01004332 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4333 vndrie_info->vndrie.id,
4334 vndrie_info->vndrie.len,
4335 vndrie_info->vndrie.oui[0],
4336 vndrie_info->vndrie.oui[1],
4337 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004338
4339 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4340 vndrie_info->ie_ptr,
4341 vndrie_info->ie_len,
4342 "del");
4343 curr_ie_buf += del_add_ie_buf_len;
4344 total_ie_buf_len += del_add_ie_buf_len;
4345 }
4346 }
4347
4348 *mgmt_ie_len = 0;
4349 /* Add if there is any extra IE */
4350 if (mgmt_ie_buf && parsed_ie_buf_len) {
4351 ptr = mgmt_ie_buf;
4352
4353 remained_buf_len = mgmt_ie_buf_len;
4354
4355 /* make a command to add new ie */
4356 for (i = 0; i < new_vndr_ies.count; i++) {
4357 vndrie_info = &new_vndr_ies.ie_info[i];
4358
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004359 /* verify remained buf size before copy data */
4360 if (remained_buf_len < (vndrie_info->vndrie.len +
4361 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004362 brcmf_err("no space in mgmt_ie_buf: len left %d",
4363 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004364 break;
4365 }
4366 remained_buf_len -= (vndrie_info->ie_len +
4367 VNDR_IE_VSIE_OFFSET);
4368
Arend van Sprield96b8012012-12-05 15:26:02 +01004369 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4370 vndrie_info->vndrie.id,
4371 vndrie_info->vndrie.len,
4372 vndrie_info->vndrie.oui[0],
4373 vndrie_info->vndrie.oui[1],
4374 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004375
4376 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4377 vndrie_info->ie_ptr,
4378 vndrie_info->ie_len,
4379 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02004380
4381 /* save the parsed IE in wl struct */
4382 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4383 vndrie_info->ie_len);
4384 *mgmt_ie_len += vndrie_info->ie_len;
4385
4386 curr_ie_buf += del_add_ie_buf_len;
4387 total_ie_buf_len += del_add_ie_buf_len;
4388 }
4389 }
4390 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004391 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004392 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004393 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004394 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004395 }
4396
4397exit:
4398 kfree(iovar_ie_buf);
4399 return err;
4400}
4401
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004402s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4403{
4404 s32 pktflags[] = {
4405 BRCMF_VNDR_IE_PRBREQ_FLAG,
4406 BRCMF_VNDR_IE_PRBRSP_FLAG,
4407 BRCMF_VNDR_IE_BEACON_FLAG
4408 };
4409 int i;
4410
4411 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4412 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4413
4414 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4415 return 0;
4416}
4417
Hante Meuleman1a873342012-09-27 14:17:54 +02004418static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004419brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4420 struct cfg80211_beacon_data *beacon)
4421{
4422 s32 err;
4423
4424 /* Set Beacon IEs to FW */
4425 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4426 beacon->tail, beacon->tail_len);
4427 if (err) {
4428 brcmf_err("Set Beacon IE Failed\n");
4429 return err;
4430 }
4431 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4432
4433 /* Set Probe Response IEs to FW */
4434 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4435 beacon->proberesp_ies,
4436 beacon->proberesp_ies_len);
4437 if (err)
4438 brcmf_err("Set Probe Resp IE Failed\n");
4439 else
4440 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4441
4442 return err;
4443}
4444
4445static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004446brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4447 struct cfg80211_ap_settings *settings)
4448{
4449 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004450 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004451 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004452 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004453 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004454 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004455 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004456 const struct brcmf_tlv *rsn_ie;
4457 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004458 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004459 enum nl80211_iftype dev_role;
4460 struct brcmf_fil_bss_enable_le bss_enable;
Rafał Miłecki8707e082016-05-27 21:07:19 +02004461 u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
Hante Meulemana44aa402014-12-03 21:05:33 +01004462 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004463 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004464
Arend van Spriel06c01582014-05-12 10:47:37 +02004465 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4466 settings->chandef.chan->hw_value,
4467 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004468 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004469 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4470 settings->ssid, settings->ssid_len, settings->auth_type,
4471 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004472 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004473 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004474
Arend van Spriel98027762014-12-21 12:43:53 +01004475 /* store current 11d setting */
4476 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4477 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4478 settings->beacon.tail_len,
4479 WLAN_EID_COUNTRY);
4480 is_11d = country_ie ? 1 : 0;
4481
Hante Meuleman1a873342012-09-27 14:17:54 +02004482 memset(&ssid_le, 0, sizeof(ssid_le));
4483 if (settings->ssid == NULL || settings->ssid_len == 0) {
4484 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4485 ssid_ie = brcmf_parse_tlvs(
4486 (u8 *)&settings->beacon.head[ie_offset],
4487 settings->beacon.head_len - ie_offset,
4488 WLAN_EID_SSID);
4489 if (!ssid_ie)
4490 return -EINVAL;
4491
4492 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4493 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004494 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004495 } else {
4496 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4497 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4498 }
4499
Hante Meulemana44aa402014-12-03 21:05:33 +01004500 if (!mbss) {
4501 brcmf_set_mpc(ifp, 0);
Franky Lin52f22fb2016-02-17 11:26:55 +01004502 brcmf_configure_arp_nd_offload(ifp, false);
Hante Meulemana44aa402014-12-03 21:05:33 +01004503 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004504
4505 /* find the RSN_IE */
4506 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4507 settings->beacon.tail_len, WLAN_EID_RSN);
4508
4509 /* find the WPA_IE */
4510 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4511 settings->beacon.tail_len);
4512
Hante Meuleman1a873342012-09-27 14:17:54 +02004513 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004514 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004515 if (wpa_ie != NULL) {
4516 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004517 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004518 if (err < 0)
4519 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004520 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004521 struct brcmf_vs_tlv *tmp_ie;
4522
4523 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4524
Hante Meuleman1a873342012-09-27 14:17:54 +02004525 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004526 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004527 if (err < 0)
4528 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004529 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004530 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004531 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004532 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004533 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004534
Hante Meulemana0f07952013-02-08 15:53:47 +01004535 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004536
Rafał Miłecki8707e082016-05-27 21:07:19 +02004537 /* Parameters shared by all radio interfaces */
Hante Meulemana44aa402014-12-03 21:05:33 +01004538 if (!mbss) {
Arend van Spriel98027762014-12-21 12:43:53 +01004539 if (is_11d != ifp->vif->is_11d) {
4540 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4541 is_11d);
4542 if (err < 0) {
4543 brcmf_err("Regulatory Set Error, %d\n", err);
4544 goto exit;
4545 }
4546 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004547 if (settings->beacon_interval) {
4548 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4549 settings->beacon_interval);
4550 if (err < 0) {
4551 brcmf_err("Beacon Interval Set Error, %d\n",
4552 err);
4553 goto exit;
4554 }
4555 }
4556 if (settings->dtim_period) {
4557 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4558 settings->dtim_period);
4559 if (err < 0) {
4560 brcmf_err("DTIM Interval Set Error, %d\n", err);
4561 goto exit;
4562 }
4563 }
4564
Hante Meuleman8abffd82015-10-29 20:33:16 +01004565 if ((dev_role == NL80211_IFTYPE_AP) &&
4566 ((ifp->ifidx == 0) ||
4567 !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004568 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4569 if (err < 0) {
4570 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4571 goto exit;
4572 }
4573 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4574 }
4575
4576 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004577 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004578 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004579 goto exit;
4580 }
Arend van Spriel98027762014-12-21 12:43:53 +01004581 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4582 /* Multiple-BSS should use same 11d configuration */
4583 err = -EINVAL;
4584 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004585 }
Rafał Miłecki8707e082016-05-27 21:07:19 +02004586
4587 /* Interface specific setup */
Hante Meulemana0f07952013-02-08 15:53:47 +01004588 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004589 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4590 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4591
Hante Meulemana0f07952013-02-08 15:53:47 +01004592 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4593 if (err < 0) {
4594 brcmf_err("setting AP mode failed %d\n", err);
4595 goto exit;
4596 }
Rafał Miłecki8707e082016-05-27 21:07:19 +02004597 if (!mbss) {
4598 /* Firmware 10.x requires setting channel after enabling
4599 * AP and before bringing interface up.
4600 */
4601 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
4602 if (err < 0) {
4603 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4604 chanspec, err);
4605 goto exit;
4606 }
4607 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004608 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4609 if (err < 0) {
4610 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4611 goto exit;
4612 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004613 /* On DOWN the firmware removes the WEP keys, reconfigure
4614 * them if they were set.
4615 */
4616 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004617
4618 memset(&join_params, 0, sizeof(join_params));
4619 /* join parameters starts with ssid */
4620 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4621 /* create softap */
4622 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4623 &join_params, sizeof(join_params));
4624 if (err < 0) {
4625 brcmf_err("SET SSID error (%d)\n", err);
4626 goto exit;
4627 }
4628 brcmf_dbg(TRACE, "AP mode configuration complete\n");
Rafał Miłecki8707e082016-05-27 21:07:19 +02004629 } else if (dev_role == NL80211_IFTYPE_P2P_GO) {
4630 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
4631 if (err < 0) {
4632 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4633 chanspec, err);
4634 goto exit;
4635 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004636 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4637 sizeof(ssid_le));
4638 if (err < 0) {
4639 brcmf_err("setting ssid failed %d\n", err);
4640 goto exit;
4641 }
Hante Meuleman37a869e2015-10-29 20:33:17 +01004642 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meulemana0f07952013-02-08 15:53:47 +01004643 bss_enable.enable = cpu_to_le32(1);
4644 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4645 sizeof(bss_enable));
4646 if (err < 0) {
4647 brcmf_err("bss_enable config failed %d\n", err);
4648 goto exit;
4649 }
4650
4651 brcmf_dbg(TRACE, "GO mode configuration complete\n");
Rafał Miłecki8707e082016-05-27 21:07:19 +02004652 } else {
4653 WARN_ON(1);
Hante Meulemana0f07952013-02-08 15:53:47 +01004654 }
Rafał Miłecki8707e082016-05-27 21:07:19 +02004655
Arend van Sprielc1179032012-10-22 13:55:33 -07004656 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004657 brcmf_net_setcarrier(ifp, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004658
4659exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004660 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004661 brcmf_set_mpc(ifp, 1);
Franky Lin52f22fb2016-02-17 11:26:55 +01004662 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meulemanb3657452013-05-27 21:09:53 +02004663 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004664 return err;
4665}
4666
4667static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4668{
Arend van Sprielc1179032012-10-22 13:55:33 -07004669 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004670 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004671 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004672 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004673
Arend van Sprield96b8012012-12-05 15:26:02 +01004674 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004675
Hante Meuleman426d0a52013-02-08 15:53:53 +01004676 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004677 /* Due to most likely deauths outstanding we sleep */
4678 /* first to make sure they get processed by fw. */
4679 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004680
Hante Meulemana44aa402014-12-03 21:05:33 +01004681 if (ifp->vif->mbss) {
4682 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4683 return err;
4684 }
4685
Hante Meuleman5c33a942013-04-02 21:06:18 +02004686 memset(&join_params, 0, sizeof(join_params));
4687 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4688 &join_params, sizeof(join_params));
4689 if (err < 0)
4690 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004691 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004692 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004693 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004694 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4695 if (err < 0)
4696 brcmf_err("setting AP mode failed %d\n", err);
4697 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4698 if (err < 0)
4699 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004700 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4701 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004702 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4703 ifp->vif->is_11d);
4704 if (err < 0)
4705 brcmf_err("restoring REGULATORY setting failed %d\n",
4706 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004707 /* Bring device back up so it can be used again */
4708 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4709 if (err < 0)
4710 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004711 } else {
Hante Meuleman37a869e2015-10-29 20:33:17 +01004712 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004713 bss_enable.enable = cpu_to_le32(0);
4714 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4715 sizeof(bss_enable));
4716 if (err < 0)
4717 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004718 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004719 brcmf_set_mpc(ifp, 1);
Franky Lin52f22fb2016-02-17 11:26:55 +01004720 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004721 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004722 brcmf_net_setcarrier(ifp, false);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004723
Hante Meuleman1a873342012-09-27 14:17:54 +02004724 return err;
4725}
4726
Hante Meulemana0f07952013-02-08 15:53:47 +01004727static s32
4728brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4729 struct cfg80211_beacon_data *info)
4730{
Hante Meulemana0f07952013-02-08 15:53:47 +01004731 struct brcmf_if *ifp = netdev_priv(ndev);
4732 s32 err;
4733
4734 brcmf_dbg(TRACE, "Enter\n");
4735
Hante Meulemana0f07952013-02-08 15:53:47 +01004736 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4737
4738 return err;
4739}
4740
Hante Meuleman1a873342012-09-27 14:17:54 +02004741static int
4742brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004743 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004744{
Hante Meulemana0f07952013-02-08 15:53:47 +01004745 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004746 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004747 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004748 s32 err;
4749
Jouni Malinen89c771e2014-10-10 20:52:40 +03004750 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004751 return -EFAULT;
4752
Jouni Malinen89c771e2014-10-10 20:52:40 +03004753 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004754
Hante Meulemana0f07952013-02-08 15:53:47 +01004755 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4756 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004757 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004758 return -EIO;
4759
Jouni Malinen89c771e2014-10-10 20:52:40 +03004760 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004761 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004762 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004763 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004764 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004765 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004766
Arend van Sprield96b8012012-12-05 15:26:02 +01004767 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004768 return err;
4769}
4770
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004771static int
4772brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4773 const u8 *mac, struct station_parameters *params)
4774{
4775 struct brcmf_if *ifp = netdev_priv(ndev);
4776 s32 err;
4777
4778 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4779 params->sta_flags_mask, params->sta_flags_set);
4780
4781 /* Ignore all 00 MAC */
4782 if (is_zero_ether_addr(mac))
4783 return 0;
4784
4785 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4786 return 0;
4787
4788 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4789 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4790 (void *)mac, ETH_ALEN);
4791 else
4792 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4793 (void *)mac, ETH_ALEN);
4794 if (err < 0)
4795 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4796
4797 return err;
4798}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004799
4800static void
4801brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4802 struct wireless_dev *wdev,
4803 u16 frame_type, bool reg)
4804{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004805 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004806 u16 mgmt_type;
4807
4808 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4809
4810 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004811 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004812 if (reg)
4813 vif->mgmt_rx_reg |= BIT(mgmt_type);
4814 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004815 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004816}
4817
4818
4819static int
4820brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004821 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004822{
4823 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004824 struct ieee80211_channel *chan = params->chan;
4825 const u8 *buf = params->buf;
4826 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004827 const struct ieee80211_mgmt *mgmt;
4828 struct brcmf_cfg80211_vif *vif;
4829 s32 err = 0;
4830 s32 ie_offset;
4831 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004832 struct brcmf_fil_action_frame_le *action_frame;
4833 struct brcmf_fil_af_params_le *af_params;
4834 bool ack;
4835 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004836 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004837
4838 brcmf_dbg(TRACE, "Enter\n");
4839
4840 *cookie = 0;
4841
4842 mgmt = (const struct ieee80211_mgmt *)buf;
4843
Hante Meulemana0f07952013-02-08 15:53:47 +01004844 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4845 brcmf_err("Driver only allows MGMT packet type\n");
4846 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004847 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004848
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004849 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4850
Hante Meulemana0f07952013-02-08 15:53:47 +01004851 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4852 /* Right now the only reason to get a probe response */
4853 /* is for p2p listen response or for p2p GO from */
4854 /* wpa_supplicant. Unfortunately the probe is send */
4855 /* on primary ndev, while dongle wants it on the p2p */
4856 /* vif. Since this is only reason for a probe */
4857 /* response to be sent, the vif is taken from cfg. */
4858 /* If ever desired to send proberesp for non p2p */
4859 /* response then data should be checked for */
4860 /* "DIRECT-". Note in future supplicant will take */
4861 /* dedicated p2p wdev to do this and then this 'hack'*/
4862 /* is not needed anymore. */
4863 ie_offset = DOT11_MGMT_HDR_LEN +
4864 DOT11_BCN_PRB_FIXED_LEN;
4865 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004866 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4867 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4868 err = brcmf_vif_set_mgmt_ie(vif,
4869 BRCMF_VNDR_IE_PRBRSP_FLAG,
4870 &buf[ie_offset],
4871 ie_len);
4872 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4873 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004874 } else if (ieee80211_is_action(mgmt->frame_control)) {
4875 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4876 if (af_params == NULL) {
4877 brcmf_err("unable to allocate frame\n");
4878 err = -ENOMEM;
4879 goto exit;
4880 }
4881 action_frame = &af_params->action_frame;
4882 /* Add the packet Id */
4883 action_frame->packet_id = cpu_to_le32(*cookie);
4884 /* Add BSSID */
4885 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4886 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4887 /* Add the length exepted for 802.11 header */
4888 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004889 /* Add the channel. Use the one specified as parameter if any or
4890 * the current one (got from the firmware) otherwise
4891 */
4892 if (chan)
4893 freq = chan->center_freq;
4894 else
4895 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4896 &freq);
4897 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004898 af_params->channel = cpu_to_le32(chan_nr);
4899
4900 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4901 le16_to_cpu(action_frame->len));
4902
4903 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004904 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004905
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004906 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004907 af_params);
4908
4909 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4910 GFP_KERNEL);
4911 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004912 } else {
4913 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4914 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4915 }
4916
Hante Meuleman18e2f612013-02-08 15:53:49 +01004917exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004918 return err;
4919}
4920
4921
4922static int
4923brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4924 struct wireless_dev *wdev,
4925 u64 cookie)
4926{
4927 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4928 struct brcmf_cfg80211_vif *vif;
4929 int err = 0;
4930
4931 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4932
4933 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4934 if (vif == NULL) {
4935 brcmf_err("No p2p device available for probe response\n");
4936 err = -ENODEV;
4937 goto exit;
4938 }
4939 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4940exit:
4941 return err;
4942}
4943
Rafał Miłeckiee6e7aa2016-05-20 13:38:58 +02004944static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
4945 struct wireless_dev *wdev,
4946 struct cfg80211_chan_def *chandef)
4947{
4948 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4949 struct net_device *ndev = wdev->netdev;
4950 struct brcmf_if *ifp;
4951 struct brcmu_chan ch;
4952 enum nl80211_band band = 0;
4953 enum nl80211_chan_width width = 0;
4954 u32 chanspec;
4955 int freq, err;
4956
4957 if (!ndev)
4958 return -ENODEV;
4959 ifp = netdev_priv(ndev);
4960
4961 err = brcmf_fil_iovar_int_get(ifp, "chanspec", &chanspec);
4962 if (err) {
4963 brcmf_err("chanspec failed (%d)\n", err);
4964 return err;
4965 }
4966
4967 ch.chspec = chanspec;
4968 cfg->d11inf.decchspec(&ch);
4969
4970 switch (ch.band) {
4971 case BRCMU_CHAN_BAND_2G:
4972 band = NL80211_BAND_2GHZ;
4973 break;
4974 case BRCMU_CHAN_BAND_5G:
4975 band = NL80211_BAND_5GHZ;
4976 break;
4977 }
4978
4979 switch (ch.bw) {
4980 case BRCMU_CHAN_BW_80:
4981 width = NL80211_CHAN_WIDTH_80;
4982 break;
4983 case BRCMU_CHAN_BW_40:
4984 width = NL80211_CHAN_WIDTH_40;
4985 break;
4986 case BRCMU_CHAN_BW_20:
4987 width = NL80211_CHAN_WIDTH_20;
4988 break;
4989 case BRCMU_CHAN_BW_80P80:
4990 width = NL80211_CHAN_WIDTH_80P80;
4991 break;
4992 case BRCMU_CHAN_BW_160:
4993 width = NL80211_CHAN_WIDTH_160;
4994 break;
4995 }
4996
4997 freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
4998 chandef->chan = ieee80211_get_channel(wiphy, freq);
4999 chandef->width = width;
5000 chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);
5001 chandef->center_freq2 = 0;
5002
5003 return 0;
5004}
5005
Piotr Haber61730d42013-04-23 12:53:12 +02005006static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
5007 struct wireless_dev *wdev,
5008 enum nl80211_crit_proto_id proto,
5009 u16 duration)
5010{
5011 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5012 struct brcmf_cfg80211_vif *vif;
5013
5014 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5015
5016 /* only DHCP support for now */
5017 if (proto != NL80211_CRIT_PROTO_DHCP)
5018 return -EINVAL;
5019
5020 /* suppress and abort scanning */
5021 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
5022 brcmf_abort_scanning(cfg);
5023
5024 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
5025}
5026
5027static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
5028 struct wireless_dev *wdev)
5029{
5030 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5031 struct brcmf_cfg80211_vif *vif;
5032
5033 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5034
5035 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
5036 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
5037}
5038
Hante Meuleman70b7d942014-07-30 13:20:07 +02005039static s32
5040brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
5041 const struct brcmf_event_msg *e, void *data)
5042{
5043 switch (e->reason) {
5044 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
5045 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
5046 break;
5047 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
5048 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
5049 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5050 break;
5051 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
5052 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
5053 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5054 break;
5055 }
5056
5057 return 0;
5058}
5059
Arend van Spriel89c2f382013-08-10 12:27:25 +02005060static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
5061{
5062 int ret;
5063
5064 switch (oper) {
5065 case NL80211_TDLS_DISCOVERY_REQ:
5066 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
5067 break;
5068 case NL80211_TDLS_SETUP:
5069 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
5070 break;
5071 case NL80211_TDLS_TEARDOWN:
5072 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
5073 break;
5074 default:
5075 brcmf_err("unsupported operation: %d\n", oper);
5076 ret = -EOPNOTSUPP;
5077 }
5078 return ret;
5079}
5080
5081static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02005082 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02005083 enum nl80211_tdls_operation oper)
5084{
5085 struct brcmf_if *ifp;
5086 struct brcmf_tdls_iovar_le info;
5087 int ret = 0;
5088
5089 ret = brcmf_convert_nl80211_tdls_oper(oper);
5090 if (ret < 0)
5091 return ret;
5092
5093 ifp = netdev_priv(ndev);
5094 memset(&info, 0, sizeof(info));
5095 info.mode = (u8)ret;
5096 if (peer)
5097 memcpy(info.ea, peer, ETH_ALEN);
5098
5099 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
5100 &info, sizeof(info));
5101 if (ret < 0)
5102 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
5103
5104 return ret;
5105}
5106
Hante Meuleman5c22fb82016-02-17 11:27:03 +01005107#ifdef CONFIG_PM
5108static int
5109brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
5110 struct cfg80211_gtk_rekey_data *gtk)
5111{
5112 struct brcmf_if *ifp = netdev_priv(ndev);
5113 struct brcmf_gtk_keyinfo_le gtk_le;
5114 int ret;
5115
5116 brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
5117
5118 memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
5119 memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
5120 memcpy(gtk_le.replay_counter, gtk->replay_ctr,
5121 sizeof(gtk_le.replay_counter));
5122
5123 ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", &gtk_le,
5124 sizeof(gtk_le));
5125 if (ret < 0)
5126 brcmf_err("gtk_key_info iovar failed: ret=%d\n", ret);
5127
5128 return ret;
5129}
5130#endif
5131
5132static struct cfg80211_ops brcmf_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01005133 .add_virtual_intf = brcmf_cfg80211_add_iface,
5134 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005135 .change_virtual_intf = brcmf_cfg80211_change_iface,
5136 .scan = brcmf_cfg80211_scan,
5137 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
5138 .join_ibss = brcmf_cfg80211_join_ibss,
5139 .leave_ibss = brcmf_cfg80211_leave_ibss,
5140 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02005141 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005142 .set_tx_power = brcmf_cfg80211_set_tx_power,
5143 .get_tx_power = brcmf_cfg80211_get_tx_power,
5144 .add_key = brcmf_cfg80211_add_key,
5145 .del_key = brcmf_cfg80211_del_key,
5146 .get_key = brcmf_cfg80211_get_key,
5147 .set_default_key = brcmf_cfg80211_config_default_key,
5148 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
5149 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005150 .connect = brcmf_cfg80211_connect,
5151 .disconnect = brcmf_cfg80211_disconnect,
5152 .suspend = brcmf_cfg80211_suspend,
5153 .resume = brcmf_cfg80211_resume,
5154 .set_pmksa = brcmf_cfg80211_set_pmksa,
5155 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02005156 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02005157 .start_ap = brcmf_cfg80211_start_ap,
5158 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01005159 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02005160 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01005161 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02005162 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
5163 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005164 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
5165 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
5166 .remain_on_channel = brcmf_p2p_remain_on_channel,
5167 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Rafał Miłeckiee6e7aa2016-05-20 13:38:58 +02005168 .get_channel = brcmf_cfg80211_get_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02005169 .start_p2p_device = brcmf_p2p_start_device,
5170 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02005171 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
5172 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02005173 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005174};
5175
Arend van Spriel3eacf862012-10-22 13:55:30 -07005176struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Rafał Miłecki26072332016-06-06 23:03:55 +02005177 enum nl80211_iftype type)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005178{
Hante Meulemana44aa402014-12-03 21:05:33 +01005179 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07005180 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01005181 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005182
Arend van Spriel33a6b152013-02-08 15:53:39 +01005183 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01005184 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07005185 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
5186 if (!vif)
5187 return ERR_PTR(-ENOMEM);
5188
5189 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005190 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07005191
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005192 brcmf_init_prof(&vif->profile);
5193
Hante Meulemana44aa402014-12-03 21:05:33 +01005194 if (type == NL80211_IFTYPE_AP) {
5195 mbss = false;
5196 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
5197 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
5198 mbss = true;
5199 break;
5200 }
5201 }
5202 vif->mbss = mbss;
5203 }
5204
Arend van Spriel3eacf862012-10-22 13:55:30 -07005205 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07005206 return vif;
5207}
5208
Arend van Spriel427dec52014-01-06 12:40:47 +01005209void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07005210{
Arend van Spriel3eacf862012-10-22 13:55:30 -07005211 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07005212 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005213}
5214
Arend van Spriel9df4d542014-01-06 12:40:49 +01005215void brcmf_cfg80211_free_netdev(struct net_device *ndev)
5216{
5217 struct brcmf_cfg80211_vif *vif;
5218 struct brcmf_if *ifp;
5219
5220 ifp = netdev_priv(ndev);
5221 vif = ifp->vif;
5222
Arend van Spriel95ef1232015-08-26 22:15:04 +02005223 if (vif)
5224 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01005225 free_netdev(ndev);
5226}
5227
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005228static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005229{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005230 u32 event = e->event_code;
5231 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005232
5233 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01005234 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005235 return true;
5236 }
5237
5238 return false;
5239}
5240
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005241static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005242{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005243 u32 event = e->event_code;
5244 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005245
Hante Meuleman68ca3952014-02-25 20:30:26 +01005246 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
5247 (event == BRCMF_E_DISASSOC_IND) ||
5248 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01005249 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005250 return true;
5251 }
5252 return false;
5253}
5254
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005255static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005256 const struct brcmf_event_msg *e)
5257{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005258 u32 event = e->event_code;
5259 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005260
5261 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01005262 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
5263 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005264 return true;
5265 }
5266
5267 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01005268 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005269 return true;
5270 }
5271
5272 return false;
5273}
5274
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005275static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005276{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005277 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005278
5279 kfree(conn_info->req_ie);
5280 conn_info->req_ie = NULL;
5281 conn_info->req_ie_len = 0;
5282 kfree(conn_info->resp_ie);
5283 conn_info->resp_ie = NULL;
5284 conn_info->resp_ie_len = 0;
5285}
5286
Hante Meuleman89286dc2013-02-08 15:53:46 +01005287static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
5288 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005289{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005290 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005291 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005292 u32 req_len;
5293 u32 resp_len;
5294 s32 err = 0;
5295
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005296 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005297
Arend van Sprielac24be62012-10-22 10:36:23 -07005298 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
5299 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005300 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005301 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005302 return err;
5303 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005304 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005305 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005306 req_len = le32_to_cpu(assoc_info->req_len);
5307 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005308 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005309 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005310 cfg->extra_buf,
5311 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005312 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005313 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005314 return err;
5315 }
5316 conn_info->req_ie_len = req_len;
5317 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005318 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005319 GFP_KERNEL);
5320 } else {
5321 conn_info->req_ie_len = 0;
5322 conn_info->req_ie = NULL;
5323 }
5324 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005325 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005326 cfg->extra_buf,
5327 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005328 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005329 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005330 return err;
5331 }
5332 conn_info->resp_ie_len = resp_len;
5333 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005334 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005335 GFP_KERNEL);
5336 } else {
5337 conn_info->resp_ie_len = 0;
5338 conn_info->resp_ie = NULL;
5339 }
Arend van Spriel16886732012-12-05 15:26:04 +01005340 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
5341 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005342
5343 return err;
5344}
5345
5346static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005347brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005348 struct net_device *ndev,
5349 const struct brcmf_event_msg *e)
5350{
Arend van Sprielc1179032012-10-22 13:55:33 -07005351 struct brcmf_if *ifp = netdev_priv(ndev);
5352 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005353 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
5354 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07005355 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005356 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07005357 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02005358 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005359 u32 freq;
5360 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07005361 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005362
Arend van Sprield96b8012012-12-05 15:26:02 +01005363 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005364
Hante Meuleman89286dc2013-02-08 15:53:46 +01005365 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005366 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005367 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005368
Franky Lina180b832012-10-10 11:13:09 -07005369 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
5370 if (buf == NULL) {
5371 err = -ENOMEM;
5372 goto done;
5373 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005374
Franky Lina180b832012-10-10 11:13:09 -07005375 /* data sent to dongle has to be little endian */
5376 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07005377 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07005378 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07005379
5380 if (err)
5381 goto done;
5382
5383 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02005384 ch.chspec = le16_to_cpu(bi->chanspec);
5385 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005386
Franky Lin83cf17a2013-04-11 13:28:50 +02005387 if (ch.band == BRCMU_CHAN_BAND_2G)
Johannes Berg57fbcce2016-04-12 15:56:15 +02005388 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005389 else
Johannes Berg57fbcce2016-04-12 15:56:15 +02005390 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005391
Rafał Miłecki4712d882016-05-20 13:38:57 +02005392 freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005393 notify_channel = ieee80211_get_channel(wiphy, freq);
5394
Franky Lina180b832012-10-10 11:13:09 -07005395done:
5396 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02005397 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005398 conn_info->req_ie, conn_info->req_ie_len,
5399 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005400 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005401
Arend van Sprielc1179032012-10-22 13:55:33 -07005402 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01005403 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005404 return err;
5405}
5406
5407static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005408brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005409 struct net_device *ndev, const struct brcmf_event_msg *e,
5410 bool completed)
5411{
Arend van Sprielc1179032012-10-22 13:55:33 -07005412 struct brcmf_if *ifp = netdev_priv(ndev);
5413 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005414 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005415
Arend van Sprield96b8012012-12-05 15:26:02 +01005416 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005417
Arend van Sprielc1179032012-10-22 13:55:33 -07005418 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5419 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005420 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01005421 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005422 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005423 brcmf_update_bss_info(cfg, ifp);
5424 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5425 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005426 }
5427 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02005428 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005429 conn_info->req_ie,
5430 conn_info->req_ie_len,
5431 conn_info->resp_ie,
5432 conn_info->resp_ie_len,
5433 completed ? WLAN_STATUS_SUCCESS :
5434 WLAN_STATUS_AUTH_TIMEOUT,
5435 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005436 brcmf_dbg(CONN, "Report connect result - connection %s\n",
5437 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005438 }
Arend van Sprield96b8012012-12-05 15:26:02 +01005439 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005440 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005441}
5442
5443static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005444brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02005445 struct net_device *ndev,
5446 const struct brcmf_event_msg *e, void *data)
5447{
Hante Meuleman7ee29602013-02-06 18:40:43 +01005448 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005449 u32 event = e->event_code;
5450 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02005451 struct station_info sinfo;
5452
Arend van Spriel16886732012-12-05 15:26:04 +01005453 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005454 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
5455 ndev != cfg_to_ndev(cfg)) {
5456 brcmf_dbg(CONN, "AP mode link down\n");
5457 complete(&cfg->vif_disabled);
5458 return 0;
5459 }
Hante Meuleman1a873342012-09-27 14:17:54 +02005460
Hante Meuleman1a873342012-09-27 14:17:54 +02005461 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01005462 (reason == BRCMF_E_STATUS_SUCCESS)) {
5463 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02005464 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005465 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02005466 return -EINVAL;
5467 }
5468 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005469 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02005470 generation++;
5471 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005472 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005473 } else if ((event == BRCMF_E_DISASSOC_IND) ||
5474 (event == BRCMF_E_DEAUTH_IND) ||
5475 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01005476 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005477 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01005478 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02005479}
5480
5481static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005482brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005483 const struct brcmf_event_msg *e, void *data)
5484{
Arend van Spriel19937322012-11-05 16:22:32 -08005485 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5486 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005487 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005488 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005489 s32 err = 0;
5490
Hante Meuleman8851cce2014-07-30 13:20:02 +02005491 if ((e->event_code == BRCMF_E_DEAUTH) ||
5492 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5493 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5494 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5495 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5496 }
5497
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005498 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005499 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005500 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005501 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005502 if (brcmf_is_ibssmode(ifp->vif)) {
Hante Meulemanb0a79082015-12-10 13:43:07 +01005503 brcmf_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005504 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005505 memcpy(profile->bssid, e->addr, ETH_ALEN);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005506 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005507 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5508 &ifp->vif->sme_state);
5509 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5510 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005511 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005512 brcmf_bss_connect_done(cfg, ndev, e, true);
Hante Meuleman92121e62015-10-08 20:33:21 +02005513 brcmf_net_setcarrier(ifp, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005514 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005515 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005516 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005517 brcmf_bss_connect_done(cfg, ndev, e, false);
Hante Meuleman42e0ed02016-01-05 11:05:50 +01005518 brcmf_link_down(ifp->vif,
5519 brcmf_map_fw_linkdown_reason(e));
5520 brcmf_init_prof(ndev_to_prof(ndev));
5521 if (ndev != cfg_to_ndev(cfg))
5522 complete(&cfg->vif_disabled);
5523 brcmf_net_setcarrier(ifp, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005524 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005525 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005526 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005527 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5528 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005529 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005530 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005531 }
5532
5533 return err;
5534}
5535
5536static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005537brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005538 const struct brcmf_event_msg *e, void *data)
5539{
Arend van Spriel19937322012-11-05 16:22:32 -08005540 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005541 u32 event = e->event_code;
5542 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005543
5544 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005545 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005546 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005547 else
Arend van Spriel19937322012-11-05 16:22:32 -08005548 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005549 }
5550
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005551 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005552}
5553
5554static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005555brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005556 const struct brcmf_event_msg *e, void *data)
5557{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005558 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005559 enum nl80211_key_type key_type;
5560
5561 if (flags & BRCMF_EVENT_MSG_GROUP)
5562 key_type = NL80211_KEYTYPE_GROUP;
5563 else
5564 key_type = NL80211_KEYTYPE_PAIRWISE;
5565
Arend van Spriel19937322012-11-05 16:22:32 -08005566 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005567 NULL, GFP_KERNEL);
5568
5569 return 0;
5570}
5571
Arend van Sprield3c0b632013-02-08 15:53:37 +01005572static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5573 const struct brcmf_event_msg *e, void *data)
5574{
5575 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5576 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5577 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5578 struct brcmf_cfg80211_vif *vif;
5579
Hante Meuleman37a869e2015-10-29 20:33:17 +01005580 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
Arend van Sprield3c0b632013-02-08 15:53:37 +01005581 ifevent->action, ifevent->flags, ifevent->ifidx,
Hante Meuleman37a869e2015-10-29 20:33:17 +01005582 ifevent->bsscfgidx);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005583
Arend van Sprield3c0b632013-02-08 15:53:37 +01005584 mutex_lock(&event->vif_event_lock);
5585 event->action = ifevent->action;
5586 vif = event->vif;
5587
5588 switch (ifevent->action) {
5589 case BRCMF_E_IF_ADD:
5590 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005591 if (!cfg->vif_event.vif) {
5592 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005593 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005594 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005595
5596 ifp->vif = vif;
5597 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005598 if (ifp->ndev) {
5599 vif->wdev.netdev = ifp->ndev;
5600 ifp->ndev->ieee80211_ptr = &vif->wdev;
5601 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5602 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005603 mutex_unlock(&event->vif_event_lock);
5604 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005605 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005606
5607 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005608 mutex_unlock(&event->vif_event_lock);
5609 /* event may not be upon user request */
5610 if (brcmf_cfg80211_vif_event_armed(cfg))
5611 wake_up(&event->vif_wq);
5612 return 0;
5613
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005614 case BRCMF_E_IF_CHANGE:
5615 mutex_unlock(&event->vif_event_lock);
5616 wake_up(&event->vif_wq);
5617 return 0;
5618
Arend van Sprield3c0b632013-02-08 15:53:37 +01005619 default:
5620 mutex_unlock(&event->vif_event_lock);
5621 break;
5622 }
5623 return -EINVAL;
5624}
5625
Arend van Spriel5b435de2011-10-05 13:19:03 +02005626static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5627{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005628 conf->frag_threshold = (u32)-1;
5629 conf->rts_threshold = (u32)-1;
5630 conf->retry_short = (u32)-1;
5631 conf->retry_long = (u32)-1;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005632}
5633
Arend van Spriel5c36b992012-11-14 18:46:05 -08005634static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005635{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005636 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5637 brcmf_notify_connect_status);
5638 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5639 brcmf_notify_connect_status);
5640 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5641 brcmf_notify_connect_status);
5642 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5643 brcmf_notify_connect_status);
5644 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5645 brcmf_notify_connect_status);
5646 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5647 brcmf_notify_connect_status);
5648 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5649 brcmf_notify_roaming_status);
5650 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5651 brcmf_notify_mic_status);
5652 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5653 brcmf_notify_connect_status);
5654 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5655 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005656 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5657 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005658 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005659 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005660 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5661 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005662 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5663 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005664 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5665 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005666 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5667 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005668}
5669
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005670static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005671{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005672 kfree(cfg->conf);
5673 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005674 kfree(cfg->extra_buf);
5675 cfg->extra_buf = NULL;
Hante Meuleman3021ad92016-01-05 11:05:45 +01005676 kfree(cfg->wowl.nd);
5677 cfg->wowl.nd = NULL;
5678 kfree(cfg->wowl.nd_info);
5679 cfg->wowl.nd_info = NULL;
Hante Meulemand5367332016-02-17 11:26:51 +01005680 kfree(cfg->escan_info.escan_buf);
5681 cfg->escan_info.escan_buf = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005682}
5683
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005684static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005685{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005686 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5687 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005688 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005689 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5690 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005691 goto init_priv_mem_out;
Hante Meuleman3021ad92016-01-05 11:05:45 +01005692 cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
5693 if (!cfg->wowl.nd)
5694 goto init_priv_mem_out;
5695 cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
5696 sizeof(struct cfg80211_wowlan_nd_match *),
5697 GFP_KERNEL);
5698 if (!cfg->wowl.nd_info)
5699 goto init_priv_mem_out;
Hante Meulemand5367332016-02-17 11:26:51 +01005700 cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
5701 if (!cfg->escan_info.escan_buf)
5702 goto init_priv_mem_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005703
5704 return 0;
5705
5706init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005707 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005708
5709 return -ENOMEM;
5710}
5711
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005712static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005713{
5714 s32 err = 0;
5715
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005716 cfg->scan_request = NULL;
5717 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005718 cfg->active_scan = true; /* we do active scan per default */
5719 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005720 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005721 if (err)
5722 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005723 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005724 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005725 brcmf_init_escan(cfg);
5726 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005727 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005728 return err;
5729}
5730
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005731static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005732{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005733 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005734 brcmf_abort_scanning(cfg);
5735 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005736}
5737
Arend van Sprield3c0b632013-02-08 15:53:37 +01005738static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5739{
5740 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005741 mutex_init(&event->vif_event_lock);
5742}
5743
Hante Meuleman1119e232015-11-25 11:32:42 +01005744static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005745{
Hante Meuleman1119e232015-11-25 11:32:42 +01005746 s32 err;
5747 u32 bcn_timeout;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005748 __le32 roamtrigger[2];
5749 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005750
Hante Meuleman1119e232015-11-25 11:32:42 +01005751 /* Configure beacon timeout value based upon roaming setting */
Hante Meuleman7d34b052016-01-02 09:41:41 +01005752 if (ifp->drvr->settings->roamoff)
Hante Meuleman1119e232015-11-25 11:32:42 +01005753 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
5754 else
5755 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
5756 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
5757 if (err) {
5758 brcmf_err("bcn_timeout error (%d)\n", err);
5759 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005760 }
5761
Hante Meuleman1119e232015-11-25 11:32:42 +01005762 /* Enable/Disable built-in roaming to allow supplicant to take care of
5763 * roaming.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005764 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005765 brcmf_dbg(INFO, "Internal Roaming = %s\n",
Hante Meuleman7d34b052016-01-02 09:41:41 +01005766 ifp->drvr->settings->roamoff ? "Off" : "On");
5767 err = brcmf_fil_iovar_int_set(ifp, "roam_off",
5768 ifp->drvr->settings->roamoff);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005769 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005770 brcmf_err("roam_off error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005771 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005772 }
5773
Arend van Sprielf588bc02011-10-12 20:51:22 +02005774 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5775 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005776 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005777 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005778 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005779 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005780 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005781 }
5782
Arend van Sprielf588bc02011-10-12 20:51:22 +02005783 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5784 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005785 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005786 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005787 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005788 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005789 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005790 }
5791
Hante Meuleman1119e232015-11-25 11:32:42 +01005792roam_setup_done:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005793 return err;
5794}
5795
5796static s32
Hante Meuleman1678ba82015-12-10 13:43:00 +01005797brcmf_dongle_scantime(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005798{
5799 s32 err = 0;
5800
Arend van Sprielac24be62012-10-22 10:36:23 -07005801 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005802 BRCMF_SCAN_CHANNEL_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005803 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005804 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005805 goto dongle_scantime_out;
5806 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005807 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005808 BRCMF_SCAN_UNASSOC_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005809 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005810 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005811 goto dongle_scantime_out;
5812 }
5813
Arend van Sprielac24be62012-10-22 10:36:23 -07005814 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005815 BRCMF_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005816 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005817 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005818 goto dongle_scantime_out;
5819 }
5820
5821dongle_scantime_out:
5822 return err;
5823}
5824
Arend van Sprielb48d8912014-07-12 08:49:41 +02005825static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5826 struct brcmu_chan *ch)
5827{
5828 u32 ht40_flag;
5829
5830 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5831 if (ch->sb == BRCMU_CHAN_SB_U) {
5832 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5833 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5834 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5835 } else {
5836 /* It should be one of
5837 * IEEE80211_CHAN_NO_HT40 or
5838 * IEEE80211_CHAN_NO_HT40PLUS
5839 */
5840 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5841 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5842 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5843 }
5844}
5845
5846static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5847 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005848{
5849 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005850 struct ieee80211_supported_band *band;
5851 struct ieee80211_channel *channel;
5852 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005853 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005854 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005855 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005856 u8 *pbuf;
5857 u32 i, j;
5858 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005859 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005860 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005861
5862 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5863
5864 if (pbuf == NULL)
5865 return -ENOMEM;
5866
5867 list = (struct brcmf_chanspec_list *)pbuf;
5868
5869 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5870 BRCMF_DCMD_MEDLEN);
5871 if (err) {
5872 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005873 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005874 }
5875
Arend van Sprielb48d8912014-07-12 08:49:41 +02005876 wiphy = cfg_to_wiphy(cfg);
Johannes Berg57fbcce2016-04-12 15:56:15 +02005877 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel58de92d2015-04-14 20:10:24 +02005878 if (band)
5879 for (i = 0; i < band->n_channels; i++)
5880 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Johannes Berg57fbcce2016-04-12 15:56:15 +02005881 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel58de92d2015-04-14 20:10:24 +02005882 if (band)
5883 for (i = 0; i < band->n_channels; i++)
5884 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005885
5886 total = le32_to_cpu(list->count);
5887 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005888 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5889 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005890
Franky Lin83cf17a2013-04-11 13:28:50 +02005891 if (ch.band == BRCMU_CHAN_BAND_2G) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02005892 band = wiphy->bands[NL80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005893 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02005894 band = wiphy->bands[NL80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005895 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005896 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005897 continue;
5898 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005899 if (!band)
5900 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005901 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005902 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005903 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005904 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005905 ch.bw == BRCMU_CHAN_BW_80)
5906 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005907
5908 channel = band->channels;
5909 index = band->n_channels;
5910 for (j = 0; j < band->n_channels; j++) {
Rafał Miłecki4712d882016-05-20 13:38:57 +02005911 if (channel[j].hw_value == ch.control_ch_num) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005912 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005913 break;
5914 }
5915 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005916 channel[index].center_freq =
Rafał Miłecki4712d882016-05-20 13:38:57 +02005917 ieee80211_channel_to_frequency(ch.control_ch_num,
5918 band->band);
5919 channel[index].hw_value = ch.control_ch_num;
Hante Meulemand48200b2013-04-03 12:40:29 +02005920
Arend van Sprielb48d8912014-07-12 08:49:41 +02005921 /* assuming the chanspecs order is HT20,
5922 * HT40 upper, HT40 lower, and VHT80.
5923 */
5924 if (ch.bw == BRCMU_CHAN_BW_80) {
5925 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5926 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5927 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5928 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005929 /* enable the channel and disable other bandwidths
5930 * for now as mentioned order assure they are enabled
5931 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005932 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005933 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5934 IEEE80211_CHAN_NO_80MHZ;
5935 ch.bw = BRCMU_CHAN_BW_20;
5936 cfg->d11inf.encchspec(&ch);
5937 chaninfo = ch.chspec;
5938 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5939 &chaninfo);
5940 if (!err) {
5941 if (chaninfo & WL_CHAN_RADAR)
5942 channel[index].flags |=
5943 (IEEE80211_CHAN_RADAR |
5944 IEEE80211_CHAN_NO_IR);
5945 if (chaninfo & WL_CHAN_PASSIVE)
5946 channel[index].flags |=
5947 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005948 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005949 }
5950 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005951
Arend van Sprielb48d8912014-07-12 08:49:41 +02005952fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005953 kfree(pbuf);
5954 return err;
5955}
5956
Arend van Sprielb48d8912014-07-12 08:49:41 +02005957static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005958{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005959 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5960 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005961 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005962 struct brcmf_chanspec_list *list;
5963 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005964 u32 val;
5965 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005966 struct brcmu_chan ch;
5967 u32 num_chan;
5968 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005969
5970 /* verify support for bw_cap command */
5971 val = WLC_BAND_5G;
5972 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5973
5974 if (!err) {
5975 /* only set 2G bandwidth using bw_cap command */
5976 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5977 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5978 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5979 sizeof(band_bwcap));
5980 } else {
5981 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5982 val = WLC_N_BW_40ALL;
5983 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5984 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005985
5986 if (!err) {
5987 /* update channel info in 2G band */
5988 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5989
5990 if (pbuf == NULL)
5991 return -ENOMEM;
5992
5993 ch.band = BRCMU_CHAN_BAND_2G;
5994 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005995 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005996 ch.chnum = 0;
5997 cfg->d11inf.encchspec(&ch);
5998
5999 /* pass encoded chanspec in query */
6000 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
6001
6002 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
6003 BRCMF_DCMD_MEDLEN);
6004 if (err) {
6005 brcmf_err("get chanspecs error (%d)\n", err);
6006 kfree(pbuf);
6007 return err;
6008 }
6009
Johannes Berg57fbcce2016-04-12 15:56:15 +02006010 band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
Arend van Sprielb48d8912014-07-12 08:49:41 +02006011 list = (struct brcmf_chanspec_list *)pbuf;
6012 num_chan = le32_to_cpu(list->count);
6013 for (i = 0; i < num_chan; i++) {
6014 ch.chspec = (u16)le32_to_cpu(list->element[i]);
6015 cfg->d11inf.decchspec(&ch);
6016 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
6017 continue;
6018 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
6019 continue;
6020 for (j = 0; j < band->n_channels; j++) {
Rafał Miłecki4712d882016-05-20 13:38:57 +02006021 if (band->channels[j].hw_value == ch.control_ch_num)
Arend van Sprielb48d8912014-07-12 08:49:41 +02006022 break;
6023 }
6024 if (WARN_ON(j == band->n_channels))
6025 continue;
6026
6027 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
6028 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02006029 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006030 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006031 return err;
6032}
6033
Arend van Spriel2375d972014-01-06 12:40:41 +01006034static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
6035{
6036 u32 band, mimo_bwcap;
6037 int err;
6038
6039 band = WLC_BAND_2G;
6040 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
6041 if (!err) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02006042 bw_cap[NL80211_BAND_2GHZ] = band;
Arend van Spriel2375d972014-01-06 12:40:41 +01006043 band = WLC_BAND_5G;
6044 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
6045 if (!err) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02006046 bw_cap[NL80211_BAND_5GHZ] = band;
Arend van Spriel2375d972014-01-06 12:40:41 +01006047 return;
6048 }
6049 WARN_ON(1);
6050 return;
6051 }
6052 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
6053 mimo_bwcap = 0;
6054 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
6055 if (err)
6056 /* assume 20MHz if firmware does not give a clue */
6057 mimo_bwcap = WLC_N_BW_20ALL;
6058
6059 switch (mimo_bwcap) {
6060 case WLC_N_BW_40ALL:
Johannes Berg57fbcce2016-04-12 15:56:15 +02006061 bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
Arend van Spriel2375d972014-01-06 12:40:41 +01006062 /* fall-thru */
6063 case WLC_N_BW_20IN2G_40IN5G:
Johannes Berg57fbcce2016-04-12 15:56:15 +02006064 bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
Arend van Spriel2375d972014-01-06 12:40:41 +01006065 /* fall-thru */
6066 case WLC_N_BW_20ALL:
Johannes Berg57fbcce2016-04-12 15:56:15 +02006067 bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
6068 bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
Arend van Spriel2375d972014-01-06 12:40:41 +01006069 break;
6070 default:
6071 brcmf_err("invalid mimo_bw_cap value\n");
6072 }
6073}
Hante Meulemand48200b2013-04-03 12:40:29 +02006074
Arend van Spriel18d6c532014-05-12 10:47:35 +02006075static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
6076 u32 bw_cap[2], u32 nchain)
6077{
6078 band->ht_cap.ht_supported = true;
6079 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
6080 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
6081 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6082 }
6083 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
6084 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
6085 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
6086 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
6087 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
6088 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
6089}
6090
6091static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
6092{
6093 u16 mcs_map;
6094 int i;
6095
6096 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
6097 mcs_map = (mcs_map << 2) | supp;
6098
6099 return cpu_to_le16(mcs_map);
6100}
6101
6102static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006103 u32 bw_cap[2], u32 nchain, u32 txstreams,
6104 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
Arend van Spriel18d6c532014-05-12 10:47:35 +02006105{
6106 __le16 mcs_map;
6107
6108 /* not allowed in 2.4G band */
Johannes Berg57fbcce2016-04-12 15:56:15 +02006109 if (band->band == NL80211_BAND_2GHZ)
Arend van Spriel18d6c532014-05-12 10:47:35 +02006110 return;
6111
6112 band->vht_cap.vht_supported = true;
6113 /* 80MHz is mandatory */
6114 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
6115 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
6116 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
6117 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
6118 }
6119 /* all support 256-QAM */
6120 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
6121 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
6122 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006123
6124 /* Beamforming support information */
6125 if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
6126 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
6127 if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
6128 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
6129 if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
6130 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
6131 if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
6132 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
6133
6134 if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
6135 band->vht_cap.cap |=
6136 (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
6137 band->vht_cap.cap |= ((txstreams - 1) <<
6138 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
6139 band->vht_cap.cap |=
6140 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
6141 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02006142}
6143
Arend van Sprielb48d8912014-07-12 08:49:41 +02006144static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006145{
Arend van Sprielb48d8912014-07-12 08:49:41 +02006146 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07006147 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02006148 u32 nmode = 0;
6149 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006150 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01006151 u32 rxchain;
6152 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006153 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02006154 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01006155 struct ieee80211_supported_band *band;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006156 u32 txstreams = 0;
6157 u32 txbf_bfe_cap = 0;
6158 u32 txbf_bfr_cap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006159
Arend van Spriel18d6c532014-05-12 10:47:35 +02006160 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02006161 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
6162 if (err) {
6163 brcmf_err("nmode error (%d)\n", err);
6164 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01006165 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02006166 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02006167 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
Johannes Berg57fbcce2016-04-12 15:56:15 +02006168 nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
6169 bw_cap[NL80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02006170
Daniel Kim4aca7a12014-02-25 20:30:36 +01006171 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
6172 if (err) {
6173 brcmf_err("rxchain error (%d)\n", err);
6174 nchain = 1;
6175 } else {
6176 for (nchain = 0; rxchain; nchain++)
6177 rxchain = rxchain & (rxchain - 1);
6178 }
6179 brcmf_dbg(INFO, "nchain=%d\n", nchain);
6180
Arend van Sprielb48d8912014-07-12 08:49:41 +02006181 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02006182 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006183 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02006184 return err;
6185 }
6186
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006187 if (vhtmode) {
6188 (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
6189 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
6190 &txbf_bfe_cap);
6191 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
6192 &txbf_bfr_cap);
6193 }
6194
Arend van Sprielb48d8912014-07-12 08:49:41 +02006195 wiphy = cfg_to_wiphy(cfg);
6196 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
6197 band = wiphy->bands[i];
6198 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01006199 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02006200
Arend van Spriel18d6c532014-05-12 10:47:35 +02006201 if (nmode)
6202 brcmf_update_ht_cap(band, bw_cap, nchain);
6203 if (vhtmode)
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006204 brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
6205 txbf_bfe_cap, txbf_bfr_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02006206 }
6207
Arend van Sprielb48d8912014-07-12 08:49:41 +02006208 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006209}
6210
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006211static const struct ieee80211_txrx_stypes
6212brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
6213 [NL80211_IFTYPE_STATION] = {
6214 .tx = 0xffff,
6215 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
6216 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
6217 },
6218 [NL80211_IFTYPE_P2P_CLIENT] = {
6219 .tx = 0xffff,
6220 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
6221 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
6222 },
6223 [NL80211_IFTYPE_P2P_GO] = {
6224 .tx = 0xffff,
6225 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
6226 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
6227 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
6228 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
6229 BIT(IEEE80211_STYPE_AUTH >> 4) |
6230 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
6231 BIT(IEEE80211_STYPE_ACTION >> 4)
6232 },
6233 [NL80211_IFTYPE_P2P_DEVICE] = {
6234 .tx = 0xffff,
6235 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
6236 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
6237 }
6238};
6239
Arend van Spriel0882dda2015-08-20 22:06:03 +02006240/**
6241 * brcmf_setup_ifmodes() - determine interface modes and combinations.
6242 *
6243 * @wiphy: wiphy object.
6244 * @ifp: interface object needed for feat module api.
6245 *
6246 * The interface modes and combinations are determined dynamically here
6247 * based on firmware functionality.
6248 *
6249 * no p2p and no mbss:
6250 *
6251 * #STA <= 1, #AP <= 1, channels = 1, 2 total
6252 *
6253 * no p2p and mbss:
6254 *
6255 * #STA <= 1, #AP <= 1, channels = 1, 2 total
6256 * #AP <= 4, matching BI, channels = 1, 4 total
6257 *
6258 * p2p, no mchan, and mbss:
6259 *
6260 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
6261 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
6262 * #AP <= 4, matching BI, channels = 1, 4 total
6263 *
6264 * p2p, mchan, and mbss:
6265 *
6266 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
6267 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
6268 * #AP <= 4, matching BI, channels = 1, 4 total
6269 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006270static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
6271{
6272 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006273 struct ieee80211_iface_limit *c0_limits = NULL;
6274 struct ieee80211_iface_limit *p2p_limits = NULL;
6275 struct ieee80211_iface_limit *mbss_limits = NULL;
6276 bool mbss, p2p;
6277 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006278
Arend van Spriel0882dda2015-08-20 22:06:03 +02006279 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
6280 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
6281
6282 n_combos = 1 + !!p2p + !!mbss;
6283 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006284 if (!combo)
6285 goto err;
6286
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006287 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
6288 BIT(NL80211_IFTYPE_ADHOC) |
6289 BIT(NL80211_IFTYPE_AP);
6290
Arend van Spriel0882dda2015-08-20 22:06:03 +02006291 c = 0;
6292 i = 0;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006293 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
6294 if (!c0_limits)
6295 goto err;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006296 c0_limits[i].max = 1;
6297 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
6298 if (p2p) {
6299 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
6300 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006301 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
6302 BIT(NL80211_IFTYPE_P2P_GO) |
6303 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02006304 c0_limits[i].max = 1;
6305 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
6306 c0_limits[i].max = 1;
6307 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
6308 BIT(NL80211_IFTYPE_P2P_GO);
6309 } else {
6310 c0_limits[i].max = 1;
6311 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006312 }
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006313 combo[c].num_different_channels = 1;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006314 combo[c].max_interfaces = i;
6315 combo[c].n_limits = i;
6316 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006317
Arend van Spriel0882dda2015-08-20 22:06:03 +02006318 if (p2p) {
6319 c++;
6320 i = 0;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006321 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
6322 if (!p2p_limits)
6323 goto err;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006324 p2p_limits[i].max = 1;
6325 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
6326 p2p_limits[i].max = 1;
6327 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
6328 p2p_limits[i].max = 1;
6329 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
6330 p2p_limits[i].max = 1;
6331 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006332 combo[c].num_different_channels = 1;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006333 combo[c].max_interfaces = i;
6334 combo[c].n_limits = i;
6335 combo[c].limits = p2p_limits;
6336 }
6337
6338 if (mbss) {
6339 c++;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006340 i = 0;
6341 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
6342 if (!mbss_limits)
6343 goto err;
6344 mbss_limits[i].max = 4;
6345 mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Arend van Spriel0882dda2015-08-20 22:06:03 +02006346 combo[c].beacon_int_infra_match = true;
6347 combo[c].num_different_channels = 1;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006348 combo[c].max_interfaces = 4;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006349 combo[c].n_limits = i;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006350 combo[c].limits = mbss_limits;
6351 }
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006352
Arend van Spriel0882dda2015-08-20 22:06:03 +02006353 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006354 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006355 return 0;
6356
6357err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02006358 kfree(c0_limits);
6359 kfree(p2p_limits);
6360 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006361 kfree(combo);
6362 return -ENOMEM;
6363}
6364
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006365static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
6366{
6367 /* scheduled scan settings */
6368 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
6369 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
6370 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
6371 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
6372}
6373
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006374#ifdef CONFIG_PM
Hante Meuleman3021ad92016-01-05 11:05:45 +01006375static struct wiphy_wowlan_support brcmf_wowlan_support = {
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006376 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01006377 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
6378 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
6379 .pattern_min_len = 1,
6380 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006381};
6382#endif
6383
Hante Meuleman3021ad92016-01-05 11:05:45 +01006384static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006385{
6386#ifdef CONFIG_PM
Hante Meuleman3021ad92016-01-05 11:05:45 +01006387 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman3021ad92016-01-05 11:05:45 +01006388
6389 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006390 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
6391 brcmf_wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
6392 init_waitqueue_head(&cfg->wowl.nd_data_wait);
Hante Meuleman3021ad92016-01-05 11:05:45 +01006393 }
6394 }
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006395 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
6396 brcmf_wowlan_support.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
6397 brcmf_wowlan_support.flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
6398 }
6399
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006400 wiphy->wowlan = &brcmf_wowlan_support;
6401#endif
6402}
6403
Arend van Sprielb48d8912014-07-12 08:49:41 +02006404static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006405{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006406 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006407 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006408 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006409 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006410 __le32 bandlist[3];
6411 u32 n_bands;
6412 int err, i;
6413
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006414 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
6415 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
Hante Meuleman6c404f32015-12-10 13:43:03 +01006416 wiphy->max_num_pmkids = BRCMF_MAXPMKID;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006417
6418 err = brcmf_setup_ifmodes(wiphy, ifp);
6419 if (err)
6420 return err;
6421
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006422 for (i = 0, combo = wiphy->iface_combinations;
6423 i < wiphy->n_iface_combinations; i++, combo++) {
6424 max_interfaces = max(max_interfaces, combo->max_interfaces);
6425 }
6426
6427 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
6428 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006429 u8 *addr = drvr->addresses[i].addr;
6430
6431 memcpy(addr, drvr->mac, ETH_ALEN);
6432 if (i) {
6433 addr[0] |= BIT(1);
6434 addr[ETH_ALEN - 1] ^= i;
6435 }
6436 }
6437 wiphy->addresses = drvr->addresses;
6438 wiphy->n_addresses = i;
6439
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006440 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
Hante Meuleman240d61a2016-02-17 11:27:10 +01006441 wiphy->cipher_suites = brcmf_cipher_suites;
6442 wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
6443 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
6444 wiphy->n_cipher_suites--;
Arend van Spriel7705ba62016-04-17 16:44:58 +02006445 wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |
6446 BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
6447 BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
6448
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006449 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
6450 WIPHY_FLAG_OFFCHAN_TX |
Hante Meulemana7b82d42015-12-10 13:43:04 +01006451 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
6452 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
6453 wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman7d34b052016-01-02 09:41:41 +01006454 if (!ifp->drvr->settings->roamoff)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006455 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
6456 wiphy->mgmt_stypes = brcmf_txrx_stypes;
6457 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02006458 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
6459 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006460
6461 /* vendor commands/events support */
6462 wiphy->vendor_commands = brcmf_vendor_cmds;
6463 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
6464
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006465 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
Hante Meuleman3021ad92016-01-05 11:05:45 +01006466 brcmf_wiphy_wowl_params(wiphy, ifp);
Arend van Spriel58de92d2015-04-14 20:10:24 +02006467 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
6468 sizeof(bandlist));
6469 if (err) {
6470 brcmf_err("could not obtain band info: err=%d\n", err);
6471 return err;
6472 }
6473 /* first entry in bandlist is number of bands */
6474 n_bands = le32_to_cpu(bandlist[0]);
6475 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
6476 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
6477 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
6478 GFP_KERNEL);
6479 if (!band)
6480 return -ENOMEM;
6481
6482 band->channels = kmemdup(&__wl_2ghz_channels,
6483 sizeof(__wl_2ghz_channels),
6484 GFP_KERNEL);
6485 if (!band->channels) {
6486 kfree(band);
6487 return -ENOMEM;
6488 }
6489
6490 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
Johannes Berg57fbcce2016-04-12 15:56:15 +02006491 wiphy->bands[NL80211_BAND_2GHZ] = band;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006492 }
6493 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
6494 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
6495 GFP_KERNEL);
6496 if (!band)
6497 return -ENOMEM;
6498
6499 band->channels = kmemdup(&__wl_5ghz_channels,
6500 sizeof(__wl_5ghz_channels),
6501 GFP_KERNEL);
6502 if (!band->channels) {
6503 kfree(band);
6504 return -ENOMEM;
6505 }
6506
6507 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
Johannes Berg57fbcce2016-04-12 15:56:15 +02006508 wiphy->bands[NL80211_BAND_5GHZ] = band;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006509 }
6510 }
6511 err = brcmf_setup_wiphybands(wiphy);
6512 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006513}
6514
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006515static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006516{
6517 struct net_device *ndev;
6518 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01006519 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006520 s32 power_mode;
6521 s32 err = 0;
6522
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006523 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006524 return err;
6525
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006526 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006527 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006528 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006529
Hante Meuleman40a23292013-01-02 15:22:51 +01006530 /* make sure RF is ready for work */
6531 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6532
Hante Meuleman1678ba82015-12-10 13:43:00 +01006533 brcmf_dongle_scantime(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006534
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006535 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006536 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006537 if (err)
6538 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006539 brcmf_dbg(INFO, "power save set to %s\n",
6540 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006541
Hante Meuleman1119e232015-11-25 11:32:42 +01006542 err = brcmf_dongle_roam(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006543 if (err)
6544 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006545 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
6546 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006547 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006548 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006549
Franky Lin52f22fb2016-02-17 11:26:55 +01006550 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meulemanb3657452013-05-27 21:09:53 +02006551
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006552 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006553default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006554
6555 return err;
6556
6557}
6558
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006559static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006560{
Arend van Sprielc1179032012-10-22 13:55:33 -07006561 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006562
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006563 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006564}
6565
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006566static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006567{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006568 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006569
Arend van Spriel5b435de2011-10-05 13:19:03 +02006570 /*
6571 * While going down, if associated with AP disassociate
6572 * from AP to save power
6573 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006574 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006575 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006576
6577 /* Make sure WPA_Supplicant receives all the event
6578 generated due to DISASSOC call to the fw to keep
6579 the state fw and WPA_Supplicant state consistent
6580 */
6581 brcmf_delay(500);
6582 }
6583
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006584 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006585 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006586
Arend van Spriel5b435de2011-10-05 13:19:03 +02006587 return 0;
6588}
6589
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006590s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006591{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006592 struct brcmf_if *ifp = netdev_priv(ndev);
6593 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006594 s32 err = 0;
6595
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006596 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006597 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006598 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006599
6600 return err;
6601}
6602
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006603s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006604{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006605 struct brcmf_if *ifp = netdev_priv(ndev);
6606 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006607 s32 err = 0;
6608
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006609 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006610 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006611 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006612
6613 return err;
6614}
6615
Arend van Spriela7965fb2013-04-11 17:08:37 +02006616enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6617{
6618 struct wireless_dev *wdev = &ifp->vif->wdev;
6619
6620 return wdev->iftype;
6621}
6622
Hante Meulemanbfe81972014-10-28 14:56:16 +01006623bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6624 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006625{
6626 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006627
6628 list_for_each_entry(vif, &cfg->vif_list, list) {
6629 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006630 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006631 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006632 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006633}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006634
6635static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6636 u8 action)
6637{
6638 u8 evt_action;
6639
6640 mutex_lock(&event->vif_event_lock);
6641 evt_action = event->action;
6642 mutex_unlock(&event->vif_event_lock);
6643 return evt_action == action;
6644}
6645
6646void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6647 struct brcmf_cfg80211_vif *vif)
6648{
6649 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6650
6651 mutex_lock(&event->vif_event_lock);
6652 event->vif = vif;
6653 event->action = 0;
6654 mutex_unlock(&event->vif_event_lock);
6655}
6656
6657bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6658{
6659 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6660 bool armed;
6661
6662 mutex_lock(&event->vif_event_lock);
6663 armed = event->vif != NULL;
6664 mutex_unlock(&event->vif_event_lock);
6665
6666 return armed;
6667}
Arend van Spriela9eb0c42016-02-17 11:26:50 +01006668
6669int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
6670 u8 action, ulong timeout)
Arend van Sprield3c0b632013-02-08 15:53:37 +01006671{
6672 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6673
6674 return wait_event_timeout(event->vif_wq,
6675 vif_event_equals(event, action), timeout);
6676}
6677
Hante Meuleman73345fd2016-02-17 11:26:53 +01006678static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
6679 struct brcmf_fil_country_le *ccreq)
6680{
Hante Meuleman4d792892016-02-17 11:27:07 +01006681 struct brcmfmac_pd_cc *country_codes;
6682 struct brcmfmac_pd_cc_entry *cc;
Hante Meuleman73345fd2016-02-17 11:26:53 +01006683 s32 found_index;
6684 int i;
6685
6686 country_codes = drvr->settings->country_codes;
6687 if (!country_codes) {
6688 brcmf_dbg(TRACE, "No country codes configured for device\n");
6689 return -EINVAL;
6690 }
6691
6692 if ((alpha2[0] == ccreq->country_abbrev[0]) &&
6693 (alpha2[1] == ccreq->country_abbrev[1])) {
6694 brcmf_dbg(TRACE, "Country code already set\n");
6695 return -EAGAIN;
6696 }
6697
6698 found_index = -1;
6699 for (i = 0; i < country_codes->table_size; i++) {
6700 cc = &country_codes->table[i];
6701 if ((cc->iso3166[0] == '\0') && (found_index == -1))
6702 found_index = i;
6703 if ((cc->iso3166[0] == alpha2[0]) &&
6704 (cc->iso3166[1] == alpha2[1])) {
6705 found_index = i;
6706 break;
6707 }
6708 }
6709 if (found_index == -1) {
6710 brcmf_dbg(TRACE, "No country code match found\n");
6711 return -EINVAL;
6712 }
6713 memset(ccreq, 0, sizeof(*ccreq));
6714 ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
6715 memcpy(ccreq->ccode, country_codes->table[found_index].cc,
6716 BRCMF_COUNTRY_BUF_SZ);
6717 ccreq->country_abbrev[0] = alpha2[0];
6718 ccreq->country_abbrev[1] = alpha2[1];
6719 ccreq->country_abbrev[2] = 0;
6720
6721 return 0;
6722}
6723
Arend van Spriel63db1a42014-12-21 12:43:51 +01006724static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6725 struct regulatory_request *req)
6726{
6727 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6728 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6729 struct brcmf_fil_country_le ccreq;
Hante Meuleman73345fd2016-02-17 11:26:53 +01006730 s32 err;
Arend van Spriel63db1a42014-12-21 12:43:51 +01006731 int i;
6732
Arend van Spriel63db1a42014-12-21 12:43:51 +01006733 /* ignore non-ISO3166 country codes */
6734 for (i = 0; i < sizeof(req->alpha2); i++)
6735 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
Hante Meuleman73345fd2016-02-17 11:26:53 +01006736 brcmf_err("not a ISO3166 code (0x%02x 0x%02x)\n",
6737 req->alpha2[0], req->alpha2[1]);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006738 return;
6739 }
Hante Meuleman73345fd2016-02-17 11:26:53 +01006740
6741 brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
6742 req->alpha2[0], req->alpha2[1]);
6743
6744 err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
6745 if (err) {
6746 brcmf_err("Country code iovar returned err = %d\n", err);
6747 return;
6748 }
6749
6750 err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
6751 if (err)
6752 return;
6753
6754 err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
6755 if (err) {
6756 brcmf_err("Firmware rejected country setting\n");
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006757 return;
6758 }
6759 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006760}
6761
Arend van Sprielb48d8912014-07-12 08:49:41 +02006762static void brcmf_free_wiphy(struct wiphy *wiphy)
6763{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006764 int i;
6765
Arend van Spriel58de92d2015-04-14 20:10:24 +02006766 if (!wiphy)
6767 return;
6768
Arend van Spriel0882dda2015-08-20 22:06:03 +02006769 if (wiphy->iface_combinations) {
6770 for (i = 0; i < wiphy->n_iface_combinations; i++)
6771 kfree(wiphy->iface_combinations[i].limits);
6772 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006773 kfree(wiphy->iface_combinations);
Johannes Berg57fbcce2016-04-12 15:56:15 +02006774 if (wiphy->bands[NL80211_BAND_2GHZ]) {
6775 kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
6776 kfree(wiphy->bands[NL80211_BAND_2GHZ]);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006777 }
Johannes Berg57fbcce2016-04-12 15:56:15 +02006778 if (wiphy->bands[NL80211_BAND_5GHZ]) {
6779 kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
6780 kfree(wiphy->bands[NL80211_BAND_5GHZ]);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006781 }
6782 wiphy_free(wiphy);
6783}
6784
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006785struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006786 struct device *busdev,
6787 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006788{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006789 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006790 struct brcmf_cfg80211_info *cfg;
6791 struct wiphy *wiphy;
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006792 struct cfg80211_ops *ops;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006793 struct brcmf_cfg80211_vif *vif;
6794 struct brcmf_if *ifp;
6795 s32 err = 0;
6796 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006797 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006798
6799 if (!ndev) {
6800 brcmf_err("ndev is invalid\n");
6801 return NULL;
6802 }
6803
Muhammad Falak R Wanid464fd82016-05-19 19:29:03 +05306804 ops = kmemdup(&brcmf_cfg80211_ops, sizeof(*ops), GFP_KERNEL);
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006805 if (!ops)
6806 return NULL;
6807
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006808 ifp = netdev_priv(ndev);
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006809#ifdef CONFIG_PM
6810 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
6811 ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
6812#endif
6813 wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info));
Arend van Sprielb48d8912014-07-12 08:49:41 +02006814 if (!wiphy) {
6815 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006816 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006817 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006818 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006819 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006820
6821 cfg = wiphy_priv(wiphy);
6822 cfg->wiphy = wiphy;
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006823 cfg->ops = ops;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006824 cfg->pub = drvr;
6825 init_vif_event(&cfg->vif_event);
6826 INIT_LIST_HEAD(&cfg->vif_list);
6827
Rafał Miłecki26072332016-06-06 23:03:55 +02006828 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006829 if (IS_ERR(vif))
6830 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006831
6832 vif->ifp = ifp;
6833 vif->wdev.netdev = ndev;
6834 ndev->ieee80211_ptr = &vif->wdev;
6835 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6836
6837 err = wl_init_priv(cfg);
6838 if (err) {
6839 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006840 brcmf_free_vif(vif);
6841 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006842 }
6843 ifp->vif = vif;
6844
Arend van Sprielb48d8912014-07-12 08:49:41 +02006845 /* determine d11 io type before wiphy setup */
6846 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006847 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006848 brcmf_err("Failed to get D11 version (%d)\n", err);
6849 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006850 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006851 cfg->d11inf.io_type = (u8)io_type;
6852 brcmu_d11_attach(&cfg->d11inf);
6853
6854 err = brcmf_setup_wiphy(wiphy, ifp);
6855 if (err < 0)
6856 goto priv_out;
6857
6858 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006859 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006860 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6861 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6862
6863 /* firmware defaults to 40MHz disabled in 2G band. We signal
6864 * cfg80211 here that we do and have it decide we can enable
6865 * it. But first check if device does support 2G operation.
6866 */
Johannes Berg57fbcce2016-04-12 15:56:15 +02006867 if (wiphy->bands[NL80211_BAND_2GHZ]) {
6868 cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006869 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6870 }
6871 err = wiphy_register(wiphy);
6872 if (err < 0) {
6873 brcmf_err("Could not register wiphy device (%d)\n", err);
6874 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006875 }
6876
6877 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6878 * setup 40MHz in 2GHz band and enable OBSS scanning.
6879 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006880 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6881 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006882 if (!err)
6883 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6884 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006885 else
6886 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006887 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006888 /* p2p might require that "if-events" get processed by fweh. So
6889 * activate the already registered event handlers now and activate
6890 * the rest when initialization has completed. drvr->config needs to
6891 * be assigned before activating events.
6892 */
6893 drvr->config = cfg;
6894 err = brcmf_fweh_activate_events(ifp);
6895 if (err) {
6896 brcmf_err("FWEH activation failed (%d)\n", err);
6897 goto wiphy_unreg_out;
6898 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006899
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006900 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006901 if (err) {
6902 brcmf_err("P2P initilisation failed (%d)\n", err);
6903 goto wiphy_unreg_out;
6904 }
6905 err = brcmf_btcoex_attach(cfg);
6906 if (err) {
6907 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6908 brcmf_p2p_detach(&cfg->p2p);
6909 goto wiphy_unreg_out;
6910 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006911
Hante Meulemana7b82d42015-12-10 13:43:04 +01006912 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
6913 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6914 if (err) {
6915 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6916 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
6917 } else {
6918 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6919 brcmf_notify_tdls_peer_event);
6920 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006921 }
6922
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006923 /* (re-) activate FWEH event handling */
6924 err = brcmf_fweh_activate_events(ifp);
6925 if (err) {
6926 brcmf_err("FWEH activation failed (%d)\n", err);
6927 goto wiphy_unreg_out;
6928 }
6929
Hante Meuleman48ed16e2016-01-02 09:41:38 +01006930 /* Fill in some of the advertised nl80211 supported features */
6931 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
6932 wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
6933#ifdef CONFIG_PM
Franky Lin6ea09152016-02-17 11:26:52 +01006934 if (wiphy->wowlan &&
6935 wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
Hante Meuleman48ed16e2016-01-02 09:41:38 +01006936 wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
6937#endif
6938 }
6939
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006940 return cfg;
6941
Arend van Sprielb48d8912014-07-12 08:49:41 +02006942wiphy_unreg_out:
6943 wiphy_unregister(cfg->wiphy);
6944priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006945 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006946 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006947 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006948wiphy_out:
6949 brcmf_free_wiphy(wiphy);
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006950 kfree(ops);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006951 return NULL;
6952}
6953
6954void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6955{
6956 if (!cfg)
6957 return;
6958
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006959 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006960 wiphy_unregister(cfg->wiphy);
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006961 kfree(cfg->ops);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006962 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006963 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006964}