blob: 53c17f9e094f6a3a89224890d9f44b03f4a7da9c [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
Joe Perches02f77192012-01-15 00:38:44 -080019#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
20
Arend van Spriel5b435de2011-10-05 13:19:03 +020021#include <linux/kernel.h>
22#include <linux/if_arp.h>
23#include <linux/sched.h>
24#include <linux/kthread.h>
25#include <linux/netdevice.h>
26#include <linux/bitops.h>
27#include <linux/etherdevice.h>
28#include <linux/ieee80211.h>
29#include <linux/uaccess.h>
30#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020031#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020032
33#include <brcmu_utils.h>
34#include <defs.h>
35#include <brcmu_wifi.h>
36#include "dhd.h"
37#include "wl_cfg80211.h"
38
Arend van Spriele5806072012-09-19 22:21:08 +020039#define BRCMF_SCAN_IE_LEN_MAX 2048
40#define BRCMF_PNO_VERSION 2
41#define BRCMF_PNO_TIME 30
42#define BRCMF_PNO_REPEAT 4
43#define BRCMF_PNO_FREQ_EXPO_MAX 3
44#define BRCMF_PNO_MAX_PFN_COUNT 16
45#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
46#define BRCMF_PNO_HIDDEN_BIT 2
47#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
48#define BRCMF_PNO_SCAN_COMPLETE 1
49#define BRCMF_PNO_SCAN_INCOMPLETE 0
50
Hante Meuleman1a873342012-09-27 14:17:54 +020051#define TLV_LEN_OFF 1 /* length offset */
Hante Meuleman04012892012-09-27 14:17:49 +020052#define TLV_HDR_LEN 2 /* header length */
Hante Meuleman1a873342012-09-27 14:17:54 +020053#define TLV_BODY_OFF 2 /* body offset */
54#define TLV_OUI_LEN 3 /* oui id length */
55#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
59
60#define VS_IE_FIXED_HDR_LEN 6
61#define WPA_IE_VERSION_LEN 2
62#define WPA_IE_MIN_OUI_LEN 4
63#define WPA_IE_SUITE_COUNT_LEN 2
64
65#define WPA_CIPHER_NONE 0 /* None */
66#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
67#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
68#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
69#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
70
71#define RSN_AKM_NONE 0 /* None (IBSS) */
72#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
73#define RSN_AKM_PSK 2 /* Pre-shared Key */
74#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
75#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
76
77#define VNDR_IE_CMD_LEN 4 /* length of the set command
78 * string :"add", "del" (+ NUL)
79 */
80#define VNDR_IE_COUNT_OFFSET 4
81#define VNDR_IE_PKTFLAG_OFFSET 8
82#define VNDR_IE_VSIE_OFFSET 12
83#define VNDR_IE_HDR_SIZE 12
84#define VNDR_IE_BEACON_FLAG 0x1
85#define VNDR_IE_PRBRSP_FLAG 0x2
86#define MAX_VNDR_IE_NUMBER 5
87
88#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
89#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020090
Arend van Spriel5b435de2011-10-05 13:19:03 +020091#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
92 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
93
94static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
95
96static u32 brcmf_dbg_level = WL_DBG_ERR;
97
Arend van Spriel5b435de2011-10-05 13:19:03 +020098static bool check_sys_up(struct wiphy *wiphy)
99{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200100 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
101 if (!test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200102 WL_INFO("device is not ready : status (%d)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200103 (int)cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200104 return false;
105 }
106 return true;
107}
108
109#define CHAN2G(_channel, _freq, _flags) { \
110 .band = IEEE80211_BAND_2GHZ, \
111 .center_freq = (_freq), \
112 .hw_value = (_channel), \
113 .flags = (_flags), \
114 .max_antenna_gain = 0, \
115 .max_power = 30, \
116}
117
118#define CHAN5G(_channel, _flags) { \
119 .band = IEEE80211_BAND_5GHZ, \
120 .center_freq = 5000 + (5 * (_channel)), \
121 .hw_value = (_channel), \
122 .flags = (_flags), \
123 .max_antenna_gain = 0, \
124 .max_power = 30, \
125}
126
127#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
128#define RATETAB_ENT(_rateid, _flags) \
129 { \
130 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
131 .hw_value = (_rateid), \
132 .flags = (_flags), \
133 }
134
135static struct ieee80211_rate __wl_rates[] = {
136 RATETAB_ENT(BRCM_RATE_1M, 0),
137 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
138 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
139 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
140 RATETAB_ENT(BRCM_RATE_6M, 0),
141 RATETAB_ENT(BRCM_RATE_9M, 0),
142 RATETAB_ENT(BRCM_RATE_12M, 0),
143 RATETAB_ENT(BRCM_RATE_18M, 0),
144 RATETAB_ENT(BRCM_RATE_24M, 0),
145 RATETAB_ENT(BRCM_RATE_36M, 0),
146 RATETAB_ENT(BRCM_RATE_48M, 0),
147 RATETAB_ENT(BRCM_RATE_54M, 0),
148};
149
150#define wl_a_rates (__wl_rates + 4)
151#define wl_a_rates_size 8
152#define wl_g_rates (__wl_rates + 0)
153#define wl_g_rates_size 12
154
155static struct ieee80211_channel __wl_2ghz_channels[] = {
156 CHAN2G(1, 2412, 0),
157 CHAN2G(2, 2417, 0),
158 CHAN2G(3, 2422, 0),
159 CHAN2G(4, 2427, 0),
160 CHAN2G(5, 2432, 0),
161 CHAN2G(6, 2437, 0),
162 CHAN2G(7, 2442, 0),
163 CHAN2G(8, 2447, 0),
164 CHAN2G(9, 2452, 0),
165 CHAN2G(10, 2457, 0),
166 CHAN2G(11, 2462, 0),
167 CHAN2G(12, 2467, 0),
168 CHAN2G(13, 2472, 0),
169 CHAN2G(14, 2484, 0),
170};
171
172static struct ieee80211_channel __wl_5ghz_a_channels[] = {
173 CHAN5G(34, 0), CHAN5G(36, 0),
174 CHAN5G(38, 0), CHAN5G(40, 0),
175 CHAN5G(42, 0), CHAN5G(44, 0),
176 CHAN5G(46, 0), CHAN5G(48, 0),
177 CHAN5G(52, 0), CHAN5G(56, 0),
178 CHAN5G(60, 0), CHAN5G(64, 0),
179 CHAN5G(100, 0), CHAN5G(104, 0),
180 CHAN5G(108, 0), CHAN5G(112, 0),
181 CHAN5G(116, 0), CHAN5G(120, 0),
182 CHAN5G(124, 0), CHAN5G(128, 0),
183 CHAN5G(132, 0), CHAN5G(136, 0),
184 CHAN5G(140, 0), CHAN5G(149, 0),
185 CHAN5G(153, 0), CHAN5G(157, 0),
186 CHAN5G(161, 0), CHAN5G(165, 0),
187 CHAN5G(184, 0), CHAN5G(188, 0),
188 CHAN5G(192, 0), CHAN5G(196, 0),
189 CHAN5G(200, 0), CHAN5G(204, 0),
190 CHAN5G(208, 0), CHAN5G(212, 0),
191 CHAN5G(216, 0),
192};
193
194static struct ieee80211_channel __wl_5ghz_n_channels[] = {
195 CHAN5G(32, 0), CHAN5G(34, 0),
196 CHAN5G(36, 0), CHAN5G(38, 0),
197 CHAN5G(40, 0), CHAN5G(42, 0),
198 CHAN5G(44, 0), CHAN5G(46, 0),
199 CHAN5G(48, 0), CHAN5G(50, 0),
200 CHAN5G(52, 0), CHAN5G(54, 0),
201 CHAN5G(56, 0), CHAN5G(58, 0),
202 CHAN5G(60, 0), CHAN5G(62, 0),
203 CHAN5G(64, 0), CHAN5G(66, 0),
204 CHAN5G(68, 0), CHAN5G(70, 0),
205 CHAN5G(72, 0), CHAN5G(74, 0),
206 CHAN5G(76, 0), CHAN5G(78, 0),
207 CHAN5G(80, 0), CHAN5G(82, 0),
208 CHAN5G(84, 0), CHAN5G(86, 0),
209 CHAN5G(88, 0), CHAN5G(90, 0),
210 CHAN5G(92, 0), CHAN5G(94, 0),
211 CHAN5G(96, 0), CHAN5G(98, 0),
212 CHAN5G(100, 0), CHAN5G(102, 0),
213 CHAN5G(104, 0), CHAN5G(106, 0),
214 CHAN5G(108, 0), CHAN5G(110, 0),
215 CHAN5G(112, 0), CHAN5G(114, 0),
216 CHAN5G(116, 0), CHAN5G(118, 0),
217 CHAN5G(120, 0), CHAN5G(122, 0),
218 CHAN5G(124, 0), CHAN5G(126, 0),
219 CHAN5G(128, 0), CHAN5G(130, 0),
220 CHAN5G(132, 0), CHAN5G(134, 0),
221 CHAN5G(136, 0), CHAN5G(138, 0),
222 CHAN5G(140, 0), CHAN5G(142, 0),
223 CHAN5G(144, 0), CHAN5G(145, 0),
224 CHAN5G(146, 0), CHAN5G(147, 0),
225 CHAN5G(148, 0), CHAN5G(149, 0),
226 CHAN5G(150, 0), CHAN5G(151, 0),
227 CHAN5G(152, 0), CHAN5G(153, 0),
228 CHAN5G(154, 0), CHAN5G(155, 0),
229 CHAN5G(156, 0), CHAN5G(157, 0),
230 CHAN5G(158, 0), CHAN5G(159, 0),
231 CHAN5G(160, 0), CHAN5G(161, 0),
232 CHAN5G(162, 0), CHAN5G(163, 0),
233 CHAN5G(164, 0), CHAN5G(165, 0),
234 CHAN5G(166, 0), CHAN5G(168, 0),
235 CHAN5G(170, 0), CHAN5G(172, 0),
236 CHAN5G(174, 0), CHAN5G(176, 0),
237 CHAN5G(178, 0), CHAN5G(180, 0),
238 CHAN5G(182, 0), CHAN5G(184, 0),
239 CHAN5G(186, 0), CHAN5G(188, 0),
240 CHAN5G(190, 0), CHAN5G(192, 0),
241 CHAN5G(194, 0), CHAN5G(196, 0),
242 CHAN5G(198, 0), CHAN5G(200, 0),
243 CHAN5G(202, 0), CHAN5G(204, 0),
244 CHAN5G(206, 0), CHAN5G(208, 0),
245 CHAN5G(210, 0), CHAN5G(212, 0),
246 CHAN5G(214, 0), CHAN5G(216, 0),
247 CHAN5G(218, 0), CHAN5G(220, 0),
248 CHAN5G(222, 0), CHAN5G(224, 0),
249 CHAN5G(226, 0), CHAN5G(228, 0),
250};
251
252static struct ieee80211_supported_band __wl_band_2ghz = {
253 .band = IEEE80211_BAND_2GHZ,
254 .channels = __wl_2ghz_channels,
255 .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
256 .bitrates = wl_g_rates,
257 .n_bitrates = wl_g_rates_size,
258};
259
260static struct ieee80211_supported_band __wl_band_5ghz_a = {
261 .band = IEEE80211_BAND_5GHZ,
262 .channels = __wl_5ghz_a_channels,
263 .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
264 .bitrates = wl_a_rates,
265 .n_bitrates = wl_a_rates_size,
266};
267
268static struct ieee80211_supported_band __wl_band_5ghz_n = {
269 .band = IEEE80211_BAND_5GHZ,
270 .channels = __wl_5ghz_n_channels,
271 .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
272 .bitrates = wl_a_rates,
273 .n_bitrates = wl_a_rates_size,
274};
275
276static const u32 __wl_cipher_suites[] = {
277 WLAN_CIPHER_SUITE_WEP40,
278 WLAN_CIPHER_SUITE_WEP104,
279 WLAN_CIPHER_SUITE_TKIP,
280 WLAN_CIPHER_SUITE_CCMP,
281 WLAN_CIPHER_SUITE_AES_CMAC,
282};
283
Alwin Beukersf8e4b412011-10-12 20:51:28 +0200284/* tag_ID/length/value_buffer tuple */
285struct brcmf_tlv {
286 u8 id;
287 u8 len;
288 u8 data[1];
289};
290
Hante Meuleman1a873342012-09-27 14:17:54 +0200291/* Vendor specific ie. id = 221, oui and type defines exact ie */
292struct brcmf_vs_tlv {
293 u8 id;
294 u8 len;
295 u8 oui[3];
296 u8 oui_type;
297};
298
299struct parsed_vndr_ie_info {
300 u8 *ie_ptr;
301 u32 ie_len; /* total length including id & length field */
302 struct brcmf_vs_tlv vndrie;
303};
304
305struct parsed_vndr_ies {
306 u32 count;
307 struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER];
308};
309
Alwin Beukersef6ac172011-10-12 20:51:26 +0200310/* Quarter dBm units to mW
311 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
312 * Table is offset so the last entry is largest mW value that fits in
313 * a u16.
314 */
315
316#define QDBM_OFFSET 153 /* Offset for first entry */
317#define QDBM_TABLE_LEN 40 /* Table size */
318
319/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
320 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
321 */
322#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
323
324/* Largest mW value that will round down to the last table entry,
325 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
326 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
327 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
328 */
329#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
330
331static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
332/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
333/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
334/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
335/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
336/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
337/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
338};
339
340static u16 brcmf_qdbm_to_mw(u8 qdbm)
341{
342 uint factor = 1;
343 int idx = qdbm - QDBM_OFFSET;
344
345 if (idx >= QDBM_TABLE_LEN)
346 /* clamp to max u16 mW value */
347 return 0xFFFF;
348
349 /* scale the qdBm index up to the range of the table 0-40
350 * where an offset of 40 qdBm equals a factor of 10 mW.
351 */
352 while (idx < 0) {
353 idx += 40;
354 factor *= 10;
355 }
356
357 /* return the mW value scaled down to the correct factor of 10,
358 * adding in factor/2 to get proper rounding.
359 */
360 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
361}
362
363static u8 brcmf_mw_to_qdbm(u16 mw)
364{
365 u8 qdbm;
366 int offset;
367 uint mw_uint = mw;
368 uint boundary;
369
370 /* handle boundary case */
371 if (mw_uint <= 1)
372 return 0;
373
374 offset = QDBM_OFFSET;
375
376 /* move mw into the range of the table */
377 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
378 mw_uint *= 10;
379 offset -= 40;
380 }
381
382 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
383 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
384 nqdBm_to_mW_map[qdbm]) / 2;
385 if (mw_uint < boundary)
386 break;
387 }
388
389 qdbm += (u8) offset;
390
391 return qdbm;
392}
393
Arend van Spriel5b435de2011-10-05 13:19:03 +0200394/* function for reading/writing a single u32 from/to the dongle */
395static int
396brcmf_exec_dcmd_u32(struct net_device *ndev, u32 cmd, u32 *par)
397{
398 int err;
399 __le32 par_le = cpu_to_le32(*par);
400
401 err = brcmf_exec_dcmd(ndev, cmd, &par_le, sizeof(__le32));
402 *par = le32_to_cpu(par_le);
403
404 return err;
405}
406
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200407static s32
408brcmf_dev_iovar_setbuf_bsscfg(struct net_device *ndev, s8 *name,
409 void *param, s32 paramlen,
410 void *buf, s32 buflen, s32 bssidx)
411{
412 s32 err = -ENOMEM;
413 u32 len;
414
415 len = brcmf_c_mkiovar_bsscfg(name, param, paramlen,
416 buf, buflen, bssidx);
417 BUG_ON(!len);
418 if (len > 0)
419 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, buf, len);
420 if (err)
421 WL_ERR("error (%d)\n", err);
422
423 return err;
424}
425
426static s32
427brcmf_dev_iovar_getbuf_bsscfg(struct net_device *ndev, s8 *name,
428 void *param, s32 paramlen,
429 void *buf, s32 buflen, s32 bssidx)
430{
431 s32 err = -ENOMEM;
432 u32 len;
433
434 len = brcmf_c_mkiovar_bsscfg(name, param, paramlen,
435 buf, buflen, bssidx);
436 BUG_ON(!len);
437 if (len > 0)
438 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, buf, len);
439 if (err)
440 WL_ERR("error (%d)\n", err);
441
442 return err;
443}
444
Arend van Spriel5b435de2011-10-05 13:19:03 +0200445static void convert_key_from_CPU(struct brcmf_wsec_key *key,
446 struct brcmf_wsec_key_le *key_le)
447{
448 key_le->index = cpu_to_le32(key->index);
449 key_le->len = cpu_to_le32(key->len);
450 key_le->algo = cpu_to_le32(key->algo);
451 key_le->flags = cpu_to_le32(key->flags);
452 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
453 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
454 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
455 memcpy(key_le->data, key->data, sizeof(key->data));
456 memcpy(key_le->ea, key->ea, sizeof(key->ea));
457}
458
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200459static int
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200460send_key_to_dongle(struct brcmf_cfg80211_info *cfg, s32 bssidx,
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200461 struct net_device *ndev, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200462{
463 int err;
464 struct brcmf_wsec_key_le key_le;
465
466 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200467
468 err = brcmf_dev_iovar_setbuf_bsscfg(ndev, "wsec_key", &key_le,
469 sizeof(key_le),
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200470 cfg->extra_buf,
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200471 WL_EXTRA_BUF_MAX, bssidx);
472
Arend van Spriel5b435de2011-10-05 13:19:03 +0200473 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200474 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200475 return err;
476}
477
478static s32
479brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
480 enum nl80211_iftype type, u32 *flags,
481 struct vif_params *params)
482{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200483 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200484 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200485 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200486 s32 err = 0;
487
Hante Meuleman1a873342012-09-27 14:17:54 +0200488 WL_TRACE("Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200489
490 switch (type) {
491 case NL80211_IFTYPE_MONITOR:
492 case NL80211_IFTYPE_WDS:
493 WL_ERR("type (%d) : currently we do not support this type\n",
494 type);
495 return -EOPNOTSUPP;
496 case NL80211_IFTYPE_ADHOC:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200497 cfg->conf->mode = WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200498 infra = 0;
499 break;
500 case NL80211_IFTYPE_STATION:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200501 cfg->conf->mode = WL_MODE_BSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200502 infra = 1;
503 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200504 case NL80211_IFTYPE_AP:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200505 cfg->conf->mode = WL_MODE_AP;
Hante Meuleman1a873342012-09-27 14:17:54 +0200506 ap = 1;
507 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200508 default:
509 err = -EINVAL;
510 goto done;
511 }
512
Hante Meuleman1a873342012-09-27 14:17:54 +0200513 if (ap) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200514 set_bit(WL_STATUS_AP_CREATING, &cfg->status);
515 if (!cfg->ap_info)
516 cfg->ap_info = kzalloc(sizeof(*cfg->ap_info),
517 GFP_KERNEL);
518 if (!cfg->ap_info) {
Hante Meuleman1a873342012-09-27 14:17:54 +0200519 err = -ENOMEM;
520 goto done;
521 }
522 WL_INFO("IF Type = AP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200523 } else {
Hante Meuleman1a873342012-09-27 14:17:54 +0200524 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra);
525 if (err) {
526 WL_ERR("WLC_SET_INFRA error (%d)\n", err);
527 err = -EAGAIN;
528 goto done;
529 }
530 WL_INFO("IF Type = %s\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200531 (cfg->conf->mode == WL_MODE_IBSS) ?
Hante Meuleman1a873342012-09-27 14:17:54 +0200532 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200533 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200534 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200535
536done:
537 WL_TRACE("Exit\n");
538
539 return err;
540}
541
542static s32 brcmf_dev_intvar_set(struct net_device *ndev, s8 *name, s32 val)
543{
544 s8 buf[BRCMF_DCMD_SMLEN];
545 u32 len;
546 s32 err = 0;
547 __le32 val_le;
548
549 val_le = cpu_to_le32(val);
Alwin Beukers53a22772011-10-12 20:51:30 +0200550 len = brcmf_c_mkiovar(name, (char *)(&val_le), sizeof(val_le), buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200551 sizeof(buf));
552 BUG_ON(!len);
553
554 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, buf, len);
555 if (err)
556 WL_ERR("error (%d)\n", err);
557
558 return err;
559}
560
561static s32
562brcmf_dev_intvar_get(struct net_device *ndev, s8 *name, s32 *retval)
563{
564 union {
565 s8 buf[BRCMF_DCMD_SMLEN];
566 __le32 val;
567 } var;
568 u32 len;
569 u32 data_null;
570 s32 err = 0;
571
572 len =
Alwin Beukers53a22772011-10-12 20:51:30 +0200573 brcmf_c_mkiovar(name, (char *)(&data_null), 0, (char *)(&var),
Arend van Spriel5b435de2011-10-05 13:19:03 +0200574 sizeof(var.buf));
575 BUG_ON(!len);
576 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, &var, len);
577 if (err)
578 WL_ERR("error (%d)\n", err);
579
580 *retval = le32_to_cpu(var.val);
581
582 return err;
583}
584
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200585static s32
586brcmf_dev_intvar_set_bsscfg(struct net_device *ndev, s8 *name, u32 val,
587 s32 bssidx)
588{
589 s8 buf[BRCMF_DCMD_SMLEN];
590 __le32 val_le;
591
592 val_le = cpu_to_le32(val);
593
594 return brcmf_dev_iovar_setbuf_bsscfg(ndev, name, &val_le,
595 sizeof(val_le), buf, sizeof(buf),
596 bssidx);
597}
598
599static s32
600brcmf_dev_intvar_get_bsscfg(struct net_device *ndev, s8 *name, s32 *val,
601 s32 bssidx)
602{
603 s8 buf[BRCMF_DCMD_SMLEN];
604 s32 err;
605 __le32 val_le;
606
607 memset(buf, 0, sizeof(buf));
608 err = brcmf_dev_iovar_getbuf_bsscfg(ndev, name, val, sizeof(*val), buf,
609 sizeof(buf), bssidx);
610 if (err == 0) {
611 memcpy(&val_le, buf, sizeof(val_le));
612 *val = le32_to_cpu(val_le);
613 }
614 return err;
615}
616
617
618/*
619 * For now brcmf_find_bssidx will return 0. Once p2p gets implemented this
620 * should return the ndev matching bssidx.
621 */
622static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200623brcmf_find_bssidx(struct brcmf_cfg80211_info *cfg, struct net_device *ndev)
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200624{
625 return 0;
626}
627
Arend van Spriel5b435de2011-10-05 13:19:03 +0200628static void brcmf_set_mpc(struct net_device *ndev, int mpc)
629{
630 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200631 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200632
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200633 if (test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200634 err = brcmf_dev_intvar_set(ndev, "mpc", mpc);
635 if (err) {
636 WL_ERR("fail to set mpc\n");
637 return;
638 }
639 WL_INFO("MPC : %d\n", mpc);
640 }
641}
642
Hante Meuleman35aafa92012-09-11 21:18:49 +0200643static void brcmf_iscan_prep(struct brcmf_scan_params_le *params_le,
644 struct brcmf_ssid *ssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200645{
646 memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
647 params_le->bss_type = DOT11_BSSTYPE_ANY;
648 params_le->scan_type = 0;
649 params_le->channel_num = 0;
650 params_le->nprobes = cpu_to_le32(-1);
651 params_le->active_time = cpu_to_le32(-1);
652 params_le->passive_time = cpu_to_le32(-1);
653 params_le->home_time = cpu_to_le32(-1);
654 if (ssid && ssid->SSID_len)
655 memcpy(&params_le->ssid_le, ssid, sizeof(struct brcmf_ssid));
656}
657
658static s32
659brcmf_dev_iovar_setbuf(struct net_device *ndev, s8 * iovar, void *param,
660 s32 paramlen, void *bufptr, s32 buflen)
661{
662 s32 iolen;
663
Alwin Beukers53a22772011-10-12 20:51:30 +0200664 iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200665 BUG_ON(!iolen);
666
667 return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, bufptr, iolen);
668}
669
670static s32
671brcmf_dev_iovar_getbuf(struct net_device *ndev, s8 * iovar, void *param,
672 s32 paramlen, void *bufptr, s32 buflen)
673{
674 s32 iolen;
675
Alwin Beukers53a22772011-10-12 20:51:30 +0200676 iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200677 BUG_ON(!iolen);
678
679 return brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, bufptr, buflen);
680}
681
682static s32
683brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
684 struct brcmf_ssid *ssid, u16 action)
685{
686 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
687 offsetof(struct brcmf_iscan_params_le, params_le);
688 struct brcmf_iscan_params_le *params;
689 s32 err = 0;
690
691 if (ssid && ssid->SSID_len)
692 params_size += sizeof(struct brcmf_ssid);
693 params = kzalloc(params_size, GFP_KERNEL);
694 if (!params)
695 return -ENOMEM;
696 BUG_ON(params_size >= BRCMF_DCMD_SMLEN);
697
Hante Meuleman35aafa92012-09-11 21:18:49 +0200698 brcmf_iscan_prep(&params->params_le, ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200699
700 params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION);
701 params->action = cpu_to_le16(action);
702 params->scan_duration = cpu_to_le16(0);
703
704 err = brcmf_dev_iovar_setbuf(iscan->ndev, "iscan", params, params_size,
705 iscan->dcmd_buf, BRCMF_DCMD_SMLEN);
706 if (err) {
707 if (err == -EBUSY)
708 WL_INFO("system busy : iscan canceled\n");
709 else
710 WL_ERR("error (%d)\n", err);
711 }
712
713 kfree(params);
714 return err;
715}
716
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200717static s32 brcmf_do_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200718{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200719 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
720 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200721 struct brcmf_ssid ssid;
Arend van Spriel66831072011-10-12 20:51:19 +0200722 __le32 passive_scan;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200723 s32 err = 0;
724
725 /* Broadcast scan by default */
726 memset(&ssid, 0, sizeof(ssid));
727
728 iscan->state = WL_ISCAN_STATE_SCANING;
729
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200730 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
731 err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCMF_C_SET_PASSIVE_SCAN,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200732 &passive_scan, sizeof(passive_scan));
733 if (err) {
734 WL_ERR("error (%d)\n", err);
735 return err;
736 }
737 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200738 cfg->iscan_kickstart = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200739 err = brcmf_run_iscan(iscan, &ssid, BRCMF_SCAN_ACTION_START);
740 if (err) {
741 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200742 cfg->iscan_kickstart = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200743 return err;
744 }
745 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
746 iscan->timer_on = 1;
747 return err;
748}
749
750static s32
Hante Meuleman35aafa92012-09-11 21:18:49 +0200751brcmf_cfg80211_iscan(struct wiphy *wiphy, struct net_device *ndev,
752 struct cfg80211_scan_request *request,
753 struct cfg80211_ssid *this_ssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200754{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200755 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200756 struct cfg80211_ssid *ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200757 struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
Arend van Spriel66831072011-10-12 20:51:19 +0200758 __le32 passive_scan;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200759 bool iscan_req;
760 bool spec_scan;
761 s32 err = 0;
762 u32 SSID_len;
763
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200764 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
765 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200766 return -EAGAIN;
767 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200768 if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200769 WL_ERR("Scanning being aborted : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200770 cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200771 return -EAGAIN;
772 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200773 if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200774 WL_ERR("Connecting : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200775 cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200776 return -EAGAIN;
777 }
778
779 iscan_req = false;
780 spec_scan = false;
781 if (request) {
782 /* scan bss */
783 ssids = request->ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200784 if (cfg->iscan_on && (!ssids || !ssids->ssid_len))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200785 iscan_req = true;
786 } else {
787 /* scan in ibss */
788 /* we don't do iscan in ibss */
789 ssids = this_ssid;
790 }
791
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200792 cfg->scan_request = request;
793 set_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200794 if (iscan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200795 err = brcmf_do_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200796 if (!err)
797 return err;
798 else
799 goto scan_out;
800 } else {
801 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
802 ssids->ssid, ssids->ssid_len);
803 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
804 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
805 sr->ssid_le.SSID_len = cpu_to_le32(0);
806 if (SSID_len) {
807 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
808 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
809 spec_scan = true;
810 } else {
811 WL_SCAN("Broadcast scan\n");
812 }
813
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200814 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200815 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
816 &passive_scan, sizeof(passive_scan));
817 if (err) {
818 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
819 goto scan_out;
820 }
821 brcmf_set_mpc(ndev, 0);
822 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
823 sizeof(sr->ssid_le));
824 if (err) {
825 if (err == -EBUSY)
826 WL_INFO("system busy : scan for \"%s\" "
827 "canceled\n", sr->ssid_le.SSID);
828 else
829 WL_ERR("WLC_SCAN error (%d)\n", err);
830
831 brcmf_set_mpc(ndev, 1);
832 goto scan_out;
833 }
834 }
835
836 return 0;
837
838scan_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200839 clear_bit(WL_STATUS_SCANNING, &cfg->status);
840 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200841 return err;
842}
843
Hante Meulemane756af52012-09-11 21:18:52 +0200844static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
845 struct cfg80211_scan_request *request)
846{
847 u32 n_ssids;
848 u32 n_channels;
849 s32 i;
850 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200851 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200852 u16 channel;
853 struct ieee80211_channel *req_channel;
854 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200855 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200856
857 memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
858 params_le->bss_type = DOT11_BSSTYPE_ANY;
859 params_le->scan_type = 0;
860 params_le->channel_num = 0;
861 params_le->nprobes = cpu_to_le32(-1);
862 params_le->active_time = cpu_to_le32(-1);
863 params_le->passive_time = cpu_to_le32(-1);
864 params_le->home_time = cpu_to_le32(-1);
865 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
866
867 /* if request is null exit so it will be all channel broadcast scan */
868 if (!request)
869 return;
870
871 n_ssids = request->n_ssids;
872 n_channels = request->n_channels;
873 /* Copy channel array if applicable */
874 WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
875 if (n_channels > 0) {
876 for (i = 0; i < n_channels; i++) {
877 chanspec = 0;
878 req_channel = request->channels[i];
879 channel = ieee80211_frequency_to_channel(
880 req_channel->center_freq);
881 if (req_channel->band == IEEE80211_BAND_2GHZ)
882 chanspec |= WL_CHANSPEC_BAND_2G;
883 else
884 chanspec |= WL_CHANSPEC_BAND_5G;
885
886 if (req_channel->flags & IEEE80211_CHAN_NO_HT40) {
887 chanspec |= WL_CHANSPEC_BW_20;
888 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
889 } else {
890 chanspec |= WL_CHANSPEC_BW_40;
891 if (req_channel->flags &
892 IEEE80211_CHAN_NO_HT40PLUS)
893 chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
894 else
895 chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
896 }
897
Arend van Spriel029591f2012-09-19 22:21:06 +0200898 chanspec |= (channel & WL_CHANSPEC_CHAN_MASK);
Hante Meulemane756af52012-09-11 21:18:52 +0200899 WL_SCAN("Chan : %d, Channel spec: %x\n",
Arend van Spriel029591f2012-09-19 22:21:06 +0200900 channel, chanspec);
901 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200902 }
903 } else {
904 WL_SCAN("Scanning all channels\n");
905 }
906 /* Copy ssid array if applicable */
907 WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids);
908 if (n_ssids > 0) {
909 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
910 n_channels * sizeof(u16);
911 offset = roundup(offset, sizeof(u32));
912 ptr = (char *)params_le + offset;
913 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200914 memset(&ssid_le, 0, sizeof(ssid_le));
915 ssid_le.SSID_len =
916 cpu_to_le32(request->ssids[i].ssid_len);
917 memcpy(ssid_le.SSID, request->ssids[i].ssid,
918 request->ssids[i].ssid_len);
919 if (!ssid_le.SSID_len)
Hante Meulemane756af52012-09-11 21:18:52 +0200920 WL_SCAN("%d: Broadcast scan\n", i);
921 else
922 WL_SCAN("%d: scan for %s size =%d\n", i,
Arend van Spriel029591f2012-09-19 22:21:06 +0200923 ssid_le.SSID, ssid_le.SSID_len);
924 memcpy(ptr, &ssid_le, sizeof(ssid_le));
925 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200926 }
927 } else {
928 WL_SCAN("Broadcast scan %p\n", request->ssids);
929 if ((request->ssids) && request->ssids->ssid_len) {
930 WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID,
931 request->ssids->ssid_len);
932 params_le->ssid_le.SSID_len =
933 cpu_to_le32(request->ssids->ssid_len);
934 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
935 request->ssids->ssid_len);
936 }
937 }
938 /* Adding mask to channel numbers */
939 params_le->channel_num =
940 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
941 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
942}
943
944static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200945brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +0200946 struct net_device *ndev,
947 bool aborted, bool fw_abort)
948{
949 struct brcmf_scan_params_le params_le;
950 struct cfg80211_scan_request *scan_request;
951 s32 err = 0;
952
953 WL_SCAN("Enter\n");
954
955 /* clear scan request, because the FW abort can cause a second call */
956 /* to this functon and might cause a double cfg80211_scan_done */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200957 scan_request = cfg->scan_request;
958 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200959
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200960 if (timer_pending(&cfg->escan_timeout))
961 del_timer_sync(&cfg->escan_timeout);
Hante Meulemane756af52012-09-11 21:18:52 +0200962
963 if (fw_abort) {
964 /* Do a scan abort to stop the driver's scan engine */
965 WL_SCAN("ABORT scan in firmware\n");
966 memset(&params_le, 0, sizeof(params_le));
967 memcpy(params_le.bssid, ether_bcast, ETH_ALEN);
968 params_le.bss_type = DOT11_BSSTYPE_ANY;
969 params_le.scan_type = 0;
970 params_le.channel_num = cpu_to_le32(1);
971 params_le.nprobes = cpu_to_le32(1);
972 params_le.active_time = cpu_to_le32(-1);
973 params_le.passive_time = cpu_to_le32(-1);
974 params_le.home_time = cpu_to_le32(-1);
975 /* Scan is aborted by setting channel_list[0] to -1 */
976 params_le.channel_list[0] = cpu_to_le16(-1);
977 /* E-Scan (or anyother type) can be aborted by SCAN */
978 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &params_le,
979 sizeof(params_le));
980 if (err)
981 WL_ERR("Scan abort failed\n");
982 }
Arend van Spriele5806072012-09-19 22:21:08 +0200983 /*
984 * e-scan can be initiated by scheduled scan
985 * which takes precedence.
986 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200987 if (cfg->sched_escan) {
Arend van Spriele5806072012-09-19 22:21:08 +0200988 WL_SCAN("scheduled scan completed\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200989 cfg->sched_escan = false;
Arend van Spriele5806072012-09-19 22:21:08 +0200990 if (!aborted)
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200991 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriele5806072012-09-19 22:21:08 +0200992 brcmf_set_mpc(ndev, 1);
993 } else if (scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +0200994 WL_SCAN("ESCAN Completed scan: %s\n",
995 aborted ? "Aborted" : "Done");
996 cfg80211_scan_done(scan_request, aborted);
997 brcmf_set_mpc(ndev, 1);
998 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200999 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02001000 WL_ERR("Scan complete while device not scanning\n");
1001 return -EPERM;
1002 }
1003
1004 return err;
1005}
1006
1007static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001008brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
Hante Meulemane756af52012-09-11 21:18:52 +02001009 struct cfg80211_scan_request *request, u16 action)
1010{
1011 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
1012 offsetof(struct brcmf_escan_params_le, params_le);
1013 struct brcmf_escan_params_le *params;
1014 s32 err = 0;
1015
1016 WL_SCAN("E-SCAN START\n");
1017
1018 if (request != NULL) {
1019 /* Allocate space for populating ssids in struct */
1020 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
1021
1022 /* Allocate space for populating ssids in struct */
1023 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
1024 }
1025
1026 params = kzalloc(params_size, GFP_KERNEL);
1027 if (!params) {
1028 err = -ENOMEM;
1029 goto exit;
1030 }
1031 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
1032 brcmf_escan_prep(&params->params_le, request);
1033 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
1034 params->action = cpu_to_le16(action);
1035 params->sync_id = cpu_to_le16(0x1234);
1036
1037 err = brcmf_dev_iovar_setbuf(ndev, "escan", params, params_size,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001038 cfg->escan_ioctl_buf, BRCMF_DCMD_MEDLEN);
Hante Meulemane756af52012-09-11 21:18:52 +02001039 if (err) {
1040 if (err == -EBUSY)
1041 WL_INFO("system busy : escan canceled\n");
1042 else
1043 WL_ERR("error (%d)\n", err);
1044 }
1045
1046 kfree(params);
1047exit:
1048 return err;
1049}
1050
1051static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001052brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Hante Meulemane756af52012-09-11 21:18:52 +02001053 struct net_device *ndev, struct cfg80211_scan_request *request)
1054{
1055 s32 err;
1056 __le32 passive_scan;
1057 struct brcmf_scan_results *results;
1058
1059 WL_SCAN("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001060 cfg->escan_info.ndev = ndev;
1061 cfg->escan_info.wiphy = wiphy;
1062 cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
1063 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
Hante Meulemane756af52012-09-11 21:18:52 +02001064 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
1065 &passive_scan, sizeof(passive_scan));
1066 if (err) {
1067 WL_ERR("error (%d)\n", err);
1068 return err;
1069 }
1070 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001071 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001072 results->version = 0;
1073 results->count = 0;
1074 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1075
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001076 err = brcmf_run_escan(cfg, ndev, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001077 if (err)
1078 brcmf_set_mpc(ndev, 1);
1079 return err;
1080}
1081
1082static s32
1083brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
1084 struct cfg80211_scan_request *request,
1085 struct cfg80211_ssid *this_ssid)
1086{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001087 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Hante Meulemane756af52012-09-11 21:18:52 +02001088 struct cfg80211_ssid *ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001089 struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
Hante Meulemane756af52012-09-11 21:18:52 +02001090 __le32 passive_scan;
1091 bool escan_req;
1092 bool spec_scan;
1093 s32 err;
1094 u32 SSID_len;
1095
1096 WL_SCAN("START ESCAN\n");
1097
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001098 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
1099 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001100 return -EAGAIN;
1101 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001102 if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02001103 WL_ERR("Scanning being aborted : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001104 cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001105 return -EAGAIN;
1106 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001107 if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02001108 WL_ERR("Connecting : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001109 cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001110 return -EAGAIN;
1111 }
1112
1113 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001114 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +02001115 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1116
1117 escan_req = false;
1118 if (request) {
1119 /* scan bss */
1120 ssids = request->ssids;
1121 escan_req = true;
1122 } else {
1123 /* scan in ibss */
1124 /* we don't do escan in ibss */
1125 ssids = this_ssid;
1126 }
1127
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001128 cfg->scan_request = request;
1129 set_bit(WL_STATUS_SCANNING, &cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001130 if (escan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001131 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001132 if (!err)
1133 return err;
1134 else
1135 goto scan_out;
1136 } else {
1137 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
1138 ssids->ssid, ssids->ssid_len);
1139 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1140 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1141 sr->ssid_le.SSID_len = cpu_to_le32(0);
1142 spec_scan = false;
1143 if (SSID_len) {
1144 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1145 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1146 spec_scan = true;
1147 } else
1148 WL_SCAN("Broadcast scan\n");
1149
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001150 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
Hante Meulemane756af52012-09-11 21:18:52 +02001151 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
1152 &passive_scan, sizeof(passive_scan));
1153 if (err) {
1154 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
1155 goto scan_out;
1156 }
1157 brcmf_set_mpc(ndev, 0);
1158 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
1159 sizeof(sr->ssid_le));
1160 if (err) {
1161 if (err == -EBUSY)
1162 WL_INFO("BUSY: scan for \"%s\" canceled\n",
1163 sr->ssid_le.SSID);
1164 else
1165 WL_ERR("WLC_SCAN error (%d)\n", err);
1166
1167 brcmf_set_mpc(ndev, 1);
1168 goto scan_out;
1169 }
1170 }
1171
1172 return 0;
1173
1174scan_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001175 clear_bit(WL_STATUS_SCANNING, &cfg->status);
1176 if (timer_pending(&cfg->escan_timeout))
1177 del_timer_sync(&cfg->escan_timeout);
1178 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001179 return err;
1180}
1181
Arend van Spriel5b435de2011-10-05 13:19:03 +02001182static s32
Johannes Bergfd014282012-06-18 19:17:03 +02001183brcmf_cfg80211_scan(struct wiphy *wiphy,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184 struct cfg80211_scan_request *request)
1185{
Johannes Bergfd014282012-06-18 19:17:03 +02001186 struct net_device *ndev = request->wdev->netdev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001187 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001188 s32 err = 0;
1189
1190 WL_TRACE("Enter\n");
1191
1192 if (!check_sys_up(wiphy))
1193 return -EIO;
1194
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001195 if (cfg->iscan_on)
Hante Meulemane756af52012-09-11 21:18:52 +02001196 err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001197 else if (cfg->escan_on)
Hante Meulemane756af52012-09-11 21:18:52 +02001198 err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
1199
Arend van Spriel5b435de2011-10-05 13:19:03 +02001200 if (err)
1201 WL_ERR("scan error (%d)\n", err);
1202
1203 WL_TRACE("Exit\n");
1204 return err;
1205}
1206
1207static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1208{
1209 s32 err = 0;
1210
1211 err = brcmf_dev_intvar_set(ndev, "rtsthresh", rts_threshold);
1212 if (err)
1213 WL_ERR("Error (%d)\n", err);
1214
1215 return err;
1216}
1217
1218static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1219{
1220 s32 err = 0;
1221
1222 err = brcmf_dev_intvar_set(ndev, "fragthresh", frag_threshold);
1223 if (err)
1224 WL_ERR("Error (%d)\n", err);
1225
1226 return err;
1227}
1228
1229static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1230{
1231 s32 err = 0;
1232 u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL);
1233
1234 err = brcmf_exec_dcmd_u32(ndev, cmd, &retry);
1235 if (err) {
1236 WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
1237 return err;
1238 }
1239 return err;
1240}
1241
1242static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1243{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001244 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1245 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001246 s32 err = 0;
1247
1248 WL_TRACE("Enter\n");
1249 if (!check_sys_up(wiphy))
1250 return -EIO;
1251
1252 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001253 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1254 cfg->conf->rts_threshold = wiphy->rts_threshold;
1255 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001256 if (!err)
1257 goto done;
1258 }
1259 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001260 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1261 cfg->conf->frag_threshold = wiphy->frag_threshold;
1262 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001263 if (!err)
1264 goto done;
1265 }
1266 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001267 && (cfg->conf->retry_long != wiphy->retry_long)) {
1268 cfg->conf->retry_long = wiphy->retry_long;
1269 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001270 if (!err)
1271 goto done;
1272 }
1273 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001274 && (cfg->conf->retry_short != wiphy->retry_short)) {
1275 cfg->conf->retry_short = wiphy->retry_short;
1276 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001277 if (!err)
1278 goto done;
1279 }
1280
1281done:
1282 WL_TRACE("Exit\n");
1283 return err;
1284}
1285
Arend van Spriel5b435de2011-10-05 13:19:03 +02001286static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001287brcmf_update_prof(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001288 const struct brcmf_event_msg *e, void *data, s32 item)
1289{
1290 s32 err = 0;
1291 struct brcmf_ssid *ssid;
1292
1293 switch (item) {
1294 case WL_PROF_SSID:
1295 ssid = (struct brcmf_ssid *) data;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001296 memset(cfg->profile->ssid.SSID, 0,
1297 sizeof(cfg->profile->ssid.SSID));
1298 memcpy(cfg->profile->ssid.SSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001299 ssid->SSID, ssid->SSID_len);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001300 cfg->profile->ssid.SSID_len = ssid->SSID_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001301 break;
1302 case WL_PROF_BSSID:
1303 if (data)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001304 memcpy(cfg->profile->bssid, data, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001305 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001306 memset(cfg->profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001307 break;
1308 case WL_PROF_SEC:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001309 memcpy(&cfg->profile->sec, data,
1310 sizeof(cfg->profile->sec));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001311 break;
1312 case WL_PROF_BEACONINT:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001313 cfg->profile->beacon_interval = *(u16 *)data;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001314 break;
1315 case WL_PROF_DTIMPERIOD:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001316 cfg->profile->dtim_period = *(u8 *)data;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001317 break;
1318 default:
1319 WL_ERR("unsupported item (%d)\n", item);
1320 err = -EOPNOTSUPP;
1321 break;
1322 }
1323
1324 return err;
1325}
1326
1327static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1328{
1329 memset(prof, 0, sizeof(*prof));
1330}
1331
1332static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
1333 size_t *join_params_size)
1334{
1335 u16 chanspec = 0;
1336
1337 if (ch != 0) {
1338 if (ch <= CH_MAX_2G_CHANNEL)
1339 chanspec |= WL_CHANSPEC_BAND_2G;
1340 else
1341 chanspec |= WL_CHANSPEC_BAND_5G;
1342
1343 chanspec |= WL_CHANSPEC_BW_20;
1344 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
1345
1346 *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
1347 sizeof(u16);
1348
1349 chanspec |= (ch & WL_CHANSPEC_CHAN_MASK);
1350 join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1351 join_params->params_le.chanspec_num = cpu_to_le32(1);
1352
1353 WL_CONN("join_params->params.chanspec_list[0]= %#X,"
1354 "channel %d, chanspec %#X\n",
1355 chanspec, ch, chanspec);
1356 }
1357}
1358
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001359static void brcmf_link_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001360{
1361 struct net_device *ndev = NULL;
1362 s32 err = 0;
1363
1364 WL_TRACE("Enter\n");
1365
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001366 if (cfg->link_up) {
1367 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001368 WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
1369 err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, NULL, 0);
1370 if (err)
1371 WL_ERR("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001372 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001373 }
1374 WL_TRACE("Exit\n");
1375}
1376
1377static s32
1378brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1379 struct cfg80211_ibss_params *params)
1380{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001381 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001382 struct brcmf_join_params join_params;
1383 size_t join_params_size = 0;
1384 s32 err = 0;
1385 s32 wsec = 0;
1386 s32 bcnprd;
1387 struct brcmf_ssid ssid;
1388
1389 WL_TRACE("Enter\n");
1390 if (!check_sys_up(wiphy))
1391 return -EIO;
1392
1393 if (params->ssid)
1394 WL_CONN("SSID: %s\n", params->ssid);
1395 else {
1396 WL_CONN("SSID: NULL, Not supported\n");
1397 return -EOPNOTSUPP;
1398 }
1399
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001400 set_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001401
1402 if (params->bssid)
Andy Shevchenko040a7832012-07-12 10:43:44 +03001403 WL_CONN("BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001404 else
1405 WL_CONN("No BSSID specified\n");
1406
1407 if (params->channel)
1408 WL_CONN("channel: %d\n", params->channel->center_freq);
1409 else
1410 WL_CONN("no channel specified\n");
1411
1412 if (params->channel_fixed)
1413 WL_CONN("fixed channel required\n");
1414 else
1415 WL_CONN("no fixed channel required\n");
1416
1417 if (params->ie && params->ie_len)
1418 WL_CONN("ie len: %d\n", params->ie_len);
1419 else
1420 WL_CONN("no ie specified\n");
1421
1422 if (params->beacon_interval)
1423 WL_CONN("beacon interval: %d\n", params->beacon_interval);
1424 else
1425 WL_CONN("no beacon interval specified\n");
1426
1427 if (params->basic_rates)
1428 WL_CONN("basic rates: %08X\n", params->basic_rates);
1429 else
1430 WL_CONN("no basic rates specified\n");
1431
1432 if (params->privacy)
1433 WL_CONN("privacy required\n");
1434 else
1435 WL_CONN("no privacy required\n");
1436
1437 /* Configure Privacy for starter */
1438 if (params->privacy)
1439 wsec |= WEP_ENABLED;
1440
1441 err = brcmf_dev_intvar_set(ndev, "wsec", wsec);
1442 if (err) {
1443 WL_ERR("wsec failed (%d)\n", err);
1444 goto done;
1445 }
1446
1447 /* Configure Beacon Interval for starter */
1448 if (params->beacon_interval)
1449 bcnprd = params->beacon_interval;
1450 else
1451 bcnprd = 100;
1452
1453 err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_BCNPRD, &bcnprd);
1454 if (err) {
1455 WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
1456 goto done;
1457 }
1458
1459 /* Configure required join parameter */
1460 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1461
1462 /* SSID */
1463 ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1464 memcpy(ssid.SSID, params->ssid, ssid.SSID_len);
1465 memcpy(join_params.ssid_le.SSID, params->ssid, ssid.SSID_len);
1466 join_params.ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
1467 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001468 brcmf_update_prof(cfg, NULL, &ssid, WL_PROF_SSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001469
1470 /* BSSID */
1471 if (params->bssid) {
1472 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1473 join_params_size = sizeof(join_params.ssid_le) +
1474 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
1475 } else {
1476 memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
1477 }
1478
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001479 brcmf_update_prof(cfg, NULL,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001480 &join_params.params_le.bssid, WL_PROF_BSSID);
1481
1482 /* Channel */
1483 if (params->channel) {
1484 u32 target_channel;
1485
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001486 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001487 ieee80211_frequency_to_channel(
1488 params->channel->center_freq);
1489 if (params->channel_fixed) {
1490 /* adding chanspec */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001491 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001492 &join_params, &join_params_size);
1493 }
1494
1495 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001496 target_channel = cfg->channel;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001497 err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_CHANNEL,
1498 &target_channel);
1499 if (err) {
1500 WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
1501 goto done;
1502 }
1503 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001504 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001505
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001506 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001507
1508
1509 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
1510 &join_params, join_params_size);
1511 if (err) {
1512 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1513 goto done;
1514 }
1515
1516done:
1517 if (err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001518 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001519 WL_TRACE("Exit\n");
1520 return err;
1521}
1522
1523static s32
1524brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1525{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001526 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001527 s32 err = 0;
1528
1529 WL_TRACE("Enter\n");
1530 if (!check_sys_up(wiphy))
1531 return -EIO;
1532
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001533 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001534
1535 WL_TRACE("Exit\n");
1536
1537 return err;
1538}
1539
1540static s32 brcmf_set_wpa_version(struct net_device *ndev,
1541 struct cfg80211_connect_params *sme)
1542{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001543 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001544 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001545 struct brcmf_cfg80211_security *sec;
1546 s32 val = 0;
1547 s32 err = 0;
1548
1549 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1550 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1551 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1552 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1553 else
1554 val = WPA_AUTH_DISABLED;
1555 WL_CONN("setting wpa_auth to 0x%0x\n", val);
1556 err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
1557 if (err) {
1558 WL_ERR("set wpa_auth failed (%d)\n", err);
1559 return err;
1560 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001561 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001562 sec->wpa_versions = sme->crypto.wpa_versions;
1563 return err;
1564}
1565
1566static s32 brcmf_set_auth_type(struct net_device *ndev,
1567 struct cfg80211_connect_params *sme)
1568{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001569 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001570 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001571 struct brcmf_cfg80211_security *sec;
1572 s32 val = 0;
1573 s32 err = 0;
1574
1575 switch (sme->auth_type) {
1576 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1577 val = 0;
1578 WL_CONN("open system\n");
1579 break;
1580 case NL80211_AUTHTYPE_SHARED_KEY:
1581 val = 1;
1582 WL_CONN("shared key\n");
1583 break;
1584 case NL80211_AUTHTYPE_AUTOMATIC:
1585 val = 2;
1586 WL_CONN("automatic\n");
1587 break;
1588 case NL80211_AUTHTYPE_NETWORK_EAP:
1589 WL_CONN("network eap\n");
1590 default:
1591 val = 2;
1592 WL_ERR("invalid auth type (%d)\n", sme->auth_type);
1593 break;
1594 }
1595
1596 err = brcmf_dev_intvar_set(ndev, "auth", val);
1597 if (err) {
1598 WL_ERR("set auth failed (%d)\n", err);
1599 return err;
1600 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001601 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001602 sec->auth_type = sme->auth_type;
1603 return err;
1604}
1605
1606static s32
1607brcmf_set_set_cipher(struct net_device *ndev,
1608 struct cfg80211_connect_params *sme)
1609{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001610 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001611 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001612 struct brcmf_cfg80211_security *sec;
1613 s32 pval = 0;
1614 s32 gval = 0;
1615 s32 err = 0;
1616
1617 if (sme->crypto.n_ciphers_pairwise) {
1618 switch (sme->crypto.ciphers_pairwise[0]) {
1619 case WLAN_CIPHER_SUITE_WEP40:
1620 case WLAN_CIPHER_SUITE_WEP104:
1621 pval = WEP_ENABLED;
1622 break;
1623 case WLAN_CIPHER_SUITE_TKIP:
1624 pval = TKIP_ENABLED;
1625 break;
1626 case WLAN_CIPHER_SUITE_CCMP:
1627 pval = AES_ENABLED;
1628 break;
1629 case WLAN_CIPHER_SUITE_AES_CMAC:
1630 pval = AES_ENABLED;
1631 break;
1632 default:
1633 WL_ERR("invalid cipher pairwise (%d)\n",
1634 sme->crypto.ciphers_pairwise[0]);
1635 return -EINVAL;
1636 }
1637 }
1638 if (sme->crypto.cipher_group) {
1639 switch (sme->crypto.cipher_group) {
1640 case WLAN_CIPHER_SUITE_WEP40:
1641 case WLAN_CIPHER_SUITE_WEP104:
1642 gval = WEP_ENABLED;
1643 break;
1644 case WLAN_CIPHER_SUITE_TKIP:
1645 gval = TKIP_ENABLED;
1646 break;
1647 case WLAN_CIPHER_SUITE_CCMP:
1648 gval = AES_ENABLED;
1649 break;
1650 case WLAN_CIPHER_SUITE_AES_CMAC:
1651 gval = AES_ENABLED;
1652 break;
1653 default:
1654 WL_ERR("invalid cipher group (%d)\n",
1655 sme->crypto.cipher_group);
1656 return -EINVAL;
1657 }
1658 }
1659
1660 WL_CONN("pval (%d) gval (%d)\n", pval, gval);
1661 err = brcmf_dev_intvar_set(ndev, "wsec", pval | gval);
1662 if (err) {
1663 WL_ERR("error (%d)\n", err);
1664 return err;
1665 }
1666
Arend van Spriel06bb1232012-09-27 14:17:56 +02001667 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001668 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1669 sec->cipher_group = sme->crypto.cipher_group;
1670
1671 return err;
1672}
1673
1674static s32
1675brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1676{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001677 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001678 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001679 struct brcmf_cfg80211_security *sec;
1680 s32 val = 0;
1681 s32 err = 0;
1682
1683 if (sme->crypto.n_akm_suites) {
1684 err = brcmf_dev_intvar_get(ndev, "wpa_auth", &val);
1685 if (err) {
1686 WL_ERR("could not get wpa_auth (%d)\n", err);
1687 return err;
1688 }
1689 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1690 switch (sme->crypto.akm_suites[0]) {
1691 case WLAN_AKM_SUITE_8021X:
1692 val = WPA_AUTH_UNSPECIFIED;
1693 break;
1694 case WLAN_AKM_SUITE_PSK:
1695 val = WPA_AUTH_PSK;
1696 break;
1697 default:
1698 WL_ERR("invalid cipher group (%d)\n",
1699 sme->crypto.cipher_group);
1700 return -EINVAL;
1701 }
1702 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1703 switch (sme->crypto.akm_suites[0]) {
1704 case WLAN_AKM_SUITE_8021X:
1705 val = WPA2_AUTH_UNSPECIFIED;
1706 break;
1707 case WLAN_AKM_SUITE_PSK:
1708 val = WPA2_AUTH_PSK;
1709 break;
1710 default:
1711 WL_ERR("invalid cipher group (%d)\n",
1712 sme->crypto.cipher_group);
1713 return -EINVAL;
1714 }
1715 }
1716
1717 WL_CONN("setting wpa_auth to %d\n", val);
1718 err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
1719 if (err) {
1720 WL_ERR("could not set wpa_auth (%d)\n", err);
1721 return err;
1722 }
1723 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001724 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001725 sec->wpa_auth = sme->crypto.akm_suites[0];
1726
1727 return err;
1728}
1729
1730static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001731brcmf_set_sharedkey(struct net_device *ndev,
1732 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001733{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001734 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001735 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001736 struct brcmf_cfg80211_security *sec;
1737 struct brcmf_wsec_key key;
1738 s32 val;
1739 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001740 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001741
1742 WL_CONN("key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001743
Roland Vossena718e2f2011-10-12 20:51:24 +02001744 if (sme->key_len == 0)
1745 return 0;
1746
Arend van Spriel06bb1232012-09-27 14:17:56 +02001747 sec = &profile->sec;
Roland Vossena718e2f2011-10-12 20:51:24 +02001748 WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n",
1749 sec->wpa_versions, sec->cipher_pairwise);
1750
1751 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1752 return 0;
1753
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001754 if (!(sec->cipher_pairwise &
1755 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1756 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001757
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001758 memset(&key, 0, sizeof(key));
1759 key.len = (u32) sme->key_len;
1760 key.index = (u32) sme->key_idx;
1761 if (key.len > sizeof(key.data)) {
1762 WL_ERR("Too long key length (%u)\n", key.len);
1763 return -EINVAL;
1764 }
1765 memcpy(key.data, sme->key, key.len);
1766 key.flags = BRCMF_PRIMARY_KEY;
1767 switch (sec->cipher_pairwise) {
1768 case WLAN_CIPHER_SUITE_WEP40:
1769 key.algo = CRYPTO_ALGO_WEP1;
1770 break;
1771 case WLAN_CIPHER_SUITE_WEP104:
1772 key.algo = CRYPTO_ALGO_WEP128;
1773 break;
1774 default:
1775 WL_ERR("Invalid algorithm (%d)\n",
1776 sme->crypto.ciphers_pairwise[0]);
1777 return -EINVAL;
1778 }
1779 /* Set the new key/index */
1780 WL_CONN("key length (%d) key index (%d) algo (%d)\n",
1781 key.len, key.index, key.algo);
1782 WL_CONN("key \"%s\"\n", key.data);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001783 bssidx = brcmf_find_bssidx(cfg, ndev);
1784 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001785 if (err)
1786 return err;
1787
1788 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
1789 WL_CONN("set auth_type to shared key\n");
1790 val = WL_AUTH_SHARED_KEY; /* shared key */
1791 err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", val, bssidx);
1792 if (err)
1793 WL_ERR("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001794 }
1795 return err;
1796}
1797
1798static s32
1799brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
1800 struct cfg80211_connect_params *sme)
1801{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001802 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001803 struct ieee80211_channel *chan = sme->channel;
1804 struct brcmf_join_params join_params;
1805 size_t join_params_size;
1806 struct brcmf_ssid ssid;
1807
1808 s32 err = 0;
1809
1810 WL_TRACE("Enter\n");
1811 if (!check_sys_up(wiphy))
1812 return -EIO;
1813
1814 if (!sme->ssid) {
1815 WL_ERR("Invalid ssid\n");
1816 return -EOPNOTSUPP;
1817 }
1818
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001819 set_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001820
1821 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001822 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001823 ieee80211_frequency_to_channel(chan->center_freq);
1824 WL_CONN("channel (%d), center_req (%d)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001825 cfg->channel, chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001826 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001827 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001828
1829 WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
1830
1831 err = brcmf_set_wpa_version(ndev, sme);
1832 if (err) {
1833 WL_ERR("wl_set_wpa_version failed (%d)\n", err);
1834 goto done;
1835 }
1836
1837 err = brcmf_set_auth_type(ndev, sme);
1838 if (err) {
1839 WL_ERR("wl_set_auth_type failed (%d)\n", err);
1840 goto done;
1841 }
1842
1843 err = brcmf_set_set_cipher(ndev, sme);
1844 if (err) {
1845 WL_ERR("wl_set_set_cipher failed (%d)\n", err);
1846 goto done;
1847 }
1848
1849 err = brcmf_set_key_mgmt(ndev, sme);
1850 if (err) {
1851 WL_ERR("wl_set_key_mgmt failed (%d)\n", err);
1852 goto done;
1853 }
1854
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001855 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001856 if (err) {
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001857 WL_ERR("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001858 goto done;
1859 }
1860
1861 memset(&join_params, 0, sizeof(join_params));
1862 join_params_size = sizeof(join_params.ssid_le);
1863
Arend van Spriel23159922012-02-09 21:09:00 +01001864 ssid.SSID_len = min_t(u32, sizeof(ssid.SSID), (u32)sme->ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001865 memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid.SSID_len);
1866 memcpy(&ssid.SSID, sme->ssid, ssid.SSID_len);
1867 join_params.ssid_le.SSID_len = cpu_to_le32(ssid.SSID_len);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001868 brcmf_update_prof(cfg, NULL, &ssid, WL_PROF_SSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001869
1870 memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
1871
1872 if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
1873 WL_CONN("ssid \"%s\", len (%d)\n",
1874 ssid.SSID, ssid.SSID_len);
1875
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001876 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001877 &join_params, &join_params_size);
1878 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
1879 &join_params, join_params_size);
1880 if (err)
1881 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1882
1883done:
1884 if (err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001885 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001886 WL_TRACE("Exit\n");
1887 return err;
1888}
1889
1890static s32
1891brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1892 u16 reason_code)
1893{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001894 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001895 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001896 struct brcmf_scb_val_le scbval;
1897 s32 err = 0;
1898
1899 WL_TRACE("Enter. Reason code = %d\n", reason_code);
1900 if (!check_sys_up(wiphy))
1901 return -EIO;
1902
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001903 clear_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001904
Arend van Spriel06bb1232012-09-27 14:17:56 +02001905 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001906 scbval.val = cpu_to_le32(reason_code);
1907 err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, &scbval,
1908 sizeof(struct brcmf_scb_val_le));
1909 if (err)
1910 WL_ERR("error (%d)\n", err);
1911
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001912 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001913
1914 WL_TRACE("Exit\n");
1915 return err;
1916}
1917
1918static s32
1919brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001920 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001921{
1922
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001923 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1924 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001925 u16 txpwrmw;
1926 s32 err = 0;
1927 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001928 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001929
1930 WL_TRACE("Enter\n");
1931 if (!check_sys_up(wiphy))
1932 return -EIO;
1933
1934 switch (type) {
1935 case NL80211_TX_POWER_AUTOMATIC:
1936 break;
1937 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001938 case NL80211_TX_POWER_FIXED:
1939 if (dbm < 0) {
1940 WL_ERR("TX_POWER_FIXED - dbm is negative\n");
1941 err = -EINVAL;
1942 goto done;
1943 }
1944 break;
1945 }
1946 /* Make sure radio is off or on as far as software is concerned */
1947 disable = WL_RADIO_SW_DISABLE << 16;
1948 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_RADIO, &disable);
1949 if (err)
1950 WL_ERR("WLC_SET_RADIO error (%d)\n", err);
1951
1952 if (dbm > 0xffff)
1953 txpwrmw = 0xffff;
1954 else
1955 txpwrmw = (u16) dbm;
1956 err = brcmf_dev_intvar_set(ndev, "qtxpower",
Alwin Beukersef6ac172011-10-12 20:51:26 +02001957 (s32) (brcmf_mw_to_qdbm(txpwrmw)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001958 if (err)
1959 WL_ERR("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001960 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001961
1962done:
1963 WL_TRACE("Exit\n");
1964 return err;
1965}
1966
1967static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
1968{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001969 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1970 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 s32 txpwrdbm;
1972 u8 result;
1973 s32 err = 0;
1974
1975 WL_TRACE("Enter\n");
1976 if (!check_sys_up(wiphy))
1977 return -EIO;
1978
1979 err = brcmf_dev_intvar_get(ndev, "qtxpower", &txpwrdbm);
1980 if (err) {
1981 WL_ERR("error (%d)\n", err);
1982 goto done;
1983 }
1984
1985 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001986 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001987
1988done:
1989 WL_TRACE("Exit\n");
1990 return err;
1991}
1992
1993static s32
1994brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1995 u8 key_idx, bool unicast, bool multicast)
1996{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001997 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001998 u32 index;
1999 u32 wsec;
2000 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002001 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002002
2003 WL_TRACE("Enter\n");
2004 WL_CONN("key index (%d)\n", key_idx);
2005 if (!check_sys_up(wiphy))
2006 return -EIO;
2007
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002008 bssidx = brcmf_find_bssidx(cfg, ndev);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002009 err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002010 if (err) {
2011 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
2012 goto done;
2013 }
2014
2015 if (wsec & WEP_ENABLED) {
2016 /* Just select a new current key */
2017 index = key_idx;
2018 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_KEY_PRIMARY,
2019 &index);
2020 if (err)
2021 WL_ERR("error (%d)\n", err);
2022 }
2023done:
2024 WL_TRACE("Exit\n");
2025 return err;
2026}
2027
2028static s32
2029brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2030 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2031{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002032 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002033 struct brcmf_wsec_key key;
2034 struct brcmf_wsec_key_le key_le;
2035 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002036 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002037
2038 memset(&key, 0, sizeof(key));
2039 key.index = (u32) key_idx;
2040 /* Instead of bcast for ea address for default wep keys,
2041 driver needs it to be Null */
2042 if (!is_multicast_ether_addr(mac_addr))
2043 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2044 key.len = (u32) params->key_len;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002045 bssidx = brcmf_find_bssidx(cfg, ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002046 /* check for key index change */
2047 if (key.len == 0) {
2048 /* key delete */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002049 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002050 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002051 WL_ERR("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002052 } else {
2053 if (key.len > sizeof(key.data)) {
2054 WL_ERR("Invalid key length (%d)\n", key.len);
2055 return -EINVAL;
2056 }
2057
2058 WL_CONN("Setting the key index %d\n", key.index);
2059 memcpy(key.data, params->key, key.len);
2060
2061 if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
2062 u8 keybuf[8];
2063 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2064 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2065 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2066 }
2067
2068 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2069 if (params->seq && params->seq_len == 6) {
2070 /* rx iv */
2071 u8 *ivptr;
2072 ivptr = (u8 *) params->seq;
2073 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2074 (ivptr[3] << 8) | ivptr[2];
2075 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2076 key.iv_initialized = true;
2077 }
2078
2079 switch (params->cipher) {
2080 case WLAN_CIPHER_SUITE_WEP40:
2081 key.algo = CRYPTO_ALGO_WEP1;
2082 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
2083 break;
2084 case WLAN_CIPHER_SUITE_WEP104:
2085 key.algo = CRYPTO_ALGO_WEP128;
2086 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
2087 break;
2088 case WLAN_CIPHER_SUITE_TKIP:
2089 key.algo = CRYPTO_ALGO_TKIP;
2090 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
2091 break;
2092 case WLAN_CIPHER_SUITE_AES_CMAC:
2093 key.algo = CRYPTO_ALGO_AES_CCM;
2094 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
2095 break;
2096 case WLAN_CIPHER_SUITE_CCMP:
2097 key.algo = CRYPTO_ALGO_AES_CCM;
2098 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
2099 break;
2100 default:
2101 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
2102 return -EINVAL;
2103 }
2104 convert_key_from_CPU(&key, &key_le);
2105
2106 brcmf_netdev_wait_pend8021x(ndev);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002107 err = brcmf_dev_iovar_setbuf_bsscfg(ndev, "wsec_key", &key_le,
2108 sizeof(key_le),
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002109 cfg->extra_buf,
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002110 WL_EXTRA_BUF_MAX, bssidx);
2111 if (err)
2112 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002113 }
2114 return err;
2115}
2116
2117static s32
2118brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2119 u8 key_idx, bool pairwise, const u8 *mac_addr,
2120 struct key_params *params)
2121{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002122 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002123 struct brcmf_wsec_key key;
2124 s32 val;
2125 s32 wsec;
2126 s32 err = 0;
2127 u8 keybuf[8];
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002128 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002129
2130 WL_TRACE("Enter\n");
2131 WL_CONN("key index (%d)\n", key_idx);
2132 if (!check_sys_up(wiphy))
2133 return -EIO;
2134
2135 if (mac_addr) {
2136 WL_TRACE("Exit");
2137 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2138 }
2139 memset(&key, 0, sizeof(key));
2140
2141 key.len = (u32) params->key_len;
2142 key.index = (u32) key_idx;
2143
2144 if (key.len > sizeof(key.data)) {
2145 WL_ERR("Too long key length (%u)\n", key.len);
2146 err = -EINVAL;
2147 goto done;
2148 }
2149 memcpy(key.data, params->key, key.len);
2150
2151 key.flags = BRCMF_PRIMARY_KEY;
2152 switch (params->cipher) {
2153 case WLAN_CIPHER_SUITE_WEP40:
2154 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002155 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002156 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
2157 break;
2158 case WLAN_CIPHER_SUITE_WEP104:
2159 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002160 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002161 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
2162 break;
2163 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002164 if (cfg->conf->mode != WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002165 WL_CONN("Swapping key\n");
2166 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2167 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2168 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2169 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002170 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002171 val = TKIP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002172 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
2173 break;
2174 case WLAN_CIPHER_SUITE_AES_CMAC:
2175 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002176 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002177 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
2178 break;
2179 case WLAN_CIPHER_SUITE_CCMP:
2180 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002181 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002182 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
2183 break;
2184 default:
2185 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
2186 err = -EINVAL;
2187 goto done;
2188 }
2189
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002190 bssidx = brcmf_find_bssidx(cfg, ndev);
2191 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002192 if (err)
2193 goto done;
2194
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002195 err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002196 if (err) {
2197 WL_ERR("get wsec error (%d)\n", err);
2198 goto done;
2199 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002200 wsec |= val;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002201 err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002202 if (err) {
2203 WL_ERR("set wsec error (%d)\n", err);
2204 goto done;
2205 }
2206
Arend van Spriel5b435de2011-10-05 13:19:03 +02002207done:
2208 WL_TRACE("Exit\n");
2209 return err;
2210}
2211
2212static s32
2213brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2214 u8 key_idx, bool pairwise, const u8 *mac_addr)
2215{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002216 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002217 struct brcmf_wsec_key key;
2218 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002219 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002220
2221 WL_TRACE("Enter\n");
2222 if (!check_sys_up(wiphy))
2223 return -EIO;
2224
2225 memset(&key, 0, sizeof(key));
2226
2227 key.index = (u32) key_idx;
2228 key.flags = BRCMF_PRIMARY_KEY;
2229 key.algo = CRYPTO_ALGO_OFF;
2230
2231 WL_CONN("key index (%d)\n", key_idx);
2232
2233 /* Set the new key/index */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002234 bssidx = brcmf_find_bssidx(cfg, ndev);
2235 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236 if (err) {
2237 if (err == -EINVAL) {
2238 if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2239 /* we ignore this key index in this case */
2240 WL_ERR("invalid key index (%d)\n", key_idx);
2241 }
2242 /* Ignore this error, may happen during DISASSOC */
2243 err = -EAGAIN;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002244 }
2245
Arend van Spriel5b435de2011-10-05 13:19:03 +02002246 WL_TRACE("Exit\n");
2247 return err;
2248}
2249
2250static s32
2251brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2252 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2253 void (*callback) (void *cookie, struct key_params * params))
2254{
2255 struct key_params params;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002256 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02002257 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002258 struct brcmf_cfg80211_security *sec;
2259 s32 wsec;
2260 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002261 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002262
2263 WL_TRACE("Enter\n");
2264 WL_CONN("key index (%d)\n", key_idx);
2265 if (!check_sys_up(wiphy))
2266 return -EIO;
2267
2268 memset(&params, 0, sizeof(params));
2269
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002270 bssidx = brcmf_find_bssidx(cfg, ndev);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002271 err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002272 if (err) {
2273 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
2274 /* Ignore this error, may happen during DISASSOC */
2275 err = -EAGAIN;
2276 goto done;
2277 }
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002278 switch (wsec & ~SES_OW_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002279 case WEP_ENABLED:
Arend van Spriel06bb1232012-09-27 14:17:56 +02002280 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002281 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2282 params.cipher = WLAN_CIPHER_SUITE_WEP40;
2283 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
2284 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2285 params.cipher = WLAN_CIPHER_SUITE_WEP104;
2286 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
2287 }
2288 break;
2289 case TKIP_ENABLED:
2290 params.cipher = WLAN_CIPHER_SUITE_TKIP;
2291 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
2292 break;
2293 case AES_ENABLED:
2294 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
2295 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
2296 break;
2297 default:
2298 WL_ERR("Invalid algo (0x%x)\n", wsec);
2299 err = -EINVAL;
2300 goto done;
2301 }
2302 callback(cookie, &params);
2303
2304done:
2305 WL_TRACE("Exit\n");
2306 return err;
2307}
2308
2309static s32
2310brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2311 struct net_device *ndev, u8 key_idx)
2312{
2313 WL_INFO("Not supported\n");
2314
2315 return -EOPNOTSUPP;
2316}
2317
2318static s32
2319brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02002320 u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002321{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002322 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02002323 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002324 struct brcmf_scb_val_le scb_val;
2325 int rssi;
2326 s32 rate;
2327 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02002328 u8 *bssid = profile->bssid;
Hante Meuleman1a873342012-09-27 14:17:54 +02002329 struct brcmf_sta_info_le *sta_info_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002330
Hante Meuleman1a873342012-09-27 14:17:54 +02002331 WL_TRACE("Enter, MAC %pM\n", mac);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002332 if (!check_sys_up(wiphy))
2333 return -EIO;
2334
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002335 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002336 err = brcmf_dev_iovar_getbuf(ndev, "sta_info", mac, ETH_ALEN,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002337 cfg->dcmd_buf,
Hante Meuleman1a873342012-09-27 14:17:54 +02002338 WL_DCMD_LEN_MAX);
2339 if (err < 0) {
2340 WL_ERR("GET STA INFO failed, %d\n", err);
2341 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002342 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002343 sta_info_le = (struct brcmf_sta_info_le *)cfg->dcmd_buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002344
Hante Meuleman1a873342012-09-27 14:17:54 +02002345 sinfo->filled = STATION_INFO_INACTIVE_TIME;
2346 sinfo->inactive_time = le32_to_cpu(sta_info_le->idle) * 1000;
2347 if (le32_to_cpu(sta_info_le->flags) & BRCMF_STA_ASSOC) {
2348 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
2349 sinfo->connected_time = le32_to_cpu(sta_info_le->in);
2350 }
2351 WL_TRACE("STA idle time : %d ms, connected time :%d sec\n",
2352 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002353 } else if (cfg->conf->mode == WL_MODE_BSS) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002354 if (memcmp(mac, bssid, ETH_ALEN)) {
2355 WL_ERR("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
2356 mac, bssid);
2357 err = -ENOENT;
2358 goto done;
2359 }
2360 /* Report the current tx rate */
2361 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_RATE, &rate);
2362 if (err) {
2363 WL_ERR("Could not get rate (%d)\n", err);
2364 goto done;
2365 } else {
2366 sinfo->filled |= STATION_INFO_TX_BITRATE;
2367 sinfo->txrate.legacy = rate * 5;
2368 WL_CONN("Rate %d Mbps\n", rate / 2);
2369 }
2370
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002371 if (test_bit(WL_STATUS_CONNECTED, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002372 memset(&scb_val, 0, sizeof(scb_val));
2373 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
2374 sizeof(scb_val));
2375 if (err) {
2376 WL_ERR("Could not get rssi (%d)\n", err);
2377 goto done;
2378 } else {
2379 rssi = le32_to_cpu(scb_val.val);
2380 sinfo->filled |= STATION_INFO_SIGNAL;
2381 sinfo->signal = rssi;
2382 WL_CONN("RSSI %d dBm\n", rssi);
2383 }
2384 }
2385 } else
2386 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002387done:
2388 WL_TRACE("Exit\n");
2389 return err;
2390}
2391
2392static s32
2393brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2394 bool enabled, s32 timeout)
2395{
2396 s32 pm;
2397 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002398 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002399
2400 WL_TRACE("Enter\n");
2401
2402 /*
2403 * Powersave enable/disable request is coming from the
2404 * cfg80211 even before the interface is up. In that
2405 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002406 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002407 * FW later while initializing the dongle
2408 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002409 cfg->pwr_save = enabled;
2410 if (!test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002411
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002412 WL_INFO("Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002413 goto done;
2414 }
2415
2416 pm = enabled ? PM_FAST : PM_OFF;
2417 WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
2418
2419 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &pm);
2420 if (err) {
2421 if (err == -ENODEV)
2422 WL_ERR("net_device is not ready yet\n");
2423 else
2424 WL_ERR("error (%d)\n", err);
2425 }
2426done:
2427 WL_TRACE("Exit\n");
2428 return err;
2429}
2430
2431static s32
2432brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
2433 const u8 *addr,
2434 const struct cfg80211_bitrate_mask *mask)
2435{
2436 struct brcm_rateset_le rateset_le;
2437 s32 rate;
2438 s32 val;
2439 s32 err_bg;
2440 s32 err_a;
2441 u32 legacy;
2442 s32 err = 0;
2443
2444 WL_TRACE("Enter\n");
2445 if (!check_sys_up(wiphy))
2446 return -EIO;
2447
2448 /* addr param is always NULL. ignore it */
2449 /* Get current rateset */
2450 err = brcmf_exec_dcmd(ndev, BRCM_GET_CURR_RATESET, &rateset_le,
2451 sizeof(rateset_le));
2452 if (err) {
2453 WL_ERR("could not get current rateset (%d)\n", err);
2454 goto done;
2455 }
2456
2457 legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
2458 if (!legacy)
2459 legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
2460 0xFFFF);
2461
2462 val = wl_g_rates[legacy - 1].bitrate * 100000;
2463
2464 if (val < le32_to_cpu(rateset_le.count))
2465 /* Select rate by rateset index */
2466 rate = rateset_le.rates[val] & 0x7f;
2467 else
2468 /* Specified rate in bps */
2469 rate = val / 500000;
2470
2471 WL_CONN("rate %d mbps\n", rate / 2);
2472
2473 /*
2474 *
2475 * Set rate override,
2476 * Since the is a/b/g-blind, both a/bg_rate are enforced.
2477 */
2478 err_bg = brcmf_dev_intvar_set(ndev, "bg_rate", rate);
2479 err_a = brcmf_dev_intvar_set(ndev, "a_rate", rate);
2480 if (err_bg && err_a) {
2481 WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
2482 err = err_bg | err_a;
2483 }
2484
2485done:
2486 WL_TRACE("Exit\n");
2487 return err;
2488}
2489
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002490static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002491 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002492{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002493 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002494 struct ieee80211_channel *notify_channel;
2495 struct cfg80211_bss *bss;
2496 struct ieee80211_supported_band *band;
2497 s32 err = 0;
2498 u16 channel;
2499 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002500 u16 notify_capability;
2501 u16 notify_interval;
2502 u8 *notify_ie;
2503 size_t notify_ielen;
2504 s32 notify_signal;
2505
2506 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
2507 WL_ERR("Bss info is larger than buffer. Discarding\n");
2508 return 0;
2509 }
2510
2511 channel = bi->ctl_ch ? bi->ctl_ch :
2512 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2513
2514 if (channel <= CH_MAX_2G_CHANNEL)
2515 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2516 else
2517 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2518
2519 freq = ieee80211_channel_to_frequency(channel, band->band);
2520 notify_channel = ieee80211_get_channel(wiphy, freq);
2521
Arend van Spriel5b435de2011-10-05 13:19:03 +02002522 notify_capability = le16_to_cpu(bi->capability);
2523 notify_interval = le16_to_cpu(bi->beacon_period);
2524 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2525 notify_ielen = le32_to_cpu(bi->ie_length);
2526 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2527
2528 WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
2529 bi->BSSID[0], bi->BSSID[1], bi->BSSID[2],
2530 bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]);
2531 WL_CONN("Channel: %d(%d)\n", channel, freq);
2532 WL_CONN("Capability: %X\n", notify_capability);
2533 WL_CONN("Beacon interval: %d\n", notify_interval);
2534 WL_CONN("Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002535
2536 bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002537 0, notify_capability, notify_interval, notify_ie,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002538 notify_ielen, notify_signal, GFP_KERNEL);
2539
Franky Line78946e2011-11-10 20:30:34 +01002540 if (!bss)
2541 return -ENOMEM;
2542
2543 cfg80211_put_bss(bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002544
2545 return err;
2546}
2547
Roland Vossen6f09be02011-10-18 14:03:02 +02002548static struct brcmf_bss_info_le *
2549next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2550{
2551 if (bss == NULL)
2552 return list->bss_info_le;
2553 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2554 le32_to_cpu(bss->length));
2555}
2556
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002557static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002558{
2559 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002560 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002561 s32 err = 0;
2562 int i;
2563
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002564 bss_list = cfg->bss_list;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002565 if (bss_list->version != BRCMF_BSS_INFO_VERSION) {
2566 WL_ERR("Version %d != WL_BSS_INFO_VERSION\n",
2567 bss_list->version);
2568 return -EOPNOTSUPP;
2569 }
2570 WL_SCAN("scanned AP count (%d)\n", bss_list->count);
2571 for (i = 0; i < bss_list->count && i < WL_AP_MAX; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002572 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002573 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002574 if (err)
2575 break;
2576 }
2577 return err;
2578}
2579
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002580static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002581 struct net_device *ndev, const u8 *bssid)
2582{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002583 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002584 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002585 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002586 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002587 struct cfg80211_bss *bss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002588 u8 *buf = NULL;
2589 s32 err = 0;
2590 u16 channel;
2591 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002592 u16 notify_capability;
2593 u16 notify_interval;
2594 u8 *notify_ie;
2595 size_t notify_ielen;
2596 s32 notify_signal;
2597
2598 WL_TRACE("Enter\n");
2599
2600 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2601 if (buf == NULL) {
2602 err = -ENOMEM;
2603 goto CleanUp;
2604 }
2605
2606 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2607
2608 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX);
2609 if (err) {
2610 WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
2611 goto CleanUp;
2612 }
2613
Roland Vossend34bf642011-10-18 14:03:01 +02002614 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002615
2616 channel = bi->ctl_ch ? bi->ctl_ch :
2617 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2618
2619 if (channel <= CH_MAX_2G_CHANNEL)
2620 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2621 else
2622 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2623
2624 freq = ieee80211_channel_to_frequency(channel, band->band);
2625 notify_channel = ieee80211_get_channel(wiphy, freq);
2626
Arend van Spriel5b435de2011-10-05 13:19:03 +02002627 notify_capability = le16_to_cpu(bi->capability);
2628 notify_interval = le16_to_cpu(bi->beacon_period);
2629 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2630 notify_ielen = le32_to_cpu(bi->ie_length);
2631 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2632
2633 WL_CONN("channel: %d(%d)\n", channel, freq);
2634 WL_CONN("capability: %X\n", notify_capability);
2635 WL_CONN("beacon interval: %d\n", notify_interval);
2636 WL_CONN("signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002637
Franky Line78946e2011-11-10 20:30:34 +01002638 bss = cfg80211_inform_bss(wiphy, notify_channel, bssid,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002639 0, notify_capability, notify_interval,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002640 notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
2641
Franky Line78946e2011-11-10 20:30:34 +01002642 if (!bss) {
2643 err = -ENOMEM;
2644 goto CleanUp;
2645 }
2646
2647 cfg80211_put_bss(bss);
2648
Arend van Spriel5b435de2011-10-05 13:19:03 +02002649CleanUp:
2650
2651 kfree(buf);
2652
2653 WL_TRACE("Exit\n");
2654
2655 return err;
2656}
2657
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002658static bool brcmf_is_ibssmode(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002659{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002660 return cfg->conf->mode == WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002661}
2662
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002663/*
2664 * Traverse a string of 1-byte tag/1-byte length/variable-length value
2665 * triples, returning a pointer to the substring whose first element
2666 * matches tag
2667 */
2668static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
2669{
2670 struct brcmf_tlv *elt;
2671 int totlen;
2672
2673 elt = (struct brcmf_tlv *) buf;
2674 totlen = buflen;
2675
2676 /* find tagged parameter */
Hante Meuleman04012892012-09-27 14:17:49 +02002677 while (totlen >= TLV_HDR_LEN) {
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002678 int len = elt->len;
2679
2680 /* validate remaining totlen */
Hante Meuleman04012892012-09-27 14:17:49 +02002681 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002682 return elt;
2683
Hante Meuleman04012892012-09-27 14:17:49 +02002684 elt = (struct brcmf_tlv *) ((u8 *) elt + (len + TLV_HDR_LEN));
2685 totlen -= (len + TLV_HDR_LEN);
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002686 }
2687
2688 return NULL;
2689}
2690
Hante Meuleman1a873342012-09-27 14:17:54 +02002691/* Is any of the tlvs the expected entry? If
2692 * not update the tlvs buffer pointer/length.
2693 */
2694static bool
2695brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
2696 u8 *oui, u32 oui_len, u8 type)
2697{
2698 /* If the contents match the OUI and the type */
2699 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
2700 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
2701 type == ie[TLV_BODY_OFF + oui_len]) {
2702 return true;
2703 }
2704
2705 if (tlvs == NULL)
2706 return false;
2707 /* point to the next ie */
2708 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
2709 /* calculate the length of the rest of the buffer */
2710 *tlvs_len -= (int)(ie - *tlvs);
2711 /* update the pointer to the start of the buffer */
2712 *tlvs = ie;
2713
2714 return false;
2715}
2716
2717struct brcmf_vs_tlv *
2718brcmf_find_wpaie(u8 *parse, u32 len)
2719{
2720 struct brcmf_tlv *ie;
2721
2722 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_WPA))) {
2723 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
2724 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
2725 return (struct brcmf_vs_tlv *)ie;
2726 }
2727 return NULL;
2728}
2729
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002730static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002731{
Arend van Spriel06bb1232012-09-27 14:17:56 +02002732 struct brcmf_cfg80211_profile *profile = cfg->profile;
Roland Vossend34bf642011-10-18 14:03:01 +02002733 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002734 struct brcmf_ssid *ssid;
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002735 struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002736 u16 beacon_interval;
2737 u8 dtim_period;
2738 size_t ie_len;
2739 u8 *ie;
2740 s32 err = 0;
2741
2742 WL_TRACE("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002743 if (brcmf_is_ibssmode(cfg))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002744 return err;
2745
Arend van Spriel06bb1232012-09-27 14:17:56 +02002746 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002747
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002748 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
2749 err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCMF_C_GET_BSS_INFO,
2750 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002751 if (err) {
2752 WL_ERR("Could not get bss info %d\n", err);
2753 goto update_bss_info_out;
2754 }
2755
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002756 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2757 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002758 if (err)
2759 goto update_bss_info_out;
2760
2761 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2762 ie_len = le32_to_cpu(bi->ie_length);
2763 beacon_interval = le16_to_cpu(bi->beacon_period);
2764
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002765 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002766 if (tim)
2767 dtim_period = tim->data[1];
2768 else {
2769 /*
2770 * active scan was done so we could not get dtim
2771 * information out of probe response.
2772 * so we speficially query dtim information to dongle.
2773 */
2774 u32 var;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002775 err = brcmf_dev_intvar_get(cfg_to_ndev(cfg),
Arend van Spriel5b435de2011-10-05 13:19:03 +02002776 "dtim_assoc", &var);
2777 if (err) {
2778 WL_ERR("wl dtim_assoc failed (%d)\n", err);
2779 goto update_bss_info_out;
2780 }
2781 dtim_period = (u8)var;
2782 }
2783
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002784 brcmf_update_prof(cfg, NULL, &beacon_interval, WL_PROF_BEACONINT);
2785 brcmf_update_prof(cfg, NULL, &dtim_period, WL_PROF_DTIMPERIOD);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002786
2787update_bss_info_out:
2788 WL_TRACE("Exit");
2789 return err;
2790}
2791
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002792static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002793{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002794 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
2795 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002796 struct brcmf_ssid ssid;
2797
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002798 set_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
2799 if (cfg->iscan_on) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002800 iscan->state = WL_ISCAN_STATE_IDLE;
2801
2802 if (iscan->timer_on) {
2803 del_timer_sync(&iscan->timer);
2804 iscan->timer_on = 0;
2805 }
2806
2807 cancel_work_sync(&iscan->work);
2808
2809 /* Abort iscan running in FW */
2810 memset(&ssid, 0, sizeof(ssid));
2811 brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002812
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002813 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002814 /* Indidate scan abort to cfg80211 layer */
2815 WL_INFO("Terminating scan in progress\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002816 cfg80211_scan_done(cfg->scan_request, true);
2817 cfg->scan_request = NULL;
Arend van Spriel108a4be2012-09-19 22:21:07 +02002818 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002819 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002820 if (cfg->escan_on && cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002821 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002822 brcmf_notify_escan_complete(cfg, escan->ndev, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002823 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002824 clear_bit(WL_STATUS_SCANNING, &cfg->status);
2825 clear_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002826}
2827
2828static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
2829 bool aborted)
2830{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002831 struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan);
2832 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002833
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002834 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002835 WL_ERR("Scan complete while device not scanning\n");
2836 return;
2837 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002838 if (cfg->scan_request) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002839 WL_SCAN("ISCAN Completed scan: %s\n",
2840 aborted ? "Aborted" : "Done");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002841 cfg80211_scan_done(cfg->scan_request, aborted);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002842 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002843 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002844 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002845 cfg->iscan_kickstart = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002846}
2847
2848static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan)
2849{
2850 if (iscan->state != WL_ISCAN_STATE_IDLE) {
2851 WL_SCAN("wake up iscan\n");
2852 schedule_work(&iscan->work);
2853 return 0;
2854 }
2855
2856 return -EIO;
2857}
2858
2859static s32
2860brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status,
2861 struct brcmf_scan_results **bss_list)
2862{
2863 struct brcmf_iscan_results list;
2864 struct brcmf_scan_results *results;
2865 struct brcmf_scan_results_le *results_le;
2866 struct brcmf_iscan_results *list_buf;
2867 s32 err = 0;
2868
2869 memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX);
2870 list_buf = (struct brcmf_iscan_results *)iscan->scan_buf;
2871 results = &list_buf->results;
2872 results_le = &list_buf->results_le;
2873 results->buflen = BRCMF_ISCAN_RESULTS_FIXED_SIZE;
2874 results->version = 0;
2875 results->count = 0;
2876
2877 memset(&list, 0, sizeof(list));
2878 list.results_le.buflen = cpu_to_le32(WL_ISCAN_BUF_MAX);
2879 err = brcmf_dev_iovar_getbuf(iscan->ndev, "iscanresults", &list,
2880 BRCMF_ISCAN_RESULTS_FIXED_SIZE,
2881 iscan->scan_buf, WL_ISCAN_BUF_MAX);
2882 if (err) {
2883 WL_ERR("error (%d)\n", err);
2884 return err;
2885 }
2886 results->buflen = le32_to_cpu(results_le->buflen);
2887 results->version = le32_to_cpu(results_le->version);
2888 results->count = le32_to_cpu(results_le->count);
2889 WL_SCAN("results->count = %d\n", results_le->count);
2890 WL_SCAN("results->buflen = %d\n", results_le->buflen);
2891 *status = le32_to_cpu(list_buf->status_le);
2892 WL_SCAN("status = %d\n", *status);
2893 *bss_list = results;
2894
2895 return err;
2896}
2897
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002898static s32 brcmf_iscan_done(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002899{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002900 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002901 s32 err = 0;
2902
2903 iscan->state = WL_ISCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002904 brcmf_inform_bss(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002905 brcmf_notify_iscan_complete(iscan, false);
2906
2907 return err;
2908}
2909
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002910static s32 brcmf_iscan_pending(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002911{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002912 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002913 s32 err = 0;
2914
2915 /* Reschedule the timer */
2916 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
2917 iscan->timer_on = 1;
2918
2919 return err;
2920}
2921
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002922static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002923{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002924 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002925 s32 err = 0;
2926
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002927 brcmf_inform_bss(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002928 brcmf_run_iscan(iscan, NULL, BRCMF_SCAN_ACTION_CONTINUE);
2929 /* Reschedule the timer */
2930 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
2931 iscan->timer_on = 1;
2932
2933 return err;
2934}
2935
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002936static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002937{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002938 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002939 s32 err = 0;
2940
2941 iscan->state = WL_ISCAN_STATE_IDLE;
2942 brcmf_notify_iscan_complete(iscan, true);
2943
2944 return err;
2945}
2946
2947static void brcmf_cfg80211_iscan_handler(struct work_struct *work)
2948{
2949 struct brcmf_cfg80211_iscan_ctrl *iscan =
2950 container_of(work, struct brcmf_cfg80211_iscan_ctrl,
2951 work);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002952 struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002953 struct brcmf_cfg80211_iscan_eloop *el = &iscan->el;
2954 u32 status = BRCMF_SCAN_RESULTS_PARTIAL;
2955
2956 if (iscan->timer_on) {
2957 del_timer_sync(&iscan->timer);
2958 iscan->timer_on = 0;
2959 }
2960
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002961 if (brcmf_get_iscan_results(iscan, &status, &cfg->bss_list)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002962 status = BRCMF_SCAN_RESULTS_ABORTED;
2963 WL_ERR("Abort iscan\n");
2964 }
2965
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002966 el->handler[status](cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002967}
2968
2969static void brcmf_iscan_timer(unsigned long data)
2970{
2971 struct brcmf_cfg80211_iscan_ctrl *iscan =
2972 (struct brcmf_cfg80211_iscan_ctrl *)data;
2973
2974 if (iscan) {
2975 iscan->timer_on = 0;
2976 WL_SCAN("timer expired\n");
2977 brcmf_wakeup_iscan(iscan);
2978 }
2979}
2980
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002981static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002982{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002983 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002984
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002985 if (cfg->iscan_on) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002986 iscan->state = WL_ISCAN_STATE_IDLE;
2987 INIT_WORK(&iscan->work, brcmf_cfg80211_iscan_handler);
2988 }
2989
2990 return 0;
2991}
2992
2993static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el)
2994{
2995 memset(el, 0, sizeof(*el));
2996 el->handler[BRCMF_SCAN_RESULTS_SUCCESS] = brcmf_iscan_done;
2997 el->handler[BRCMF_SCAN_RESULTS_PARTIAL] = brcmf_iscan_inprogress;
2998 el->handler[BRCMF_SCAN_RESULTS_PENDING] = brcmf_iscan_pending;
2999 el->handler[BRCMF_SCAN_RESULTS_ABORTED] = brcmf_iscan_aborted;
3000 el->handler[BRCMF_SCAN_RESULTS_NO_MEM] = brcmf_iscan_aborted;
3001}
3002
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003003static s32 brcmf_init_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003004{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003005 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003006 int err = 0;
3007
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003008 if (cfg->iscan_on) {
3009 iscan->ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003010 brcmf_init_iscan_eloop(&iscan->el);
3011 iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS;
3012 init_timer(&iscan->timer);
3013 iscan->timer.data = (unsigned long) iscan;
3014 iscan->timer.function = brcmf_iscan_timer;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003015 err = brcmf_invoke_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003016 if (!err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003017 iscan->data = cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003018 }
3019
3020 return err;
3021}
3022
Hante Meulemane756af52012-09-11 21:18:52 +02003023static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
3024{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003025 struct brcmf_cfg80211_info *cfg =
3026 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02003027 escan_timeout_work);
3028
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003029 brcmf_notify_escan_complete(cfg,
3030 cfg->escan_info.ndev, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02003031}
3032
3033static void brcmf_escan_timeout(unsigned long data)
3034{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003035 struct brcmf_cfg80211_info *cfg =
3036 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02003037
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003038 if (cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02003039 WL_ERR("timer expired\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003040 if (cfg->escan_on)
3041 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02003042 }
3043}
3044
3045static s32
3046brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
3047 struct brcmf_bss_info_le *bss_info_le)
3048{
3049 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
3050 (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
3051 CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
3052 bss_info_le->SSID_len == bss->SSID_len &&
3053 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
3054 if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
3055 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02003056 s16 bss_rssi = le16_to_cpu(bss->RSSI);
3057 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
3058
Hante Meulemane756af52012-09-11 21:18:52 +02003059 /* preserve max RSSI if the measurements are
3060 * both on-channel or both off-channel
3061 */
Arend van Spriel029591f2012-09-19 22:21:06 +02003062 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02003063 bss->RSSI = bss_info_le->RSSI;
3064 } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
3065 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
3066 /* preserve the on-channel rssi measurement
3067 * if the new measurement is off channel
3068 */
3069 bss->RSSI = bss_info_le->RSSI;
3070 bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
3071 }
3072 return 1;
3073 }
3074 return 0;
3075}
3076
3077static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003078brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +02003079 struct net_device *ndev,
3080 const struct brcmf_event_msg *e, void *data)
3081{
3082 s32 status;
3083 s32 err = 0;
3084 struct brcmf_escan_result_le *escan_result_le;
3085 struct brcmf_bss_info_le *bss_info_le;
3086 struct brcmf_bss_info_le *bss = NULL;
3087 u32 bi_length;
3088 struct brcmf_scan_results *list;
3089 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003090 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02003091
3092 status = be32_to_cpu(e->status);
3093
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003094 if (!ndev || !cfg->escan_on ||
3095 !test_bit(WL_STATUS_SCANNING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02003096 WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003097 ndev, cfg->escan_on,
3098 !test_bit(WL_STATUS_SCANNING, &cfg->status));
Hante Meulemane756af52012-09-11 21:18:52 +02003099 return -EPERM;
3100 }
3101
3102 if (status == BRCMF_E_STATUS_PARTIAL) {
3103 WL_SCAN("ESCAN Partial result\n");
3104 escan_result_le = (struct brcmf_escan_result_le *) data;
3105 if (!escan_result_le) {
3106 WL_ERR("Invalid escan result (NULL pointer)\n");
3107 goto exit;
3108 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003109 if (!cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02003110 WL_SCAN("result without cfg80211 request\n");
3111 goto exit;
3112 }
3113
3114 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
3115 WL_ERR("Invalid bss_count %d: ignoring\n",
3116 escan_result_le->bss_count);
3117 goto exit;
3118 }
3119 bss_info_le = &escan_result_le->bss_info_le;
3120
3121 bi_length = le32_to_cpu(bss_info_le->length);
3122 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
3123 WL_ESCAN_RESULTS_FIXED_SIZE)) {
3124 WL_ERR("Invalid bss_info length %d: ignoring\n",
3125 bi_length);
3126 goto exit;
3127 }
3128
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003129 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02003130 BIT(NL80211_IFTYPE_ADHOC))) {
3131 if (le16_to_cpu(bss_info_le->capability) &
3132 WLAN_CAPABILITY_IBSS) {
3133 WL_ERR("Ignoring IBSS result\n");
3134 goto exit;
3135 }
3136 }
3137
3138 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003139 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02003140 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
3141 WL_ERR("Buffer is too small: ignoring\n");
3142 goto exit;
3143 }
3144
3145 for (i = 0; i < list->count; i++) {
3146 bss = bss ? (struct brcmf_bss_info_le *)
3147 ((unsigned char *)bss +
3148 le32_to_cpu(bss->length)) : list->bss_info_le;
3149 if (brcmf_compare_update_same_bss(bss, bss_info_le))
3150 goto exit;
3151 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003152 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02003153 bss_info_le, bi_length);
3154 list->version = le32_to_cpu(bss_info_le->version);
3155 list->buflen += bi_length;
3156 list->count++;
3157 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003158 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3159 if (cfg->scan_request) {
3160 cfg->bss_list = (struct brcmf_scan_results *)
3161 cfg->escan_info.escan_buf;
3162 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003163 aborted = status != BRCMF_E_STATUS_SUCCESS;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003164 brcmf_notify_escan_complete(cfg, ndev, aborted,
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003165 false);
Hante Meulemane756af52012-09-11 21:18:52 +02003166 } else
3167 WL_ERR("Unexpected scan result 0x%x\n", status);
3168 }
3169exit:
3170 return err;
3171}
3172
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003173static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003174{
3175
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003176 if (cfg->escan_on) {
3177 cfg->el.handler[BRCMF_E_ESCAN_RESULT] =
Hante Meulemane756af52012-09-11 21:18:52 +02003178 brcmf_cfg80211_escan_handler;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003179 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meulemane756af52012-09-11 21:18:52 +02003180 /* Init scan_timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003181 init_timer(&cfg->escan_timeout);
3182 cfg->escan_timeout.data = (unsigned long) cfg;
3183 cfg->escan_timeout.function = brcmf_escan_timeout;
3184 INIT_WORK(&cfg->escan_timeout_work,
Hante Meulemane756af52012-09-11 21:18:52 +02003185 brcmf_cfg80211_escan_timeout_worker);
3186 }
3187}
3188
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003189static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003190{
3191 if (ms < 1000 / HZ) {
3192 cond_resched();
3193 mdelay(ms);
3194 } else {
3195 msleep(ms);
3196 }
3197}
3198
3199static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3200{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003201 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003202
3203 /*
3204 * Check for WL_STATUS_READY before any function call which
3205 * could result is bus access. Don't block the resume for
3206 * any driver error conditions
3207 */
3208 WL_TRACE("Enter\n");
3209
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003210 if (test_bit(WL_STATUS_READY, &cfg->status))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003211 brcmf_invoke_iscan(wiphy_to_cfg(wiphy));
3212
3213 WL_TRACE("Exit\n");
3214 return 0;
3215}
3216
3217static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
3218 struct cfg80211_wowlan *wow)
3219{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003220 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3221 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003222
3223 WL_TRACE("Enter\n");
3224
3225 /*
3226 * Check for WL_STATUS_READY before any function call which
3227 * could result is bus access. Don't block the suspend for
3228 * any driver error conditions
3229 */
3230
3231 /*
3232 * While going to suspend if associated with AP disassociate
3233 * from AP to save power while system is in suspended state
3234 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003235 if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
3236 test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
3237 test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02003238 WL_INFO("Disassociating from AP"
3239 " while entering suspend state\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003240 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003241
3242 /*
3243 * Make sure WPA_Supplicant receives all the event
3244 * generated due to DISASSOC call to the fw to keep
3245 * the state fw and WPA_Supplicant state consistent
3246 */
3247 brcmf_delay(500);
3248 }
3249
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003250 if (test_bit(WL_STATUS_READY, &cfg->status))
3251 brcmf_abort_scanning(cfg);
Arend van Spriel108a4be2012-09-19 22:21:07 +02003252 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003253 clear_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003254
3255 /* Turn off watchdog timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003256 if (test_bit(WL_STATUS_READY, &cfg->status))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003257 brcmf_set_mpc(ndev, 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003258
3259 WL_TRACE("Exit\n");
3260
3261 return 0;
3262}
3263
3264static __used s32
3265brcmf_dev_bufvar_set(struct net_device *ndev, s8 *name, s8 *buf, s32 len)
3266{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003267 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003268 u32 buflen;
3269
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003270 buflen = brcmf_c_mkiovar(name, buf, len, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003271 WL_DCMD_LEN_MAX);
3272 BUG_ON(!buflen);
3273
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003274 return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003275 buflen);
3276}
3277
3278static s32
3279brcmf_dev_bufvar_get(struct net_device *ndev, s8 *name, s8 *buf,
3280 s32 buf_len)
3281{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003282 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003283 u32 len;
3284 s32 err = 0;
3285
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003286 len = brcmf_c_mkiovar(name, NULL, 0, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003287 WL_DCMD_LEN_MAX);
3288 BUG_ON(!len);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003289 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003290 WL_DCMD_LEN_MAX);
3291 if (err) {
3292 WL_ERR("error (%d)\n", err);
3293 return err;
3294 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003295 memcpy(buf, cfg->dcmd_buf, buf_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003296
3297 return err;
3298}
3299
3300static __used s32
3301brcmf_update_pmklist(struct net_device *ndev,
3302 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3303{
3304 int i, j;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003305 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003306
Arend van Spriel40c8e952011-10-12 20:51:20 +02003307 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3308
3309 WL_CONN("No of elements %d\n", pmkid_len);
3310 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02003311 WL_CONN("PMKID[%d]: %pM =\n", i,
3312 &pmk_list->pmkids.pmkid[i].BSSID);
3313 for (j = 0; j < WLAN_PMKID_LEN; j++)
3314 WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]);
3315 }
3316
3317 if (!err)
3318 brcmf_dev_bufvar_set(ndev, "pmkid_info", (char *)pmk_list,
3319 sizeof(*pmk_list));
3320
3321 return err;
3322}
3323
3324static s32
3325brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3326 struct cfg80211_pmksa *pmksa)
3327{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003328 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3329 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003330 s32 err = 0;
3331 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003332 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003333
3334 WL_TRACE("Enter\n");
3335 if (!check_sys_up(wiphy))
3336 return -EIO;
3337
Arend van Spriel40c8e952011-10-12 20:51:20 +02003338 pmkid_len = le32_to_cpu(pmkids->npmkid);
3339 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003340 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3341 break;
3342 if (i < WL_NUM_PMKIDS_MAX) {
3343 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3344 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003345 if (i == pmkid_len) {
3346 pmkid_len++;
3347 pmkids->npmkid = cpu_to_le32(pmkid_len);
3348 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003349 } else
3350 err = -EINVAL;
3351
3352 WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
Arend van Spriel40c8e952011-10-12 20:51:20 +02003353 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003354 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel40c8e952011-10-12 20:51:20 +02003355 WL_CONN("%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003356
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003357 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003358
3359 WL_TRACE("Exit\n");
3360 return err;
3361}
3362
3363static s32
3364brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3365 struct cfg80211_pmksa *pmksa)
3366{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003367 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003368 struct pmkid_list pmkid;
3369 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003370 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003371
3372 WL_TRACE("Enter\n");
3373 if (!check_sys_up(wiphy))
3374 return -EIO;
3375
3376 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3377 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3378
3379 WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3380 &pmkid.pmkid[0].BSSID);
3381 for (i = 0; i < WLAN_PMKID_LEN; i++)
3382 WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]);
3383
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003384 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003385 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003386 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003387 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003388 ETH_ALEN))
3389 break;
3390
Arend van Spriel40c8e952011-10-12 20:51:20 +02003391 if ((pmkid_len > 0)
3392 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003393 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003394 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003395 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003396 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3397 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003398 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003399 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3400 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003401 WLAN_PMKID_LEN);
3402 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003403 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003404 } else
3405 err = -EINVAL;
3406
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003407 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003408
3409 WL_TRACE("Exit\n");
3410 return err;
3411
3412}
3413
3414static s32
3415brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3416{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003417 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003418 s32 err = 0;
3419
3420 WL_TRACE("Enter\n");
3421 if (!check_sys_up(wiphy))
3422 return -EIO;
3423
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003424 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3425 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003426
3427 WL_TRACE("Exit\n");
3428 return err;
3429
3430}
3431
Arend van Spriele5806072012-09-19 22:21:08 +02003432/*
3433 * PFN result doesn't have all the info which are
3434 * required by the supplicant
3435 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3436 * via wl_inform_single_bss in the required format. Escan does require the
3437 * scan request in the form of cfg80211_scan_request. For timebeing, create
3438 * cfg80211_scan_request one out of the received PNO event.
3439 */
3440static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003441brcmf_notify_sched_scan_results(struct brcmf_cfg80211_info *cfg,
Arend van Spriele5806072012-09-19 22:21:08 +02003442 struct net_device *ndev,
3443 const struct brcmf_event_msg *e, void *data)
3444{
3445 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3446 struct cfg80211_scan_request *request = NULL;
3447 struct cfg80211_ssid *ssid = NULL;
3448 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003449 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003450 int err = 0;
3451 int channel_req = 0;
3452 int band = 0;
3453 struct brcmf_pno_scanresults_le *pfn_result;
3454 u32 result_count;
3455 u32 status;
3456
3457 WL_SCAN("Enter\n");
3458
3459 if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) {
3460 WL_SCAN("PFN NET LOST event. Do Nothing\n");
3461 return 0;
3462 }
3463
3464 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3465 result_count = le32_to_cpu(pfn_result->count);
3466 status = le32_to_cpu(pfn_result->status);
3467
3468 /*
3469 * PFN event is limited to fit 512 bytes so we may get
3470 * multiple NET_FOUND events. For now place a warning here.
3471 */
3472 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3473 WL_SCAN("PFN NET FOUND event. count: %d\n", result_count);
3474 if (result_count > 0) {
3475 int i;
3476
3477 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003478 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3479 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003480 if (!request || !ssid || !channel) {
3481 err = -ENOMEM;
3482 goto out_err;
3483 }
3484
3485 request->wiphy = wiphy;
3486 data += sizeof(struct brcmf_pno_scanresults_le);
3487 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3488
3489 for (i = 0; i < result_count; i++) {
3490 netinfo = &netinfo_start[i];
3491 if (!netinfo) {
3492 WL_ERR("Invalid netinfo ptr. index: %d\n", i);
3493 err = -EINVAL;
3494 goto out_err;
3495 }
3496
3497 WL_SCAN("SSID:%s Channel:%d\n",
3498 netinfo->SSID, netinfo->channel);
3499 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3500 ssid[i].ssid_len = netinfo->SSID_len;
3501 request->n_ssids++;
3502
3503 channel_req = netinfo->channel;
3504 if (channel_req <= CH_MAX_2G_CHANNEL)
3505 band = NL80211_BAND_2GHZ;
3506 else
3507 band = NL80211_BAND_5GHZ;
3508 channel[i].center_freq =
3509 ieee80211_channel_to_frequency(channel_req,
3510 band);
3511 channel[i].band = band;
3512 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3513 request->channels[i] = &channel[i];
3514 request->n_channels++;
3515 }
3516
3517 /* assign parsed ssid array */
3518 if (request->n_ssids)
3519 request->ssids = &ssid[0];
3520
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003521 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003522 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003523 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003524 }
3525
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003526 set_bit(WL_STATUS_SCANNING, &cfg->status);
3527 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003528 if (err) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003529 clear_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriele5806072012-09-19 22:21:08 +02003530 goto out_err;
3531 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003532 cfg->sched_escan = true;
3533 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003534 } else {
3535 WL_ERR("FALSE PNO Event. (pfn_count == 0)\n");
3536 goto out_err;
3537 }
3538
3539 kfree(ssid);
3540 kfree(channel);
3541 kfree(request);
3542 return 0;
3543
3544out_err:
3545 kfree(ssid);
3546 kfree(channel);
3547 kfree(request);
3548 cfg80211_sched_scan_stopped(wiphy);
3549 return err;
3550}
3551
3552#ifndef CONFIG_BRCMISCAN
3553static int brcmf_dev_pno_clean(struct net_device *ndev)
3554{
3555 char iovbuf[128];
3556 int ret;
3557
3558 /* Disable pfn */
3559 ret = brcmf_dev_intvar_set(ndev, "pfn", 0);
3560 if (ret == 0) {
3561 /* clear pfn */
3562 ret = brcmf_dev_iovar_setbuf(ndev, "pfnclear", NULL, 0,
3563 iovbuf, sizeof(iovbuf));
3564 }
3565 if (ret < 0)
3566 WL_ERR("failed code %d\n", ret);
3567
3568 return ret;
3569}
3570
3571static int brcmf_dev_pno_config(struct net_device *ndev)
3572{
3573 struct brcmf_pno_param_le pfn_param;
3574 char iovbuf[128];
3575
3576 memset(&pfn_param, 0, sizeof(pfn_param));
3577 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3578
3579 /* set extra pno params */
3580 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3581 pfn_param.repeat = BRCMF_PNO_REPEAT;
3582 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3583
3584 /* set up pno scan fr */
3585 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3586
3587 return brcmf_dev_iovar_setbuf(ndev, "pfn_set",
3588 &pfn_param, sizeof(pfn_param),
3589 iovbuf, sizeof(iovbuf));
3590}
3591
3592static int
3593brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3594 struct net_device *ndev,
3595 struct cfg80211_sched_scan_request *request)
3596{
3597 char iovbuf[128];
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003598 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003599 struct brcmf_pno_net_param_le pfn;
3600 int i;
3601 int ret = 0;
3602
3603 WL_SCAN("Enter n_match_sets:%d n_ssids:%d\n",
3604 request->n_match_sets, request->n_ssids);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003605 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
3606 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Arend van Spriele5806072012-09-19 22:21:08 +02003607 return -EAGAIN;
3608 }
3609
3610 if (!request || !request->n_ssids || !request->n_match_sets) {
3611 WL_ERR("Invalid sched scan req!! n_ssids:%d\n",
3612 request->n_ssids);
3613 return -EINVAL;
3614 }
3615
3616 if (request->n_ssids > 0) {
3617 for (i = 0; i < request->n_ssids; i++) {
3618 /* Active scan req for ssids */
3619 WL_SCAN(">>> Active scan req for ssid (%s)\n",
3620 request->ssids[i].ssid);
3621
3622 /*
3623 * match_set ssids is a supert set of n_ssid list,
3624 * so we need not add these set seperately.
3625 */
3626 }
3627 }
3628
3629 if (request->n_match_sets > 0) {
3630 /* clean up everything */
3631 ret = brcmf_dev_pno_clean(ndev);
3632 if (ret < 0) {
3633 WL_ERR("failed error=%d\n", ret);
3634 return ret;
3635 }
3636
3637 /* configure pno */
3638 ret = brcmf_dev_pno_config(ndev);
3639 if (ret < 0) {
3640 WL_ERR("PNO setup failed!! ret=%d\n", ret);
3641 return -EINVAL;
3642 }
3643
3644 /* configure each match set */
3645 for (i = 0; i < request->n_match_sets; i++) {
3646 struct cfg80211_ssid *ssid;
3647 u32 ssid_len;
3648
3649 ssid = &request->match_sets[i].ssid;
3650 ssid_len = ssid->ssid_len;
3651
3652 if (!ssid_len) {
3653 WL_ERR("skip broadcast ssid\n");
3654 continue;
3655 }
3656 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3657 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3658 pfn.wsec = cpu_to_le32(0);
3659 pfn.infra = cpu_to_le32(1);
3660 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3661 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3662 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
3663 ret = brcmf_dev_iovar_setbuf(ndev, "pfn_add",
3664 &pfn, sizeof(pfn),
3665 iovbuf, sizeof(iovbuf));
3666 WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
3667 ret == 0 ? "set" : "failed",
3668 ssid->ssid);
3669 }
3670 /* Enable the PNO */
3671 if (brcmf_dev_intvar_set(ndev, "pfn", 1) < 0) {
3672 WL_ERR("PNO enable failed!! ret=%d\n", ret);
3673 return -EINVAL;
3674 }
3675 } else {
3676 return -EINVAL;
3677 }
3678
3679 return 0;
3680}
3681
3682static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3683 struct net_device *ndev)
3684{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003685 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003686
3687 WL_SCAN("enter\n");
3688 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003689 if (cfg->sched_escan)
3690 brcmf_notify_escan_complete(cfg, ndev, true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003691 return 0;
3692}
3693#endif /* CONFIG_BRCMISCAN */
3694
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003695#ifdef CONFIG_NL80211_TESTMODE
3696static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3697{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003698 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3699 struct net_device *ndev = cfg->wdev->netdev;
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003700 struct brcmf_dcmd *dcmd = data;
3701 struct sk_buff *reply;
3702 int ret;
3703
3704 ret = brcmf_netlink_dcmd(ndev, dcmd);
3705 if (ret == 0) {
3706 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3707 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3708 ret = cfg80211_testmode_reply(reply);
3709 }
3710 return ret;
3711}
3712#endif
3713
Hante Meuleman1a873342012-09-27 14:17:54 +02003714static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
3715{
3716 s32 err;
3717
3718 /* set auth */
3719 err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", 0, bssidx);
3720 if (err < 0) {
3721 WL_ERR("auth error %d\n", err);
3722 return err;
3723 }
3724 /* set wsec */
3725 err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", 0, bssidx);
3726 if (err < 0) {
3727 WL_ERR("wsec error %d\n", err);
3728 return err;
3729 }
3730 /* set upper-layer auth */
3731 err = brcmf_dev_intvar_set_bsscfg(ndev, "wpa_auth",
3732 WPA_AUTH_NONE, bssidx);
3733 if (err < 0) {
3734 WL_ERR("wpa_auth error %d\n", err);
3735 return err;
3736 }
3737
3738 return 0;
3739}
3740
3741static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3742{
3743 if (is_rsn_ie)
3744 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3745
3746 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3747}
3748
3749static s32
3750brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
3751 bool is_rsn_ie, s32 bssidx)
3752{
3753 u32 auth = 0; /* d11 open authentication */
3754 u16 count;
3755 s32 err = 0;
3756 s32 len = 0;
3757 u32 i;
3758 u32 wsec;
3759 u32 pval = 0;
3760 u32 gval = 0;
3761 u32 wpa_auth = 0;
3762 u32 offset;
3763 u8 *data;
3764 u16 rsn_cap;
3765 u32 wme_bss_disable;
3766
3767 WL_TRACE("Enter\n");
3768 if (wpa_ie == NULL)
3769 goto exit;
3770
3771 len = wpa_ie->len + TLV_HDR_LEN;
3772 data = (u8 *)wpa_ie;
3773 offset = 0;
3774 if (!is_rsn_ie)
3775 offset += VS_IE_FIXED_HDR_LEN;
3776 offset += WPA_IE_VERSION_LEN;
3777
3778 /* check for multicast cipher suite */
3779 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3780 err = -EINVAL;
3781 WL_ERR("no multicast cipher suite\n");
3782 goto exit;
3783 }
3784
3785 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3786 err = -EINVAL;
3787 WL_ERR("ivalid OUI\n");
3788 goto exit;
3789 }
3790 offset += TLV_OUI_LEN;
3791
3792 /* pick up multicast cipher */
3793 switch (data[offset]) {
3794 case WPA_CIPHER_NONE:
3795 gval = 0;
3796 break;
3797 case WPA_CIPHER_WEP_40:
3798 case WPA_CIPHER_WEP_104:
3799 gval = WEP_ENABLED;
3800 break;
3801 case WPA_CIPHER_TKIP:
3802 gval = TKIP_ENABLED;
3803 break;
3804 case WPA_CIPHER_AES_CCM:
3805 gval = AES_ENABLED;
3806 break;
3807 default:
3808 err = -EINVAL;
3809 WL_ERR("Invalid multi cast cipher info\n");
3810 goto exit;
3811 }
3812
3813 offset++;
3814 /* walk thru unicast cipher list and pick up what we recognize */
3815 count = data[offset] + (data[offset + 1] << 8);
3816 offset += WPA_IE_SUITE_COUNT_LEN;
3817 /* Check for unicast suite(s) */
3818 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3819 err = -EINVAL;
3820 WL_ERR("no unicast cipher suite\n");
3821 goto exit;
3822 }
3823 for (i = 0; i < count; i++) {
3824 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3825 err = -EINVAL;
3826 WL_ERR("ivalid OUI\n");
3827 goto exit;
3828 }
3829 offset += TLV_OUI_LEN;
3830 switch (data[offset]) {
3831 case WPA_CIPHER_NONE:
3832 break;
3833 case WPA_CIPHER_WEP_40:
3834 case WPA_CIPHER_WEP_104:
3835 pval |= WEP_ENABLED;
3836 break;
3837 case WPA_CIPHER_TKIP:
3838 pval |= TKIP_ENABLED;
3839 break;
3840 case WPA_CIPHER_AES_CCM:
3841 pval |= AES_ENABLED;
3842 break;
3843 default:
3844 WL_ERR("Ivalid unicast security info\n");
3845 }
3846 offset++;
3847 }
3848 /* walk thru auth management suite list and pick up what we recognize */
3849 count = data[offset] + (data[offset + 1] << 8);
3850 offset += WPA_IE_SUITE_COUNT_LEN;
3851 /* Check for auth key management suite(s) */
3852 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3853 err = -EINVAL;
3854 WL_ERR("no auth key mgmt suite\n");
3855 goto exit;
3856 }
3857 for (i = 0; i < count; i++) {
3858 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3859 err = -EINVAL;
3860 WL_ERR("ivalid OUI\n");
3861 goto exit;
3862 }
3863 offset += TLV_OUI_LEN;
3864 switch (data[offset]) {
3865 case RSN_AKM_NONE:
3866 WL_TRACE("RSN_AKM_NONE\n");
3867 wpa_auth |= WPA_AUTH_NONE;
3868 break;
3869 case RSN_AKM_UNSPECIFIED:
3870 WL_TRACE("RSN_AKM_UNSPECIFIED\n");
3871 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3872 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3873 break;
3874 case RSN_AKM_PSK:
3875 WL_TRACE("RSN_AKM_PSK\n");
3876 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3877 (wpa_auth |= WPA_AUTH_PSK);
3878 break;
3879 default:
3880 WL_ERR("Ivalid key mgmt info\n");
3881 }
3882 offset++;
3883 }
3884
3885 if (is_rsn_ie) {
3886 wme_bss_disable = 1;
3887 if ((offset + RSN_CAP_LEN) <= len) {
3888 rsn_cap = data[offset] + (data[offset + 1] << 8);
3889 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3890 wme_bss_disable = 0;
3891 }
3892 /* set wme_bss_disable to sync RSN Capabilities */
3893 err = brcmf_dev_intvar_set_bsscfg(ndev, "wme_bss_disable",
3894 wme_bss_disable, bssidx);
3895 if (err < 0) {
3896 WL_ERR("wme_bss_disable error %d\n", err);
3897 goto exit;
3898 }
3899 }
3900 /* FOR WPS , set SES_OW_ENABLED */
3901 wsec = (pval | gval | SES_OW_ENABLED);
3902
3903 /* set auth */
3904 err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", auth, bssidx);
3905 if (err < 0) {
3906 WL_ERR("auth error %d\n", err);
3907 goto exit;
3908 }
3909 /* set wsec */
3910 err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", wsec, bssidx);
3911 if (err < 0) {
3912 WL_ERR("wsec error %d\n", err);
3913 goto exit;
3914 }
3915 /* set upper-layer auth */
3916 err = brcmf_dev_intvar_set_bsscfg(ndev, "wpa_auth", wpa_auth, bssidx);
3917 if (err < 0) {
3918 WL_ERR("wpa_auth error %d\n", err);
3919 goto exit;
3920 }
3921
3922exit:
3923 return err;
3924}
3925
3926static s32
3927brcmf_parse_vndr_ies(u8 *vndr_ie_buf, u32 vndr_ie_len,
3928 struct parsed_vndr_ies *vndr_ies)
3929{
3930 s32 err = 0;
3931 struct brcmf_vs_tlv *vndrie;
3932 struct brcmf_tlv *ie;
3933 struct parsed_vndr_ie_info *parsed_info;
3934 s32 remaining_len;
3935
3936 remaining_len = (s32)vndr_ie_len;
3937 memset(vndr_ies, 0, sizeof(*vndr_ies));
3938
3939 ie = (struct brcmf_tlv *)vndr_ie_buf;
3940 while (ie) {
3941 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3942 goto next;
3943 vndrie = (struct brcmf_vs_tlv *)ie;
3944 /* len should be bigger than OUI length + one */
3945 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
3946 WL_ERR("invalid vndr ie. length is too small %d\n",
3947 vndrie->len);
3948 goto next;
3949 }
3950 /* if wpa or wme ie, do not add ie */
3951 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3952 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3953 (vndrie->oui_type == WME_OUI_TYPE))) {
3954 WL_TRACE("Found WPA/WME oui. Do not add it\n");
3955 goto next;
3956 }
3957
3958 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3959
3960 /* save vndr ie information */
3961 parsed_info->ie_ptr = (char *)vndrie;
3962 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3963 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3964
3965 vndr_ies->count++;
3966
3967 WL_TRACE("** OUI %02x %02x %02x, type 0x%02x\n",
3968 parsed_info->vndrie.oui[0],
3969 parsed_info->vndrie.oui[1],
3970 parsed_info->vndrie.oui[2],
3971 parsed_info->vndrie.oui_type);
3972
3973 if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
3974 break;
3975next:
3976 remaining_len -= ie->len;
3977 if (remaining_len <= 2)
3978 ie = NULL;
3979 else
3980 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len);
3981 }
3982 return err;
3983}
3984
3985static u32
3986brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3987{
3988
3989 __le32 iecount_le;
3990 __le32 pktflag_le;
3991
3992 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3993 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3994
3995 iecount_le = cpu_to_le32(1);
3996 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3997
3998 pktflag_le = cpu_to_le32(pktflag);
3999 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
4000
4001 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
4002
4003 return ie_len + VNDR_IE_HDR_SIZE;
4004}
4005
4006s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004007brcmf_set_management_ie(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004008 struct net_device *ndev, s32 bssidx, s32 pktflag,
4009 u8 *vndr_ie_buf, u32 vndr_ie_len)
4010{
4011 s32 err = 0;
4012 u8 *iovar_ie_buf;
4013 u8 *curr_ie_buf;
4014 u8 *mgmt_ie_buf = NULL;
4015 u32 mgmt_ie_buf_len = 0;
4016 u32 *mgmt_ie_len = 0;
4017 u32 del_add_ie_buf_len = 0;
4018 u32 total_ie_buf_len = 0;
4019 u32 parsed_ie_buf_len = 0;
4020 struct parsed_vndr_ies old_vndr_ies;
4021 struct parsed_vndr_ies new_vndr_ies;
4022 struct parsed_vndr_ie_info *vndrie_info;
4023 s32 i;
4024 u8 *ptr;
4025 u32 remained_buf_len;
4026
4027 WL_TRACE("bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag);
4028 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4029 if (!iovar_ie_buf)
4030 return -ENOMEM;
4031 curr_ie_buf = iovar_ie_buf;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004032 if (test_bit(WL_STATUS_AP_CREATING, &cfg->status) ||
4033 test_bit(WL_STATUS_AP_CREATED, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004034 switch (pktflag) {
4035 case VNDR_IE_PRBRSP_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004036 mgmt_ie_buf = cfg->ap_info->probe_res_ie;
4037 mgmt_ie_len = &cfg->ap_info->probe_res_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004038 mgmt_ie_buf_len =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004039 sizeof(cfg->ap_info->probe_res_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02004040 break;
4041 case VNDR_IE_BEACON_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004042 mgmt_ie_buf = cfg->ap_info->beacon_ie;
4043 mgmt_ie_len = &cfg->ap_info->beacon_ie_len;
4044 mgmt_ie_buf_len = sizeof(cfg->ap_info->beacon_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02004045 break;
4046 default:
4047 err = -EPERM;
4048 WL_ERR("not suitable type\n");
4049 goto exit;
4050 }
4051 bssidx = 0;
4052 } else {
4053 err = -EPERM;
4054 WL_ERR("not suitable type\n");
4055 goto exit;
4056 }
4057
4058 if (vndr_ie_len > mgmt_ie_buf_len) {
4059 err = -ENOMEM;
4060 WL_ERR("extra IE size too big\n");
4061 goto exit;
4062 }
4063
4064 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
4065 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
4066 ptr = curr_ie_buf;
4067 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
4068 for (i = 0; i < new_vndr_ies.count; i++) {
4069 vndrie_info = &new_vndr_ies.ie_info[i];
4070 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
4071 vndrie_info->ie_len);
4072 parsed_ie_buf_len += vndrie_info->ie_len;
4073 }
4074 }
4075
4076 if (mgmt_ie_buf != NULL) {
4077 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
4078 (memcmp(mgmt_ie_buf, curr_ie_buf,
4079 parsed_ie_buf_len) == 0)) {
4080 WL_TRACE("Previous mgmt IE is equals to current IE");
4081 goto exit;
4082 }
4083
4084 /* parse old vndr_ie */
4085 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
4086
4087 /* make a command to delete old ie */
4088 for (i = 0; i < old_vndr_ies.count; i++) {
4089 vndrie_info = &old_vndr_ies.ie_info[i];
4090
4091 WL_TRACE("DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4092 vndrie_info->vndrie.id,
4093 vndrie_info->vndrie.len,
4094 vndrie_info->vndrie.oui[0],
4095 vndrie_info->vndrie.oui[1],
4096 vndrie_info->vndrie.oui[2]);
4097
4098 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4099 vndrie_info->ie_ptr,
4100 vndrie_info->ie_len,
4101 "del");
4102 curr_ie_buf += del_add_ie_buf_len;
4103 total_ie_buf_len += del_add_ie_buf_len;
4104 }
4105 }
4106
4107 *mgmt_ie_len = 0;
4108 /* Add if there is any extra IE */
4109 if (mgmt_ie_buf && parsed_ie_buf_len) {
4110 ptr = mgmt_ie_buf;
4111
4112 remained_buf_len = mgmt_ie_buf_len;
4113
4114 /* make a command to add new ie */
4115 for (i = 0; i < new_vndr_ies.count; i++) {
4116 vndrie_info = &new_vndr_ies.ie_info[i];
4117
4118 WL_TRACE("ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4119 vndrie_info->vndrie.id,
4120 vndrie_info->vndrie.len,
4121 vndrie_info->vndrie.oui[0],
4122 vndrie_info->vndrie.oui[1],
4123 vndrie_info->vndrie.oui[2]);
4124
4125 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4126 vndrie_info->ie_ptr,
4127 vndrie_info->ie_len,
4128 "add");
4129 /* verify remained buf size before copy data */
4130 remained_buf_len -= vndrie_info->ie_len;
4131 if (remained_buf_len < 0) {
4132 WL_ERR("no space in mgmt_ie_buf: len left %d",
4133 remained_buf_len);
4134 break;
4135 }
4136
4137 /* save the parsed IE in wl struct */
4138 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4139 vndrie_info->ie_len);
4140 *mgmt_ie_len += vndrie_info->ie_len;
4141
4142 curr_ie_buf += del_add_ie_buf_len;
4143 total_ie_buf_len += del_add_ie_buf_len;
4144 }
4145 }
4146 if (total_ie_buf_len) {
4147 err = brcmf_dev_iovar_setbuf_bsscfg(ndev, "vndr_ie",
4148 iovar_ie_buf,
4149 total_ie_buf_len,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004150 cfg->extra_buf,
Hante Meuleman1a873342012-09-27 14:17:54 +02004151 WL_EXTRA_BUF_MAX, bssidx);
4152 if (err)
4153 WL_ERR("vndr ie set error : %d\n", err);
4154 }
4155
4156exit:
4157 kfree(iovar_ie_buf);
4158 return err;
4159}
4160
4161static s32
4162brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4163 struct cfg80211_ap_settings *settings)
4164{
4165 s32 ie_offset;
4166 struct brcmf_tlv *ssid_ie;
4167 struct brcmf_ssid_le ssid_le;
4168 s32 ioctl_value;
4169 s32 err = -EPERM;
4170 struct brcmf_tlv *rsn_ie;
4171 struct brcmf_vs_tlv *wpa_ie;
4172 struct brcmf_join_params join_params;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004173 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004174 s32 bssidx = 0;
4175
4176 WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
4177 settings->channel_type, settings->beacon_interval,
4178 settings->dtim_period);
4179 WL_TRACE("ssid=%s(%d), auth_type=%d, inactivity_timeout=%d\n",
4180 settings->ssid, settings->ssid_len, settings->auth_type,
4181 settings->inactivity_timeout);
4182
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004183 if (!test_bit(WL_STATUS_AP_CREATING, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004184 WL_ERR("Not in AP creation mode\n");
4185 return -EPERM;
4186 }
4187
4188 memset(&ssid_le, 0, sizeof(ssid_le));
4189 if (settings->ssid == NULL || settings->ssid_len == 0) {
4190 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4191 ssid_ie = brcmf_parse_tlvs(
4192 (u8 *)&settings->beacon.head[ie_offset],
4193 settings->beacon.head_len - ie_offset,
4194 WLAN_EID_SSID);
4195 if (!ssid_ie)
4196 return -EINVAL;
4197
4198 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4199 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
4200 WL_TRACE("SSID is (%s) in Head\n", ssid_le.SSID);
4201 } else {
4202 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4203 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4204 }
4205
4206 brcmf_set_mpc(ndev, 0);
4207 ioctl_value = 1;
4208 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_DOWN, &ioctl_value);
4209 if (err < 0) {
4210 WL_ERR("BRCMF_C_DOWN error %d\n", err);
4211 goto exit;
4212 }
4213 ioctl_value = 1;
4214 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &ioctl_value);
4215 if (err < 0) {
4216 WL_ERR("SET INFRA error %d\n", err);
4217 goto exit;
4218 }
4219 ioctl_value = 1;
4220 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AP, &ioctl_value);
4221 if (err < 0) {
4222 WL_ERR("setting AP mode failed %d\n", err);
4223 goto exit;
4224 }
4225
4226 /* find the RSN_IE */
4227 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4228 settings->beacon.tail_len, WLAN_EID_RSN);
4229
4230 /* find the WPA_IE */
4231 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4232 settings->beacon.tail_len);
4233
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004234 kfree(cfg->ap_info->rsn_ie);
4235 cfg->ap_info->rsn_ie = NULL;
4236 kfree(cfg->ap_info->wpa_ie);
4237 cfg->ap_info->wpa_ie = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02004238
4239 if ((wpa_ie != NULL || rsn_ie != NULL)) {
4240 WL_TRACE("WPA(2) IE is found\n");
4241 if (wpa_ie != NULL) {
4242 /* WPA IE */
4243 err = brcmf_configure_wpaie(ndev, wpa_ie, false,
4244 bssidx);
4245 if (err < 0)
4246 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004247 cfg->ap_info->wpa_ie = kmemdup(wpa_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02004248 wpa_ie->len +
4249 TLV_HDR_LEN,
4250 GFP_KERNEL);
4251 } else {
4252 /* RSN IE */
4253 err = brcmf_configure_wpaie(ndev,
4254 (struct brcmf_vs_tlv *)rsn_ie, true, bssidx);
4255 if (err < 0)
4256 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004257 cfg->ap_info->rsn_ie = kmemdup(rsn_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02004258 rsn_ie->len +
4259 TLV_HDR_LEN,
4260 GFP_KERNEL);
4261 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004262 cfg->ap_info->security_mode = true;
Hante Meuleman1a873342012-09-27 14:17:54 +02004263 } else {
4264 WL_TRACE("No WPA(2) IEs found\n");
4265 brcmf_configure_opensecurity(ndev, bssidx);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004266 cfg->ap_info->security_mode = false;
Hante Meuleman1a873342012-09-27 14:17:54 +02004267 }
4268 /* Set Beacon IEs to FW */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004269 err = brcmf_set_management_ie(cfg, ndev, bssidx,
Hante Meuleman1a873342012-09-27 14:17:54 +02004270 VNDR_IE_BEACON_FLAG,
4271 (u8 *)settings->beacon.tail,
4272 settings->beacon.tail_len);
4273 if (err)
4274 WL_ERR("Set Beacon IE Failed\n");
4275 else
4276 WL_TRACE("Applied Vndr IEs for Beacon\n");
4277
4278 /* Set Probe Response IEs to FW */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004279 err = brcmf_set_management_ie(cfg, ndev, bssidx,
Hante Meuleman1a873342012-09-27 14:17:54 +02004280 VNDR_IE_PRBRSP_FLAG,
4281 (u8 *)settings->beacon.proberesp_ies,
4282 settings->beacon.proberesp_ies_len);
4283 if (err)
4284 WL_ERR("Set Probe Resp IE Failed\n");
4285 else
4286 WL_TRACE("Applied Vndr IEs for Probe Resp\n");
4287
4288 if (settings->beacon_interval) {
4289 ioctl_value = settings->beacon_interval;
4290 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_BCNPRD,
4291 &ioctl_value);
4292 if (err < 0) {
4293 WL_ERR("Beacon Interval Set Error, %d\n", err);
4294 goto exit;
4295 }
4296 }
4297 if (settings->dtim_period) {
4298 ioctl_value = settings->dtim_period;
4299 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_DTIMPRD,
4300 &ioctl_value);
4301 if (err < 0) {
4302 WL_ERR("DTIM Interval Set Error, %d\n", err);
4303 goto exit;
4304 }
4305 }
4306 ioctl_value = 1;
4307 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_UP, &ioctl_value);
4308 if (err < 0) {
4309 WL_ERR("BRCMF_C_UP error (%d)\n", err);
4310 goto exit;
4311 }
4312
4313 memset(&join_params, 0, sizeof(join_params));
4314 /* join parameters starts with ssid */
4315 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4316 /* create softap */
4317 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID, &join_params,
4318 sizeof(join_params));
4319 if (err < 0) {
4320 WL_ERR("SET SSID error (%d)\n", err);
4321 goto exit;
4322 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004323 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4324 set_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004325
4326exit:
4327 if (err)
4328 brcmf_set_mpc(ndev, 1);
4329 return err;
4330}
4331
4332static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4333{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004334 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004335 s32 ioctl_value;
4336 s32 err = -EPERM;
4337
4338 WL_TRACE("Enter\n");
4339
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004340 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004341 /* Due to most likely deauths outstanding we sleep */
4342 /* first to make sure they get processed by fw. */
4343 msleep(400);
4344 ioctl_value = 0;
4345 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AP, &ioctl_value);
4346 if (err < 0) {
4347 WL_ERR("setting AP mode failed %d\n", err);
4348 goto exit;
4349 }
4350 ioctl_value = 0;
4351 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_UP, &ioctl_value);
4352 if (err < 0) {
4353 WL_ERR("BRCMF_C_UP error %d\n", err);
4354 goto exit;
4355 }
4356 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004357 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4358 clear_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004359 }
4360exit:
4361 return err;
4362}
4363
4364static int
4365brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
4366 u8 *mac)
4367{
4368 struct brcmf_scb_val_le scbval;
4369 s32 err;
4370
4371 if (!mac)
4372 return -EFAULT;
4373
4374 WL_TRACE("Enter %pM\n", mac);
4375
4376 if (!check_sys_up(wiphy))
4377 return -EIO;
4378
4379 memcpy(&scbval.ea, mac, ETH_ALEN);
4380 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
4381 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
4382 &scbval, sizeof(scbval));
4383 if (err)
4384 WL_ERR("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
4385
4386 WL_TRACE("Exit\n");
4387 return err;
4388}
4389
Arend van Spriel5b435de2011-10-05 13:19:03 +02004390static struct cfg80211_ops wl_cfg80211_ops = {
4391 .change_virtual_intf = brcmf_cfg80211_change_iface,
4392 .scan = brcmf_cfg80211_scan,
4393 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4394 .join_ibss = brcmf_cfg80211_join_ibss,
4395 .leave_ibss = brcmf_cfg80211_leave_ibss,
4396 .get_station = brcmf_cfg80211_get_station,
4397 .set_tx_power = brcmf_cfg80211_set_tx_power,
4398 .get_tx_power = brcmf_cfg80211_get_tx_power,
4399 .add_key = brcmf_cfg80211_add_key,
4400 .del_key = brcmf_cfg80211_del_key,
4401 .get_key = brcmf_cfg80211_get_key,
4402 .set_default_key = brcmf_cfg80211_config_default_key,
4403 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4404 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
4405 .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
4406 .connect = brcmf_cfg80211_connect,
4407 .disconnect = brcmf_cfg80211_disconnect,
4408 .suspend = brcmf_cfg80211_suspend,
4409 .resume = brcmf_cfg80211_resume,
4410 .set_pmksa = brcmf_cfg80211_set_pmksa,
4411 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004412 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004413 .start_ap = brcmf_cfg80211_start_ap,
4414 .stop_ap = brcmf_cfg80211_stop_ap,
4415 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004416#ifndef CONFIG_BRCMISCAN
4417 /* scheduled scan need e-scan, which is mutual exclusive with i-scan */
4418 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4419 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
4420#endif
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004421#ifdef CONFIG_NL80211_TESTMODE
4422 .testmode_cmd = brcmf_cfg80211_testmode
4423#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02004424};
4425
4426static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
4427{
4428 s32 err = 0;
4429
4430 switch (mode) {
4431 case WL_MODE_BSS:
4432 return NL80211_IFTYPE_STATION;
4433 case WL_MODE_IBSS:
4434 return NL80211_IFTYPE_ADHOC;
4435 default:
4436 return NL80211_IFTYPE_UNSPECIFIED;
4437 }
4438
4439 return err;
4440}
4441
Arend van Spriele5806072012-09-19 22:21:08 +02004442static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
4443{
4444#ifndef CONFIG_BRCMFISCAN
4445 /* scheduled scan settings */
4446 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
4447 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
4448 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
4449 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
4450#endif
4451}
4452
Arend van Spriel5db6e952012-09-27 14:17:53 +02004453static struct wireless_dev *brcmf_alloc_wdev(struct device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004454{
4455 struct wireless_dev *wdev;
4456 s32 err = 0;
4457
4458 wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
4459 if (!wdev)
4460 return ERR_PTR(-ENOMEM);
4461
Arend van Spriel5db6e952012-09-27 14:17:53 +02004462 wdev->wiphy = wiphy_new(&wl_cfg80211_ops,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004463 sizeof(struct brcmf_cfg80211_info));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004464 if (!wdev->wiphy) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004465 WL_ERR("Could not allocate wiphy device\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004466 err = -ENOMEM;
4467 goto wiphy_new_out;
4468 }
4469 set_wiphy_dev(wdev->wiphy, ndev);
4470 wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
4471 wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Hante Meuleman1a873342012-09-27 14:17:54 +02004472 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
4473 BIT(NL80211_IFTYPE_ADHOC) |
4474 BIT(NL80211_IFTYPE_AP);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004475 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
4476 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
4477 * it as 11a by default.
4478 * This will be updated with
4479 * 11n phy tables in
4480 * "ifconfig up"
4481 * if phy has 11n capability
4482 */
4483 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
4484 wdev->wiphy->cipher_suites = __wl_cipher_suites;
4485 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
4486 wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
4487 * save mode
4488 * by default
4489 */
Arend van Spriele5806072012-09-19 22:21:08 +02004490 brcmf_wiphy_pno_params(wdev->wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004491 err = wiphy_register(wdev->wiphy);
4492 if (err < 0) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004493 WL_ERR("Could not register wiphy device (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004494 goto wiphy_register_out;
4495 }
4496 return wdev;
4497
4498wiphy_register_out:
4499 wiphy_free(wdev->wiphy);
4500
4501wiphy_new_out:
4502 kfree(wdev);
4503
4504 return ERR_PTR(err);
4505}
4506
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004507static void brcmf_free_wdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004508{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004509 struct wireless_dev *wdev = cfg->wdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004510
4511 if (!wdev) {
4512 WL_ERR("wdev is invalid\n");
4513 return;
4514 }
4515 wiphy_unregister(wdev->wiphy);
4516 wiphy_free(wdev->wiphy);
4517 kfree(wdev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004518 cfg->wdev = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004519}
4520
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004521static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004522 const struct brcmf_event_msg *e)
4523{
4524 u32 event = be32_to_cpu(e->event_type);
4525 u32 status = be32_to_cpu(e->status);
4526
4527 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
4528 WL_CONN("Processing set ssid\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004529 cfg->link_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004530 return true;
4531 }
4532
4533 return false;
4534}
4535
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004536static bool brcmf_is_linkdown(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004537 const struct brcmf_event_msg *e)
4538{
4539 u32 event = be32_to_cpu(e->event_type);
4540 u16 flags = be16_to_cpu(e->flags);
4541
4542 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
4543 WL_CONN("Processing link down\n");
4544 return true;
4545 }
4546 return false;
4547}
4548
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004549static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004550 const struct brcmf_event_msg *e)
4551{
4552 u32 event = be32_to_cpu(e->event_type);
4553 u32 status = be32_to_cpu(e->status);
4554
4555 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
4556 WL_CONN("Processing Link %s & no network found\n",
4557 be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ?
4558 "up" : "down");
4559 return true;
4560 }
4561
4562 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
4563 WL_CONN("Processing connecting & no network found\n");
4564 return true;
4565 }
4566
4567 return false;
4568}
4569
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004570static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004571{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004572 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004573
4574 kfree(conn_info->req_ie);
4575 conn_info->req_ie = NULL;
4576 conn_info->req_ie_len = 0;
4577 kfree(conn_info->resp_ie);
4578 conn_info->resp_ie = NULL;
4579 conn_info->resp_ie_len = 0;
4580}
4581
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004582static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004583{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004584 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004585 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004586 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004587 u32 req_len;
4588 u32 resp_len;
4589 s32 err = 0;
4590
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004591 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004592
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004593 err = brcmf_dev_bufvar_get(ndev, "assoc_info", cfg->extra_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004594 WL_ASSOC_INFO_MAX);
4595 if (err) {
4596 WL_ERR("could not get assoc info (%d)\n", err);
4597 return err;
4598 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004599 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004600 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004601 req_len = le32_to_cpu(assoc_info->req_len);
4602 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004603 if (req_len) {
4604 err = brcmf_dev_bufvar_get(ndev, "assoc_req_ies",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004605 cfg->extra_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004606 WL_ASSOC_INFO_MAX);
4607 if (err) {
4608 WL_ERR("could not get assoc req (%d)\n", err);
4609 return err;
4610 }
4611 conn_info->req_ie_len = req_len;
4612 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004613 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004614 GFP_KERNEL);
4615 } else {
4616 conn_info->req_ie_len = 0;
4617 conn_info->req_ie = NULL;
4618 }
4619 if (resp_len) {
4620 err = brcmf_dev_bufvar_get(ndev, "assoc_resp_ies",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004621 cfg->extra_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004622 WL_ASSOC_INFO_MAX);
4623 if (err) {
4624 WL_ERR("could not get assoc resp (%d)\n", err);
4625 return err;
4626 }
4627 conn_info->resp_ie_len = resp_len;
4628 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004629 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004630 GFP_KERNEL);
4631 } else {
4632 conn_info->resp_ie_len = 0;
4633 conn_info->resp_ie = NULL;
4634 }
4635 WL_CONN("req len (%d) resp len (%d)\n",
4636 conn_info->req_ie_len, conn_info->resp_ie_len);
4637
4638 return err;
4639}
4640
4641static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004642brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004643 struct net_device *ndev,
4644 const struct brcmf_event_msg *e)
4645{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004646 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004647 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4648 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004649 struct brcmf_channel_info_le channel_le;
4650 struct ieee80211_channel *notify_channel;
4651 struct ieee80211_supported_band *band;
4652 u32 freq;
4653 s32 err = 0;
4654 u32 target_channel;
4655
4656 WL_TRACE("Enter\n");
4657
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004658 brcmf_get_assoc_ies(cfg);
4659 brcmf_update_prof(cfg, NULL, &e->addr, WL_PROF_BSSID);
4660 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004661
4662 brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_le,
4663 sizeof(channel_le));
4664
4665 target_channel = le32_to_cpu(channel_le.target_channel);
4666 WL_CONN("Roamed to channel %d\n", target_channel);
4667
4668 if (target_channel <= CH_MAX_2G_CHANNEL)
4669 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4670 else
4671 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4672
4673 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4674 notify_channel = ieee80211_get_channel(wiphy, freq);
4675
Arend van Spriel06bb1232012-09-27 14:17:56 +02004676 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004677 conn_info->req_ie, conn_info->req_ie_len,
4678 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
4679 WL_CONN("Report roaming result\n");
4680
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004681 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004682 WL_TRACE("Exit\n");
4683 return err;
4684}
4685
4686static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004687brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004688 struct net_device *ndev, const struct brcmf_event_msg *e,
4689 bool completed)
4690{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004691 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004692 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004693 s32 err = 0;
4694
4695 WL_TRACE("Enter\n");
4696
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004697 if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004698 if (completed) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004699 brcmf_get_assoc_ies(cfg);
4700 brcmf_update_prof(cfg, NULL, &e->addr,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004701 WL_PROF_BSSID);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004702 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004703 }
4704 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004705 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004706 conn_info->req_ie,
4707 conn_info->req_ie_len,
4708 conn_info->resp_ie,
4709 conn_info->resp_ie_len,
4710 completed ? WLAN_STATUS_SUCCESS :
4711 WLAN_STATUS_AUTH_TIMEOUT,
4712 GFP_KERNEL);
4713 if (completed)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004714 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004715 WL_CONN("Report connect result - connection %s\n",
4716 completed ? "succeeded" : "failed");
4717 }
4718 WL_TRACE("Exit\n");
4719 return err;
4720}
4721
4722static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004723brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004724 struct net_device *ndev,
4725 const struct brcmf_event_msg *e, void *data)
4726{
4727 s32 err = 0;
4728 u32 event = be32_to_cpu(e->event_type);
4729 u32 reason = be32_to_cpu(e->reason);
4730 u32 len = be32_to_cpu(e->datalen);
4731 static int generation;
4732
4733 struct station_info sinfo;
4734
4735 WL_CONN("event %d, reason %d\n", event, reason);
4736 memset(&sinfo, 0, sizeof(sinfo));
4737
4738 sinfo.filled = 0;
4739 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
4740 reason == BRCMF_E_STATUS_SUCCESS) {
4741 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4742 if (!data) {
4743 WL_ERR("No IEs present in ASSOC/REASSOC_IND");
4744 return -EINVAL;
4745 }
4746 sinfo.assoc_req_ies = data;
4747 sinfo.assoc_req_ies_len = len;
4748 generation++;
4749 sinfo.generation = generation;
4750 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_ATOMIC);
4751 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4752 (event == BRCMF_E_DEAUTH_IND) ||
4753 (event == BRCMF_E_DEAUTH)) {
4754 generation++;
4755 sinfo.generation = generation;
4756 cfg80211_del_sta(ndev, e->addr, GFP_ATOMIC);
4757 }
4758 return err;
4759}
4760
4761static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004762brcmf_notify_connect_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004763 struct net_device *ndev,
4764 const struct brcmf_event_msg *e, void *data)
4765{
4766 s32 err = 0;
4767
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004768 if (cfg->conf->mode == WL_MODE_AP) {
4769 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
4770 } else if (brcmf_is_linkup(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004771 WL_CONN("Linkup\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004772 if (brcmf_is_ibssmode(cfg)) {
4773 brcmf_update_prof(cfg, NULL, (void *)e->addr,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004774 WL_PROF_BSSID);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004775 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004776 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004777 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
4778 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004779 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004780 brcmf_bss_connect_done(cfg, ndev, e, true);
4781 } else if (brcmf_is_linkdown(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004782 WL_CONN("Linkdown\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004783 if (brcmf_is_ibssmode(cfg)) {
4784 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004785 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004786 &cfg->status))
4787 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004788 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004789 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004790 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004791 &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004792 cfg80211_disconnected(ndev, 0, NULL, 0,
4793 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004794 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004795 }
4796 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004797 brcmf_init_prof(cfg->profile);
4798 } else if (brcmf_is_nonetwork(cfg, e)) {
4799 if (brcmf_is_ibssmode(cfg))
4800 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004801 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004802 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004803 }
4804
4805 return err;
4806}
4807
4808static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004809brcmf_notify_roaming_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004810 struct net_device *ndev,
4811 const struct brcmf_event_msg *e, void *data)
4812{
4813 s32 err = 0;
4814 u32 event = be32_to_cpu(e->event_type);
4815 u32 status = be32_to_cpu(e->status);
4816
4817 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004818 if (test_bit(WL_STATUS_CONNECTED, &cfg->status))
4819 brcmf_bss_roaming_done(cfg, ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004820 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004821 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004822 }
4823
4824 return err;
4825}
4826
4827static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004828brcmf_notify_mic_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004829 struct net_device *ndev,
4830 const struct brcmf_event_msg *e, void *data)
4831{
4832 u16 flags = be16_to_cpu(e->flags);
4833 enum nl80211_key_type key_type;
4834
4835 if (flags & BRCMF_EVENT_MSG_GROUP)
4836 key_type = NL80211_KEYTYPE_GROUP;
4837 else
4838 key_type = NL80211_KEYTYPE_PAIRWISE;
4839
4840 cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1,
4841 NULL, GFP_KERNEL);
4842
4843 return 0;
4844}
4845
4846static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004847brcmf_notify_scan_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004848 struct net_device *ndev,
4849 const struct brcmf_event_msg *e, void *data)
4850{
4851 struct brcmf_channel_info_le channel_inform_le;
4852 struct brcmf_scan_results_le *bss_list_le;
4853 u32 len = WL_SCAN_BUF_MAX;
4854 s32 err = 0;
4855 bool scan_abort = false;
4856 u32 scan_channel;
4857
4858 WL_TRACE("Enter\n");
4859
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004860 if (cfg->iscan_on && cfg->iscan_kickstart) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004861 WL_TRACE("Exit\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004862 return brcmf_wakeup_iscan(cfg_to_iscan(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004863 }
4864
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004865 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004866 WL_ERR("Scan complete while device not scanning\n");
4867 scan_abort = true;
4868 err = -EINVAL;
4869 goto scan_done_out;
4870 }
4871
4872 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_inform_le,
4873 sizeof(channel_inform_le));
4874 if (err) {
4875 WL_ERR("scan busy (%d)\n", err);
4876 scan_abort = true;
4877 goto scan_done_out;
4878 }
4879 scan_channel = le32_to_cpu(channel_inform_le.scan_channel);
4880 if (scan_channel)
4881 WL_CONN("channel_inform.scan_channel (%d)\n", scan_channel);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004882 cfg->bss_list = cfg->scan_results;
4883 bss_list_le = (struct brcmf_scan_results_le *) cfg->bss_list;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004884
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004885 memset(cfg->scan_results, 0, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004886 bss_list_le->buflen = cpu_to_le32(len);
4887 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN_RESULTS,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004888 cfg->scan_results, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004889 if (err) {
4890 WL_ERR("%s Scan_results error (%d)\n", ndev->name, err);
4891 err = -EINVAL;
4892 scan_abort = true;
4893 goto scan_done_out;
4894 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004895 cfg->scan_results->buflen = le32_to_cpu(bss_list_le->buflen);
4896 cfg->scan_results->version = le32_to_cpu(bss_list_le->version);
4897 cfg->scan_results->count = le32_to_cpu(bss_list_le->count);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004898
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004899 err = brcmf_inform_bss(cfg);
Hante Meuleman35aafa92012-09-11 21:18:49 +02004900 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004901 scan_abort = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004902
4903scan_done_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004904 if (cfg->scan_request) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004905 WL_SCAN("calling cfg80211_scan_done\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004906 cfg80211_scan_done(cfg->scan_request, scan_abort);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004907 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004908 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004909 }
4910
4911 WL_TRACE("Exit\n");
4912
4913 return err;
4914}
4915
4916static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4917{
4918 conf->mode = (u32)-1;
4919 conf->frag_threshold = (u32)-1;
4920 conf->rts_threshold = (u32)-1;
4921 conf->retry_short = (u32)-1;
4922 conf->retry_long = (u32)-1;
4923 conf->tx_power = -1;
4924}
4925
4926static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
4927{
4928 memset(el, 0, sizeof(*el));
4929 el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status;
4930 el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status;
Hante Meuleman1a873342012-09-27 14:17:54 +02004931 el->handler[BRCMF_E_DEAUTH_IND] = brcmf_notify_connect_status;
4932 el->handler[BRCMF_E_DEAUTH] = brcmf_notify_connect_status;
4933 el->handler[BRCMF_E_DISASSOC_IND] = brcmf_notify_connect_status;
4934 el->handler[BRCMF_E_ASSOC_IND] = brcmf_notify_connect_status;
4935 el->handler[BRCMF_E_REASSOC_IND] = brcmf_notify_connect_status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004936 el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
4937 el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
4938 el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
Arend van Spriele5806072012-09-19 22:21:08 +02004939 el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004940}
4941
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004942static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004943{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004944 kfree(cfg->scan_results);
4945 cfg->scan_results = NULL;
4946 kfree(cfg->bss_info);
4947 cfg->bss_info = NULL;
4948 kfree(cfg->conf);
4949 cfg->conf = NULL;
4950 kfree(cfg->profile);
4951 cfg->profile = NULL;
4952 kfree(cfg->scan_req_int);
4953 cfg->scan_req_int = NULL;
4954 kfree(cfg->escan_ioctl_buf);
4955 cfg->escan_ioctl_buf = NULL;
4956 kfree(cfg->dcmd_buf);
4957 cfg->dcmd_buf = NULL;
4958 kfree(cfg->extra_buf);
4959 cfg->extra_buf = NULL;
4960 kfree(cfg->iscan);
4961 cfg->iscan = NULL;
4962 kfree(cfg->pmk_list);
4963 cfg->pmk_list = NULL;
4964 if (cfg->ap_info) {
4965 kfree(cfg->ap_info->wpa_ie);
4966 kfree(cfg->ap_info->rsn_ie);
4967 kfree(cfg->ap_info);
4968 cfg->ap_info = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02004969 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004970}
4971
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004972static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004973{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004974 cfg->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL);
4975 if (!cfg->scan_results)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004976 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004977 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4978 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004979 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004980 cfg->profile = kzalloc(sizeof(*cfg->profile), GFP_KERNEL);
4981 if (!cfg->profile)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004982 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004983 cfg->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4984 if (!cfg->bss_info)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004985 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004986 cfg->scan_req_int = kzalloc(sizeof(*cfg->scan_req_int),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004987 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004988 if (!cfg->scan_req_int)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004989 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004990 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4991 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004992 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004993 cfg->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
4994 if (!cfg->dcmd_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004995 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004996 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4997 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004998 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004999 cfg->iscan = kzalloc(sizeof(*cfg->iscan), GFP_KERNEL);
5000 if (!cfg->iscan)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005001 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005002 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5003 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005004 goto init_priv_mem_out;
5005
5006 return 0;
5007
5008init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005009 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005010
5011 return -ENOMEM;
5012}
5013
5014/*
5015* retrieve first queued event from head
5016*/
5017
5018static struct brcmf_cfg80211_event_q *brcmf_deq_event(
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005019 struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005020{
5021 struct brcmf_cfg80211_event_q *e = NULL;
5022
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005023 spin_lock_irq(&cfg->evt_q_lock);
5024 if (!list_empty(&cfg->evt_q_list)) {
5025 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005026 struct brcmf_cfg80211_event_q, evt_q_list);
5027 list_del(&e->evt_q_list);
5028 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005029 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005030
5031 return e;
5032}
5033
5034/*
Arend van Sprielbcbec9e2012-02-09 21:09:06 +01005035* push event to tail of the queue
5036*
5037* remark: this function may not sleep as it is called in atomic context.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005038*/
5039
5040static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005041brcmf_enq_event(struct brcmf_cfg80211_info *cfg, u32 event,
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005042 const struct brcmf_event_msg *msg, void *data)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005043{
5044 struct brcmf_cfg80211_event_q *e;
5045 s32 err = 0;
Arend van Sprielcf440662012-02-09 21:09:07 +01005046 ulong flags;
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005047 u32 data_len;
5048 u32 total_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005049
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005050 total_len = sizeof(struct brcmf_cfg80211_event_q);
5051 if (data)
5052 data_len = be32_to_cpu(msg->datalen);
5053 else
5054 data_len = 0;
5055 total_len += data_len;
5056 e = kzalloc(total_len, GFP_ATOMIC);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005057 if (!e)
5058 return -ENOMEM;
5059
5060 e->etype = event;
5061 memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005062 if (data)
5063 memcpy(&e->edata, data, data_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005064
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005065 spin_lock_irqsave(&cfg->evt_q_lock, flags);
5066 list_add_tail(&e->evt_q_list, &cfg->evt_q_list);
5067 spin_unlock_irqrestore(&cfg->evt_q_lock, flags);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005068
5069 return err;
5070}
5071
5072static void brcmf_put_event(struct brcmf_cfg80211_event_q *e)
5073{
5074 kfree(e);
5075}
5076
5077static void brcmf_cfg80211_event_handler(struct work_struct *work)
5078{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005079 struct brcmf_cfg80211_info *cfg =
5080 container_of(work, struct brcmf_cfg80211_info,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005081 event_work);
5082 struct brcmf_cfg80211_event_q *e;
5083
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005084 e = brcmf_deq_event(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005085 if (unlikely(!e)) {
5086 WL_ERR("event queue empty...\n");
5087 return;
5088 }
5089
5090 do {
5091 WL_INFO("event type (%d)\n", e->etype);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005092 if (cfg->el.handler[e->etype])
5093 cfg->el.handler[e->etype](cfg,
5094 cfg_to_ndev(cfg),
Arend van Spriel5b435de2011-10-05 13:19:03 +02005095 &e->emsg, e->edata);
5096 else
5097 WL_INFO("Unknown Event (%d): ignoring\n", e->etype);
5098 brcmf_put_event(e);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005099 } while ((e = brcmf_deq_event(cfg)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005100
5101}
5102
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005103static void brcmf_init_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005104{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005105 spin_lock_init(&cfg->evt_q_lock);
5106 INIT_LIST_HEAD(&cfg->evt_q_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005107}
5108
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005109static void brcmf_flush_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005110{
5111 struct brcmf_cfg80211_event_q *e;
5112
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005113 spin_lock_irq(&cfg->evt_q_lock);
5114 while (!list_empty(&cfg->evt_q_list)) {
5115 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005116 struct brcmf_cfg80211_event_q, evt_q_list);
5117 list_del(&e->evt_q_list);
5118 kfree(e);
5119 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005120 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005121}
5122
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005123static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005124{
5125 s32 err = 0;
5126
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005127 cfg->scan_request = NULL;
5128 cfg->pwr_save = true;
Hante Meulemane756af52012-09-11 21:18:52 +02005129#ifdef CONFIG_BRCMISCAN
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005130 cfg->iscan_on = true; /* iscan on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005131 we enable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005132 cfg->escan_on = false; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02005133 we disable escan per default */
5134#else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005135 cfg->iscan_on = false; /* iscan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02005136 we disable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005137 cfg->escan_on = true; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02005138 we enable escan per default */
5139#endif
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005140 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005141 we enable roam per default */
5142
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005143 cfg->iscan_kickstart = false;
5144 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02005145 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005146 cfg->dongle_up = false; /* dongle is not up yet */
5147 brcmf_init_eq(cfg);
5148 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005149 if (err)
5150 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005151 INIT_WORK(&cfg->event_work, brcmf_cfg80211_event_handler);
5152 brcmf_init_eloop_handler(&cfg->el);
5153 mutex_init(&cfg->usr_sync);
5154 err = brcmf_init_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005155 if (err)
5156 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005157 brcmf_init_escan(cfg);
5158 brcmf_init_conf(cfg->conf);
5159 brcmf_init_prof(cfg->profile);
5160 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005161
5162 return err;
5163}
5164
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005165static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005166{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005167 cancel_work_sync(&cfg->event_work);
5168 cfg->dongle_up = false; /* dongle down */
5169 brcmf_flush_eq(cfg);
5170 brcmf_link_down(cfg);
5171 brcmf_abort_scanning(cfg);
5172 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005173}
5174
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005175struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct net_device *ndev,
Arend van Sprielb451ec92012-09-27 14:17:52 +02005176 struct device *busdev,
5177 struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005178{
5179 struct wireless_dev *wdev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005180 struct brcmf_cfg80211_info *cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005181 s32 err = 0;
5182
5183 if (!ndev) {
5184 WL_ERR("ndev is invalid\n");
5185 return NULL;
5186 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005187
Arend van Spriel5db6e952012-09-27 14:17:53 +02005188 wdev = brcmf_alloc_wdev(busdev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005189 if (IS_ERR(wdev)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005190 return NULL;
5191 }
5192
5193 wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005194 cfg = wdev_to_cfg(wdev);
5195 cfg->wdev = wdev;
5196 cfg->pub = drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005197 ndev->ieee80211_ptr = wdev;
5198 SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
5199 wdev->netdev = ndev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005200 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201 if (err) {
5202 WL_ERR("Failed to init iwm_priv (%d)\n", err);
5203 goto cfg80211_attach_out;
5204 }
Arend van Spriel5db6e952012-09-27 14:17:53 +02005205
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005206 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005207
5208cfg80211_attach_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005209 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210 return NULL;
5211}
5212
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005213void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005214{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005215 wl_deinit_priv(cfg);
5216 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005217}
5218
5219void
5220brcmf_cfg80211_event(struct net_device *ndev,
5221 const struct brcmf_event_msg *e, void *data)
5222{
5223 u32 event_type = be32_to_cpu(e->event_type);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005224 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005225
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005226 if (!brcmf_enq_event(cfg, event_type, e, data))
5227 schedule_work(&cfg->event_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005228}
5229
5230static s32 brcmf_dongle_mode(struct net_device *ndev, s32 iftype)
5231{
5232 s32 infra = 0;
5233 s32 err = 0;
5234
5235 switch (iftype) {
5236 case NL80211_IFTYPE_MONITOR:
5237 case NL80211_IFTYPE_WDS:
5238 WL_ERR("type (%d) : currently we do not support this mode\n",
5239 iftype);
5240 err = -EINVAL;
5241 return err;
5242 case NL80211_IFTYPE_ADHOC:
5243 infra = 0;
5244 break;
5245 case NL80211_IFTYPE_STATION:
5246 infra = 1;
5247 break;
Hante Meuleman1a873342012-09-27 14:17:54 +02005248 case NL80211_IFTYPE_AP:
5249 infra = 1;
5250 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005251 default:
5252 err = -EINVAL;
5253 WL_ERR("invalid type (%d)\n", iftype);
5254 return err;
5255 }
5256 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra);
5257 if (err) {
5258 WL_ERR("WLC_SET_INFRA error (%d)\n", err);
5259 return err;
5260 }
5261
5262 return 0;
5263}
5264
5265static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
5266{
5267 /* Room for "event_msgs" + '\0' + bitvec */
5268 s8 iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
5269 s8 eventmask[BRCMF_EVENTING_MASK_LEN];
5270 s32 err = 0;
5271
5272 WL_TRACE("Enter\n");
5273
5274 /* Setup event_msgs */
Alwin Beukers53a22772011-10-12 20:51:30 +02005275 brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
5276 iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005277 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, iovbuf, sizeof(iovbuf));
5278 if (err) {
5279 WL_ERR("Get event_msgs error (%d)\n", err);
5280 goto dongle_eventmsg_out;
5281 }
5282 memcpy(eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
5283
5284 setbit(eventmask, BRCMF_E_SET_SSID);
5285 setbit(eventmask, BRCMF_E_ROAM);
5286 setbit(eventmask, BRCMF_E_PRUNE);
5287 setbit(eventmask, BRCMF_E_AUTH);
5288 setbit(eventmask, BRCMF_E_REASSOC);
5289 setbit(eventmask, BRCMF_E_REASSOC_IND);
5290 setbit(eventmask, BRCMF_E_DEAUTH_IND);
5291 setbit(eventmask, BRCMF_E_DISASSOC_IND);
5292 setbit(eventmask, BRCMF_E_DISASSOC);
5293 setbit(eventmask, BRCMF_E_JOIN);
5294 setbit(eventmask, BRCMF_E_ASSOC_IND);
5295 setbit(eventmask, BRCMF_E_PSK_SUP);
5296 setbit(eventmask, BRCMF_E_LINK);
5297 setbit(eventmask, BRCMF_E_NDIS_LINK);
5298 setbit(eventmask, BRCMF_E_MIC_ERROR);
5299 setbit(eventmask, BRCMF_E_PMKID_CACHE);
5300 setbit(eventmask, BRCMF_E_TXFAIL);
5301 setbit(eventmask, BRCMF_E_JOIN_START);
5302 setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
Hante Meulemane756af52012-09-11 21:18:52 +02005303 setbit(eventmask, BRCMF_E_ESCAN_RESULT);
Arend van Spriele5806072012-09-19 22:21:08 +02005304 setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005305
Alwin Beukers53a22772011-10-12 20:51:30 +02005306 brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
5307 iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005308 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
5309 if (err) {
5310 WL_ERR("Set event_msgs error (%d)\n", err);
5311 goto dongle_eventmsg_out;
5312 }
5313
5314dongle_eventmsg_out:
5315 WL_TRACE("Exit\n");
5316 return err;
5317}
5318
5319static s32
5320brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
5321{
5322 s8 iovbuf[32];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005323 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005324 __le32 roamtrigger[2];
5325 __le32 roam_delta[2];
5326 __le32 bcn_to_le;
5327 __le32 roamvar_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005328
5329 /*
5330 * Setup timeout if Beacons are lost and roam is
5331 * off to report link down
5332 */
5333 if (roamvar) {
Arend van Sprielf588bc02011-10-12 20:51:22 +02005334 bcn_to_le = cpu_to_le32(bcn_timeout);
Alwin Beukers53a22772011-10-12 20:51:30 +02005335 brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_to_le,
Arend van Sprielf588bc02011-10-12 20:51:22 +02005336 sizeof(bcn_to_le), iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005337 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR,
5338 iovbuf, sizeof(iovbuf));
5339 if (err) {
5340 WL_ERR("bcn_timeout error (%d)\n", err);
5341 goto dongle_rom_out;
5342 }
5343 }
5344
5345 /*
5346 * Enable/Disable built-in roaming to allow supplicant
5347 * to take care of roaming
5348 */
5349 WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
Arend van Sprielf588bc02011-10-12 20:51:22 +02005350 roamvar_le = cpu_to_le32(roamvar);
Alwin Beukers53a22772011-10-12 20:51:30 +02005351 brcmf_c_mkiovar("roam_off", (char *)&roamvar_le,
Arend van Sprielf588bc02011-10-12 20:51:22 +02005352 sizeof(roamvar_le), iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005353 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
5354 if (err) {
5355 WL_ERR("roam_off error (%d)\n", err);
5356 goto dongle_rom_out;
5357 }
5358
Arend van Sprielf588bc02011-10-12 20:51:22 +02005359 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5360 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005361 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_TRIGGER,
5362 (void *)roamtrigger, sizeof(roamtrigger));
5363 if (err) {
5364 WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
5365 goto dongle_rom_out;
5366 }
5367
Arend van Sprielf588bc02011-10-12 20:51:22 +02005368 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5369 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005370 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_DELTA,
5371 (void *)roam_delta, sizeof(roam_delta));
5372 if (err) {
5373 WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
5374 goto dongle_rom_out;
5375 }
5376
5377dongle_rom_out:
5378 return err;
5379}
5380
5381static s32
5382brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005383 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005384{
5385 s32 err = 0;
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005386 __le32 scan_assoc_tm_le = cpu_to_le32(scan_assoc_time);
5387 __le32 scan_unassoc_tm_le = cpu_to_le32(scan_unassoc_time);
5388 __le32 scan_passive_tm_le = cpu_to_le32(scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005389
5390 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005391 &scan_assoc_tm_le, sizeof(scan_assoc_tm_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005392 if (err) {
5393 if (err == -EOPNOTSUPP)
5394 WL_INFO("Scan assoc time is not supported\n");
5395 else
5396 WL_ERR("Scan assoc time error (%d)\n", err);
5397 goto dongle_scantime_out;
5398 }
5399 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005400 &scan_unassoc_tm_le, sizeof(scan_unassoc_tm_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005401 if (err) {
5402 if (err == -EOPNOTSUPP)
5403 WL_INFO("Scan unassoc time is not supported\n");
5404 else
5405 WL_ERR("Scan unassoc time error (%d)\n", err);
5406 goto dongle_scantime_out;
5407 }
5408
5409 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005410 &scan_passive_tm_le, sizeof(scan_passive_tm_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005411 if (err) {
5412 if (err == -EOPNOTSUPP)
5413 WL_INFO("Scan passive time is not supported\n");
5414 else
5415 WL_ERR("Scan passive time error (%d)\n", err);
5416 goto dongle_scantime_out;
5417 }
5418
5419dongle_scantime_out:
5420 return err;
5421}
5422
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005423static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005424{
5425 struct wiphy *wiphy;
5426 s32 phy_list;
5427 s8 phy;
5428 s32 err = 0;
5429
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005430 err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCM_GET_PHYLIST,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005431 &phy_list, sizeof(phy_list));
5432 if (err) {
5433 WL_ERR("error (%d)\n", err);
5434 return err;
5435 }
5436
Hante Meuleman3ba81372012-09-19 22:21:13 +02005437 phy = ((char *)&phy_list)[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005438 WL_INFO("%c phy\n", phy);
5439 if (phy == 'n' || phy == 'a') {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005440 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005441 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
5442 }
5443
5444 return err;
5445}
5446
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005447static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005448{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005449 return wl_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005450}
5451
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005452static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005453{
5454 struct net_device *ndev;
5455 struct wireless_dev *wdev;
5456 s32 power_mode;
5457 s32 err = 0;
5458
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005459 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005460 return err;
5461
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005462 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005463 wdev = ndev->ieee80211_ptr;
5464
5465 brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
5466 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
5467
5468 err = brcmf_dongle_eventmsg(ndev);
5469 if (err)
5470 goto default_conf_out;
5471
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005472 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005473 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &power_mode);
5474 if (err)
5475 goto default_conf_out;
5476 WL_INFO("power save set to %s\n",
5477 (power_mode ? "enabled" : "disabled"));
5478
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005479 err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
Arend van Spriel5b435de2011-10-05 13:19:03 +02005480 WL_BEACON_TIMEOUT);
5481 if (err)
5482 goto default_conf_out;
5483 err = brcmf_dongle_mode(ndev, wdev->iftype);
5484 if (err && err != -EINPROGRESS)
5485 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005486 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005487 if (err)
5488 goto default_conf_out;
5489
5490 /* -EINPROGRESS: Call commit handler */
5491
5492default_conf_out:
5493
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005494 cfg->dongle_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005495
5496 return err;
5497
5498}
5499
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005500static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005501{
5502 char buf[10+IFNAMSIZ];
5503 struct dentry *fd;
5504 s32 err = 0;
5505
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005506 sprintf(buf, "netdev:%s", cfg_to_ndev(cfg)->name);
5507 cfg->debugfsdir = debugfs_create_dir(buf,
5508 cfg_to_wiphy(cfg)->debugfsdir);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005509
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005510 fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg->debugfsdir,
5511 (u16 *)&cfg->profile->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005512 if (!fd) {
5513 err = -ENOMEM;
5514 goto err_out;
5515 }
5516
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005517 fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg->debugfsdir,
5518 (u8 *)&cfg->profile->dtim_period);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005519 if (!fd) {
5520 err = -ENOMEM;
5521 goto err_out;
5522 }
5523
5524err_out:
5525 return err;
5526}
5527
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005528static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005529{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005530 debugfs_remove_recursive(cfg->debugfsdir);
5531 cfg->debugfsdir = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005532}
5533
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005534static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005535{
5536 s32 err = 0;
5537
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005538 set_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005539
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005540 brcmf_debugfs_add_netdev_params(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005541
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005542 err = brcmf_config_dongle(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005543 if (err)
5544 return err;
5545
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005546 brcmf_invoke_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005547
5548 return err;
5549}
5550
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005551static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005552{
5553 /*
5554 * While going down, if associated with AP disassociate
5555 * from AP to save power
5556 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005557 if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
5558 test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
5559 test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005560 WL_INFO("Disassociating from AP");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005561 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005562
5563 /* Make sure WPA_Supplicant receives all the event
5564 generated due to DISASSOC call to the fw to keep
5565 the state fw and WPA_Supplicant state consistent
5566 */
5567 brcmf_delay(500);
5568 }
5569
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005570 brcmf_abort_scanning(cfg);
5571 clear_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005572
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005573 brcmf_debugfs_remove_netdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005574
5575 return 0;
5576}
5577
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005578s32 brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005579{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005580 s32 err = 0;
5581
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005582 mutex_lock(&cfg->usr_sync);
5583 err = __brcmf_cfg80211_up(cfg);
5584 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005585
5586 return err;
5587}
5588
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005589s32 brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005590{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005591 s32 err = 0;
5592
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005593 mutex_lock(&cfg->usr_sync);
5594 err = __brcmf_cfg80211_down(cfg);
5595 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005596
5597 return err;
5598}
5599