blob: b27e245c2a116e48f6eba55760b242046afa3466 [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);
Hante Meulemaned205b32012-09-11 21:16:47 +0200654 if (ssid && ssid->SSID_len) {
655 params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len);
656 memcpy(&params_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len);
657 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200658}
659
660static s32
661brcmf_dev_iovar_setbuf(struct net_device *ndev, s8 * iovar, void *param,
662 s32 paramlen, void *bufptr, s32 buflen)
663{
664 s32 iolen;
665
Alwin Beukers53a22772011-10-12 20:51:30 +0200666 iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200667 BUG_ON(!iolen);
668
669 return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, bufptr, iolen);
670}
671
672static s32
673brcmf_dev_iovar_getbuf(struct net_device *ndev, s8 * iovar, void *param,
674 s32 paramlen, void *bufptr, s32 buflen)
675{
676 s32 iolen;
677
Alwin Beukers53a22772011-10-12 20:51:30 +0200678 iolen = brcmf_c_mkiovar(iovar, param, paramlen, bufptr, buflen);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200679 BUG_ON(!iolen);
680
681 return brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, bufptr, buflen);
682}
683
684static s32
685brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
686 struct brcmf_ssid *ssid, u16 action)
687{
688 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
689 offsetof(struct brcmf_iscan_params_le, params_le);
690 struct brcmf_iscan_params_le *params;
691 s32 err = 0;
692
693 if (ssid && ssid->SSID_len)
694 params_size += sizeof(struct brcmf_ssid);
695 params = kzalloc(params_size, GFP_KERNEL);
696 if (!params)
697 return -ENOMEM;
698 BUG_ON(params_size >= BRCMF_DCMD_SMLEN);
699
Hante Meuleman35aafa92012-09-11 21:18:49 +0200700 brcmf_iscan_prep(&params->params_le, ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200701
702 params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION);
703 params->action = cpu_to_le16(action);
704 params->scan_duration = cpu_to_le16(0);
705
706 err = brcmf_dev_iovar_setbuf(iscan->ndev, "iscan", params, params_size,
707 iscan->dcmd_buf, BRCMF_DCMD_SMLEN);
708 if (err) {
709 if (err == -EBUSY)
710 WL_INFO("system busy : iscan canceled\n");
711 else
712 WL_ERR("error (%d)\n", err);
713 }
714
715 kfree(params);
716 return err;
717}
718
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200719static s32 brcmf_do_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200720{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200721 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
722 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200723 struct brcmf_ssid ssid;
Arend van Spriel66831072011-10-12 20:51:19 +0200724 __le32 passive_scan;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200725 s32 err = 0;
726
727 /* Broadcast scan by default */
728 memset(&ssid, 0, sizeof(ssid));
729
730 iscan->state = WL_ISCAN_STATE_SCANING;
731
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200732 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
733 err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCMF_C_SET_PASSIVE_SCAN,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200734 &passive_scan, sizeof(passive_scan));
735 if (err) {
736 WL_ERR("error (%d)\n", err);
737 return err;
738 }
739 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200740 cfg->iscan_kickstart = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200741 err = brcmf_run_iscan(iscan, &ssid, BRCMF_SCAN_ACTION_START);
742 if (err) {
743 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200744 cfg->iscan_kickstart = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200745 return err;
746 }
747 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
748 iscan->timer_on = 1;
749 return err;
750}
751
752static s32
Hante Meuleman35aafa92012-09-11 21:18:49 +0200753brcmf_cfg80211_iscan(struct wiphy *wiphy, struct net_device *ndev,
754 struct cfg80211_scan_request *request,
755 struct cfg80211_ssid *this_ssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200756{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200757 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200758 struct cfg80211_ssid *ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200759 struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
Arend van Spriel66831072011-10-12 20:51:19 +0200760 __le32 passive_scan;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200761 bool iscan_req;
762 bool spec_scan;
763 s32 err = 0;
764 u32 SSID_len;
765
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200766 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
767 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200768 return -EAGAIN;
769 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200770 if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200771 WL_ERR("Scanning being aborted : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200772 cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200773 return -EAGAIN;
774 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200775 if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200776 WL_ERR("Connecting : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200777 cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200778 return -EAGAIN;
779 }
780
781 iscan_req = false;
782 spec_scan = false;
783 if (request) {
784 /* scan bss */
785 ssids = request->ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200786 if (cfg->iscan_on && (!ssids || !ssids->ssid_len))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200787 iscan_req = true;
788 } else {
789 /* scan in ibss */
790 /* we don't do iscan in ibss */
791 ssids = this_ssid;
792 }
793
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200794 cfg->scan_request = request;
795 set_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200796 if (iscan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200797 err = brcmf_do_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200798 if (!err)
799 return err;
800 else
801 goto scan_out;
802 } else {
803 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
804 ssids->ssid, ssids->ssid_len);
805 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
806 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
807 sr->ssid_le.SSID_len = cpu_to_le32(0);
808 if (SSID_len) {
809 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
810 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
811 spec_scan = true;
812 } else {
813 WL_SCAN("Broadcast scan\n");
814 }
815
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200816 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200817 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
818 &passive_scan, sizeof(passive_scan));
819 if (err) {
820 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
821 goto scan_out;
822 }
823 brcmf_set_mpc(ndev, 0);
824 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
825 sizeof(sr->ssid_le));
826 if (err) {
827 if (err == -EBUSY)
828 WL_INFO("system busy : scan for \"%s\" "
829 "canceled\n", sr->ssid_le.SSID);
830 else
831 WL_ERR("WLC_SCAN error (%d)\n", err);
832
833 brcmf_set_mpc(ndev, 1);
834 goto scan_out;
835 }
836 }
837
838 return 0;
839
840scan_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200841 clear_bit(WL_STATUS_SCANNING, &cfg->status);
842 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200843 return err;
844}
845
Hante Meulemane756af52012-09-11 21:18:52 +0200846static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
847 struct cfg80211_scan_request *request)
848{
849 u32 n_ssids;
850 u32 n_channels;
851 s32 i;
852 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200853 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200854 u16 channel;
855 struct ieee80211_channel *req_channel;
856 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200857 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200858
859 memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
860 params_le->bss_type = DOT11_BSSTYPE_ANY;
861 params_le->scan_type = 0;
862 params_le->channel_num = 0;
863 params_le->nprobes = cpu_to_le32(-1);
864 params_le->active_time = cpu_to_le32(-1);
865 params_le->passive_time = cpu_to_le32(-1);
866 params_le->home_time = cpu_to_le32(-1);
867 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
868
869 /* if request is null exit so it will be all channel broadcast scan */
870 if (!request)
871 return;
872
873 n_ssids = request->n_ssids;
874 n_channels = request->n_channels;
875 /* Copy channel array if applicable */
876 WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
877 if (n_channels > 0) {
878 for (i = 0; i < n_channels; i++) {
879 chanspec = 0;
880 req_channel = request->channels[i];
881 channel = ieee80211_frequency_to_channel(
882 req_channel->center_freq);
883 if (req_channel->band == IEEE80211_BAND_2GHZ)
884 chanspec |= WL_CHANSPEC_BAND_2G;
885 else
886 chanspec |= WL_CHANSPEC_BAND_5G;
887
888 if (req_channel->flags & IEEE80211_CHAN_NO_HT40) {
889 chanspec |= WL_CHANSPEC_BW_20;
890 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
891 } else {
892 chanspec |= WL_CHANSPEC_BW_40;
893 if (req_channel->flags &
894 IEEE80211_CHAN_NO_HT40PLUS)
895 chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
896 else
897 chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
898 }
899
Arend van Spriel029591f2012-09-19 22:21:06 +0200900 chanspec |= (channel & WL_CHANSPEC_CHAN_MASK);
Hante Meulemane756af52012-09-11 21:18:52 +0200901 WL_SCAN("Chan : %d, Channel spec: %x\n",
Arend van Spriel029591f2012-09-19 22:21:06 +0200902 channel, chanspec);
903 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200904 }
905 } else {
906 WL_SCAN("Scanning all channels\n");
907 }
908 /* Copy ssid array if applicable */
909 WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids);
910 if (n_ssids > 0) {
911 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
912 n_channels * sizeof(u16);
913 offset = roundup(offset, sizeof(u32));
914 ptr = (char *)params_le + offset;
915 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200916 memset(&ssid_le, 0, sizeof(ssid_le));
917 ssid_le.SSID_len =
918 cpu_to_le32(request->ssids[i].ssid_len);
919 memcpy(ssid_le.SSID, request->ssids[i].ssid,
920 request->ssids[i].ssid_len);
921 if (!ssid_le.SSID_len)
Hante Meulemane756af52012-09-11 21:18:52 +0200922 WL_SCAN("%d: Broadcast scan\n", i);
923 else
924 WL_SCAN("%d: scan for %s size =%d\n", i,
Arend van Spriel029591f2012-09-19 22:21:06 +0200925 ssid_le.SSID, ssid_le.SSID_len);
926 memcpy(ptr, &ssid_le, sizeof(ssid_le));
927 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200928 }
929 } else {
930 WL_SCAN("Broadcast scan %p\n", request->ssids);
931 if ((request->ssids) && request->ssids->ssid_len) {
932 WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID,
933 request->ssids->ssid_len);
934 params_le->ssid_le.SSID_len =
935 cpu_to_le32(request->ssids->ssid_len);
936 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
937 request->ssids->ssid_len);
938 }
939 }
940 /* Adding mask to channel numbers */
941 params_le->channel_num =
942 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
943 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
944}
945
946static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200947brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +0200948 struct net_device *ndev,
949 bool aborted, bool fw_abort)
950{
951 struct brcmf_scan_params_le params_le;
952 struct cfg80211_scan_request *scan_request;
953 s32 err = 0;
954
955 WL_SCAN("Enter\n");
956
957 /* clear scan request, because the FW abort can cause a second call */
958 /* to this functon and might cause a double cfg80211_scan_done */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200959 scan_request = cfg->scan_request;
960 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200961
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200962 if (timer_pending(&cfg->escan_timeout))
963 del_timer_sync(&cfg->escan_timeout);
Hante Meulemane756af52012-09-11 21:18:52 +0200964
965 if (fw_abort) {
966 /* Do a scan abort to stop the driver's scan engine */
967 WL_SCAN("ABORT scan in firmware\n");
968 memset(&params_le, 0, sizeof(params_le));
969 memcpy(params_le.bssid, ether_bcast, ETH_ALEN);
970 params_le.bss_type = DOT11_BSSTYPE_ANY;
971 params_le.scan_type = 0;
972 params_le.channel_num = cpu_to_le32(1);
973 params_le.nprobes = cpu_to_le32(1);
974 params_le.active_time = cpu_to_le32(-1);
975 params_le.passive_time = cpu_to_le32(-1);
976 params_le.home_time = cpu_to_le32(-1);
977 /* Scan is aborted by setting channel_list[0] to -1 */
978 params_le.channel_list[0] = cpu_to_le16(-1);
979 /* E-Scan (or anyother type) can be aborted by SCAN */
980 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &params_le,
981 sizeof(params_le));
982 if (err)
983 WL_ERR("Scan abort failed\n");
984 }
Arend van Spriele5806072012-09-19 22:21:08 +0200985 /*
986 * e-scan can be initiated by scheduled scan
987 * which takes precedence.
988 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200989 if (cfg->sched_escan) {
Arend van Spriele5806072012-09-19 22:21:08 +0200990 WL_SCAN("scheduled scan completed\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200991 cfg->sched_escan = false;
Arend van Spriele5806072012-09-19 22:21:08 +0200992 if (!aborted)
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200993 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriele5806072012-09-19 22:21:08 +0200994 brcmf_set_mpc(ndev, 1);
995 } else if (scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +0200996 WL_SCAN("ESCAN Completed scan: %s\n",
997 aborted ? "Aborted" : "Done");
998 cfg80211_scan_done(scan_request, aborted);
999 brcmf_set_mpc(ndev, 1);
1000 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001001 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02001002 WL_ERR("Scan complete while device not scanning\n");
1003 return -EPERM;
1004 }
1005
1006 return err;
1007}
1008
1009static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001010brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
Hante Meulemane756af52012-09-11 21:18:52 +02001011 struct cfg80211_scan_request *request, u16 action)
1012{
1013 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
1014 offsetof(struct brcmf_escan_params_le, params_le);
1015 struct brcmf_escan_params_le *params;
1016 s32 err = 0;
1017
1018 WL_SCAN("E-SCAN START\n");
1019
1020 if (request != NULL) {
1021 /* Allocate space for populating ssids in struct */
1022 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
1023
1024 /* Allocate space for populating ssids in struct */
1025 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
1026 }
1027
1028 params = kzalloc(params_size, GFP_KERNEL);
1029 if (!params) {
1030 err = -ENOMEM;
1031 goto exit;
1032 }
1033 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
1034 brcmf_escan_prep(&params->params_le, request);
1035 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
1036 params->action = cpu_to_le16(action);
1037 params->sync_id = cpu_to_le16(0x1234);
1038
1039 err = brcmf_dev_iovar_setbuf(ndev, "escan", params, params_size,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001040 cfg->escan_ioctl_buf, BRCMF_DCMD_MEDLEN);
Hante Meulemane756af52012-09-11 21:18:52 +02001041 if (err) {
1042 if (err == -EBUSY)
1043 WL_INFO("system busy : escan canceled\n");
1044 else
1045 WL_ERR("error (%d)\n", err);
1046 }
1047
1048 kfree(params);
1049exit:
1050 return err;
1051}
1052
1053static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001054brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Hante Meulemane756af52012-09-11 21:18:52 +02001055 struct net_device *ndev, struct cfg80211_scan_request *request)
1056{
1057 s32 err;
1058 __le32 passive_scan;
1059 struct brcmf_scan_results *results;
1060
1061 WL_SCAN("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001062 cfg->escan_info.ndev = ndev;
1063 cfg->escan_info.wiphy = wiphy;
1064 cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
1065 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
Hante Meulemane756af52012-09-11 21:18:52 +02001066 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
1067 &passive_scan, sizeof(passive_scan));
1068 if (err) {
1069 WL_ERR("error (%d)\n", err);
1070 return err;
1071 }
1072 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001073 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001074 results->version = 0;
1075 results->count = 0;
1076 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1077
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001078 err = brcmf_run_escan(cfg, ndev, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001079 if (err)
1080 brcmf_set_mpc(ndev, 1);
1081 return err;
1082}
1083
1084static s32
1085brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
1086 struct cfg80211_scan_request *request,
1087 struct cfg80211_ssid *this_ssid)
1088{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001089 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Hante Meulemane756af52012-09-11 21:18:52 +02001090 struct cfg80211_ssid *ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001091 struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
Hante Meulemane756af52012-09-11 21:18:52 +02001092 __le32 passive_scan;
1093 bool escan_req;
1094 bool spec_scan;
1095 s32 err;
1096 u32 SSID_len;
1097
1098 WL_SCAN("START ESCAN\n");
1099
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001100 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
1101 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001102 return -EAGAIN;
1103 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001104 if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02001105 WL_ERR("Scanning being aborted : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001106 cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001107 return -EAGAIN;
1108 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001109 if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02001110 WL_ERR("Connecting : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001111 cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001112 return -EAGAIN;
1113 }
1114
1115 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001116 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +02001117 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1118
1119 escan_req = false;
1120 if (request) {
1121 /* scan bss */
1122 ssids = request->ssids;
1123 escan_req = true;
1124 } else {
1125 /* scan in ibss */
1126 /* we don't do escan in ibss */
1127 ssids = this_ssid;
1128 }
1129
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001130 cfg->scan_request = request;
1131 set_bit(WL_STATUS_SCANNING, &cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +02001132 if (escan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001133 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001134 if (!err)
1135 return err;
1136 else
1137 goto scan_out;
1138 } else {
1139 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
1140 ssids->ssid, ssids->ssid_len);
1141 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1142 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1143 sr->ssid_le.SSID_len = cpu_to_le32(0);
1144 spec_scan = false;
1145 if (SSID_len) {
1146 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1147 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1148 spec_scan = true;
1149 } else
1150 WL_SCAN("Broadcast scan\n");
1151
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001152 passive_scan = cfg->active_scan ? 0 : cpu_to_le32(1);
Hante Meulemane756af52012-09-11 21:18:52 +02001153 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_PASSIVE_SCAN,
1154 &passive_scan, sizeof(passive_scan));
1155 if (err) {
1156 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
1157 goto scan_out;
1158 }
1159 brcmf_set_mpc(ndev, 0);
1160 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN, &sr->ssid_le,
1161 sizeof(sr->ssid_le));
1162 if (err) {
1163 if (err == -EBUSY)
1164 WL_INFO("BUSY: scan for \"%s\" canceled\n",
1165 sr->ssid_le.SSID);
1166 else
1167 WL_ERR("WLC_SCAN error (%d)\n", err);
1168
1169 brcmf_set_mpc(ndev, 1);
1170 goto scan_out;
1171 }
1172 }
1173
1174 return 0;
1175
1176scan_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001177 clear_bit(WL_STATUS_SCANNING, &cfg->status);
1178 if (timer_pending(&cfg->escan_timeout))
1179 del_timer_sync(&cfg->escan_timeout);
1180 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001181 return err;
1182}
1183
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184static s32
Johannes Bergfd014282012-06-18 19:17:03 +02001185brcmf_cfg80211_scan(struct wiphy *wiphy,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001186 struct cfg80211_scan_request *request)
1187{
Johannes Bergfd014282012-06-18 19:17:03 +02001188 struct net_device *ndev = request->wdev->netdev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001189 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001190 s32 err = 0;
1191
1192 WL_TRACE("Enter\n");
1193
1194 if (!check_sys_up(wiphy))
1195 return -EIO;
1196
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001197 if (cfg->iscan_on)
Hante Meulemane756af52012-09-11 21:18:52 +02001198 err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001199 else if (cfg->escan_on)
Hante Meulemane756af52012-09-11 21:18:52 +02001200 err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
1201
Arend van Spriel5b435de2011-10-05 13:19:03 +02001202 if (err)
1203 WL_ERR("scan error (%d)\n", err);
1204
1205 WL_TRACE("Exit\n");
1206 return err;
1207}
1208
1209static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1210{
1211 s32 err = 0;
1212
1213 err = brcmf_dev_intvar_set(ndev, "rtsthresh", rts_threshold);
1214 if (err)
1215 WL_ERR("Error (%d)\n", err);
1216
1217 return err;
1218}
1219
1220static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1221{
1222 s32 err = 0;
1223
1224 err = brcmf_dev_intvar_set(ndev, "fragthresh", frag_threshold);
1225 if (err)
1226 WL_ERR("Error (%d)\n", err);
1227
1228 return err;
1229}
1230
1231static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1232{
1233 s32 err = 0;
1234 u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL);
1235
1236 err = brcmf_exec_dcmd_u32(ndev, cmd, &retry);
1237 if (err) {
1238 WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
1239 return err;
1240 }
1241 return err;
1242}
1243
1244static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1245{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001246 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1247 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001248 s32 err = 0;
1249
1250 WL_TRACE("Enter\n");
1251 if (!check_sys_up(wiphy))
1252 return -EIO;
1253
1254 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001255 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1256 cfg->conf->rts_threshold = wiphy->rts_threshold;
1257 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001258 if (!err)
1259 goto done;
1260 }
1261 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001262 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1263 cfg->conf->frag_threshold = wiphy->frag_threshold;
1264 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001265 if (!err)
1266 goto done;
1267 }
1268 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001269 && (cfg->conf->retry_long != wiphy->retry_long)) {
1270 cfg->conf->retry_long = wiphy->retry_long;
1271 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001272 if (!err)
1273 goto done;
1274 }
1275 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001276 && (cfg->conf->retry_short != wiphy->retry_short)) {
1277 cfg->conf->retry_short = wiphy->retry_short;
1278 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001279 if (!err)
1280 goto done;
1281 }
1282
1283done:
1284 WL_TRACE("Exit\n");
1285 return err;
1286}
1287
Arend van Spriel5b435de2011-10-05 13:19:03 +02001288static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1289{
1290 memset(prof, 0, sizeof(*prof));
1291}
1292
1293static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
1294 size_t *join_params_size)
1295{
1296 u16 chanspec = 0;
1297
1298 if (ch != 0) {
1299 if (ch <= CH_MAX_2G_CHANNEL)
1300 chanspec |= WL_CHANSPEC_BAND_2G;
1301 else
1302 chanspec |= WL_CHANSPEC_BAND_5G;
1303
1304 chanspec |= WL_CHANSPEC_BW_20;
1305 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
1306
1307 *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
1308 sizeof(u16);
1309
1310 chanspec |= (ch & WL_CHANSPEC_CHAN_MASK);
1311 join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1312 join_params->params_le.chanspec_num = cpu_to_le32(1);
1313
1314 WL_CONN("join_params->params.chanspec_list[0]= %#X,"
1315 "channel %d, chanspec %#X\n",
1316 chanspec, ch, chanspec);
1317 }
1318}
1319
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001320static void brcmf_link_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321{
1322 struct net_device *ndev = NULL;
1323 s32 err = 0;
1324
1325 WL_TRACE("Enter\n");
1326
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001327 if (cfg->link_up) {
1328 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001329 WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
1330 err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, NULL, 0);
1331 if (err)
1332 WL_ERR("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001333 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334 }
1335 WL_TRACE("Exit\n");
1336}
1337
1338static s32
1339brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1340 struct cfg80211_ibss_params *params)
1341{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001342 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001343 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001344 struct brcmf_join_params join_params;
1345 size_t join_params_size = 0;
1346 s32 err = 0;
1347 s32 wsec = 0;
1348 s32 bcnprd;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001349
1350 WL_TRACE("Enter\n");
1351 if (!check_sys_up(wiphy))
1352 return -EIO;
1353
1354 if (params->ssid)
1355 WL_CONN("SSID: %s\n", params->ssid);
1356 else {
1357 WL_CONN("SSID: NULL, Not supported\n");
1358 return -EOPNOTSUPP;
1359 }
1360
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001361 set_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001362
1363 if (params->bssid)
Andy Shevchenko040a7832012-07-12 10:43:44 +03001364 WL_CONN("BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001365 else
1366 WL_CONN("No BSSID specified\n");
1367
1368 if (params->channel)
1369 WL_CONN("channel: %d\n", params->channel->center_freq);
1370 else
1371 WL_CONN("no channel specified\n");
1372
1373 if (params->channel_fixed)
1374 WL_CONN("fixed channel required\n");
1375 else
1376 WL_CONN("no fixed channel required\n");
1377
1378 if (params->ie && params->ie_len)
1379 WL_CONN("ie len: %d\n", params->ie_len);
1380 else
1381 WL_CONN("no ie specified\n");
1382
1383 if (params->beacon_interval)
1384 WL_CONN("beacon interval: %d\n", params->beacon_interval);
1385 else
1386 WL_CONN("no beacon interval specified\n");
1387
1388 if (params->basic_rates)
1389 WL_CONN("basic rates: %08X\n", params->basic_rates);
1390 else
1391 WL_CONN("no basic rates specified\n");
1392
1393 if (params->privacy)
1394 WL_CONN("privacy required\n");
1395 else
1396 WL_CONN("no privacy required\n");
1397
1398 /* Configure Privacy for starter */
1399 if (params->privacy)
1400 wsec |= WEP_ENABLED;
1401
1402 err = brcmf_dev_intvar_set(ndev, "wsec", wsec);
1403 if (err) {
1404 WL_ERR("wsec failed (%d)\n", err);
1405 goto done;
1406 }
1407
1408 /* Configure Beacon Interval for starter */
1409 if (params->beacon_interval)
1410 bcnprd = params->beacon_interval;
1411 else
1412 bcnprd = 100;
1413
1414 err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_BCNPRD, &bcnprd);
1415 if (err) {
1416 WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
1417 goto done;
1418 }
1419
1420 /* Configure required join parameter */
1421 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1422
1423 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001424 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1425 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1426 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1427 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001428 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001429
1430 /* BSSID */
1431 if (params->bssid) {
1432 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1433 join_params_size = sizeof(join_params.ssid_le) +
1434 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001435 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001436 } else {
1437 memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001438 memset(profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001439 }
1440
Arend van Spriel5b435de2011-10-05 13:19:03 +02001441 /* Channel */
1442 if (params->channel) {
1443 u32 target_channel;
1444
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001445 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001446 ieee80211_frequency_to_channel(
1447 params->channel->center_freq);
1448 if (params->channel_fixed) {
1449 /* adding chanspec */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001450 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001451 &join_params, &join_params_size);
1452 }
1453
1454 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001455 target_channel = cfg->channel;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001456 err = brcmf_exec_dcmd_u32(ndev, BRCM_SET_CHANNEL,
1457 &target_channel);
1458 if (err) {
1459 WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
1460 goto done;
1461 }
1462 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001463 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001464
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001465 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001466
1467
1468 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
1469 &join_params, join_params_size);
1470 if (err) {
1471 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1472 goto done;
1473 }
1474
1475done:
1476 if (err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001477 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001478 WL_TRACE("Exit\n");
1479 return err;
1480}
1481
1482static s32
1483brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1484{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001485 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001486 s32 err = 0;
1487
1488 WL_TRACE("Enter\n");
1489 if (!check_sys_up(wiphy))
1490 return -EIO;
1491
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001492 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001493
1494 WL_TRACE("Exit\n");
1495
1496 return err;
1497}
1498
1499static s32 brcmf_set_wpa_version(struct net_device *ndev,
1500 struct cfg80211_connect_params *sme)
1501{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001502 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001503 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001504 struct brcmf_cfg80211_security *sec;
1505 s32 val = 0;
1506 s32 err = 0;
1507
1508 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1509 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1510 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1511 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1512 else
1513 val = WPA_AUTH_DISABLED;
1514 WL_CONN("setting wpa_auth to 0x%0x\n", val);
1515 err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
1516 if (err) {
1517 WL_ERR("set wpa_auth failed (%d)\n", err);
1518 return err;
1519 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001520 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001521 sec->wpa_versions = sme->crypto.wpa_versions;
1522 return err;
1523}
1524
1525static s32 brcmf_set_auth_type(struct net_device *ndev,
1526 struct cfg80211_connect_params *sme)
1527{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001528 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001529 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001530 struct brcmf_cfg80211_security *sec;
1531 s32 val = 0;
1532 s32 err = 0;
1533
1534 switch (sme->auth_type) {
1535 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1536 val = 0;
1537 WL_CONN("open system\n");
1538 break;
1539 case NL80211_AUTHTYPE_SHARED_KEY:
1540 val = 1;
1541 WL_CONN("shared key\n");
1542 break;
1543 case NL80211_AUTHTYPE_AUTOMATIC:
1544 val = 2;
1545 WL_CONN("automatic\n");
1546 break;
1547 case NL80211_AUTHTYPE_NETWORK_EAP:
1548 WL_CONN("network eap\n");
1549 default:
1550 val = 2;
1551 WL_ERR("invalid auth type (%d)\n", sme->auth_type);
1552 break;
1553 }
1554
1555 err = brcmf_dev_intvar_set(ndev, "auth", val);
1556 if (err) {
1557 WL_ERR("set auth failed (%d)\n", err);
1558 return err;
1559 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001560 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001561 sec->auth_type = sme->auth_type;
1562 return err;
1563}
1564
1565static s32
1566brcmf_set_set_cipher(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 pval = 0;
1573 s32 gval = 0;
1574 s32 err = 0;
1575
1576 if (sme->crypto.n_ciphers_pairwise) {
1577 switch (sme->crypto.ciphers_pairwise[0]) {
1578 case WLAN_CIPHER_SUITE_WEP40:
1579 case WLAN_CIPHER_SUITE_WEP104:
1580 pval = WEP_ENABLED;
1581 break;
1582 case WLAN_CIPHER_SUITE_TKIP:
1583 pval = TKIP_ENABLED;
1584 break;
1585 case WLAN_CIPHER_SUITE_CCMP:
1586 pval = AES_ENABLED;
1587 break;
1588 case WLAN_CIPHER_SUITE_AES_CMAC:
1589 pval = AES_ENABLED;
1590 break;
1591 default:
1592 WL_ERR("invalid cipher pairwise (%d)\n",
1593 sme->crypto.ciphers_pairwise[0]);
1594 return -EINVAL;
1595 }
1596 }
1597 if (sme->crypto.cipher_group) {
1598 switch (sme->crypto.cipher_group) {
1599 case WLAN_CIPHER_SUITE_WEP40:
1600 case WLAN_CIPHER_SUITE_WEP104:
1601 gval = WEP_ENABLED;
1602 break;
1603 case WLAN_CIPHER_SUITE_TKIP:
1604 gval = TKIP_ENABLED;
1605 break;
1606 case WLAN_CIPHER_SUITE_CCMP:
1607 gval = AES_ENABLED;
1608 break;
1609 case WLAN_CIPHER_SUITE_AES_CMAC:
1610 gval = AES_ENABLED;
1611 break;
1612 default:
1613 WL_ERR("invalid cipher group (%d)\n",
1614 sme->crypto.cipher_group);
1615 return -EINVAL;
1616 }
1617 }
1618
1619 WL_CONN("pval (%d) gval (%d)\n", pval, gval);
1620 err = brcmf_dev_intvar_set(ndev, "wsec", pval | gval);
1621 if (err) {
1622 WL_ERR("error (%d)\n", err);
1623 return err;
1624 }
1625
Arend van Spriel06bb1232012-09-27 14:17:56 +02001626 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001627 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1628 sec->cipher_group = sme->crypto.cipher_group;
1629
1630 return err;
1631}
1632
1633static s32
1634brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1635{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001636 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001637 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001638 struct brcmf_cfg80211_security *sec;
1639 s32 val = 0;
1640 s32 err = 0;
1641
1642 if (sme->crypto.n_akm_suites) {
1643 err = brcmf_dev_intvar_get(ndev, "wpa_auth", &val);
1644 if (err) {
1645 WL_ERR("could not get wpa_auth (%d)\n", err);
1646 return err;
1647 }
1648 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1649 switch (sme->crypto.akm_suites[0]) {
1650 case WLAN_AKM_SUITE_8021X:
1651 val = WPA_AUTH_UNSPECIFIED;
1652 break;
1653 case WLAN_AKM_SUITE_PSK:
1654 val = WPA_AUTH_PSK;
1655 break;
1656 default:
1657 WL_ERR("invalid cipher group (%d)\n",
1658 sme->crypto.cipher_group);
1659 return -EINVAL;
1660 }
1661 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1662 switch (sme->crypto.akm_suites[0]) {
1663 case WLAN_AKM_SUITE_8021X:
1664 val = WPA2_AUTH_UNSPECIFIED;
1665 break;
1666 case WLAN_AKM_SUITE_PSK:
1667 val = WPA2_AUTH_PSK;
1668 break;
1669 default:
1670 WL_ERR("invalid cipher group (%d)\n",
1671 sme->crypto.cipher_group);
1672 return -EINVAL;
1673 }
1674 }
1675
1676 WL_CONN("setting wpa_auth to %d\n", val);
1677 err = brcmf_dev_intvar_set(ndev, "wpa_auth", val);
1678 if (err) {
1679 WL_ERR("could not set wpa_auth (%d)\n", err);
1680 return err;
1681 }
1682 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001683 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001684 sec->wpa_auth = sme->crypto.akm_suites[0];
1685
1686 return err;
1687}
1688
1689static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001690brcmf_set_sharedkey(struct net_device *ndev,
1691 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001692{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001693 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001694 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001695 struct brcmf_cfg80211_security *sec;
1696 struct brcmf_wsec_key key;
1697 s32 val;
1698 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001699 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001700
1701 WL_CONN("key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001702
Roland Vossena718e2f2011-10-12 20:51:24 +02001703 if (sme->key_len == 0)
1704 return 0;
1705
Arend van Spriel06bb1232012-09-27 14:17:56 +02001706 sec = &profile->sec;
Roland Vossena718e2f2011-10-12 20:51:24 +02001707 WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n",
1708 sec->wpa_versions, sec->cipher_pairwise);
1709
1710 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1711 return 0;
1712
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001713 if (!(sec->cipher_pairwise &
1714 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1715 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001716
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001717 memset(&key, 0, sizeof(key));
1718 key.len = (u32) sme->key_len;
1719 key.index = (u32) sme->key_idx;
1720 if (key.len > sizeof(key.data)) {
1721 WL_ERR("Too long key length (%u)\n", key.len);
1722 return -EINVAL;
1723 }
1724 memcpy(key.data, sme->key, key.len);
1725 key.flags = BRCMF_PRIMARY_KEY;
1726 switch (sec->cipher_pairwise) {
1727 case WLAN_CIPHER_SUITE_WEP40:
1728 key.algo = CRYPTO_ALGO_WEP1;
1729 break;
1730 case WLAN_CIPHER_SUITE_WEP104:
1731 key.algo = CRYPTO_ALGO_WEP128;
1732 break;
1733 default:
1734 WL_ERR("Invalid algorithm (%d)\n",
1735 sme->crypto.ciphers_pairwise[0]);
1736 return -EINVAL;
1737 }
1738 /* Set the new key/index */
1739 WL_CONN("key length (%d) key index (%d) algo (%d)\n",
1740 key.len, key.index, key.algo);
1741 WL_CONN("key \"%s\"\n", key.data);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001742 bssidx = brcmf_find_bssidx(cfg, ndev);
1743 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001744 if (err)
1745 return err;
1746
1747 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
1748 WL_CONN("set auth_type to shared key\n");
1749 val = WL_AUTH_SHARED_KEY; /* shared key */
1750 err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", val, bssidx);
1751 if (err)
1752 WL_ERR("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001753 }
1754 return err;
1755}
1756
1757static s32
1758brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
1759 struct cfg80211_connect_params *sme)
1760{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001761 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001762 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001763 struct ieee80211_channel *chan = sme->channel;
1764 struct brcmf_join_params join_params;
1765 size_t join_params_size;
1766 struct brcmf_ssid ssid;
1767
1768 s32 err = 0;
1769
1770 WL_TRACE("Enter\n");
1771 if (!check_sys_up(wiphy))
1772 return -EIO;
1773
1774 if (!sme->ssid) {
1775 WL_ERR("Invalid ssid\n");
1776 return -EOPNOTSUPP;
1777 }
1778
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001779 set_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001780
1781 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001782 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001783 ieee80211_frequency_to_channel(chan->center_freq);
1784 WL_CONN("channel (%d), center_req (%d)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001785 cfg->channel, chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001786 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001787 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001788
1789 WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
1790
1791 err = brcmf_set_wpa_version(ndev, sme);
1792 if (err) {
1793 WL_ERR("wl_set_wpa_version failed (%d)\n", err);
1794 goto done;
1795 }
1796
1797 err = brcmf_set_auth_type(ndev, sme);
1798 if (err) {
1799 WL_ERR("wl_set_auth_type failed (%d)\n", err);
1800 goto done;
1801 }
1802
1803 err = brcmf_set_set_cipher(ndev, sme);
1804 if (err) {
1805 WL_ERR("wl_set_set_cipher failed (%d)\n", err);
1806 goto done;
1807 }
1808
1809 err = brcmf_set_key_mgmt(ndev, sme);
1810 if (err) {
1811 WL_ERR("wl_set_key_mgmt failed (%d)\n", err);
1812 goto done;
1813 }
1814
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001815 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001816 if (err) {
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001817 WL_ERR("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001818 goto done;
1819 }
1820
1821 memset(&join_params, 0, sizeof(join_params));
1822 join_params_size = sizeof(join_params.ssid_le);
1823
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001824 profile->ssid.SSID_len = min_t(u32,
1825 sizeof(ssid.SSID), (u32)sme->ssid_len);
1826 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
1827 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1828 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001829
1830 memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
1831
1832 if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
1833 WL_CONN("ssid \"%s\", len (%d)\n",
1834 ssid.SSID, ssid.SSID_len);
1835
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001836 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001837 &join_params, &join_params_size);
1838 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID,
1839 &join_params, join_params_size);
1840 if (err)
1841 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1842
1843done:
1844 if (err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001845 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001846 WL_TRACE("Exit\n");
1847 return err;
1848}
1849
1850static s32
1851brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1852 u16 reason_code)
1853{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001854 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001855 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001856 struct brcmf_scb_val_le scbval;
1857 s32 err = 0;
1858
1859 WL_TRACE("Enter. Reason code = %d\n", reason_code);
1860 if (!check_sys_up(wiphy))
1861 return -EIO;
1862
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001863 clear_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001864
Arend van Spriel06bb1232012-09-27 14:17:56 +02001865 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001866 scbval.val = cpu_to_le32(reason_code);
1867 err = brcmf_exec_dcmd(ndev, BRCMF_C_DISASSOC, &scbval,
1868 sizeof(struct brcmf_scb_val_le));
1869 if (err)
1870 WL_ERR("error (%d)\n", err);
1871
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001872 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001873
1874 WL_TRACE("Exit\n");
1875 return err;
1876}
1877
1878static s32
1879brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001880 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001881{
1882
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001883 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1884 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001885 u16 txpwrmw;
1886 s32 err = 0;
1887 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001888 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001889
1890 WL_TRACE("Enter\n");
1891 if (!check_sys_up(wiphy))
1892 return -EIO;
1893
1894 switch (type) {
1895 case NL80211_TX_POWER_AUTOMATIC:
1896 break;
1897 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001898 case NL80211_TX_POWER_FIXED:
1899 if (dbm < 0) {
1900 WL_ERR("TX_POWER_FIXED - dbm is negative\n");
1901 err = -EINVAL;
1902 goto done;
1903 }
1904 break;
1905 }
1906 /* Make sure radio is off or on as far as software is concerned */
1907 disable = WL_RADIO_SW_DISABLE << 16;
1908 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_RADIO, &disable);
1909 if (err)
1910 WL_ERR("WLC_SET_RADIO error (%d)\n", err);
1911
1912 if (dbm > 0xffff)
1913 txpwrmw = 0xffff;
1914 else
1915 txpwrmw = (u16) dbm;
1916 err = brcmf_dev_intvar_set(ndev, "qtxpower",
Alwin Beukersef6ac172011-10-12 20:51:26 +02001917 (s32) (brcmf_mw_to_qdbm(txpwrmw)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001918 if (err)
1919 WL_ERR("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001920 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001921
1922done:
1923 WL_TRACE("Exit\n");
1924 return err;
1925}
1926
1927static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
1928{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001929 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1930 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001931 s32 txpwrdbm;
1932 u8 result;
1933 s32 err = 0;
1934
1935 WL_TRACE("Enter\n");
1936 if (!check_sys_up(wiphy))
1937 return -EIO;
1938
1939 err = brcmf_dev_intvar_get(ndev, "qtxpower", &txpwrdbm);
1940 if (err) {
1941 WL_ERR("error (%d)\n", err);
1942 goto done;
1943 }
1944
1945 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001946 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001947
1948done:
1949 WL_TRACE("Exit\n");
1950 return err;
1951}
1952
1953static s32
1954brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1955 u8 key_idx, bool unicast, bool multicast)
1956{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001957 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001958 u32 index;
1959 u32 wsec;
1960 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001961 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001962
1963 WL_TRACE("Enter\n");
1964 WL_CONN("key index (%d)\n", key_idx);
1965 if (!check_sys_up(wiphy))
1966 return -EIO;
1967
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001968 bssidx = brcmf_find_bssidx(cfg, ndev);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001969 err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001970 if (err) {
1971 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
1972 goto done;
1973 }
1974
1975 if (wsec & WEP_ENABLED) {
1976 /* Just select a new current key */
1977 index = key_idx;
1978 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_KEY_PRIMARY,
1979 &index);
1980 if (err)
1981 WL_ERR("error (%d)\n", err);
1982 }
1983done:
1984 WL_TRACE("Exit\n");
1985 return err;
1986}
1987
1988static s32
1989brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
1990 u8 key_idx, const u8 *mac_addr, struct key_params *params)
1991{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001992 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001993 struct brcmf_wsec_key key;
1994 struct brcmf_wsec_key_le key_le;
1995 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001996 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001997
1998 memset(&key, 0, sizeof(key));
1999 key.index = (u32) key_idx;
2000 /* Instead of bcast for ea address for default wep keys,
2001 driver needs it to be Null */
2002 if (!is_multicast_ether_addr(mac_addr))
2003 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2004 key.len = (u32) params->key_len;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002005 bssidx = brcmf_find_bssidx(cfg, ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002006 /* check for key index change */
2007 if (key.len == 0) {
2008 /* key delete */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002009 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002010 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002011 WL_ERR("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002012 } else {
2013 if (key.len > sizeof(key.data)) {
2014 WL_ERR("Invalid key length (%d)\n", key.len);
2015 return -EINVAL;
2016 }
2017
2018 WL_CONN("Setting the key index %d\n", key.index);
2019 memcpy(key.data, params->key, key.len);
2020
2021 if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
2022 u8 keybuf[8];
2023 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2024 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2025 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2026 }
2027
2028 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2029 if (params->seq && params->seq_len == 6) {
2030 /* rx iv */
2031 u8 *ivptr;
2032 ivptr = (u8 *) params->seq;
2033 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2034 (ivptr[3] << 8) | ivptr[2];
2035 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2036 key.iv_initialized = true;
2037 }
2038
2039 switch (params->cipher) {
2040 case WLAN_CIPHER_SUITE_WEP40:
2041 key.algo = CRYPTO_ALGO_WEP1;
2042 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
2043 break;
2044 case WLAN_CIPHER_SUITE_WEP104:
2045 key.algo = CRYPTO_ALGO_WEP128;
2046 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
2047 break;
2048 case WLAN_CIPHER_SUITE_TKIP:
2049 key.algo = CRYPTO_ALGO_TKIP;
2050 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
2051 break;
2052 case WLAN_CIPHER_SUITE_AES_CMAC:
2053 key.algo = CRYPTO_ALGO_AES_CCM;
2054 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
2055 break;
2056 case WLAN_CIPHER_SUITE_CCMP:
2057 key.algo = CRYPTO_ALGO_AES_CCM;
2058 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
2059 break;
2060 default:
2061 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
2062 return -EINVAL;
2063 }
2064 convert_key_from_CPU(&key, &key_le);
2065
2066 brcmf_netdev_wait_pend8021x(ndev);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002067 err = brcmf_dev_iovar_setbuf_bsscfg(ndev, "wsec_key", &key_le,
2068 sizeof(key_le),
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002069 cfg->extra_buf,
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002070 WL_EXTRA_BUF_MAX, bssidx);
2071 if (err)
2072 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002073 }
2074 return err;
2075}
2076
2077static s32
2078brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2079 u8 key_idx, bool pairwise, const u8 *mac_addr,
2080 struct key_params *params)
2081{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002082 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002083 struct brcmf_wsec_key key;
2084 s32 val;
2085 s32 wsec;
2086 s32 err = 0;
2087 u8 keybuf[8];
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002088 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002089
2090 WL_TRACE("Enter\n");
2091 WL_CONN("key index (%d)\n", key_idx);
2092 if (!check_sys_up(wiphy))
2093 return -EIO;
2094
2095 if (mac_addr) {
2096 WL_TRACE("Exit");
2097 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2098 }
2099 memset(&key, 0, sizeof(key));
2100
2101 key.len = (u32) params->key_len;
2102 key.index = (u32) key_idx;
2103
2104 if (key.len > sizeof(key.data)) {
2105 WL_ERR("Too long key length (%u)\n", key.len);
2106 err = -EINVAL;
2107 goto done;
2108 }
2109 memcpy(key.data, params->key, key.len);
2110
2111 key.flags = BRCMF_PRIMARY_KEY;
2112 switch (params->cipher) {
2113 case WLAN_CIPHER_SUITE_WEP40:
2114 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002115 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002116 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
2117 break;
2118 case WLAN_CIPHER_SUITE_WEP104:
2119 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002120 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002121 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
2122 break;
2123 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002124 if (cfg->conf->mode != WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002125 WL_CONN("Swapping key\n");
2126 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2127 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2128 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2129 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002130 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002131 val = TKIP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002132 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
2133 break;
2134 case WLAN_CIPHER_SUITE_AES_CMAC:
2135 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002136 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002137 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
2138 break;
2139 case WLAN_CIPHER_SUITE_CCMP:
2140 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002141 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002142 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
2143 break;
2144 default:
2145 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
2146 err = -EINVAL;
2147 goto done;
2148 }
2149
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002150 bssidx = brcmf_find_bssidx(cfg, ndev);
2151 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002152 if (err)
2153 goto done;
2154
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002155 err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002156 if (err) {
2157 WL_ERR("get wsec error (%d)\n", err);
2158 goto done;
2159 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002160 wsec |= val;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002161 err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002162 if (err) {
2163 WL_ERR("set wsec error (%d)\n", err);
2164 goto done;
2165 }
2166
Arend van Spriel5b435de2011-10-05 13:19:03 +02002167done:
2168 WL_TRACE("Exit\n");
2169 return err;
2170}
2171
2172static s32
2173brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2174 u8 key_idx, bool pairwise, const u8 *mac_addr)
2175{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002176 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002177 struct brcmf_wsec_key key;
2178 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002179 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002180
2181 WL_TRACE("Enter\n");
2182 if (!check_sys_up(wiphy))
2183 return -EIO;
2184
2185 memset(&key, 0, sizeof(key));
2186
2187 key.index = (u32) key_idx;
2188 key.flags = BRCMF_PRIMARY_KEY;
2189 key.algo = CRYPTO_ALGO_OFF;
2190
2191 WL_CONN("key index (%d)\n", key_idx);
2192
2193 /* Set the new key/index */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002194 bssidx = brcmf_find_bssidx(cfg, ndev);
2195 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002196 if (err) {
2197 if (err == -EINVAL) {
2198 if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2199 /* we ignore this key index in this case */
2200 WL_ERR("invalid key index (%d)\n", key_idx);
2201 }
2202 /* Ignore this error, may happen during DISASSOC */
2203 err = -EAGAIN;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002204 }
2205
Arend van Spriel5b435de2011-10-05 13:19:03 +02002206 WL_TRACE("Exit\n");
2207 return err;
2208}
2209
2210static s32
2211brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2212 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2213 void (*callback) (void *cookie, struct key_params * params))
2214{
2215 struct key_params params;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002216 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02002217 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002218 struct brcmf_cfg80211_security *sec;
2219 s32 wsec;
2220 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002221 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002222
2223 WL_TRACE("Enter\n");
2224 WL_CONN("key index (%d)\n", key_idx);
2225 if (!check_sys_up(wiphy))
2226 return -EIO;
2227
2228 memset(&params, 0, sizeof(params));
2229
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002230 bssidx = brcmf_find_bssidx(cfg, ndev);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002231 err = brcmf_dev_intvar_get_bsscfg(ndev, "wsec", &wsec, bssidx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002232 if (err) {
2233 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
2234 /* Ignore this error, may happen during DISASSOC */
2235 err = -EAGAIN;
2236 goto done;
2237 }
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002238 switch (wsec & ~SES_OW_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002239 case WEP_ENABLED:
Arend van Spriel06bb1232012-09-27 14:17:56 +02002240 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002241 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2242 params.cipher = WLAN_CIPHER_SUITE_WEP40;
2243 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
2244 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2245 params.cipher = WLAN_CIPHER_SUITE_WEP104;
2246 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
2247 }
2248 break;
2249 case TKIP_ENABLED:
2250 params.cipher = WLAN_CIPHER_SUITE_TKIP;
2251 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
2252 break;
2253 case AES_ENABLED:
2254 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
2255 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
2256 break;
2257 default:
2258 WL_ERR("Invalid algo (0x%x)\n", wsec);
2259 err = -EINVAL;
2260 goto done;
2261 }
2262 callback(cookie, &params);
2263
2264done:
2265 WL_TRACE("Exit\n");
2266 return err;
2267}
2268
2269static s32
2270brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2271 struct net_device *ndev, u8 key_idx)
2272{
2273 WL_INFO("Not supported\n");
2274
2275 return -EOPNOTSUPP;
2276}
2277
2278static s32
2279brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02002280 u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002281{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002282 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02002283 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002284 struct brcmf_scb_val_le scb_val;
2285 int rssi;
2286 s32 rate;
2287 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02002288 u8 *bssid = profile->bssid;
Hante Meuleman1a873342012-09-27 14:17:54 +02002289 struct brcmf_sta_info_le *sta_info_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002290
Hante Meuleman1a873342012-09-27 14:17:54 +02002291 WL_TRACE("Enter, MAC %pM\n", mac);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002292 if (!check_sys_up(wiphy))
2293 return -EIO;
2294
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002295 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002296 err = brcmf_dev_iovar_getbuf(ndev, "sta_info", mac, ETH_ALEN,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002297 cfg->dcmd_buf,
Hante Meuleman1a873342012-09-27 14:17:54 +02002298 WL_DCMD_LEN_MAX);
2299 if (err < 0) {
2300 WL_ERR("GET STA INFO failed, %d\n", err);
2301 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002302 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002303 sta_info_le = (struct brcmf_sta_info_le *)cfg->dcmd_buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002304
Hante Meuleman1a873342012-09-27 14:17:54 +02002305 sinfo->filled = STATION_INFO_INACTIVE_TIME;
2306 sinfo->inactive_time = le32_to_cpu(sta_info_le->idle) * 1000;
2307 if (le32_to_cpu(sta_info_le->flags) & BRCMF_STA_ASSOC) {
2308 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
2309 sinfo->connected_time = le32_to_cpu(sta_info_le->in);
2310 }
2311 WL_TRACE("STA idle time : %d ms, connected time :%d sec\n",
2312 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002313 } else if (cfg->conf->mode == WL_MODE_BSS) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002314 if (memcmp(mac, bssid, ETH_ALEN)) {
2315 WL_ERR("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
2316 mac, bssid);
2317 err = -ENOENT;
2318 goto done;
2319 }
2320 /* Report the current tx rate */
2321 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_GET_RATE, &rate);
2322 if (err) {
2323 WL_ERR("Could not get rate (%d)\n", err);
2324 goto done;
2325 } else {
2326 sinfo->filled |= STATION_INFO_TX_BITRATE;
2327 sinfo->txrate.legacy = rate * 5;
2328 WL_CONN("Rate %d Mbps\n", rate / 2);
2329 }
2330
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002331 if (test_bit(WL_STATUS_CONNECTED, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002332 memset(&scb_val, 0, sizeof(scb_val));
2333 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_RSSI, &scb_val,
2334 sizeof(scb_val));
2335 if (err) {
2336 WL_ERR("Could not get rssi (%d)\n", err);
2337 goto done;
2338 } else {
2339 rssi = le32_to_cpu(scb_val.val);
2340 sinfo->filled |= STATION_INFO_SIGNAL;
2341 sinfo->signal = rssi;
2342 WL_CONN("RSSI %d dBm\n", rssi);
2343 }
2344 }
2345 } else
2346 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002347done:
2348 WL_TRACE("Exit\n");
2349 return err;
2350}
2351
2352static s32
2353brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2354 bool enabled, s32 timeout)
2355{
2356 s32 pm;
2357 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002358 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002359
2360 WL_TRACE("Enter\n");
2361
2362 /*
2363 * Powersave enable/disable request is coming from the
2364 * cfg80211 even before the interface is up. In that
2365 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002366 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002367 * FW later while initializing the dongle
2368 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002369 cfg->pwr_save = enabled;
2370 if (!test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002371
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002372 WL_INFO("Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002373 goto done;
2374 }
2375
2376 pm = enabled ? PM_FAST : PM_OFF;
2377 WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
2378
2379 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &pm);
2380 if (err) {
2381 if (err == -ENODEV)
2382 WL_ERR("net_device is not ready yet\n");
2383 else
2384 WL_ERR("error (%d)\n", err);
2385 }
2386done:
2387 WL_TRACE("Exit\n");
2388 return err;
2389}
2390
2391static s32
2392brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
2393 const u8 *addr,
2394 const struct cfg80211_bitrate_mask *mask)
2395{
2396 struct brcm_rateset_le rateset_le;
2397 s32 rate;
2398 s32 val;
2399 s32 err_bg;
2400 s32 err_a;
2401 u32 legacy;
2402 s32 err = 0;
2403
2404 WL_TRACE("Enter\n");
2405 if (!check_sys_up(wiphy))
2406 return -EIO;
2407
2408 /* addr param is always NULL. ignore it */
2409 /* Get current rateset */
2410 err = brcmf_exec_dcmd(ndev, BRCM_GET_CURR_RATESET, &rateset_le,
2411 sizeof(rateset_le));
2412 if (err) {
2413 WL_ERR("could not get current rateset (%d)\n", err);
2414 goto done;
2415 }
2416
2417 legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
2418 if (!legacy)
2419 legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
2420 0xFFFF);
2421
2422 val = wl_g_rates[legacy - 1].bitrate * 100000;
2423
2424 if (val < le32_to_cpu(rateset_le.count))
2425 /* Select rate by rateset index */
2426 rate = rateset_le.rates[val] & 0x7f;
2427 else
2428 /* Specified rate in bps */
2429 rate = val / 500000;
2430
2431 WL_CONN("rate %d mbps\n", rate / 2);
2432
2433 /*
2434 *
2435 * Set rate override,
2436 * Since the is a/b/g-blind, both a/bg_rate are enforced.
2437 */
2438 err_bg = brcmf_dev_intvar_set(ndev, "bg_rate", rate);
2439 err_a = brcmf_dev_intvar_set(ndev, "a_rate", rate);
2440 if (err_bg && err_a) {
2441 WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
2442 err = err_bg | err_a;
2443 }
2444
2445done:
2446 WL_TRACE("Exit\n");
2447 return err;
2448}
2449
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002450static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002451 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002452{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002453 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002454 struct ieee80211_channel *notify_channel;
2455 struct cfg80211_bss *bss;
2456 struct ieee80211_supported_band *band;
2457 s32 err = 0;
2458 u16 channel;
2459 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002460 u16 notify_capability;
2461 u16 notify_interval;
2462 u8 *notify_ie;
2463 size_t notify_ielen;
2464 s32 notify_signal;
2465
2466 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
2467 WL_ERR("Bss info is larger than buffer. Discarding\n");
2468 return 0;
2469 }
2470
2471 channel = bi->ctl_ch ? bi->ctl_ch :
2472 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2473
2474 if (channel <= CH_MAX_2G_CHANNEL)
2475 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2476 else
2477 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2478
2479 freq = ieee80211_channel_to_frequency(channel, band->band);
2480 notify_channel = ieee80211_get_channel(wiphy, freq);
2481
Arend van Spriel5b435de2011-10-05 13:19:03 +02002482 notify_capability = le16_to_cpu(bi->capability);
2483 notify_interval = le16_to_cpu(bi->beacon_period);
2484 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2485 notify_ielen = le32_to_cpu(bi->ie_length);
2486 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2487
2488 WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
2489 bi->BSSID[0], bi->BSSID[1], bi->BSSID[2],
2490 bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]);
2491 WL_CONN("Channel: %d(%d)\n", channel, freq);
2492 WL_CONN("Capability: %X\n", notify_capability);
2493 WL_CONN("Beacon interval: %d\n", notify_interval);
2494 WL_CONN("Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002495
2496 bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002497 0, notify_capability, notify_interval, notify_ie,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002498 notify_ielen, notify_signal, GFP_KERNEL);
2499
Franky Line78946e2011-11-10 20:30:34 +01002500 if (!bss)
2501 return -ENOMEM;
2502
2503 cfg80211_put_bss(bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002504
2505 return err;
2506}
2507
Roland Vossen6f09be02011-10-18 14:03:02 +02002508static struct brcmf_bss_info_le *
2509next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2510{
2511 if (bss == NULL)
2512 return list->bss_info_le;
2513 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2514 le32_to_cpu(bss->length));
2515}
2516
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002517static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002518{
2519 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002520 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002521 s32 err = 0;
2522 int i;
2523
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002524 bss_list = cfg->bss_list;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002525 if (bss_list->version != BRCMF_BSS_INFO_VERSION) {
2526 WL_ERR("Version %d != WL_BSS_INFO_VERSION\n",
2527 bss_list->version);
2528 return -EOPNOTSUPP;
2529 }
2530 WL_SCAN("scanned AP count (%d)\n", bss_list->count);
2531 for (i = 0; i < bss_list->count && i < WL_AP_MAX; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002532 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002533 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002534 if (err)
2535 break;
2536 }
2537 return err;
2538}
2539
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002540static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002541 struct net_device *ndev, const u8 *bssid)
2542{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002543 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002544 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002545 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002546 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002547 struct cfg80211_bss *bss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002548 u8 *buf = NULL;
2549 s32 err = 0;
2550 u16 channel;
2551 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002552 u16 notify_capability;
2553 u16 notify_interval;
2554 u8 *notify_ie;
2555 size_t notify_ielen;
2556 s32 notify_signal;
2557
2558 WL_TRACE("Enter\n");
2559
2560 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2561 if (buf == NULL) {
2562 err = -ENOMEM;
2563 goto CleanUp;
2564 }
2565
2566 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2567
2568 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX);
2569 if (err) {
2570 WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
2571 goto CleanUp;
2572 }
2573
Roland Vossend34bf642011-10-18 14:03:01 +02002574 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002575
2576 channel = bi->ctl_ch ? bi->ctl_ch :
2577 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2578
2579 if (channel <= CH_MAX_2G_CHANNEL)
2580 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2581 else
2582 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2583
2584 freq = ieee80211_channel_to_frequency(channel, band->band);
2585 notify_channel = ieee80211_get_channel(wiphy, freq);
2586
Arend van Spriel5b435de2011-10-05 13:19:03 +02002587 notify_capability = le16_to_cpu(bi->capability);
2588 notify_interval = le16_to_cpu(bi->beacon_period);
2589 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2590 notify_ielen = le32_to_cpu(bi->ie_length);
2591 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2592
2593 WL_CONN("channel: %d(%d)\n", channel, freq);
2594 WL_CONN("capability: %X\n", notify_capability);
2595 WL_CONN("beacon interval: %d\n", notify_interval);
2596 WL_CONN("signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002597
Franky Line78946e2011-11-10 20:30:34 +01002598 bss = cfg80211_inform_bss(wiphy, notify_channel, bssid,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002599 0, notify_capability, notify_interval,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002600 notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
2601
Franky Line78946e2011-11-10 20:30:34 +01002602 if (!bss) {
2603 err = -ENOMEM;
2604 goto CleanUp;
2605 }
2606
2607 cfg80211_put_bss(bss);
2608
Arend van Spriel5b435de2011-10-05 13:19:03 +02002609CleanUp:
2610
2611 kfree(buf);
2612
2613 WL_TRACE("Exit\n");
2614
2615 return err;
2616}
2617
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002618static bool brcmf_is_ibssmode(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002619{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002620 return cfg->conf->mode == WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002621}
2622
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002623/*
2624 * Traverse a string of 1-byte tag/1-byte length/variable-length value
2625 * triples, returning a pointer to the substring whose first element
2626 * matches tag
2627 */
2628static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
2629{
2630 struct brcmf_tlv *elt;
2631 int totlen;
2632
2633 elt = (struct brcmf_tlv *) buf;
2634 totlen = buflen;
2635
2636 /* find tagged parameter */
Hante Meuleman04012892012-09-27 14:17:49 +02002637 while (totlen >= TLV_HDR_LEN) {
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002638 int len = elt->len;
2639
2640 /* validate remaining totlen */
Hante Meuleman04012892012-09-27 14:17:49 +02002641 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002642 return elt;
2643
Hante Meuleman04012892012-09-27 14:17:49 +02002644 elt = (struct brcmf_tlv *) ((u8 *) elt + (len + TLV_HDR_LEN));
2645 totlen -= (len + TLV_HDR_LEN);
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002646 }
2647
2648 return NULL;
2649}
2650
Hante Meuleman1a873342012-09-27 14:17:54 +02002651/* Is any of the tlvs the expected entry? If
2652 * not update the tlvs buffer pointer/length.
2653 */
2654static bool
2655brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
2656 u8 *oui, u32 oui_len, u8 type)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002657{
Hante Meuleman1a873342012-09-27 14:17:54 +02002658 /* If the contents match the OUI and the type */
2659 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
2660 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
2661 type == ie[TLV_BODY_OFF + oui_len]) {
2662 return true;
2663 }
2664
2665 if (tlvs == NULL)
2666 return false;
2667 /* point to the next ie */
2668 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
2669 /* calculate the length of the rest of the buffer */
2670 *tlvs_len -= (int)(ie - *tlvs);
2671 /* update the pointer to the start of the buffer */
2672 *tlvs = ie;
2673
2674 return false;
2675}
2676
Franky Lin3cb91f52012-10-10 11:13:08 -07002677static struct brcmf_vs_tlv *
Hante Meuleman1a873342012-09-27 14:17:54 +02002678brcmf_find_wpaie(u8 *parse, u32 len)
2679{
2680 struct brcmf_tlv *ie;
2681
Arend van Spriel04b23122012-10-12 12:28:14 +02002682 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002683 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
2684 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
2685 return (struct brcmf_vs_tlv *)ie;
2686 }
2687 return NULL;
2688}
2689
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002690static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002691{
Arend van Spriel06bb1232012-09-27 14:17:56 +02002692 struct brcmf_cfg80211_profile *profile = cfg->profile;
Roland Vossend34bf642011-10-18 14:03:01 +02002693 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002694 struct brcmf_ssid *ssid;
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002695 struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002696 u16 beacon_interval;
2697 u8 dtim_period;
2698 size_t ie_len;
2699 u8 *ie;
2700 s32 err = 0;
2701
2702 WL_TRACE("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002703 if (brcmf_is_ibssmode(cfg))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002704 return err;
2705
Arend van Spriel06bb1232012-09-27 14:17:56 +02002706 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002707
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002708 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
2709 err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCMF_C_GET_BSS_INFO,
2710 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002711 if (err) {
2712 WL_ERR("Could not get bss info %d\n", err);
2713 goto update_bss_info_out;
2714 }
2715
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002716 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2717 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002718 if (err)
2719 goto update_bss_info_out;
2720
2721 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2722 ie_len = le32_to_cpu(bi->ie_length);
2723 beacon_interval = le16_to_cpu(bi->beacon_period);
2724
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002725 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002726 if (tim)
2727 dtim_period = tim->data[1];
2728 else {
2729 /*
2730 * active scan was done so we could not get dtim
2731 * information out of probe response.
2732 * so we speficially query dtim information to dongle.
2733 */
2734 u32 var;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002735 err = brcmf_dev_intvar_get(cfg_to_ndev(cfg),
Arend van Spriel5b435de2011-10-05 13:19:03 +02002736 "dtim_assoc", &var);
2737 if (err) {
2738 WL_ERR("wl dtim_assoc failed (%d)\n", err);
2739 goto update_bss_info_out;
2740 }
2741 dtim_period = (u8)var;
2742 }
2743
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02002744 profile->beacon_interval = beacon_interval;
2745 profile->dtim_period = dtim_period;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002746
2747update_bss_info_out:
2748 WL_TRACE("Exit");
2749 return err;
2750}
2751
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002752static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002753{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002754 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
2755 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002756 struct brcmf_ssid ssid;
2757
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002758 set_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
2759 if (cfg->iscan_on) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002760 iscan->state = WL_ISCAN_STATE_IDLE;
2761
2762 if (iscan->timer_on) {
2763 del_timer_sync(&iscan->timer);
2764 iscan->timer_on = 0;
2765 }
2766
2767 cancel_work_sync(&iscan->work);
2768
2769 /* Abort iscan running in FW */
2770 memset(&ssid, 0, sizeof(ssid));
2771 brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002772
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002773 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002774 /* Indidate scan abort to cfg80211 layer */
2775 WL_INFO("Terminating scan in progress\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002776 cfg80211_scan_done(cfg->scan_request, true);
2777 cfg->scan_request = NULL;
Arend van Spriel108a4be2012-09-19 22:21:07 +02002778 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002779 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002780 if (cfg->escan_on && cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002781 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002782 brcmf_notify_escan_complete(cfg, escan->ndev, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002783 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002784 clear_bit(WL_STATUS_SCANNING, &cfg->status);
2785 clear_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002786}
2787
2788static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
2789 bool aborted)
2790{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002791 struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan);
2792 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002793
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002794 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002795 WL_ERR("Scan complete while device not scanning\n");
2796 return;
2797 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002798 if (cfg->scan_request) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002799 WL_SCAN("ISCAN Completed scan: %s\n",
2800 aborted ? "Aborted" : "Done");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002801 cfg80211_scan_done(cfg->scan_request, aborted);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002802 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002803 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002804 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002805 cfg->iscan_kickstart = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002806}
2807
2808static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan)
2809{
2810 if (iscan->state != WL_ISCAN_STATE_IDLE) {
2811 WL_SCAN("wake up iscan\n");
2812 schedule_work(&iscan->work);
2813 return 0;
2814 }
2815
2816 return -EIO;
2817}
2818
2819static s32
2820brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status,
2821 struct brcmf_scan_results **bss_list)
2822{
2823 struct brcmf_iscan_results list;
2824 struct brcmf_scan_results *results;
2825 struct brcmf_scan_results_le *results_le;
2826 struct brcmf_iscan_results *list_buf;
2827 s32 err = 0;
2828
2829 memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX);
2830 list_buf = (struct brcmf_iscan_results *)iscan->scan_buf;
2831 results = &list_buf->results;
2832 results_le = &list_buf->results_le;
2833 results->buflen = BRCMF_ISCAN_RESULTS_FIXED_SIZE;
2834 results->version = 0;
2835 results->count = 0;
2836
2837 memset(&list, 0, sizeof(list));
2838 list.results_le.buflen = cpu_to_le32(WL_ISCAN_BUF_MAX);
2839 err = brcmf_dev_iovar_getbuf(iscan->ndev, "iscanresults", &list,
2840 BRCMF_ISCAN_RESULTS_FIXED_SIZE,
2841 iscan->scan_buf, WL_ISCAN_BUF_MAX);
2842 if (err) {
2843 WL_ERR("error (%d)\n", err);
2844 return err;
2845 }
2846 results->buflen = le32_to_cpu(results_le->buflen);
2847 results->version = le32_to_cpu(results_le->version);
2848 results->count = le32_to_cpu(results_le->count);
2849 WL_SCAN("results->count = %d\n", results_le->count);
2850 WL_SCAN("results->buflen = %d\n", results_le->buflen);
2851 *status = le32_to_cpu(list_buf->status_le);
2852 WL_SCAN("status = %d\n", *status);
2853 *bss_list = results;
2854
2855 return err;
2856}
2857
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002858static s32 brcmf_iscan_done(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002859{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002860 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002861 s32 err = 0;
2862
2863 iscan->state = WL_ISCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002864 brcmf_inform_bss(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002865 brcmf_notify_iscan_complete(iscan, false);
2866
2867 return err;
2868}
2869
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002870static s32 brcmf_iscan_pending(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002871{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002872 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002873 s32 err = 0;
2874
2875 /* Reschedule the timer */
2876 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
2877 iscan->timer_on = 1;
2878
2879 return err;
2880}
2881
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002882static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002883{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002884 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002885 s32 err = 0;
2886
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002887 brcmf_inform_bss(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002888 brcmf_run_iscan(iscan, NULL, BRCMF_SCAN_ACTION_CONTINUE);
2889 /* Reschedule the timer */
2890 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
2891 iscan->timer_on = 1;
2892
2893 return err;
2894}
2895
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002896static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002897{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002898 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002899 s32 err = 0;
2900
2901 iscan->state = WL_ISCAN_STATE_IDLE;
2902 brcmf_notify_iscan_complete(iscan, true);
2903
2904 return err;
2905}
2906
2907static void brcmf_cfg80211_iscan_handler(struct work_struct *work)
2908{
2909 struct brcmf_cfg80211_iscan_ctrl *iscan =
2910 container_of(work, struct brcmf_cfg80211_iscan_ctrl,
2911 work);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002912 struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002913 struct brcmf_cfg80211_iscan_eloop *el = &iscan->el;
2914 u32 status = BRCMF_SCAN_RESULTS_PARTIAL;
2915
2916 if (iscan->timer_on) {
2917 del_timer_sync(&iscan->timer);
2918 iscan->timer_on = 0;
2919 }
2920
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002921 if (brcmf_get_iscan_results(iscan, &status, &cfg->bss_list)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002922 status = BRCMF_SCAN_RESULTS_ABORTED;
2923 WL_ERR("Abort iscan\n");
2924 }
2925
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002926 el->handler[status](cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002927}
2928
2929static void brcmf_iscan_timer(unsigned long data)
2930{
2931 struct brcmf_cfg80211_iscan_ctrl *iscan =
2932 (struct brcmf_cfg80211_iscan_ctrl *)data;
2933
2934 if (iscan) {
2935 iscan->timer_on = 0;
2936 WL_SCAN("timer expired\n");
2937 brcmf_wakeup_iscan(iscan);
2938 }
2939}
2940
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002941static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002942{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002943 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002944
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002945 if (cfg->iscan_on) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002946 iscan->state = WL_ISCAN_STATE_IDLE;
2947 INIT_WORK(&iscan->work, brcmf_cfg80211_iscan_handler);
2948 }
2949
2950 return 0;
2951}
2952
2953static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el)
2954{
2955 memset(el, 0, sizeof(*el));
2956 el->handler[BRCMF_SCAN_RESULTS_SUCCESS] = brcmf_iscan_done;
2957 el->handler[BRCMF_SCAN_RESULTS_PARTIAL] = brcmf_iscan_inprogress;
2958 el->handler[BRCMF_SCAN_RESULTS_PENDING] = brcmf_iscan_pending;
2959 el->handler[BRCMF_SCAN_RESULTS_ABORTED] = brcmf_iscan_aborted;
2960 el->handler[BRCMF_SCAN_RESULTS_NO_MEM] = brcmf_iscan_aborted;
2961}
2962
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002963static s32 brcmf_init_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002964{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002965 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002966 int err = 0;
2967
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002968 if (cfg->iscan_on) {
2969 iscan->ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002970 brcmf_init_iscan_eloop(&iscan->el);
2971 iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS;
2972 init_timer(&iscan->timer);
2973 iscan->timer.data = (unsigned long) iscan;
2974 iscan->timer.function = brcmf_iscan_timer;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002975 err = brcmf_invoke_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002976 if (!err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002977 iscan->data = cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002978 }
2979
2980 return err;
2981}
2982
Hante Meulemane756af52012-09-11 21:18:52 +02002983static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2984{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002985 struct brcmf_cfg80211_info *cfg =
2986 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002987 escan_timeout_work);
2988
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002989 brcmf_notify_escan_complete(cfg,
2990 cfg->escan_info.ndev, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002991}
2992
2993static void brcmf_escan_timeout(unsigned long data)
2994{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002995 struct brcmf_cfg80211_info *cfg =
2996 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002997
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002998 if (cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02002999 WL_ERR("timer expired\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003000 if (cfg->escan_on)
3001 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02003002 }
3003}
3004
3005static s32
3006brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
3007 struct brcmf_bss_info_le *bss_info_le)
3008{
3009 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
3010 (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
3011 CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
3012 bss_info_le->SSID_len == bss->SSID_len &&
3013 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
3014 if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
3015 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02003016 s16 bss_rssi = le16_to_cpu(bss->RSSI);
3017 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
3018
Hante Meulemane756af52012-09-11 21:18:52 +02003019 /* preserve max RSSI if the measurements are
3020 * both on-channel or both off-channel
3021 */
Arend van Spriel029591f2012-09-19 22:21:06 +02003022 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02003023 bss->RSSI = bss_info_le->RSSI;
3024 } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
3025 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
3026 /* preserve the on-channel rssi measurement
3027 * if the new measurement is off channel
3028 */
3029 bss->RSSI = bss_info_le->RSSI;
3030 bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
3031 }
3032 return 1;
3033 }
3034 return 0;
3035}
3036
3037static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003038brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +02003039 struct net_device *ndev,
3040 const struct brcmf_event_msg *e, void *data)
3041{
3042 s32 status;
3043 s32 err = 0;
3044 struct brcmf_escan_result_le *escan_result_le;
3045 struct brcmf_bss_info_le *bss_info_le;
3046 struct brcmf_bss_info_le *bss = NULL;
3047 u32 bi_length;
3048 struct brcmf_scan_results *list;
3049 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003050 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02003051
3052 status = be32_to_cpu(e->status);
3053
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003054 if (!ndev || !cfg->escan_on ||
3055 !test_bit(WL_STATUS_SCANNING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02003056 WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003057 ndev, cfg->escan_on,
3058 !test_bit(WL_STATUS_SCANNING, &cfg->status));
Hante Meulemane756af52012-09-11 21:18:52 +02003059 return -EPERM;
3060 }
3061
3062 if (status == BRCMF_E_STATUS_PARTIAL) {
3063 WL_SCAN("ESCAN Partial result\n");
3064 escan_result_le = (struct brcmf_escan_result_le *) data;
3065 if (!escan_result_le) {
3066 WL_ERR("Invalid escan result (NULL pointer)\n");
3067 goto exit;
3068 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003069 if (!cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02003070 WL_SCAN("result without cfg80211 request\n");
3071 goto exit;
3072 }
3073
3074 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
3075 WL_ERR("Invalid bss_count %d: ignoring\n",
3076 escan_result_le->bss_count);
3077 goto exit;
3078 }
3079 bss_info_le = &escan_result_le->bss_info_le;
3080
3081 bi_length = le32_to_cpu(bss_info_le->length);
3082 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
3083 WL_ESCAN_RESULTS_FIXED_SIZE)) {
3084 WL_ERR("Invalid bss_info length %d: ignoring\n",
3085 bi_length);
3086 goto exit;
3087 }
3088
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003089 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02003090 BIT(NL80211_IFTYPE_ADHOC))) {
3091 if (le16_to_cpu(bss_info_le->capability) &
3092 WLAN_CAPABILITY_IBSS) {
3093 WL_ERR("Ignoring IBSS result\n");
3094 goto exit;
3095 }
3096 }
3097
3098 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003099 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02003100 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
3101 WL_ERR("Buffer is too small: ignoring\n");
3102 goto exit;
3103 }
3104
3105 for (i = 0; i < list->count; i++) {
3106 bss = bss ? (struct brcmf_bss_info_le *)
3107 ((unsigned char *)bss +
3108 le32_to_cpu(bss->length)) : list->bss_info_le;
3109 if (brcmf_compare_update_same_bss(bss, bss_info_le))
3110 goto exit;
3111 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003112 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02003113 bss_info_le, bi_length);
3114 list->version = le32_to_cpu(bss_info_le->version);
3115 list->buflen += bi_length;
3116 list->count++;
3117 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003118 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3119 if (cfg->scan_request) {
3120 cfg->bss_list = (struct brcmf_scan_results *)
3121 cfg->escan_info.escan_buf;
3122 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003123 aborted = status != BRCMF_E_STATUS_SUCCESS;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003124 brcmf_notify_escan_complete(cfg, ndev, aborted,
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003125 false);
Hante Meulemane756af52012-09-11 21:18:52 +02003126 } else
3127 WL_ERR("Unexpected scan result 0x%x\n", status);
3128 }
3129exit:
3130 return err;
3131}
3132
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003133static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003134{
3135
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003136 if (cfg->escan_on) {
3137 cfg->el.handler[BRCMF_E_ESCAN_RESULT] =
Hante Meulemane756af52012-09-11 21:18:52 +02003138 brcmf_cfg80211_escan_handler;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003139 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meulemane756af52012-09-11 21:18:52 +02003140 /* Init scan_timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003141 init_timer(&cfg->escan_timeout);
3142 cfg->escan_timeout.data = (unsigned long) cfg;
3143 cfg->escan_timeout.function = brcmf_escan_timeout;
3144 INIT_WORK(&cfg->escan_timeout_work,
Hante Meulemane756af52012-09-11 21:18:52 +02003145 brcmf_cfg80211_escan_timeout_worker);
3146 }
3147}
3148
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003149static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003150{
3151 if (ms < 1000 / HZ) {
3152 cond_resched();
3153 mdelay(ms);
3154 } else {
3155 msleep(ms);
3156 }
3157}
3158
3159static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3160{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003161 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003162
3163 /*
3164 * Check for WL_STATUS_READY before any function call which
3165 * could result is bus access. Don't block the resume for
3166 * any driver error conditions
3167 */
3168 WL_TRACE("Enter\n");
3169
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003170 if (test_bit(WL_STATUS_READY, &cfg->status))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003171 brcmf_invoke_iscan(wiphy_to_cfg(wiphy));
3172
3173 WL_TRACE("Exit\n");
3174 return 0;
3175}
3176
3177static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
3178 struct cfg80211_wowlan *wow)
3179{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003180 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3181 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003182
3183 WL_TRACE("Enter\n");
3184
3185 /*
3186 * Check for WL_STATUS_READY before any function call which
3187 * could result is bus access. Don't block the suspend for
3188 * any driver error conditions
3189 */
3190
3191 /*
3192 * While going to suspend if associated with AP disassociate
3193 * from AP to save power while system is in suspended state
3194 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003195 if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
3196 test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
3197 test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02003198 WL_INFO("Disassociating from AP"
3199 " while entering suspend state\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003200 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003201
3202 /*
3203 * Make sure WPA_Supplicant receives all the event
3204 * generated due to DISASSOC call to the fw to keep
3205 * the state fw and WPA_Supplicant state consistent
3206 */
3207 brcmf_delay(500);
3208 }
3209
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003210 if (test_bit(WL_STATUS_READY, &cfg->status))
3211 brcmf_abort_scanning(cfg);
Arend van Spriel108a4be2012-09-19 22:21:07 +02003212 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003213 clear_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003214
3215 /* Turn off watchdog timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003216 if (test_bit(WL_STATUS_READY, &cfg->status))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003217 brcmf_set_mpc(ndev, 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003218
3219 WL_TRACE("Exit\n");
3220
3221 return 0;
3222}
3223
3224static __used s32
3225brcmf_dev_bufvar_set(struct net_device *ndev, s8 *name, s8 *buf, s32 len)
3226{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003227 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003228 u32 buflen;
3229
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003230 buflen = brcmf_c_mkiovar(name, buf, len, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003231 WL_DCMD_LEN_MAX);
3232 BUG_ON(!buflen);
3233
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003234 return brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003235 buflen);
3236}
3237
3238static s32
3239brcmf_dev_bufvar_get(struct net_device *ndev, s8 *name, s8 *buf,
3240 s32 buf_len)
3241{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003242 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003243 u32 len;
3244 s32 err = 0;
3245
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003246 len = brcmf_c_mkiovar(name, NULL, 0, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003247 WL_DCMD_LEN_MAX);
3248 BUG_ON(!len);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003249 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, cfg->dcmd_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003250 WL_DCMD_LEN_MAX);
3251 if (err) {
3252 WL_ERR("error (%d)\n", err);
3253 return err;
3254 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003255 memcpy(buf, cfg->dcmd_buf, buf_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003256
3257 return err;
3258}
3259
3260static __used s32
3261brcmf_update_pmklist(struct net_device *ndev,
3262 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3263{
3264 int i, j;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003265 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003266
Arend van Spriel40c8e952011-10-12 20:51:20 +02003267 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3268
3269 WL_CONN("No of elements %d\n", pmkid_len);
3270 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02003271 WL_CONN("PMKID[%d]: %pM =\n", i,
3272 &pmk_list->pmkids.pmkid[i].BSSID);
3273 for (j = 0; j < WLAN_PMKID_LEN; j++)
3274 WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]);
3275 }
3276
3277 if (!err)
3278 brcmf_dev_bufvar_set(ndev, "pmkid_info", (char *)pmk_list,
3279 sizeof(*pmk_list));
3280
3281 return err;
3282}
3283
3284static s32
3285brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3286 struct cfg80211_pmksa *pmksa)
3287{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003288 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3289 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003290 s32 err = 0;
3291 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003292 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003293
3294 WL_TRACE("Enter\n");
3295 if (!check_sys_up(wiphy))
3296 return -EIO;
3297
Arend van Spriel40c8e952011-10-12 20:51:20 +02003298 pmkid_len = le32_to_cpu(pmkids->npmkid);
3299 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003300 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3301 break;
3302 if (i < WL_NUM_PMKIDS_MAX) {
3303 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3304 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003305 if (i == pmkid_len) {
3306 pmkid_len++;
3307 pmkids->npmkid = cpu_to_le32(pmkid_len);
3308 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003309 } else
3310 err = -EINVAL;
3311
3312 WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
Arend van Spriel40c8e952011-10-12 20:51:20 +02003313 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003314 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel40c8e952011-10-12 20:51:20 +02003315 WL_CONN("%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003316
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003317 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003318
3319 WL_TRACE("Exit\n");
3320 return err;
3321}
3322
3323static s32
3324brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3325 struct cfg80211_pmksa *pmksa)
3326{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003327 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003328 struct pmkid_list pmkid;
3329 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003330 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003331
3332 WL_TRACE("Enter\n");
3333 if (!check_sys_up(wiphy))
3334 return -EIO;
3335
3336 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3337 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3338
3339 WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3340 &pmkid.pmkid[0].BSSID);
3341 for (i = 0; i < WLAN_PMKID_LEN; i++)
3342 WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]);
3343
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003344 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003345 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003346 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003347 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003348 ETH_ALEN))
3349 break;
3350
Arend van Spriel40c8e952011-10-12 20:51:20 +02003351 if ((pmkid_len > 0)
3352 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003353 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003354 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003355 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003356 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3357 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003358 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003359 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3360 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003361 WLAN_PMKID_LEN);
3362 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003363 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003364 } else
3365 err = -EINVAL;
3366
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003367 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003368
3369 WL_TRACE("Exit\n");
3370 return err;
3371
3372}
3373
3374static s32
3375brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3376{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003377 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003378 s32 err = 0;
3379
3380 WL_TRACE("Enter\n");
3381 if (!check_sys_up(wiphy))
3382 return -EIO;
3383
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003384 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3385 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003386
3387 WL_TRACE("Exit\n");
3388 return err;
3389
3390}
3391
Arend van Spriele5806072012-09-19 22:21:08 +02003392/*
3393 * PFN result doesn't have all the info which are
3394 * required by the supplicant
3395 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3396 * via wl_inform_single_bss in the required format. Escan does require the
3397 * scan request in the form of cfg80211_scan_request. For timebeing, create
3398 * cfg80211_scan_request one out of the received PNO event.
3399 */
3400static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003401brcmf_notify_sched_scan_results(struct brcmf_cfg80211_info *cfg,
Arend van Spriele5806072012-09-19 22:21:08 +02003402 struct net_device *ndev,
3403 const struct brcmf_event_msg *e, void *data)
3404{
3405 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3406 struct cfg80211_scan_request *request = NULL;
3407 struct cfg80211_ssid *ssid = NULL;
3408 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003409 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003410 int err = 0;
3411 int channel_req = 0;
3412 int band = 0;
3413 struct brcmf_pno_scanresults_le *pfn_result;
3414 u32 result_count;
3415 u32 status;
3416
3417 WL_SCAN("Enter\n");
3418
3419 if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) {
3420 WL_SCAN("PFN NET LOST event. Do Nothing\n");
3421 return 0;
3422 }
3423
3424 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3425 result_count = le32_to_cpu(pfn_result->count);
3426 status = le32_to_cpu(pfn_result->status);
3427
3428 /*
3429 * PFN event is limited to fit 512 bytes so we may get
3430 * multiple NET_FOUND events. For now place a warning here.
3431 */
3432 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3433 WL_SCAN("PFN NET FOUND event. count: %d\n", result_count);
3434 if (result_count > 0) {
3435 int i;
3436
3437 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003438 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3439 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003440 if (!request || !ssid || !channel) {
3441 err = -ENOMEM;
3442 goto out_err;
3443 }
3444
3445 request->wiphy = wiphy;
3446 data += sizeof(struct brcmf_pno_scanresults_le);
3447 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3448
3449 for (i = 0; i < result_count; i++) {
3450 netinfo = &netinfo_start[i];
3451 if (!netinfo) {
3452 WL_ERR("Invalid netinfo ptr. index: %d\n", i);
3453 err = -EINVAL;
3454 goto out_err;
3455 }
3456
3457 WL_SCAN("SSID:%s Channel:%d\n",
3458 netinfo->SSID, netinfo->channel);
3459 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3460 ssid[i].ssid_len = netinfo->SSID_len;
3461 request->n_ssids++;
3462
3463 channel_req = netinfo->channel;
3464 if (channel_req <= CH_MAX_2G_CHANNEL)
3465 band = NL80211_BAND_2GHZ;
3466 else
3467 band = NL80211_BAND_5GHZ;
3468 channel[i].center_freq =
3469 ieee80211_channel_to_frequency(channel_req,
3470 band);
3471 channel[i].band = band;
3472 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3473 request->channels[i] = &channel[i];
3474 request->n_channels++;
3475 }
3476
3477 /* assign parsed ssid array */
3478 if (request->n_ssids)
3479 request->ssids = &ssid[0];
3480
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003481 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003482 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003483 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003484 }
3485
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003486 set_bit(WL_STATUS_SCANNING, &cfg->status);
3487 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003488 if (err) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003489 clear_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriele5806072012-09-19 22:21:08 +02003490 goto out_err;
3491 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003492 cfg->sched_escan = true;
3493 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003494 } else {
3495 WL_ERR("FALSE PNO Event. (pfn_count == 0)\n");
3496 goto out_err;
3497 }
3498
3499 kfree(ssid);
3500 kfree(channel);
3501 kfree(request);
3502 return 0;
3503
3504out_err:
3505 kfree(ssid);
3506 kfree(channel);
3507 kfree(request);
3508 cfg80211_sched_scan_stopped(wiphy);
3509 return err;
3510}
3511
3512#ifndef CONFIG_BRCMISCAN
3513static int brcmf_dev_pno_clean(struct net_device *ndev)
3514{
3515 char iovbuf[128];
3516 int ret;
3517
3518 /* Disable pfn */
3519 ret = brcmf_dev_intvar_set(ndev, "pfn", 0);
3520 if (ret == 0) {
3521 /* clear pfn */
3522 ret = brcmf_dev_iovar_setbuf(ndev, "pfnclear", NULL, 0,
3523 iovbuf, sizeof(iovbuf));
3524 }
3525 if (ret < 0)
3526 WL_ERR("failed code %d\n", ret);
3527
3528 return ret;
3529}
3530
3531static int brcmf_dev_pno_config(struct net_device *ndev)
3532{
3533 struct brcmf_pno_param_le pfn_param;
3534 char iovbuf[128];
3535
3536 memset(&pfn_param, 0, sizeof(pfn_param));
3537 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3538
3539 /* set extra pno params */
3540 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3541 pfn_param.repeat = BRCMF_PNO_REPEAT;
3542 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3543
3544 /* set up pno scan fr */
3545 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3546
3547 return brcmf_dev_iovar_setbuf(ndev, "pfn_set",
3548 &pfn_param, sizeof(pfn_param),
3549 iovbuf, sizeof(iovbuf));
3550}
3551
3552static int
3553brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3554 struct net_device *ndev,
3555 struct cfg80211_sched_scan_request *request)
3556{
3557 char iovbuf[128];
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003558 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003559 struct brcmf_pno_net_param_le pfn;
3560 int i;
3561 int ret = 0;
3562
3563 WL_SCAN("Enter n_match_sets:%d n_ssids:%d\n",
3564 request->n_match_sets, request->n_ssids);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003565 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
3566 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Arend van Spriele5806072012-09-19 22:21:08 +02003567 return -EAGAIN;
3568 }
3569
3570 if (!request || !request->n_ssids || !request->n_match_sets) {
3571 WL_ERR("Invalid sched scan req!! n_ssids:%d\n",
3572 request->n_ssids);
3573 return -EINVAL;
3574 }
3575
3576 if (request->n_ssids > 0) {
3577 for (i = 0; i < request->n_ssids; i++) {
3578 /* Active scan req for ssids */
3579 WL_SCAN(">>> Active scan req for ssid (%s)\n",
3580 request->ssids[i].ssid);
3581
3582 /*
3583 * match_set ssids is a supert set of n_ssid list,
3584 * so we need not add these set seperately.
3585 */
3586 }
3587 }
3588
3589 if (request->n_match_sets > 0) {
3590 /* clean up everything */
3591 ret = brcmf_dev_pno_clean(ndev);
3592 if (ret < 0) {
3593 WL_ERR("failed error=%d\n", ret);
3594 return ret;
3595 }
3596
3597 /* configure pno */
3598 ret = brcmf_dev_pno_config(ndev);
3599 if (ret < 0) {
3600 WL_ERR("PNO setup failed!! ret=%d\n", ret);
3601 return -EINVAL;
3602 }
3603
3604 /* configure each match set */
3605 for (i = 0; i < request->n_match_sets; i++) {
3606 struct cfg80211_ssid *ssid;
3607 u32 ssid_len;
3608
3609 ssid = &request->match_sets[i].ssid;
3610 ssid_len = ssid->ssid_len;
3611
3612 if (!ssid_len) {
3613 WL_ERR("skip broadcast ssid\n");
3614 continue;
3615 }
3616 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3617 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3618 pfn.wsec = cpu_to_le32(0);
3619 pfn.infra = cpu_to_le32(1);
3620 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3621 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3622 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
3623 ret = brcmf_dev_iovar_setbuf(ndev, "pfn_add",
3624 &pfn, sizeof(pfn),
3625 iovbuf, sizeof(iovbuf));
3626 WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
3627 ret == 0 ? "set" : "failed",
3628 ssid->ssid);
3629 }
3630 /* Enable the PNO */
3631 if (brcmf_dev_intvar_set(ndev, "pfn", 1) < 0) {
3632 WL_ERR("PNO enable failed!! ret=%d\n", ret);
3633 return -EINVAL;
3634 }
3635 } else {
3636 return -EINVAL;
3637 }
3638
3639 return 0;
3640}
3641
3642static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3643 struct net_device *ndev)
3644{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003645 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003646
3647 WL_SCAN("enter\n");
3648 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003649 if (cfg->sched_escan)
3650 brcmf_notify_escan_complete(cfg, ndev, true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003651 return 0;
3652}
3653#endif /* CONFIG_BRCMISCAN */
3654
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003655#ifdef CONFIG_NL80211_TESTMODE
3656static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3657{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003658 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3659 struct net_device *ndev = cfg->wdev->netdev;
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003660 struct brcmf_dcmd *dcmd = data;
3661 struct sk_buff *reply;
3662 int ret;
3663
3664 ret = brcmf_netlink_dcmd(ndev, dcmd);
3665 if (ret == 0) {
3666 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3667 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3668 ret = cfg80211_testmode_reply(reply);
3669 }
3670 return ret;
3671}
3672#endif
3673
Hante Meuleman1a873342012-09-27 14:17:54 +02003674static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
3675{
3676 s32 err;
3677
3678 /* set auth */
3679 err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", 0, bssidx);
3680 if (err < 0) {
3681 WL_ERR("auth error %d\n", err);
3682 return err;
3683 }
3684 /* set wsec */
3685 err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", 0, bssidx);
3686 if (err < 0) {
3687 WL_ERR("wsec error %d\n", err);
3688 return err;
3689 }
3690 /* set upper-layer auth */
3691 err = brcmf_dev_intvar_set_bsscfg(ndev, "wpa_auth",
3692 WPA_AUTH_NONE, bssidx);
3693 if (err < 0) {
3694 WL_ERR("wpa_auth error %d\n", err);
3695 return err;
3696 }
3697
3698 return 0;
3699}
3700
3701static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3702{
3703 if (is_rsn_ie)
3704 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3705
3706 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3707}
3708
3709static s32
3710brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
3711 bool is_rsn_ie, s32 bssidx)
3712{
3713 u32 auth = 0; /* d11 open authentication */
3714 u16 count;
3715 s32 err = 0;
3716 s32 len = 0;
3717 u32 i;
3718 u32 wsec;
3719 u32 pval = 0;
3720 u32 gval = 0;
3721 u32 wpa_auth = 0;
3722 u32 offset;
3723 u8 *data;
3724 u16 rsn_cap;
3725 u32 wme_bss_disable;
3726
3727 WL_TRACE("Enter\n");
3728 if (wpa_ie == NULL)
3729 goto exit;
3730
3731 len = wpa_ie->len + TLV_HDR_LEN;
3732 data = (u8 *)wpa_ie;
3733 offset = 0;
3734 if (!is_rsn_ie)
3735 offset += VS_IE_FIXED_HDR_LEN;
3736 offset += WPA_IE_VERSION_LEN;
3737
3738 /* check for multicast cipher suite */
3739 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3740 err = -EINVAL;
3741 WL_ERR("no multicast cipher suite\n");
3742 goto exit;
3743 }
3744
3745 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3746 err = -EINVAL;
3747 WL_ERR("ivalid OUI\n");
3748 goto exit;
3749 }
3750 offset += TLV_OUI_LEN;
3751
3752 /* pick up multicast cipher */
3753 switch (data[offset]) {
3754 case WPA_CIPHER_NONE:
3755 gval = 0;
3756 break;
3757 case WPA_CIPHER_WEP_40:
3758 case WPA_CIPHER_WEP_104:
3759 gval = WEP_ENABLED;
3760 break;
3761 case WPA_CIPHER_TKIP:
3762 gval = TKIP_ENABLED;
3763 break;
3764 case WPA_CIPHER_AES_CCM:
3765 gval = AES_ENABLED;
3766 break;
3767 default:
3768 err = -EINVAL;
3769 WL_ERR("Invalid multi cast cipher info\n");
3770 goto exit;
3771 }
3772
3773 offset++;
3774 /* walk thru unicast cipher list and pick up what we recognize */
3775 count = data[offset] + (data[offset + 1] << 8);
3776 offset += WPA_IE_SUITE_COUNT_LEN;
3777 /* Check for unicast suite(s) */
3778 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3779 err = -EINVAL;
3780 WL_ERR("no unicast cipher suite\n");
3781 goto exit;
3782 }
3783 for (i = 0; i < count; i++) {
3784 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3785 err = -EINVAL;
3786 WL_ERR("ivalid OUI\n");
3787 goto exit;
3788 }
3789 offset += TLV_OUI_LEN;
3790 switch (data[offset]) {
3791 case WPA_CIPHER_NONE:
3792 break;
3793 case WPA_CIPHER_WEP_40:
3794 case WPA_CIPHER_WEP_104:
3795 pval |= WEP_ENABLED;
3796 break;
3797 case WPA_CIPHER_TKIP:
3798 pval |= TKIP_ENABLED;
3799 break;
3800 case WPA_CIPHER_AES_CCM:
3801 pval |= AES_ENABLED;
3802 break;
3803 default:
3804 WL_ERR("Ivalid unicast security info\n");
3805 }
3806 offset++;
3807 }
3808 /* walk thru auth management suite list and pick up what we recognize */
3809 count = data[offset] + (data[offset + 1] << 8);
3810 offset += WPA_IE_SUITE_COUNT_LEN;
3811 /* Check for auth key management suite(s) */
3812 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3813 err = -EINVAL;
3814 WL_ERR("no auth key mgmt suite\n");
3815 goto exit;
3816 }
3817 for (i = 0; i < count; i++) {
3818 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3819 err = -EINVAL;
3820 WL_ERR("ivalid OUI\n");
3821 goto exit;
3822 }
3823 offset += TLV_OUI_LEN;
3824 switch (data[offset]) {
3825 case RSN_AKM_NONE:
3826 WL_TRACE("RSN_AKM_NONE\n");
3827 wpa_auth |= WPA_AUTH_NONE;
3828 break;
3829 case RSN_AKM_UNSPECIFIED:
3830 WL_TRACE("RSN_AKM_UNSPECIFIED\n");
3831 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3832 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3833 break;
3834 case RSN_AKM_PSK:
3835 WL_TRACE("RSN_AKM_PSK\n");
3836 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3837 (wpa_auth |= WPA_AUTH_PSK);
3838 break;
3839 default:
3840 WL_ERR("Ivalid key mgmt info\n");
3841 }
3842 offset++;
3843 }
3844
3845 if (is_rsn_ie) {
3846 wme_bss_disable = 1;
3847 if ((offset + RSN_CAP_LEN) <= len) {
3848 rsn_cap = data[offset] + (data[offset + 1] << 8);
3849 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3850 wme_bss_disable = 0;
3851 }
3852 /* set wme_bss_disable to sync RSN Capabilities */
3853 err = brcmf_dev_intvar_set_bsscfg(ndev, "wme_bss_disable",
3854 wme_bss_disable, bssidx);
3855 if (err < 0) {
3856 WL_ERR("wme_bss_disable error %d\n", err);
3857 goto exit;
3858 }
3859 }
3860 /* FOR WPS , set SES_OW_ENABLED */
3861 wsec = (pval | gval | SES_OW_ENABLED);
3862
3863 /* set auth */
3864 err = brcmf_dev_intvar_set_bsscfg(ndev, "auth", auth, bssidx);
3865 if (err < 0) {
3866 WL_ERR("auth error %d\n", err);
3867 goto exit;
3868 }
3869 /* set wsec */
3870 err = brcmf_dev_intvar_set_bsscfg(ndev, "wsec", wsec, bssidx);
3871 if (err < 0) {
3872 WL_ERR("wsec error %d\n", err);
3873 goto exit;
3874 }
3875 /* set upper-layer auth */
3876 err = brcmf_dev_intvar_set_bsscfg(ndev, "wpa_auth", wpa_auth, bssidx);
3877 if (err < 0) {
3878 WL_ERR("wpa_auth error %d\n", err);
3879 goto exit;
3880 }
3881
3882exit:
3883 return err;
3884}
3885
3886static s32
3887brcmf_parse_vndr_ies(u8 *vndr_ie_buf, u32 vndr_ie_len,
3888 struct parsed_vndr_ies *vndr_ies)
3889{
3890 s32 err = 0;
3891 struct brcmf_vs_tlv *vndrie;
3892 struct brcmf_tlv *ie;
3893 struct parsed_vndr_ie_info *parsed_info;
3894 s32 remaining_len;
3895
3896 remaining_len = (s32)vndr_ie_len;
3897 memset(vndr_ies, 0, sizeof(*vndr_ies));
3898
3899 ie = (struct brcmf_tlv *)vndr_ie_buf;
3900 while (ie) {
3901 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3902 goto next;
3903 vndrie = (struct brcmf_vs_tlv *)ie;
3904 /* len should be bigger than OUI length + one */
3905 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
3906 WL_ERR("invalid vndr ie. length is too small %d\n",
3907 vndrie->len);
3908 goto next;
3909 }
3910 /* if wpa or wme ie, do not add ie */
3911 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3912 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3913 (vndrie->oui_type == WME_OUI_TYPE))) {
3914 WL_TRACE("Found WPA/WME oui. Do not add it\n");
3915 goto next;
3916 }
3917
3918 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3919
3920 /* save vndr ie information */
3921 parsed_info->ie_ptr = (char *)vndrie;
3922 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3923 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3924
3925 vndr_ies->count++;
3926
3927 WL_TRACE("** OUI %02x %02x %02x, type 0x%02x\n",
3928 parsed_info->vndrie.oui[0],
3929 parsed_info->vndrie.oui[1],
3930 parsed_info->vndrie.oui[2],
3931 parsed_info->vndrie.oui_type);
3932
3933 if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
3934 break;
3935next:
3936 remaining_len -= ie->len;
3937 if (remaining_len <= 2)
3938 ie = NULL;
3939 else
3940 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len);
3941 }
3942 return err;
3943}
3944
3945static u32
3946brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3947{
3948
3949 __le32 iecount_le;
3950 __le32 pktflag_le;
3951
3952 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3953 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3954
3955 iecount_le = cpu_to_le32(1);
3956 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3957
3958 pktflag_le = cpu_to_le32(pktflag);
3959 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3960
3961 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3962
3963 return ie_len + VNDR_IE_HDR_SIZE;
3964}
3965
Franky Lin3cb91f52012-10-10 11:13:08 -07003966static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003967brcmf_set_management_ie(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02003968 struct net_device *ndev, s32 bssidx, s32 pktflag,
3969 u8 *vndr_ie_buf, u32 vndr_ie_len)
3970{
3971 s32 err = 0;
3972 u8 *iovar_ie_buf;
3973 u8 *curr_ie_buf;
3974 u8 *mgmt_ie_buf = NULL;
Dan Carpenter81118d12012-10-10 11:13:07 -07003975 u32 mgmt_ie_buf_len;
3976 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003977 u32 del_add_ie_buf_len = 0;
3978 u32 total_ie_buf_len = 0;
3979 u32 parsed_ie_buf_len = 0;
3980 struct parsed_vndr_ies old_vndr_ies;
3981 struct parsed_vndr_ies new_vndr_ies;
3982 struct parsed_vndr_ie_info *vndrie_info;
3983 s32 i;
3984 u8 *ptr;
3985 u32 remained_buf_len;
3986
3987 WL_TRACE("bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag);
3988 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3989 if (!iovar_ie_buf)
3990 return -ENOMEM;
3991 curr_ie_buf = iovar_ie_buf;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003992 if (test_bit(WL_STATUS_AP_CREATING, &cfg->status) ||
3993 test_bit(WL_STATUS_AP_CREATED, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003994 switch (pktflag) {
3995 case VNDR_IE_PRBRSP_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003996 mgmt_ie_buf = cfg->ap_info->probe_res_ie;
3997 mgmt_ie_len = &cfg->ap_info->probe_res_ie_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003998 mgmt_ie_buf_len = sizeof(cfg->ap_info->probe_res_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003999 break;
4000 case VNDR_IE_BEACON_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004001 mgmt_ie_buf = cfg->ap_info->beacon_ie;
4002 mgmt_ie_len = &cfg->ap_info->beacon_ie_len;
4003 mgmt_ie_buf_len = sizeof(cfg->ap_info->beacon_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02004004 break;
4005 default:
4006 err = -EPERM;
4007 WL_ERR("not suitable type\n");
4008 goto exit;
4009 }
4010 bssidx = 0;
4011 } else {
4012 err = -EPERM;
4013 WL_ERR("not suitable type\n");
4014 goto exit;
4015 }
4016
4017 if (vndr_ie_len > mgmt_ie_buf_len) {
4018 err = -ENOMEM;
4019 WL_ERR("extra IE size too big\n");
4020 goto exit;
4021 }
4022
4023 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
4024 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
4025 ptr = curr_ie_buf;
4026 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
4027 for (i = 0; i < new_vndr_ies.count; i++) {
4028 vndrie_info = &new_vndr_ies.ie_info[i];
4029 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
4030 vndrie_info->ie_len);
4031 parsed_ie_buf_len += vndrie_info->ie_len;
4032 }
4033 }
4034
4035 if (mgmt_ie_buf != NULL) {
4036 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
4037 (memcmp(mgmt_ie_buf, curr_ie_buf,
4038 parsed_ie_buf_len) == 0)) {
4039 WL_TRACE("Previous mgmt IE is equals to current IE");
4040 goto exit;
4041 }
4042
4043 /* parse old vndr_ie */
4044 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
4045
4046 /* make a command to delete old ie */
4047 for (i = 0; i < old_vndr_ies.count; i++) {
4048 vndrie_info = &old_vndr_ies.ie_info[i];
4049
4050 WL_TRACE("DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4051 vndrie_info->vndrie.id,
4052 vndrie_info->vndrie.len,
4053 vndrie_info->vndrie.oui[0],
4054 vndrie_info->vndrie.oui[1],
4055 vndrie_info->vndrie.oui[2]);
4056
4057 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4058 vndrie_info->ie_ptr,
4059 vndrie_info->ie_len,
4060 "del");
4061 curr_ie_buf += del_add_ie_buf_len;
4062 total_ie_buf_len += del_add_ie_buf_len;
4063 }
4064 }
4065
4066 *mgmt_ie_len = 0;
4067 /* Add if there is any extra IE */
4068 if (mgmt_ie_buf && parsed_ie_buf_len) {
4069 ptr = mgmt_ie_buf;
4070
4071 remained_buf_len = mgmt_ie_buf_len;
4072
4073 /* make a command to add new ie */
4074 for (i = 0; i < new_vndr_ies.count; i++) {
4075 vndrie_info = &new_vndr_ies.ie_info[i];
4076
4077 WL_TRACE("ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4078 vndrie_info->vndrie.id,
4079 vndrie_info->vndrie.len,
4080 vndrie_info->vndrie.oui[0],
4081 vndrie_info->vndrie.oui[1],
4082 vndrie_info->vndrie.oui[2]);
4083
4084 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4085 vndrie_info->ie_ptr,
4086 vndrie_info->ie_len,
4087 "add");
4088 /* verify remained buf size before copy data */
4089 remained_buf_len -= vndrie_info->ie_len;
4090 if (remained_buf_len < 0) {
4091 WL_ERR("no space in mgmt_ie_buf: len left %d",
4092 remained_buf_len);
4093 break;
4094 }
4095
4096 /* save the parsed IE in wl struct */
4097 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4098 vndrie_info->ie_len);
4099 *mgmt_ie_len += vndrie_info->ie_len;
4100
4101 curr_ie_buf += del_add_ie_buf_len;
4102 total_ie_buf_len += del_add_ie_buf_len;
4103 }
4104 }
4105 if (total_ie_buf_len) {
4106 err = brcmf_dev_iovar_setbuf_bsscfg(ndev, "vndr_ie",
4107 iovar_ie_buf,
4108 total_ie_buf_len,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004109 cfg->extra_buf,
Hante Meuleman1a873342012-09-27 14:17:54 +02004110 WL_EXTRA_BUF_MAX, bssidx);
4111 if (err)
4112 WL_ERR("vndr ie set error : %d\n", err);
4113 }
4114
4115exit:
4116 kfree(iovar_ie_buf);
4117 return err;
4118}
4119
4120static s32
4121brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4122 struct cfg80211_ap_settings *settings)
4123{
4124 s32 ie_offset;
4125 struct brcmf_tlv *ssid_ie;
4126 struct brcmf_ssid_le ssid_le;
4127 s32 ioctl_value;
4128 s32 err = -EPERM;
4129 struct brcmf_tlv *rsn_ie;
4130 struct brcmf_vs_tlv *wpa_ie;
4131 struct brcmf_join_params join_params;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004132 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004133 s32 bssidx = 0;
4134
4135 WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
4136 settings->channel_type, settings->beacon_interval,
4137 settings->dtim_period);
4138 WL_TRACE("ssid=%s(%d), auth_type=%d, inactivity_timeout=%d\n",
4139 settings->ssid, settings->ssid_len, settings->auth_type,
4140 settings->inactivity_timeout);
4141
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004142 if (!test_bit(WL_STATUS_AP_CREATING, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004143 WL_ERR("Not in AP creation mode\n");
4144 return -EPERM;
4145 }
4146
4147 memset(&ssid_le, 0, sizeof(ssid_le));
4148 if (settings->ssid == NULL || settings->ssid_len == 0) {
4149 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4150 ssid_ie = brcmf_parse_tlvs(
4151 (u8 *)&settings->beacon.head[ie_offset],
4152 settings->beacon.head_len - ie_offset,
4153 WLAN_EID_SSID);
4154 if (!ssid_ie)
4155 return -EINVAL;
4156
4157 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4158 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
4159 WL_TRACE("SSID is (%s) in Head\n", ssid_le.SSID);
4160 } else {
4161 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4162 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4163 }
4164
4165 brcmf_set_mpc(ndev, 0);
4166 ioctl_value = 1;
4167 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_DOWN, &ioctl_value);
4168 if (err < 0) {
4169 WL_ERR("BRCMF_C_DOWN error %d\n", err);
4170 goto exit;
4171 }
4172 ioctl_value = 1;
4173 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &ioctl_value);
4174 if (err < 0) {
4175 WL_ERR("SET INFRA error %d\n", err);
4176 goto exit;
4177 }
4178 ioctl_value = 1;
4179 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AP, &ioctl_value);
4180 if (err < 0) {
4181 WL_ERR("setting AP mode failed %d\n", err);
4182 goto exit;
4183 }
4184
4185 /* find the RSN_IE */
4186 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4187 settings->beacon.tail_len, WLAN_EID_RSN);
4188
4189 /* find the WPA_IE */
4190 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4191 settings->beacon.tail_len);
4192
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004193 kfree(cfg->ap_info->rsn_ie);
4194 cfg->ap_info->rsn_ie = NULL;
4195 kfree(cfg->ap_info->wpa_ie);
4196 cfg->ap_info->wpa_ie = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02004197
4198 if ((wpa_ie != NULL || rsn_ie != NULL)) {
4199 WL_TRACE("WPA(2) IE is found\n");
4200 if (wpa_ie != NULL) {
4201 /* WPA IE */
4202 err = brcmf_configure_wpaie(ndev, wpa_ie, false,
4203 bssidx);
4204 if (err < 0)
4205 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004206 cfg->ap_info->wpa_ie = kmemdup(wpa_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02004207 wpa_ie->len +
4208 TLV_HDR_LEN,
4209 GFP_KERNEL);
4210 } else {
4211 /* RSN IE */
4212 err = brcmf_configure_wpaie(ndev,
4213 (struct brcmf_vs_tlv *)rsn_ie, true, bssidx);
4214 if (err < 0)
4215 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004216 cfg->ap_info->rsn_ie = kmemdup(rsn_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02004217 rsn_ie->len +
4218 TLV_HDR_LEN,
4219 GFP_KERNEL);
4220 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004221 cfg->ap_info->security_mode = true;
Hante Meuleman1a873342012-09-27 14:17:54 +02004222 } else {
4223 WL_TRACE("No WPA(2) IEs found\n");
4224 brcmf_configure_opensecurity(ndev, bssidx);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004225 cfg->ap_info->security_mode = false;
Hante Meuleman1a873342012-09-27 14:17:54 +02004226 }
4227 /* Set Beacon IEs to FW */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004228 err = brcmf_set_management_ie(cfg, ndev, bssidx,
Hante Meuleman1a873342012-09-27 14:17:54 +02004229 VNDR_IE_BEACON_FLAG,
4230 (u8 *)settings->beacon.tail,
4231 settings->beacon.tail_len);
4232 if (err)
4233 WL_ERR("Set Beacon IE Failed\n");
4234 else
4235 WL_TRACE("Applied Vndr IEs for Beacon\n");
4236
4237 /* Set Probe Response IEs to FW */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004238 err = brcmf_set_management_ie(cfg, ndev, bssidx,
Hante Meuleman1a873342012-09-27 14:17:54 +02004239 VNDR_IE_PRBRSP_FLAG,
4240 (u8 *)settings->beacon.proberesp_ies,
4241 settings->beacon.proberesp_ies_len);
4242 if (err)
4243 WL_ERR("Set Probe Resp IE Failed\n");
4244 else
4245 WL_TRACE("Applied Vndr IEs for Probe Resp\n");
4246
4247 if (settings->beacon_interval) {
4248 ioctl_value = settings->beacon_interval;
4249 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_BCNPRD,
4250 &ioctl_value);
4251 if (err < 0) {
4252 WL_ERR("Beacon Interval Set Error, %d\n", err);
4253 goto exit;
4254 }
4255 }
4256 if (settings->dtim_period) {
4257 ioctl_value = settings->dtim_period;
4258 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_DTIMPRD,
4259 &ioctl_value);
4260 if (err < 0) {
4261 WL_ERR("DTIM Interval Set Error, %d\n", err);
4262 goto exit;
4263 }
4264 }
4265 ioctl_value = 1;
4266 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_UP, &ioctl_value);
4267 if (err < 0) {
4268 WL_ERR("BRCMF_C_UP error (%d)\n", err);
4269 goto exit;
4270 }
4271
4272 memset(&join_params, 0, sizeof(join_params));
4273 /* join parameters starts with ssid */
4274 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4275 /* create softap */
4276 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SSID, &join_params,
4277 sizeof(join_params));
4278 if (err < 0) {
4279 WL_ERR("SET SSID error (%d)\n", err);
4280 goto exit;
4281 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004282 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4283 set_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004284
4285exit:
4286 if (err)
4287 brcmf_set_mpc(ndev, 1);
4288 return err;
4289}
4290
4291static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4292{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004293 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004294 s32 ioctl_value;
4295 s32 err = -EPERM;
4296
4297 WL_TRACE("Enter\n");
4298
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004299 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004300 /* Due to most likely deauths outstanding we sleep */
4301 /* first to make sure they get processed by fw. */
4302 msleep(400);
4303 ioctl_value = 0;
4304 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_AP, &ioctl_value);
4305 if (err < 0) {
4306 WL_ERR("setting AP mode failed %d\n", err);
4307 goto exit;
4308 }
4309 ioctl_value = 0;
4310 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_UP, &ioctl_value);
4311 if (err < 0) {
4312 WL_ERR("BRCMF_C_UP error %d\n", err);
4313 goto exit;
4314 }
4315 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004316 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4317 clear_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004318 }
4319exit:
4320 return err;
4321}
4322
4323static int
4324brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
4325 u8 *mac)
4326{
4327 struct brcmf_scb_val_le scbval;
4328 s32 err;
4329
4330 if (!mac)
4331 return -EFAULT;
4332
4333 WL_TRACE("Enter %pM\n", mac);
4334
4335 if (!check_sys_up(wiphy))
4336 return -EIO;
4337
4338 memcpy(&scbval.ea, mac, ETH_ALEN);
4339 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
4340 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
4341 &scbval, sizeof(scbval));
4342 if (err)
4343 WL_ERR("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
4344
4345 WL_TRACE("Exit\n");
4346 return err;
4347}
4348
Arend van Spriel5b435de2011-10-05 13:19:03 +02004349static struct cfg80211_ops wl_cfg80211_ops = {
4350 .change_virtual_intf = brcmf_cfg80211_change_iface,
4351 .scan = brcmf_cfg80211_scan,
4352 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4353 .join_ibss = brcmf_cfg80211_join_ibss,
4354 .leave_ibss = brcmf_cfg80211_leave_ibss,
4355 .get_station = brcmf_cfg80211_get_station,
4356 .set_tx_power = brcmf_cfg80211_set_tx_power,
4357 .get_tx_power = brcmf_cfg80211_get_tx_power,
4358 .add_key = brcmf_cfg80211_add_key,
4359 .del_key = brcmf_cfg80211_del_key,
4360 .get_key = brcmf_cfg80211_get_key,
4361 .set_default_key = brcmf_cfg80211_config_default_key,
4362 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4363 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
4364 .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
4365 .connect = brcmf_cfg80211_connect,
4366 .disconnect = brcmf_cfg80211_disconnect,
4367 .suspend = brcmf_cfg80211_suspend,
4368 .resume = brcmf_cfg80211_resume,
4369 .set_pmksa = brcmf_cfg80211_set_pmksa,
4370 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004371 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004372 .start_ap = brcmf_cfg80211_start_ap,
4373 .stop_ap = brcmf_cfg80211_stop_ap,
4374 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004375#ifndef CONFIG_BRCMISCAN
4376 /* scheduled scan need e-scan, which is mutual exclusive with i-scan */
4377 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4378 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
4379#endif
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004380#ifdef CONFIG_NL80211_TESTMODE
4381 .testmode_cmd = brcmf_cfg80211_testmode
4382#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02004383};
4384
4385static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
4386{
4387 s32 err = 0;
4388
4389 switch (mode) {
4390 case WL_MODE_BSS:
4391 return NL80211_IFTYPE_STATION;
4392 case WL_MODE_IBSS:
4393 return NL80211_IFTYPE_ADHOC;
4394 default:
4395 return NL80211_IFTYPE_UNSPECIFIED;
4396 }
4397
4398 return err;
4399}
4400
Arend van Spriele5806072012-09-19 22:21:08 +02004401static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
4402{
4403#ifndef CONFIG_BRCMFISCAN
4404 /* scheduled scan settings */
4405 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
4406 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
4407 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
4408 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
4409#endif
4410}
4411
Arend van Spriel5db6e952012-09-27 14:17:53 +02004412static struct wireless_dev *brcmf_alloc_wdev(struct device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004413{
4414 struct wireless_dev *wdev;
4415 s32 err = 0;
4416
4417 wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
4418 if (!wdev)
4419 return ERR_PTR(-ENOMEM);
4420
Arend van Spriel5db6e952012-09-27 14:17:53 +02004421 wdev->wiphy = wiphy_new(&wl_cfg80211_ops,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004422 sizeof(struct brcmf_cfg80211_info));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004423 if (!wdev->wiphy) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004424 WL_ERR("Could not allocate wiphy device\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004425 err = -ENOMEM;
4426 goto wiphy_new_out;
4427 }
4428 set_wiphy_dev(wdev->wiphy, ndev);
4429 wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
4430 wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Hante Meuleman1a873342012-09-27 14:17:54 +02004431 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
4432 BIT(NL80211_IFTYPE_ADHOC) |
4433 BIT(NL80211_IFTYPE_AP);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004434 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
4435 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
4436 * it as 11a by default.
4437 * This will be updated with
4438 * 11n phy tables in
4439 * "ifconfig up"
4440 * if phy has 11n capability
4441 */
4442 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
4443 wdev->wiphy->cipher_suites = __wl_cipher_suites;
4444 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
4445 wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
4446 * save mode
4447 * by default
4448 */
Arend van Spriele5806072012-09-19 22:21:08 +02004449 brcmf_wiphy_pno_params(wdev->wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004450 err = wiphy_register(wdev->wiphy);
4451 if (err < 0) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004452 WL_ERR("Could not register wiphy device (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004453 goto wiphy_register_out;
4454 }
4455 return wdev;
4456
4457wiphy_register_out:
4458 wiphy_free(wdev->wiphy);
4459
4460wiphy_new_out:
4461 kfree(wdev);
4462
4463 return ERR_PTR(err);
4464}
4465
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004466static void brcmf_free_wdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004467{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004468 struct wireless_dev *wdev = cfg->wdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004469
4470 if (!wdev) {
4471 WL_ERR("wdev is invalid\n");
4472 return;
4473 }
4474 wiphy_unregister(wdev->wiphy);
4475 wiphy_free(wdev->wiphy);
4476 kfree(wdev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004477 cfg->wdev = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004478}
4479
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004480static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004481 const struct brcmf_event_msg *e)
4482{
4483 u32 event = be32_to_cpu(e->event_type);
4484 u32 status = be32_to_cpu(e->status);
4485
4486 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
4487 WL_CONN("Processing set ssid\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004488 cfg->link_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004489 return true;
4490 }
4491
4492 return false;
4493}
4494
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004495static bool brcmf_is_linkdown(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004496 const struct brcmf_event_msg *e)
4497{
4498 u32 event = be32_to_cpu(e->event_type);
4499 u16 flags = be16_to_cpu(e->flags);
4500
4501 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
4502 WL_CONN("Processing link down\n");
4503 return true;
4504 }
4505 return false;
4506}
4507
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004508static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004509 const struct brcmf_event_msg *e)
4510{
4511 u32 event = be32_to_cpu(e->event_type);
4512 u32 status = be32_to_cpu(e->status);
4513
4514 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
4515 WL_CONN("Processing Link %s & no network found\n",
4516 be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ?
4517 "up" : "down");
4518 return true;
4519 }
4520
4521 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
4522 WL_CONN("Processing connecting & no network found\n");
4523 return true;
4524 }
4525
4526 return false;
4527}
4528
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004529static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004530{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004531 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004532
4533 kfree(conn_info->req_ie);
4534 conn_info->req_ie = NULL;
4535 conn_info->req_ie_len = 0;
4536 kfree(conn_info->resp_ie);
4537 conn_info->resp_ie = NULL;
4538 conn_info->resp_ie_len = 0;
4539}
4540
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004541static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004542{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004543 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004544 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004545 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004546 u32 req_len;
4547 u32 resp_len;
4548 s32 err = 0;
4549
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004550 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004551
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004552 err = brcmf_dev_bufvar_get(ndev, "assoc_info", cfg->extra_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004553 WL_ASSOC_INFO_MAX);
4554 if (err) {
4555 WL_ERR("could not get assoc info (%d)\n", err);
4556 return err;
4557 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004558 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004559 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004560 req_len = le32_to_cpu(assoc_info->req_len);
4561 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004562 if (req_len) {
4563 err = brcmf_dev_bufvar_get(ndev, "assoc_req_ies",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004564 cfg->extra_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004565 WL_ASSOC_INFO_MAX);
4566 if (err) {
4567 WL_ERR("could not get assoc req (%d)\n", err);
4568 return err;
4569 }
4570 conn_info->req_ie_len = req_len;
4571 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004572 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004573 GFP_KERNEL);
4574 } else {
4575 conn_info->req_ie_len = 0;
4576 conn_info->req_ie = NULL;
4577 }
4578 if (resp_len) {
4579 err = brcmf_dev_bufvar_get(ndev, "assoc_resp_ies",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004580 cfg->extra_buf,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004581 WL_ASSOC_INFO_MAX);
4582 if (err) {
4583 WL_ERR("could not get assoc resp (%d)\n", err);
4584 return err;
4585 }
4586 conn_info->resp_ie_len = resp_len;
4587 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004588 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004589 GFP_KERNEL);
4590 } else {
4591 conn_info->resp_ie_len = 0;
4592 conn_info->resp_ie = NULL;
4593 }
4594 WL_CONN("req len (%d) resp len (%d)\n",
4595 conn_info->req_ie_len, conn_info->resp_ie_len);
4596
4597 return err;
4598}
4599
4600static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004601brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004602 struct net_device *ndev,
4603 const struct brcmf_event_msg *e)
4604{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004605 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004606 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4607 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004608 struct brcmf_channel_info_le channel_le;
4609 struct ieee80211_channel *notify_channel;
4610 struct ieee80211_supported_band *band;
4611 u32 freq;
4612 s32 err = 0;
4613 u32 target_channel;
4614
4615 WL_TRACE("Enter\n");
4616
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004617 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004618 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004619 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004620
4621 brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_le,
4622 sizeof(channel_le));
4623
4624 target_channel = le32_to_cpu(channel_le.target_channel);
4625 WL_CONN("Roamed to channel %d\n", target_channel);
4626
4627 if (target_channel <= CH_MAX_2G_CHANNEL)
4628 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4629 else
4630 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4631
4632 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4633 notify_channel = ieee80211_get_channel(wiphy, freq);
4634
Arend van Spriel06bb1232012-09-27 14:17:56 +02004635 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004636 conn_info->req_ie, conn_info->req_ie_len,
4637 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
4638 WL_CONN("Report roaming result\n");
4639
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004640 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004641 WL_TRACE("Exit\n");
4642 return err;
4643}
4644
4645static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004646brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004647 struct net_device *ndev, const struct brcmf_event_msg *e,
4648 bool completed)
4649{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004650 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004651 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004652 s32 err = 0;
4653
4654 WL_TRACE("Enter\n");
4655
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004656 if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004657 if (completed) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004658 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004659 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004660 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004661 }
4662 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004663 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004664 conn_info->req_ie,
4665 conn_info->req_ie_len,
4666 conn_info->resp_ie,
4667 conn_info->resp_ie_len,
4668 completed ? WLAN_STATUS_SUCCESS :
4669 WLAN_STATUS_AUTH_TIMEOUT,
4670 GFP_KERNEL);
4671 if (completed)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004672 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004673 WL_CONN("Report connect result - connection %s\n",
4674 completed ? "succeeded" : "failed");
4675 }
4676 WL_TRACE("Exit\n");
4677 return err;
4678}
4679
4680static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004681brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004682 struct net_device *ndev,
4683 const struct brcmf_event_msg *e, void *data)
4684{
4685 s32 err = 0;
4686 u32 event = be32_to_cpu(e->event_type);
4687 u32 reason = be32_to_cpu(e->reason);
4688 u32 len = be32_to_cpu(e->datalen);
4689 static int generation;
4690
4691 struct station_info sinfo;
4692
4693 WL_CONN("event %d, reason %d\n", event, reason);
4694 memset(&sinfo, 0, sizeof(sinfo));
4695
4696 sinfo.filled = 0;
4697 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
4698 reason == BRCMF_E_STATUS_SUCCESS) {
4699 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4700 if (!data) {
4701 WL_ERR("No IEs present in ASSOC/REASSOC_IND");
4702 return -EINVAL;
4703 }
4704 sinfo.assoc_req_ies = data;
4705 sinfo.assoc_req_ies_len = len;
4706 generation++;
4707 sinfo.generation = generation;
4708 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_ATOMIC);
4709 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4710 (event == BRCMF_E_DEAUTH_IND) ||
4711 (event == BRCMF_E_DEAUTH)) {
4712 generation++;
4713 sinfo.generation = generation;
4714 cfg80211_del_sta(ndev, e->addr, GFP_ATOMIC);
4715 }
4716 return err;
4717}
4718
4719static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004720brcmf_notify_connect_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004721 struct net_device *ndev,
4722 const struct brcmf_event_msg *e, void *data)
4723{
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004724 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004725 s32 err = 0;
4726
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004727 if (cfg->conf->mode == WL_MODE_AP) {
4728 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
4729 } else if (brcmf_is_linkup(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004730 WL_CONN("Linkup\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004731 if (brcmf_is_ibssmode(cfg)) {
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004732 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004733 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004734 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004735 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
4736 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004737 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004738 brcmf_bss_connect_done(cfg, ndev, e, true);
4739 } else if (brcmf_is_linkdown(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004740 WL_CONN("Linkdown\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004741 if (brcmf_is_ibssmode(cfg)) {
4742 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004743 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004744 &cfg->status))
4745 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004746 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004747 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004748 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004749 &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004750 cfg80211_disconnected(ndev, 0, NULL, 0,
4751 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004752 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004753 }
4754 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004755 brcmf_init_prof(cfg->profile);
4756 } else if (brcmf_is_nonetwork(cfg, e)) {
4757 if (brcmf_is_ibssmode(cfg))
4758 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004759 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004760 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004761 }
4762
4763 return err;
4764}
4765
4766static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004767brcmf_notify_roaming_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004768 struct net_device *ndev,
4769 const struct brcmf_event_msg *e, void *data)
4770{
4771 s32 err = 0;
4772 u32 event = be32_to_cpu(e->event_type);
4773 u32 status = be32_to_cpu(e->status);
4774
4775 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004776 if (test_bit(WL_STATUS_CONNECTED, &cfg->status))
4777 brcmf_bss_roaming_done(cfg, ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004778 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004779 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004780 }
4781
4782 return err;
4783}
4784
4785static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004786brcmf_notify_mic_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004787 struct net_device *ndev,
4788 const struct brcmf_event_msg *e, void *data)
4789{
4790 u16 flags = be16_to_cpu(e->flags);
4791 enum nl80211_key_type key_type;
4792
4793 if (flags & BRCMF_EVENT_MSG_GROUP)
4794 key_type = NL80211_KEYTYPE_GROUP;
4795 else
4796 key_type = NL80211_KEYTYPE_PAIRWISE;
4797
4798 cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1,
4799 NULL, GFP_KERNEL);
4800
4801 return 0;
4802}
4803
4804static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004805brcmf_notify_scan_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004806 struct net_device *ndev,
4807 const struct brcmf_event_msg *e, void *data)
4808{
4809 struct brcmf_channel_info_le channel_inform_le;
4810 struct brcmf_scan_results_le *bss_list_le;
4811 u32 len = WL_SCAN_BUF_MAX;
4812 s32 err = 0;
4813 bool scan_abort = false;
4814 u32 scan_channel;
4815
4816 WL_TRACE("Enter\n");
4817
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004818 if (cfg->iscan_on && cfg->iscan_kickstart) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004819 WL_TRACE("Exit\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004820 return brcmf_wakeup_iscan(cfg_to_iscan(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004821 }
4822
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004823 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004824 WL_ERR("Scan complete while device not scanning\n");
4825 scan_abort = true;
4826 err = -EINVAL;
4827 goto scan_done_out;
4828 }
4829
4830 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_CHANNEL, &channel_inform_le,
4831 sizeof(channel_inform_le));
4832 if (err) {
4833 WL_ERR("scan busy (%d)\n", err);
4834 scan_abort = true;
4835 goto scan_done_out;
4836 }
4837 scan_channel = le32_to_cpu(channel_inform_le.scan_channel);
4838 if (scan_channel)
4839 WL_CONN("channel_inform.scan_channel (%d)\n", scan_channel);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004840 cfg->bss_list = cfg->scan_results;
4841 bss_list_le = (struct brcmf_scan_results_le *) cfg->bss_list;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004842
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004843 memset(cfg->scan_results, 0, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004844 bss_list_le->buflen = cpu_to_le32(len);
4845 err = brcmf_exec_dcmd(ndev, BRCMF_C_SCAN_RESULTS,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004846 cfg->scan_results, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004847 if (err) {
4848 WL_ERR("%s Scan_results error (%d)\n", ndev->name, err);
4849 err = -EINVAL;
4850 scan_abort = true;
4851 goto scan_done_out;
4852 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004853 cfg->scan_results->buflen = le32_to_cpu(bss_list_le->buflen);
4854 cfg->scan_results->version = le32_to_cpu(bss_list_le->version);
4855 cfg->scan_results->count = le32_to_cpu(bss_list_le->count);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004856
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004857 err = brcmf_inform_bss(cfg);
Hante Meuleman35aafa92012-09-11 21:18:49 +02004858 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004859 scan_abort = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004860
4861scan_done_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004862 if (cfg->scan_request) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004863 WL_SCAN("calling cfg80211_scan_done\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004864 cfg80211_scan_done(cfg->scan_request, scan_abort);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004865 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004866 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004867 }
4868
4869 WL_TRACE("Exit\n");
4870
4871 return err;
4872}
4873
4874static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4875{
4876 conf->mode = (u32)-1;
4877 conf->frag_threshold = (u32)-1;
4878 conf->rts_threshold = (u32)-1;
4879 conf->retry_short = (u32)-1;
4880 conf->retry_long = (u32)-1;
4881 conf->tx_power = -1;
4882}
4883
4884static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
4885{
4886 memset(el, 0, sizeof(*el));
4887 el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status;
4888 el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status;
Hante Meuleman1a873342012-09-27 14:17:54 +02004889 el->handler[BRCMF_E_DEAUTH_IND] = brcmf_notify_connect_status;
4890 el->handler[BRCMF_E_DEAUTH] = brcmf_notify_connect_status;
4891 el->handler[BRCMF_E_DISASSOC_IND] = brcmf_notify_connect_status;
4892 el->handler[BRCMF_E_ASSOC_IND] = brcmf_notify_connect_status;
4893 el->handler[BRCMF_E_REASSOC_IND] = brcmf_notify_connect_status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004894 el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
4895 el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
4896 el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
Arend van Spriele5806072012-09-19 22:21:08 +02004897 el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004898}
4899
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004900static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004901{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004902 kfree(cfg->scan_results);
4903 cfg->scan_results = NULL;
4904 kfree(cfg->bss_info);
4905 cfg->bss_info = NULL;
4906 kfree(cfg->conf);
4907 cfg->conf = NULL;
4908 kfree(cfg->profile);
4909 cfg->profile = NULL;
4910 kfree(cfg->scan_req_int);
4911 cfg->scan_req_int = NULL;
4912 kfree(cfg->escan_ioctl_buf);
4913 cfg->escan_ioctl_buf = NULL;
4914 kfree(cfg->dcmd_buf);
4915 cfg->dcmd_buf = NULL;
4916 kfree(cfg->extra_buf);
4917 cfg->extra_buf = NULL;
4918 kfree(cfg->iscan);
4919 cfg->iscan = NULL;
4920 kfree(cfg->pmk_list);
4921 cfg->pmk_list = NULL;
4922 if (cfg->ap_info) {
4923 kfree(cfg->ap_info->wpa_ie);
4924 kfree(cfg->ap_info->rsn_ie);
4925 kfree(cfg->ap_info);
4926 cfg->ap_info = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02004927 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004928}
4929
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004930static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004931{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004932 cfg->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL);
4933 if (!cfg->scan_results)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004934 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004935 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4936 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004937 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004938 cfg->profile = kzalloc(sizeof(*cfg->profile), GFP_KERNEL);
4939 if (!cfg->profile)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004940 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004941 cfg->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4942 if (!cfg->bss_info)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004943 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004944 cfg->scan_req_int = kzalloc(sizeof(*cfg->scan_req_int),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004945 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004946 if (!cfg->scan_req_int)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004947 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004948 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4949 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004950 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004951 cfg->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
4952 if (!cfg->dcmd_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004953 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004954 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4955 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004956 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004957 cfg->iscan = kzalloc(sizeof(*cfg->iscan), GFP_KERNEL);
4958 if (!cfg->iscan)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004959 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004960 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4961 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004962 goto init_priv_mem_out;
4963
4964 return 0;
4965
4966init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004967 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004968
4969 return -ENOMEM;
4970}
4971
4972/*
4973* retrieve first queued event from head
4974*/
4975
4976static struct brcmf_cfg80211_event_q *brcmf_deq_event(
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004977 struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004978{
4979 struct brcmf_cfg80211_event_q *e = NULL;
4980
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004981 spin_lock_irq(&cfg->evt_q_lock);
4982 if (!list_empty(&cfg->evt_q_list)) {
4983 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004984 struct brcmf_cfg80211_event_q, evt_q_list);
4985 list_del(&e->evt_q_list);
4986 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004987 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004988
4989 return e;
4990}
4991
4992/*
Arend van Sprielbcbec9e2012-02-09 21:09:06 +01004993* push event to tail of the queue
4994*
4995* remark: this function may not sleep as it is called in atomic context.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004996*/
4997
4998static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004999brcmf_enq_event(struct brcmf_cfg80211_info *cfg, u32 event,
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005000 const struct brcmf_event_msg *msg, void *data)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005001{
5002 struct brcmf_cfg80211_event_q *e;
5003 s32 err = 0;
Arend van Sprielcf440662012-02-09 21:09:07 +01005004 ulong flags;
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005005 u32 data_len;
5006 u32 total_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005007
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005008 total_len = sizeof(struct brcmf_cfg80211_event_q);
5009 if (data)
5010 data_len = be32_to_cpu(msg->datalen);
5011 else
5012 data_len = 0;
5013 total_len += data_len;
5014 e = kzalloc(total_len, GFP_ATOMIC);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005015 if (!e)
5016 return -ENOMEM;
5017
5018 e->etype = event;
5019 memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
Hante Meulemanc4fdb052012-09-11 21:18:47 +02005020 if (data)
5021 memcpy(&e->edata, data, data_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005022
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005023 spin_lock_irqsave(&cfg->evt_q_lock, flags);
5024 list_add_tail(&e->evt_q_list, &cfg->evt_q_list);
5025 spin_unlock_irqrestore(&cfg->evt_q_lock, flags);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005026
5027 return err;
5028}
5029
5030static void brcmf_put_event(struct brcmf_cfg80211_event_q *e)
5031{
5032 kfree(e);
5033}
5034
5035static void brcmf_cfg80211_event_handler(struct work_struct *work)
5036{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005037 struct brcmf_cfg80211_info *cfg =
5038 container_of(work, struct brcmf_cfg80211_info,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005039 event_work);
5040 struct brcmf_cfg80211_event_q *e;
5041
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005042 e = brcmf_deq_event(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005043 if (unlikely(!e)) {
5044 WL_ERR("event queue empty...\n");
5045 return;
5046 }
5047
5048 do {
5049 WL_INFO("event type (%d)\n", e->etype);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005050 if (cfg->el.handler[e->etype])
5051 cfg->el.handler[e->etype](cfg,
5052 cfg_to_ndev(cfg),
Arend van Spriel5b435de2011-10-05 13:19:03 +02005053 &e->emsg, e->edata);
5054 else
5055 WL_INFO("Unknown Event (%d): ignoring\n", e->etype);
5056 brcmf_put_event(e);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005057 } while ((e = brcmf_deq_event(cfg)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005058
5059}
5060
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005061static void brcmf_init_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005062{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005063 spin_lock_init(&cfg->evt_q_lock);
5064 INIT_LIST_HEAD(&cfg->evt_q_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005065}
5066
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005067static void brcmf_flush_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005068{
5069 struct brcmf_cfg80211_event_q *e;
5070
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005071 spin_lock_irq(&cfg->evt_q_lock);
5072 while (!list_empty(&cfg->evt_q_list)) {
5073 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005074 struct brcmf_cfg80211_event_q, evt_q_list);
5075 list_del(&e->evt_q_list);
5076 kfree(e);
5077 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005078 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005079}
5080
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005081static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005082{
5083 s32 err = 0;
5084
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005085 cfg->scan_request = NULL;
5086 cfg->pwr_save = true;
Hante Meulemane756af52012-09-11 21:18:52 +02005087#ifdef CONFIG_BRCMISCAN
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005088 cfg->iscan_on = true; /* iscan on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005089 we enable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005090 cfg->escan_on = false; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02005091 we disable escan per default */
5092#else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005093 cfg->iscan_on = false; /* iscan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02005094 we disable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005095 cfg->escan_on = true; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02005096 we enable escan per default */
5097#endif
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005098 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005099 we enable roam per default */
5100
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005101 cfg->iscan_kickstart = false;
5102 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02005103 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005104 cfg->dongle_up = false; /* dongle is not up yet */
5105 brcmf_init_eq(cfg);
5106 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005107 if (err)
5108 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005109 INIT_WORK(&cfg->event_work, brcmf_cfg80211_event_handler);
5110 brcmf_init_eloop_handler(&cfg->el);
5111 mutex_init(&cfg->usr_sync);
5112 err = brcmf_init_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005113 if (err)
5114 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005115 brcmf_init_escan(cfg);
5116 brcmf_init_conf(cfg->conf);
5117 brcmf_init_prof(cfg->profile);
5118 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005119
5120 return err;
5121}
5122
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005123static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005124{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005125 cancel_work_sync(&cfg->event_work);
5126 cfg->dongle_up = false; /* dongle down */
5127 brcmf_flush_eq(cfg);
5128 brcmf_link_down(cfg);
5129 brcmf_abort_scanning(cfg);
5130 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005131}
5132
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005133struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct net_device *ndev,
Arend van Sprielb451ec92012-09-27 14:17:52 +02005134 struct device *busdev,
5135 struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005136{
5137 struct wireless_dev *wdev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005138 struct brcmf_cfg80211_info *cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005139 s32 err = 0;
5140
5141 if (!ndev) {
5142 WL_ERR("ndev is invalid\n");
5143 return NULL;
5144 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005145
Arend van Spriel5db6e952012-09-27 14:17:53 +02005146 wdev = brcmf_alloc_wdev(busdev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005147 if (IS_ERR(wdev)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005148 return NULL;
5149 }
5150
5151 wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005152 cfg = wdev_to_cfg(wdev);
5153 cfg->wdev = wdev;
5154 cfg->pub = drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005155 ndev->ieee80211_ptr = wdev;
5156 SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
5157 wdev->netdev = ndev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005158 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005159 if (err) {
5160 WL_ERR("Failed to init iwm_priv (%d)\n", err);
5161 goto cfg80211_attach_out;
5162 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005163
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005164 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005165
5166cfg80211_attach_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005167 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005168 return NULL;
5169}
5170
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005171void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005172{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005173 wl_deinit_priv(cfg);
5174 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005175}
5176
5177void
5178brcmf_cfg80211_event(struct net_device *ndev,
5179 const struct brcmf_event_msg *e, void *data)
5180{
5181 u32 event_type = be32_to_cpu(e->event_type);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005182 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005183
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005184 if (!brcmf_enq_event(cfg, event_type, e, data))
5185 schedule_work(&cfg->event_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005186}
5187
5188static s32 brcmf_dongle_mode(struct net_device *ndev, s32 iftype)
5189{
5190 s32 infra = 0;
5191 s32 err = 0;
5192
5193 switch (iftype) {
5194 case NL80211_IFTYPE_MONITOR:
5195 case NL80211_IFTYPE_WDS:
5196 WL_ERR("type (%d) : currently we do not support this mode\n",
5197 iftype);
5198 err = -EINVAL;
5199 return err;
5200 case NL80211_IFTYPE_ADHOC:
5201 infra = 0;
5202 break;
5203 case NL80211_IFTYPE_STATION:
5204 infra = 1;
5205 break;
Hante Meuleman1a873342012-09-27 14:17:54 +02005206 case NL80211_IFTYPE_AP:
5207 infra = 1;
5208 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005209 default:
5210 err = -EINVAL;
5211 WL_ERR("invalid type (%d)\n", iftype);
5212 return err;
5213 }
5214 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_INFRA, &infra);
5215 if (err) {
5216 WL_ERR("WLC_SET_INFRA error (%d)\n", err);
5217 return err;
5218 }
5219
5220 return 0;
5221}
5222
5223static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
5224{
5225 /* Room for "event_msgs" + '\0' + bitvec */
5226 s8 iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
5227 s8 eventmask[BRCMF_EVENTING_MASK_LEN];
5228 s32 err = 0;
5229
5230 WL_TRACE("Enter\n");
5231
5232 /* Setup event_msgs */
Alwin Beukers53a22772011-10-12 20:51:30 +02005233 brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
5234 iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005235 err = brcmf_exec_dcmd(ndev, BRCMF_C_GET_VAR, iovbuf, sizeof(iovbuf));
5236 if (err) {
5237 WL_ERR("Get event_msgs error (%d)\n", err);
5238 goto dongle_eventmsg_out;
5239 }
5240 memcpy(eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
5241
5242 setbit(eventmask, BRCMF_E_SET_SSID);
5243 setbit(eventmask, BRCMF_E_ROAM);
5244 setbit(eventmask, BRCMF_E_PRUNE);
5245 setbit(eventmask, BRCMF_E_AUTH);
5246 setbit(eventmask, BRCMF_E_REASSOC);
5247 setbit(eventmask, BRCMF_E_REASSOC_IND);
5248 setbit(eventmask, BRCMF_E_DEAUTH_IND);
5249 setbit(eventmask, BRCMF_E_DISASSOC_IND);
5250 setbit(eventmask, BRCMF_E_DISASSOC);
5251 setbit(eventmask, BRCMF_E_JOIN);
5252 setbit(eventmask, BRCMF_E_ASSOC_IND);
5253 setbit(eventmask, BRCMF_E_PSK_SUP);
5254 setbit(eventmask, BRCMF_E_LINK);
5255 setbit(eventmask, BRCMF_E_NDIS_LINK);
5256 setbit(eventmask, BRCMF_E_MIC_ERROR);
5257 setbit(eventmask, BRCMF_E_PMKID_CACHE);
5258 setbit(eventmask, BRCMF_E_TXFAIL);
5259 setbit(eventmask, BRCMF_E_JOIN_START);
5260 setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
Hante Meulemane756af52012-09-11 21:18:52 +02005261 setbit(eventmask, BRCMF_E_ESCAN_RESULT);
Arend van Spriele5806072012-09-19 22:21:08 +02005262 setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005263
Alwin Beukers53a22772011-10-12 20:51:30 +02005264 brcmf_c_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN,
5265 iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005266 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
5267 if (err) {
5268 WL_ERR("Set event_msgs error (%d)\n", err);
5269 goto dongle_eventmsg_out;
5270 }
5271
5272dongle_eventmsg_out:
5273 WL_TRACE("Exit\n");
5274 return err;
5275}
5276
5277static s32
5278brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
5279{
5280 s8 iovbuf[32];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005281 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005282 __le32 roamtrigger[2];
5283 __le32 roam_delta[2];
5284 __le32 bcn_to_le;
5285 __le32 roamvar_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005286
5287 /*
5288 * Setup timeout if Beacons are lost and roam is
5289 * off to report link down
5290 */
5291 if (roamvar) {
Arend van Sprielf588bc02011-10-12 20:51:22 +02005292 bcn_to_le = cpu_to_le32(bcn_timeout);
Alwin Beukers53a22772011-10-12 20:51:30 +02005293 brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_to_le,
Arend van Sprielf588bc02011-10-12 20:51:22 +02005294 sizeof(bcn_to_le), iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005295 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR,
5296 iovbuf, sizeof(iovbuf));
5297 if (err) {
5298 WL_ERR("bcn_timeout error (%d)\n", err);
5299 goto dongle_rom_out;
5300 }
5301 }
5302
5303 /*
5304 * Enable/Disable built-in roaming to allow supplicant
5305 * to take care of roaming
5306 */
5307 WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
Arend van Sprielf588bc02011-10-12 20:51:22 +02005308 roamvar_le = cpu_to_le32(roamvar);
Alwin Beukers53a22772011-10-12 20:51:30 +02005309 brcmf_c_mkiovar("roam_off", (char *)&roamvar_le,
Arend van Sprielf588bc02011-10-12 20:51:22 +02005310 sizeof(roamvar_le), iovbuf, sizeof(iovbuf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005311 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
5312 if (err) {
5313 WL_ERR("roam_off error (%d)\n", err);
5314 goto dongle_rom_out;
5315 }
5316
Arend van Sprielf588bc02011-10-12 20:51:22 +02005317 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5318 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005319 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_TRIGGER,
5320 (void *)roamtrigger, sizeof(roamtrigger));
5321 if (err) {
5322 WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
5323 goto dongle_rom_out;
5324 }
5325
Arend van Sprielf588bc02011-10-12 20:51:22 +02005326 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5327 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005328 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_ROAM_DELTA,
5329 (void *)roam_delta, sizeof(roam_delta));
5330 if (err) {
5331 WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
5332 goto dongle_rom_out;
5333 }
5334
5335dongle_rom_out:
5336 return err;
5337}
5338
5339static s32
5340brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005341 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005342{
5343 s32 err = 0;
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005344 __le32 scan_assoc_tm_le = cpu_to_le32(scan_assoc_time);
5345 __le32 scan_unassoc_tm_le = cpu_to_le32(scan_unassoc_time);
5346 __le32 scan_passive_tm_le = cpu_to_le32(scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005347
5348 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005349 &scan_assoc_tm_le, sizeof(scan_assoc_tm_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005350 if (err) {
5351 if (err == -EOPNOTSUPP)
5352 WL_INFO("Scan assoc time is not supported\n");
5353 else
5354 WL_ERR("Scan assoc time error (%d)\n", err);
5355 goto dongle_scantime_out;
5356 }
5357 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005358 &scan_unassoc_tm_le, sizeof(scan_unassoc_tm_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005359 if (err) {
5360 if (err == -EOPNOTSUPP)
5361 WL_INFO("Scan unassoc time is not supported\n");
5362 else
5363 WL_ERR("Scan unassoc time error (%d)\n", err);
5364 goto dongle_scantime_out;
5365 }
5366
5367 err = brcmf_exec_dcmd(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005368 &scan_passive_tm_le, sizeof(scan_passive_tm_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005369 if (err) {
5370 if (err == -EOPNOTSUPP)
5371 WL_INFO("Scan passive time is not supported\n");
5372 else
5373 WL_ERR("Scan passive time error (%d)\n", err);
5374 goto dongle_scantime_out;
5375 }
5376
5377dongle_scantime_out:
5378 return err;
5379}
5380
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005381static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005382{
5383 struct wiphy *wiphy;
5384 s32 phy_list;
5385 s8 phy;
5386 s32 err = 0;
5387
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005388 err = brcmf_exec_dcmd(cfg_to_ndev(cfg), BRCM_GET_PHYLIST,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005389 &phy_list, sizeof(phy_list));
5390 if (err) {
5391 WL_ERR("error (%d)\n", err);
5392 return err;
5393 }
5394
Hante Meuleman3ba81372012-09-19 22:21:13 +02005395 phy = ((char *)&phy_list)[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005396 WL_INFO("%c phy\n", phy);
5397 if (phy == 'n' || phy == 'a') {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005398 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005399 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
5400 }
5401
5402 return err;
5403}
5404
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005405static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005406{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005407 return wl_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005408}
5409
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005410static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005411{
5412 struct net_device *ndev;
5413 struct wireless_dev *wdev;
5414 s32 power_mode;
5415 s32 err = 0;
5416
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005417 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005418 return err;
5419
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005420 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005421 wdev = ndev->ieee80211_ptr;
5422
5423 brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
5424 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
5425
5426 err = brcmf_dongle_eventmsg(ndev);
5427 if (err)
5428 goto default_conf_out;
5429
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005430 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005431 err = brcmf_exec_dcmd_u32(ndev, BRCMF_C_SET_PM, &power_mode);
5432 if (err)
5433 goto default_conf_out;
5434 WL_INFO("power save set to %s\n",
5435 (power_mode ? "enabled" : "disabled"));
5436
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005437 err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
Arend van Spriel5b435de2011-10-05 13:19:03 +02005438 WL_BEACON_TIMEOUT);
5439 if (err)
5440 goto default_conf_out;
5441 err = brcmf_dongle_mode(ndev, wdev->iftype);
5442 if (err && err != -EINPROGRESS)
5443 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005444 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005445 if (err)
5446 goto default_conf_out;
5447
5448 /* -EINPROGRESS: Call commit handler */
5449
5450default_conf_out:
5451
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005452 cfg->dongle_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005453
5454 return err;
5455
5456}
5457
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005458static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005459{
5460 char buf[10+IFNAMSIZ];
5461 struct dentry *fd;
5462 s32 err = 0;
5463
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005464 sprintf(buf, "netdev:%s", cfg_to_ndev(cfg)->name);
5465 cfg->debugfsdir = debugfs_create_dir(buf,
5466 cfg_to_wiphy(cfg)->debugfsdir);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005467
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005468 fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg->debugfsdir,
5469 (u16 *)&cfg->profile->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005470 if (!fd) {
5471 err = -ENOMEM;
5472 goto err_out;
5473 }
5474
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005475 fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg->debugfsdir,
5476 (u8 *)&cfg->profile->dtim_period);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005477 if (!fd) {
5478 err = -ENOMEM;
5479 goto err_out;
5480 }
5481
5482err_out:
5483 return err;
5484}
5485
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005486static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005487{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005488 debugfs_remove_recursive(cfg->debugfsdir);
5489 cfg->debugfsdir = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005490}
5491
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005492static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005493{
5494 s32 err = 0;
5495
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005496 set_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005497
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005498 brcmf_debugfs_add_netdev_params(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005499
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005500 err = brcmf_config_dongle(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005501 if (err)
5502 return err;
5503
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005504 brcmf_invoke_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005505
5506 return err;
5507}
5508
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005509static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005510{
5511 /*
5512 * While going down, if associated with AP disassociate
5513 * from AP to save power
5514 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005515 if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
5516 test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
5517 test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005518 WL_INFO("Disassociating from AP");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005519 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005520
5521 /* Make sure WPA_Supplicant receives all the event
5522 generated due to DISASSOC call to the fw to keep
5523 the state fw and WPA_Supplicant state consistent
5524 */
5525 brcmf_delay(500);
5526 }
5527
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005528 brcmf_abort_scanning(cfg);
5529 clear_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005530
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005531 brcmf_debugfs_remove_netdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005532
5533 return 0;
5534}
5535
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005536s32 brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005537{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005538 s32 err = 0;
5539
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005540 mutex_lock(&cfg->usr_sync);
5541 err = __brcmf_cfg80211_up(cfg);
5542 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005543
5544 return err;
5545}
5546
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005547s32 brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005548{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005549 s32 err = 0;
5550
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005551 mutex_lock(&cfg->usr_sync);
5552 err = __brcmf_cfg80211_down(cfg);
5553 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005554
5555 return err;
5556}
5557