blob: beba7f69577b9708614837bf00fa972a7795e347 [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>
Arend van Spriel5b435de2011-10-05 13:19:03 +020021#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020022#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023
24#include <brcmu_utils.h>
25#include <defs.h>
26#include <brcmu_wifi.h>
27#include "dhd.h"
Arend van Spriel16886732012-12-05 15:26:04 +010028#include "dhd_dbg.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020029#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010030#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010031#include "p2p.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020032#include "wl_cfg80211.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070033#include "fwil.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020034
Arend van Spriele5806072012-09-19 22:21:08 +020035#define BRCMF_SCAN_IE_LEN_MAX 2048
36#define BRCMF_PNO_VERSION 2
37#define BRCMF_PNO_TIME 30
38#define BRCMF_PNO_REPEAT 4
39#define BRCMF_PNO_FREQ_EXPO_MAX 3
40#define BRCMF_PNO_MAX_PFN_COUNT 16
41#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
42#define BRCMF_PNO_HIDDEN_BIT 2
43#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
44#define BRCMF_PNO_SCAN_COMPLETE 1
45#define BRCMF_PNO_SCAN_INCOMPLETE 0
46
Arend van Spriel9f440b72013-02-08 15:53:36 +010047#define BRCMF_IFACE_MAX_CNT 3
Arend van Spriel3eacf862012-10-22 13:55:30 -070048
Hante Meuleman1a873342012-09-27 14:17:54 +020049#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
50#define WPA_OUI_TYPE 1
51#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
52#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010053#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020054
55#define VS_IE_FIXED_HDR_LEN 6
56#define WPA_IE_VERSION_LEN 2
57#define WPA_IE_MIN_OUI_LEN 4
58#define WPA_IE_SUITE_COUNT_LEN 2
59
60#define WPA_CIPHER_NONE 0 /* None */
61#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
62#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
63#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
64#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
65
66#define RSN_AKM_NONE 0 /* None (IBSS) */
67#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
68#define RSN_AKM_PSK 2 /* Pre-shared Key */
69#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
70#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
71
72#define VNDR_IE_CMD_LEN 4 /* length of the set command
73 * string :"add", "del" (+ NUL)
74 */
75#define VNDR_IE_COUNT_OFFSET 4
76#define VNDR_IE_PKTFLAG_OFFSET 8
77#define VNDR_IE_VSIE_OFFSET 12
78#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010079#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020080
81#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
82#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020083
Hante Meuleman89286dc2013-02-08 15:53:46 +010084#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
85#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
86#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
87
Arend van Spriel5b435de2011-10-05 13:19:03 +020088#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
89 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
90
Arend van Sprielce81e312012-10-22 13:55:37 -070091static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +020092{
Arend van Sprielc1179032012-10-22 13:55:33 -070093 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +010094 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
95 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +020096 return false;
97 }
98 return true;
99}
100
101#define CHAN2G(_channel, _freq, _flags) { \
102 .band = IEEE80211_BAND_2GHZ, \
103 .center_freq = (_freq), \
104 .hw_value = (_channel), \
105 .flags = (_flags), \
106 .max_antenna_gain = 0, \
107 .max_power = 30, \
108}
109
110#define CHAN5G(_channel, _flags) { \
111 .band = IEEE80211_BAND_5GHZ, \
112 .center_freq = 5000 + (5 * (_channel)), \
113 .hw_value = (_channel), \
114 .flags = (_flags), \
115 .max_antenna_gain = 0, \
116 .max_power = 30, \
117}
118
119#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
120#define RATETAB_ENT(_rateid, _flags) \
121 { \
122 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
123 .hw_value = (_rateid), \
124 .flags = (_flags), \
125 }
126
127static struct ieee80211_rate __wl_rates[] = {
128 RATETAB_ENT(BRCM_RATE_1M, 0),
129 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
130 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
131 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
132 RATETAB_ENT(BRCM_RATE_6M, 0),
133 RATETAB_ENT(BRCM_RATE_9M, 0),
134 RATETAB_ENT(BRCM_RATE_12M, 0),
135 RATETAB_ENT(BRCM_RATE_18M, 0),
136 RATETAB_ENT(BRCM_RATE_24M, 0),
137 RATETAB_ENT(BRCM_RATE_36M, 0),
138 RATETAB_ENT(BRCM_RATE_48M, 0),
139 RATETAB_ENT(BRCM_RATE_54M, 0),
140};
141
142#define wl_a_rates (__wl_rates + 4)
143#define wl_a_rates_size 8
144#define wl_g_rates (__wl_rates + 0)
145#define wl_g_rates_size 12
146
147static struct ieee80211_channel __wl_2ghz_channels[] = {
148 CHAN2G(1, 2412, 0),
149 CHAN2G(2, 2417, 0),
150 CHAN2G(3, 2422, 0),
151 CHAN2G(4, 2427, 0),
152 CHAN2G(5, 2432, 0),
153 CHAN2G(6, 2437, 0),
154 CHAN2G(7, 2442, 0),
155 CHAN2G(8, 2447, 0),
156 CHAN2G(9, 2452, 0),
157 CHAN2G(10, 2457, 0),
158 CHAN2G(11, 2462, 0),
159 CHAN2G(12, 2467, 0),
160 CHAN2G(13, 2472, 0),
161 CHAN2G(14, 2484, 0),
162};
163
164static struct ieee80211_channel __wl_5ghz_a_channels[] = {
165 CHAN5G(34, 0), CHAN5G(36, 0),
166 CHAN5G(38, 0), CHAN5G(40, 0),
167 CHAN5G(42, 0), CHAN5G(44, 0),
168 CHAN5G(46, 0), CHAN5G(48, 0),
169 CHAN5G(52, 0), CHAN5G(56, 0),
170 CHAN5G(60, 0), CHAN5G(64, 0),
171 CHAN5G(100, 0), CHAN5G(104, 0),
172 CHAN5G(108, 0), CHAN5G(112, 0),
173 CHAN5G(116, 0), CHAN5G(120, 0),
174 CHAN5G(124, 0), CHAN5G(128, 0),
175 CHAN5G(132, 0), CHAN5G(136, 0),
176 CHAN5G(140, 0), CHAN5G(149, 0),
177 CHAN5G(153, 0), CHAN5G(157, 0),
178 CHAN5G(161, 0), CHAN5G(165, 0),
179 CHAN5G(184, 0), CHAN5G(188, 0),
180 CHAN5G(192, 0), CHAN5G(196, 0),
181 CHAN5G(200, 0), CHAN5G(204, 0),
182 CHAN5G(208, 0), CHAN5G(212, 0),
183 CHAN5G(216, 0),
184};
185
Arend van Spriel5b435de2011-10-05 13:19:03 +0200186static struct ieee80211_supported_band __wl_band_2ghz = {
187 .band = IEEE80211_BAND_2GHZ,
188 .channels = __wl_2ghz_channels,
189 .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
190 .bitrates = wl_g_rates,
191 .n_bitrates = wl_g_rates_size,
192};
193
194static struct ieee80211_supported_band __wl_band_5ghz_a = {
195 .band = IEEE80211_BAND_5GHZ,
196 .channels = __wl_5ghz_a_channels,
197 .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
198 .bitrates = wl_a_rates,
199 .n_bitrates = wl_a_rates_size,
200};
201
Hante Meulemand48200b2013-04-03 12:40:29 +0200202/* This is to override regulatory domains defined in cfg80211 module (reg.c)
203 * By default world regulatory domain defined in reg.c puts the flags
204 * NL80211_RRF_PASSIVE_SCAN and NL80211_RRF_NO_IBSS for 5GHz channels (for
205 * 36..48 and 149..165). With respect to these flags, wpa_supplicant doesn't
206 * start p2p operations on 5GHz channels. All the changes in world regulatory
207 * domain are to be done here.
208 */
209static const struct ieee80211_regdomain brcmf_regdom = {
210 .n_reg_rules = 4,
211 .alpha2 = "99",
212 .reg_rules = {
213 /* IEEE 802.11b/g, channels 1..11 */
214 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
215 /* If any */
216 /* IEEE 802.11 channel 14 - Only JP enables
217 * this and for 802.11b only
218 */
219 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
220 /* IEEE 802.11a, channel 36..64 */
221 REG_RULE(5150-10, 5350+10, 40, 6, 20, 0),
222 /* IEEE 802.11a, channel 100..165 */
223 REG_RULE(5470-10, 5850+10, 40, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200224};
225
226static const u32 __wl_cipher_suites[] = {
227 WLAN_CIPHER_SUITE_WEP40,
228 WLAN_CIPHER_SUITE_WEP104,
229 WLAN_CIPHER_SUITE_TKIP,
230 WLAN_CIPHER_SUITE_CCMP,
231 WLAN_CIPHER_SUITE_AES_CMAC,
232};
233
Hante Meuleman1a873342012-09-27 14:17:54 +0200234/* Vendor specific ie. id = 221, oui and type defines exact ie */
235struct brcmf_vs_tlv {
236 u8 id;
237 u8 len;
238 u8 oui[3];
239 u8 oui_type;
240};
241
242struct parsed_vndr_ie_info {
243 u8 *ie_ptr;
244 u32 ie_len; /* total length including id & length field */
245 struct brcmf_vs_tlv vndrie;
246};
247
248struct parsed_vndr_ies {
249 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100250 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200251};
252
Alwin Beukersef6ac172011-10-12 20:51:26 +0200253/* Quarter dBm units to mW
254 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
255 * Table is offset so the last entry is largest mW value that fits in
256 * a u16.
257 */
258
259#define QDBM_OFFSET 153 /* Offset for first entry */
260#define QDBM_TABLE_LEN 40 /* Table size */
261
262/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
263 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
264 */
265#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
266
267/* Largest mW value that will round down to the last table entry,
268 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
269 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
270 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
271 */
272#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
273
274static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
275/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
276/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
277/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
278/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
279/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
280/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
281};
282
283static u16 brcmf_qdbm_to_mw(u8 qdbm)
284{
285 uint factor = 1;
286 int idx = qdbm - QDBM_OFFSET;
287
288 if (idx >= QDBM_TABLE_LEN)
289 /* clamp to max u16 mW value */
290 return 0xFFFF;
291
292 /* scale the qdBm index up to the range of the table 0-40
293 * where an offset of 40 qdBm equals a factor of 10 mW.
294 */
295 while (idx < 0) {
296 idx += 40;
297 factor *= 10;
298 }
299
300 /* return the mW value scaled down to the correct factor of 10,
301 * adding in factor/2 to get proper rounding.
302 */
303 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
304}
305
306static u8 brcmf_mw_to_qdbm(u16 mw)
307{
308 u8 qdbm;
309 int offset;
310 uint mw_uint = mw;
311 uint boundary;
312
313 /* handle boundary case */
314 if (mw_uint <= 1)
315 return 0;
316
317 offset = QDBM_OFFSET;
318
319 /* move mw into the range of the table */
320 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
321 mw_uint *= 10;
322 offset -= 40;
323 }
324
325 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
326 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
327 nqdBm_to_mW_map[qdbm]) / 2;
328 if (mw_uint < boundary)
329 break;
330 }
331
332 qdbm += (u8) offset;
333
334 return qdbm;
335}
336
Arend van Spriel9f440b72013-02-08 15:53:36 +0100337u16 channel_to_chanspec(struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700338{
339 u16 chanspec;
340
341 chanspec = ieee80211_frequency_to_channel(ch->center_freq);
342 chanspec &= WL_CHANSPEC_CHAN_MASK;
343
344 if (ch->band == IEEE80211_BAND_2GHZ)
345 chanspec |= WL_CHANSPEC_BAND_2G;
346 else
347 chanspec |= WL_CHANSPEC_BAND_5G;
348
Hante Meuleman17012612013-02-06 18:40:44 +0100349 chanspec |= WL_CHANSPEC_BW_20;
350 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
351
Arend van Spriel6e186162012-10-22 10:36:22 -0700352 return chanspec;
353}
354
Hante Meuleman89286dc2013-02-08 15:53:46 +0100355/* Traverse a string of 1-byte tag/1-byte length/variable-length value
356 * triples, returning a pointer to the substring whose first element
357 * matches tag
358 */
359struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
360{
361 struct brcmf_tlv *elt;
362 int totlen;
363
364 elt = (struct brcmf_tlv *)buf;
365 totlen = buflen;
366
367 /* find tagged parameter */
368 while (totlen >= TLV_HDR_LEN) {
369 int len = elt->len;
370
371 /* validate remaining totlen */
372 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
373 return elt;
374
375 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
376 totlen -= (len + TLV_HDR_LEN);
377 }
378
379 return NULL;
380}
381
382/* Is any of the tlvs the expected entry? If
383 * not update the tlvs buffer pointer/length.
384 */
385static bool
386brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
387 u8 *oui, u32 oui_len, u8 type)
388{
389 /* If the contents match the OUI and the type */
390 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
391 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
392 type == ie[TLV_BODY_OFF + oui_len]) {
393 return true;
394 }
395
396 if (tlvs == NULL)
397 return false;
398 /* point to the next ie */
399 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
400 /* calculate the length of the rest of the buffer */
401 *tlvs_len -= (int)(ie - *tlvs);
402 /* update the pointer to the start of the buffer */
403 *tlvs = ie;
404
405 return false;
406}
407
408static struct brcmf_vs_tlv *
409brcmf_find_wpaie(u8 *parse, u32 len)
410{
411 struct brcmf_tlv *ie;
412
413 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
414 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
415 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
416 return (struct brcmf_vs_tlv *)ie;
417 }
418 return NULL;
419}
420
421static struct brcmf_vs_tlv *
422brcmf_find_wpsie(u8 *parse, u32 len)
423{
424 struct brcmf_tlv *ie;
425
426 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
427 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
428 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
429 return (struct brcmf_vs_tlv *)ie;
430 }
431 return NULL;
432}
433
434
Arend van Spriel5b435de2011-10-05 13:19:03 +0200435static void convert_key_from_CPU(struct brcmf_wsec_key *key,
436 struct brcmf_wsec_key_le *key_le)
437{
438 key_le->index = cpu_to_le32(key->index);
439 key_le->len = cpu_to_le32(key->len);
440 key_le->algo = cpu_to_le32(key->algo);
441 key_le->flags = cpu_to_le32(key->flags);
442 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
443 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
444 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
445 memcpy(key_le->data, key->data, sizeof(key->data));
446 memcpy(key_le->ea, key->ea, sizeof(key->ea));
447}
448
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200449static int
Arend van Spriel2eaba7e2012-10-22 10:36:26 -0700450send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200451{
452 int err;
453 struct brcmf_wsec_key_le key_le;
454
455 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200456
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700457 brcmf_netdev_wait_pend8021x(ndev);
458
Arend van Sprielac24be62012-10-22 10:36:23 -0700459 err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700460 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200461
Arend van Spriel5b435de2011-10-05 13:19:03 +0200462 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100463 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200464 return err;
465}
466
Arend van Spriel9f440b72013-02-08 15:53:36 +0100467static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
468 const char *name,
469 enum nl80211_iftype type,
470 u32 *flags,
471 struct vif_params *params)
472{
473 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
474 switch (type) {
475 case NL80211_IFTYPE_ADHOC:
476 case NL80211_IFTYPE_STATION:
477 case NL80211_IFTYPE_AP:
478 case NL80211_IFTYPE_AP_VLAN:
479 case NL80211_IFTYPE_WDS:
480 case NL80211_IFTYPE_MONITOR:
481 case NL80211_IFTYPE_MESH_POINT:
482 return ERR_PTR(-EOPNOTSUPP);
483 case NL80211_IFTYPE_P2P_CLIENT:
484 case NL80211_IFTYPE_P2P_GO:
485 return brcmf_p2p_add_vif(wiphy, name, type, flags, params);
486 case NL80211_IFTYPE_UNSPECIFIED:
487 case NL80211_IFTYPE_P2P_DEVICE:
488 default:
489 return ERR_PTR(-EINVAL);
490 }
491}
492
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100493void brcmf_set_mpc(struct net_device *ndev, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100494{
495 struct brcmf_if *ifp = netdev_priv(ndev);
496 s32 err = 0;
497
498 if (check_vif_up(ifp->vif)) {
499 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
500 if (err) {
501 brcmf_err("fail to set mpc\n");
502 return;
503 }
504 brcmf_dbg(INFO, "MPC : %d\n", mpc);
505 }
506}
507
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100508s32
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100509brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
510 struct net_device *ndev,
511 bool aborted, bool fw_abort)
512{
513 struct brcmf_scan_params_le params_le;
514 struct cfg80211_scan_request *scan_request;
515 s32 err = 0;
516
517 brcmf_dbg(SCAN, "Enter\n");
518
519 /* clear scan request, because the FW abort can cause a second call */
520 /* to this functon and might cause a double cfg80211_scan_done */
521 scan_request = cfg->scan_request;
522 cfg->scan_request = NULL;
523
524 if (timer_pending(&cfg->escan_timeout))
525 del_timer_sync(&cfg->escan_timeout);
526
527 if (fw_abort) {
528 /* Do a scan abort to stop the driver's scan engine */
529 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
530 memset(&params_le, 0, sizeof(params_le));
531 memset(params_le.bssid, 0xFF, ETH_ALEN);
532 params_le.bss_type = DOT11_BSSTYPE_ANY;
533 params_le.scan_type = 0;
534 params_le.channel_num = cpu_to_le32(1);
535 params_le.nprobes = cpu_to_le32(1);
536 params_le.active_time = cpu_to_le32(-1);
537 params_le.passive_time = cpu_to_le32(-1);
538 params_le.home_time = cpu_to_le32(-1);
539 /* Scan is aborted by setting channel_list[0] to -1 */
540 params_le.channel_list[0] = cpu_to_le16(-1);
541 /* E-Scan (or anyother type) can be aborted by SCAN */
542 err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN,
543 &params_le, sizeof(params_le));
544 if (err)
545 brcmf_err("Scan abort failed\n");
546 }
547 /*
548 * e-scan can be initiated by scheduled scan
549 * which takes precedence.
550 */
551 if (cfg->sched_escan) {
552 brcmf_dbg(SCAN, "scheduled scan completed\n");
553 cfg->sched_escan = false;
554 if (!aborted)
555 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
556 brcmf_set_mpc(ndev, 1);
557 } else if (scan_request) {
558 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
559 aborted ? "Aborted" : "Done");
560 cfg80211_scan_done(scan_request, aborted);
561 brcmf_set_mpc(ndev, 1);
562 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100563 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
564 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100565
566 return err;
567}
568
Arend van Spriel9f440b72013-02-08 15:53:36 +0100569static
570int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
571{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100572 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
573 struct net_device *ndev = wdev->netdev;
574
575 /* vif event pending in firmware */
576 if (brcmf_cfg80211_vif_event_armed(cfg))
577 return -EBUSY;
578
579 if (ndev) {
580 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
581 cfg->escan_info.ndev == ndev)
582 brcmf_notify_escan_complete(cfg, ndev, true,
583 true);
584
585 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
586 }
587
Arend van Spriel9f440b72013-02-08 15:53:36 +0100588 switch (wdev->iftype) {
589 case NL80211_IFTYPE_ADHOC:
590 case NL80211_IFTYPE_STATION:
591 case NL80211_IFTYPE_AP:
592 case NL80211_IFTYPE_AP_VLAN:
593 case NL80211_IFTYPE_WDS:
594 case NL80211_IFTYPE_MONITOR:
595 case NL80211_IFTYPE_MESH_POINT:
596 return -EOPNOTSUPP;
597 case NL80211_IFTYPE_P2P_CLIENT:
598 case NL80211_IFTYPE_P2P_GO:
599 return brcmf_p2p_del_vif(wiphy, wdev);
600 case NL80211_IFTYPE_UNSPECIFIED:
601 case NL80211_IFTYPE_P2P_DEVICE:
602 default:
603 return -EINVAL;
604 }
605 return -EOPNOTSUPP;
606}
607
Arend van Spriel5b435de2011-10-05 13:19:03 +0200608static s32
609brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
610 enum nl80211_iftype type, u32 *flags,
611 struct vif_params *params)
612{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100613 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700614 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100615 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200616 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200617 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200618 s32 err = 0;
619
Arend van Sprield96b8012012-12-05 15:26:02 +0100620 brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200621
622 switch (type) {
623 case NL80211_IFTYPE_MONITOR:
624 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100625 brcmf_err("type (%d) : currently we do not support this type\n",
626 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200627 return -EOPNOTSUPP;
628 case NL80211_IFTYPE_ADHOC:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100629 vif->mode = WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200630 infra = 0;
631 break;
632 case NL80211_IFTYPE_STATION:
Hante Meuleman1bc7c652013-02-08 15:53:56 +0100633 /* Ignore change for p2p IF. Unclear why supplicant does this */
634 if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
635 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
636 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
637 /* WAR: It is unexpected to get a change of VIF for P2P
638 * IF, but it happens. The request can not be handled
639 * but returning EPERM causes a crash. Returning 0
640 * without setting ieee80211_ptr->iftype causes trace
641 * (WARN_ON) but it works with wpa_supplicant
642 */
643 return 0;
644 }
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100645 vif->mode = WL_MODE_BSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200646 infra = 1;
647 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200648 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100649 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100650 vif->mode = WL_MODE_AP;
Hante Meuleman1a873342012-09-27 14:17:54 +0200651 ap = 1;
652 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200653 default:
654 err = -EINVAL;
655 goto done;
656 }
657
Hante Meuleman1a873342012-09-27 14:17:54 +0200658 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100659 if (type == NL80211_IFTYPE_P2P_GO) {
660 brcmf_dbg(INFO, "IF Type = P2P GO\n");
661 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
662 }
663 if (!err) {
664 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
665 brcmf_dbg(INFO, "IF Type = AP\n");
666 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200667 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100668 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200669 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100670 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200671 err = -EAGAIN;
672 goto done;
673 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100674 brcmf_dbg(INFO, "IF Type = %s\n", (vif->mode == WL_MODE_IBSS) ?
675 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200676 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200677 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200678
679done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100680 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200681
682 return err;
683}
684
Hante Meulemane756af52012-09-11 21:18:52 +0200685static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
686 struct cfg80211_scan_request *request)
687{
688 u32 n_ssids;
689 u32 n_channels;
690 s32 i;
691 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200692 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200693 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200694 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200695
Arend van Sprielba40d162012-10-22 13:55:38 -0700696 memset(params_le->bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200697 params_le->bss_type = DOT11_BSSTYPE_ANY;
698 params_le->scan_type = 0;
699 params_le->channel_num = 0;
700 params_le->nprobes = cpu_to_le32(-1);
701 params_le->active_time = cpu_to_le32(-1);
702 params_le->passive_time = cpu_to_le32(-1);
703 params_le->home_time = cpu_to_le32(-1);
704 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
705
706 /* if request is null exit so it will be all channel broadcast scan */
707 if (!request)
708 return;
709
710 n_ssids = request->n_ssids;
711 n_channels = request->n_channels;
712 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100713 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
714 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200715 if (n_channels > 0) {
716 for (i = 0; i < n_channels; i++) {
Arend van Spriel6e186162012-10-22 10:36:22 -0700717 chanspec = channel_to_chanspec(request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100718 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
719 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200720 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200721 }
722 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100723 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200724 }
725 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100726 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200727 if (n_ssids > 0) {
728 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
729 n_channels * sizeof(u16);
730 offset = roundup(offset, sizeof(u32));
731 ptr = (char *)params_le + offset;
732 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200733 memset(&ssid_le, 0, sizeof(ssid_le));
734 ssid_le.SSID_len =
735 cpu_to_le32(request->ssids[i].ssid_len);
736 memcpy(ssid_le.SSID, request->ssids[i].ssid,
737 request->ssids[i].ssid_len);
738 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100739 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200740 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100741 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
742 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200743 memcpy(ptr, &ssid_le, sizeof(ssid_le));
744 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200745 }
746 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100747 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200748 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100749 brcmf_dbg(SCAN, "SSID %s len=%d\n",
750 params_le->ssid_le.SSID,
751 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200752 params_le->ssid_le.SSID_len =
753 cpu_to_le32(request->ssids->ssid_len);
754 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
755 request->ssids->ssid_len);
756 }
757 }
758 /* Adding mask to channel numbers */
759 params_le->channel_num =
760 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
761 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
762}
763
764static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200765brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
Hante Meulemane756af52012-09-11 21:18:52 +0200766 struct cfg80211_scan_request *request, u16 action)
767{
768 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
769 offsetof(struct brcmf_escan_params_le, params_le);
770 struct brcmf_escan_params_le *params;
771 s32 err = 0;
772
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100773 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200774
775 if (request != NULL) {
776 /* Allocate space for populating ssids in struct */
777 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
778
779 /* Allocate space for populating ssids in struct */
780 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
781 }
782
783 params = kzalloc(params_size, GFP_KERNEL);
784 if (!params) {
785 err = -ENOMEM;
786 goto exit;
787 }
788 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
789 brcmf_escan_prep(&params->params_le, request);
790 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
791 params->action = cpu_to_le16(action);
792 params->sync_id = cpu_to_le16(0x1234);
793
Arend van Sprielac24be62012-10-22 10:36:23 -0700794 err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan",
795 params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200796 if (err) {
797 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100798 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200799 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100800 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200801 }
802
803 kfree(params);
804exit:
805 return err;
806}
807
808static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200809brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Hante Meulemane756af52012-09-11 21:18:52 +0200810 struct net_device *ndev, struct cfg80211_scan_request *request)
811{
812 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700813 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200814 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100815 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +0200816
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100817 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriel9f440b72013-02-08 15:53:36 +0100818 escan->ndev = ndev;
819 escan->wiphy = wiphy;
820 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700821 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielac24be62012-10-22 10:36:23 -0700822 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700823 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200824 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100825 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200826 return err;
827 }
828 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200829 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +0200830 results->version = 0;
831 results->count = 0;
832 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
833
Arend van Spriel9f440b72013-02-08 15:53:36 +0100834 err = escan->run(cfg, ndev, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200835 if (err)
836 brcmf_set_mpc(ndev, 1);
837 return err;
838}
839
840static s32
841brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
842 struct cfg80211_scan_request *request,
843 struct cfg80211_ssid *this_ssid)
844{
Arend van Sprielc1179032012-10-22 13:55:33 -0700845 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200846 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Hante Meulemane756af52012-09-11 21:18:52 +0200847 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -0800848 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700849 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200850 bool escan_req;
851 bool spec_scan;
852 s32 err;
853 u32 SSID_len;
854
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100855 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200856
Arend van Sprielc1179032012-10-22 13:55:33 -0700857 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100858 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200859 return -EAGAIN;
860 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700861 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100862 brcmf_err("Scanning being aborted: status (%lu)\n",
863 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200864 return -EAGAIN;
865 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700866 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100867 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +0200868 return -EAGAIN;
869 }
870
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100871 /* If scan req comes for p2p0, send it over primary I/F */
872 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) {
873 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
874 ndev = ifp->ndev;
875 }
876
Hante Meulemane756af52012-09-11 21:18:52 +0200877 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200878 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +0200879 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
880
881 escan_req = false;
882 if (request) {
883 /* scan bss */
884 ssids = request->ssids;
885 escan_req = true;
886 } else {
887 /* scan in ibss */
888 /* we don't do escan in ibss */
889 ssids = this_ssid;
890 }
891
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200892 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -0700893 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200894 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +0100895 cfg->escan_info.run = brcmf_run_escan;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +0100896 err = brcmf_p2p_scan_prep(wiphy, request, ifp->vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100897 if (err)
898 goto scan_out;
899
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200900 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -0800901 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +0200902 goto scan_out;
903 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100904 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
905 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200906 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
907 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
908 sr->ssid_le.SSID_len = cpu_to_le32(0);
909 spec_scan = false;
910 if (SSID_len) {
911 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
912 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
913 spec_scan = true;
914 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100915 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200916
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700917 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -0700918 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700919 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200920 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100921 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200922 goto scan_out;
923 }
924 brcmf_set_mpc(ndev, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -0700925 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -0700926 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200927 if (err) {
928 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100929 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
930 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +0200931 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100932 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200933
934 brcmf_set_mpc(ndev, 1);
935 goto scan_out;
936 }
937 }
938
939 return 0;
940
941scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -0700942 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200943 if (timer_pending(&cfg->escan_timeout))
944 del_timer_sync(&cfg->escan_timeout);
945 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200946 return err;
947}
948
Arend van Spriel5b435de2011-10-05 13:19:03 +0200949static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700950brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200951{
Johannes Bergfd014282012-06-18 19:17:03 +0200952 struct net_device *ndev = request->wdev->netdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200953 s32 err = 0;
954
Arend van Sprield96b8012012-12-05 15:26:02 +0100955 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200956
Arend van Sprielce81e312012-10-22 13:55:37 -0700957 if (!check_vif_up(container_of(request->wdev,
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700958 struct brcmf_cfg80211_vif, wdev)))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200959 return -EIO;
960
Hante Meulemanf07998952012-11-05 16:22:13 -0800961 err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +0200962
Arend van Spriel5b435de2011-10-05 13:19:03 +0200963 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100964 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200965
Arend van Sprield96b8012012-12-05 15:26:02 +0100966 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200967 return err;
968}
969
970static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
971{
972 s32 err = 0;
973
Arend van Sprielac24be62012-10-22 10:36:23 -0700974 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
975 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200976 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100977 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200978
979 return err;
980}
981
982static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
983{
984 s32 err = 0;
985
Arend van Sprielac24be62012-10-22 10:36:23 -0700986 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
987 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200988 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100989 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200990
991 return err;
992}
993
994static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
995{
996 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -0800997 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200998
Arend van Sprielac24be62012-10-22 10:36:23 -0700999 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001000 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001001 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001002 return err;
1003 }
1004 return err;
1005}
1006
1007static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1008{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001009 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1010 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001011 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001012 s32 err = 0;
1013
Arend van Sprield96b8012012-12-05 15:26:02 +01001014 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001015 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001016 return -EIO;
1017
1018 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001019 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1020 cfg->conf->rts_threshold = wiphy->rts_threshold;
1021 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001022 if (!err)
1023 goto done;
1024 }
1025 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001026 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1027 cfg->conf->frag_threshold = wiphy->frag_threshold;
1028 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001029 if (!err)
1030 goto done;
1031 }
1032 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001033 && (cfg->conf->retry_long != wiphy->retry_long)) {
1034 cfg->conf->retry_long = wiphy->retry_long;
1035 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001036 if (!err)
1037 goto done;
1038 }
1039 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001040 && (cfg->conf->retry_short != wiphy->retry_short)) {
1041 cfg->conf->retry_short = wiphy->retry_short;
1042 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001043 if (!err)
1044 goto done;
1045 }
1046
1047done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001048 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001049 return err;
1050}
1051
Arend van Spriel5b435de2011-10-05 13:19:03 +02001052static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1053{
1054 memset(prof, 0, sizeof(*prof));
1055}
1056
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001057static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001058{
Arend van Spriel5b435de2011-10-05 13:19:03 +02001059 s32 err = 0;
1060
Arend van Sprield96b8012012-12-05 15:26:02 +01001061 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001062
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001063 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001064 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001065 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001066 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001067 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001068 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001069 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001070 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001071 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001072 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001073}
1074
1075static s32
1076brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1077 struct cfg80211_ibss_params *params)
1078{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001079 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001080 struct brcmf_if *ifp = netdev_priv(ndev);
1081 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001082 struct brcmf_join_params join_params;
1083 size_t join_params_size = 0;
1084 s32 err = 0;
1085 s32 wsec = 0;
1086 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001087 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001088
Arend van Sprield96b8012012-12-05 15:26:02 +01001089 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001090 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001091 return -EIO;
1092
1093 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001094 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001095 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001096 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001097 return -EOPNOTSUPP;
1098 }
1099
Arend van Sprielc1179032012-10-22 13:55:33 -07001100 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001101
1102 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001103 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001104 else
Arend van Spriel16886732012-12-05 15:26:04 +01001105 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001106
Johannes Berg683b6d32012-11-08 21:25:48 +01001107 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001108 brcmf_dbg(CONN, "channel: %d\n",
1109 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001110 else
Arend van Spriel16886732012-12-05 15:26:04 +01001111 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001112
1113 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001114 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001115 else
Arend van Spriel16886732012-12-05 15:26:04 +01001116 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001117
1118 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001119 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001120 else
Arend van Spriel16886732012-12-05 15:26:04 +01001121 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001122
1123 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001124 brcmf_dbg(CONN, "beacon interval: %d\n",
1125 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001126 else
Arend van Spriel16886732012-12-05 15:26:04 +01001127 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001128
1129 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001130 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001131 else
Arend van Spriel16886732012-12-05 15:26:04 +01001132 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001133
1134 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001135 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001136 else
Arend van Spriel16886732012-12-05 15:26:04 +01001137 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001138
1139 /* Configure Privacy for starter */
1140 if (params->privacy)
1141 wsec |= WEP_ENABLED;
1142
Arend van Sprielc1179032012-10-22 13:55:33 -07001143 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001144 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001145 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001146 goto done;
1147 }
1148
1149 /* Configure Beacon Interval for starter */
1150 if (params->beacon_interval)
1151 bcnprd = params->beacon_interval;
1152 else
1153 bcnprd = 100;
1154
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001155 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001156 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001157 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001158 goto done;
1159 }
1160
1161 /* Configure required join parameter */
1162 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1163
1164 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001165 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1166 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1167 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1168 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001169 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001170
1171 /* BSSID */
1172 if (params->bssid) {
1173 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1174 join_params_size = sizeof(join_params.ssid_le) +
1175 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001176 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001177 } else {
Arend van Sprielba40d162012-10-22 13:55:38 -07001178 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001179 memset(profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001180 }
1181
Arend van Spriel5b435de2011-10-05 13:19:03 +02001182 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001183 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184 u32 target_channel;
1185
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001186 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001187 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001188 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001189 if (params->channel_fixed) {
1190 /* adding chanspec */
Hante Meuleman17012612013-02-06 18:40:44 +01001191 chanspec = channel_to_chanspec(params->chandef.chan);
1192 join_params.params_le.chanspec_list[0] =
1193 cpu_to_le16(chanspec);
1194 join_params.params_le.chanspec_num = cpu_to_le32(1);
1195 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001196 }
1197
1198 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001199 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001200 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001201 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001202 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001203 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001204 goto done;
1205 }
1206 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001207 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001208
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001209 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001210
1211
Arend van Sprielc1179032012-10-22 13:55:33 -07001212 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001213 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001214 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001215 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001216 goto done;
1217 }
1218
1219done:
1220 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001221 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001222 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001223 return err;
1224}
1225
1226static s32
1227brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1228{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001229 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001230 s32 err = 0;
1231
Arend van Sprield96b8012012-12-05 15:26:02 +01001232 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001233 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001234 return -EIO;
1235
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001236 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001237
Arend van Sprield96b8012012-12-05 15:26:02 +01001238 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001239
1240 return err;
1241}
1242
1243static s32 brcmf_set_wpa_version(struct net_device *ndev,
1244 struct cfg80211_connect_params *sme)
1245{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001246 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001247 struct brcmf_cfg80211_security *sec;
1248 s32 val = 0;
1249 s32 err = 0;
1250
1251 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1252 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1253 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1254 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1255 else
1256 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001257 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001258 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001259 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001260 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001261 return err;
1262 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001263 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001264 sec->wpa_versions = sme->crypto.wpa_versions;
1265 return err;
1266}
1267
1268static s32 brcmf_set_auth_type(struct net_device *ndev,
1269 struct cfg80211_connect_params *sme)
1270{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001271 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001272 struct brcmf_cfg80211_security *sec;
1273 s32 val = 0;
1274 s32 err = 0;
1275
1276 switch (sme->auth_type) {
1277 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1278 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001279 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001280 break;
1281 case NL80211_AUTHTYPE_SHARED_KEY:
1282 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001283 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001284 break;
1285 case NL80211_AUTHTYPE_AUTOMATIC:
1286 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001287 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001288 break;
1289 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001290 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001291 default:
1292 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001293 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001294 break;
1295 }
1296
Hante Meuleman89286dc2013-02-08 15:53:46 +01001297 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001298 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001299 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001300 return err;
1301 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001302 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001303 sec->auth_type = sme->auth_type;
1304 return err;
1305}
1306
1307static s32
1308brcmf_set_set_cipher(struct net_device *ndev,
1309 struct cfg80211_connect_params *sme)
1310{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001311 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001312 struct brcmf_cfg80211_security *sec;
1313 s32 pval = 0;
1314 s32 gval = 0;
1315 s32 err = 0;
1316
1317 if (sme->crypto.n_ciphers_pairwise) {
1318 switch (sme->crypto.ciphers_pairwise[0]) {
1319 case WLAN_CIPHER_SUITE_WEP40:
1320 case WLAN_CIPHER_SUITE_WEP104:
1321 pval = WEP_ENABLED;
1322 break;
1323 case WLAN_CIPHER_SUITE_TKIP:
1324 pval = TKIP_ENABLED;
1325 break;
1326 case WLAN_CIPHER_SUITE_CCMP:
1327 pval = AES_ENABLED;
1328 break;
1329 case WLAN_CIPHER_SUITE_AES_CMAC:
1330 pval = AES_ENABLED;
1331 break;
1332 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001333 brcmf_err("invalid cipher pairwise (%d)\n",
1334 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001335 return -EINVAL;
1336 }
1337 }
1338 if (sme->crypto.cipher_group) {
1339 switch (sme->crypto.cipher_group) {
1340 case WLAN_CIPHER_SUITE_WEP40:
1341 case WLAN_CIPHER_SUITE_WEP104:
1342 gval = WEP_ENABLED;
1343 break;
1344 case WLAN_CIPHER_SUITE_TKIP:
1345 gval = TKIP_ENABLED;
1346 break;
1347 case WLAN_CIPHER_SUITE_CCMP:
1348 gval = AES_ENABLED;
1349 break;
1350 case WLAN_CIPHER_SUITE_AES_CMAC:
1351 gval = AES_ENABLED;
1352 break;
1353 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001354 brcmf_err("invalid cipher group (%d)\n",
1355 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001356 return -EINVAL;
1357 }
1358 }
1359
Arend van Spriel16886732012-12-05 15:26:04 +01001360 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001361 /* In case of privacy, but no security and WPS then simulate */
1362 /* setting AES. WPS-2.0 allows no security */
1363 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1364 sme->privacy)
1365 pval = AES_ENABLED;
1366 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", pval | gval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001367 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001368 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001369 return err;
1370 }
1371
Arend van Spriel06bb1232012-09-27 14:17:56 +02001372 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001373 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1374 sec->cipher_group = sme->crypto.cipher_group;
1375
1376 return err;
1377}
1378
1379static s32
1380brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1381{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001382 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001383 struct brcmf_cfg80211_security *sec;
1384 s32 val = 0;
1385 s32 err = 0;
1386
1387 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001388 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1389 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001390 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001391 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001392 return err;
1393 }
1394 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1395 switch (sme->crypto.akm_suites[0]) {
1396 case WLAN_AKM_SUITE_8021X:
1397 val = WPA_AUTH_UNSPECIFIED;
1398 break;
1399 case WLAN_AKM_SUITE_PSK:
1400 val = WPA_AUTH_PSK;
1401 break;
1402 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001403 brcmf_err("invalid cipher group (%d)\n",
1404 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001405 return -EINVAL;
1406 }
1407 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1408 switch (sme->crypto.akm_suites[0]) {
1409 case WLAN_AKM_SUITE_8021X:
1410 val = WPA2_AUTH_UNSPECIFIED;
1411 break;
1412 case WLAN_AKM_SUITE_PSK:
1413 val = WPA2_AUTH_PSK;
1414 break;
1415 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001416 brcmf_err("invalid cipher group (%d)\n",
1417 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001418 return -EINVAL;
1419 }
1420 }
1421
Arend van Spriel16886732012-12-05 15:26:04 +01001422 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001423 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1424 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001425 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001426 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001427 return err;
1428 }
1429 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001430 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001431 sec->wpa_auth = sme->crypto.akm_suites[0];
1432
1433 return err;
1434}
1435
1436static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001437brcmf_set_sharedkey(struct net_device *ndev,
1438 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001439{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001440 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001441 struct brcmf_cfg80211_security *sec;
1442 struct brcmf_wsec_key key;
1443 s32 val;
1444 s32 err = 0;
1445
Arend van Spriel16886732012-12-05 15:26:04 +01001446 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001447
Roland Vossena718e2f2011-10-12 20:51:24 +02001448 if (sme->key_len == 0)
1449 return 0;
1450
Arend van Spriel06bb1232012-09-27 14:17:56 +02001451 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001452 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1453 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001454
1455 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1456 return 0;
1457
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001458 if (!(sec->cipher_pairwise &
1459 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1460 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001461
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001462 memset(&key, 0, sizeof(key));
1463 key.len = (u32) sme->key_len;
1464 key.index = (u32) sme->key_idx;
1465 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001466 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001467 return -EINVAL;
1468 }
1469 memcpy(key.data, sme->key, key.len);
1470 key.flags = BRCMF_PRIMARY_KEY;
1471 switch (sec->cipher_pairwise) {
1472 case WLAN_CIPHER_SUITE_WEP40:
1473 key.algo = CRYPTO_ALGO_WEP1;
1474 break;
1475 case WLAN_CIPHER_SUITE_WEP104:
1476 key.algo = CRYPTO_ALGO_WEP128;
1477 break;
1478 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001479 brcmf_err("Invalid algorithm (%d)\n",
1480 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001481 return -EINVAL;
1482 }
1483 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001484 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1485 key.len, key.index, key.algo);
1486 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001487 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001488 if (err)
1489 return err;
1490
1491 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001492 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001493 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001494 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001495 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001496 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001497 }
1498 return err;
1499}
1500
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001501static
1502enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1503 enum nl80211_auth_type type)
1504{
1505 u32 ci;
1506 if (type == NL80211_AUTHTYPE_AUTOMATIC) {
1507 /* shift to ignore chip revision */
1508 ci = brcmf_get_chip_info(ifp) >> 4;
1509 switch (ci) {
1510 case 43236:
1511 brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n");
1512 return NL80211_AUTHTYPE_OPEN_SYSTEM;
1513 default:
1514 break;
1515 }
1516 }
1517 return type;
1518}
1519
Arend van Spriel5b435de2011-10-05 13:19:03 +02001520static s32
1521brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001522 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001523{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001524 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001525 struct brcmf_if *ifp = netdev_priv(ndev);
1526 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001527 struct ieee80211_channel *chan = sme->channel;
1528 struct brcmf_join_params join_params;
1529 size_t join_params_size;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001530 struct brcmf_tlv *rsn_ie;
1531 struct brcmf_vs_tlv *wpa_ie;
1532 void *ie;
1533 u32 ie_len;
1534 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001535 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001536
1537 s32 err = 0;
1538
Arend van Sprield96b8012012-12-05 15:26:02 +01001539 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001540 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001541 return -EIO;
1542
1543 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001544 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001545 return -EOPNOTSUPP;
1546 }
1547
Hante Meuleman89286dc2013-02-08 15:53:46 +01001548 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1549 /* A normal (non P2P) connection request setup. */
1550 ie = NULL;
1551 ie_len = 0;
1552 /* find the WPA_IE */
1553 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1554 if (wpa_ie) {
1555 ie = wpa_ie;
1556 ie_len = wpa_ie->len + TLV_HDR_LEN;
1557 } else {
1558 /* find the RSN_IE */
1559 rsn_ie = brcmf_parse_tlvs((u8 *)sme->ie, sme->ie_len,
1560 WLAN_EID_RSN);
1561 if (rsn_ie) {
1562 ie = rsn_ie;
1563 ie_len = rsn_ie->len + TLV_HDR_LEN;
1564 }
1565 }
1566 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1567 }
1568
1569 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1570 sme->ie, sme->ie_len);
1571 if (err)
1572 brcmf_err("Set Assoc REQ IE Failed\n");
1573 else
1574 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1575
Arend van Sprielc1179032012-10-22 13:55:33 -07001576 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001577
1578 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001579 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001580 ieee80211_frequency_to_channel(chan->center_freq);
Hante Meuleman17012612013-02-06 18:40:44 +01001581 chanspec = channel_to_chanspec(chan);
1582 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1583 cfg->channel, chan->center_freq, chanspec);
1584 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001585 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001586 chanspec = 0;
1587 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001588
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001589 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001590
1591 err = brcmf_set_wpa_version(ndev, sme);
1592 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001593 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001594 goto done;
1595 }
1596
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001597 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001598 err = brcmf_set_auth_type(ndev, sme);
1599 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001600 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001601 goto done;
1602 }
1603
1604 err = brcmf_set_set_cipher(ndev, sme);
1605 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001606 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001607 goto done;
1608 }
1609
1610 err = brcmf_set_key_mgmt(ndev, sme);
1611 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001612 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001613 goto done;
1614 }
1615
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001616 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001617 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001618 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001619 goto done;
1620 }
1621
Hante Meuleman89286dc2013-02-08 15:53:46 +01001622 profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
1623 (u32)sme->ssid_len);
1624 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1625 if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1626 profile->ssid.SSID[profile->ssid.SSID_len] = 0;
1627 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
1628 profile->ssid.SSID_len);
1629 }
1630
1631 /* Join with specific BSSID and cached SSID
1632 * If SSID is zero join based on BSSID only
1633 */
1634 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1635 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1636 if (cfg->channel)
1637 join_params_size += sizeof(u16);
1638 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1639 if (ext_join_params == NULL) {
1640 err = -ENOMEM;
1641 goto done;
1642 }
1643 ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
1644 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
1645 profile->ssid.SSID_len);
1646 /*increase dwell time to receive probe response or detect Beacon
1647 * from target AP at a noisy air only during connect command
1648 */
1649 ext_join_params->scan_le.active_time =
1650 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1651 ext_join_params->scan_le.passive_time =
1652 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1653 /* Set up join scan parameters */
1654 ext_join_params->scan_le.scan_type = -1;
1655 /* to sync with presence period of VSDB GO.
1656 * Send probe request more frequently. Probe request will be stopped
1657 * when it gets probe response from target AP/GO.
1658 */
1659 ext_join_params->scan_le.nprobes =
1660 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1661 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1662 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1663
1664 if (sme->bssid)
1665 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1666 else
1667 memset(&ext_join_params->assoc_le.bssid, 0xFF, ETH_ALEN);
1668
1669 if (cfg->channel) {
1670 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1671
1672 ext_join_params->assoc_le.chanspec_list[0] =
1673 cpu_to_le16(chanspec);
1674 }
1675
1676 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1677 join_params_size);
1678 kfree(ext_join_params);
1679 if (!err)
1680 /* This is it. join command worked, we are done */
1681 goto done;
1682
1683 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001684 memset(&join_params, 0, sizeof(join_params));
1685 join_params_size = sizeof(join_params.ssid_le);
1686
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001687 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001688 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001689
Hante Meuleman89286dc2013-02-08 15:53:46 +01001690 if (sme->bssid)
1691 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1692 else
1693 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001694
Hante Meuleman17012612013-02-06 18:40:44 +01001695 if (cfg->channel) {
1696 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1697 join_params.params_le.chanspec_num = cpu_to_le32(1);
1698 join_params_size += sizeof(join_params.params_le);
1699 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001700 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001701 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001702 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001703 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001704
1705done:
1706 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001707 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001708 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001709 return err;
1710}
1711
1712static s32
1713brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1714 u16 reason_code)
1715{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001716 struct brcmf_if *ifp = netdev_priv(ndev);
1717 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001718 struct brcmf_scb_val_le scbval;
1719 s32 err = 0;
1720
Arend van Sprield96b8012012-12-05 15:26:02 +01001721 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001722 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001723 return -EIO;
1724
Arend van Sprielc1179032012-10-22 13:55:33 -07001725 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001726
Arend van Spriel06bb1232012-09-27 14:17:56 +02001727 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001728 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001729 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001730 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001731 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001732 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001733
Arend van Sprield96b8012012-12-05 15:26:02 +01001734 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001735 return err;
1736}
1737
1738static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001739brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001740 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001741{
1742
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001743 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001744 struct net_device *ndev = cfg_to_ndev(cfg);
1745 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001746 u16 txpwrmw;
1747 s32 err = 0;
1748 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001749 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001750
Arend van Sprield96b8012012-12-05 15:26:02 +01001751 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001752 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001753 return -EIO;
1754
1755 switch (type) {
1756 case NL80211_TX_POWER_AUTOMATIC:
1757 break;
1758 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001759 case NL80211_TX_POWER_FIXED:
1760 if (dbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001761 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001762 err = -EINVAL;
1763 goto done;
1764 }
1765 break;
1766 }
1767 /* Make sure radio is off or on as far as software is concerned */
1768 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001769 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001770 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001771 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001772
1773 if (dbm > 0xffff)
1774 txpwrmw = 0xffff;
1775 else
1776 txpwrmw = (u16) dbm;
Arend van Sprielac24be62012-10-22 10:36:23 -07001777 err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
1778 (s32)brcmf_mw_to_qdbm(txpwrmw));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001779 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001780 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001781 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001782
1783done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001784 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001785 return err;
1786}
1787
Johannes Bergc8442112012-10-24 10:17:18 +02001788static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
1789 struct wireless_dev *wdev,
1790 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001791{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001792 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001793 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001794 s32 txpwrdbm;
1795 u8 result;
1796 s32 err = 0;
1797
Arend van Sprield96b8012012-12-05 15:26:02 +01001798 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001799 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001800 return -EIO;
1801
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001802 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001803 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001804 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001805 goto done;
1806 }
1807
1808 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001809 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001810
1811done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001812 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001813 return err;
1814}
1815
1816static s32
1817brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1818 u8 key_idx, bool unicast, bool multicast)
1819{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001820 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001821 u32 index;
1822 u32 wsec;
1823 s32 err = 0;
1824
Arend van Sprield96b8012012-12-05 15:26:02 +01001825 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01001826 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001827 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001828 return -EIO;
1829
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001830 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001831 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001832 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001833 goto done;
1834 }
1835
1836 if (wsec & WEP_ENABLED) {
1837 /* Just select a new current key */
1838 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001839 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001840 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001841 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001842 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001843 }
1844done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001845 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001846 return err;
1847}
1848
1849static s32
1850brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
1851 u8 key_idx, const u8 *mac_addr, struct key_params *params)
1852{
1853 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001854 s32 err = 0;
1855
1856 memset(&key, 0, sizeof(key));
1857 key.index = (u32) key_idx;
1858 /* Instead of bcast for ea address for default wep keys,
1859 driver needs it to be Null */
1860 if (!is_multicast_ether_addr(mac_addr))
1861 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
1862 key.len = (u32) params->key_len;
1863 /* check for key index change */
1864 if (key.len == 0) {
1865 /* key delete */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001866 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001867 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001868 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001869 } else {
1870 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001871 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001872 return -EINVAL;
1873 }
1874
Arend van Spriel16886732012-12-05 15:26:04 +01001875 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001876 memcpy(key.data, params->key, key.len);
1877
1878 if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
1879 u8 keybuf[8];
1880 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1881 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1882 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1883 }
1884
1885 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
1886 if (params->seq && params->seq_len == 6) {
1887 /* rx iv */
1888 u8 *ivptr;
1889 ivptr = (u8 *) params->seq;
1890 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
1891 (ivptr[3] << 8) | ivptr[2];
1892 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
1893 key.iv_initialized = true;
1894 }
1895
1896 switch (params->cipher) {
1897 case WLAN_CIPHER_SUITE_WEP40:
1898 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01001899 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001900 break;
1901 case WLAN_CIPHER_SUITE_WEP104:
1902 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01001903 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001904 break;
1905 case WLAN_CIPHER_SUITE_TKIP:
1906 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01001907 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001908 break;
1909 case WLAN_CIPHER_SUITE_AES_CMAC:
1910 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01001911 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001912 break;
1913 case WLAN_CIPHER_SUITE_CCMP:
1914 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01001915 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001916 break;
1917 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001918 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001919 return -EINVAL;
1920 }
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001921 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001922 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001923 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001924 }
1925 return err;
1926}
1927
1928static s32
1929brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1930 u8 key_idx, bool pairwise, const u8 *mac_addr,
1931 struct key_params *params)
1932{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001933 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001934 struct brcmf_wsec_key key;
1935 s32 val;
1936 s32 wsec;
1937 s32 err = 0;
1938 u8 keybuf[8];
1939
Arend van Sprield96b8012012-12-05 15:26:02 +01001940 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01001941 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001942 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001943 return -EIO;
1944
1945 if (mac_addr) {
Arend van Sprield96b8012012-12-05 15:26:02 +01001946 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001947 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
1948 }
1949 memset(&key, 0, sizeof(key));
1950
1951 key.len = (u32) params->key_len;
1952 key.index = (u32) key_idx;
1953
1954 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001955 brcmf_err("Too long key length (%u)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001956 err = -EINVAL;
1957 goto done;
1958 }
1959 memcpy(key.data, params->key, key.len);
1960
1961 key.flags = BRCMF_PRIMARY_KEY;
1962 switch (params->cipher) {
1963 case WLAN_CIPHER_SUITE_WEP40:
1964 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001965 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001966 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001967 break;
1968 case WLAN_CIPHER_SUITE_WEP104:
1969 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001970 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001971 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001972 break;
1973 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel128ce3b2012-11-28 21:44:12 +01001974 if (ifp->vif->mode != WL_MODE_AP) {
Arend van Spriel16886732012-12-05 15:26:04 +01001975 brcmf_dbg(CONN, "Swapping key\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02001976 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1977 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1978 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1979 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001980 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001981 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001982 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001983 break;
1984 case WLAN_CIPHER_SUITE_AES_CMAC:
1985 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001986 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001987 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001988 break;
1989 case WLAN_CIPHER_SUITE_CCMP:
1990 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001991 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001992 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001993 break;
1994 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001995 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001996 err = -EINVAL;
1997 goto done;
1998 }
1999
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07002000 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002001 if (err)
2002 goto done;
2003
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002004 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002005 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002006 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002007 goto done;
2008 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002009 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002010 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002011 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002012 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002013 goto done;
2014 }
2015
Arend van Spriel5b435de2011-10-05 13:19:03 +02002016done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002017 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002018 return err;
2019}
2020
2021static s32
2022brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2023 u8 key_idx, bool pairwise, const u8 *mac_addr)
2024{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002025 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002026 struct brcmf_wsec_key key;
2027 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002028
Arend van Sprield96b8012012-12-05 15:26:02 +01002029 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002030 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002031 return -EIO;
2032
Hante Meuleman256c3742012-11-05 16:22:28 -08002033 if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
2034 /* we ignore this key index in this case */
Arend van Spriel57d6e912012-12-05 15:26:00 +01002035 brcmf_err("invalid key index (%d)\n", key_idx);
Hante Meuleman256c3742012-11-05 16:22:28 -08002036 return -EINVAL;
2037 }
2038
Arend van Spriel5b435de2011-10-05 13:19:03 +02002039 memset(&key, 0, sizeof(key));
2040
2041 key.index = (u32) key_idx;
2042 key.flags = BRCMF_PRIMARY_KEY;
2043 key.algo = CRYPTO_ALGO_OFF;
2044
Arend van Spriel16886732012-12-05 15:26:04 +01002045 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002046
2047 /* Set the new key/index */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07002048 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002049
Arend van Sprield96b8012012-12-05 15:26:02 +01002050 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002051 return err;
2052}
2053
2054static s32
2055brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2056 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2057 void (*callback) (void *cookie, struct key_params * params))
2058{
2059 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002060 struct brcmf_if *ifp = netdev_priv(ndev);
2061 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002062 struct brcmf_cfg80211_security *sec;
2063 s32 wsec;
2064 s32 err = 0;
2065
Arend van Sprield96b8012012-12-05 15:26:02 +01002066 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002067 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002068 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002069 return -EIO;
2070
2071 memset(&params, 0, sizeof(params));
2072
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002073 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002074 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002075 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002076 /* Ignore this error, may happen during DISASSOC */
2077 err = -EAGAIN;
2078 goto done;
2079 }
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002080 switch (wsec & ~SES_OW_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002081 case WEP_ENABLED:
Arend van Spriel06bb1232012-09-27 14:17:56 +02002082 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002083 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2084 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002085 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002086 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2087 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002088 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002089 }
2090 break;
2091 case TKIP_ENABLED:
2092 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002093 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002094 break;
2095 case AES_ENABLED:
2096 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002097 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002098 break;
2099 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002100 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002101 err = -EINVAL;
2102 goto done;
2103 }
2104 callback(cookie, &params);
2105
2106done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002107 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002108 return err;
2109}
2110
2111static s32
2112brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2113 struct net_device *ndev, u8 key_idx)
2114{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002115 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002116
2117 return -EOPNOTSUPP;
2118}
2119
2120static s32
2121brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02002122 u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002123{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002124 struct brcmf_if *ifp = netdev_priv(ndev);
2125 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002126 struct brcmf_scb_val_le scb_val;
2127 int rssi;
2128 s32 rate;
2129 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02002130 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002131 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002132
Arend van Sprield96b8012012-12-05 15:26:02 +01002133 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002134 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002135 return -EIO;
2136
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002137 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002138 memcpy(&sta_info_le, mac, ETH_ALEN);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002139 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002140 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002141 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002142 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002143 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002144 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002145 }
Hante Meuleman1a873342012-09-27 14:17:54 +02002146 sinfo->filled = STATION_INFO_INACTIVE_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002147 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2148 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002149 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002150 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02002151 }
Arend van Sprield96b8012012-12-05 15:26:02 +01002152 brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
2153 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002154 } else if (ifp->vif->mode == WL_MODE_BSS) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002155 if (memcmp(mac, bssid, ETH_ALEN)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002156 brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
2157 mac, bssid);
Hante Meuleman1a873342012-09-27 14:17:54 +02002158 err = -ENOENT;
2159 goto done;
2160 }
2161 /* Report the current tx rate */
Hante Meuleman89286dc2013-02-08 15:53:46 +01002162 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02002163 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002164 brcmf_err("Could not get rate (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002165 goto done;
2166 } else {
2167 sinfo->filled |= STATION_INFO_TX_BITRATE;
2168 sinfo->txrate.legacy = rate * 5;
Arend van Spriel16886732012-12-05 15:26:04 +01002169 brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
Hante Meuleman1a873342012-09-27 14:17:54 +02002170 }
2171
Arend van Sprielc1179032012-10-22 13:55:33 -07002172 if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
2173 &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002174 memset(&scb_val, 0, sizeof(scb_val));
Arend van Sprielc1179032012-10-22 13:55:33 -07002175 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
2176 &scb_val, sizeof(scb_val));
Hante Meuleman1a873342012-09-27 14:17:54 +02002177 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002178 brcmf_err("Could not get rssi (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002179 goto done;
2180 } else {
2181 rssi = le32_to_cpu(scb_val.val);
2182 sinfo->filled |= STATION_INFO_SIGNAL;
2183 sinfo->signal = rssi;
Arend van Spriel16886732012-12-05 15:26:04 +01002184 brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
Hante Meuleman1a873342012-09-27 14:17:54 +02002185 }
2186 }
2187 } else
2188 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002189done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002190 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002191 return err;
2192}
2193
2194static s32
2195brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2196 bool enabled, s32 timeout)
2197{
2198 s32 pm;
2199 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002200 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002201 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002202
Arend van Sprield96b8012012-12-05 15:26:02 +01002203 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002204
2205 /*
2206 * Powersave enable/disable request is coming from the
2207 * cfg80211 even before the interface is up. In that
2208 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002209 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002210 * FW later while initializing the dongle
2211 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002212 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002213 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002214
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002215 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002216 goto done;
2217 }
2218
2219 pm = enabled ? PM_FAST : PM_OFF;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002220 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002221
Arend van Sprielc1179032012-10-22 13:55:33 -07002222 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002223 if (err) {
2224 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002225 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002226 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002227 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002228 }
2229done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002230 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002231 return err;
2232}
2233
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002234static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002235 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002237 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002238 struct ieee80211_channel *notify_channel;
2239 struct cfg80211_bss *bss;
2240 struct ieee80211_supported_band *band;
2241 s32 err = 0;
2242 u16 channel;
2243 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002244 u16 notify_capability;
2245 u16 notify_interval;
2246 u8 *notify_ie;
2247 size_t notify_ielen;
2248 s32 notify_signal;
2249
2250 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002251 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002252 return 0;
2253 }
2254
2255 channel = bi->ctl_ch ? bi->ctl_ch :
2256 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2257
2258 if (channel <= CH_MAX_2G_CHANNEL)
2259 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2260 else
2261 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2262
2263 freq = ieee80211_channel_to_frequency(channel, band->band);
2264 notify_channel = ieee80211_get_channel(wiphy, freq);
2265
Arend van Spriel5b435de2011-10-05 13:19:03 +02002266 notify_capability = le16_to_cpu(bi->capability);
2267 notify_interval = le16_to_cpu(bi->beacon_period);
2268 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2269 notify_ielen = le32_to_cpu(bi->ie_length);
2270 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2271
Arend van Spriel16886732012-12-05 15:26:04 +01002272 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2273 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2274 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2275 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2276 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002277
2278 bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002279 0, notify_capability, notify_interval, notify_ie,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002280 notify_ielen, notify_signal, GFP_KERNEL);
2281
Franky Line78946e2011-11-10 20:30:34 +01002282 if (!bss)
2283 return -ENOMEM;
2284
Johannes Berg5b112d32013-02-01 01:49:58 +01002285 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002286
2287 return err;
2288}
2289
Roland Vossen6f09be02011-10-18 14:03:02 +02002290static struct brcmf_bss_info_le *
2291next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2292{
2293 if (bss == NULL)
2294 return list->bss_info_le;
2295 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2296 le32_to_cpu(bss->length));
2297}
2298
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002299static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002300{
2301 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002302 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002303 s32 err = 0;
2304 int i;
2305
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002306 bss_list = cfg->bss_list;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002307 if (bss_list->count != 0 &&
2308 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002309 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2310 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002311 return -EOPNOTSUPP;
2312 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002313 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002314 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002315 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002316 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002317 if (err)
2318 break;
2319 }
2320 return err;
2321}
2322
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002323static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002324 struct net_device *ndev, const u8 *bssid)
2325{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002326 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002327 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002328 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002329 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002330 struct cfg80211_bss *bss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002331 u8 *buf = NULL;
2332 s32 err = 0;
2333 u16 channel;
2334 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002335 u16 notify_capability;
2336 u16 notify_interval;
2337 u8 *notify_ie;
2338 size_t notify_ielen;
2339 s32 notify_signal;
2340
Arend van Sprield96b8012012-12-05 15:26:02 +01002341 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002342
2343 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2344 if (buf == NULL) {
2345 err = -ENOMEM;
2346 goto CleanUp;
2347 }
2348
2349 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2350
Arend van Sprielac24be62012-10-22 10:36:23 -07002351 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2352 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002353 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002354 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002355 goto CleanUp;
2356 }
2357
Roland Vossend34bf642011-10-18 14:03:01 +02002358 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002359
2360 channel = bi->ctl_ch ? bi->ctl_ch :
2361 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2362
2363 if (channel <= CH_MAX_2G_CHANNEL)
2364 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2365 else
2366 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2367
2368 freq = ieee80211_channel_to_frequency(channel, band->band);
2369 notify_channel = ieee80211_get_channel(wiphy, freq);
2370
Arend van Spriel5b435de2011-10-05 13:19:03 +02002371 notify_capability = le16_to_cpu(bi->capability);
2372 notify_interval = le16_to_cpu(bi->beacon_period);
2373 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2374 notify_ielen = le32_to_cpu(bi->ie_length);
2375 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2376
Arend van Spriel16886732012-12-05 15:26:04 +01002377 brcmf_dbg(CONN, "channel: %d(%d)\n", channel, freq);
2378 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2379 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2380 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002381
Franky Line78946e2011-11-10 20:30:34 +01002382 bss = cfg80211_inform_bss(wiphy, notify_channel, bssid,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002383 0, notify_capability, notify_interval,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002384 notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
2385
Franky Line78946e2011-11-10 20:30:34 +01002386 if (!bss) {
2387 err = -ENOMEM;
2388 goto CleanUp;
2389 }
2390
Johannes Berg5b112d32013-02-01 01:49:58 +01002391 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002392
Arend van Spriel5b435de2011-10-05 13:19:03 +02002393CleanUp:
2394
2395 kfree(buf);
2396
Arend van Sprield96b8012012-12-05 15:26:02 +01002397 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002398
2399 return err;
2400}
2401
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002402static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002403{
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002404 return vif->mode == WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002405}
2406
Hante Meuleman89286dc2013-02-08 15:53:46 +01002407static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2408 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002409{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002410 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002411 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002412 struct brcmf_ssid *ssid;
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002413 struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002414 u16 beacon_interval;
2415 u8 dtim_period;
2416 size_t ie_len;
2417 u8 *ie;
2418 s32 err = 0;
2419
Arend van Sprield96b8012012-12-05 15:26:02 +01002420 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002421 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002422 return err;
2423
Arend van Spriel06bb1232012-09-27 14:17:56 +02002424 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002425
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002426 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002427 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002428 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002429 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002430 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002431 goto update_bss_info_out;
2432 }
2433
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002434 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2435 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002436 if (err)
2437 goto update_bss_info_out;
2438
2439 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2440 ie_len = le32_to_cpu(bi->ie_length);
2441 beacon_interval = le16_to_cpu(bi->beacon_period);
2442
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002443 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002444 if (tim)
2445 dtim_period = tim->data[1];
2446 else {
2447 /*
2448 * active scan was done so we could not get dtim
2449 * information out of probe response.
2450 * so we speficially query dtim information to dongle.
2451 */
2452 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002453 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002454 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002455 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002456 goto update_bss_info_out;
2457 }
2458 dtim_period = (u8)var;
2459 }
2460
Arend van Spriel5b435de2011-10-05 13:19:03 +02002461update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002462 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002463 return err;
2464}
2465
Hante Meuleman18e2f612013-02-08 15:53:49 +01002466void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002467{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002468 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002469
Arend van Sprielc1179032012-10-22 13:55:33 -07002470 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002471 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002472 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002473 brcmf_notify_escan_complete(cfg, escan->ndev, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002474 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002475 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2476 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002477}
2478
Hante Meulemane756af52012-09-11 21:18:52 +02002479static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2480{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002481 struct brcmf_cfg80211_info *cfg =
2482 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002483 escan_timeout_work);
2484
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002485 brcmf_notify_escan_complete(cfg, cfg->escan_info.ndev, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002486}
2487
2488static void brcmf_escan_timeout(unsigned long data)
2489{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002490 struct brcmf_cfg80211_info *cfg =
2491 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002492
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002493 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002494 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002495 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002496 }
2497}
2498
2499static s32
2500brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
2501 struct brcmf_bss_info_le *bss_info_le)
2502{
2503 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
2504 (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
2505 CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
2506 bss_info_le->SSID_len == bss->SSID_len &&
2507 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
2508 if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
2509 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002510 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2511 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2512
Hante Meulemane756af52012-09-11 21:18:52 +02002513 /* preserve max RSSI if the measurements are
2514 * both on-channel or both off-channel
2515 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002516 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002517 bss->RSSI = bss_info_le->RSSI;
2518 } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
2519 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
2520 /* preserve the on-channel rssi measurement
2521 * if the new measurement is off channel
2522 */
2523 bss->RSSI = bss_info_le->RSSI;
2524 bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
2525 }
2526 return 1;
2527 }
2528 return 0;
2529}
2530
2531static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002532brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002533 const struct brcmf_event_msg *e, void *data)
2534{
Arend van Spriel19937322012-11-05 16:22:32 -08002535 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
2536 struct net_device *ndev = ifp->ndev;
Hante Meulemane756af52012-09-11 21:18:52 +02002537 s32 status;
2538 s32 err = 0;
2539 struct brcmf_escan_result_le *escan_result_le;
2540 struct brcmf_bss_info_le *bss_info_le;
2541 struct brcmf_bss_info_le *bss = NULL;
2542 u32 bi_length;
2543 struct brcmf_scan_results *list;
2544 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002545 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002546
Arend van Spriel5c36b992012-11-14 18:46:05 -08002547 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002548
Hante Meulemanf07998952012-11-05 16:22:13 -08002549 if (!ndev || !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002550 brcmf_err("scan not ready ndev %p drv_status %x\n", ndev,
2551 !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status));
Hante Meulemane756af52012-09-11 21:18:52 +02002552 return -EPERM;
2553 }
2554
2555 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002556 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002557 escan_result_le = (struct brcmf_escan_result_le *) data;
2558 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002559 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002560 goto exit;
2561 }
Hante Meulemane756af52012-09-11 21:18:52 +02002562 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002563 brcmf_err("Invalid bss_count %d: ignoring\n",
2564 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002565 goto exit;
2566 }
2567 bss_info_le = &escan_result_le->bss_info_le;
2568
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002569 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2570 goto exit;
2571
2572 if (!cfg->scan_request) {
2573 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2574 goto exit;
2575 }
2576
Hante Meulemane756af52012-09-11 21:18:52 +02002577 bi_length = le32_to_cpu(bss_info_le->length);
2578 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2579 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002580 brcmf_err("Invalid bss_info length %d: ignoring\n",
2581 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002582 goto exit;
2583 }
2584
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002585 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002586 BIT(NL80211_IFTYPE_ADHOC))) {
2587 if (le16_to_cpu(bss_info_le->capability) &
2588 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002589 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002590 goto exit;
2591 }
2592 }
2593
2594 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002595 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002596 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002597 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002598 goto exit;
2599 }
2600
2601 for (i = 0; i < list->count; i++) {
2602 bss = bss ? (struct brcmf_bss_info_le *)
2603 ((unsigned char *)bss +
2604 le32_to_cpu(bss->length)) : list->bss_info_le;
2605 if (brcmf_compare_update_same_bss(bss, bss_info_le))
2606 goto exit;
2607 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002608 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002609 bss_info_le, bi_length);
2610 list->version = le32_to_cpu(bss_info_le->version);
2611 list->buflen += bi_length;
2612 list->count++;
2613 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002614 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002615 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2616 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002617 if (cfg->scan_request) {
2618 cfg->bss_list = (struct brcmf_scan_results *)
2619 cfg->escan_info.escan_buf;
2620 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002621 aborted = status != BRCMF_E_STATUS_SUCCESS;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002622 brcmf_notify_escan_complete(cfg, ndev, aborted,
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002623 false);
Hante Meulemane756af52012-09-11 21:18:52 +02002624 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002625 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2626 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002627 }
2628exit:
2629 return err;
2630}
2631
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002632static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002633{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002634 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
2635 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08002636 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2637 /* Init scan_timeout timer */
2638 init_timer(&cfg->escan_timeout);
2639 cfg->escan_timeout.data = (unsigned long) cfg;
2640 cfg->escan_timeout.function = brcmf_escan_timeout;
2641 INIT_WORK(&cfg->escan_timeout_work,
2642 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002643}
2644
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002645static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002646{
2647 if (ms < 1000 / HZ) {
2648 cond_resched();
2649 mdelay(ms);
2650 } else {
2651 msleep(ms);
2652 }
2653}
2654
2655static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
2656{
Arend van Sprield96b8012012-12-05 15:26:02 +01002657 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002658
Arend van Spriel5b435de2011-10-05 13:19:03 +02002659 return 0;
2660}
2661
2662static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
2663 struct cfg80211_wowlan *wow)
2664{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002665 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2666 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel7d641072012-10-22 13:55:39 -07002667 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002668
Arend van Sprield96b8012012-12-05 15:26:02 +01002669 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002670
2671 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002672 * if the primary net_device is not READY there is nothing
2673 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02002674 */
Arend van Spriel7d641072012-10-22 13:55:39 -07002675 vif = ((struct brcmf_if *)netdev_priv(ndev))->vif;
2676 if (!check_vif_up(vif))
2677 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002678
Arend van Spriel7d641072012-10-22 13:55:39 -07002679 list_for_each_entry(vif, &cfg->vif_list, list) {
2680 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
2681 continue;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002682 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002683 * While going to suspend if associated with AP disassociate
2684 * from AP to save power while system is in suspended state
Arend van Spriel5b435de2011-10-05 13:19:03 +02002685 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01002686 brcmf_link_down(vif);
Arend van Spriel7d641072012-10-22 13:55:39 -07002687
Arend van Spriel903e0ee2012-11-28 21:44:11 +01002688 /* Make sure WPA_Supplicant receives all the event
2689 * generated due to DISASSOC call to the fw to keep
2690 * the state fw and WPA_Supplicant state consistent
2691 */
2692 brcmf_delay(500);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002693 }
2694
Arend van Spriel7d641072012-10-22 13:55:39 -07002695 /* end any scanning */
2696 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002697 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002698
2699 /* Turn off watchdog timer */
Arend van Spriel7d641072012-10-22 13:55:39 -07002700 brcmf_set_mpc(ndev, 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002701
Arend van Spriel7d641072012-10-22 13:55:39 -07002702exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01002703 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07002704 /* clear any scanning activity */
2705 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002706 return 0;
2707}
2708
2709static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002710brcmf_update_pmklist(struct net_device *ndev,
2711 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
2712{
2713 int i, j;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002714 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002715
Arend van Spriel40c8e952011-10-12 20:51:20 +02002716 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
2717
Arend van Spriel16886732012-12-05 15:26:04 +01002718 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002719 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01002720 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
2721 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002722 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01002723 brcmf_dbg(CONN, "%02x\n",
2724 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002725 }
2726
2727 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07002728 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
2729 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002730
2731 return err;
2732}
2733
2734static s32
2735brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2736 struct cfg80211_pmksa *pmksa)
2737{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002738 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002739 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002740 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002741 s32 err = 0;
2742 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002743 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002744
Arend van Sprield96b8012012-12-05 15:26:02 +01002745 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002746 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002747 return -EIO;
2748
Arend van Spriel40c8e952011-10-12 20:51:20 +02002749 pmkid_len = le32_to_cpu(pmkids->npmkid);
2750 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002751 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
2752 break;
2753 if (i < WL_NUM_PMKIDS_MAX) {
2754 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
2755 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002756 if (i == pmkid_len) {
2757 pmkid_len++;
2758 pmkids->npmkid = cpu_to_le32(pmkid_len);
2759 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002760 } else
2761 err = -EINVAL;
2762
Arend van Spriel16886732012-12-05 15:26:04 +01002763 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
2764 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002765 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01002766 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002767
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002768 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002769
Arend van Sprield96b8012012-12-05 15:26:02 +01002770 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002771 return err;
2772}
2773
2774static s32
2775brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2776 struct cfg80211_pmksa *pmksa)
2777{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002778 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002779 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002780 struct pmkid_list pmkid;
2781 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002782 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002783
Arend van Sprield96b8012012-12-05 15:26:02 +01002784 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002785 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002786 return -EIO;
2787
2788 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
2789 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
2790
Arend van Spriel16886732012-12-05 15:26:04 +01002791 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
2792 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002793 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01002794 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002795
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002796 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002797 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002798 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002799 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002800 ETH_ALEN))
2801 break;
2802
Arend van Spriel40c8e952011-10-12 20:51:20 +02002803 if ((pmkid_len > 0)
2804 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002805 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002806 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02002807 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002808 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
2809 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002810 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002811 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
2812 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002813 WLAN_PMKID_LEN);
2814 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002815 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002816 } else
2817 err = -EINVAL;
2818
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002819 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002820
Arend van Sprield96b8012012-12-05 15:26:02 +01002821 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002822 return err;
2823
2824}
2825
2826static s32
2827brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
2828{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002829 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002830 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002831 s32 err = 0;
2832
Arend van Sprield96b8012012-12-05 15:26:02 +01002833 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002834 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002835 return -EIO;
2836
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002837 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
2838 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002839
Arend van Sprield96b8012012-12-05 15:26:02 +01002840 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002841 return err;
2842
2843}
2844
Arend van Spriele5806072012-09-19 22:21:08 +02002845/*
2846 * PFN result doesn't have all the info which are
2847 * required by the supplicant
2848 * (For e.g IEs) Do a target Escan so that sched scan results are reported
2849 * via wl_inform_single_bss in the required format. Escan does require the
2850 * scan request in the form of cfg80211_scan_request. For timebeing, create
2851 * cfg80211_scan_request one out of the received PNO event.
2852 */
2853static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002854brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02002855 const struct brcmf_event_msg *e, void *data)
2856{
Arend van Spriel19937322012-11-05 16:22:32 -08002857 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
2858 struct net_device *ndev = ifp->ndev;
Arend van Spriele5806072012-09-19 22:21:08 +02002859 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
2860 struct cfg80211_scan_request *request = NULL;
2861 struct cfg80211_ssid *ssid = NULL;
2862 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002863 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002864 int err = 0;
2865 int channel_req = 0;
2866 int band = 0;
2867 struct brcmf_pno_scanresults_le *pfn_result;
2868 u32 result_count;
2869 u32 status;
2870
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002871 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02002872
Arend van Spriel5c36b992012-11-14 18:46:05 -08002873 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002874 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02002875 return 0;
2876 }
2877
2878 pfn_result = (struct brcmf_pno_scanresults_le *)data;
2879 result_count = le32_to_cpu(pfn_result->count);
2880 status = le32_to_cpu(pfn_result->status);
2881
2882 /*
2883 * PFN event is limited to fit 512 bytes so we may get
2884 * multiple NET_FOUND events. For now place a warning here.
2885 */
2886 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002887 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02002888 if (result_count > 0) {
2889 int i;
2890
2891 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03002892 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
2893 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02002894 if (!request || !ssid || !channel) {
2895 err = -ENOMEM;
2896 goto out_err;
2897 }
2898
2899 request->wiphy = wiphy;
2900 data += sizeof(struct brcmf_pno_scanresults_le);
2901 netinfo_start = (struct brcmf_pno_net_info_le *)data;
2902
2903 for (i = 0; i < result_count; i++) {
2904 netinfo = &netinfo_start[i];
2905 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002906 brcmf_err("Invalid netinfo ptr. index: %d\n",
2907 i);
Arend van Spriele5806072012-09-19 22:21:08 +02002908 err = -EINVAL;
2909 goto out_err;
2910 }
2911
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002912 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
2913 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02002914 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
2915 ssid[i].ssid_len = netinfo->SSID_len;
2916 request->n_ssids++;
2917
2918 channel_req = netinfo->channel;
2919 if (channel_req <= CH_MAX_2G_CHANNEL)
2920 band = NL80211_BAND_2GHZ;
2921 else
2922 band = NL80211_BAND_5GHZ;
2923 channel[i].center_freq =
2924 ieee80211_channel_to_frequency(channel_req,
2925 band);
2926 channel[i].band = band;
2927 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
2928 request->channels[i] = &channel[i];
2929 request->n_channels++;
2930 }
2931
2932 /* assign parsed ssid array */
2933 if (request->n_ssids)
2934 request->ssids = &ssid[0];
2935
Arend van Sprielc1179032012-10-22 13:55:33 -07002936 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02002937 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002938 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002939 }
2940
Arend van Sprielc1179032012-10-22 13:55:33 -07002941 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002942 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriele5806072012-09-19 22:21:08 +02002943 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07002944 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02002945 goto out_err;
2946 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002947 cfg->sched_escan = true;
2948 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02002949 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002950 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02002951 goto out_err;
2952 }
2953
2954 kfree(ssid);
2955 kfree(channel);
2956 kfree(request);
2957 return 0;
2958
2959out_err:
2960 kfree(ssid);
2961 kfree(channel);
2962 kfree(request);
2963 cfg80211_sched_scan_stopped(wiphy);
2964 return err;
2965}
2966
Arend van Spriele5806072012-09-19 22:21:08 +02002967static int brcmf_dev_pno_clean(struct net_device *ndev)
2968{
Arend van Spriele5806072012-09-19 22:21:08 +02002969 int ret;
2970
2971 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002972 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002973 if (ret == 0) {
2974 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002975 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
2976 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002977 }
2978 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002979 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02002980
2981 return ret;
2982}
2983
2984static int brcmf_dev_pno_config(struct net_device *ndev)
2985{
2986 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02002987
2988 memset(&pfn_param, 0, sizeof(pfn_param));
2989 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
2990
2991 /* set extra pno params */
2992 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
2993 pfn_param.repeat = BRCMF_PNO_REPEAT;
2994 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
2995
2996 /* set up pno scan fr */
2997 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
2998
Arend van Sprielac24be62012-10-22 10:36:23 -07002999 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3000 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003001}
3002
3003static int
3004brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3005 struct net_device *ndev,
3006 struct cfg80211_sched_scan_request *request)
3007{
Arend van Sprielc1179032012-10-22 13:55:33 -07003008 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003009 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003010 struct brcmf_pno_net_param_le pfn;
3011 int i;
3012 int ret = 0;
3013
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003014 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003015 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003016 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003017 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003018 return -EAGAIN;
3019 }
3020
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003021 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003022 brcmf_err("Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003023 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003024 return -EINVAL;
3025 }
3026
3027 if (request->n_ssids > 0) {
3028 for (i = 0; i < request->n_ssids; i++) {
3029 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003030 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3031 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003032
3033 /*
3034 * match_set ssids is a supert set of n_ssid list,
3035 * so we need not add these set seperately.
3036 */
3037 }
3038 }
3039
3040 if (request->n_match_sets > 0) {
3041 /* clean up everything */
3042 ret = brcmf_dev_pno_clean(ndev);
3043 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003044 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003045 return ret;
3046 }
3047
3048 /* configure pno */
3049 ret = brcmf_dev_pno_config(ndev);
3050 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003051 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003052 return -EINVAL;
3053 }
3054
3055 /* configure each match set */
3056 for (i = 0; i < request->n_match_sets; i++) {
3057 struct cfg80211_ssid *ssid;
3058 u32 ssid_len;
3059
3060 ssid = &request->match_sets[i].ssid;
3061 ssid_len = ssid->ssid_len;
3062
3063 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003064 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003065 continue;
3066 }
3067 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3068 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3069 pfn.wsec = cpu_to_le32(0);
3070 pfn.infra = cpu_to_le32(1);
3071 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3072 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3073 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003074 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003075 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003076 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3077 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003078 }
3079 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003080 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003081 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003082 return -EINVAL;
3083 }
3084 } else {
3085 return -EINVAL;
3086 }
3087
3088 return 0;
3089}
3090
3091static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3092 struct net_device *ndev)
3093{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003094 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003095
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003096 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003097 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003098 if (cfg->sched_escan)
3099 brcmf_notify_escan_complete(cfg, ndev, true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003100 return 0;
3101}
Arend van Spriele5806072012-09-19 22:21:08 +02003102
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003103#ifdef CONFIG_NL80211_TESTMODE
3104static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3105{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003106 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003107 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003108 struct brcmf_dcmd *dcmd = data;
3109 struct sk_buff *reply;
3110 int ret;
3111
Arend van Sprield96b8012012-12-05 15:26:02 +01003112 brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
3113 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003114
3115 if (dcmd->set)
Arend van Sprielac24be62012-10-22 10:36:23 -07003116 ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
3117 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003118 else
Arend van Sprielac24be62012-10-22 10:36:23 -07003119 ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
3120 dcmd->buf, dcmd->len);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003121 if (ret == 0) {
3122 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3123 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3124 ret = cfg80211_testmode_reply(reply);
3125 }
3126 return ret;
3127}
3128#endif
3129
Hante Meuleman1f170112013-02-06 18:40:38 +01003130static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003131{
3132 s32 err;
3133
3134 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003135 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003136 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003137 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003138 return err;
3139 }
3140 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003141 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003142 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003143 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003144 return err;
3145 }
3146 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003147 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003148 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003149 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003150 return err;
3151 }
3152
3153 return 0;
3154}
3155
3156static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3157{
3158 if (is_rsn_ie)
3159 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3160
3161 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3162}
3163
3164static s32
3165brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
Arend van Spriel34778522012-11-05 16:22:19 -08003166 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003167{
Arend van Sprielac24be62012-10-22 10:36:23 -07003168 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003169 u32 auth = 0; /* d11 open authentication */
3170 u16 count;
3171 s32 err = 0;
3172 s32 len = 0;
3173 u32 i;
3174 u32 wsec;
3175 u32 pval = 0;
3176 u32 gval = 0;
3177 u32 wpa_auth = 0;
3178 u32 offset;
3179 u8 *data;
3180 u16 rsn_cap;
3181 u32 wme_bss_disable;
3182
Arend van Sprield96b8012012-12-05 15:26:02 +01003183 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003184 if (wpa_ie == NULL)
3185 goto exit;
3186
3187 len = wpa_ie->len + TLV_HDR_LEN;
3188 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003189 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003190 if (!is_rsn_ie)
3191 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003192 else
3193 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003194
3195 /* check for multicast cipher suite */
3196 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3197 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003198 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003199 goto exit;
3200 }
3201
3202 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3203 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003204 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003205 goto exit;
3206 }
3207 offset += TLV_OUI_LEN;
3208
3209 /* pick up multicast cipher */
3210 switch (data[offset]) {
3211 case WPA_CIPHER_NONE:
3212 gval = 0;
3213 break;
3214 case WPA_CIPHER_WEP_40:
3215 case WPA_CIPHER_WEP_104:
3216 gval = WEP_ENABLED;
3217 break;
3218 case WPA_CIPHER_TKIP:
3219 gval = TKIP_ENABLED;
3220 break;
3221 case WPA_CIPHER_AES_CCM:
3222 gval = AES_ENABLED;
3223 break;
3224 default:
3225 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003226 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003227 goto exit;
3228 }
3229
3230 offset++;
3231 /* walk thru unicast cipher list and pick up what we recognize */
3232 count = data[offset] + (data[offset + 1] << 8);
3233 offset += WPA_IE_SUITE_COUNT_LEN;
3234 /* Check for unicast suite(s) */
3235 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3236 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003237 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003238 goto exit;
3239 }
3240 for (i = 0; i < count; i++) {
3241 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3242 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003243 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003244 goto exit;
3245 }
3246 offset += TLV_OUI_LEN;
3247 switch (data[offset]) {
3248 case WPA_CIPHER_NONE:
3249 break;
3250 case WPA_CIPHER_WEP_40:
3251 case WPA_CIPHER_WEP_104:
3252 pval |= WEP_ENABLED;
3253 break;
3254 case WPA_CIPHER_TKIP:
3255 pval |= TKIP_ENABLED;
3256 break;
3257 case WPA_CIPHER_AES_CCM:
3258 pval |= AES_ENABLED;
3259 break;
3260 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003261 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003262 }
3263 offset++;
3264 }
3265 /* walk thru auth management suite list and pick up what we recognize */
3266 count = data[offset] + (data[offset + 1] << 8);
3267 offset += WPA_IE_SUITE_COUNT_LEN;
3268 /* Check for auth key management suite(s) */
3269 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3270 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003271 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003272 goto exit;
3273 }
3274 for (i = 0; i < count; i++) {
3275 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3276 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003277 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003278 goto exit;
3279 }
3280 offset += TLV_OUI_LEN;
3281 switch (data[offset]) {
3282 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003283 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003284 wpa_auth |= WPA_AUTH_NONE;
3285 break;
3286 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003287 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003288 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3289 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3290 break;
3291 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003292 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003293 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3294 (wpa_auth |= WPA_AUTH_PSK);
3295 break;
3296 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003297 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003298 }
3299 offset++;
3300 }
3301
3302 if (is_rsn_ie) {
3303 wme_bss_disable = 1;
3304 if ((offset + RSN_CAP_LEN) <= len) {
3305 rsn_cap = data[offset] + (data[offset + 1] << 8);
3306 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3307 wme_bss_disable = 0;
3308 }
3309 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003310 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003311 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003312 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003313 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003314 goto exit;
3315 }
3316 }
3317 /* FOR WPS , set SES_OW_ENABLED */
3318 wsec = (pval | gval | SES_OW_ENABLED);
3319
3320 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003321 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003322 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003323 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003324 goto exit;
3325 }
3326 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003327 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003328 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003329 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003330 goto exit;
3331 }
3332 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003333 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003334 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003335 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003336 goto exit;
3337 }
3338
3339exit:
3340 return err;
3341}
3342
3343static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003344brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003345 struct parsed_vndr_ies *vndr_ies)
3346{
3347 s32 err = 0;
3348 struct brcmf_vs_tlv *vndrie;
3349 struct brcmf_tlv *ie;
3350 struct parsed_vndr_ie_info *parsed_info;
3351 s32 remaining_len;
3352
3353 remaining_len = (s32)vndr_ie_len;
3354 memset(vndr_ies, 0, sizeof(*vndr_ies));
3355
3356 ie = (struct brcmf_tlv *)vndr_ie_buf;
3357 while (ie) {
3358 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3359 goto next;
3360 vndrie = (struct brcmf_vs_tlv *)ie;
3361 /* len should be bigger than OUI length + one */
3362 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003363 brcmf_err("invalid vndr ie. length is too small %d\n",
3364 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003365 goto next;
3366 }
3367 /* if wpa or wme ie, do not add ie */
3368 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3369 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3370 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003371 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003372 goto next;
3373 }
3374
3375 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3376
3377 /* save vndr ie information */
3378 parsed_info->ie_ptr = (char *)vndrie;
3379 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3380 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3381
3382 vndr_ies->count++;
3383
Arend van Sprield96b8012012-12-05 15:26:02 +01003384 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3385 parsed_info->vndrie.oui[0],
3386 parsed_info->vndrie.oui[1],
3387 parsed_info->vndrie.oui[2],
3388 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003389
Arend van Spriel9f440b72013-02-08 15:53:36 +01003390 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003391 break;
3392next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003393 remaining_len -= (ie->len + TLV_HDR_LEN);
3394 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003395 ie = NULL;
3396 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003397 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3398 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003399 }
3400 return err;
3401}
3402
3403static u32
3404brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3405{
3406
3407 __le32 iecount_le;
3408 __le32 pktflag_le;
3409
3410 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3411 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3412
3413 iecount_le = cpu_to_le32(1);
3414 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3415
3416 pktflag_le = cpu_to_le32(pktflag);
3417 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3418
3419 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3420
3421 return ie_len + VNDR_IE_HDR_SIZE;
3422}
3423
Arend van Spriel1332e262012-11-05 16:22:18 -08003424s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3425 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003426{
Arend van Spriel1332e262012-11-05 16:22:18 -08003427 struct brcmf_if *ifp;
3428 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003429 s32 err = 0;
3430 u8 *iovar_ie_buf;
3431 u8 *curr_ie_buf;
3432 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003433 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003434 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003435 u32 del_add_ie_buf_len = 0;
3436 u32 total_ie_buf_len = 0;
3437 u32 parsed_ie_buf_len = 0;
3438 struct parsed_vndr_ies old_vndr_ies;
3439 struct parsed_vndr_ies new_vndr_ies;
3440 struct parsed_vndr_ie_info *vndrie_info;
3441 s32 i;
3442 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003443 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003444
Arend van Spriel1332e262012-11-05 16:22:18 -08003445 if (!vif)
3446 return -ENODEV;
3447 ifp = vif->ifp;
3448 saved_ie = &vif->saved_ie;
3449
Arend van Sprield96b8012012-12-05 15:26:02 +01003450 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003451 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3452 if (!iovar_ie_buf)
3453 return -ENOMEM;
3454 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003455 switch (pktflag) {
3456 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3457 mgmt_ie_buf = saved_ie->probe_req_ie;
3458 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3459 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3460 break;
3461 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3462 mgmt_ie_buf = saved_ie->probe_res_ie;
3463 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3464 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3465 break;
3466 case BRCMF_VNDR_IE_BEACON_FLAG:
3467 mgmt_ie_buf = saved_ie->beacon_ie;
3468 mgmt_ie_len = &saved_ie->beacon_ie_len;
3469 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3470 break;
3471 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3472 mgmt_ie_buf = saved_ie->assoc_req_ie;
3473 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3474 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3475 break;
3476 default:
3477 err = -EPERM;
3478 brcmf_err("not suitable type\n");
3479 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003480 }
3481
3482 if (vndr_ie_len > mgmt_ie_buf_len) {
3483 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003484 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003485 goto exit;
3486 }
3487
3488 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3489 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3490 ptr = curr_ie_buf;
3491 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3492 for (i = 0; i < new_vndr_ies.count; i++) {
3493 vndrie_info = &new_vndr_ies.ie_info[i];
3494 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3495 vndrie_info->ie_len);
3496 parsed_ie_buf_len += vndrie_info->ie_len;
3497 }
3498 }
3499
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003500 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003501 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3502 (memcmp(mgmt_ie_buf, curr_ie_buf,
3503 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003504 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003505 goto exit;
3506 }
3507
3508 /* parse old vndr_ie */
3509 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3510
3511 /* make a command to delete old ie */
3512 for (i = 0; i < old_vndr_ies.count; i++) {
3513 vndrie_info = &old_vndr_ies.ie_info[i];
3514
Arend van Sprield96b8012012-12-05 15:26:02 +01003515 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3516 vndrie_info->vndrie.id,
3517 vndrie_info->vndrie.len,
3518 vndrie_info->vndrie.oui[0],
3519 vndrie_info->vndrie.oui[1],
3520 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003521
3522 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3523 vndrie_info->ie_ptr,
3524 vndrie_info->ie_len,
3525 "del");
3526 curr_ie_buf += del_add_ie_buf_len;
3527 total_ie_buf_len += del_add_ie_buf_len;
3528 }
3529 }
3530
3531 *mgmt_ie_len = 0;
3532 /* Add if there is any extra IE */
3533 if (mgmt_ie_buf && parsed_ie_buf_len) {
3534 ptr = mgmt_ie_buf;
3535
3536 remained_buf_len = mgmt_ie_buf_len;
3537
3538 /* make a command to add new ie */
3539 for (i = 0; i < new_vndr_ies.count; i++) {
3540 vndrie_info = &new_vndr_ies.ie_info[i];
3541
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003542 /* verify remained buf size before copy data */
3543 if (remained_buf_len < (vndrie_info->vndrie.len +
3544 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003545 brcmf_err("no space in mgmt_ie_buf: len left %d",
3546 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003547 break;
3548 }
3549 remained_buf_len -= (vndrie_info->ie_len +
3550 VNDR_IE_VSIE_OFFSET);
3551
Arend van Sprield96b8012012-12-05 15:26:02 +01003552 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3553 vndrie_info->vndrie.id,
3554 vndrie_info->vndrie.len,
3555 vndrie_info->vndrie.oui[0],
3556 vndrie_info->vndrie.oui[1],
3557 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003558
3559 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3560 vndrie_info->ie_ptr,
3561 vndrie_info->ie_len,
3562 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003563
3564 /* save the parsed IE in wl struct */
3565 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3566 vndrie_info->ie_len);
3567 *mgmt_ie_len += vndrie_info->ie_len;
3568
3569 curr_ie_buf += del_add_ie_buf_len;
3570 total_ie_buf_len += del_add_ie_buf_len;
3571 }
3572 }
3573 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003574 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003575 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003576 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003577 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003578 }
3579
3580exit:
3581 kfree(iovar_ie_buf);
3582 return err;
3583}
3584
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01003585s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
3586{
3587 s32 pktflags[] = {
3588 BRCMF_VNDR_IE_PRBREQ_FLAG,
3589 BRCMF_VNDR_IE_PRBRSP_FLAG,
3590 BRCMF_VNDR_IE_BEACON_FLAG
3591 };
3592 int i;
3593
3594 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
3595 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
3596
3597 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
3598 return 0;
3599}
3600
Hante Meuleman1a873342012-09-27 14:17:54 +02003601static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01003602brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
3603 struct cfg80211_beacon_data *beacon)
3604{
3605 s32 err;
3606
3607 /* Set Beacon IEs to FW */
3608 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
3609 beacon->tail, beacon->tail_len);
3610 if (err) {
3611 brcmf_err("Set Beacon IE Failed\n");
3612 return err;
3613 }
3614 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
3615
3616 /* Set Probe Response IEs to FW */
3617 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
3618 beacon->proberesp_ies,
3619 beacon->proberesp_ies_len);
3620 if (err)
3621 brcmf_err("Set Probe Resp IE Failed\n");
3622 else
3623 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
3624
3625 return err;
3626}
3627
3628static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02003629brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3630 struct cfg80211_ap_settings *settings)
3631{
3632 s32 ie_offset;
Arend van Sprielac24be62012-10-22 10:36:23 -07003633 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003634 struct brcmf_tlv *ssid_ie;
3635 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003636 s32 err = -EPERM;
3637 struct brcmf_tlv *rsn_ie;
3638 struct brcmf_vs_tlv *wpa_ie;
3639 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01003640 enum nl80211_iftype dev_role;
3641 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman1a873342012-09-27 14:17:54 +02003642
Arend van Sprield96b8012012-12-05 15:26:02 +01003643 brcmf_dbg(TRACE, "channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
3644 cfg80211_get_chandef_type(&settings->chandef),
3645 settings->beacon_interval,
3646 settings->dtim_period);
3647 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
3648 settings->ssid, settings->ssid_len, settings->auth_type,
3649 settings->inactivity_timeout);
Hante Meuleman1a873342012-09-27 14:17:54 +02003650
Hante Meuleman426d0a52013-02-08 15:53:53 +01003651 dev_role = ifp->vif->wdev.iftype;
Hante Meuleman1a873342012-09-27 14:17:54 +02003652
3653 memset(&ssid_le, 0, sizeof(ssid_le));
3654 if (settings->ssid == NULL || settings->ssid_len == 0) {
3655 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
3656 ssid_ie = brcmf_parse_tlvs(
3657 (u8 *)&settings->beacon.head[ie_offset],
3658 settings->beacon.head_len - ie_offset,
3659 WLAN_EID_SSID);
3660 if (!ssid_ie)
3661 return -EINVAL;
3662
3663 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
3664 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01003665 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02003666 } else {
3667 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
3668 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3669 }
3670
3671 brcmf_set_mpc(ndev, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003672
3673 /* find the RSN_IE */
3674 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
3675 settings->beacon.tail_len, WLAN_EID_RSN);
3676
3677 /* find the WPA_IE */
3678 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
3679 settings->beacon.tail_len);
3680
Hante Meuleman1a873342012-09-27 14:17:54 +02003681 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003682 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003683 if (wpa_ie != NULL) {
3684 /* WPA IE */
Arend van Spriel34778522012-11-05 16:22:19 -08003685 err = brcmf_configure_wpaie(ndev, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02003686 if (err < 0)
3687 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003688 } else {
3689 /* RSN IE */
3690 err = brcmf_configure_wpaie(ndev,
Arend van Spriel34778522012-11-05 16:22:19 -08003691 (struct brcmf_vs_tlv *)rsn_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02003692 if (err < 0)
3693 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003694 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003695 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01003696 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01003697 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02003698 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003699
Hante Meulemana0f07952013-02-08 15:53:47 +01003700 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02003701
3702 if (settings->beacon_interval) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003703 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003704 settings->beacon_interval);
Hante Meuleman1a873342012-09-27 14:17:54 +02003705 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003706 brcmf_err("Beacon Interval Set Error, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003707 goto exit;
3708 }
3709 }
3710 if (settings->dtim_period) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003711 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003712 settings->dtim_period);
Hante Meuleman1a873342012-09-27 14:17:54 +02003713 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003714 brcmf_err("DTIM Interval Set Error, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003715 goto exit;
3716 }
3717 }
Hante Meulemana0f07952013-02-08 15:53:47 +01003718
3719 if (dev_role == NL80211_IFTYPE_AP) {
3720 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
3721 if (err < 0) {
3722 brcmf_err("BRCMF_C_DOWN error %d\n", err);
3723 goto exit;
3724 }
Hante Meuleman2880b862013-02-08 12:06:31 +01003725 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003726 }
3727
Hante Meulemana0f07952013-02-08 15:53:47 +01003728 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003729 if (err < 0) {
Hante Meulemana0f07952013-02-08 15:53:47 +01003730 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003731 goto exit;
3732 }
Hante Meulemana0f07952013-02-08 15:53:47 +01003733 if (dev_role == NL80211_IFTYPE_AP) {
3734 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
3735 if (err < 0) {
3736 brcmf_err("setting AP mode failed %d\n", err);
3737 goto exit;
3738 }
3739 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
3740 if (err < 0) {
3741 brcmf_err("BRCMF_C_UP error (%d)\n", err);
3742 goto exit;
3743 }
3744
3745 memset(&join_params, 0, sizeof(join_params));
3746 /* join parameters starts with ssid */
3747 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
3748 /* create softap */
3749 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
3750 &join_params, sizeof(join_params));
3751 if (err < 0) {
3752 brcmf_err("SET SSID error (%d)\n", err);
3753 goto exit;
3754 }
3755 brcmf_dbg(TRACE, "AP mode configuration complete\n");
3756 } else {
3757 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
3758 sizeof(ssid_le));
3759 if (err < 0) {
3760 brcmf_err("setting ssid failed %d\n", err);
3761 goto exit;
3762 }
3763 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
3764 bss_enable.enable = cpu_to_le32(1);
3765 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
3766 sizeof(bss_enable));
3767 if (err < 0) {
3768 brcmf_err("bss_enable config failed %d\n", err);
3769 goto exit;
3770 }
3771
3772 brcmf_dbg(TRACE, "GO mode configuration complete\n");
3773 }
Arend van Sprielc1179032012-10-22 13:55:33 -07003774 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3775 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003776
3777exit:
3778 if (err)
3779 brcmf_set_mpc(ndev, 1);
3780 return err;
3781}
3782
3783static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
3784{
Arend van Sprielc1179032012-10-22 13:55:33 -07003785 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003786 s32 err = -EPERM;
Hante Meuleman426d0a52013-02-08 15:53:53 +01003787 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman1a873342012-09-27 14:17:54 +02003788
Arend van Sprield96b8012012-12-05 15:26:02 +01003789 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003790
Hante Meuleman426d0a52013-02-08 15:53:53 +01003791 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003792 /* Due to most likely deauths outstanding we sleep */
3793 /* first to make sure they get processed by fw. */
3794 msleep(400);
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003795 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003796 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003797 brcmf_err("setting AP mode failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003798 goto exit;
3799 }
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003800 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003801 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003802 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003803 goto exit;
3804 }
Hante Meuleman426d0a52013-02-08 15:53:53 +01003805 } else {
3806 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
3807 bss_enable.enable = cpu_to_le32(0);
3808 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
3809 sizeof(bss_enable));
3810 if (err < 0)
3811 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003812 }
Hante Meuleman426d0a52013-02-08 15:53:53 +01003813 brcmf_set_mpc(ndev, 1);
3814 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3815 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
3816
Hante Meuleman1a873342012-09-27 14:17:54 +02003817exit:
3818 return err;
3819}
3820
Hante Meulemana0f07952013-02-08 15:53:47 +01003821static s32
3822brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
3823 struct cfg80211_beacon_data *info)
3824{
Hante Meulemana0f07952013-02-08 15:53:47 +01003825 struct brcmf_if *ifp = netdev_priv(ndev);
3826 s32 err;
3827
3828 brcmf_dbg(TRACE, "Enter\n");
3829
Hante Meulemana0f07952013-02-08 15:53:47 +01003830 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
3831
3832 return err;
3833}
3834
Hante Meuleman1a873342012-09-27 14:17:54 +02003835static int
3836brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
3837 u8 *mac)
3838{
Hante Meulemana0f07952013-02-08 15:53:47 +01003839 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02003840 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003841 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003842 s32 err;
3843
3844 if (!mac)
3845 return -EFAULT;
3846
Arend van Sprield96b8012012-12-05 15:26:02 +01003847 brcmf_dbg(TRACE, "Enter %pM\n", mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02003848
Hante Meulemana0f07952013-02-08 15:53:47 +01003849 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
3850 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07003851 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02003852 return -EIO;
3853
3854 memcpy(&scbval.ea, mac, ETH_ALEN);
3855 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003856 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003857 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02003858 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003859 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01003860
Arend van Sprield96b8012012-12-05 15:26:02 +01003861 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003862 return err;
3863}
3864
Hante Meuleman0de8aac2013-02-08 15:53:38 +01003865
3866static void
3867brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
3868 struct wireless_dev *wdev,
3869 u16 frame_type, bool reg)
3870{
3871 struct brcmf_if *ifp = netdev_priv(wdev->netdev);
3872 struct brcmf_cfg80211_vif *vif = ifp->vif;
3873 u16 mgmt_type;
3874
3875 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
3876
3877 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
3878 if (reg)
3879 vif->mgmt_rx_reg |= BIT(mgmt_type);
3880 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01003881 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01003882}
3883
3884
3885static int
3886brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
3887 struct ieee80211_channel *chan, bool offchan,
3888 unsigned int wait, const u8 *buf, size_t len,
3889 bool no_cck, bool dont_wait_for_ack, u64 *cookie)
3890{
3891 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3892 const struct ieee80211_mgmt *mgmt;
Hante Meulemana0f07952013-02-08 15:53:47 +01003893 struct brcmf_if *ifp;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01003894 struct brcmf_cfg80211_vif *vif;
3895 s32 err = 0;
3896 s32 ie_offset;
3897 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01003898 struct brcmf_fil_action_frame_le *action_frame;
3899 struct brcmf_fil_af_params_le *af_params;
3900 bool ack;
3901 s32 chan_nr;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01003902
3903 brcmf_dbg(TRACE, "Enter\n");
3904
3905 *cookie = 0;
3906
3907 mgmt = (const struct ieee80211_mgmt *)buf;
3908
Hante Meulemana0f07952013-02-08 15:53:47 +01003909 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
3910 brcmf_err("Driver only allows MGMT packet type\n");
3911 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01003912 }
Hante Meulemana0f07952013-02-08 15:53:47 +01003913
3914 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
3915 /* Right now the only reason to get a probe response */
3916 /* is for p2p listen response or for p2p GO from */
3917 /* wpa_supplicant. Unfortunately the probe is send */
3918 /* on primary ndev, while dongle wants it on the p2p */
3919 /* vif. Since this is only reason for a probe */
3920 /* response to be sent, the vif is taken from cfg. */
3921 /* If ever desired to send proberesp for non p2p */
3922 /* response then data should be checked for */
3923 /* "DIRECT-". Note in future supplicant will take */
3924 /* dedicated p2p wdev to do this and then this 'hack'*/
3925 /* is not needed anymore. */
3926 ie_offset = DOT11_MGMT_HDR_LEN +
3927 DOT11_BCN_PRB_FIXED_LEN;
3928 ie_len = len - ie_offset;
3929 ifp = netdev_priv(wdev->netdev);
3930 vif = ifp->vif;
3931 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
3932 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
3933 err = brcmf_vif_set_mgmt_ie(vif,
3934 BRCMF_VNDR_IE_PRBRSP_FLAG,
3935 &buf[ie_offset],
3936 ie_len);
3937 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
3938 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01003939 } else if (ieee80211_is_action(mgmt->frame_control)) {
3940 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
3941 if (af_params == NULL) {
3942 brcmf_err("unable to allocate frame\n");
3943 err = -ENOMEM;
3944 goto exit;
3945 }
3946 action_frame = &af_params->action_frame;
3947 /* Add the packet Id */
3948 action_frame->packet_id = cpu_to_le32(*cookie);
3949 /* Add BSSID */
3950 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
3951 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
3952 /* Add the length exepted for 802.11 header */
3953 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
3954 /* Add the channel */
3955 chan_nr = ieee80211_frequency_to_channel(chan->center_freq);
3956 af_params->channel = cpu_to_le32(chan_nr);
3957
3958 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
3959 le16_to_cpu(action_frame->len));
3960
3961 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
3962 *cookie, le16_to_cpu(action_frame->len),
3963 chan->center_freq);
3964
3965 ack = brcmf_p2p_send_action_frame(cfg, wdev->netdev,
3966 af_params);
3967
3968 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
3969 GFP_KERNEL);
3970 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01003971 } else {
3972 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
3973 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
3974 }
3975
Hante Meuleman18e2f612013-02-08 15:53:49 +01003976exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01003977 return err;
3978}
3979
3980
3981static int
3982brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
3983 struct wireless_dev *wdev,
3984 u64 cookie)
3985{
3986 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3987 struct brcmf_cfg80211_vif *vif;
3988 int err = 0;
3989
3990 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
3991
3992 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
3993 if (vif == NULL) {
3994 brcmf_err("No p2p device available for probe response\n");
3995 err = -ENODEV;
3996 goto exit;
3997 }
3998 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
3999exit:
4000 return err;
4001}
4002
Arend van Spriel5b435de2011-10-05 13:19:03 +02004003static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004004 .add_virtual_intf = brcmf_cfg80211_add_iface,
4005 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004006 .change_virtual_intf = brcmf_cfg80211_change_iface,
4007 .scan = brcmf_cfg80211_scan,
4008 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4009 .join_ibss = brcmf_cfg80211_join_ibss,
4010 .leave_ibss = brcmf_cfg80211_leave_ibss,
4011 .get_station = brcmf_cfg80211_get_station,
4012 .set_tx_power = brcmf_cfg80211_set_tx_power,
4013 .get_tx_power = brcmf_cfg80211_get_tx_power,
4014 .add_key = brcmf_cfg80211_add_key,
4015 .del_key = brcmf_cfg80211_del_key,
4016 .get_key = brcmf_cfg80211_get_key,
4017 .set_default_key = brcmf_cfg80211_config_default_key,
4018 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4019 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004020 .connect = brcmf_cfg80211_connect,
4021 .disconnect = brcmf_cfg80211_disconnect,
4022 .suspend = brcmf_cfg80211_suspend,
4023 .resume = brcmf_cfg80211_resume,
4024 .set_pmksa = brcmf_cfg80211_set_pmksa,
4025 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004026 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004027 .start_ap = brcmf_cfg80211_start_ap,
4028 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004029 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004030 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004031 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4032 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004033 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4034 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4035 .remain_on_channel = brcmf_p2p_remain_on_channel,
4036 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004037#ifdef CONFIG_NL80211_TESTMODE
4038 .testmode_cmd = brcmf_cfg80211_testmode
4039#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02004040};
4041
Arend van Spriel9f440b72013-02-08 15:53:36 +01004042static s32 brcmf_nl80211_iftype_to_mode(enum nl80211_iftype type)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004043{
Arend van Spriel9f440b72013-02-08 15:53:36 +01004044 switch (type) {
4045 case NL80211_IFTYPE_AP_VLAN:
4046 case NL80211_IFTYPE_WDS:
4047 case NL80211_IFTYPE_MONITOR:
4048 case NL80211_IFTYPE_MESH_POINT:
4049 return -ENOTSUPP;
4050 case NL80211_IFTYPE_ADHOC:
4051 return WL_MODE_IBSS;
4052 case NL80211_IFTYPE_STATION:
4053 case NL80211_IFTYPE_P2P_CLIENT:
4054 return WL_MODE_BSS;
4055 case NL80211_IFTYPE_AP:
4056 case NL80211_IFTYPE_P2P_GO:
4057 return WL_MODE_AP;
4058 case NL80211_IFTYPE_P2P_DEVICE:
4059 return WL_MODE_P2P;
4060 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02004061 default:
Arend van Spriel9f440b72013-02-08 15:53:36 +01004062 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004063 }
4064
Arend van Spriel9f440b72013-02-08 15:53:36 +01004065 return -EINVAL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004066}
4067
Arend van Spriele5806072012-09-19 22:21:08 +02004068static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
4069{
Arend van Spriele5806072012-09-19 22:21:08 +02004070 /* scheduled scan settings */
4071 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
4072 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
4073 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
4074 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
Arend van Spriele5806072012-09-19 22:21:08 +02004075}
4076
Arend van Spriel9f440b72013-02-08 15:53:36 +01004077static const struct ieee80211_iface_limit brcmf_iface_limits[] = {
4078 {
Hante Meulemandded3d52013-02-08 15:53:57 +01004079 .max = 2,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004080 .types = BIT(NL80211_IFTYPE_STATION) |
4081 BIT(NL80211_IFTYPE_ADHOC) |
4082 BIT(NL80211_IFTYPE_AP)
4083 },
4084 {
4085 .max = 1,
Hante Meulemandded3d52013-02-08 15:53:57 +01004086 .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
4087 },
4088 {
4089 .max = 1,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004090 .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
4091 BIT(NL80211_IFTYPE_P2P_GO)
4092 },
4093};
4094static const struct ieee80211_iface_combination brcmf_iface_combos[] = {
4095 {
Hante Meulemandded3d52013-02-08 15:53:57 +01004096 .max_interfaces = BRCMF_IFACE_MAX_CNT,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004097 .num_different_channels = 1, /* no multi-channel for now */
4098 .n_limits = ARRAY_SIZE(brcmf_iface_limits),
4099 .limits = brcmf_iface_limits
4100 }
4101};
4102
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004103static const struct ieee80211_txrx_stypes
4104brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
4105 [NL80211_IFTYPE_STATION] = {
4106 .tx = 0xffff,
4107 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
4108 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
4109 },
4110 [NL80211_IFTYPE_P2P_CLIENT] = {
4111 .tx = 0xffff,
4112 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
4113 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
4114 },
4115 [NL80211_IFTYPE_P2P_GO] = {
4116 .tx = 0xffff,
4117 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
4118 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
4119 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
4120 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
4121 BIT(IEEE80211_STYPE_AUTH >> 4) |
4122 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
4123 BIT(IEEE80211_STYPE_ACTION >> 4)
4124 }
4125};
4126
Arend van Spriel3eacf862012-10-22 13:55:30 -07004127static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004128{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004129 struct wiphy *wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004130 s32 err = 0;
4131
Arend van Spriel3eacf862012-10-22 13:55:30 -07004132 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
4133 if (!wiphy) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004134 brcmf_err("Could not allocate wiphy device\n");
Arend van Spriel3eacf862012-10-22 13:55:30 -07004135 return ERR_PTR(-ENOMEM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004136 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07004137 set_wiphy_dev(wiphy, phydev);
4138 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004139 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004140 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
4141 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
4142 BIT(NL80211_IFTYPE_ADHOC) |
Arend van Spriel9f440b72013-02-08 15:53:36 +01004143 BIT(NL80211_IFTYPE_AP) |
4144 BIT(NL80211_IFTYPE_P2P_CLIENT) |
Hante Meulemandded3d52013-02-08 15:53:57 +01004145 BIT(NL80211_IFTYPE_P2P_GO) |
4146 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel9f440b72013-02-08 15:53:36 +01004147 wiphy->iface_combinations = brcmf_iface_combos;
4148 wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004149 wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004150 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
4151 wiphy->cipher_suites = __wl_cipher_suites;
4152 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004153 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
Hante Meuleman6eda4e22013-02-08 15:54:02 +01004154 WIPHY_FLAG_OFFCHAN_TX |
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004155 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
4156 wiphy->mgmt_stypes = brcmf_txrx_stypes;
4157 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004158 brcmf_wiphy_pno_params(wiphy);
Hante Meulemand48200b2013-04-03 12:40:29 +02004159 brcmf_dbg(INFO, "Registering custom regulatory\n");
4160 wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY;
4161 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004162 err = wiphy_register(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004163 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004164 brcmf_err("Could not register wiphy device (%d)\n", err);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004165 wiphy_free(wiphy);
4166 return ERR_PTR(err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004167 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07004168 return wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004169}
4170
Arend van Spriel3eacf862012-10-22 13:55:30 -07004171struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004172 enum nl80211_iftype type,
4173 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004174{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004175 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004176
Arend van Spriel3eacf862012-10-22 13:55:30 -07004177 if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT)
4178 return ERR_PTR(-ENOSPC);
4179
Arend van Spriel33a6b152013-02-08 15:53:39 +01004180 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004181 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004182 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4183 if (!vif)
4184 return ERR_PTR(-ENOMEM);
4185
4186 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004187 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004188
Arend van Spriel9f440b72013-02-08 15:53:36 +01004189 vif->mode = brcmf_nl80211_iftype_to_mode(type);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004190 vif->pm_block = pm_block;
4191 vif->roam_off = -1;
4192
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004193 brcmf_init_prof(&vif->profile);
4194
Arend van Spriel3eacf862012-10-22 13:55:30 -07004195 list_add_tail(&vif->list, &cfg->vif_list);
4196 cfg->vif_cnt++;
4197 return vif;
4198}
4199
Arend van Spriel9f440b72013-02-08 15:53:36 +01004200void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004201{
4202 struct brcmf_cfg80211_info *cfg;
4203 struct wiphy *wiphy;
4204
4205 wiphy = vif->wdev.wiphy;
4206 cfg = wiphy_priv(wiphy);
4207 list_del(&vif->list);
4208 cfg->vif_cnt--;
4209
4210 kfree(vif);
4211 if (!cfg->vif_cnt) {
4212 wiphy_unregister(wiphy);
4213 wiphy_free(wiphy);
4214 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004215}
4216
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004217static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004218{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004219 u32 event = e->event_code;
4220 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004221
4222 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004223 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004224 return true;
4225 }
4226
4227 return false;
4228}
4229
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004230static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004231{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004232 u32 event = e->event_code;
4233 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004234
4235 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004236 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004237 return true;
4238 }
4239 return false;
4240}
4241
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004242static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004243 const struct brcmf_event_msg *e)
4244{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004245 u32 event = e->event_code;
4246 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004247
4248 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004249 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4250 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004251 return true;
4252 }
4253
4254 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004255 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004256 return true;
4257 }
4258
4259 return false;
4260}
4261
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004262static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004263{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004264 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004265
4266 kfree(conn_info->req_ie);
4267 conn_info->req_ie = NULL;
4268 conn_info->req_ie_len = 0;
4269 kfree(conn_info->resp_ie);
4270 conn_info->resp_ie = NULL;
4271 conn_info->resp_ie_len = 0;
4272}
4273
Hante Meuleman89286dc2013-02-08 15:53:46 +01004274static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4275 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004276{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004277 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004278 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004279 u32 req_len;
4280 u32 resp_len;
4281 s32 err = 0;
4282
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004283 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004284
Arend van Sprielac24be62012-10-22 10:36:23 -07004285 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4286 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004287 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004288 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004289 return err;
4290 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004291 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004292 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004293 req_len = le32_to_cpu(assoc_info->req_len);
4294 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004295 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004296 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004297 cfg->extra_buf,
4298 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004299 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004300 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004301 return err;
4302 }
4303 conn_info->req_ie_len = req_len;
4304 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004305 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004306 GFP_KERNEL);
4307 } else {
4308 conn_info->req_ie_len = 0;
4309 conn_info->req_ie = NULL;
4310 }
4311 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004312 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004313 cfg->extra_buf,
4314 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004315 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004316 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004317 return err;
4318 }
4319 conn_info->resp_ie_len = resp_len;
4320 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004321 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004322 GFP_KERNEL);
4323 } else {
4324 conn_info->resp_ie_len = 0;
4325 conn_info->resp_ie = NULL;
4326 }
Arend van Spriel16886732012-12-05 15:26:04 +01004327 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4328 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004329
4330 return err;
4331}
4332
4333static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004334brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004335 struct net_device *ndev,
4336 const struct brcmf_event_msg *e)
4337{
Arend van Sprielc1179032012-10-22 13:55:33 -07004338 struct brcmf_if *ifp = netdev_priv(ndev);
4339 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004340 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4341 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004342 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004343 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004344 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004345 u32 freq;
4346 s32 err = 0;
4347 u32 target_channel;
Franky Lina180b832012-10-10 11:13:09 -07004348 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004349
Arend van Sprield96b8012012-12-05 15:26:02 +01004350 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004351
Hante Meuleman89286dc2013-02-08 15:53:46 +01004352 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004353 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004354 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004355
Franky Lina180b832012-10-10 11:13:09 -07004356 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4357 if (buf == NULL) {
4358 err = -ENOMEM;
4359 goto done;
4360 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004361
Franky Lina180b832012-10-10 11:13:09 -07004362 /* data sent to dongle has to be little endian */
4363 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004364 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004365 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004366
4367 if (err)
4368 goto done;
4369
4370 bi = (struct brcmf_bss_info_le *)(buf + 4);
4371 target_channel = bi->ctl_ch ? bi->ctl_ch :
4372 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004373
4374 if (target_channel <= CH_MAX_2G_CHANNEL)
4375 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4376 else
4377 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4378
4379 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4380 notify_channel = ieee80211_get_channel(wiphy, freq);
4381
Franky Lina180b832012-10-10 11:13:09 -07004382done:
4383 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004384 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004385 conn_info->req_ie, conn_info->req_ie_len,
4386 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004387 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004388
Arend van Sprielc1179032012-10-22 13:55:33 -07004389 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004390 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004391 return err;
4392}
4393
4394static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004395brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004396 struct net_device *ndev, const struct brcmf_event_msg *e,
4397 bool completed)
4398{
Arend van Sprielc1179032012-10-22 13:55:33 -07004399 struct brcmf_if *ifp = netdev_priv(ndev);
4400 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004401 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004402 s32 err = 0;
4403
Arend van Sprield96b8012012-12-05 15:26:02 +01004404 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004405
Arend van Sprielc1179032012-10-22 13:55:33 -07004406 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4407 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004408 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004409 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004410 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004411 brcmf_update_bss_info(cfg, ifp);
4412 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4413 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004414 }
4415 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004416 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004417 conn_info->req_ie,
4418 conn_info->req_ie_len,
4419 conn_info->resp_ie,
4420 conn_info->resp_ie_len,
4421 completed ? WLAN_STATUS_SUCCESS :
4422 WLAN_STATUS_AUTH_TIMEOUT,
4423 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004424 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4425 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004426 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004427 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004428 return err;
4429}
4430
4431static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004432brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004433 struct net_device *ndev,
4434 const struct brcmf_event_msg *e, void *data)
4435{
Hante Meuleman7ee29602013-02-06 18:40:43 +01004436 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004437 u32 event = e->event_code;
4438 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004439 struct station_info sinfo;
4440
Arend van Spriel16886732012-12-05 15:26:04 +01004441 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004442 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4443 ndev != cfg_to_ndev(cfg)) {
4444 brcmf_dbg(CONN, "AP mode link down\n");
4445 complete(&cfg->vif_disabled);
4446 return 0;
4447 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004448
Hante Meuleman1a873342012-09-27 14:17:54 +02004449 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004450 (reason == BRCMF_E_STATUS_SUCCESS)) {
4451 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004452 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4453 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004454 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004455 return -EINVAL;
4456 }
4457 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004458 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004459 generation++;
4460 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004461 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004462 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4463 (event == BRCMF_E_DEAUTH_IND) ||
4464 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004465 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004466 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004467 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004468}
4469
4470static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004471brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004472 const struct brcmf_event_msg *e, void *data)
4473{
Arend van Spriel19937322012-11-05 16:22:32 -08004474 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4475 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004476 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004477 s32 err = 0;
4478
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004479 if (ifp->vif->mode == WL_MODE_AP) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004480 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004481 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004482 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004483 if (brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004484 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004485 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004486 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004487 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4488 &ifp->vif->sme_state);
4489 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4490 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004491 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004492 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004493 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004494 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004495 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004496 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Sprielc1179032012-10-22 13:55:33 -07004497 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004498 &ifp->vif->sme_state))
Arend van Spriel5b435de2011-10-05 13:19:03 +02004499 cfg80211_disconnected(ndev, 0, NULL, 0,
Arend van Sprielc1179032012-10-22 13:55:33 -07004500 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004501 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004502 brcmf_link_down(ifp->vif);
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004503 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004504 if (ndev != cfg_to_ndev(cfg))
4505 complete(&cfg->vif_disabled);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004506 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004507 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07004508 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4509 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004510 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004511 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004512 }
4513
4514 return err;
4515}
4516
4517static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004518brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004519 const struct brcmf_event_msg *e, void *data)
4520{
Arend van Spriel19937322012-11-05 16:22:32 -08004521 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004522 s32 err = 0;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004523 u32 event = e->event_code;
4524 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004525
4526 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004527 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08004528 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004529 else
Arend van Spriel19937322012-11-05 16:22:32 -08004530 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004531 }
4532
4533 return err;
4534}
4535
4536static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004537brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004538 const struct brcmf_event_msg *e, void *data)
4539{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004540 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004541 enum nl80211_key_type key_type;
4542
4543 if (flags & BRCMF_EVENT_MSG_GROUP)
4544 key_type = NL80211_KEYTYPE_GROUP;
4545 else
4546 key_type = NL80211_KEYTYPE_PAIRWISE;
4547
Arend van Spriel19937322012-11-05 16:22:32 -08004548 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004549 NULL, GFP_KERNEL);
4550
4551 return 0;
4552}
4553
Arend van Sprield3c0b632013-02-08 15:53:37 +01004554static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
4555 const struct brcmf_event_msg *e, void *data)
4556{
4557 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4558 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
4559 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
4560 struct brcmf_cfg80211_vif *vif;
4561
4562 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
4563 ifevent->action, ifevent->flags, ifevent->ifidx,
4564 ifevent->bssidx);
4565
Arend van Sprield3c0b632013-02-08 15:53:37 +01004566 mutex_lock(&event->vif_event_lock);
4567 event->action = ifevent->action;
4568 vif = event->vif;
4569
4570 switch (ifevent->action) {
4571 case BRCMF_E_IF_ADD:
4572 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08004573 if (!cfg->vif_event.vif) {
4574 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01004575 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08004576 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01004577
4578 ifp->vif = vif;
4579 vif->ifp = ifp;
4580 vif->wdev.netdev = ifp->ndev;
4581 ifp->ndev->ieee80211_ptr = &vif->wdev;
4582 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
4583 mutex_unlock(&event->vif_event_lock);
4584 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01004585 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01004586
4587 case BRCMF_E_IF_DEL:
4588 ifp->vif = NULL;
Arend van Sprield3c0b632013-02-08 15:53:37 +01004589 mutex_unlock(&event->vif_event_lock);
4590 /* event may not be upon user request */
4591 if (brcmf_cfg80211_vif_event_armed(cfg))
4592 wake_up(&event->vif_wq);
4593 return 0;
4594
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01004595 case BRCMF_E_IF_CHANGE:
4596 mutex_unlock(&event->vif_event_lock);
4597 wake_up(&event->vif_wq);
4598 return 0;
4599
Arend van Sprield3c0b632013-02-08 15:53:37 +01004600 default:
4601 mutex_unlock(&event->vif_event_lock);
4602 break;
4603 }
4604 return -EINVAL;
4605}
4606
Arend van Spriel5b435de2011-10-05 13:19:03 +02004607static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4608{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004609 conf->frag_threshold = (u32)-1;
4610 conf->rts_threshold = (u32)-1;
4611 conf->retry_short = (u32)-1;
4612 conf->retry_long = (u32)-1;
4613 conf->tx_power = -1;
4614}
4615
Arend van Spriel5c36b992012-11-14 18:46:05 -08004616static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004617{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004618 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
4619 brcmf_notify_connect_status);
4620 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
4621 brcmf_notify_connect_status);
4622 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
4623 brcmf_notify_connect_status);
4624 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
4625 brcmf_notify_connect_status);
4626 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
4627 brcmf_notify_connect_status);
4628 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
4629 brcmf_notify_connect_status);
4630 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
4631 brcmf_notify_roaming_status);
4632 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
4633 brcmf_notify_mic_status);
4634 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
4635 brcmf_notify_connect_status);
4636 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
4637 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01004638 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
4639 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004640 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01004641 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004642 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
4643 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01004644 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
4645 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004646 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
4647 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01004648 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
4649 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004650}
4651
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004652static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004653{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004654 kfree(cfg->conf);
4655 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004656 kfree(cfg->escan_ioctl_buf);
4657 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004658 kfree(cfg->extra_buf);
4659 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004660 kfree(cfg->pmk_list);
4661 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004662}
4663
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004664static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004665{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004666 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4667 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004668 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004669 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4670 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004671 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004672 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4673 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004674 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004675 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4676 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004677 goto init_priv_mem_out;
4678
4679 return 0;
4680
4681init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004682 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004683
4684 return -ENOMEM;
4685}
4686
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004687static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004688{
4689 s32 err = 0;
4690
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004691 cfg->scan_request = NULL;
4692 cfg->pwr_save = true;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004693 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004694 we enable roam per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004695 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02004696 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004697 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004698 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004699 if (err)
4700 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004701 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004702 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004703 brcmf_init_escan(cfg);
4704 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004705 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004706 return err;
4707}
4708
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004709static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004710{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004711 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004712 brcmf_abort_scanning(cfg);
4713 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004714}
4715
Arend van Sprield3c0b632013-02-08 15:53:37 +01004716static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
4717{
4718 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01004719 mutex_init(&event->vif_event_lock);
4720}
4721
Arend van Sprield9cb2592012-12-05 15:25:54 +01004722struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
4723 struct device *busdev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004724{
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004725 struct net_device *ndev = drvr->iflist[0]->ndev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004726 struct brcmf_cfg80211_info *cfg;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004727 struct wiphy *wiphy;
4728 struct brcmf_cfg80211_vif *vif;
4729 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004730 s32 err = 0;
4731
4732 if (!ndev) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004733 brcmf_err("ndev is invalid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004734 return NULL;
4735 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004736
Arend van Spriel3eacf862012-10-22 13:55:30 -07004737 ifp = netdev_priv(ndev);
4738 wiphy = brcmf_setup_wiphy(busdev);
4739 if (IS_ERR(wiphy))
4740 return NULL;
4741
4742 cfg = wiphy_priv(wiphy);
4743 cfg->wiphy = wiphy;
4744 cfg->pub = drvr;
Arend van Sprield3c0b632013-02-08 15:53:37 +01004745 init_vif_event(&cfg->vif_event);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004746 INIT_LIST_HEAD(&cfg->vif_list);
4747
Arend van Sprield3c0b632013-02-08 15:53:37 +01004748 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004749 if (IS_ERR(vif)) {
4750 wiphy_free(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004751 return NULL;
4752 }
4753
Arend van Sprield3c0b632013-02-08 15:53:37 +01004754 vif->ifp = ifp;
4755 vif->wdev.netdev = ndev;
4756 ndev->ieee80211_ptr = &vif->wdev;
4757 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
4758
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004759 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004760 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004761 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004762 goto cfg80211_attach_out;
4763 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07004764 ifp->vif = vif;
Hante Meuleman2fde59d2013-02-08 15:53:52 +01004765
4766 err = brcmf_p2p_attach(cfg);
4767 if (err) {
4768 brcmf_err("P2P initilisation failed (%d)\n", err);
4769 goto cfg80211_p2p_attach_out;
4770 }
4771
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004772 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004773
Hante Meuleman2fde59d2013-02-08 15:53:52 +01004774cfg80211_p2p_attach_out:
4775 wl_deinit_priv(cfg);
4776
Arend van Spriel5b435de2011-10-05 13:19:03 +02004777cfg80211_attach_out:
Arend van Spriel3eacf862012-10-22 13:55:30 -07004778 brcmf_free_vif(vif);
Hante Meuleman2880b862013-02-08 12:06:31 +01004779 wiphy_free(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004780 return NULL;
4781}
4782
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004783void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004784{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004785 struct brcmf_cfg80211_vif *vif;
4786 struct brcmf_cfg80211_vif *tmp;
4787
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004788 wl_deinit_priv(cfg);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004789 list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) {
4790 brcmf_free_vif(vif);
4791 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004792}
4793
Arend van Spriel5b435de2011-10-05 13:19:03 +02004794static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01004795brcmf_dongle_roam(struct brcmf_if *ifp, u32 roamvar, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004796{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004797 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02004798 __le32 roamtrigger[2];
4799 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02004800
4801 /*
4802 * Setup timeout if Beacons are lost and roam is
4803 * off to report link down
4804 */
4805 if (roamvar) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004806 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004807 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004808 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004809 goto dongle_rom_out;
4810 }
4811 }
4812
4813 /*
4814 * Enable/Disable built-in roaming to allow supplicant
4815 * to take care of roaming
4816 */
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004817 brcmf_dbg(INFO, "Internal Roaming = %s\n", roamvar ? "Off" : "On");
Arend van Sprielac24be62012-10-22 10:36:23 -07004818 err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004819 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004820 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004821 goto dongle_rom_out;
4822 }
4823
Arend van Sprielf588bc02011-10-12 20:51:22 +02004824 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
4825 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004826 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004827 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004828 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004829 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004830 goto dongle_rom_out;
4831 }
4832
Arend van Sprielf588bc02011-10-12 20:51:22 +02004833 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
4834 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004835 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004836 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004837 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004838 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004839 goto dongle_rom_out;
4840 }
4841
4842dongle_rom_out:
4843 return err;
4844}
4845
4846static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01004847brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02004848 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004849{
4850 s32 err = 0;
4851
Arend van Sprielac24be62012-10-22 10:36:23 -07004852 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004853 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004854 if (err) {
4855 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004856 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004857 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01004858 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004859 goto dongle_scantime_out;
4860 }
Arend van Sprielac24be62012-10-22 10:36:23 -07004861 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004862 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004863 if (err) {
4864 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004865 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004866 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01004867 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004868 goto dongle_scantime_out;
4869 }
4870
Arend van Sprielac24be62012-10-22 10:36:23 -07004871 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004872 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004873 if (err) {
4874 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004875 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004876 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01004877 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004878 goto dongle_scantime_out;
4879 }
4880
4881dongle_scantime_out:
4882 return err;
4883}
4884
Hante Meulemand48200b2013-04-03 12:40:29 +02004885
4886static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, u32 bw_cap)
4887{
4888 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
4889 struct ieee80211_channel *band_chan_arr;
4890 struct brcmf_chanspec_list *list;
4891 s32 err;
4892 u8 *pbuf;
4893 u32 i, j;
4894 u32 total;
4895 u16 chanspec;
4896 enum ieee80211_band band;
4897 u32 channel;
4898 u32 *n_cnt;
4899 bool ht40_allowed;
4900 u32 index;
4901 u32 ht40_flag;
4902 bool update;
4903 u32 array_size;
4904
4905 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4906
4907 if (pbuf == NULL)
4908 return -ENOMEM;
4909
4910 list = (struct brcmf_chanspec_list *)pbuf;
4911
4912 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
4913 BRCMF_DCMD_MEDLEN);
4914 if (err) {
4915 brcmf_err("get chanspecs error (%d)\n", err);
4916 goto exit;
4917 }
4918
4919 __wl_band_2ghz.n_channels = 0;
4920 __wl_band_5ghz_a.n_channels = 0;
4921
4922 total = le32_to_cpu(list->count);
4923 for (i = 0; i < total; i++) {
4924 chanspec = (u16)le32_to_cpu(list->element[i]);
4925 channel = CHSPEC_CHANNEL(chanspec);
4926
4927 if (CHSPEC_IS40(chanspec)) {
4928 if (CHSPEC_SB_UPPER(chanspec))
4929 channel += CH_10MHZ_APART;
4930 else
4931 channel -= CH_10MHZ_APART;
4932 } else if (CHSPEC_IS80(chanspec)) {
4933 brcmf_dbg(INFO, "HT80 center channel : %d\n",
4934 channel);
4935 continue;
4936 }
4937 if (CHSPEC_IS2G(chanspec) && (channel >= CH_MIN_2G_CHANNEL) &&
4938 (channel <= CH_MAX_2G_CHANNEL)) {
4939 band_chan_arr = __wl_2ghz_channels;
4940 array_size = ARRAY_SIZE(__wl_2ghz_channels);
4941 n_cnt = &__wl_band_2ghz.n_channels;
4942 band = IEEE80211_BAND_2GHZ;
4943 ht40_allowed = (bw_cap == WLC_N_BW_40ALL);
4944 } else if (CHSPEC_IS5G(chanspec) &&
4945 channel >= CH_MIN_5G_CHANNEL) {
4946 band_chan_arr = __wl_5ghz_a_channels;
4947 array_size = ARRAY_SIZE(__wl_5ghz_a_channels);
4948 n_cnt = &__wl_band_5ghz_a.n_channels;
4949 band = IEEE80211_BAND_5GHZ;
4950 ht40_allowed = !(bw_cap == WLC_N_BW_20ALL);
4951 } else {
4952 brcmf_err("Invalid channel Sepc. 0x%x.\n", chanspec);
4953 continue;
4954 }
4955 if (!ht40_allowed && CHSPEC_IS40(chanspec))
4956 continue;
4957 update = false;
4958 for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) {
4959 if (band_chan_arr[j].hw_value == channel) {
4960 update = true;
4961 break;
4962 }
4963 }
4964 if (update)
4965 index = j;
4966 else
4967 index = *n_cnt;
4968 if (index < array_size) {
4969 band_chan_arr[index].center_freq =
4970 ieee80211_channel_to_frequency(channel, band);
4971 band_chan_arr[index].hw_value = channel;
4972
4973 if (CHSPEC_IS40(chanspec) && ht40_allowed) {
4974 /* assuming the order is HT20, HT40 Upper,
4975 * HT40 lower from chanspecs
4976 */
4977 ht40_flag = band_chan_arr[index].flags &
4978 IEEE80211_CHAN_NO_HT40;
4979 if (CHSPEC_SB_UPPER(chanspec)) {
4980 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
4981 band_chan_arr[index].flags &=
4982 ~IEEE80211_CHAN_NO_HT40;
4983 band_chan_arr[index].flags |=
4984 IEEE80211_CHAN_NO_HT40PLUS;
4985 } else {
4986 /* It should be one of
4987 * IEEE80211_CHAN_NO_HT40 or
4988 * IEEE80211_CHAN_NO_HT40PLUS
4989 */
4990 band_chan_arr[index].flags &=
4991 ~IEEE80211_CHAN_NO_HT40;
4992 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
4993 band_chan_arr[index].flags |=
4994 IEEE80211_CHAN_NO_HT40MINUS;
4995 }
4996 } else {
4997 band_chan_arr[index].flags =
4998 IEEE80211_CHAN_NO_HT40;
4999 if (band == IEEE80211_BAND_2GHZ)
5000 channel |= WL_CHANSPEC_BAND_2G;
5001 else
5002 channel |= WL_CHANSPEC_BAND_5G;
5003 channel |= WL_CHANSPEC_BW_20;
5004 err = brcmf_fil_bsscfg_int_get(ifp,
5005 "per_chan_info",
5006 &channel);
5007 if (!err) {
5008 if (channel & WL_CHAN_RADAR)
5009 band_chan_arr[index].flags |=
5010 (IEEE80211_CHAN_RADAR |
5011 IEEE80211_CHAN_NO_IBSS);
5012 if (channel & WL_CHAN_PASSIVE)
5013 band_chan_arr[index].flags |=
5014 IEEE80211_CHAN_PASSIVE_SCAN;
5015 }
5016 }
5017 if (!update)
5018 (*n_cnt)++;
5019 }
5020 }
5021exit:
5022 kfree(pbuf);
5023 return err;
5024}
5025
5026
5027static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005028{
Arend van Sprielac24be62012-10-22 10:36:23 -07005029 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005030 struct wiphy *wiphy;
5031 s32 phy_list;
Hante Meulemand48200b2013-04-03 12:40:29 +02005032 u32 band_list[3];
5033 u32 nmode;
5034 u32 bw_cap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005035 s8 phy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005036 s32 err;
5037 u32 nband;
5038 s32 i;
5039 struct ieee80211_supported_band *bands[IEEE80211_NUM_BANDS];
5040 s32 index;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005041
Hante Meulemanb87e2c42012-11-14 18:46:23 -08005042 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005043 &phy_list, sizeof(phy_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005044 if (err) {
Hante Meulemand48200b2013-04-03 12:40:29 +02005045 brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005046 return err;
5047 }
5048
Hante Meuleman3ba81372012-09-19 22:21:13 +02005049 phy = ((char *)&phy_list)[0];
Hante Meulemand48200b2013-04-03 12:40:29 +02005050 brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy);
5051
5052
5053 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST,
5054 &band_list, sizeof(band_list));
5055 if (err) {
5056 brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err);
5057 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005058 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005059 brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n",
5060 band_list[0], band_list[1], band_list[2]);
5061
5062 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5063 if (err) {
5064 brcmf_err("nmode error (%d)\n", err);
5065 } else {
5066 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &bw_cap);
5067 if (err)
5068 brcmf_err("mimo_bw_cap error (%d)\n", err);
5069 }
5070 brcmf_dbg(INFO, "nmode=%d, mimo_bw_cap=%d\n", nmode, bw_cap);
5071
5072 err = brcmf_construct_reginfo(cfg, bw_cap);
5073 if (err) {
5074 brcmf_err("brcmf_construct_reginfo failed (%d)\n", err);
5075 return err;
5076 }
5077
5078 nband = band_list[0];
5079 memset(bands, 0, sizeof(bands));
5080
5081 for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) {
5082 index = -1;
5083 if ((band_list[i] == WLC_BAND_5G) &&
5084 (__wl_band_5ghz_a.n_channels > 0)) {
5085 index = IEEE80211_BAND_5GHZ;
5086 bands[index] = &__wl_band_5ghz_a;
5087 if ((bw_cap == WLC_N_BW_40ALL) ||
5088 (bw_cap == WLC_N_BW_20IN2G_40IN5G))
5089 bands[index]->ht_cap.cap |=
5090 IEEE80211_HT_CAP_SGI_40;
5091 } else if ((band_list[i] == WLC_BAND_2G) &&
5092 (__wl_band_2ghz.n_channels > 0)) {
5093 index = IEEE80211_BAND_2GHZ;
5094 bands[index] = &__wl_band_2ghz;
5095 if (bw_cap == WLC_N_BW_40ALL)
5096 bands[index]->ht_cap.cap |=
5097 IEEE80211_HT_CAP_SGI_40;
5098 }
5099
5100 if ((index >= 0) && nmode) {
5101 bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5102 bands[index]->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5103 bands[index]->ht_cap.ht_supported = true;
5104 bands[index]->ht_cap.ampdu_factor =
5105 IEEE80211_HT_MAX_AMPDU_64K;
5106 bands[index]->ht_cap.ampdu_density =
5107 IEEE80211_HT_MPDU_DENSITY_16;
5108 /* An HT shall support all EQM rates for one spatial
5109 * stream
5110 */
5111 bands[index]->ht_cap.mcs.rx_mask[0] = 0xff;
5112 }
5113 }
5114
5115 wiphy = cfg_to_wiphy(cfg);
5116 wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ];
5117 wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ];
5118 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005119
5120 return err;
5121}
5122
Hante Meulemand48200b2013-04-03 12:40:29 +02005123
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005124static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005125{
Hante Meulemand48200b2013-04-03 12:40:29 +02005126 return brcmf_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005127}
5128
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005129static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005130{
5131 struct net_device *ndev;
5132 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005133 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005134 s32 power_mode;
5135 s32 err = 0;
5136
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005137 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005138 return err;
5139
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005140 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005141 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01005142 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005143
Hante Meuleman40a23292013-01-02 15:22:51 +01005144 /* make sure RF is ready for work */
5145 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
5146
5147 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
5148 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005149
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005150 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01005151 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005152 if (err)
5153 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005154 brcmf_dbg(INFO, "power save set to %s\n",
5155 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005156
Hante Meuleman40a23292013-01-02 15:22:51 +01005157 err = brcmf_dongle_roam(ifp, (cfg->roam_on ? 0 : 1), WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005158 if (err)
5159 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005160 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5161 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01005162 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005163 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005164 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005165 if (err)
5166 goto default_conf_out;
5167
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005168 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01005169default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005170
5171 return err;
5172
5173}
5174
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005175static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005176{
Arend van Sprielc1179032012-10-22 13:55:33 -07005177 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005178
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005179 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005180}
5181
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005182static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005183{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005184 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07005185
Arend van Spriel5b435de2011-10-05 13:19:03 +02005186 /*
5187 * While going down, if associated with AP disassociate
5188 * from AP to save power
5189 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005190 if (check_vif_up(ifp->vif)) {
5191 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005192
5193 /* Make sure WPA_Supplicant receives all the event
5194 generated due to DISASSOC call to the fw to keep
5195 the state fw and WPA_Supplicant state consistent
5196 */
5197 brcmf_delay(500);
5198 }
5199
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005200 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07005201 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005202
Arend van Spriel5b435de2011-10-05 13:19:03 +02005203 return 0;
5204}
5205
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005206s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005207{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005208 struct brcmf_if *ifp = netdev_priv(ndev);
5209 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210 s32 err = 0;
5211
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005212 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005213 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005214 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005215
5216 return err;
5217}
5218
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005219s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005220{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005221 struct brcmf_if *ifp = netdev_priv(ndev);
5222 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005223 s32 err = 0;
5224
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005225 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005226 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005227 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005228
5229 return err;
5230}
5231
Arend van Spriel9f440b72013-02-08 15:53:36 +01005232u32 wl_get_vif_state_all(struct brcmf_cfg80211_info *cfg, unsigned long state)
5233{
5234 struct brcmf_cfg80211_vif *vif;
5235 bool result = 0;
5236
5237 list_for_each_entry(vif, &cfg->vif_list, list) {
5238 if (test_bit(state, &vif->sme_state))
5239 result++;
5240 }
5241 return result;
5242}
Arend van Sprield3c0b632013-02-08 15:53:37 +01005243
5244static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
5245 u8 action)
5246{
5247 u8 evt_action;
5248
5249 mutex_lock(&event->vif_event_lock);
5250 evt_action = event->action;
5251 mutex_unlock(&event->vif_event_lock);
5252 return evt_action == action;
5253}
5254
5255void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
5256 struct brcmf_cfg80211_vif *vif)
5257{
5258 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5259
5260 mutex_lock(&event->vif_event_lock);
5261 event->vif = vif;
5262 event->action = 0;
5263 mutex_unlock(&event->vif_event_lock);
5264}
5265
5266bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
5267{
5268 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5269 bool armed;
5270
5271 mutex_lock(&event->vif_event_lock);
5272 armed = event->vif != NULL;
5273 mutex_unlock(&event->vif_event_lock);
5274
5275 return armed;
5276}
5277int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
5278 u8 action, ulong timeout)
5279{
5280 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5281
5282 return wait_event_timeout(event->vif_wq,
5283 vif_event_equals(event, action), timeout);
5284}
5285