blob: db3d8487dc429b8959d430412314828994063aac [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>
Arend van Spriel5b435de2011-10-05 13:19:03 +020022#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020023#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020024
25#include <brcmu_utils.h>
26#include <defs.h>
27#include <brcmu_wifi.h>
28#include "dhd.h"
Arend van Spriel16886732012-12-05 15:26:04 +010029#include "dhd_dbg.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020030#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010031#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010032#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020033#include "btcoex.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020034#include "wl_cfg80211.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070035#include "fwil.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020036
Arend van Spriele5806072012-09-19 22:21:08 +020037#define BRCMF_SCAN_IE_LEN_MAX 2048
38#define BRCMF_PNO_VERSION 2
39#define BRCMF_PNO_TIME 30
40#define BRCMF_PNO_REPEAT 4
41#define BRCMF_PNO_FREQ_EXPO_MAX 3
42#define BRCMF_PNO_MAX_PFN_COUNT 16
43#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
44#define BRCMF_PNO_HIDDEN_BIT 2
45#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
46#define BRCMF_PNO_SCAN_COMPLETE 1
47#define BRCMF_PNO_SCAN_INCOMPLETE 0
48
Arend van Spriel9f440b72013-02-08 15:53:36 +010049#define BRCMF_IFACE_MAX_CNT 3
Arend van Spriel3eacf862012-10-22 13:55:30 -070050
Hante Meuleman1a873342012-09-27 14:17:54 +020051#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
52#define WPA_OUI_TYPE 1
53#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
54#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010055#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020056
57#define VS_IE_FIXED_HDR_LEN 6
58#define WPA_IE_VERSION_LEN 2
59#define WPA_IE_MIN_OUI_LEN 4
60#define WPA_IE_SUITE_COUNT_LEN 2
61
62#define WPA_CIPHER_NONE 0 /* None */
63#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
64#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
65#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
66#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
67
68#define RSN_AKM_NONE 0 /* None (IBSS) */
69#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
70#define RSN_AKM_PSK 2 /* Pre-shared Key */
71#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
72#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
73
74#define VNDR_IE_CMD_LEN 4 /* length of the set command
75 * string :"add", "del" (+ NUL)
76 */
77#define VNDR_IE_COUNT_OFFSET 4
78#define VNDR_IE_PKTFLAG_OFFSET 8
79#define VNDR_IE_VSIE_OFFSET 12
80#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010081#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020082
83#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
84#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020085
Hante Meuleman89286dc2013-02-08 15:53:46 +010086#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
87#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
88#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
89
Arend van Spriel5b435de2011-10-05 13:19:03 +020090#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
91 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
92
Arend van Sprielce81e312012-10-22 13:55:37 -070093static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +020094{
Arend van Sprielc1179032012-10-22 13:55:33 -070095 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +010096 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
97 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +020098 return false;
99 }
100 return true;
101}
102
103#define CHAN2G(_channel, _freq, _flags) { \
104 .band = IEEE80211_BAND_2GHZ, \
105 .center_freq = (_freq), \
106 .hw_value = (_channel), \
107 .flags = (_flags), \
108 .max_antenna_gain = 0, \
109 .max_power = 30, \
110}
111
112#define CHAN5G(_channel, _flags) { \
113 .band = IEEE80211_BAND_5GHZ, \
114 .center_freq = 5000 + (5 * (_channel)), \
115 .hw_value = (_channel), \
116 .flags = (_flags), \
117 .max_antenna_gain = 0, \
118 .max_power = 30, \
119}
120
121#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
122#define RATETAB_ENT(_rateid, _flags) \
123 { \
124 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
125 .hw_value = (_rateid), \
126 .flags = (_flags), \
127 }
128
129static struct ieee80211_rate __wl_rates[] = {
130 RATETAB_ENT(BRCM_RATE_1M, 0),
131 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
132 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
133 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
134 RATETAB_ENT(BRCM_RATE_6M, 0),
135 RATETAB_ENT(BRCM_RATE_9M, 0),
136 RATETAB_ENT(BRCM_RATE_12M, 0),
137 RATETAB_ENT(BRCM_RATE_18M, 0),
138 RATETAB_ENT(BRCM_RATE_24M, 0),
139 RATETAB_ENT(BRCM_RATE_36M, 0),
140 RATETAB_ENT(BRCM_RATE_48M, 0),
141 RATETAB_ENT(BRCM_RATE_54M, 0),
142};
143
144#define wl_a_rates (__wl_rates + 4)
145#define wl_a_rates_size 8
146#define wl_g_rates (__wl_rates + 0)
147#define wl_g_rates_size 12
148
149static struct ieee80211_channel __wl_2ghz_channels[] = {
150 CHAN2G(1, 2412, 0),
151 CHAN2G(2, 2417, 0),
152 CHAN2G(3, 2422, 0),
153 CHAN2G(4, 2427, 0),
154 CHAN2G(5, 2432, 0),
155 CHAN2G(6, 2437, 0),
156 CHAN2G(7, 2442, 0),
157 CHAN2G(8, 2447, 0),
158 CHAN2G(9, 2452, 0),
159 CHAN2G(10, 2457, 0),
160 CHAN2G(11, 2462, 0),
161 CHAN2G(12, 2467, 0),
162 CHAN2G(13, 2472, 0),
163 CHAN2G(14, 2484, 0),
164};
165
166static struct ieee80211_channel __wl_5ghz_a_channels[] = {
167 CHAN5G(34, 0), CHAN5G(36, 0),
168 CHAN5G(38, 0), CHAN5G(40, 0),
169 CHAN5G(42, 0), CHAN5G(44, 0),
170 CHAN5G(46, 0), CHAN5G(48, 0),
171 CHAN5G(52, 0), CHAN5G(56, 0),
172 CHAN5G(60, 0), CHAN5G(64, 0),
173 CHAN5G(100, 0), CHAN5G(104, 0),
174 CHAN5G(108, 0), CHAN5G(112, 0),
175 CHAN5G(116, 0), CHAN5G(120, 0),
176 CHAN5G(124, 0), CHAN5G(128, 0),
177 CHAN5G(132, 0), CHAN5G(136, 0),
178 CHAN5G(140, 0), CHAN5G(149, 0),
179 CHAN5G(153, 0), CHAN5G(157, 0),
180 CHAN5G(161, 0), CHAN5G(165, 0),
181 CHAN5G(184, 0), CHAN5G(188, 0),
182 CHAN5G(192, 0), CHAN5G(196, 0),
183 CHAN5G(200, 0), CHAN5G(204, 0),
184 CHAN5G(208, 0), CHAN5G(212, 0),
185 CHAN5G(216, 0),
186};
187
Arend van Spriel5b435de2011-10-05 13:19:03 +0200188static struct ieee80211_supported_band __wl_band_2ghz = {
189 .band = IEEE80211_BAND_2GHZ,
190 .channels = __wl_2ghz_channels,
191 .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
192 .bitrates = wl_g_rates,
193 .n_bitrates = wl_g_rates_size,
Daniel Kimd2353672014-03-20 10:18:00 +0100194 .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true},
Arend van Spriel5b435de2011-10-05 13:19:03 +0200195};
196
197static struct ieee80211_supported_band __wl_band_5ghz_a = {
198 .band = IEEE80211_BAND_5GHZ,
199 .channels = __wl_5ghz_a_channels,
200 .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
201 .bitrates = wl_a_rates,
202 .n_bitrates = wl_a_rates_size,
203};
204
Hante Meulemand48200b2013-04-03 12:40:29 +0200205/* This is to override regulatory domains defined in cfg80211 module (reg.c)
206 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200207 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
208 * With respect to these flags, wpa_supplicant doesn't * start p2p
209 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200210 * domain are to be done here.
211 */
212static const struct ieee80211_regdomain brcmf_regdom = {
213 .n_reg_rules = 4,
214 .alpha2 = "99",
215 .reg_rules = {
216 /* IEEE 802.11b/g, channels 1..11 */
217 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
218 /* If any */
219 /* IEEE 802.11 channel 14 - Only JP enables
220 * this and for 802.11b only
221 */
222 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
223 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200224 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200225 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200226 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200227};
228
229static const u32 __wl_cipher_suites[] = {
230 WLAN_CIPHER_SUITE_WEP40,
231 WLAN_CIPHER_SUITE_WEP104,
232 WLAN_CIPHER_SUITE_TKIP,
233 WLAN_CIPHER_SUITE_CCMP,
234 WLAN_CIPHER_SUITE_AES_CMAC,
235};
236
Hante Meuleman1a873342012-09-27 14:17:54 +0200237/* Vendor specific ie. id = 221, oui and type defines exact ie */
238struct brcmf_vs_tlv {
239 u8 id;
240 u8 len;
241 u8 oui[3];
242 u8 oui_type;
243};
244
245struct parsed_vndr_ie_info {
246 u8 *ie_ptr;
247 u32 ie_len; /* total length including id & length field */
248 struct brcmf_vs_tlv vndrie;
249};
250
251struct parsed_vndr_ies {
252 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100253 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200254};
255
Hante Meuleman68ca3952014-02-25 20:30:26 +0100256static int brcmf_roamoff;
257module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
258MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
259
Alwin Beukersef6ac172011-10-12 20:51:26 +0200260/* Quarter dBm units to mW
261 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
262 * Table is offset so the last entry is largest mW value that fits in
263 * a u16.
264 */
265
266#define QDBM_OFFSET 153 /* Offset for first entry */
267#define QDBM_TABLE_LEN 40 /* Table size */
268
269/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
270 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
271 */
272#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
273
274/* Largest mW value that will round down to the last table entry,
275 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
276 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
277 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
278 */
279#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
280
281static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
282/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
283/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
284/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
285/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
286/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
287/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
288};
289
290static u16 brcmf_qdbm_to_mw(u8 qdbm)
291{
292 uint factor = 1;
293 int idx = qdbm - QDBM_OFFSET;
294
295 if (idx >= QDBM_TABLE_LEN)
296 /* clamp to max u16 mW value */
297 return 0xFFFF;
298
299 /* scale the qdBm index up to the range of the table 0-40
300 * where an offset of 40 qdBm equals a factor of 10 mW.
301 */
302 while (idx < 0) {
303 idx += 40;
304 factor *= 10;
305 }
306
307 /* return the mW value scaled down to the correct factor of 10,
308 * adding in factor/2 to get proper rounding.
309 */
310 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
311}
312
313static u8 brcmf_mw_to_qdbm(u16 mw)
314{
315 u8 qdbm;
316 int offset;
317 uint mw_uint = mw;
318 uint boundary;
319
320 /* handle boundary case */
321 if (mw_uint <= 1)
322 return 0;
323
324 offset = QDBM_OFFSET;
325
326 /* move mw into the range of the table */
327 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
328 mw_uint *= 10;
329 offset -= 40;
330 }
331
332 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
333 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
334 nqdBm_to_mW_map[qdbm]) / 2;
335 if (mw_uint < boundary)
336 break;
337 }
338
339 qdbm += (u8) offset;
340
341 return qdbm;
342}
343
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200344static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
345 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200346{
347 struct brcmu_chan ch_inf;
348 s32 primary_offset;
349
350 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
351 ch->chan->center_freq, ch->center_freq1, ch->width);
352 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
353 primary_offset = ch->center_freq1 - ch->chan->center_freq;
354 switch (ch->width) {
355 case NL80211_CHAN_WIDTH_20:
356 ch_inf.bw = BRCMU_CHAN_BW_20;
357 WARN_ON(primary_offset != 0);
358 break;
359 case NL80211_CHAN_WIDTH_40:
360 ch_inf.bw = BRCMU_CHAN_BW_40;
361 if (primary_offset < 0)
362 ch_inf.sb = BRCMU_CHAN_SB_U;
363 else
364 ch_inf.sb = BRCMU_CHAN_SB_L;
365 break;
366 case NL80211_CHAN_WIDTH_80:
367 ch_inf.bw = BRCMU_CHAN_BW_80;
368 if (primary_offset < 0) {
369 if (primary_offset < -CH_10MHZ_APART)
370 ch_inf.sb = BRCMU_CHAN_SB_UU;
371 else
372 ch_inf.sb = BRCMU_CHAN_SB_UL;
373 } else {
374 if (primary_offset > CH_10MHZ_APART)
375 ch_inf.sb = BRCMU_CHAN_SB_LL;
376 else
377 ch_inf.sb = BRCMU_CHAN_SB_LU;
378 }
379 break;
380 default:
381 WARN_ON_ONCE(1);
382 }
383 switch (ch->chan->band) {
384 case IEEE80211_BAND_2GHZ:
385 ch_inf.band = BRCMU_CHAN_BAND_2G;
386 break;
387 case IEEE80211_BAND_5GHZ:
388 ch_inf.band = BRCMU_CHAN_BAND_5G;
389 break;
390 default:
391 WARN_ON_ONCE(1);
392 }
393 d11inf->encchspec(&ch_inf);
394
395 return ch_inf.chspec;
396}
397
Franky Lin83cf17a2013-04-11 13:28:50 +0200398u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
399 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700400{
Franky Lin83cf17a2013-04-11 13:28:50 +0200401 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700402
Franky Lin83cf17a2013-04-11 13:28:50 +0200403 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
404 ch_inf.bw = BRCMU_CHAN_BW_20;
405 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700406
Franky Lin83cf17a2013-04-11 13:28:50 +0200407 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700408}
409
Hante Meuleman89286dc2013-02-08 15:53:46 +0100410/* Traverse a string of 1-byte tag/1-byte length/variable-length value
411 * triples, returning a pointer to the substring whose first element
412 * matches tag
413 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100414const struct brcmf_tlv *
415brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100416{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100417 const struct brcmf_tlv *elt = buf;
418 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100419
420 /* find tagged parameter */
421 while (totlen >= TLV_HDR_LEN) {
422 int len = elt->len;
423
424 /* validate remaining totlen */
425 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
426 return elt;
427
428 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
429 totlen -= (len + TLV_HDR_LEN);
430 }
431
432 return NULL;
433}
434
435/* Is any of the tlvs the expected entry? If
436 * not update the tlvs buffer pointer/length.
437 */
438static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100439brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
440 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100441{
442 /* If the contents match the OUI and the type */
443 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
444 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
445 type == ie[TLV_BODY_OFF + oui_len]) {
446 return true;
447 }
448
449 if (tlvs == NULL)
450 return false;
451 /* point to the next ie */
452 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
453 /* calculate the length of the rest of the buffer */
454 *tlvs_len -= (int)(ie - *tlvs);
455 /* update the pointer to the start of the buffer */
456 *tlvs = ie;
457
458 return false;
459}
460
461static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100462brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100463{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100464 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100465
466 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100467 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100468 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
469 return (struct brcmf_vs_tlv *)ie;
470 }
471 return NULL;
472}
473
474static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100475brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100476{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100477 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100478
479 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
480 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
481 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
482 return (struct brcmf_vs_tlv *)ie;
483 }
484 return NULL;
485}
486
487
Arend van Spriel5b435de2011-10-05 13:19:03 +0200488static void convert_key_from_CPU(struct brcmf_wsec_key *key,
489 struct brcmf_wsec_key_le *key_le)
490{
491 key_le->index = cpu_to_le32(key->index);
492 key_le->len = cpu_to_le32(key->len);
493 key_le->algo = cpu_to_le32(key->algo);
494 key_le->flags = cpu_to_le32(key->flags);
495 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
496 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
497 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
498 memcpy(key_le->data, key->data, sizeof(key->data));
499 memcpy(key_le->ea, key->ea, sizeof(key->ea));
500}
501
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200502static int
Arend van Spriel2eaba7e2012-10-22 10:36:26 -0700503send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200504{
505 int err;
506 struct brcmf_wsec_key_le key_le;
507
508 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200509
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700510 brcmf_netdev_wait_pend8021x(ndev);
511
Arend van Sprielac24be62012-10-22 10:36:23 -0700512 err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700513 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200514
Arend van Spriel5b435de2011-10-05 13:19:03 +0200515 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100516 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200517 return err;
518}
519
Hante Meulemanb3657452013-05-27 21:09:53 +0200520static s32
521brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
522{
523 s32 err;
524 u32 mode;
525
526 if (enable)
527 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
528 else
529 mode = 0;
530
531 /* Try to set and enable ARP offload feature, this may fail, then it */
532 /* is simply not supported and err 0 will be returned */
533 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
534 if (err) {
535 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
536 mode, err);
537 err = 0;
538 } else {
539 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
540 if (err) {
541 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
542 enable, err);
543 err = 0;
544 } else
545 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
546 enable, mode);
547 }
548
549 return err;
550}
551
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100552static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
553{
554 enum nl80211_iftype iftype;
555
556 iftype = vif->wdev.iftype;
557 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
558}
559
560static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
561{
562 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
563}
564
Arend van Spriel9f440b72013-02-08 15:53:36 +0100565static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
566 const char *name,
567 enum nl80211_iftype type,
568 u32 *flags,
569 struct vif_params *params)
570{
571 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
572 switch (type) {
573 case NL80211_IFTYPE_ADHOC:
574 case NL80211_IFTYPE_STATION:
575 case NL80211_IFTYPE_AP:
576 case NL80211_IFTYPE_AP_VLAN:
577 case NL80211_IFTYPE_WDS:
578 case NL80211_IFTYPE_MONITOR:
579 case NL80211_IFTYPE_MESH_POINT:
580 return ERR_PTR(-EOPNOTSUPP);
581 case NL80211_IFTYPE_P2P_CLIENT:
582 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200583 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100584 return brcmf_p2p_add_vif(wiphy, name, type, flags, params);
585 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100586 default:
587 return ERR_PTR(-EINVAL);
588 }
589}
590
Arend van Sprielf96aa072013-04-05 10:57:48 +0200591void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100592{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100593 s32 err = 0;
594
595 if (check_vif_up(ifp->vif)) {
596 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
597 if (err) {
598 brcmf_err("fail to set mpc\n");
599 return;
600 }
601 brcmf_dbg(INFO, "MPC : %d\n", mpc);
602 }
603}
604
Arend van Spriela0f472a2013-04-05 10:57:49 +0200605s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
606 struct brcmf_if *ifp, bool aborted,
607 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100608{
609 struct brcmf_scan_params_le params_le;
610 struct cfg80211_scan_request *scan_request;
611 s32 err = 0;
612
613 brcmf_dbg(SCAN, "Enter\n");
614
615 /* clear scan request, because the FW abort can cause a second call */
616 /* to this functon and might cause a double cfg80211_scan_done */
617 scan_request = cfg->scan_request;
618 cfg->scan_request = NULL;
619
620 if (timer_pending(&cfg->escan_timeout))
621 del_timer_sync(&cfg->escan_timeout);
622
623 if (fw_abort) {
624 /* Do a scan abort to stop the driver's scan engine */
625 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
626 memset(&params_le, 0, sizeof(params_le));
627 memset(params_le.bssid, 0xFF, ETH_ALEN);
628 params_le.bss_type = DOT11_BSSTYPE_ANY;
629 params_le.scan_type = 0;
630 params_le.channel_num = cpu_to_le32(1);
631 params_le.nprobes = cpu_to_le32(1);
632 params_le.active_time = cpu_to_le32(-1);
633 params_le.passive_time = cpu_to_le32(-1);
634 params_le.home_time = cpu_to_le32(-1);
635 /* Scan is aborted by setting channel_list[0] to -1 */
636 params_le.channel_list[0] = cpu_to_le16(-1);
637 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200638 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100639 &params_le, sizeof(params_le));
640 if (err)
641 brcmf_err("Scan abort failed\n");
642 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200643
644 brcmf_set_mpc(ifp, 1);
645
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100646 /*
647 * e-scan can be initiated by scheduled scan
648 * which takes precedence.
649 */
650 if (cfg->sched_escan) {
651 brcmf_dbg(SCAN, "scheduled scan completed\n");
652 cfg->sched_escan = false;
653 if (!aborted)
654 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100655 } else if (scan_request) {
656 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
657 aborted ? "Aborted" : "Done");
658 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100659 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100660 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
661 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100662
663 return err;
664}
665
Arend van Spriel9f440b72013-02-08 15:53:36 +0100666static
667int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
668{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100669 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
670 struct net_device *ndev = wdev->netdev;
671
672 /* vif event pending in firmware */
673 if (brcmf_cfg80211_vif_event_armed(cfg))
674 return -EBUSY;
675
676 if (ndev) {
677 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200678 cfg->escan_info.ifp == netdev_priv(ndev))
679 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
680 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100681
682 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
683 }
684
Arend van Spriel9f440b72013-02-08 15:53:36 +0100685 switch (wdev->iftype) {
686 case NL80211_IFTYPE_ADHOC:
687 case NL80211_IFTYPE_STATION:
688 case NL80211_IFTYPE_AP:
689 case NL80211_IFTYPE_AP_VLAN:
690 case NL80211_IFTYPE_WDS:
691 case NL80211_IFTYPE_MONITOR:
692 case NL80211_IFTYPE_MESH_POINT:
693 return -EOPNOTSUPP;
694 case NL80211_IFTYPE_P2P_CLIENT:
695 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200696 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100697 return brcmf_p2p_del_vif(wiphy, wdev);
698 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100699 default:
700 return -EINVAL;
701 }
702 return -EOPNOTSUPP;
703}
704
Arend van Spriel5b435de2011-10-05 13:19:03 +0200705static s32
706brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
707 enum nl80211_iftype type, u32 *flags,
708 struct vif_params *params)
709{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100710 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700711 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100712 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200713 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200714 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200715 s32 err = 0;
716
Arend van Sprield96b8012012-12-05 15:26:02 +0100717 brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200718
719 switch (type) {
720 case NL80211_IFTYPE_MONITOR:
721 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100722 brcmf_err("type (%d) : currently we do not support this type\n",
723 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200724 return -EOPNOTSUPP;
725 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200726 infra = 0;
727 break;
728 case NL80211_IFTYPE_STATION:
Hante Meuleman1bc7c652013-02-08 15:53:56 +0100729 /* Ignore change for p2p IF. Unclear why supplicant does this */
730 if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
731 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
732 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
733 /* WAR: It is unexpected to get a change of VIF for P2P
734 * IF, but it happens. The request can not be handled
735 * but returning EPERM causes a crash. Returning 0
736 * without setting ieee80211_ptr->iftype causes trace
737 * (WARN_ON) but it works with wpa_supplicant
738 */
739 return 0;
740 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200741 infra = 1;
742 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200743 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100744 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200745 ap = 1;
746 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200747 default:
748 err = -EINVAL;
749 goto done;
750 }
751
Hante Meuleman1a873342012-09-27 14:17:54 +0200752 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100753 if (type == NL80211_IFTYPE_P2P_GO) {
754 brcmf_dbg(INFO, "IF Type = P2P GO\n");
755 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
756 }
757 if (!err) {
758 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
759 brcmf_dbg(INFO, "IF Type = AP\n");
760 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200761 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100762 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200763 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100764 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200765 err = -EAGAIN;
766 goto done;
767 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100768 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100769 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200770 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200771 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200772
773done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100774 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200775
776 return err;
777}
778
Franky Lin83cf17a2013-04-11 13:28:50 +0200779static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
780 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200781 struct cfg80211_scan_request *request)
782{
783 u32 n_ssids;
784 u32 n_channels;
785 s32 i;
786 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200787 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200788 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200789 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200790
Arend van Sprielba40d162012-10-22 13:55:38 -0700791 memset(params_le->bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200792 params_le->bss_type = DOT11_BSSTYPE_ANY;
793 params_le->scan_type = 0;
794 params_le->channel_num = 0;
795 params_le->nprobes = cpu_to_le32(-1);
796 params_le->active_time = cpu_to_le32(-1);
797 params_le->passive_time = cpu_to_le32(-1);
798 params_le->home_time = cpu_to_le32(-1);
799 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
800
801 /* if request is null exit so it will be all channel broadcast scan */
802 if (!request)
803 return;
804
805 n_ssids = request->n_ssids;
806 n_channels = request->n_channels;
807 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100808 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
809 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200810 if (n_channels > 0) {
811 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200812 chanspec = channel_to_chanspec(&cfg->d11inf,
813 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100814 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
815 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200816 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200817 }
818 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100819 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200820 }
821 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100822 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200823 if (n_ssids > 0) {
824 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
825 n_channels * sizeof(u16);
826 offset = roundup(offset, sizeof(u32));
827 ptr = (char *)params_le + offset;
828 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200829 memset(&ssid_le, 0, sizeof(ssid_le));
830 ssid_le.SSID_len =
831 cpu_to_le32(request->ssids[i].ssid_len);
832 memcpy(ssid_le.SSID, request->ssids[i].ssid,
833 request->ssids[i].ssid_len);
834 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100835 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200836 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100837 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
838 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200839 memcpy(ptr, &ssid_le, sizeof(ssid_le));
840 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200841 }
842 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100843 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200844 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100845 brcmf_dbg(SCAN, "SSID %s len=%d\n",
846 params_le->ssid_le.SSID,
847 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200848 params_le->ssid_le.SSID_len =
849 cpu_to_le32(request->ssids->ssid_len);
850 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
851 request->ssids->ssid_len);
852 }
853 }
854 /* Adding mask to channel numbers */
855 params_le->channel_num =
856 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
857 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
858}
859
860static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200861brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +0200862 struct cfg80211_scan_request *request, u16 action)
863{
864 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
865 offsetof(struct brcmf_escan_params_le, params_le);
866 struct brcmf_escan_params_le *params;
867 s32 err = 0;
868
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100869 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200870
871 if (request != NULL) {
872 /* Allocate space for populating ssids in struct */
873 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
874
875 /* Allocate space for populating ssids in struct */
876 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
877 }
878
879 params = kzalloc(params_size, GFP_KERNEL);
880 if (!params) {
881 err = -ENOMEM;
882 goto exit;
883 }
884 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200885 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200886 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
887 params->action = cpu_to_le16(action);
888 params->sync_id = cpu_to_le16(0x1234);
889
Arend van Spriela0f472a2013-04-05 10:57:49 +0200890 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200891 if (err) {
892 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100893 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200894 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100895 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200896 }
897
898 kfree(params);
899exit:
900 return err;
901}
902
903static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200904brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +0200905 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200906{
907 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700908 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200909 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100910 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +0200911
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100912 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +0200913 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100914 escan->wiphy = wiphy;
915 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700916 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +0200917 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700918 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200919 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100920 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200921 return err;
922 }
Arend van Sprielf96aa072013-04-05 10:57:48 +0200923 brcmf_set_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200924 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +0200925 results->version = 0;
926 results->count = 0;
927 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
928
Arend van Spriela0f472a2013-04-05 10:57:49 +0200929 err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200930 if (err)
Arend van Sprielf96aa072013-04-05 10:57:48 +0200931 brcmf_set_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +0200932 return err;
933}
934
935static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200936brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +0200937 struct cfg80211_scan_request *request,
938 struct cfg80211_ssid *this_ssid)
939{
Arend van Spriela0f472a2013-04-05 10:57:49 +0200940 struct brcmf_if *ifp = vif->ifp;
941 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +0200942 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -0800943 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700944 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200945 bool escan_req;
946 bool spec_scan;
947 s32 err;
948 u32 SSID_len;
949
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100950 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200951
Arend van Sprielc1179032012-10-22 13:55:33 -0700952 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100953 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200954 return -EAGAIN;
955 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700956 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100957 brcmf_err("Scanning being aborted: status (%lu)\n",
958 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200959 return -EAGAIN;
960 }
Arend van Spriel1687eee2013-04-23 12:53:11 +0200961 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
962 brcmf_err("Scanning suppressed: status (%lu)\n",
963 cfg->scan_status);
964 return -EAGAIN;
965 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700966 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100967 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +0200968 return -EAGAIN;
969 }
970
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100971 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +0200972 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
973 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100974
Hante Meulemane756af52012-09-11 21:18:52 +0200975 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200976 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +0200977 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
978
979 escan_req = false;
980 if (request) {
981 /* scan bss */
982 ssids = request->ssids;
983 escan_req = true;
984 } else {
985 /* scan in ibss */
986 /* we don't do escan in ibss */
987 ssids = this_ssid;
988 }
989
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200990 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -0700991 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200992 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +0100993 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +0200994 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100995 if (err)
996 goto scan_out;
997
Arend van Spriela0f472a2013-04-05 10:57:49 +0200998 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -0800999 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001000 goto scan_out;
1001 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001002 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1003 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001004 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1005 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1006 sr->ssid_le.SSID_len = cpu_to_le32(0);
1007 spec_scan = false;
1008 if (SSID_len) {
1009 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1010 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1011 spec_scan = true;
1012 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001013 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001014
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001015 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001016 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001017 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001018 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001019 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001020 goto scan_out;
1021 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02001022 brcmf_set_mpc(ifp, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -07001023 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -07001024 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001025 if (err) {
1026 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001027 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
1028 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001029 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001030 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001031
Arend van Sprielf96aa072013-04-05 10:57:48 +02001032 brcmf_set_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001033 goto scan_out;
1034 }
1035 }
1036
1037 return 0;
1038
1039scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001040 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001041 if (timer_pending(&cfg->escan_timeout))
1042 del_timer_sync(&cfg->escan_timeout);
1043 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001044 return err;
1045}
1046
Arend van Spriel5b435de2011-10-05 13:19:03 +02001047static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001048brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001049{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001050 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001051 s32 err = 0;
1052
Arend van Sprield96b8012012-12-05 15:26:02 +01001053 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001054 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1055 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001056 return -EIO;
1057
Arend van Spriela0f472a2013-04-05 10:57:49 +02001058 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001059
Arend van Spriel5b435de2011-10-05 13:19:03 +02001060 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001061 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001062
Arend van Sprield96b8012012-12-05 15:26:02 +01001063 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001064 return err;
1065}
1066
1067static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1068{
1069 s32 err = 0;
1070
Arend van Sprielac24be62012-10-22 10:36:23 -07001071 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1072 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001073 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001074 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001075
1076 return err;
1077}
1078
1079static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1080{
1081 s32 err = 0;
1082
Arend van Sprielac24be62012-10-22 10:36:23 -07001083 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1084 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001085 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001086 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001087
1088 return err;
1089}
1090
1091static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1092{
1093 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001094 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001095
Arend van Sprielac24be62012-10-22 10:36:23 -07001096 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001097 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001098 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001099 return err;
1100 }
1101 return err;
1102}
1103
1104static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1105{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001106 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1107 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001108 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001109 s32 err = 0;
1110
Arend van Sprield96b8012012-12-05 15:26:02 +01001111 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001112 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001113 return -EIO;
1114
1115 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001116 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1117 cfg->conf->rts_threshold = wiphy->rts_threshold;
1118 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001119 if (!err)
1120 goto done;
1121 }
1122 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001123 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1124 cfg->conf->frag_threshold = wiphy->frag_threshold;
1125 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001126 if (!err)
1127 goto done;
1128 }
1129 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001130 && (cfg->conf->retry_long != wiphy->retry_long)) {
1131 cfg->conf->retry_long = wiphy->retry_long;
1132 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001133 if (!err)
1134 goto done;
1135 }
1136 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001137 && (cfg->conf->retry_short != wiphy->retry_short)) {
1138 cfg->conf->retry_short = wiphy->retry_short;
1139 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001140 if (!err)
1141 goto done;
1142 }
1143
1144done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001145 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001146 return err;
1147}
1148
Arend van Spriel5b435de2011-10-05 13:19:03 +02001149static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1150{
1151 memset(prof, 0, sizeof(*prof));
1152}
1153
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001154static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001155{
Piotr Haber61730d42013-04-23 12:53:12 +02001156 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001157 s32 err = 0;
1158
Arend van Sprield96b8012012-12-05 15:26:02 +01001159 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001160
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001161 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001162 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001163 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001164 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001165 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001166 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001167 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001168 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001169 cfg80211_disconnected(vif->wdev.netdev, 0, NULL, 0, GFP_KERNEL);
1170
Arend van Spriel5b435de2011-10-05 13:19:03 +02001171 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001172 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001173 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1174 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001175 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001176}
1177
1178static s32
1179brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1180 struct cfg80211_ibss_params *params)
1181{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001182 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001183 struct brcmf_if *ifp = netdev_priv(ndev);
1184 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001185 struct brcmf_join_params join_params;
1186 size_t join_params_size = 0;
1187 s32 err = 0;
1188 s32 wsec = 0;
1189 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001190 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001191
Arend van Sprield96b8012012-12-05 15:26:02 +01001192 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001193 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001194 return -EIO;
1195
1196 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001197 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001198 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001199 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001200 return -EOPNOTSUPP;
1201 }
1202
Arend van Sprielc1179032012-10-22 13:55:33 -07001203 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001204
1205 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001206 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001207 else
Arend van Spriel16886732012-12-05 15:26:04 +01001208 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001209
Johannes Berg683b6d32012-11-08 21:25:48 +01001210 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001211 brcmf_dbg(CONN, "channel: %d\n",
1212 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001213 else
Arend van Spriel16886732012-12-05 15:26:04 +01001214 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001215
1216 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001217 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001218 else
Arend van Spriel16886732012-12-05 15:26:04 +01001219 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001220
1221 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001222 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001223 else
Arend van Spriel16886732012-12-05 15:26:04 +01001224 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001225
1226 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001227 brcmf_dbg(CONN, "beacon interval: %d\n",
1228 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001229 else
Arend van Spriel16886732012-12-05 15:26:04 +01001230 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001231
1232 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001233 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001234 else
Arend van Spriel16886732012-12-05 15:26:04 +01001235 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001236
1237 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001238 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001239 else
Arend van Spriel16886732012-12-05 15:26:04 +01001240 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001241
1242 /* Configure Privacy for starter */
1243 if (params->privacy)
1244 wsec |= WEP_ENABLED;
1245
Arend van Sprielc1179032012-10-22 13:55:33 -07001246 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001247 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001248 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001249 goto done;
1250 }
1251
1252 /* Configure Beacon Interval for starter */
1253 if (params->beacon_interval)
1254 bcnprd = params->beacon_interval;
1255 else
1256 bcnprd = 100;
1257
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001258 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001259 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001260 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001261 goto done;
1262 }
1263
1264 /* Configure required join parameter */
1265 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1266
1267 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001268 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1269 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1270 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1271 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001272 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001273
1274 /* BSSID */
1275 if (params->bssid) {
1276 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1277 join_params_size = sizeof(join_params.ssid_le) +
1278 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001279 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001280 } else {
Arend van Sprielba40d162012-10-22 13:55:38 -07001281 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001282 memset(profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001283 }
1284
Arend van Spriel5b435de2011-10-05 13:19:03 +02001285 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001286 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001287 u32 target_channel;
1288
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001289 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001290 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001291 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001292 if (params->channel_fixed) {
1293 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001294 chanspec = chandef_to_chanspec(&cfg->d11inf,
1295 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001296 join_params.params_le.chanspec_list[0] =
1297 cpu_to_le16(chanspec);
1298 join_params.params_le.chanspec_num = cpu_to_le32(1);
1299 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001300 }
1301
1302 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001303 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001304 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001305 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001306 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001307 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001308 goto done;
1309 }
1310 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001311 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001312
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001313 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001314
1315
Arend van Sprielc1179032012-10-22 13:55:33 -07001316 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001317 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001318 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001319 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001320 goto done;
1321 }
1322
1323done:
1324 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001325 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001326 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001327 return err;
1328}
1329
1330static s32
1331brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1332{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001333 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334
Arend van Sprield96b8012012-12-05 15:26:02 +01001335 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001336 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337 return -EIO;
1338
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001339 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001340
Arend van Sprield96b8012012-12-05 15:26:02 +01001341 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001342
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001343 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001344}
1345
1346static s32 brcmf_set_wpa_version(struct net_device *ndev,
1347 struct cfg80211_connect_params *sme)
1348{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001349 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001350 struct brcmf_cfg80211_security *sec;
1351 s32 val = 0;
1352 s32 err = 0;
1353
1354 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1355 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1356 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1357 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1358 else
1359 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001360 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001361 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001362 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001363 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001364 return err;
1365 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001366 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001367 sec->wpa_versions = sme->crypto.wpa_versions;
1368 return err;
1369}
1370
1371static s32 brcmf_set_auth_type(struct net_device *ndev,
1372 struct cfg80211_connect_params *sme)
1373{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001374 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001375 struct brcmf_cfg80211_security *sec;
1376 s32 val = 0;
1377 s32 err = 0;
1378
1379 switch (sme->auth_type) {
1380 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1381 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001382 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001383 break;
1384 case NL80211_AUTHTYPE_SHARED_KEY:
1385 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001386 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001387 break;
1388 case NL80211_AUTHTYPE_AUTOMATIC:
1389 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001390 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001391 break;
1392 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001393 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001394 default:
1395 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001396 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001397 break;
1398 }
1399
Hante Meuleman89286dc2013-02-08 15:53:46 +01001400 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001401 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001402 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001403 return err;
1404 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001405 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001406 sec->auth_type = sme->auth_type;
1407 return err;
1408}
1409
1410static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001411brcmf_set_wsec_mode(struct net_device *ndev,
1412 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001413{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001414 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415 struct brcmf_cfg80211_security *sec;
1416 s32 pval = 0;
1417 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001418 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001419 s32 err = 0;
1420
1421 if (sme->crypto.n_ciphers_pairwise) {
1422 switch (sme->crypto.ciphers_pairwise[0]) {
1423 case WLAN_CIPHER_SUITE_WEP40:
1424 case WLAN_CIPHER_SUITE_WEP104:
1425 pval = WEP_ENABLED;
1426 break;
1427 case WLAN_CIPHER_SUITE_TKIP:
1428 pval = TKIP_ENABLED;
1429 break;
1430 case WLAN_CIPHER_SUITE_CCMP:
1431 pval = AES_ENABLED;
1432 break;
1433 case WLAN_CIPHER_SUITE_AES_CMAC:
1434 pval = AES_ENABLED;
1435 break;
1436 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001437 brcmf_err("invalid cipher pairwise (%d)\n",
1438 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001439 return -EINVAL;
1440 }
1441 }
1442 if (sme->crypto.cipher_group) {
1443 switch (sme->crypto.cipher_group) {
1444 case WLAN_CIPHER_SUITE_WEP40:
1445 case WLAN_CIPHER_SUITE_WEP104:
1446 gval = WEP_ENABLED;
1447 break;
1448 case WLAN_CIPHER_SUITE_TKIP:
1449 gval = TKIP_ENABLED;
1450 break;
1451 case WLAN_CIPHER_SUITE_CCMP:
1452 gval = AES_ENABLED;
1453 break;
1454 case WLAN_CIPHER_SUITE_AES_CMAC:
1455 gval = AES_ENABLED;
1456 break;
1457 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001458 brcmf_err("invalid cipher group (%d)\n",
1459 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001460 return -EINVAL;
1461 }
1462 }
1463
Arend van Spriel16886732012-12-05 15:26:04 +01001464 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001465 /* In case of privacy, but no security and WPS then simulate */
1466 /* setting AES. WPS-2.0 allows no security */
1467 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1468 sme->privacy)
1469 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001470
1471 if (mfp)
1472 wsec = pval | gval | MFP_CAPABLE;
1473 else
1474 wsec = pval | gval;
1475 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001476 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001477 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001478 return err;
1479 }
1480
Arend van Spriel06bb1232012-09-27 14:17:56 +02001481 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001482 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1483 sec->cipher_group = sme->crypto.cipher_group;
1484
1485 return err;
1486}
1487
1488static s32
1489brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1490{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001491 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001492 struct brcmf_cfg80211_security *sec;
1493 s32 val = 0;
1494 s32 err = 0;
1495
1496 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001497 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1498 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001499 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001500 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001501 return err;
1502 }
1503 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1504 switch (sme->crypto.akm_suites[0]) {
1505 case WLAN_AKM_SUITE_8021X:
1506 val = WPA_AUTH_UNSPECIFIED;
1507 break;
1508 case WLAN_AKM_SUITE_PSK:
1509 val = WPA_AUTH_PSK;
1510 break;
1511 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001512 brcmf_err("invalid cipher group (%d)\n",
1513 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001514 return -EINVAL;
1515 }
1516 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1517 switch (sme->crypto.akm_suites[0]) {
1518 case WLAN_AKM_SUITE_8021X:
1519 val = WPA2_AUTH_UNSPECIFIED;
1520 break;
1521 case WLAN_AKM_SUITE_PSK:
1522 val = WPA2_AUTH_PSK;
1523 break;
1524 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001525 brcmf_err("invalid cipher group (%d)\n",
1526 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001527 return -EINVAL;
1528 }
1529 }
1530
Arend van Spriel16886732012-12-05 15:26:04 +01001531 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001532 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1533 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001534 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001535 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001536 return err;
1537 }
1538 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001539 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001540 sec->wpa_auth = sme->crypto.akm_suites[0];
1541
1542 return err;
1543}
1544
1545static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001546brcmf_set_sharedkey(struct net_device *ndev,
1547 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001548{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001549 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001550 struct brcmf_cfg80211_security *sec;
1551 struct brcmf_wsec_key key;
1552 s32 val;
1553 s32 err = 0;
1554
Arend van Spriel16886732012-12-05 15:26:04 +01001555 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001556
Roland Vossena718e2f2011-10-12 20:51:24 +02001557 if (sme->key_len == 0)
1558 return 0;
1559
Arend van Spriel06bb1232012-09-27 14:17:56 +02001560 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001561 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1562 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001563
1564 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1565 return 0;
1566
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001567 if (!(sec->cipher_pairwise &
1568 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1569 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001570
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001571 memset(&key, 0, sizeof(key));
1572 key.len = (u32) sme->key_len;
1573 key.index = (u32) sme->key_idx;
1574 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001575 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001576 return -EINVAL;
1577 }
1578 memcpy(key.data, sme->key, key.len);
1579 key.flags = BRCMF_PRIMARY_KEY;
1580 switch (sec->cipher_pairwise) {
1581 case WLAN_CIPHER_SUITE_WEP40:
1582 key.algo = CRYPTO_ALGO_WEP1;
1583 break;
1584 case WLAN_CIPHER_SUITE_WEP104:
1585 key.algo = CRYPTO_ALGO_WEP128;
1586 break;
1587 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001588 brcmf_err("Invalid algorithm (%d)\n",
1589 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001590 return -EINVAL;
1591 }
1592 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001593 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1594 key.len, key.index, key.algo);
1595 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001596 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001597 if (err)
1598 return err;
1599
1600 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001601 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001602 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001603 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001604 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001605 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001606 }
1607 return err;
1608}
1609
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001610static
1611enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1612 enum nl80211_auth_type type)
1613{
1614 u32 ci;
1615 if (type == NL80211_AUTHTYPE_AUTOMATIC) {
1616 /* shift to ignore chip revision */
1617 ci = brcmf_get_chip_info(ifp) >> 4;
1618 switch (ci) {
1619 case 43236:
1620 brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n");
1621 return NL80211_AUTHTYPE_OPEN_SYSTEM;
1622 default:
1623 break;
1624 }
1625 }
1626 return type;
1627}
1628
Arend van Spriel5b435de2011-10-05 13:19:03 +02001629static s32
1630brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001631 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001632{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001633 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001634 struct brcmf_if *ifp = netdev_priv(ndev);
1635 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001636 struct ieee80211_channel *chan = sme->channel;
1637 struct brcmf_join_params join_params;
1638 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001639 const struct brcmf_tlv *rsn_ie;
1640 const struct brcmf_vs_tlv *wpa_ie;
1641 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001642 u32 ie_len;
1643 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001644 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001645 s32 err = 0;
1646
Arend van Sprield96b8012012-12-05 15:26:02 +01001647 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001648 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001649 return -EIO;
1650
1651 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001652 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001653 return -EOPNOTSUPP;
1654 }
1655
Hante Meuleman89286dc2013-02-08 15:53:46 +01001656 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1657 /* A normal (non P2P) connection request setup. */
1658 ie = NULL;
1659 ie_len = 0;
1660 /* find the WPA_IE */
1661 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1662 if (wpa_ie) {
1663 ie = wpa_ie;
1664 ie_len = wpa_ie->len + TLV_HDR_LEN;
1665 } else {
1666 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001667 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1668 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001669 WLAN_EID_RSN);
1670 if (rsn_ie) {
1671 ie = rsn_ie;
1672 ie_len = rsn_ie->len + TLV_HDR_LEN;
1673 }
1674 }
1675 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1676 }
1677
1678 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1679 sme->ie, sme->ie_len);
1680 if (err)
1681 brcmf_err("Set Assoc REQ IE Failed\n");
1682 else
1683 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1684
Arend van Sprielc1179032012-10-22 13:55:33 -07001685 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001686
1687 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001688 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001689 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001690 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001691 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1692 cfg->channel, chan->center_freq, chanspec);
1693 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001694 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001695 chanspec = 0;
1696 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001697
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001698 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001699
1700 err = brcmf_set_wpa_version(ndev, sme);
1701 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001702 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001703 goto done;
1704 }
1705
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001706 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001707 err = brcmf_set_auth_type(ndev, sme);
1708 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001709 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001710 goto done;
1711 }
1712
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001713 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001714 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001715 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001716 goto done;
1717 }
1718
1719 err = brcmf_set_key_mgmt(ndev, sme);
1720 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001721 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001722 goto done;
1723 }
1724
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001725 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001726 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001727 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001728 goto done;
1729 }
1730
Hante Meuleman89286dc2013-02-08 15:53:46 +01001731 profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
1732 (u32)sme->ssid_len);
1733 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1734 if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1735 profile->ssid.SSID[profile->ssid.SSID_len] = 0;
1736 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
1737 profile->ssid.SSID_len);
1738 }
1739
1740 /* Join with specific BSSID and cached SSID
1741 * If SSID is zero join based on BSSID only
1742 */
1743 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1744 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1745 if (cfg->channel)
1746 join_params_size += sizeof(u16);
1747 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1748 if (ext_join_params == NULL) {
1749 err = -ENOMEM;
1750 goto done;
1751 }
1752 ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
1753 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
1754 profile->ssid.SSID_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001755
Hante Meuleman89286dc2013-02-08 15:53:46 +01001756 /* Set up join scan parameters */
1757 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001758 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1759
1760 if (sme->bssid)
1761 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1762 else
1763 memset(&ext_join_params->assoc_le.bssid, 0xFF, ETH_ALEN);
1764
1765 if (cfg->channel) {
1766 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1767
1768 ext_join_params->assoc_le.chanspec_list[0] =
1769 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001770 /* Increase dwell time to receive probe response or detect
1771 * beacon from target AP at a noisy air only during connect
1772 * command.
1773 */
1774 ext_join_params->scan_le.active_time =
1775 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1776 ext_join_params->scan_le.passive_time =
1777 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1778 /* To sync with presence period of VSDB GO send probe request
1779 * more frequently. Probe request will be stopped when it gets
1780 * probe response from target AP/GO.
1781 */
1782 ext_join_params->scan_le.nprobes =
1783 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1784 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1785 } else {
1786 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1787 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1788 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001789 }
1790
1791 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1792 join_params_size);
1793 kfree(ext_join_params);
1794 if (!err)
1795 /* This is it. join command worked, we are done */
1796 goto done;
1797
1798 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001799 memset(&join_params, 0, sizeof(join_params));
1800 join_params_size = sizeof(join_params.ssid_le);
1801
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001802 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001803 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001804
Hante Meuleman89286dc2013-02-08 15:53:46 +01001805 if (sme->bssid)
1806 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1807 else
1808 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001809
Hante Meuleman17012612013-02-06 18:40:44 +01001810 if (cfg->channel) {
1811 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1812 join_params.params_le.chanspec_num = cpu_to_le32(1);
1813 join_params_size += sizeof(join_params.params_le);
1814 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001815 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001816 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001817 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001818 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001819
1820done:
1821 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001822 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001823 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001824 return err;
1825}
1826
1827static s32
1828brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1829 u16 reason_code)
1830{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001831 struct brcmf_if *ifp = netdev_priv(ndev);
1832 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001833 struct brcmf_scb_val_le scbval;
1834 s32 err = 0;
1835
Arend van Sprield96b8012012-12-05 15:26:02 +01001836 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001837 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001838 return -EIO;
1839
Arend van Sprielc1179032012-10-22 13:55:33 -07001840 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001841 cfg80211_disconnected(ndev, reason_code, NULL, 0, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001842
Arend van Spriel06bb1232012-09-27 14:17:56 +02001843 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001844 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001845 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001846 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001847 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001848 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001849
Arend van Sprield96b8012012-12-05 15:26:02 +01001850 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001851 return err;
1852}
1853
1854static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001855brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001856 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001857{
1858
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001859 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001860 struct net_device *ndev = cfg_to_ndev(cfg);
1861 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001862 u16 txpwrmw;
1863 s32 err = 0;
1864 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001865 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001866
Arend van Sprield96b8012012-12-05 15:26:02 +01001867 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001868 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001869 return -EIO;
1870
1871 switch (type) {
1872 case NL80211_TX_POWER_AUTOMATIC:
1873 break;
1874 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001875 case NL80211_TX_POWER_FIXED:
1876 if (dbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001877 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001878 err = -EINVAL;
1879 goto done;
1880 }
1881 break;
1882 }
1883 /* Make sure radio is off or on as far as software is concerned */
1884 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001885 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001886 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001887 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001888
1889 if (dbm > 0xffff)
1890 txpwrmw = 0xffff;
1891 else
1892 txpwrmw = (u16) dbm;
Arend van Sprielac24be62012-10-22 10:36:23 -07001893 err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
1894 (s32)brcmf_mw_to_qdbm(txpwrmw));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001895 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001896 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001897 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001898
1899done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001900 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001901 return err;
1902}
1903
Johannes Bergc8442112012-10-24 10:17:18 +02001904static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
1905 struct wireless_dev *wdev,
1906 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001907{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001908 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001909 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001910 s32 txpwrdbm;
1911 u8 result;
1912 s32 err = 0;
1913
Arend van Sprield96b8012012-12-05 15:26:02 +01001914 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001915 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001916 return -EIO;
1917
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001918 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001919 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001920 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001921 goto done;
1922 }
1923
1924 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001925 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001926
1927done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001928 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001929 return err;
1930}
1931
1932static s32
1933brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1934 u8 key_idx, bool unicast, bool multicast)
1935{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001936 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001937 u32 index;
1938 u32 wsec;
1939 s32 err = 0;
1940
Arend van Sprield96b8012012-12-05 15:26:02 +01001941 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01001942 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001943 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001944 return -EIO;
1945
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001946 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001947 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001948 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001949 goto done;
1950 }
1951
1952 if (wsec & WEP_ENABLED) {
1953 /* Just select a new current key */
1954 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001955 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001956 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001957 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001958 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001959 }
1960done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001961 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001962 return err;
1963}
1964
1965static s32
1966brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
1967 u8 key_idx, const u8 *mac_addr, struct key_params *params)
1968{
Hante Meuleman992f6062013-04-02 21:06:17 +02001969 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001970 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02001972 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02001973
1974 memset(&key, 0, sizeof(key));
1975 key.index = (u32) key_idx;
1976 /* Instead of bcast for ea address for default wep keys,
1977 driver needs it to be Null */
1978 if (!is_multicast_ether_addr(mac_addr))
1979 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
1980 key.len = (u32) params->key_len;
1981 /* check for key index change */
1982 if (key.len == 0) {
1983 /* key delete */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001984 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001985 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001986 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001987 } else {
1988 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001989 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001990 return -EINVAL;
1991 }
1992
Arend van Spriel16886732012-12-05 15:26:04 +01001993 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001994 memcpy(key.data, params->key, key.len);
1995
Arend van Spriel967fe2c2014-03-15 17:18:21 +01001996 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02001997 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
1998 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001999 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2000 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2001 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2002 }
2003
2004 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2005 if (params->seq && params->seq_len == 6) {
2006 /* rx iv */
2007 u8 *ivptr;
2008 ivptr = (u8 *) params->seq;
2009 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2010 (ivptr[3] << 8) | ivptr[2];
2011 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2012 key.iv_initialized = true;
2013 }
2014
2015 switch (params->cipher) {
2016 case WLAN_CIPHER_SUITE_WEP40:
2017 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002018 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002019 break;
2020 case WLAN_CIPHER_SUITE_WEP104:
2021 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002022 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002023 break;
2024 case WLAN_CIPHER_SUITE_TKIP:
2025 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002026 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002027 break;
2028 case WLAN_CIPHER_SUITE_AES_CMAC:
2029 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002030 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002031 break;
2032 case WLAN_CIPHER_SUITE_CCMP:
2033 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002034 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002035 break;
2036 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002037 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002038 return -EINVAL;
2039 }
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07002040 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002041 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002042 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002043 }
2044 return err;
2045}
2046
2047static s32
2048brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2049 u8 key_idx, bool pairwise, const u8 *mac_addr,
2050 struct key_params *params)
2051{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002052 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002053 struct brcmf_wsec_key key;
2054 s32 val;
2055 s32 wsec;
2056 s32 err = 0;
2057 u8 keybuf[8];
2058
Arend van Sprield96b8012012-12-05 15:26:02 +01002059 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002060 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002061 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002062 return -EIO;
2063
Daniel Kim787eb032014-01-29 15:32:23 +01002064 if (mac_addr &&
2065 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2066 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002067 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002068 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2069 }
2070 memset(&key, 0, sizeof(key));
2071
2072 key.len = (u32) params->key_len;
2073 key.index = (u32) key_idx;
2074
2075 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002076 brcmf_err("Too long key length (%u)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002077 err = -EINVAL;
2078 goto done;
2079 }
2080 memcpy(key.data, params->key, key.len);
2081
2082 key.flags = BRCMF_PRIMARY_KEY;
2083 switch (params->cipher) {
2084 case WLAN_CIPHER_SUITE_WEP40:
2085 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002086 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002087 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002088 break;
2089 case WLAN_CIPHER_SUITE_WEP104:
2090 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002091 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002092 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002093 break;
2094 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002095 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002096 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02002097 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2098 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2099 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2100 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002101 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002102 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002103 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002104 break;
2105 case WLAN_CIPHER_SUITE_AES_CMAC:
2106 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002107 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002108 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002109 break;
2110 case WLAN_CIPHER_SUITE_CCMP:
2111 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002112 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002113 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002114 break;
2115 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002116 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002117 err = -EINVAL;
2118 goto done;
2119 }
2120
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07002121 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002122 if (err)
2123 goto done;
2124
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002125 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002126 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002127 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002128 goto done;
2129 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002130 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002131 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002132 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002133 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002134 goto done;
2135 }
2136
Arend van Spriel5b435de2011-10-05 13:19:03 +02002137done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002138 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002139 return err;
2140}
2141
2142static s32
2143brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2144 u8 key_idx, bool pairwise, const u8 *mac_addr)
2145{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002146 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002147 struct brcmf_wsec_key key;
2148 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002149
Arend van Sprield96b8012012-12-05 15:26:02 +01002150 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002151 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002152 return -EIO;
2153
Hante Meuleman256c3742012-11-05 16:22:28 -08002154 if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
2155 /* we ignore this key index in this case */
Arend van Spriel57d6e912012-12-05 15:26:00 +01002156 brcmf_err("invalid key index (%d)\n", key_idx);
Hante Meuleman256c3742012-11-05 16:22:28 -08002157 return -EINVAL;
2158 }
2159
Arend van Spriel5b435de2011-10-05 13:19:03 +02002160 memset(&key, 0, sizeof(key));
2161
2162 key.index = (u32) key_idx;
2163 key.flags = BRCMF_PRIMARY_KEY;
2164 key.algo = CRYPTO_ALGO_OFF;
2165
Arend van Spriel16886732012-12-05 15:26:04 +01002166 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002167
2168 /* Set the new key/index */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07002169 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002170
Arend van Sprield96b8012012-12-05 15:26:02 +01002171 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002172 return err;
2173}
2174
2175static s32
2176brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2177 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2178 void (*callback) (void *cookie, struct key_params * params))
2179{
2180 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002181 struct brcmf_if *ifp = netdev_priv(ndev);
2182 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002183 struct brcmf_cfg80211_security *sec;
2184 s32 wsec;
2185 s32 err = 0;
2186
Arend van Sprield96b8012012-12-05 15:26:02 +01002187 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002188 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002189 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002190 return -EIO;
2191
2192 memset(&params, 0, sizeof(params));
2193
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002194 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002195 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002196 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002197 /* Ignore this error, may happen during DISASSOC */
2198 err = -EAGAIN;
2199 goto done;
2200 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002201 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002202 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002203 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2204 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002205 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002206 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2207 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002208 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002209 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002210 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002211 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002212 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002213 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002214 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002215 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002216 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002217 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002218 err = -EINVAL;
2219 goto done;
2220 }
2221 callback(cookie, &params);
2222
2223done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002224 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002225 return err;
2226}
2227
2228static s32
2229brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2230 struct net_device *ndev, u8 key_idx)
2231{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002232 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002233
2234 return -EOPNOTSUPP;
2235}
2236
2237static s32
2238brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002239 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002240{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002241 struct brcmf_if *ifp = netdev_priv(ndev);
2242 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002243 struct brcmf_scb_val_le scb_val;
2244 int rssi;
2245 s32 rate;
2246 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02002247 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002248 struct brcmf_sta_info_le sta_info_le;
Hante Meuleman9ee66d12014-01-29 15:32:11 +01002249 u32 beacon_period;
2250 u32 dtim_period;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002251
Arend van Sprield96b8012012-12-05 15:26:02 +01002252 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002253 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002254 return -EIO;
2255
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002256 if (brcmf_is_apmode(ifp->vif)) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002257 memcpy(&sta_info_le, mac, ETH_ALEN);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002258 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002259 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002260 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002261 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002262 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002263 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002264 }
Hante Meuleman1a873342012-09-27 14:17:54 +02002265 sinfo->filled = STATION_INFO_INACTIVE_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002266 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2267 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002268 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002269 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02002270 }
Arend van Sprield96b8012012-12-05 15:26:02 +01002271 brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
2272 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002273 } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002274 if (memcmp(mac, bssid, ETH_ALEN)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002275 brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
2276 mac, bssid);
Hante Meuleman1a873342012-09-27 14:17:54 +02002277 err = -ENOENT;
2278 goto done;
2279 }
2280 /* Report the current tx rate */
Hante Meuleman89286dc2013-02-08 15:53:46 +01002281 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02002282 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002283 brcmf_err("Could not get rate (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002284 goto done;
2285 } else {
2286 sinfo->filled |= STATION_INFO_TX_BITRATE;
2287 sinfo->txrate.legacy = rate * 5;
Arend van Spriel16886732012-12-05 15:26:04 +01002288 brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
Hante Meuleman1a873342012-09-27 14:17:54 +02002289 }
2290
Arend van Sprielc1179032012-10-22 13:55:33 -07002291 if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
2292 &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002293 memset(&scb_val, 0, sizeof(scb_val));
Arend van Sprielc1179032012-10-22 13:55:33 -07002294 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
2295 &scb_val, sizeof(scb_val));
Hante Meuleman1a873342012-09-27 14:17:54 +02002296 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002297 brcmf_err("Could not get rssi (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002298 goto done;
2299 } else {
2300 rssi = le32_to_cpu(scb_val.val);
2301 sinfo->filled |= STATION_INFO_SIGNAL;
2302 sinfo->signal = rssi;
Arend van Spriel16886732012-12-05 15:26:04 +01002303 brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
Hante Meuleman1a873342012-09-27 14:17:54 +02002304 }
Hante Meuleman9ee66d12014-01-29 15:32:11 +01002305 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
2306 &beacon_period);
2307 if (err) {
2308 brcmf_err("Could not get beacon period (%d)\n",
2309 err);
2310 goto done;
2311 } else {
2312 sinfo->bss_param.beacon_interval =
2313 beacon_period;
2314 brcmf_dbg(CONN, "Beacon peroid %d\n",
2315 beacon_period);
2316 }
2317 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
2318 &dtim_period);
2319 if (err) {
2320 brcmf_err("Could not get DTIM period (%d)\n",
2321 err);
2322 goto done;
2323 } else {
2324 sinfo->bss_param.dtim_period = dtim_period;
2325 brcmf_dbg(CONN, "DTIM peroid %d\n",
2326 dtim_period);
2327 }
2328 sinfo->filled |= STATION_INFO_BSS_PARAM;
Hante Meuleman1a873342012-09-27 14:17:54 +02002329 }
2330 } else
2331 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002332done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002333 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002334 return err;
2335}
2336
2337static s32
2338brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2339 bool enabled, s32 timeout)
2340{
2341 s32 pm;
2342 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002343 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002344 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002345
Arend van Sprield96b8012012-12-05 15:26:02 +01002346 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002347
2348 /*
2349 * Powersave enable/disable request is coming from the
2350 * cfg80211 even before the interface is up. In that
2351 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002352 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002353 * FW later while initializing the dongle
2354 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002355 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002356 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002357
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002358 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002359 goto done;
2360 }
2361
2362 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002363 /* Do not enable the power save after assoc if it is a p2p interface */
2364 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2365 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2366 pm = PM_OFF;
2367 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002368 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002369
Arend van Sprielc1179032012-10-22 13:55:33 -07002370 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002371 if (err) {
2372 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002373 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002374 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002375 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002376 }
2377done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002378 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002379 return err;
2380}
2381
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002382static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002383 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002384{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002385 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002386 struct ieee80211_channel *notify_channel;
2387 struct cfg80211_bss *bss;
2388 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002389 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002390 u16 channel;
2391 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002392 u16 notify_capability;
2393 u16 notify_interval;
2394 u8 *notify_ie;
2395 size_t notify_ielen;
2396 s32 notify_signal;
2397
2398 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002399 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002400 return 0;
2401 }
2402
Franky Lin83cf17a2013-04-11 13:28:50 +02002403 if (!bi->ctl_ch) {
2404 ch.chspec = le16_to_cpu(bi->chanspec);
2405 cfg->d11inf.decchspec(&ch);
2406 bi->ctl_ch = ch.chnum;
2407 }
2408 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002409
2410 if (channel <= CH_MAX_2G_CHANNEL)
2411 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2412 else
2413 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2414
2415 freq = ieee80211_channel_to_frequency(channel, band->band);
2416 notify_channel = ieee80211_get_channel(wiphy, freq);
2417
Arend van Spriel5b435de2011-10-05 13:19:03 +02002418 notify_capability = le16_to_cpu(bi->capability);
2419 notify_interval = le16_to_cpu(bi->beacon_period);
2420 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2421 notify_ielen = le32_to_cpu(bi->ie_length);
2422 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2423
Arend van Spriel16886732012-12-05 15:26:04 +01002424 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2425 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2426 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2427 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2428 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002429
2430 bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002431 0, notify_capability, notify_interval, notify_ie,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002432 notify_ielen, notify_signal, GFP_KERNEL);
2433
Franky Line78946e2011-11-10 20:30:34 +01002434 if (!bss)
2435 return -ENOMEM;
2436
Johannes Berg5b112d32013-02-01 01:49:58 +01002437 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002438
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002439 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002440}
2441
Roland Vossen6f09be02011-10-18 14:03:02 +02002442static struct brcmf_bss_info_le *
2443next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2444{
2445 if (bss == NULL)
2446 return list->bss_info_le;
2447 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2448 le32_to_cpu(bss->length));
2449}
2450
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002451static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002452{
2453 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002454 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002455 s32 err = 0;
2456 int i;
2457
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002458 bss_list = cfg->bss_list;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002459 if (bss_list->count != 0 &&
2460 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002461 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2462 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002463 return -EOPNOTSUPP;
2464 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002465 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002466 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002467 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002468 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002469 if (err)
2470 break;
2471 }
2472 return err;
2473}
2474
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002475static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002476 struct net_device *ndev, const u8 *bssid)
2477{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002478 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002479 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002480 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002481 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002482 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002483 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002484 u8 *buf = NULL;
2485 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002486 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002487 u16 notify_capability;
2488 u16 notify_interval;
2489 u8 *notify_ie;
2490 size_t notify_ielen;
2491 s32 notify_signal;
2492
Arend van Sprield96b8012012-12-05 15:26:02 +01002493 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002494
2495 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2496 if (buf == NULL) {
2497 err = -ENOMEM;
2498 goto CleanUp;
2499 }
2500
2501 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2502
Arend van Sprielac24be62012-10-22 10:36:23 -07002503 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2504 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002505 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002506 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002507 goto CleanUp;
2508 }
2509
Roland Vossend34bf642011-10-18 14:03:01 +02002510 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002511
Franky Lin83cf17a2013-04-11 13:28:50 +02002512 ch.chspec = le16_to_cpu(bi->chanspec);
2513 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002514
Franky Lin83cf17a2013-04-11 13:28:50 +02002515 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002516 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2517 else
2518 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2519
Franky Lin83cf17a2013-04-11 13:28:50 +02002520 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002521 notify_channel = ieee80211_get_channel(wiphy, freq);
2522
Arend van Spriel5b435de2011-10-05 13:19:03 +02002523 notify_capability = le16_to_cpu(bi->capability);
2524 notify_interval = le16_to_cpu(bi->beacon_period);
2525 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2526 notify_ielen = le32_to_cpu(bi->ie_length);
2527 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2528
Franky Lin83cf17a2013-04-11 13:28:50 +02002529 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002530 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2531 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2532 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002533
Franky Line78946e2011-11-10 20:30:34 +01002534 bss = cfg80211_inform_bss(wiphy, notify_channel, bssid,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002535 0, notify_capability, notify_interval,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002536 notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
2537
Franky Line78946e2011-11-10 20:30:34 +01002538 if (!bss) {
2539 err = -ENOMEM;
2540 goto CleanUp;
2541 }
2542
Johannes Berg5b112d32013-02-01 01:49:58 +01002543 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002544
Arend van Spriel5b435de2011-10-05 13:19:03 +02002545CleanUp:
2546
2547 kfree(buf);
2548
Arend van Sprield96b8012012-12-05 15:26:02 +01002549 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002550
2551 return err;
2552}
2553
Hante Meuleman89286dc2013-02-08 15:53:46 +01002554static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2555 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002556{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002557 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002558 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002559 struct brcmf_ssid *ssid;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002560 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002561 u16 beacon_interval;
2562 u8 dtim_period;
2563 size_t ie_len;
2564 u8 *ie;
2565 s32 err = 0;
2566
Arend van Sprield96b8012012-12-05 15:26:02 +01002567 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002568 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002569 return err;
2570
Arend van Spriel06bb1232012-09-27 14:17:56 +02002571 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002572
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002573 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002574 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002575 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002576 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002577 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002578 goto update_bss_info_out;
2579 }
2580
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002581 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2582 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002583 if (err)
2584 goto update_bss_info_out;
2585
2586 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2587 ie_len = le32_to_cpu(bi->ie_length);
2588 beacon_interval = le16_to_cpu(bi->beacon_period);
2589
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002590 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002591 if (tim)
2592 dtim_period = tim->data[1];
2593 else {
2594 /*
2595 * active scan was done so we could not get dtim
2596 * information out of probe response.
2597 * so we speficially query dtim information to dongle.
2598 */
2599 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002600 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002601 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002602 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002603 goto update_bss_info_out;
2604 }
2605 dtim_period = (u8)var;
2606 }
2607
Arend van Spriel5b435de2011-10-05 13:19:03 +02002608update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002609 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002610 return err;
2611}
2612
Hante Meuleman18e2f612013-02-08 15:53:49 +01002613void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002614{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002615 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002616
Arend van Sprielc1179032012-10-22 13:55:33 -07002617 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002618 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002619 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002620 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002621 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002622 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2623 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002624}
2625
Hante Meulemane756af52012-09-11 21:18:52 +02002626static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2627{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002628 struct brcmf_cfg80211_info *cfg =
2629 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002630 escan_timeout_work);
2631
Arend van Spriela0f472a2013-04-05 10:57:49 +02002632 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002633}
2634
2635static void brcmf_escan_timeout(unsigned long data)
2636{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002637 struct brcmf_cfg80211_info *cfg =
2638 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002639
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002640 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002641 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002642 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002643 }
2644}
2645
2646static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002647brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2648 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002649 struct brcmf_bss_info_le *bss_info_le)
2650{
Franky Lin83cf17a2013-04-11 13:28:50 +02002651 struct brcmu_chan ch_bss, ch_bss_info_le;
2652
2653 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2654 cfg->d11inf.decchspec(&ch_bss);
2655 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2656 cfg->d11inf.decchspec(&ch_bss_info_le);
2657
Hante Meulemane756af52012-09-11 21:18:52 +02002658 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002659 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002660 bss_info_le->SSID_len == bss->SSID_len &&
2661 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002662 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2663 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002664 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2665 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2666
Hante Meulemane756af52012-09-11 21:18:52 +02002667 /* preserve max RSSI if the measurements are
2668 * both on-channel or both off-channel
2669 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002670 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002671 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002672 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2673 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002674 /* preserve the on-channel rssi measurement
2675 * if the new measurement is off channel
2676 */
2677 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002678 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002679 }
2680 return 1;
2681 }
2682 return 0;
2683}
2684
2685static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002686brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002687 const struct brcmf_event_msg *e, void *data)
2688{
Arend van Spriel19937322012-11-05 16:22:32 -08002689 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002690 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002691 struct brcmf_escan_result_le *escan_result_le;
2692 struct brcmf_bss_info_le *bss_info_le;
2693 struct brcmf_bss_info_le *bss = NULL;
2694 u32 bi_length;
2695 struct brcmf_scan_results *list;
2696 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002697 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002698
Arend van Spriel5c36b992012-11-14 18:46:05 -08002699 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002700
Arend van Spriela0f472a2013-04-05 10:57:49 +02002701 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2702 brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002703 return -EPERM;
2704 }
2705
2706 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002707 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002708 escan_result_le = (struct brcmf_escan_result_le *) data;
2709 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002710 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002711 goto exit;
2712 }
Hante Meulemane756af52012-09-11 21:18:52 +02002713 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002714 brcmf_err("Invalid bss_count %d: ignoring\n",
2715 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002716 goto exit;
2717 }
2718 bss_info_le = &escan_result_le->bss_info_le;
2719
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002720 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2721 goto exit;
2722
2723 if (!cfg->scan_request) {
2724 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2725 goto exit;
2726 }
2727
Hante Meulemane756af52012-09-11 21:18:52 +02002728 bi_length = le32_to_cpu(bss_info_le->length);
2729 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2730 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002731 brcmf_err("Invalid bss_info length %d: ignoring\n",
2732 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002733 goto exit;
2734 }
2735
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002736 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002737 BIT(NL80211_IFTYPE_ADHOC))) {
2738 if (le16_to_cpu(bss_info_le->capability) &
2739 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002740 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002741 goto exit;
2742 }
2743 }
2744
2745 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002746 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002747 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002748 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002749 goto exit;
2750 }
2751
2752 for (i = 0; i < list->count; i++) {
2753 bss = bss ? (struct brcmf_bss_info_le *)
2754 ((unsigned char *)bss +
2755 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002756 if (brcmf_compare_update_same_bss(cfg, bss,
2757 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002758 goto exit;
2759 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002760 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002761 bss_info_le, bi_length);
2762 list->version = le32_to_cpu(bss_info_le->version);
2763 list->buflen += bi_length;
2764 list->count++;
2765 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002766 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002767 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2768 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002769 if (cfg->scan_request) {
2770 cfg->bss_list = (struct brcmf_scan_results *)
2771 cfg->escan_info.escan_buf;
2772 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002773 aborted = status != BRCMF_E_STATUS_SUCCESS;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002774 brcmf_notify_escan_complete(cfg, ifp, aborted,
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002775 false);
Hante Meulemane756af52012-09-11 21:18:52 +02002776 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002777 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2778 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002779 }
2780exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002781 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02002782}
2783
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002784static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002785{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002786 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
2787 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08002788 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2789 /* Init scan_timeout timer */
2790 init_timer(&cfg->escan_timeout);
2791 cfg->escan_timeout.data = (unsigned long) cfg;
2792 cfg->escan_timeout.function = brcmf_escan_timeout;
2793 INIT_WORK(&cfg->escan_timeout_work,
2794 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002795}
2796
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002797static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002798{
2799 if (ms < 1000 / HZ) {
2800 cond_resched();
2801 mdelay(ms);
2802 } else {
2803 msleep(ms);
2804 }
2805}
2806
2807static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
2808{
Arend van Sprield96b8012012-12-05 15:26:02 +01002809 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002810
Arend van Spriel5b435de2011-10-05 13:19:03 +02002811 return 0;
2812}
2813
2814static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
2815 struct cfg80211_wowlan *wow)
2816{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002817 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2818 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel7d641072012-10-22 13:55:39 -07002819 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002820
Arend van Sprield96b8012012-12-05 15:26:02 +01002821 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002822
2823 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002824 * if the primary net_device is not READY there is nothing
2825 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02002826 */
Arend van Spriel7d641072012-10-22 13:55:39 -07002827 vif = ((struct brcmf_if *)netdev_priv(ndev))->vif;
2828 if (!check_vif_up(vif))
2829 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002830
Arend van Spriel7d641072012-10-22 13:55:39 -07002831 list_for_each_entry(vif, &cfg->vif_list, list) {
2832 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
2833 continue;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002834 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002835 * While going to suspend if associated with AP disassociate
2836 * from AP to save power while system is in suspended state
Arend van Spriel5b435de2011-10-05 13:19:03 +02002837 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01002838 brcmf_link_down(vif);
Arend van Spriel7d641072012-10-22 13:55:39 -07002839
Arend van Spriel903e0ee2012-11-28 21:44:11 +01002840 /* Make sure WPA_Supplicant receives all the event
2841 * generated due to DISASSOC call to the fw to keep
2842 * the state fw and WPA_Supplicant state consistent
2843 */
2844 brcmf_delay(500);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002845 }
2846
Arend van Spriel7d641072012-10-22 13:55:39 -07002847 /* end any scanning */
2848 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002849 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002850
2851 /* Turn off watchdog timer */
Arend van Sprielf96aa072013-04-05 10:57:48 +02002852 brcmf_set_mpc(netdev_priv(ndev), 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002853
Arend van Spriel7d641072012-10-22 13:55:39 -07002854exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01002855 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07002856 /* clear any scanning activity */
2857 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002858 return 0;
2859}
2860
2861static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002862brcmf_update_pmklist(struct net_device *ndev,
2863 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
2864{
2865 int i, j;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002866 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002867
Arend van Spriel40c8e952011-10-12 20:51:20 +02002868 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
2869
Arend van Spriel16886732012-12-05 15:26:04 +01002870 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002871 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01002872 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
2873 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002874 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01002875 brcmf_dbg(CONN, "%02x\n",
2876 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002877 }
2878
2879 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07002880 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
2881 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002882
2883 return err;
2884}
2885
2886static s32
2887brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2888 struct cfg80211_pmksa *pmksa)
2889{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002890 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002891 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002892 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002893 s32 err = 0;
2894 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002895 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002896
Arend van Sprield96b8012012-12-05 15:26:02 +01002897 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002898 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002899 return -EIO;
2900
Arend van Spriel40c8e952011-10-12 20:51:20 +02002901 pmkid_len = le32_to_cpu(pmkids->npmkid);
2902 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002903 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
2904 break;
2905 if (i < WL_NUM_PMKIDS_MAX) {
2906 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
2907 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002908 if (i == pmkid_len) {
2909 pmkid_len++;
2910 pmkids->npmkid = cpu_to_le32(pmkid_len);
2911 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002912 } else
2913 err = -EINVAL;
2914
Arend van Spriel16886732012-12-05 15:26:04 +01002915 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
2916 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002917 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01002918 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002919
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002920 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002921
Arend van Sprield96b8012012-12-05 15:26:02 +01002922 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002923 return err;
2924}
2925
2926static s32
2927brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2928 struct cfg80211_pmksa *pmksa)
2929{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002930 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002931 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002932 struct pmkid_list pmkid;
2933 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002934 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002935
Arend van Sprield96b8012012-12-05 15:26:02 +01002936 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002937 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002938 return -EIO;
2939
2940 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
2941 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
2942
Arend van Spriel16886732012-12-05 15:26:04 +01002943 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
2944 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002945 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01002946 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002947
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002948 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002949 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002950 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002951 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002952 ETH_ALEN))
2953 break;
2954
Arend van Spriel40c8e952011-10-12 20:51:20 +02002955 if ((pmkid_len > 0)
2956 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002957 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002958 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02002959 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002960 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
2961 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002962 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002963 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
2964 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002965 WLAN_PMKID_LEN);
2966 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002967 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002968 } else
2969 err = -EINVAL;
2970
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002971 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002972
Arend van Sprield96b8012012-12-05 15:26:02 +01002973 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002974 return err;
2975
2976}
2977
2978static s32
2979brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
2980{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002981 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002982 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002983 s32 err = 0;
2984
Arend van Sprield96b8012012-12-05 15:26:02 +01002985 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002986 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002987 return -EIO;
2988
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002989 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
2990 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002991
Arend van Sprield96b8012012-12-05 15:26:02 +01002992 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002993 return err;
2994
2995}
2996
Arend van Spriele5806072012-09-19 22:21:08 +02002997/*
2998 * PFN result doesn't have all the info which are
2999 * required by the supplicant
3000 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3001 * via wl_inform_single_bss in the required format. Escan does require the
3002 * scan request in the form of cfg80211_scan_request. For timebeing, create
3003 * cfg80211_scan_request one out of the received PNO event.
3004 */
3005static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003006brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003007 const struct brcmf_event_msg *e, void *data)
3008{
Arend van Spriel19937322012-11-05 16:22:32 -08003009 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003010 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3011 struct cfg80211_scan_request *request = NULL;
3012 struct cfg80211_ssid *ssid = NULL;
3013 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003014 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003015 int err = 0;
3016 int channel_req = 0;
3017 int band = 0;
3018 struct brcmf_pno_scanresults_le *pfn_result;
3019 u32 result_count;
3020 u32 status;
3021
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003022 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003023
Arend van Spriel5c36b992012-11-14 18:46:05 -08003024 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003025 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003026 return 0;
3027 }
3028
3029 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3030 result_count = le32_to_cpu(pfn_result->count);
3031 status = le32_to_cpu(pfn_result->status);
3032
3033 /*
3034 * PFN event is limited to fit 512 bytes so we may get
3035 * multiple NET_FOUND events. For now place a warning here.
3036 */
3037 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003038 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003039 if (result_count > 0) {
3040 int i;
3041
3042 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003043 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3044 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003045 if (!request || !ssid || !channel) {
3046 err = -ENOMEM;
3047 goto out_err;
3048 }
3049
3050 request->wiphy = wiphy;
3051 data += sizeof(struct brcmf_pno_scanresults_le);
3052 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3053
3054 for (i = 0; i < result_count; i++) {
3055 netinfo = &netinfo_start[i];
3056 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003057 brcmf_err("Invalid netinfo ptr. index: %d\n",
3058 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003059 err = -EINVAL;
3060 goto out_err;
3061 }
3062
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003063 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3064 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003065 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3066 ssid[i].ssid_len = netinfo->SSID_len;
3067 request->n_ssids++;
3068
3069 channel_req = netinfo->channel;
3070 if (channel_req <= CH_MAX_2G_CHANNEL)
3071 band = NL80211_BAND_2GHZ;
3072 else
3073 band = NL80211_BAND_5GHZ;
3074 channel[i].center_freq =
3075 ieee80211_channel_to_frequency(channel_req,
3076 band);
3077 channel[i].band = band;
3078 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3079 request->channels[i] = &channel[i];
3080 request->n_channels++;
3081 }
3082
3083 /* assign parsed ssid array */
3084 if (request->n_ssids)
3085 request->ssids = &ssid[0];
3086
Arend van Sprielc1179032012-10-22 13:55:33 -07003087 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003088 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003089 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003090 }
3091
Arend van Sprielc1179032012-10-22 13:55:33 -07003092 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003093 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003094 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003095 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003096 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003097 goto out_err;
3098 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003099 cfg->sched_escan = true;
3100 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003101 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003102 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003103 goto out_err;
3104 }
3105
3106 kfree(ssid);
3107 kfree(channel);
3108 kfree(request);
3109 return 0;
3110
3111out_err:
3112 kfree(ssid);
3113 kfree(channel);
3114 kfree(request);
3115 cfg80211_sched_scan_stopped(wiphy);
3116 return err;
3117}
3118
Arend van Spriele5806072012-09-19 22:21:08 +02003119static int brcmf_dev_pno_clean(struct net_device *ndev)
3120{
Arend van Spriele5806072012-09-19 22:21:08 +02003121 int ret;
3122
3123 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003124 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003125 if (ret == 0) {
3126 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003127 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3128 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003129 }
3130 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003131 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003132
3133 return ret;
3134}
3135
3136static int brcmf_dev_pno_config(struct net_device *ndev)
3137{
3138 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003139
3140 memset(&pfn_param, 0, sizeof(pfn_param));
3141 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3142
3143 /* set extra pno params */
3144 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3145 pfn_param.repeat = BRCMF_PNO_REPEAT;
3146 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3147
3148 /* set up pno scan fr */
3149 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3150
Arend van Sprielac24be62012-10-22 10:36:23 -07003151 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3152 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003153}
3154
3155static int
3156brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3157 struct net_device *ndev,
3158 struct cfg80211_sched_scan_request *request)
3159{
Arend van Sprielc1179032012-10-22 13:55:33 -07003160 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003161 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003162 struct brcmf_pno_net_param_le pfn;
3163 int i;
3164 int ret = 0;
3165
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003166 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003167 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003168 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003169 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003170 return -EAGAIN;
3171 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003172 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3173 brcmf_err("Scanning suppressed: status (%lu)\n",
3174 cfg->scan_status);
3175 return -EAGAIN;
3176 }
Arend van Spriele5806072012-09-19 22:21:08 +02003177
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003178 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003179 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003180 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003181 return -EINVAL;
3182 }
3183
3184 if (request->n_ssids > 0) {
3185 for (i = 0; i < request->n_ssids; i++) {
3186 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003187 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3188 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003189
3190 /*
3191 * match_set ssids is a supert set of n_ssid list,
3192 * so we need not add these set seperately.
3193 */
3194 }
3195 }
3196
3197 if (request->n_match_sets > 0) {
3198 /* clean up everything */
3199 ret = brcmf_dev_pno_clean(ndev);
3200 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003201 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003202 return ret;
3203 }
3204
3205 /* configure pno */
3206 ret = brcmf_dev_pno_config(ndev);
3207 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003208 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003209 return -EINVAL;
3210 }
3211
3212 /* configure each match set */
3213 for (i = 0; i < request->n_match_sets; i++) {
3214 struct cfg80211_ssid *ssid;
3215 u32 ssid_len;
3216
3217 ssid = &request->match_sets[i].ssid;
3218 ssid_len = ssid->ssid_len;
3219
3220 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003221 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003222 continue;
3223 }
3224 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3225 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3226 pfn.wsec = cpu_to_le32(0);
3227 pfn.infra = cpu_to_le32(1);
3228 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3229 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3230 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003231 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003232 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003233 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3234 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003235 }
3236 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003237 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003238 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003239 return -EINVAL;
3240 }
3241 } else {
3242 return -EINVAL;
3243 }
3244
3245 return 0;
3246}
3247
3248static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3249 struct net_device *ndev)
3250{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003251 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003252
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003253 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003254 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003255 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003256 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003257 return 0;
3258}
Arend van Spriele5806072012-09-19 22:21:08 +02003259
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003260#ifdef CONFIG_NL80211_TESTMODE
David Spinadelfc73f112013-07-31 18:04:15 +03003261static int brcmf_cfg80211_testmode(struct wiphy *wiphy,
3262 struct wireless_dev *wdev,
3263 void *data, int len)
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003264{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003265 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003266 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003267 struct brcmf_dcmd *dcmd = data;
3268 struct sk_buff *reply;
3269 int ret;
3270
Arend van Sprield96b8012012-12-05 15:26:02 +01003271 brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
3272 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003273
3274 if (dcmd->set)
Arend van Sprielac24be62012-10-22 10:36:23 -07003275 ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
3276 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003277 else
Arend van Sprielac24be62012-10-22 10:36:23 -07003278 ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
3279 dcmd->buf, dcmd->len);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003280 if (ret == 0) {
3281 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3282 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3283 ret = cfg80211_testmode_reply(reply);
3284 }
3285 return ret;
3286}
3287#endif
3288
Hante Meuleman1f170112013-02-06 18:40:38 +01003289static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003290{
3291 s32 err;
3292
3293 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003294 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003295 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003296 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003297 return err;
3298 }
3299 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003300 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003301 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003302 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003303 return err;
3304 }
3305 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003306 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003307 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003308 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003309 return err;
3310 }
3311
3312 return 0;
3313}
3314
3315static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3316{
3317 if (is_rsn_ie)
3318 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3319
3320 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3321}
3322
3323static s32
Johannes Berg4b5800f2014-01-15 14:55:59 +01003324brcmf_configure_wpaie(struct net_device *ndev,
3325 const struct brcmf_vs_tlv *wpa_ie,
3326 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003327{
Arend van Sprielac24be62012-10-22 10:36:23 -07003328 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003329 u32 auth = 0; /* d11 open authentication */
3330 u16 count;
3331 s32 err = 0;
3332 s32 len = 0;
3333 u32 i;
3334 u32 wsec;
3335 u32 pval = 0;
3336 u32 gval = 0;
3337 u32 wpa_auth = 0;
3338 u32 offset;
3339 u8 *data;
3340 u16 rsn_cap;
3341 u32 wme_bss_disable;
3342
Arend van Sprield96b8012012-12-05 15:26:02 +01003343 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003344 if (wpa_ie == NULL)
3345 goto exit;
3346
3347 len = wpa_ie->len + TLV_HDR_LEN;
3348 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003349 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003350 if (!is_rsn_ie)
3351 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003352 else
3353 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003354
3355 /* check for multicast cipher suite */
3356 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3357 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003358 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003359 goto exit;
3360 }
3361
3362 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3363 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003364 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003365 goto exit;
3366 }
3367 offset += TLV_OUI_LEN;
3368
3369 /* pick up multicast cipher */
3370 switch (data[offset]) {
3371 case WPA_CIPHER_NONE:
3372 gval = 0;
3373 break;
3374 case WPA_CIPHER_WEP_40:
3375 case WPA_CIPHER_WEP_104:
3376 gval = WEP_ENABLED;
3377 break;
3378 case WPA_CIPHER_TKIP:
3379 gval = TKIP_ENABLED;
3380 break;
3381 case WPA_CIPHER_AES_CCM:
3382 gval = AES_ENABLED;
3383 break;
3384 default:
3385 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003386 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003387 goto exit;
3388 }
3389
3390 offset++;
3391 /* walk thru unicast cipher list and pick up what we recognize */
3392 count = data[offset] + (data[offset + 1] << 8);
3393 offset += WPA_IE_SUITE_COUNT_LEN;
3394 /* Check for unicast suite(s) */
3395 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3396 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003397 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003398 goto exit;
3399 }
3400 for (i = 0; i < count; i++) {
3401 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3402 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003403 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003404 goto exit;
3405 }
3406 offset += TLV_OUI_LEN;
3407 switch (data[offset]) {
3408 case WPA_CIPHER_NONE:
3409 break;
3410 case WPA_CIPHER_WEP_40:
3411 case WPA_CIPHER_WEP_104:
3412 pval |= WEP_ENABLED;
3413 break;
3414 case WPA_CIPHER_TKIP:
3415 pval |= TKIP_ENABLED;
3416 break;
3417 case WPA_CIPHER_AES_CCM:
3418 pval |= AES_ENABLED;
3419 break;
3420 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003421 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003422 }
3423 offset++;
3424 }
3425 /* walk thru auth management suite list and pick up what we recognize */
3426 count = data[offset] + (data[offset + 1] << 8);
3427 offset += WPA_IE_SUITE_COUNT_LEN;
3428 /* Check for auth key management suite(s) */
3429 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3430 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003431 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003432 goto exit;
3433 }
3434 for (i = 0; i < count; i++) {
3435 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3436 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003437 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003438 goto exit;
3439 }
3440 offset += TLV_OUI_LEN;
3441 switch (data[offset]) {
3442 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003443 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003444 wpa_auth |= WPA_AUTH_NONE;
3445 break;
3446 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003447 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003448 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3449 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3450 break;
3451 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003452 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003453 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3454 (wpa_auth |= WPA_AUTH_PSK);
3455 break;
3456 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003457 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003458 }
3459 offset++;
3460 }
3461
3462 if (is_rsn_ie) {
3463 wme_bss_disable = 1;
3464 if ((offset + RSN_CAP_LEN) <= len) {
3465 rsn_cap = data[offset] + (data[offset + 1] << 8);
3466 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3467 wme_bss_disable = 0;
3468 }
3469 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003470 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003471 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003472 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003473 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003474 goto exit;
3475 }
3476 }
3477 /* FOR WPS , set SES_OW_ENABLED */
3478 wsec = (pval | gval | SES_OW_ENABLED);
3479
3480 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003481 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003482 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003483 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003484 goto exit;
3485 }
3486 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003487 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003488 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003489 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003490 goto exit;
3491 }
3492 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003493 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003494 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003495 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003496 goto exit;
3497 }
3498
3499exit:
3500 return err;
3501}
3502
3503static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003504brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003505 struct parsed_vndr_ies *vndr_ies)
3506{
Hante Meuleman1a873342012-09-27 14:17:54 +02003507 struct brcmf_vs_tlv *vndrie;
3508 struct brcmf_tlv *ie;
3509 struct parsed_vndr_ie_info *parsed_info;
3510 s32 remaining_len;
3511
3512 remaining_len = (s32)vndr_ie_len;
3513 memset(vndr_ies, 0, sizeof(*vndr_ies));
3514
3515 ie = (struct brcmf_tlv *)vndr_ie_buf;
3516 while (ie) {
3517 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3518 goto next;
3519 vndrie = (struct brcmf_vs_tlv *)ie;
3520 /* len should be bigger than OUI length + one */
3521 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003522 brcmf_err("invalid vndr ie. length is too small %d\n",
3523 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003524 goto next;
3525 }
3526 /* if wpa or wme ie, do not add ie */
3527 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3528 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3529 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003530 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003531 goto next;
3532 }
3533
3534 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3535
3536 /* save vndr ie information */
3537 parsed_info->ie_ptr = (char *)vndrie;
3538 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3539 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3540
3541 vndr_ies->count++;
3542
Arend van Sprield96b8012012-12-05 15:26:02 +01003543 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3544 parsed_info->vndrie.oui[0],
3545 parsed_info->vndrie.oui[1],
3546 parsed_info->vndrie.oui[2],
3547 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003548
Arend van Spriel9f440b72013-02-08 15:53:36 +01003549 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003550 break;
3551next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003552 remaining_len -= (ie->len + TLV_HDR_LEN);
3553 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003554 ie = NULL;
3555 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003556 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3557 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003558 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003559 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003560}
3561
3562static u32
3563brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3564{
3565
3566 __le32 iecount_le;
3567 __le32 pktflag_le;
3568
3569 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3570 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3571
3572 iecount_le = cpu_to_le32(1);
3573 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3574
3575 pktflag_le = cpu_to_le32(pktflag);
3576 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3577
3578 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3579
3580 return ie_len + VNDR_IE_HDR_SIZE;
3581}
3582
Arend van Spriel1332e262012-11-05 16:22:18 -08003583s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3584 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003585{
Arend van Spriel1332e262012-11-05 16:22:18 -08003586 struct brcmf_if *ifp;
3587 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003588 s32 err = 0;
3589 u8 *iovar_ie_buf;
3590 u8 *curr_ie_buf;
3591 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003592 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003593 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003594 u32 del_add_ie_buf_len = 0;
3595 u32 total_ie_buf_len = 0;
3596 u32 parsed_ie_buf_len = 0;
3597 struct parsed_vndr_ies old_vndr_ies;
3598 struct parsed_vndr_ies new_vndr_ies;
3599 struct parsed_vndr_ie_info *vndrie_info;
3600 s32 i;
3601 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003602 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003603
Arend van Spriel1332e262012-11-05 16:22:18 -08003604 if (!vif)
3605 return -ENODEV;
3606 ifp = vif->ifp;
3607 saved_ie = &vif->saved_ie;
3608
Arend van Sprield96b8012012-12-05 15:26:02 +01003609 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003610 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3611 if (!iovar_ie_buf)
3612 return -ENOMEM;
3613 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003614 switch (pktflag) {
3615 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3616 mgmt_ie_buf = saved_ie->probe_req_ie;
3617 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3618 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3619 break;
3620 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3621 mgmt_ie_buf = saved_ie->probe_res_ie;
3622 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3623 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3624 break;
3625 case BRCMF_VNDR_IE_BEACON_FLAG:
3626 mgmt_ie_buf = saved_ie->beacon_ie;
3627 mgmt_ie_len = &saved_ie->beacon_ie_len;
3628 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3629 break;
3630 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3631 mgmt_ie_buf = saved_ie->assoc_req_ie;
3632 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3633 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3634 break;
3635 default:
3636 err = -EPERM;
3637 brcmf_err("not suitable type\n");
3638 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003639 }
3640
3641 if (vndr_ie_len > mgmt_ie_buf_len) {
3642 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003643 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003644 goto exit;
3645 }
3646
3647 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3648 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3649 ptr = curr_ie_buf;
3650 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3651 for (i = 0; i < new_vndr_ies.count; i++) {
3652 vndrie_info = &new_vndr_ies.ie_info[i];
3653 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3654 vndrie_info->ie_len);
3655 parsed_ie_buf_len += vndrie_info->ie_len;
3656 }
3657 }
3658
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003659 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003660 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3661 (memcmp(mgmt_ie_buf, curr_ie_buf,
3662 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003663 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003664 goto exit;
3665 }
3666
3667 /* parse old vndr_ie */
3668 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3669
3670 /* make a command to delete old ie */
3671 for (i = 0; i < old_vndr_ies.count; i++) {
3672 vndrie_info = &old_vndr_ies.ie_info[i];
3673
Arend van Sprield96b8012012-12-05 15:26:02 +01003674 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3675 vndrie_info->vndrie.id,
3676 vndrie_info->vndrie.len,
3677 vndrie_info->vndrie.oui[0],
3678 vndrie_info->vndrie.oui[1],
3679 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003680
3681 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3682 vndrie_info->ie_ptr,
3683 vndrie_info->ie_len,
3684 "del");
3685 curr_ie_buf += del_add_ie_buf_len;
3686 total_ie_buf_len += del_add_ie_buf_len;
3687 }
3688 }
3689
3690 *mgmt_ie_len = 0;
3691 /* Add if there is any extra IE */
3692 if (mgmt_ie_buf && parsed_ie_buf_len) {
3693 ptr = mgmt_ie_buf;
3694
3695 remained_buf_len = mgmt_ie_buf_len;
3696
3697 /* make a command to add new ie */
3698 for (i = 0; i < new_vndr_ies.count; i++) {
3699 vndrie_info = &new_vndr_ies.ie_info[i];
3700
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003701 /* verify remained buf size before copy data */
3702 if (remained_buf_len < (vndrie_info->vndrie.len +
3703 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003704 brcmf_err("no space in mgmt_ie_buf: len left %d",
3705 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003706 break;
3707 }
3708 remained_buf_len -= (vndrie_info->ie_len +
3709 VNDR_IE_VSIE_OFFSET);
3710
Arend van Sprield96b8012012-12-05 15:26:02 +01003711 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3712 vndrie_info->vndrie.id,
3713 vndrie_info->vndrie.len,
3714 vndrie_info->vndrie.oui[0],
3715 vndrie_info->vndrie.oui[1],
3716 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003717
3718 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3719 vndrie_info->ie_ptr,
3720 vndrie_info->ie_len,
3721 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003722
3723 /* save the parsed IE in wl struct */
3724 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3725 vndrie_info->ie_len);
3726 *mgmt_ie_len += vndrie_info->ie_len;
3727
3728 curr_ie_buf += del_add_ie_buf_len;
3729 total_ie_buf_len += del_add_ie_buf_len;
3730 }
3731 }
3732 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003733 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003734 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003735 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003736 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003737 }
3738
3739exit:
3740 kfree(iovar_ie_buf);
3741 return err;
3742}
3743
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01003744s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
3745{
3746 s32 pktflags[] = {
3747 BRCMF_VNDR_IE_PRBREQ_FLAG,
3748 BRCMF_VNDR_IE_PRBRSP_FLAG,
3749 BRCMF_VNDR_IE_BEACON_FLAG
3750 };
3751 int i;
3752
3753 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
3754 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
3755
3756 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
3757 return 0;
3758}
3759
Hante Meuleman1a873342012-09-27 14:17:54 +02003760static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01003761brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
3762 struct cfg80211_beacon_data *beacon)
3763{
3764 s32 err;
3765
3766 /* Set Beacon IEs to FW */
3767 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
3768 beacon->tail, beacon->tail_len);
3769 if (err) {
3770 brcmf_err("Set Beacon IE Failed\n");
3771 return err;
3772 }
3773 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
3774
3775 /* Set Probe Response IEs to FW */
3776 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
3777 beacon->proberesp_ies,
3778 beacon->proberesp_ies_len);
3779 if (err)
3780 brcmf_err("Set Probe Resp IE Failed\n");
3781 else
3782 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
3783
3784 return err;
3785}
3786
3787static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02003788brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3789 struct cfg80211_ap_settings *settings)
3790{
3791 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02003792 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07003793 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01003794 const struct brcmf_tlv *ssid_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003795 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003796 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01003797 const struct brcmf_tlv *rsn_ie;
3798 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003799 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01003800 enum nl80211_iftype dev_role;
3801 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02003802 u16 chanspec;
Hante Meuleman1a873342012-09-27 14:17:54 +02003803
Arend van Spriel06c01582014-05-12 10:47:37 +02003804 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
3805 settings->chandef.chan->hw_value,
3806 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02003807 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01003808 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
3809 settings->ssid, settings->ssid_len, settings->auth_type,
3810 settings->inactivity_timeout);
Hante Meuleman1a873342012-09-27 14:17:54 +02003811
Hante Meuleman426d0a52013-02-08 15:53:53 +01003812 dev_role = ifp->vif->wdev.iftype;
Hante Meuleman1a873342012-09-27 14:17:54 +02003813
3814 memset(&ssid_le, 0, sizeof(ssid_le));
3815 if (settings->ssid == NULL || settings->ssid_len == 0) {
3816 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
3817 ssid_ie = brcmf_parse_tlvs(
3818 (u8 *)&settings->beacon.head[ie_offset],
3819 settings->beacon.head_len - ie_offset,
3820 WLAN_EID_SSID);
3821 if (!ssid_ie)
3822 return -EINVAL;
3823
3824 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
3825 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01003826 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02003827 } else {
3828 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
3829 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3830 }
3831
Arend van Sprielf96aa072013-04-05 10:57:48 +02003832 brcmf_set_mpc(ifp, 0);
Hante Meulemanb3657452013-05-27 21:09:53 +02003833 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02003834
3835 /* find the RSN_IE */
3836 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
3837 settings->beacon.tail_len, WLAN_EID_RSN);
3838
3839 /* find the WPA_IE */
3840 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
3841 settings->beacon.tail_len);
3842
Hante Meuleman1a873342012-09-27 14:17:54 +02003843 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003844 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003845 if (wpa_ie != NULL) {
3846 /* WPA IE */
Arend van Spriel34778522012-11-05 16:22:19 -08003847 err = brcmf_configure_wpaie(ndev, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02003848 if (err < 0)
3849 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003850 } else {
3851 /* RSN IE */
3852 err = brcmf_configure_wpaie(ndev,
Arend van Spriel34778522012-11-05 16:22:19 -08003853 (struct brcmf_vs_tlv *)rsn_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02003854 if (err < 0)
3855 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003856 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003857 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01003858 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01003859 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02003860 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003861
Hante Meulemana0f07952013-02-08 15:53:47 +01003862 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02003863
Arend van Spriel600a8972014-05-12 10:47:39 +02003864 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
Arend van Spriel06c01582014-05-12 10:47:37 +02003865 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02003866 if (err < 0) {
Arend van Spriel06c01582014-05-12 10:47:37 +02003867 brcmf_err("Set Channel failed: chspec=%d, %d\n", chanspec, err);
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02003868 goto exit;
3869 }
3870
Hante Meuleman1a873342012-09-27 14:17:54 +02003871 if (settings->beacon_interval) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003872 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003873 settings->beacon_interval);
Hante Meuleman1a873342012-09-27 14:17:54 +02003874 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003875 brcmf_err("Beacon Interval Set Error, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003876 goto exit;
3877 }
3878 }
3879 if (settings->dtim_period) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003880 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003881 settings->dtim_period);
Hante Meuleman1a873342012-09-27 14:17:54 +02003882 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003883 brcmf_err("DTIM Interval Set Error, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003884 goto exit;
3885 }
3886 }
Hante Meulemana0f07952013-02-08 15:53:47 +01003887
3888 if (dev_role == NL80211_IFTYPE_AP) {
3889 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
3890 if (err < 0) {
3891 brcmf_err("BRCMF_C_DOWN error %d\n", err);
3892 goto exit;
3893 }
Hante Meuleman2880b862013-02-08 12:06:31 +01003894 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003895 }
3896
Hante Meulemana0f07952013-02-08 15:53:47 +01003897 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003898 if (err < 0) {
Hante Meulemana0f07952013-02-08 15:53:47 +01003899 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003900 goto exit;
3901 }
Hante Meulemana0f07952013-02-08 15:53:47 +01003902 if (dev_role == NL80211_IFTYPE_AP) {
3903 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
3904 if (err < 0) {
3905 brcmf_err("setting AP mode failed %d\n", err);
3906 goto exit;
3907 }
3908 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
3909 if (err < 0) {
3910 brcmf_err("BRCMF_C_UP error (%d)\n", err);
3911 goto exit;
3912 }
3913
3914 memset(&join_params, 0, sizeof(join_params));
3915 /* join parameters starts with ssid */
3916 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
3917 /* create softap */
3918 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
3919 &join_params, sizeof(join_params));
3920 if (err < 0) {
3921 brcmf_err("SET SSID error (%d)\n", err);
3922 goto exit;
3923 }
3924 brcmf_dbg(TRACE, "AP mode configuration complete\n");
3925 } else {
3926 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
3927 sizeof(ssid_le));
3928 if (err < 0) {
3929 brcmf_err("setting ssid failed %d\n", err);
3930 goto exit;
3931 }
3932 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
3933 bss_enable.enable = cpu_to_le32(1);
3934 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
3935 sizeof(bss_enable));
3936 if (err < 0) {
3937 brcmf_err("bss_enable config failed %d\n", err);
3938 goto exit;
3939 }
3940
3941 brcmf_dbg(TRACE, "GO mode configuration complete\n");
3942 }
Arend van Sprielc1179032012-10-22 13:55:33 -07003943 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3944 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003945
3946exit:
Hante Meulemanb3657452013-05-27 21:09:53 +02003947 if (err) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02003948 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02003949 brcmf_configure_arp_offload(ifp, true);
3950 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003951 return err;
3952}
3953
3954static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
3955{
Arend van Sprielc1179032012-10-22 13:55:33 -07003956 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02003957 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01003958 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02003959 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02003960
Arend van Sprield96b8012012-12-05 15:26:02 +01003961 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003962
Hante Meuleman426d0a52013-02-08 15:53:53 +01003963 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003964 /* Due to most likely deauths outstanding we sleep */
3965 /* first to make sure they get processed by fw. */
3966 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02003967
3968 memset(&join_params, 0, sizeof(join_params));
3969 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
3970 &join_params, sizeof(join_params));
3971 if (err < 0)
3972 brcmf_err("SET SSID error (%d)\n", err);
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003973 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
Hante Meuleman5c33a942013-04-02 21:06:18 +02003974 if (err < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003975 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02003976 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
3977 if (err < 0)
3978 brcmf_err("setting AP mode failed %d\n", err);
3979 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
3980 if (err < 0)
3981 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01003982 } else {
3983 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
3984 bss_enable.enable = cpu_to_le32(0);
3985 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
3986 sizeof(bss_enable));
3987 if (err < 0)
3988 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003989 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02003990 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02003991 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01003992 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3993 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
3994
Hante Meuleman1a873342012-09-27 14:17:54 +02003995 return err;
3996}
3997
Hante Meulemana0f07952013-02-08 15:53:47 +01003998static s32
3999brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4000 struct cfg80211_beacon_data *info)
4001{
Hante Meulemana0f07952013-02-08 15:53:47 +01004002 struct brcmf_if *ifp = netdev_priv(ndev);
4003 s32 err;
4004
4005 brcmf_dbg(TRACE, "Enter\n");
4006
Hante Meulemana0f07952013-02-08 15:53:47 +01004007 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4008
4009 return err;
4010}
4011
Hante Meuleman1a873342012-09-27 14:17:54 +02004012static int
4013brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004014 const u8 *mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004015{
Hante Meulemana0f07952013-02-08 15:53:47 +01004016 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004017 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004018 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004019 s32 err;
4020
4021 if (!mac)
4022 return -EFAULT;
4023
Arend van Sprield96b8012012-12-05 15:26:02 +01004024 brcmf_dbg(TRACE, "Enter %pM\n", mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004025
Hante Meulemana0f07952013-02-08 15:53:47 +01004026 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4027 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004028 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004029 return -EIO;
4030
4031 memcpy(&scbval.ea, mac, ETH_ALEN);
4032 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004033 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004034 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004035 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004036 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004037
Arend van Sprield96b8012012-12-05 15:26:02 +01004038 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004039 return err;
4040}
4041
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004042
4043static void
4044brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4045 struct wireless_dev *wdev,
4046 u16 frame_type, bool reg)
4047{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004048 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004049 u16 mgmt_type;
4050
4051 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4052
4053 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004054 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004055 if (reg)
4056 vif->mgmt_rx_reg |= BIT(mgmt_type);
4057 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004058 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004059}
4060
4061
4062static int
4063brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004064 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004065{
4066 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004067 struct ieee80211_channel *chan = params->chan;
4068 const u8 *buf = params->buf;
4069 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004070 const struct ieee80211_mgmt *mgmt;
4071 struct brcmf_cfg80211_vif *vif;
4072 s32 err = 0;
4073 s32 ie_offset;
4074 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004075 struct brcmf_fil_action_frame_le *action_frame;
4076 struct brcmf_fil_af_params_le *af_params;
4077 bool ack;
4078 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004079 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004080
4081 brcmf_dbg(TRACE, "Enter\n");
4082
4083 *cookie = 0;
4084
4085 mgmt = (const struct ieee80211_mgmt *)buf;
4086
Hante Meulemana0f07952013-02-08 15:53:47 +01004087 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4088 brcmf_err("Driver only allows MGMT packet type\n");
4089 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004090 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004091
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004092 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4093
Hante Meulemana0f07952013-02-08 15:53:47 +01004094 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4095 /* Right now the only reason to get a probe response */
4096 /* is for p2p listen response or for p2p GO from */
4097 /* wpa_supplicant. Unfortunately the probe is send */
4098 /* on primary ndev, while dongle wants it on the p2p */
4099 /* vif. Since this is only reason for a probe */
4100 /* response to be sent, the vif is taken from cfg. */
4101 /* If ever desired to send proberesp for non p2p */
4102 /* response then data should be checked for */
4103 /* "DIRECT-". Note in future supplicant will take */
4104 /* dedicated p2p wdev to do this and then this 'hack'*/
4105 /* is not needed anymore. */
4106 ie_offset = DOT11_MGMT_HDR_LEN +
4107 DOT11_BCN_PRB_FIXED_LEN;
4108 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004109 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4110 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4111 err = brcmf_vif_set_mgmt_ie(vif,
4112 BRCMF_VNDR_IE_PRBRSP_FLAG,
4113 &buf[ie_offset],
4114 ie_len);
4115 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4116 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004117 } else if (ieee80211_is_action(mgmt->frame_control)) {
4118 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4119 if (af_params == NULL) {
4120 brcmf_err("unable to allocate frame\n");
4121 err = -ENOMEM;
4122 goto exit;
4123 }
4124 action_frame = &af_params->action_frame;
4125 /* Add the packet Id */
4126 action_frame->packet_id = cpu_to_le32(*cookie);
4127 /* Add BSSID */
4128 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4129 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4130 /* Add the length exepted for 802.11 header */
4131 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004132 /* Add the channel. Use the one specified as parameter if any or
4133 * the current one (got from the firmware) otherwise
4134 */
4135 if (chan)
4136 freq = chan->center_freq;
4137 else
4138 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4139 &freq);
4140 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004141 af_params->channel = cpu_to_le32(chan_nr);
4142
4143 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4144 le16_to_cpu(action_frame->len));
4145
4146 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004147 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004148
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004149 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004150 af_params);
4151
4152 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4153 GFP_KERNEL);
4154 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004155 } else {
4156 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4157 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4158 }
4159
Hante Meuleman18e2f612013-02-08 15:53:49 +01004160exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004161 return err;
4162}
4163
4164
4165static int
4166brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4167 struct wireless_dev *wdev,
4168 u64 cookie)
4169{
4170 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4171 struct brcmf_cfg80211_vif *vif;
4172 int err = 0;
4173
4174 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4175
4176 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4177 if (vif == NULL) {
4178 brcmf_err("No p2p device available for probe response\n");
4179 err = -ENODEV;
4180 goto exit;
4181 }
4182 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4183exit:
4184 return err;
4185}
4186
Piotr Haber61730d42013-04-23 12:53:12 +02004187static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4188 struct wireless_dev *wdev,
4189 enum nl80211_crit_proto_id proto,
4190 u16 duration)
4191{
4192 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4193 struct brcmf_cfg80211_vif *vif;
4194
4195 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4196
4197 /* only DHCP support for now */
4198 if (proto != NL80211_CRIT_PROTO_DHCP)
4199 return -EINVAL;
4200
4201 /* suppress and abort scanning */
4202 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4203 brcmf_abort_scanning(cfg);
4204
4205 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4206}
4207
4208static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4209 struct wireless_dev *wdev)
4210{
4211 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4212 struct brcmf_cfg80211_vif *vif;
4213
4214 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4215
4216 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4217 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4218}
4219
Arend van Spriel89c2f382013-08-10 12:27:25 +02004220static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4221{
4222 int ret;
4223
4224 switch (oper) {
4225 case NL80211_TDLS_DISCOVERY_REQ:
4226 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4227 break;
4228 case NL80211_TDLS_SETUP:
4229 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4230 break;
4231 case NL80211_TDLS_TEARDOWN:
4232 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4233 break;
4234 default:
4235 brcmf_err("unsupported operation: %d\n", oper);
4236 ret = -EOPNOTSUPP;
4237 }
4238 return ret;
4239}
4240
4241static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004242 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004243 enum nl80211_tdls_operation oper)
4244{
4245 struct brcmf_if *ifp;
4246 struct brcmf_tdls_iovar_le info;
4247 int ret = 0;
4248
4249 ret = brcmf_convert_nl80211_tdls_oper(oper);
4250 if (ret < 0)
4251 return ret;
4252
4253 ifp = netdev_priv(ndev);
4254 memset(&info, 0, sizeof(info));
4255 info.mode = (u8)ret;
4256 if (peer)
4257 memcpy(info.ea, peer, ETH_ALEN);
4258
4259 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4260 &info, sizeof(info));
4261 if (ret < 0)
4262 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4263
4264 return ret;
4265}
4266
Arend van Spriel5b435de2011-10-05 13:19:03 +02004267static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004268 .add_virtual_intf = brcmf_cfg80211_add_iface,
4269 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004270 .change_virtual_intf = brcmf_cfg80211_change_iface,
4271 .scan = brcmf_cfg80211_scan,
4272 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4273 .join_ibss = brcmf_cfg80211_join_ibss,
4274 .leave_ibss = brcmf_cfg80211_leave_ibss,
4275 .get_station = brcmf_cfg80211_get_station,
4276 .set_tx_power = brcmf_cfg80211_set_tx_power,
4277 .get_tx_power = brcmf_cfg80211_get_tx_power,
4278 .add_key = brcmf_cfg80211_add_key,
4279 .del_key = brcmf_cfg80211_del_key,
4280 .get_key = brcmf_cfg80211_get_key,
4281 .set_default_key = brcmf_cfg80211_config_default_key,
4282 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4283 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004284 .connect = brcmf_cfg80211_connect,
4285 .disconnect = brcmf_cfg80211_disconnect,
4286 .suspend = brcmf_cfg80211_suspend,
4287 .resume = brcmf_cfg80211_resume,
4288 .set_pmksa = brcmf_cfg80211_set_pmksa,
4289 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004290 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004291 .start_ap = brcmf_cfg80211_start_ap,
4292 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004293 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004294 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004295 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4296 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004297 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4298 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4299 .remain_on_channel = brcmf_p2p_remain_on_channel,
4300 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004301 .start_p2p_device = brcmf_p2p_start_device,
4302 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004303 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4304 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004305 .tdls_oper = brcmf_cfg80211_tdls_oper,
Johannes Berge3335472013-08-06 11:13:19 +02004306 CFG80211_TESTMODE_CMD(brcmf_cfg80211_testmode)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004307};
4308
Arend van Spriele5806072012-09-19 22:21:08 +02004309static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
4310{
Arend van Spriele5806072012-09-19 22:21:08 +02004311 /* scheduled scan settings */
4312 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
4313 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
4314 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
4315 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
Arend van Spriele5806072012-09-19 22:21:08 +02004316}
4317
Arend van Spriel9f440b72013-02-08 15:53:36 +01004318static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
4319 {
Hante Meulemandded3d52013-02-08 15:53:57 +01004320 .max = 2,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004321 .types = BIT(NL80211_IFTYPE_STATION) |
4322 BIT(NL80211_IFTYPE_ADHOC) |
4323 BIT(NL80211_IFTYPE_AP)
4324 },
4325 {
4326 .max = 1,
4327 .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
4328 BIT(NL80211_IFTYPE_P2P_GO)
4329 },
Arend van Spriel9af221b2013-05-14 20:52:36 +02004330 {
4331 .max = 1,
4332 .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
4333 }
Arend van Spriel9f440b72013-02-08 15:53:36 +01004334};
4335static const struct ieee80211_iface_combination brcmf_iface_combos[] = {
4336 {
Hante Meulemandded3d52013-02-08 15:53:57 +01004337 .max_interfaces = BRCMF_IFACE_MAX_CNT,
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004338 .num_different_channels = 2,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004339 .n_limits = ARRAY_SIZE(brcmf_iface_limits),
4340 .limits = brcmf_iface_limits
4341 }
4342};
4343
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004344static const struct ieee80211_txrx_stypes
4345brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
4346 [NL80211_IFTYPE_STATION] = {
4347 .tx = 0xffff,
4348 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
4349 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
4350 },
4351 [NL80211_IFTYPE_P2P_CLIENT] = {
4352 .tx = 0xffff,
4353 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
4354 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
4355 },
4356 [NL80211_IFTYPE_P2P_GO] = {
4357 .tx = 0xffff,
4358 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
4359 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
4360 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
4361 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
4362 BIT(IEEE80211_STYPE_AUTH >> 4) |
4363 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
4364 BIT(IEEE80211_STYPE_ACTION >> 4)
Arend van Sprielbffc61c2013-04-05 10:57:52 +02004365 },
4366 [NL80211_IFTYPE_P2P_DEVICE] = {
4367 .tx = 0xffff,
4368 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
4369 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004370 }
4371};
4372
Arend van Spriel3eacf862012-10-22 13:55:30 -07004373static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004374{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004375 struct wiphy *wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004376 s32 err = 0;
4377
Arend van Spriel3eacf862012-10-22 13:55:30 -07004378 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
4379 if (!wiphy) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004380 brcmf_err("Could not allocate wiphy device\n");
Arend van Spriel3eacf862012-10-22 13:55:30 -07004381 return ERR_PTR(-ENOMEM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004382 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07004383 set_wiphy_dev(wiphy, phydev);
4384 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004385 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004386 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
4387 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
4388 BIT(NL80211_IFTYPE_ADHOC) |
Arend van Spriel9f440b72013-02-08 15:53:36 +01004389 BIT(NL80211_IFTYPE_AP) |
4390 BIT(NL80211_IFTYPE_P2P_CLIENT) |
Arend van Spriel9af221b2013-05-14 20:52:36 +02004391 BIT(NL80211_IFTYPE_P2P_GO) |
4392 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel9f440b72013-02-08 15:53:36 +01004393 wiphy->iface_combinations = brcmf_iface_combos;
4394 wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004395 wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004396 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
4397 wiphy->cipher_suites = __wl_cipher_suites;
4398 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004399 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
Hante Meuleman6eda4e22013-02-08 15:54:02 +01004400 WIPHY_FLAG_OFFCHAN_TX |
Arend van Spriel89c2f382013-08-10 12:27:25 +02004401 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
4402 WIPHY_FLAG_SUPPORTS_TDLS;
Daniel Kim604bf232014-05-12 10:47:27 +02004403 if (!brcmf_roamoff)
4404 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004405 wiphy->mgmt_stypes = brcmf_txrx_stypes;
4406 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004407 brcmf_wiphy_pno_params(wiphy);
Hante Meulemand48200b2013-04-03 12:40:29 +02004408 brcmf_dbg(INFO, "Registering custom regulatory\n");
Luis R. Rodrigueza2f73b62013-11-11 22:15:29 +01004409 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
Hante Meulemand48200b2013-04-03 12:40:29 +02004410 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004411 err = wiphy_register(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004412 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004413 brcmf_err("Could not register wiphy device (%d)\n", err);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004414 wiphy_free(wiphy);
4415 return ERR_PTR(err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004416 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07004417 return wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004418}
4419
Arend van Spriel3eacf862012-10-22 13:55:30 -07004420struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004421 enum nl80211_iftype type,
4422 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004423{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004424 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004425
Arend van Spriel33a6b152013-02-08 15:53:39 +01004426 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004427 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004428 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4429 if (!vif)
4430 return ERR_PTR(-ENOMEM);
4431
4432 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004433 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004434
Arend van Spriel3eacf862012-10-22 13:55:30 -07004435 vif->pm_block = pm_block;
4436 vif->roam_off = -1;
4437
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004438 brcmf_init_prof(&vif->profile);
4439
Arend van Spriel3eacf862012-10-22 13:55:30 -07004440 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004441 return vif;
4442}
4443
Arend van Spriel427dec52014-01-06 12:40:47 +01004444void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004445{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004446 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004447 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004448}
4449
Arend van Spriel9df4d542014-01-06 12:40:49 +01004450void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4451{
4452 struct brcmf_cfg80211_vif *vif;
4453 struct brcmf_if *ifp;
4454
4455 ifp = netdev_priv(ndev);
4456 vif = ifp->vif;
4457
4458 brcmf_free_vif(vif);
4459 free_netdev(ndev);
4460}
4461
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004462static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004463{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004464 u32 event = e->event_code;
4465 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004466
4467 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004468 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004469 return true;
4470 }
4471
4472 return false;
4473}
4474
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004475static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004476{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004477 u32 event = e->event_code;
4478 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004479
Hante Meuleman68ca3952014-02-25 20:30:26 +01004480 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4481 (event == BRCMF_E_DISASSOC_IND) ||
4482 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004483 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004484 return true;
4485 }
4486 return false;
4487}
4488
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004489static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004490 const struct brcmf_event_msg *e)
4491{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004492 u32 event = e->event_code;
4493 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004494
4495 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004496 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4497 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004498 return true;
4499 }
4500
4501 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004502 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004503 return true;
4504 }
4505
4506 return false;
4507}
4508
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004509static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004510{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004511 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004512
4513 kfree(conn_info->req_ie);
4514 conn_info->req_ie = NULL;
4515 conn_info->req_ie_len = 0;
4516 kfree(conn_info->resp_ie);
4517 conn_info->resp_ie = NULL;
4518 conn_info->resp_ie_len = 0;
4519}
4520
Hante Meuleman89286dc2013-02-08 15:53:46 +01004521static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4522 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004523{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004524 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004525 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004526 u32 req_len;
4527 u32 resp_len;
4528 s32 err = 0;
4529
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004530 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004531
Arend van Sprielac24be62012-10-22 10:36:23 -07004532 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4533 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004534 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004535 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004536 return err;
4537 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004538 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004539 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004540 req_len = le32_to_cpu(assoc_info->req_len);
4541 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004542 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004543 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004544 cfg->extra_buf,
4545 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004546 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004547 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004548 return err;
4549 }
4550 conn_info->req_ie_len = req_len;
4551 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004552 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004553 GFP_KERNEL);
4554 } else {
4555 conn_info->req_ie_len = 0;
4556 conn_info->req_ie = NULL;
4557 }
4558 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004559 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004560 cfg->extra_buf,
4561 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004562 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004563 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004564 return err;
4565 }
4566 conn_info->resp_ie_len = resp_len;
4567 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004568 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004569 GFP_KERNEL);
4570 } else {
4571 conn_info->resp_ie_len = 0;
4572 conn_info->resp_ie = NULL;
4573 }
Arend van Spriel16886732012-12-05 15:26:04 +01004574 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4575 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004576
4577 return err;
4578}
4579
4580static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004581brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004582 struct net_device *ndev,
4583 const struct brcmf_event_msg *e)
4584{
Arend van Sprielc1179032012-10-22 13:55:33 -07004585 struct brcmf_if *ifp = netdev_priv(ndev);
4586 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004587 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4588 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004589 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004590 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004591 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004592 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004593 u32 freq;
4594 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004595 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004596
Arend van Sprield96b8012012-12-05 15:26:02 +01004597 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004598
Hante Meuleman89286dc2013-02-08 15:53:46 +01004599 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004600 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004601 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004602
Franky Lina180b832012-10-10 11:13:09 -07004603 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4604 if (buf == NULL) {
4605 err = -ENOMEM;
4606 goto done;
4607 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004608
Franky Lina180b832012-10-10 11:13:09 -07004609 /* data sent to dongle has to be little endian */
4610 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004611 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004612 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004613
4614 if (err)
4615 goto done;
4616
4617 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004618 ch.chspec = le16_to_cpu(bi->chanspec);
4619 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004620
Franky Lin83cf17a2013-04-11 13:28:50 +02004621 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004622 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4623 else
4624 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4625
Franky Lin83cf17a2013-04-11 13:28:50 +02004626 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004627 notify_channel = ieee80211_get_channel(wiphy, freq);
4628
Franky Lina180b832012-10-10 11:13:09 -07004629done:
4630 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004631 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004632 conn_info->req_ie, conn_info->req_ie_len,
4633 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004634 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004635
Arend van Sprielc1179032012-10-22 13:55:33 -07004636 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004637 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004638 return err;
4639}
4640
4641static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004642brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004643 struct net_device *ndev, const struct brcmf_event_msg *e,
4644 bool completed)
4645{
Arend van Sprielc1179032012-10-22 13:55:33 -07004646 struct brcmf_if *ifp = netdev_priv(ndev);
4647 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004648 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004649
Arend van Sprield96b8012012-12-05 15:26:02 +01004650 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004651
Arend van Sprielc1179032012-10-22 13:55:33 -07004652 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4653 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004654 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004655 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004656 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004657 brcmf_update_bss_info(cfg, ifp);
4658 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4659 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004660 }
4661 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004662 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004663 conn_info->req_ie,
4664 conn_info->req_ie_len,
4665 conn_info->resp_ie,
4666 conn_info->resp_ie_len,
4667 completed ? WLAN_STATUS_SUCCESS :
4668 WLAN_STATUS_AUTH_TIMEOUT,
4669 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004670 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4671 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004672 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004673 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004674 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004675}
4676
4677static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004678brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004679 struct net_device *ndev,
4680 const struct brcmf_event_msg *e, void *data)
4681{
Hante Meuleman7ee29602013-02-06 18:40:43 +01004682 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004683 u32 event = e->event_code;
4684 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004685 struct station_info sinfo;
4686
Arend van Spriel16886732012-12-05 15:26:04 +01004687 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004688 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4689 ndev != cfg_to_ndev(cfg)) {
4690 brcmf_dbg(CONN, "AP mode link down\n");
4691 complete(&cfg->vif_disabled);
4692 return 0;
4693 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004694
Hante Meuleman1a873342012-09-27 14:17:54 +02004695 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004696 (reason == BRCMF_E_STATUS_SUCCESS)) {
4697 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004698 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4699 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004700 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004701 return -EINVAL;
4702 }
4703 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004704 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004705 generation++;
4706 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004707 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004708 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4709 (event == BRCMF_E_DEAUTH_IND) ||
4710 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004711 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004712 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004713 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004714}
4715
4716static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004717brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004718 const struct brcmf_event_msg *e, void *data)
4719{
Arend van Spriel19937322012-11-05 16:22:32 -08004720 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4721 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004722 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004723 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004724 s32 err = 0;
4725
Arend van Spriel967fe2c2014-03-15 17:18:21 +01004726 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004727 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004728 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004729 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004730 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004731 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004732 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004733 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004734 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004735 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4736 &ifp->vif->sme_state);
4737 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4738 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004739 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004740 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004741 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004742 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004743 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004744 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004745 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004746 brcmf_link_down(ifp->vif);
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004747 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004748 if (ndev != cfg_to_ndev(cfg))
4749 complete(&cfg->vif_disabled);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004750 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004751 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07004752 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4753 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004754 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004755 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004756 }
4757
4758 return err;
4759}
4760
4761static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004762brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004763 const struct brcmf_event_msg *e, void *data)
4764{
Arend van Spriel19937322012-11-05 16:22:32 -08004765 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004766 u32 event = e->event_code;
4767 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004768
4769 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004770 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08004771 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004772 else
Arend van Spriel19937322012-11-05 16:22:32 -08004773 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004774 }
4775
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004776 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004777}
4778
4779static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004780brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004781 const struct brcmf_event_msg *e, void *data)
4782{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004783 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004784 enum nl80211_key_type key_type;
4785
4786 if (flags & BRCMF_EVENT_MSG_GROUP)
4787 key_type = NL80211_KEYTYPE_GROUP;
4788 else
4789 key_type = NL80211_KEYTYPE_PAIRWISE;
4790
Arend van Spriel19937322012-11-05 16:22:32 -08004791 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004792 NULL, GFP_KERNEL);
4793
4794 return 0;
4795}
4796
Arend van Sprield3c0b632013-02-08 15:53:37 +01004797static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
4798 const struct brcmf_event_msg *e, void *data)
4799{
4800 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4801 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
4802 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
4803 struct brcmf_cfg80211_vif *vif;
4804
4805 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
4806 ifevent->action, ifevent->flags, ifevent->ifidx,
4807 ifevent->bssidx);
4808
Arend van Sprield3c0b632013-02-08 15:53:37 +01004809 mutex_lock(&event->vif_event_lock);
4810 event->action = ifevent->action;
4811 vif = event->vif;
4812
4813 switch (ifevent->action) {
4814 case BRCMF_E_IF_ADD:
4815 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08004816 if (!cfg->vif_event.vif) {
4817 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01004818 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08004819 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01004820
4821 ifp->vif = vif;
4822 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02004823 if (ifp->ndev) {
4824 vif->wdev.netdev = ifp->ndev;
4825 ifp->ndev->ieee80211_ptr = &vif->wdev;
4826 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
4827 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01004828 mutex_unlock(&event->vif_event_lock);
4829 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01004830 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01004831
4832 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01004833 mutex_unlock(&event->vif_event_lock);
4834 /* event may not be upon user request */
4835 if (brcmf_cfg80211_vif_event_armed(cfg))
4836 wake_up(&event->vif_wq);
4837 return 0;
4838
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01004839 case BRCMF_E_IF_CHANGE:
4840 mutex_unlock(&event->vif_event_lock);
4841 wake_up(&event->vif_wq);
4842 return 0;
4843
Arend van Sprield3c0b632013-02-08 15:53:37 +01004844 default:
4845 mutex_unlock(&event->vif_event_lock);
4846 break;
4847 }
4848 return -EINVAL;
4849}
4850
Arend van Spriel5b435de2011-10-05 13:19:03 +02004851static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4852{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004853 conf->frag_threshold = (u32)-1;
4854 conf->rts_threshold = (u32)-1;
4855 conf->retry_short = (u32)-1;
4856 conf->retry_long = (u32)-1;
4857 conf->tx_power = -1;
4858}
4859
Arend van Spriel5c36b992012-11-14 18:46:05 -08004860static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004861{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004862 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
4863 brcmf_notify_connect_status);
4864 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
4865 brcmf_notify_connect_status);
4866 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
4867 brcmf_notify_connect_status);
4868 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
4869 brcmf_notify_connect_status);
4870 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
4871 brcmf_notify_connect_status);
4872 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
4873 brcmf_notify_connect_status);
4874 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
4875 brcmf_notify_roaming_status);
4876 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
4877 brcmf_notify_mic_status);
4878 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
4879 brcmf_notify_connect_status);
4880 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
4881 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01004882 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
4883 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004884 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01004885 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004886 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
4887 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01004888 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
4889 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004890 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
4891 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01004892 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
4893 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004894}
4895
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004896static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004897{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004898 kfree(cfg->conf);
4899 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004900 kfree(cfg->escan_ioctl_buf);
4901 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004902 kfree(cfg->extra_buf);
4903 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004904 kfree(cfg->pmk_list);
4905 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004906}
4907
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004908static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004909{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004910 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4911 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004912 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004913 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4914 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004915 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004916 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4917 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004918 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004919 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4920 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004921 goto init_priv_mem_out;
4922
4923 return 0;
4924
4925init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004926 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004927
4928 return -ENOMEM;
4929}
4930
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004931static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004932{
4933 s32 err = 0;
4934
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004935 cfg->scan_request = NULL;
4936 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01004937 cfg->active_scan = true; /* we do active scan per default */
4938 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004939 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004940 if (err)
4941 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004942 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004943 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004944 brcmf_init_escan(cfg);
4945 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004946 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004947 return err;
4948}
4949
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004950static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004951{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004952 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004953 brcmf_abort_scanning(cfg);
4954 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004955}
4956
Arend van Sprield3c0b632013-02-08 15:53:37 +01004957static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
4958{
4959 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01004960 mutex_init(&event->vif_event_lock);
4961}
4962
Arend van Spriel67b3bd42014-03-20 10:18:03 +01004963static int brcmf_enable_bw40_2g(struct brcmf_if *ifp)
Daniel Kimd2353672014-03-20 10:18:00 +01004964{
4965 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Spriel67b3bd42014-03-20 10:18:03 +01004966 u32 val;
Daniel Kimd2353672014-03-20 10:18:00 +01004967 int err;
4968
Arend van Spriel67b3bd42014-03-20 10:18:03 +01004969 /* verify support for bw_cap command */
4970 val = WLC_BAND_5G;
4971 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
Daniel Kimd2353672014-03-20 10:18:00 +01004972
Arend van Spriel67b3bd42014-03-20 10:18:03 +01004973 if (!err) {
4974 /* only set 2G bandwidth using bw_cap command */
4975 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
Daniel Kim03e5da12014-05-09 12:37:05 +02004976 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
Arend van Spriel67b3bd42014-03-20 10:18:03 +01004977 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
4978 sizeof(band_bwcap));
4979 } else {
4980 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
4981 val = WLC_N_BW_40ALL;
4982 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
4983 }
Daniel Kimd2353672014-03-20 10:18:00 +01004984 return err;
4985}
4986
Arend van Sprield9cb2592012-12-05 15:25:54 +01004987struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
4988 struct device *busdev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004989{
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004990 struct net_device *ndev = drvr->iflist[0]->ndev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004991 struct brcmf_cfg80211_info *cfg;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004992 struct wiphy *wiphy;
4993 struct brcmf_cfg80211_vif *vif;
4994 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004995 s32 err = 0;
Franky Lin83cf17a2013-04-11 13:28:50 +02004996 s32 io_type;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004997
4998 if (!ndev) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004999 brcmf_err("ndev is invalid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005000 return NULL;
5001 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005002
Arend van Spriel3eacf862012-10-22 13:55:30 -07005003 ifp = netdev_priv(ndev);
5004 wiphy = brcmf_setup_wiphy(busdev);
5005 if (IS_ERR(wiphy))
5006 return NULL;
5007
5008 cfg = wiphy_priv(wiphy);
5009 cfg->wiphy = wiphy;
5010 cfg->pub = drvr;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005011 init_vif_event(&cfg->vif_event);
Arend van Spriel3eacf862012-10-22 13:55:30 -07005012 INIT_LIST_HEAD(&cfg->vif_list);
5013
Arend van Sprield3c0b632013-02-08 15:53:37 +01005014 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Spriel3eacf862012-10-22 13:55:30 -07005015 if (IS_ERR(vif)) {
5016 wiphy_free(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005017 return NULL;
5018 }
5019
Arend van Sprield3c0b632013-02-08 15:53:37 +01005020 vif->ifp = ifp;
5021 vif->wdev.netdev = ndev;
5022 ndev->ieee80211_ptr = &vif->wdev;
5023 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
5024
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005025 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005026 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005027 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005028 goto cfg80211_attach_out;
5029 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07005030 ifp->vif = vif;
Hante Meuleman2fde59d2013-02-08 15:53:52 +01005031
5032 err = brcmf_p2p_attach(cfg);
5033 if (err) {
5034 brcmf_err("P2P initilisation failed (%d)\n", err);
5035 goto cfg80211_p2p_attach_out;
5036 }
Piotr Haber61730d42013-04-23 12:53:12 +02005037 err = brcmf_btcoex_attach(cfg);
5038 if (err) {
5039 brcmf_err("BT-coex initialisation failed (%d)\n", err);
5040 brcmf_p2p_detach(&cfg->p2p);
5041 goto cfg80211_p2p_attach_out;
5042 }
Hante Meuleman2fde59d2013-02-08 15:53:52 +01005043
Daniel Kimd2353672014-03-20 10:18:00 +01005044 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
5045 * setup 40MHz in 2GHz band and enable OBSS scanning.
5046 */
5047 if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap &
5048 IEEE80211_HT_CAP_SUP_WIDTH_20_40) {
Arend van Spriel67b3bd42014-03-20 10:18:03 +01005049 err = brcmf_enable_bw40_2g(ifp);
5050 if (!err)
Daniel Kimd2353672014-03-20 10:18:00 +01005051 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
5052 BRCMF_OBSS_COEX_AUTO);
Daniel Kimd2353672014-03-20 10:18:00 +01005053 }
5054
Arend van Spriel89c2f382013-08-10 12:27:25 +02005055 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
5056 if (err) {
5057 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
5058 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
5059 }
5060
Franky Lin83cf17a2013-04-11 13:28:50 +02005061 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION,
5062 &io_type);
5063 if (err) {
5064 brcmf_err("Failed to get D11 version (%d)\n", err);
5065 goto cfg80211_p2p_attach_out;
5066 }
5067 cfg->d11inf.io_type = (u8)io_type;
5068 brcmu_d11_attach(&cfg->d11inf);
5069
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005070 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005071
Hante Meuleman2fde59d2013-02-08 15:53:52 +01005072cfg80211_p2p_attach_out:
5073 wl_deinit_priv(cfg);
5074
Arend van Spriel5b435de2011-10-05 13:19:03 +02005075cfg80211_attach_out:
Arend van Spriel427dec52014-01-06 12:40:47 +01005076 brcmf_free_vif(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005077 return NULL;
5078}
5079
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005080void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005081{
Arend van Spriele1479952014-01-06 12:40:48 +01005082 if (!cfg)
5083 return;
5084
Arend van Spriel427dec52014-01-06 12:40:47 +01005085 WARN_ON(!list_empty(&cfg->vif_list));
5086 wiphy_unregister(cfg->wiphy);
Piotr Haber61730d42013-04-23 12:53:12 +02005087 brcmf_btcoex_detach(cfg);
Arend van Spriel427dec52014-01-06 12:40:47 +01005088 wl_deinit_priv(cfg);
5089 wiphy_free(cfg->wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005090}
5091
Arend van Spriel5b435de2011-10-05 13:19:03 +02005092static s32
Hante Meuleman68ca3952014-02-25 20:30:26 +01005093brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005094{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005095 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005096 __le32 roamtrigger[2];
5097 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005098
5099 /*
5100 * Setup timeout if Beacons are lost and roam is
5101 * off to report link down
5102 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005103 if (brcmf_roamoff) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005104 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005105 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005106 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005107 goto dongle_rom_out;
5108 }
5109 }
5110
5111 /*
5112 * Enable/Disable built-in roaming to allow supplicant
5113 * to take care of roaming
5114 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005115 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5116 brcmf_roamoff ? "Off" : "On");
5117 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005118 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005119 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005120 goto dongle_rom_out;
5121 }
5122
Arend van Sprielf588bc02011-10-12 20:51:22 +02005123 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5124 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005125 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005126 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005127 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005128 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005129 goto dongle_rom_out;
5130 }
5131
Arend van Sprielf588bc02011-10-12 20:51:22 +02005132 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5133 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005134 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005135 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005136 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005137 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005138 goto dongle_rom_out;
5139 }
5140
5141dongle_rom_out:
5142 return err;
5143}
5144
5145static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01005146brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005147 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005148{
5149 s32 err = 0;
5150
Arend van Sprielac24be62012-10-22 10:36:23 -07005151 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005152 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005153 if (err) {
5154 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005155 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005156 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005157 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005158 goto dongle_scantime_out;
5159 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005160 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005161 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005162 if (err) {
5163 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005164 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005165 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005166 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005167 goto dongle_scantime_out;
5168 }
5169
Arend van Sprielac24be62012-10-22 10:36:23 -07005170 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005171 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005172 if (err) {
5173 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005174 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005175 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005176 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005177 goto dongle_scantime_out;
5178 }
5179
5180dongle_scantime_out:
5181 return err;
5182}
5183
Hante Meulemand48200b2013-04-03 12:40:29 +02005184
Arend van Spriel2375d972014-01-06 12:40:41 +01005185static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg,
5186 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005187{
5188 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5189 struct ieee80211_channel *band_chan_arr;
5190 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005191 struct brcmu_chan ch;
Hante Meulemand48200b2013-04-03 12:40:29 +02005192 s32 err;
5193 u8 *pbuf;
5194 u32 i, j;
5195 u32 total;
Hante Meulemand48200b2013-04-03 12:40:29 +02005196 enum ieee80211_band band;
5197 u32 channel;
5198 u32 *n_cnt;
Hante Meulemand48200b2013-04-03 12:40:29 +02005199 u32 index;
5200 u32 ht40_flag;
5201 bool update;
5202 u32 array_size;
5203
5204 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5205
5206 if (pbuf == NULL)
5207 return -ENOMEM;
5208
5209 list = (struct brcmf_chanspec_list *)pbuf;
5210
5211 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5212 BRCMF_DCMD_MEDLEN);
5213 if (err) {
5214 brcmf_err("get chanspecs error (%d)\n", err);
5215 goto exit;
5216 }
5217
5218 __wl_band_2ghz.n_channels = 0;
5219 __wl_band_5ghz_a.n_channels = 0;
5220
5221 total = le32_to_cpu(list->count);
5222 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005223 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5224 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005225
Franky Lin83cf17a2013-04-11 13:28:50 +02005226 if (ch.band == BRCMU_CHAN_BAND_2G) {
Hante Meulemand48200b2013-04-03 12:40:29 +02005227 band_chan_arr = __wl_2ghz_channels;
5228 array_size = ARRAY_SIZE(__wl_2ghz_channels);
5229 n_cnt = &__wl_band_2ghz.n_channels;
5230 band = IEEE80211_BAND_2GHZ;
Franky Lin83cf17a2013-04-11 13:28:50 +02005231 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Hante Meulemand48200b2013-04-03 12:40:29 +02005232 band_chan_arr = __wl_5ghz_a_channels;
5233 array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
5234 n_cnt = &__wl_band_5ghz_a.n_channels;
5235 band = IEEE80211_BAND_5GHZ;
Hante Meulemand48200b2013-04-03 12:40:29 +02005236 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005237 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005238 continue;
5239 }
Arend van Spriel2375d972014-01-06 12:40:41 +01005240 if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) &&
5241 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005242 continue;
Arend van Sprielee942ec2014-05-12 10:47:38 +02005243 if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) &&
5244 ch.bw == BRCMU_CHAN_BW_80)
5245 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005246 update = false;
5247 for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005248 if (band_chan_arr[j].hw_value == ch.chnum) {
Hante Meulemand48200b2013-04-03 12:40:29 +02005249 update = true;
5250 break;
5251 }
5252 }
5253 if (update)
5254 index = j;
5255 else
5256 index = *n_cnt;
5257 if (index < array_size) {
5258 band_chan_arr[index].center_freq =
Franky Lin83cf17a2013-04-11 13:28:50 +02005259 ieee80211_channel_to_frequency(ch.chnum, band);
5260 band_chan_arr[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005261
Arend van Sprielee942ec2014-05-12 10:47:38 +02005262 /* assuming the chanspecs order is HT20,
5263 * HT40 upper, HT40 lower, and VHT80.
5264 */
5265 if (ch.bw == BRCMU_CHAN_BW_80) {
5266 band_chan_arr[index].flags &=
5267 ~IEEE80211_CHAN_NO_80MHZ;
5268 } else if (ch.bw == BRCMU_CHAN_BW_40) {
Hante Meulemand48200b2013-04-03 12:40:29 +02005269 ht40_flag = band_chan_arr[index].flags &
5270 IEEE80211_CHAN_NO_HT40;
Franky Lin83cf17a2013-04-11 13:28:50 +02005271 if (ch.sb == BRCMU_CHAN_SB_U) {
Hante Meulemand48200b2013-04-03 12:40:29 +02005272 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5273 band_chan_arr[index].flags &=
5274 ~IEEE80211_CHAN_NO_HT40;
5275 band_chan_arr[index].flags |=
5276 IEEE80211_CHAN_NO_HT40PLUS;
5277 } else {
5278 /* It should be one of
5279 * IEEE80211_CHAN_NO_HT40 or
5280 * IEEE80211_CHAN_NO_HT40PLUS
5281 */
5282 band_chan_arr[index].flags &=
5283 ~IEEE80211_CHAN_NO_HT40;
5284 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5285 band_chan_arr[index].flags |=
5286 IEEE80211_CHAN_NO_HT40MINUS;
5287 }
5288 } else {
Arend van Sprielee942ec2014-05-12 10:47:38 +02005289 /* disable other bandwidths for now as mentioned
5290 * order assure they are enabled for subsequent
5291 * chanspecs.
5292 */
Hante Meulemand48200b2013-04-03 12:40:29 +02005293 band_chan_arr[index].flags =
Arend van Sprielee942ec2014-05-12 10:47:38 +02005294 IEEE80211_CHAN_NO_HT40 |
5295 IEEE80211_CHAN_NO_80MHZ;
Franky Lin83cf17a2013-04-11 13:28:50 +02005296 ch.bw = BRCMU_CHAN_BW_20;
5297 cfg->d11inf.encchspec(&ch);
5298 channel = ch.chspec;
Hante Meulemand48200b2013-04-03 12:40:29 +02005299 err = brcmf_fil_bsscfg_int_get(ifp,
5300 "per_chan_info",
5301 &channel);
5302 if (!err) {
5303 if (channel & WL_CHAN_RADAR)
5304 band_chan_arr[index].flags |=
5305 (IEEE80211_CHAN_RADAR |
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +02005306 IEEE80211_CHAN_NO_IR);
Hante Meulemand48200b2013-04-03 12:40:29 +02005307 if (channel & WL_CHAN_PASSIVE)
5308 band_chan_arr[index].flags |=
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +02005309 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005310 }
5311 }
5312 if (!update)
5313 (*n_cnt)++;
5314 }
5315 }
5316exit:
5317 kfree(pbuf);
5318 return err;
5319}
5320
Arend van Spriel2375d972014-01-06 12:40:41 +01005321static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5322{
5323 u32 band, mimo_bwcap;
5324 int err;
5325
5326 band = WLC_BAND_2G;
5327 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5328 if (!err) {
5329 bw_cap[IEEE80211_BAND_2GHZ] = band;
5330 band = WLC_BAND_5G;
5331 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5332 if (!err) {
5333 bw_cap[IEEE80211_BAND_5GHZ] = band;
5334 return;
5335 }
5336 WARN_ON(1);
5337 return;
5338 }
5339 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5340 mimo_bwcap = 0;
5341 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5342 if (err)
5343 /* assume 20MHz if firmware does not give a clue */
5344 mimo_bwcap = WLC_N_BW_20ALL;
5345
5346 switch (mimo_bwcap) {
5347 case WLC_N_BW_40ALL:
5348 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5349 /* fall-thru */
5350 case WLC_N_BW_20IN2G_40IN5G:
5351 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5352 /* fall-thru */
5353 case WLC_N_BW_20ALL:
5354 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5355 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5356 break;
5357 default:
5358 brcmf_err("invalid mimo_bw_cap value\n");
5359 }
5360}
Hante Meulemand48200b2013-04-03 12:40:29 +02005361
Arend van Spriel18d6c532014-05-12 10:47:35 +02005362static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5363 u32 bw_cap[2], u32 nchain)
5364{
5365 band->ht_cap.ht_supported = true;
5366 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5367 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5368 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5369 }
5370 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5371 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5372 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5373 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5374 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5375 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5376}
5377
5378static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5379{
5380 u16 mcs_map;
5381 int i;
5382
5383 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5384 mcs_map = (mcs_map << 2) | supp;
5385
5386 return cpu_to_le16(mcs_map);
5387}
5388
5389static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
5390 u32 bw_cap[2], u32 nchain)
5391{
5392 __le16 mcs_map;
5393
5394 /* not allowed in 2.4G band */
5395 if (band->band == IEEE80211_BAND_2GHZ)
5396 return;
5397
5398 band->vht_cap.vht_supported = true;
5399 /* 80MHz is mandatory */
5400 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5401 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5402 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5403 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5404 }
5405 /* all support 256-QAM */
5406 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5407 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5408 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
5409}
5410
Hante Meulemand48200b2013-04-03 12:40:29 +02005411static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005412{
Arend van Sprielac24be62012-10-22 10:36:23 -07005413 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005414 struct wiphy *wiphy;
5415 s32 phy_list;
Hante Meulemand48200b2013-04-03 12:40:29 +02005416 u32 band_list[3];
Arend van Spriel18d6c532014-05-12 10:47:35 +02005417 u32 nmode = 0;
5418 u32 vhtmode = 0;
Arend van Spriel2375d972014-01-06 12:40:41 +01005419 u32 bw_cap[2] = { 0, 0 };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005420 u32 rxchain;
5421 u32 nchain;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005422 s8 phy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005423 s32 err;
5424 u32 nband;
5425 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005426 struct ieee80211_supported_band *bands[2] = { NULL, NULL };
5427 struct ieee80211_supported_band *band;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005428
Hante Meulemanb87e2c42012-11-14 18:46:23 -08005429 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005430 &phy_list, sizeof(phy_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005431 if (err) {
Hante Meulemand48200b2013-04-03 12:40:29 +02005432 brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005433 return err;
5434 }
5435
Hante Meuleman3ba81372012-09-19 22:21:13 +02005436 phy = ((char *)&phy_list)[0];
Hante Meulemand48200b2013-04-03 12:40:29 +02005437 brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy);
5438
5439
5440 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST,
5441 &band_list, sizeof(band_list));
5442 if (err) {
5443 brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err);
5444 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005445 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005446 brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
5447 band_list[0], band_list[1], band_list[2]);
5448
Arend van Spriel18d6c532014-05-12 10:47:35 +02005449 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005450 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5451 if (err) {
5452 brcmf_err("nmode error (%d)\n", err);
5453 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005454 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005455 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005456 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5457 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5458 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005459
Daniel Kim4aca7a12014-02-25 20:30:36 +01005460 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5461 if (err) {
5462 brcmf_err("rxchain error (%d)\n", err);
5463 nchain = 1;
5464 } else {
5465 for (nchain = 0; rxchain; nchain++)
5466 rxchain = rxchain & (rxchain - 1);
5467 }
5468 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5469
Hante Meulemand48200b2013-04-03 12:40:29 +02005470 err = brcmf_construct_reginfo(cfg, bw_cap);
5471 if (err) {
5472 brcmf_err("brcmf_construct_reginfo failed (%d)\n", err);
5473 return err;
5474 }
5475
5476 nband = band_list[0];
Hante Meulemand48200b2013-04-03 12:40:29 +02005477
5478 for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) {
Arend van Spriel2375d972014-01-06 12:40:41 +01005479 band = NULL;
Hante Meulemand48200b2013-04-03 12:40:29 +02005480 if ((band_list[i] == WLC_BAND_5G) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005481 (__wl_band_5ghz_a.n_channels > 0))
5482 band = &__wl_band_5ghz_a;
5483 else if ((band_list[i] == WLC_BAND_2G) &&
5484 (__wl_band_2ghz.n_channels > 0))
5485 band = &__wl_band_2ghz;
5486 else
5487 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005488
Arend van Spriel18d6c532014-05-12 10:47:35 +02005489 if (nmode)
5490 brcmf_update_ht_cap(band, bw_cap, nchain);
5491 if (vhtmode)
5492 brcmf_update_vht_cap(band, bw_cap, nchain);
Arend van Spriel2375d972014-01-06 12:40:41 +01005493 bands[band->band] = band;
Hante Meulemand48200b2013-04-03 12:40:29 +02005494 }
5495
5496 wiphy = cfg_to_wiphy(cfg);
5497 wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ];
5498 wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ];
5499 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005500
5501 return err;
5502}
5503
Hante Meulemand48200b2013-04-03 12:40:29 +02005504
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005505static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005506{
Hante Meulemand48200b2013-04-03 12:40:29 +02005507 return brcmf_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005508}
5509
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005510static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005511{
5512 struct net_device *ndev;
5513 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005514 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005515 s32 power_mode;
5516 s32 err = 0;
5517
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005518 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005519 return err;
5520
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005521 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005522 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01005523 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005524
Hante Meuleman40a23292013-01-02 15:22:51 +01005525 /* make sure RF is ready for work */
5526 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
5527
5528 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
5529 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005530
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005531 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01005532 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005533 if (err)
5534 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005535 brcmf_dbg(INFO, "power save set to %s\n",
5536 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005537
Hante Meuleman68ca3952014-02-25 20:30:26 +01005538 err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005539 if (err)
5540 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005541 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5542 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01005543 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005544 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005545 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005546 if (err)
5547 goto default_conf_out;
5548
Hante Meulemanb3657452013-05-27 21:09:53 +02005549 brcmf_configure_arp_offload(ifp, true);
5550
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005551 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01005552default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005553
5554 return err;
5555
5556}
5557
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005558static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005559{
Arend van Sprielc1179032012-10-22 13:55:33 -07005560 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005561
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005562 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005563}
5564
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005565static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005566{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005567 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07005568
Arend van Spriel5b435de2011-10-05 13:19:03 +02005569 /*
5570 * While going down, if associated with AP disassociate
5571 * from AP to save power
5572 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005573 if (check_vif_up(ifp->vif)) {
5574 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005575
5576 /* Make sure WPA_Supplicant receives all the event
5577 generated due to DISASSOC call to the fw to keep
5578 the state fw and WPA_Supplicant state consistent
5579 */
5580 brcmf_delay(500);
5581 }
5582
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005583 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07005584 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005585
Arend van Spriel5b435de2011-10-05 13:19:03 +02005586 return 0;
5587}
5588
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005589s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005590{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005591 struct brcmf_if *ifp = netdev_priv(ndev);
5592 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005593 s32 err = 0;
5594
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005595 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005596 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005597 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005598
5599 return err;
5600}
5601
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005602s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005603{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005604 struct brcmf_if *ifp = netdev_priv(ndev);
5605 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005606 s32 err = 0;
5607
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005608 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005609 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005610 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005611
5612 return err;
5613}
5614
Arend van Spriela7965fb2013-04-11 17:08:37 +02005615enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
5616{
5617 struct wireless_dev *wdev = &ifp->vif->wdev;
5618
5619 return wdev->iftype;
5620}
5621
Arend van Spriel9f440b72013-02-08 15:53:36 +01005622u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
5623{
5624 struct brcmf_cfg80211_vif *vif;
5625 bool result = 0;
5626
5627 list_for_each_entry(vif, &cfg->vif_list, list) {
5628 if (test_bit(state, &vif->sme_state))
5629 result++;
5630 }
5631 return result;
5632}
Arend van Sprield3c0b632013-02-08 15:53:37 +01005633
5634static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
5635 u8 action)
5636{
5637 u8 evt_action;
5638
5639 mutex_lock(&event->vif_event_lock);
5640 evt_action = event->action;
5641 mutex_unlock(&event->vif_event_lock);
5642 return evt_action == action;
5643}
5644
5645void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
5646 struct brcmf_cfg80211_vif *vif)
5647{
5648 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5649
5650 mutex_lock(&event->vif_event_lock);
5651 event->vif = vif;
5652 event->action = 0;
5653 mutex_unlock(&event->vif_event_lock);
5654}
5655
5656bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
5657{
5658 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5659 bool armed;
5660
5661 mutex_lock(&event->vif_event_lock);
5662 armed = event->vif != NULL;
5663 mutex_unlock(&event->vif_event_lock);
5664
5665 return armed;
5666}
5667int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
5668 u8 action, ulong timeout)
5669{
5670 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5671
5672 return wait_event_timeout(event->vif_wq,
5673 vif_event_equals(event, action), timeout);
5674}
5675