blob: 0beb2c6db2a064952eb88ec17099fade63c09a67 [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"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070038#include "fwil.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020039
Arend van Spriele5806072012-09-19 22:21:08 +020040#define BRCMF_SCAN_IE_LEN_MAX 2048
41#define BRCMF_PNO_VERSION 2
42#define BRCMF_PNO_TIME 30
43#define BRCMF_PNO_REPEAT 4
44#define BRCMF_PNO_FREQ_EXPO_MAX 3
45#define BRCMF_PNO_MAX_PFN_COUNT 16
46#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
47#define BRCMF_PNO_HIDDEN_BIT 2
48#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
49#define BRCMF_PNO_SCAN_COMPLETE 1
50#define BRCMF_PNO_SCAN_INCOMPLETE 0
51
Hante Meuleman1a873342012-09-27 14:17:54 +020052#define TLV_LEN_OFF 1 /* length offset */
Hante Meuleman04012892012-09-27 14:17:49 +020053#define TLV_HDR_LEN 2 /* header length */
Hante Meuleman1a873342012-09-27 14:17:54 +020054#define TLV_BODY_OFF 2 /* body offset */
55#define TLV_OUI_LEN 3 /* oui id length */
56#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
57#define WPA_OUI_TYPE 1
58#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
59#define WME_OUI_TYPE 2
60
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
75#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
76#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
77
78#define VNDR_IE_CMD_LEN 4 /* length of the set command
79 * string :"add", "del" (+ NUL)
80 */
81#define VNDR_IE_COUNT_OFFSET 4
82#define VNDR_IE_PKTFLAG_OFFSET 8
83#define VNDR_IE_VSIE_OFFSET 12
84#define VNDR_IE_HDR_SIZE 12
85#define VNDR_IE_BEACON_FLAG 0x1
86#define VNDR_IE_PRBRSP_FLAG 0x2
87#define MAX_VNDR_IE_NUMBER 5
88
89#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
90#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020091
Arend van Spriel5b435de2011-10-05 13:19:03 +020092#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
93 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
94
95static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
96
97static u32 brcmf_dbg_level = WL_DBG_ERR;
98
Arend van Spriel5b435de2011-10-05 13:19:03 +020099static bool check_sys_up(struct wiphy *wiphy)
100{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200101 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
102 if (!test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200103 WL_INFO("device is not ready : status (%d)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200104 (int)cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200105 return false;
106 }
107 return true;
108}
109
110#define CHAN2G(_channel, _freq, _flags) { \
111 .band = IEEE80211_BAND_2GHZ, \
112 .center_freq = (_freq), \
113 .hw_value = (_channel), \
114 .flags = (_flags), \
115 .max_antenna_gain = 0, \
116 .max_power = 30, \
117}
118
119#define CHAN5G(_channel, _flags) { \
120 .band = IEEE80211_BAND_5GHZ, \
121 .center_freq = 5000 + (5 * (_channel)), \
122 .hw_value = (_channel), \
123 .flags = (_flags), \
124 .max_antenna_gain = 0, \
125 .max_power = 30, \
126}
127
128#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
129#define RATETAB_ENT(_rateid, _flags) \
130 { \
131 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
132 .hw_value = (_rateid), \
133 .flags = (_flags), \
134 }
135
136static struct ieee80211_rate __wl_rates[] = {
137 RATETAB_ENT(BRCM_RATE_1M, 0),
138 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
139 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
140 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
141 RATETAB_ENT(BRCM_RATE_6M, 0),
142 RATETAB_ENT(BRCM_RATE_9M, 0),
143 RATETAB_ENT(BRCM_RATE_12M, 0),
144 RATETAB_ENT(BRCM_RATE_18M, 0),
145 RATETAB_ENT(BRCM_RATE_24M, 0),
146 RATETAB_ENT(BRCM_RATE_36M, 0),
147 RATETAB_ENT(BRCM_RATE_48M, 0),
148 RATETAB_ENT(BRCM_RATE_54M, 0),
149};
150
151#define wl_a_rates (__wl_rates + 4)
152#define wl_a_rates_size 8
153#define wl_g_rates (__wl_rates + 0)
154#define wl_g_rates_size 12
155
156static struct ieee80211_channel __wl_2ghz_channels[] = {
157 CHAN2G(1, 2412, 0),
158 CHAN2G(2, 2417, 0),
159 CHAN2G(3, 2422, 0),
160 CHAN2G(4, 2427, 0),
161 CHAN2G(5, 2432, 0),
162 CHAN2G(6, 2437, 0),
163 CHAN2G(7, 2442, 0),
164 CHAN2G(8, 2447, 0),
165 CHAN2G(9, 2452, 0),
166 CHAN2G(10, 2457, 0),
167 CHAN2G(11, 2462, 0),
168 CHAN2G(12, 2467, 0),
169 CHAN2G(13, 2472, 0),
170 CHAN2G(14, 2484, 0),
171};
172
173static struct ieee80211_channel __wl_5ghz_a_channels[] = {
174 CHAN5G(34, 0), CHAN5G(36, 0),
175 CHAN5G(38, 0), CHAN5G(40, 0),
176 CHAN5G(42, 0), CHAN5G(44, 0),
177 CHAN5G(46, 0), CHAN5G(48, 0),
178 CHAN5G(52, 0), CHAN5G(56, 0),
179 CHAN5G(60, 0), CHAN5G(64, 0),
180 CHAN5G(100, 0), CHAN5G(104, 0),
181 CHAN5G(108, 0), CHAN5G(112, 0),
182 CHAN5G(116, 0), CHAN5G(120, 0),
183 CHAN5G(124, 0), CHAN5G(128, 0),
184 CHAN5G(132, 0), CHAN5G(136, 0),
185 CHAN5G(140, 0), CHAN5G(149, 0),
186 CHAN5G(153, 0), CHAN5G(157, 0),
187 CHAN5G(161, 0), CHAN5G(165, 0),
188 CHAN5G(184, 0), CHAN5G(188, 0),
189 CHAN5G(192, 0), CHAN5G(196, 0),
190 CHAN5G(200, 0), CHAN5G(204, 0),
191 CHAN5G(208, 0), CHAN5G(212, 0),
192 CHAN5G(216, 0),
193};
194
195static struct ieee80211_channel __wl_5ghz_n_channels[] = {
196 CHAN5G(32, 0), CHAN5G(34, 0),
197 CHAN5G(36, 0), CHAN5G(38, 0),
198 CHAN5G(40, 0), CHAN5G(42, 0),
199 CHAN5G(44, 0), CHAN5G(46, 0),
200 CHAN5G(48, 0), CHAN5G(50, 0),
201 CHAN5G(52, 0), CHAN5G(54, 0),
202 CHAN5G(56, 0), CHAN5G(58, 0),
203 CHAN5G(60, 0), CHAN5G(62, 0),
204 CHAN5G(64, 0), CHAN5G(66, 0),
205 CHAN5G(68, 0), CHAN5G(70, 0),
206 CHAN5G(72, 0), CHAN5G(74, 0),
207 CHAN5G(76, 0), CHAN5G(78, 0),
208 CHAN5G(80, 0), CHAN5G(82, 0),
209 CHAN5G(84, 0), CHAN5G(86, 0),
210 CHAN5G(88, 0), CHAN5G(90, 0),
211 CHAN5G(92, 0), CHAN5G(94, 0),
212 CHAN5G(96, 0), CHAN5G(98, 0),
213 CHAN5G(100, 0), CHAN5G(102, 0),
214 CHAN5G(104, 0), CHAN5G(106, 0),
215 CHAN5G(108, 0), CHAN5G(110, 0),
216 CHAN5G(112, 0), CHAN5G(114, 0),
217 CHAN5G(116, 0), CHAN5G(118, 0),
218 CHAN5G(120, 0), CHAN5G(122, 0),
219 CHAN5G(124, 0), CHAN5G(126, 0),
220 CHAN5G(128, 0), CHAN5G(130, 0),
221 CHAN5G(132, 0), CHAN5G(134, 0),
222 CHAN5G(136, 0), CHAN5G(138, 0),
223 CHAN5G(140, 0), CHAN5G(142, 0),
224 CHAN5G(144, 0), CHAN5G(145, 0),
225 CHAN5G(146, 0), CHAN5G(147, 0),
226 CHAN5G(148, 0), CHAN5G(149, 0),
227 CHAN5G(150, 0), CHAN5G(151, 0),
228 CHAN5G(152, 0), CHAN5G(153, 0),
229 CHAN5G(154, 0), CHAN5G(155, 0),
230 CHAN5G(156, 0), CHAN5G(157, 0),
231 CHAN5G(158, 0), CHAN5G(159, 0),
232 CHAN5G(160, 0), CHAN5G(161, 0),
233 CHAN5G(162, 0), CHAN5G(163, 0),
234 CHAN5G(164, 0), CHAN5G(165, 0),
235 CHAN5G(166, 0), CHAN5G(168, 0),
236 CHAN5G(170, 0), CHAN5G(172, 0),
237 CHAN5G(174, 0), CHAN5G(176, 0),
238 CHAN5G(178, 0), CHAN5G(180, 0),
239 CHAN5G(182, 0), CHAN5G(184, 0),
240 CHAN5G(186, 0), CHAN5G(188, 0),
241 CHAN5G(190, 0), CHAN5G(192, 0),
242 CHAN5G(194, 0), CHAN5G(196, 0),
243 CHAN5G(198, 0), CHAN5G(200, 0),
244 CHAN5G(202, 0), CHAN5G(204, 0),
245 CHAN5G(206, 0), CHAN5G(208, 0),
246 CHAN5G(210, 0), CHAN5G(212, 0),
247 CHAN5G(214, 0), CHAN5G(216, 0),
248 CHAN5G(218, 0), CHAN5G(220, 0),
249 CHAN5G(222, 0), CHAN5G(224, 0),
250 CHAN5G(226, 0), CHAN5G(228, 0),
251};
252
253static struct ieee80211_supported_band __wl_band_2ghz = {
254 .band = IEEE80211_BAND_2GHZ,
255 .channels = __wl_2ghz_channels,
256 .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
257 .bitrates = wl_g_rates,
258 .n_bitrates = wl_g_rates_size,
259};
260
261static struct ieee80211_supported_band __wl_band_5ghz_a = {
262 .band = IEEE80211_BAND_5GHZ,
263 .channels = __wl_5ghz_a_channels,
264 .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
265 .bitrates = wl_a_rates,
266 .n_bitrates = wl_a_rates_size,
267};
268
269static struct ieee80211_supported_band __wl_band_5ghz_n = {
270 .band = IEEE80211_BAND_5GHZ,
271 .channels = __wl_5ghz_n_channels,
272 .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
273 .bitrates = wl_a_rates,
274 .n_bitrates = wl_a_rates_size,
275};
276
277static const u32 __wl_cipher_suites[] = {
278 WLAN_CIPHER_SUITE_WEP40,
279 WLAN_CIPHER_SUITE_WEP104,
280 WLAN_CIPHER_SUITE_TKIP,
281 WLAN_CIPHER_SUITE_CCMP,
282 WLAN_CIPHER_SUITE_AES_CMAC,
283};
284
Alwin Beukersf8e4b412011-10-12 20:51:28 +0200285/* tag_ID/length/value_buffer tuple */
286struct brcmf_tlv {
287 u8 id;
288 u8 len;
289 u8 data[1];
290};
291
Hante Meuleman1a873342012-09-27 14:17:54 +0200292/* Vendor specific ie. id = 221, oui and type defines exact ie */
293struct brcmf_vs_tlv {
294 u8 id;
295 u8 len;
296 u8 oui[3];
297 u8 oui_type;
298};
299
300struct parsed_vndr_ie_info {
301 u8 *ie_ptr;
302 u32 ie_len; /* total length including id & length field */
303 struct brcmf_vs_tlv vndrie;
304};
305
306struct parsed_vndr_ies {
307 u32 count;
308 struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER];
309};
310
Alwin Beukersef6ac172011-10-12 20:51:26 +0200311/* Quarter dBm units to mW
312 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
313 * Table is offset so the last entry is largest mW value that fits in
314 * a u16.
315 */
316
317#define QDBM_OFFSET 153 /* Offset for first entry */
318#define QDBM_TABLE_LEN 40 /* Table size */
319
320/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
321 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
322 */
323#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
324
325/* Largest mW value that will round down to the last table entry,
326 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
327 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
328 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
329 */
330#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
331
332static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
333/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
334/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
335/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
336/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
337/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
338/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
339};
340
341static u16 brcmf_qdbm_to_mw(u8 qdbm)
342{
343 uint factor = 1;
344 int idx = qdbm - QDBM_OFFSET;
345
346 if (idx >= QDBM_TABLE_LEN)
347 /* clamp to max u16 mW value */
348 return 0xFFFF;
349
350 /* scale the qdBm index up to the range of the table 0-40
351 * where an offset of 40 qdBm equals a factor of 10 mW.
352 */
353 while (idx < 0) {
354 idx += 40;
355 factor *= 10;
356 }
357
358 /* return the mW value scaled down to the correct factor of 10,
359 * adding in factor/2 to get proper rounding.
360 */
361 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
362}
363
364static u8 brcmf_mw_to_qdbm(u16 mw)
365{
366 u8 qdbm;
367 int offset;
368 uint mw_uint = mw;
369 uint boundary;
370
371 /* handle boundary case */
372 if (mw_uint <= 1)
373 return 0;
374
375 offset = QDBM_OFFSET;
376
377 /* move mw into the range of the table */
378 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
379 mw_uint *= 10;
380 offset -= 40;
381 }
382
383 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
384 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
385 nqdBm_to_mW_map[qdbm]) / 2;
386 if (mw_uint < boundary)
387 break;
388 }
389
390 qdbm += (u8) offset;
391
392 return qdbm;
393}
394
Arend van Spriel5b435de2011-10-05 13:19:03 +0200395static void convert_key_from_CPU(struct brcmf_wsec_key *key,
396 struct brcmf_wsec_key_le *key_le)
397{
398 key_le->index = cpu_to_le32(key->index);
399 key_le->len = cpu_to_le32(key->len);
400 key_le->algo = cpu_to_le32(key->algo);
401 key_le->flags = cpu_to_le32(key->flags);
402 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
403 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
404 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
405 memcpy(key_le->data, key->data, sizeof(key->data));
406 memcpy(key_le->ea, key->ea, sizeof(key->ea));
407}
408
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200409static int
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200410send_key_to_dongle(struct brcmf_cfg80211_info *cfg, s32 bssidx,
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200411 struct net_device *ndev, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200412{
413 int err;
414 struct brcmf_wsec_key_le key_le;
415
416 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200417
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700418 brcmf_netdev_wait_pend8021x(ndev);
419
420 err = brcmf_fil_bsscfg_data_set(ndev, bssidx, "wsec_key", &key_le,
421 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200422
Arend van Spriel5b435de2011-10-05 13:19:03 +0200423 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200424 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200425 return err;
426}
427
428static s32
429brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
430 enum nl80211_iftype type, u32 *flags,
431 struct vif_params *params)
432{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200433 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200434 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200435 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200436 s32 err = 0;
437
Hante Meuleman1a873342012-09-27 14:17:54 +0200438 WL_TRACE("Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200439
440 switch (type) {
441 case NL80211_IFTYPE_MONITOR:
442 case NL80211_IFTYPE_WDS:
443 WL_ERR("type (%d) : currently we do not support this type\n",
444 type);
445 return -EOPNOTSUPP;
446 case NL80211_IFTYPE_ADHOC:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200447 cfg->conf->mode = WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200448 infra = 0;
449 break;
450 case NL80211_IFTYPE_STATION:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200451 cfg->conf->mode = WL_MODE_BSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200452 infra = 1;
453 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200454 case NL80211_IFTYPE_AP:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200455 cfg->conf->mode = WL_MODE_AP;
Hante Meuleman1a873342012-09-27 14:17:54 +0200456 ap = 1;
457 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200458 default:
459 err = -EINVAL;
460 goto done;
461 }
462
Hante Meuleman1a873342012-09-27 14:17:54 +0200463 if (ap) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200464 set_bit(WL_STATUS_AP_CREATING, &cfg->status);
465 if (!cfg->ap_info)
466 cfg->ap_info = kzalloc(sizeof(*cfg->ap_info),
467 GFP_KERNEL);
468 if (!cfg->ap_info) {
Hante Meuleman1a873342012-09-27 14:17:54 +0200469 err = -ENOMEM;
470 goto done;
471 }
472 WL_INFO("IF Type = AP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200473 } else {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700474 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200475 if (err) {
476 WL_ERR("WLC_SET_INFRA error (%d)\n", err);
477 err = -EAGAIN;
478 goto done;
479 }
480 WL_INFO("IF Type = %s\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200481 (cfg->conf->mode == WL_MODE_IBSS) ?
Hante Meuleman1a873342012-09-27 14:17:54 +0200482 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200483 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200484 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200485
486done:
487 WL_TRACE("Exit\n");
488
489 return err;
490}
491
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200492/*
493 * For now brcmf_find_bssidx will return 0. Once p2p gets implemented this
494 * should return the ndev matching bssidx.
495 */
496static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200497brcmf_find_bssidx(struct brcmf_cfg80211_info *cfg, struct net_device *ndev)
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200498{
499 return 0;
500}
501
Arend van Spriel5b435de2011-10-05 13:19:03 +0200502static void brcmf_set_mpc(struct net_device *ndev, int mpc)
503{
504 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200505 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200506
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200507 if (test_bit(WL_STATUS_READY, &cfg->status)) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700508 err = brcmf_fil_iovar_int_set(ndev, "mpc", mpc);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200509 if (err) {
510 WL_ERR("fail to set mpc\n");
511 return;
512 }
513 WL_INFO("MPC : %d\n", mpc);
514 }
515}
516
Hante Meuleman35aafa92012-09-11 21:18:49 +0200517static void brcmf_iscan_prep(struct brcmf_scan_params_le *params_le,
518 struct brcmf_ssid *ssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200519{
520 memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
521 params_le->bss_type = DOT11_BSSTYPE_ANY;
522 params_le->scan_type = 0;
523 params_le->channel_num = 0;
524 params_le->nprobes = cpu_to_le32(-1);
525 params_le->active_time = cpu_to_le32(-1);
526 params_le->passive_time = cpu_to_le32(-1);
527 params_le->home_time = cpu_to_le32(-1);
Hante Meulemaned205b32012-09-11 21:16:47 +0200528 if (ssid && ssid->SSID_len) {
529 params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len);
530 memcpy(&params_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len);
531 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200532}
533
534static s32
Arend van Spriel5b435de2011-10-05 13:19:03 +0200535brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
536 struct brcmf_ssid *ssid, u16 action)
537{
538 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
539 offsetof(struct brcmf_iscan_params_le, params_le);
540 struct brcmf_iscan_params_le *params;
541 s32 err = 0;
542
543 if (ssid && ssid->SSID_len)
544 params_size += sizeof(struct brcmf_ssid);
545 params = kzalloc(params_size, GFP_KERNEL);
546 if (!params)
547 return -ENOMEM;
548 BUG_ON(params_size >= BRCMF_DCMD_SMLEN);
549
Hante Meuleman35aafa92012-09-11 21:18:49 +0200550 brcmf_iscan_prep(&params->params_le, ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200551
552 params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION);
553 params->action = cpu_to_le16(action);
554 params->scan_duration = cpu_to_le16(0);
555
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700556 err = brcmf_fil_iovar_data_set(iscan->ndev, "iscan", params,
557 params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200558 if (err) {
559 if (err == -EBUSY)
560 WL_INFO("system busy : iscan canceled\n");
561 else
562 WL_ERR("error (%d)\n", err);
563 }
564
565 kfree(params);
566 return err;
567}
568
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200569static s32 brcmf_do_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200570{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200571 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
572 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200573 struct brcmf_ssid ssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700574 u32 passive_scan;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200575 s32 err = 0;
576
577 /* Broadcast scan by default */
578 memset(&ssid, 0, sizeof(ssid));
579
580 iscan->state = WL_ISCAN_STATE_SCANING;
581
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700582 passive_scan = cfg->active_scan ? 0 : 1;
583 err = brcmf_fil_cmd_int_set(cfg_to_ndev(cfg),
584 BRCMF_C_SET_PASSIVE_SCAN, passive_scan);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200585 if (err) {
586 WL_ERR("error (%d)\n", err);
587 return err;
588 }
589 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200590 cfg->iscan_kickstart = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200591 err = brcmf_run_iscan(iscan, &ssid, BRCMF_SCAN_ACTION_START);
592 if (err) {
593 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200594 cfg->iscan_kickstart = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200595 return err;
596 }
597 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
598 iscan->timer_on = 1;
599 return err;
600}
601
602static s32
Hante Meuleman35aafa92012-09-11 21:18:49 +0200603brcmf_cfg80211_iscan(struct wiphy *wiphy, struct net_device *ndev,
604 struct cfg80211_scan_request *request,
605 struct cfg80211_ssid *this_ssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200606{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200607 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200608 struct cfg80211_ssid *ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200609 struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700610 u32 passive_scan;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200611 bool iscan_req;
612 bool spec_scan;
613 s32 err = 0;
614 u32 SSID_len;
615
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200616 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
617 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200618 return -EAGAIN;
619 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200620 if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200621 WL_ERR("Scanning being aborted : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200622 cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200623 return -EAGAIN;
624 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200625 if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200626 WL_ERR("Connecting : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200627 cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200628 return -EAGAIN;
629 }
630
631 iscan_req = false;
632 spec_scan = false;
633 if (request) {
634 /* scan bss */
635 ssids = request->ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200636 if (cfg->iscan_on && (!ssids || !ssids->ssid_len))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200637 iscan_req = true;
638 } else {
639 /* scan in ibss */
640 /* we don't do iscan in ibss */
641 ssids = this_ssid;
642 }
643
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200644 cfg->scan_request = request;
645 set_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200646 if (iscan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200647 err = brcmf_do_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200648 if (!err)
649 return err;
650 else
651 goto scan_out;
652 } else {
653 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
654 ssids->ssid, ssids->ssid_len);
655 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
656 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
657 sr->ssid_le.SSID_len = cpu_to_le32(0);
658 if (SSID_len) {
659 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
660 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
661 spec_scan = true;
662 } else {
663 WL_SCAN("Broadcast scan\n");
664 }
665
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700666 passive_scan = cfg->active_scan ? 0 : 1;
667 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_PASSIVE_SCAN,
668 passive_scan);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200669 if (err) {
670 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
671 goto scan_out;
672 }
673 brcmf_set_mpc(ndev, 0);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700674 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SCAN, &sr->ssid_le,
675 sizeof(sr->ssid_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +0200676 if (err) {
677 if (err == -EBUSY)
678 WL_INFO("system busy : scan for \"%s\" "
679 "canceled\n", sr->ssid_le.SSID);
680 else
681 WL_ERR("WLC_SCAN error (%d)\n", err);
682
683 brcmf_set_mpc(ndev, 1);
684 goto scan_out;
685 }
686 }
687
688 return 0;
689
690scan_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200691 clear_bit(WL_STATUS_SCANNING, &cfg->status);
692 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200693 return err;
694}
695
Hante Meulemane756af52012-09-11 21:18:52 +0200696static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
697 struct cfg80211_scan_request *request)
698{
699 u32 n_ssids;
700 u32 n_channels;
701 s32 i;
702 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200703 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200704 u16 channel;
705 struct ieee80211_channel *req_channel;
706 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200707 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200708
709 memcpy(params_le->bssid, ether_bcast, ETH_ALEN);
710 params_le->bss_type = DOT11_BSSTYPE_ANY;
711 params_le->scan_type = 0;
712 params_le->channel_num = 0;
713 params_le->nprobes = cpu_to_le32(-1);
714 params_le->active_time = cpu_to_le32(-1);
715 params_le->passive_time = cpu_to_le32(-1);
716 params_le->home_time = cpu_to_le32(-1);
717 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
718
719 /* if request is null exit so it will be all channel broadcast scan */
720 if (!request)
721 return;
722
723 n_ssids = request->n_ssids;
724 n_channels = request->n_channels;
725 /* Copy channel array if applicable */
726 WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
727 if (n_channels > 0) {
728 for (i = 0; i < n_channels; i++) {
729 chanspec = 0;
730 req_channel = request->channels[i];
731 channel = ieee80211_frequency_to_channel(
732 req_channel->center_freq);
733 if (req_channel->band == IEEE80211_BAND_2GHZ)
734 chanspec |= WL_CHANSPEC_BAND_2G;
735 else
736 chanspec |= WL_CHANSPEC_BAND_5G;
737
738 if (req_channel->flags & IEEE80211_CHAN_NO_HT40) {
739 chanspec |= WL_CHANSPEC_BW_20;
740 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
741 } else {
742 chanspec |= WL_CHANSPEC_BW_40;
743 if (req_channel->flags &
744 IEEE80211_CHAN_NO_HT40PLUS)
745 chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
746 else
747 chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
748 }
749
Arend van Spriel029591f2012-09-19 22:21:06 +0200750 chanspec |= (channel & WL_CHANSPEC_CHAN_MASK);
Hante Meulemane756af52012-09-11 21:18:52 +0200751 WL_SCAN("Chan : %d, Channel spec: %x\n",
Arend van Spriel029591f2012-09-19 22:21:06 +0200752 channel, chanspec);
753 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200754 }
755 } else {
756 WL_SCAN("Scanning all channels\n");
757 }
758 /* Copy ssid array if applicable */
759 WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids);
760 if (n_ssids > 0) {
761 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
762 n_channels * sizeof(u16);
763 offset = roundup(offset, sizeof(u32));
764 ptr = (char *)params_le + offset;
765 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200766 memset(&ssid_le, 0, sizeof(ssid_le));
767 ssid_le.SSID_len =
768 cpu_to_le32(request->ssids[i].ssid_len);
769 memcpy(ssid_le.SSID, request->ssids[i].ssid,
770 request->ssids[i].ssid_len);
771 if (!ssid_le.SSID_len)
Hante Meulemane756af52012-09-11 21:18:52 +0200772 WL_SCAN("%d: Broadcast scan\n", i);
773 else
774 WL_SCAN("%d: scan for %s size =%d\n", i,
Arend van Spriel029591f2012-09-19 22:21:06 +0200775 ssid_le.SSID, ssid_le.SSID_len);
776 memcpy(ptr, &ssid_le, sizeof(ssid_le));
777 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200778 }
779 } else {
780 WL_SCAN("Broadcast scan %p\n", request->ssids);
781 if ((request->ssids) && request->ssids->ssid_len) {
782 WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID,
783 request->ssids->ssid_len);
784 params_le->ssid_le.SSID_len =
785 cpu_to_le32(request->ssids->ssid_len);
786 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
787 request->ssids->ssid_len);
788 }
789 }
790 /* Adding mask to channel numbers */
791 params_le->channel_num =
792 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
793 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
794}
795
796static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200797brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +0200798 struct net_device *ndev,
799 bool aborted, bool fw_abort)
800{
801 struct brcmf_scan_params_le params_le;
802 struct cfg80211_scan_request *scan_request;
803 s32 err = 0;
804
805 WL_SCAN("Enter\n");
806
807 /* clear scan request, because the FW abort can cause a second call */
808 /* to this functon and might cause a double cfg80211_scan_done */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200809 scan_request = cfg->scan_request;
810 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200811
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200812 if (timer_pending(&cfg->escan_timeout))
813 del_timer_sync(&cfg->escan_timeout);
Hante Meulemane756af52012-09-11 21:18:52 +0200814
815 if (fw_abort) {
816 /* Do a scan abort to stop the driver's scan engine */
817 WL_SCAN("ABORT scan in firmware\n");
818 memset(&params_le, 0, sizeof(params_le));
819 memcpy(params_le.bssid, ether_bcast, ETH_ALEN);
820 params_le.bss_type = DOT11_BSSTYPE_ANY;
821 params_le.scan_type = 0;
822 params_le.channel_num = cpu_to_le32(1);
823 params_le.nprobes = cpu_to_le32(1);
824 params_le.active_time = cpu_to_le32(-1);
825 params_le.passive_time = cpu_to_le32(-1);
826 params_le.home_time = cpu_to_le32(-1);
827 /* Scan is aborted by setting channel_list[0] to -1 */
828 params_le.channel_list[0] = cpu_to_le16(-1);
829 /* E-Scan (or anyother type) can be aborted by SCAN */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700830 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SCAN, &params_le,
831 sizeof(params_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200832 if (err)
833 WL_ERR("Scan abort failed\n");
834 }
Arend van Spriele5806072012-09-19 22:21:08 +0200835 /*
836 * e-scan can be initiated by scheduled scan
837 * which takes precedence.
838 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200839 if (cfg->sched_escan) {
Arend van Spriele5806072012-09-19 22:21:08 +0200840 WL_SCAN("scheduled scan completed\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200841 cfg->sched_escan = false;
Arend van Spriele5806072012-09-19 22:21:08 +0200842 if (!aborted)
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200843 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriele5806072012-09-19 22:21:08 +0200844 brcmf_set_mpc(ndev, 1);
845 } else if (scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +0200846 WL_SCAN("ESCAN Completed scan: %s\n",
847 aborted ? "Aborted" : "Done");
848 cfg80211_scan_done(scan_request, aborted);
849 brcmf_set_mpc(ndev, 1);
850 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200851 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +0200852 WL_ERR("Scan complete while device not scanning\n");
853 return -EPERM;
854 }
855
856 return err;
857}
858
859static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200860brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
Hante Meulemane756af52012-09-11 21:18:52 +0200861 struct cfg80211_scan_request *request, u16 action)
862{
863 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
864 offsetof(struct brcmf_escan_params_le, params_le);
865 struct brcmf_escan_params_le *params;
866 s32 err = 0;
867
868 WL_SCAN("E-SCAN START\n");
869
870 if (request != NULL) {
871 /* Allocate space for populating ssids in struct */
872 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
873
874 /* Allocate space for populating ssids in struct */
875 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
876 }
877
878 params = kzalloc(params_size, GFP_KERNEL);
879 if (!params) {
880 err = -ENOMEM;
881 goto exit;
882 }
883 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
884 brcmf_escan_prep(&params->params_le, request);
885 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
886 params->action = cpu_to_le16(action);
887 params->sync_id = cpu_to_le16(0x1234);
888
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700889 err = brcmf_fil_iovar_data_set(ndev, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200890 if (err) {
891 if (err == -EBUSY)
892 WL_INFO("system busy : escan canceled\n");
893 else
894 WL_ERR("error (%d)\n", err);
895 }
896
897 kfree(params);
898exit:
899 return err;
900}
901
902static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200903brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Hante Meulemane756af52012-09-11 21:18:52 +0200904 struct net_device *ndev, struct cfg80211_scan_request *request)
905{
906 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700907 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200908 struct brcmf_scan_results *results;
909
910 WL_SCAN("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200911 cfg->escan_info.ndev = ndev;
912 cfg->escan_info.wiphy = wiphy;
913 cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700914 passive_scan = cfg->active_scan ? 0 : 1;
915 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_PASSIVE_SCAN,
916 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200917 if (err) {
918 WL_ERR("error (%d)\n", err);
919 return err;
920 }
921 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200922 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +0200923 results->version = 0;
924 results->count = 0;
925 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
926
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200927 err = brcmf_run_escan(cfg, ndev, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200928 if (err)
929 brcmf_set_mpc(ndev, 1);
930 return err;
931}
932
933static s32
934brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
935 struct cfg80211_scan_request *request,
936 struct cfg80211_ssid *this_ssid)
937{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200938 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Hante Meulemane756af52012-09-11 21:18:52 +0200939 struct cfg80211_ssid *ssids;
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200940 struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700941 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200942 bool escan_req;
943 bool spec_scan;
944 s32 err;
945 u32 SSID_len;
946
947 WL_SCAN("START ESCAN\n");
948
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200949 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
950 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +0200951 return -EAGAIN;
952 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200953 if (test_bit(WL_STATUS_SCAN_ABORTING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +0200954 WL_ERR("Scanning being aborted : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200955 cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +0200956 return -EAGAIN;
957 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200958 if (test_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +0200959 WL_ERR("Connecting : status (%lu)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200960 cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +0200961 return -EAGAIN;
962 }
963
964 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200965 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +0200966 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
967
968 escan_req = false;
969 if (request) {
970 /* scan bss */
971 ssids = request->ssids;
972 escan_req = true;
973 } else {
974 /* scan in ibss */
975 /* we don't do escan in ibss */
976 ssids = this_ssid;
977 }
978
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200979 cfg->scan_request = request;
980 set_bit(WL_STATUS_SCANNING, &cfg->status);
Hante Meulemane756af52012-09-11 21:18:52 +0200981 if (escan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200982 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200983 if (!err)
984 return err;
985 else
986 goto scan_out;
987 } else {
988 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
989 ssids->ssid, ssids->ssid_len);
990 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
991 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
992 sr->ssid_le.SSID_len = cpu_to_le32(0);
993 spec_scan = false;
994 if (SSID_len) {
995 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
996 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
997 spec_scan = true;
998 } else
999 WL_SCAN("Broadcast scan\n");
1000
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001001 passive_scan = cfg->active_scan ? 0 : 1;
1002 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_PASSIVE_SCAN,
1003 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001004 if (err) {
1005 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
1006 goto scan_out;
1007 }
1008 brcmf_set_mpc(ndev, 0);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001009 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SCAN, &sr->ssid_le,
1010 sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001011 if (err) {
1012 if (err == -EBUSY)
1013 WL_INFO("BUSY: scan for \"%s\" canceled\n",
1014 sr->ssid_le.SSID);
1015 else
1016 WL_ERR("WLC_SCAN error (%d)\n", err);
1017
1018 brcmf_set_mpc(ndev, 1);
1019 goto scan_out;
1020 }
1021 }
1022
1023 return 0;
1024
1025scan_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001026 clear_bit(WL_STATUS_SCANNING, &cfg->status);
1027 if (timer_pending(&cfg->escan_timeout))
1028 del_timer_sync(&cfg->escan_timeout);
1029 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001030 return err;
1031}
1032
Arend van Spriel5b435de2011-10-05 13:19:03 +02001033static s32
Johannes Bergfd014282012-06-18 19:17:03 +02001034brcmf_cfg80211_scan(struct wiphy *wiphy,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001035 struct cfg80211_scan_request *request)
1036{
Johannes Bergfd014282012-06-18 19:17:03 +02001037 struct net_device *ndev = request->wdev->netdev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001038 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001039 s32 err = 0;
1040
1041 WL_TRACE("Enter\n");
1042
1043 if (!check_sys_up(wiphy))
1044 return -EIO;
1045
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001046 if (cfg->iscan_on)
Hante Meulemane756af52012-09-11 21:18:52 +02001047 err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001048 else if (cfg->escan_on)
Hante Meulemane756af52012-09-11 21:18:52 +02001049 err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
1050
Arend van Spriel5b435de2011-10-05 13:19:03 +02001051 if (err)
1052 WL_ERR("scan error (%d)\n", err);
1053
1054 WL_TRACE("Exit\n");
1055 return err;
1056}
1057
1058static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1059{
1060 s32 err = 0;
1061
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001062 err = brcmf_fil_iovar_int_set(ndev, "rtsthresh", rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001063 if (err)
1064 WL_ERR("Error (%d)\n", err);
1065
1066 return err;
1067}
1068
1069static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1070{
1071 s32 err = 0;
1072
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001073 err = brcmf_fil_iovar_int_set(ndev, "fragthresh", frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001074 if (err)
1075 WL_ERR("Error (%d)\n", err);
1076
1077 return err;
1078}
1079
1080static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1081{
1082 s32 err = 0;
1083 u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL);
1084
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001085 err = brcmf_fil_cmd_int_set(ndev, cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001086 if (err) {
1087 WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
1088 return err;
1089 }
1090 return err;
1091}
1092
1093static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1094{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001095 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1096 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001097 s32 err = 0;
1098
1099 WL_TRACE("Enter\n");
1100 if (!check_sys_up(wiphy))
1101 return -EIO;
1102
1103 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001104 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1105 cfg->conf->rts_threshold = wiphy->rts_threshold;
1106 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001107 if (!err)
1108 goto done;
1109 }
1110 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001111 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1112 cfg->conf->frag_threshold = wiphy->frag_threshold;
1113 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001114 if (!err)
1115 goto done;
1116 }
1117 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001118 && (cfg->conf->retry_long != wiphy->retry_long)) {
1119 cfg->conf->retry_long = wiphy->retry_long;
1120 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001121 if (!err)
1122 goto done;
1123 }
1124 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001125 && (cfg->conf->retry_short != wiphy->retry_short)) {
1126 cfg->conf->retry_short = wiphy->retry_short;
1127 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001128 if (!err)
1129 goto done;
1130 }
1131
1132done:
1133 WL_TRACE("Exit\n");
1134 return err;
1135}
1136
Arend van Spriel5b435de2011-10-05 13:19:03 +02001137static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1138{
1139 memset(prof, 0, sizeof(*prof));
1140}
1141
1142static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
1143 size_t *join_params_size)
1144{
1145 u16 chanspec = 0;
1146
1147 if (ch != 0) {
1148 if (ch <= CH_MAX_2G_CHANNEL)
1149 chanspec |= WL_CHANSPEC_BAND_2G;
1150 else
1151 chanspec |= WL_CHANSPEC_BAND_5G;
1152
1153 chanspec |= WL_CHANSPEC_BW_20;
1154 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
1155
1156 *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
1157 sizeof(u16);
1158
1159 chanspec |= (ch & WL_CHANSPEC_CHAN_MASK);
1160 join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1161 join_params->params_le.chanspec_num = cpu_to_le32(1);
1162
1163 WL_CONN("join_params->params.chanspec_list[0]= %#X,"
1164 "channel %d, chanspec %#X\n",
1165 chanspec, ch, chanspec);
1166 }
1167}
1168
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001169static void brcmf_link_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001170{
1171 struct net_device *ndev = NULL;
1172 s32 err = 0;
1173
1174 WL_TRACE("Enter\n");
1175
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001176 if (cfg->link_up) {
1177 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001178 WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001179 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001180 if (err)
1181 WL_ERR("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001182 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001183 }
1184 WL_TRACE("Exit\n");
1185}
1186
1187static s32
1188brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1189 struct cfg80211_ibss_params *params)
1190{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001191 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001192 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001193 struct brcmf_join_params join_params;
1194 size_t join_params_size = 0;
1195 s32 err = 0;
1196 s32 wsec = 0;
1197 s32 bcnprd;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001198
1199 WL_TRACE("Enter\n");
1200 if (!check_sys_up(wiphy))
1201 return -EIO;
1202
1203 if (params->ssid)
1204 WL_CONN("SSID: %s\n", params->ssid);
1205 else {
1206 WL_CONN("SSID: NULL, Not supported\n");
1207 return -EOPNOTSUPP;
1208 }
1209
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001210 set_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001211
1212 if (params->bssid)
Andy Shevchenko040a7832012-07-12 10:43:44 +03001213 WL_CONN("BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001214 else
1215 WL_CONN("No BSSID specified\n");
1216
1217 if (params->channel)
1218 WL_CONN("channel: %d\n", params->channel->center_freq);
1219 else
1220 WL_CONN("no channel specified\n");
1221
1222 if (params->channel_fixed)
1223 WL_CONN("fixed channel required\n");
1224 else
1225 WL_CONN("no fixed channel required\n");
1226
1227 if (params->ie && params->ie_len)
1228 WL_CONN("ie len: %d\n", params->ie_len);
1229 else
1230 WL_CONN("no ie specified\n");
1231
1232 if (params->beacon_interval)
1233 WL_CONN("beacon interval: %d\n", params->beacon_interval);
1234 else
1235 WL_CONN("no beacon interval specified\n");
1236
1237 if (params->basic_rates)
1238 WL_CONN("basic rates: %08X\n", params->basic_rates);
1239 else
1240 WL_CONN("no basic rates specified\n");
1241
1242 if (params->privacy)
1243 WL_CONN("privacy required\n");
1244 else
1245 WL_CONN("no privacy required\n");
1246
1247 /* Configure Privacy for starter */
1248 if (params->privacy)
1249 wsec |= WEP_ENABLED;
1250
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001251 err = brcmf_fil_iovar_int_set(ndev, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001252 if (err) {
1253 WL_ERR("wsec failed (%d)\n", err);
1254 goto done;
1255 }
1256
1257 /* Configure Beacon Interval for starter */
1258 if (params->beacon_interval)
1259 bcnprd = params->beacon_interval;
1260 else
1261 bcnprd = 100;
1262
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001263 err = brcmf_fil_cmd_int_set(ndev, BRCM_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001264 if (err) {
1265 WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
1266 goto done;
1267 }
1268
1269 /* Configure required join parameter */
1270 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1271
1272 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001273 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1274 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1275 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1276 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001277 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001278
1279 /* BSSID */
1280 if (params->bssid) {
1281 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1282 join_params_size = sizeof(join_params.ssid_le) +
1283 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001284 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001285 } else {
1286 memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001287 memset(profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001288 }
1289
Arend van Spriel5b435de2011-10-05 13:19:03 +02001290 /* Channel */
1291 if (params->channel) {
1292 u32 target_channel;
1293
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001294 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001295 ieee80211_frequency_to_channel(
1296 params->channel->center_freq);
1297 if (params->channel_fixed) {
1298 /* adding chanspec */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001299 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001300 &join_params, &join_params_size);
1301 }
1302
1303 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001304 target_channel = cfg->channel;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001305 err = brcmf_fil_cmd_int_set(ndev, BRCM_SET_CHANNEL,
1306 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001307 if (err) {
1308 WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
1309 goto done;
1310 }
1311 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001312 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001313
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001314 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001315
1316
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001317 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_SSID,
1318 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001319 if (err) {
1320 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1321 goto done;
1322 }
1323
1324done:
1325 if (err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001326 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001327 WL_TRACE("Exit\n");
1328 return err;
1329}
1330
1331static s32
1332brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1333{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001334 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001335 s32 err = 0;
1336
1337 WL_TRACE("Enter\n");
1338 if (!check_sys_up(wiphy))
1339 return -EIO;
1340
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001341 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001342
1343 WL_TRACE("Exit\n");
1344
1345 return err;
1346}
1347
1348static s32 brcmf_set_wpa_version(struct net_device *ndev,
1349 struct cfg80211_connect_params *sme)
1350{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001351 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001352 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001353 struct brcmf_cfg80211_security *sec;
1354 s32 val = 0;
1355 s32 err = 0;
1356
1357 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1358 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1359 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1360 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1361 else
1362 val = WPA_AUTH_DISABLED;
1363 WL_CONN("setting wpa_auth to 0x%0x\n", val);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001364 err = brcmf_fil_iovar_int_set(ndev, "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001365 if (err) {
1366 WL_ERR("set wpa_auth failed (%d)\n", err);
1367 return err;
1368 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001369 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001370 sec->wpa_versions = sme->crypto.wpa_versions;
1371 return err;
1372}
1373
1374static s32 brcmf_set_auth_type(struct net_device *ndev,
1375 struct cfg80211_connect_params *sme)
1376{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001377 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001378 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001379 struct brcmf_cfg80211_security *sec;
1380 s32 val = 0;
1381 s32 err = 0;
1382
1383 switch (sme->auth_type) {
1384 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1385 val = 0;
1386 WL_CONN("open system\n");
1387 break;
1388 case NL80211_AUTHTYPE_SHARED_KEY:
1389 val = 1;
1390 WL_CONN("shared key\n");
1391 break;
1392 case NL80211_AUTHTYPE_AUTOMATIC:
1393 val = 2;
1394 WL_CONN("automatic\n");
1395 break;
1396 case NL80211_AUTHTYPE_NETWORK_EAP:
1397 WL_CONN("network eap\n");
1398 default:
1399 val = 2;
1400 WL_ERR("invalid auth type (%d)\n", sme->auth_type);
1401 break;
1402 }
1403
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001404 err = brcmf_fil_iovar_int_set(ndev, "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001405 if (err) {
1406 WL_ERR("set auth failed (%d)\n", err);
1407 return err;
1408 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001409 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001410 sec->auth_type = sme->auth_type;
1411 return err;
1412}
1413
1414static s32
1415brcmf_set_set_cipher(struct net_device *ndev,
1416 struct cfg80211_connect_params *sme)
1417{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001418 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001419 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001420 struct brcmf_cfg80211_security *sec;
1421 s32 pval = 0;
1422 s32 gval = 0;
1423 s32 err = 0;
1424
1425 if (sme->crypto.n_ciphers_pairwise) {
1426 switch (sme->crypto.ciphers_pairwise[0]) {
1427 case WLAN_CIPHER_SUITE_WEP40:
1428 case WLAN_CIPHER_SUITE_WEP104:
1429 pval = WEP_ENABLED;
1430 break;
1431 case WLAN_CIPHER_SUITE_TKIP:
1432 pval = TKIP_ENABLED;
1433 break;
1434 case WLAN_CIPHER_SUITE_CCMP:
1435 pval = AES_ENABLED;
1436 break;
1437 case WLAN_CIPHER_SUITE_AES_CMAC:
1438 pval = AES_ENABLED;
1439 break;
1440 default:
1441 WL_ERR("invalid cipher pairwise (%d)\n",
1442 sme->crypto.ciphers_pairwise[0]);
1443 return -EINVAL;
1444 }
1445 }
1446 if (sme->crypto.cipher_group) {
1447 switch (sme->crypto.cipher_group) {
1448 case WLAN_CIPHER_SUITE_WEP40:
1449 case WLAN_CIPHER_SUITE_WEP104:
1450 gval = WEP_ENABLED;
1451 break;
1452 case WLAN_CIPHER_SUITE_TKIP:
1453 gval = TKIP_ENABLED;
1454 break;
1455 case WLAN_CIPHER_SUITE_CCMP:
1456 gval = AES_ENABLED;
1457 break;
1458 case WLAN_CIPHER_SUITE_AES_CMAC:
1459 gval = AES_ENABLED;
1460 break;
1461 default:
1462 WL_ERR("invalid cipher group (%d)\n",
1463 sme->crypto.cipher_group);
1464 return -EINVAL;
1465 }
1466 }
1467
1468 WL_CONN("pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001469 err = brcmf_fil_iovar_int_set(ndev, "wsec", pval | gval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001470 if (err) {
1471 WL_ERR("error (%d)\n", err);
1472 return err;
1473 }
1474
Arend van Spriel06bb1232012-09-27 14:17:56 +02001475 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001476 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1477 sec->cipher_group = sme->crypto.cipher_group;
1478
1479 return err;
1480}
1481
1482static s32
1483brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1484{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001485 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001486 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001487 struct brcmf_cfg80211_security *sec;
1488 s32 val = 0;
1489 s32 err = 0;
1490
1491 if (sme->crypto.n_akm_suites) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001492 err = brcmf_fil_iovar_int_get(ndev, "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001493 if (err) {
1494 WL_ERR("could not get wpa_auth (%d)\n", err);
1495 return err;
1496 }
1497 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1498 switch (sme->crypto.akm_suites[0]) {
1499 case WLAN_AKM_SUITE_8021X:
1500 val = WPA_AUTH_UNSPECIFIED;
1501 break;
1502 case WLAN_AKM_SUITE_PSK:
1503 val = WPA_AUTH_PSK;
1504 break;
1505 default:
1506 WL_ERR("invalid cipher group (%d)\n",
1507 sme->crypto.cipher_group);
1508 return -EINVAL;
1509 }
1510 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1511 switch (sme->crypto.akm_suites[0]) {
1512 case WLAN_AKM_SUITE_8021X:
1513 val = WPA2_AUTH_UNSPECIFIED;
1514 break;
1515 case WLAN_AKM_SUITE_PSK:
1516 val = WPA2_AUTH_PSK;
1517 break;
1518 default:
1519 WL_ERR("invalid cipher group (%d)\n",
1520 sme->crypto.cipher_group);
1521 return -EINVAL;
1522 }
1523 }
1524
1525 WL_CONN("setting wpa_auth to %d\n", val);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001526 err = brcmf_fil_iovar_int_set(ndev, "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001527 if (err) {
1528 WL_ERR("could not set wpa_auth (%d)\n", err);
1529 return err;
1530 }
1531 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001532 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001533 sec->wpa_auth = sme->crypto.akm_suites[0];
1534
1535 return err;
1536}
1537
1538static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001539brcmf_set_sharedkey(struct net_device *ndev,
1540 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001541{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001542 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001543 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001544 struct brcmf_cfg80211_security *sec;
1545 struct brcmf_wsec_key key;
1546 s32 val;
1547 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001548 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001549
1550 WL_CONN("key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001551
Roland Vossena718e2f2011-10-12 20:51:24 +02001552 if (sme->key_len == 0)
1553 return 0;
1554
Arend van Spriel06bb1232012-09-27 14:17:56 +02001555 sec = &profile->sec;
Roland Vossena718e2f2011-10-12 20:51:24 +02001556 WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n",
1557 sec->wpa_versions, sec->cipher_pairwise);
1558
1559 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1560 return 0;
1561
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001562 if (!(sec->cipher_pairwise &
1563 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1564 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001565
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001566 memset(&key, 0, sizeof(key));
1567 key.len = (u32) sme->key_len;
1568 key.index = (u32) sme->key_idx;
1569 if (key.len > sizeof(key.data)) {
1570 WL_ERR("Too long key length (%u)\n", key.len);
1571 return -EINVAL;
1572 }
1573 memcpy(key.data, sme->key, key.len);
1574 key.flags = BRCMF_PRIMARY_KEY;
1575 switch (sec->cipher_pairwise) {
1576 case WLAN_CIPHER_SUITE_WEP40:
1577 key.algo = CRYPTO_ALGO_WEP1;
1578 break;
1579 case WLAN_CIPHER_SUITE_WEP104:
1580 key.algo = CRYPTO_ALGO_WEP128;
1581 break;
1582 default:
1583 WL_ERR("Invalid algorithm (%d)\n",
1584 sme->crypto.ciphers_pairwise[0]);
1585 return -EINVAL;
1586 }
1587 /* Set the new key/index */
1588 WL_CONN("key length (%d) key index (%d) algo (%d)\n",
1589 key.len, key.index, key.algo);
1590 WL_CONN("key \"%s\"\n", key.data);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001591 bssidx = brcmf_find_bssidx(cfg, ndev);
1592 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001593 if (err)
1594 return err;
1595
1596 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
1597 WL_CONN("set auth_type to shared key\n");
1598 val = WL_AUTH_SHARED_KEY; /* shared key */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001599 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001600 if (err)
1601 WL_ERR("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001602 }
1603 return err;
1604}
1605
1606static s32
1607brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
1608 struct cfg80211_connect_params *sme)
1609{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001610 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001611 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001612 struct ieee80211_channel *chan = sme->channel;
1613 struct brcmf_join_params join_params;
1614 size_t join_params_size;
1615 struct brcmf_ssid ssid;
1616
1617 s32 err = 0;
1618
1619 WL_TRACE("Enter\n");
1620 if (!check_sys_up(wiphy))
1621 return -EIO;
1622
1623 if (!sme->ssid) {
1624 WL_ERR("Invalid ssid\n");
1625 return -EOPNOTSUPP;
1626 }
1627
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001628 set_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001629
1630 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001631 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001632 ieee80211_frequency_to_channel(chan->center_freq);
1633 WL_CONN("channel (%d), center_req (%d)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001634 cfg->channel, chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001635 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001636 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001637
1638 WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
1639
1640 err = brcmf_set_wpa_version(ndev, sme);
1641 if (err) {
1642 WL_ERR("wl_set_wpa_version failed (%d)\n", err);
1643 goto done;
1644 }
1645
1646 err = brcmf_set_auth_type(ndev, sme);
1647 if (err) {
1648 WL_ERR("wl_set_auth_type failed (%d)\n", err);
1649 goto done;
1650 }
1651
1652 err = brcmf_set_set_cipher(ndev, sme);
1653 if (err) {
1654 WL_ERR("wl_set_set_cipher failed (%d)\n", err);
1655 goto done;
1656 }
1657
1658 err = brcmf_set_key_mgmt(ndev, sme);
1659 if (err) {
1660 WL_ERR("wl_set_key_mgmt failed (%d)\n", err);
1661 goto done;
1662 }
1663
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001664 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001665 if (err) {
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001666 WL_ERR("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001667 goto done;
1668 }
1669
1670 memset(&join_params, 0, sizeof(join_params));
1671 join_params_size = sizeof(join_params.ssid_le);
1672
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001673 profile->ssid.SSID_len = min_t(u32,
1674 sizeof(ssid.SSID), (u32)sme->ssid_len);
1675 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
1676 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1677 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001678
1679 memcpy(join_params.params_le.bssid, ether_bcast, ETH_ALEN);
1680
1681 if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
1682 WL_CONN("ssid \"%s\", len (%d)\n",
1683 ssid.SSID, ssid.SSID_len);
1684
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001685 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001686 &join_params, &join_params_size);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001687 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_SSID,
1688 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001689 if (err)
1690 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1691
1692done:
1693 if (err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001694 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001695 WL_TRACE("Exit\n");
1696 return err;
1697}
1698
1699static s32
1700brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1701 u16 reason_code)
1702{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001703 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02001704 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001705 struct brcmf_scb_val_le scbval;
1706 s32 err = 0;
1707
1708 WL_TRACE("Enter. Reason code = %d\n", reason_code);
1709 if (!check_sys_up(wiphy))
1710 return -EIO;
1711
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001712 clear_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001713
Arend van Spriel06bb1232012-09-27 14:17:56 +02001714 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001715 scbval.val = cpu_to_le32(reason_code);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001716 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_DISASSOC, &scbval,
1717 sizeof(struct brcmf_scb_val_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001718 if (err)
1719 WL_ERR("error (%d)\n", err);
1720
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001721 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001722
1723 WL_TRACE("Exit\n");
1724 return err;
1725}
1726
1727static s32
1728brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001729 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001730{
1731
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001732 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1733 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001734 u16 txpwrmw;
1735 s32 err = 0;
1736 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001737 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001738
1739 WL_TRACE("Enter\n");
1740 if (!check_sys_up(wiphy))
1741 return -EIO;
1742
1743 switch (type) {
1744 case NL80211_TX_POWER_AUTOMATIC:
1745 break;
1746 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001747 case NL80211_TX_POWER_FIXED:
1748 if (dbm < 0) {
1749 WL_ERR("TX_POWER_FIXED - dbm is negative\n");
1750 err = -EINVAL;
1751 goto done;
1752 }
1753 break;
1754 }
1755 /* Make sure radio is off or on as far as software is concerned */
1756 disable = WL_RADIO_SW_DISABLE << 16;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001757 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001758 if (err)
1759 WL_ERR("WLC_SET_RADIO error (%d)\n", err);
1760
1761 if (dbm > 0xffff)
1762 txpwrmw = 0xffff;
1763 else
1764 txpwrmw = (u16) dbm;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001765 err = brcmf_fil_iovar_int_set(ndev, "qtxpower",
Alwin Beukersef6ac172011-10-12 20:51:26 +02001766 (s32) (brcmf_mw_to_qdbm(txpwrmw)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001767 if (err)
1768 WL_ERR("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001769 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001770
1771done:
1772 WL_TRACE("Exit\n");
1773 return err;
1774}
1775
1776static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
1777{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001778 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1779 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001780 s32 txpwrdbm;
1781 u8 result;
1782 s32 err = 0;
1783
1784 WL_TRACE("Enter\n");
1785 if (!check_sys_up(wiphy))
1786 return -EIO;
1787
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001788 err = brcmf_fil_iovar_int_get(ndev, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001789 if (err) {
1790 WL_ERR("error (%d)\n", err);
1791 goto done;
1792 }
1793
1794 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001795 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001796
1797done:
1798 WL_TRACE("Exit\n");
1799 return err;
1800}
1801
1802static s32
1803brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1804 u8 key_idx, bool unicast, bool multicast)
1805{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001806 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001807 u32 index;
1808 u32 wsec;
1809 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001810 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001811
1812 WL_TRACE("Enter\n");
1813 WL_CONN("key index (%d)\n", key_idx);
1814 if (!check_sys_up(wiphy))
1815 return -EIO;
1816
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001817 bssidx = brcmf_find_bssidx(cfg, ndev);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001818 err = brcmf_fil_bsscfg_int_get(ndev, bssidx, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001819 if (err) {
1820 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
1821 goto done;
1822 }
1823
1824 if (wsec & WEP_ENABLED) {
1825 /* Just select a new current key */
1826 index = key_idx;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001827 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_KEY_PRIMARY,
1828 index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001829 if (err)
1830 WL_ERR("error (%d)\n", err);
1831 }
1832done:
1833 WL_TRACE("Exit\n");
1834 return err;
1835}
1836
1837static s32
1838brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
1839 u8 key_idx, const u8 *mac_addr, struct key_params *params)
1840{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001841 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001842 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001843 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001844 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001845
1846 memset(&key, 0, sizeof(key));
1847 key.index = (u32) key_idx;
1848 /* Instead of bcast for ea address for default wep keys,
1849 driver needs it to be Null */
1850 if (!is_multicast_ether_addr(mac_addr))
1851 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
1852 key.len = (u32) params->key_len;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001853 bssidx = brcmf_find_bssidx(cfg, ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001854 /* check for key index change */
1855 if (key.len == 0) {
1856 /* key delete */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001857 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001858 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001859 WL_ERR("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001860 } else {
1861 if (key.len > sizeof(key.data)) {
1862 WL_ERR("Invalid key length (%d)\n", key.len);
1863 return -EINVAL;
1864 }
1865
1866 WL_CONN("Setting the key index %d\n", key.index);
1867 memcpy(key.data, params->key, key.len);
1868
1869 if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
1870 u8 keybuf[8];
1871 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1872 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1873 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1874 }
1875
1876 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
1877 if (params->seq && params->seq_len == 6) {
1878 /* rx iv */
1879 u8 *ivptr;
1880 ivptr = (u8 *) params->seq;
1881 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
1882 (ivptr[3] << 8) | ivptr[2];
1883 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
1884 key.iv_initialized = true;
1885 }
1886
1887 switch (params->cipher) {
1888 case WLAN_CIPHER_SUITE_WEP40:
1889 key.algo = CRYPTO_ALGO_WEP1;
1890 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1891 break;
1892 case WLAN_CIPHER_SUITE_WEP104:
1893 key.algo = CRYPTO_ALGO_WEP128;
1894 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1895 break;
1896 case WLAN_CIPHER_SUITE_TKIP:
1897 key.algo = CRYPTO_ALGO_TKIP;
1898 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1899 break;
1900 case WLAN_CIPHER_SUITE_AES_CMAC:
1901 key.algo = CRYPTO_ALGO_AES_CCM;
1902 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1903 break;
1904 case WLAN_CIPHER_SUITE_CCMP:
1905 key.algo = CRYPTO_ALGO_AES_CCM;
1906 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
1907 break;
1908 default:
1909 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
1910 return -EINVAL;
1911 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001912 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001913 if (err)
1914 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001915 }
1916 return err;
1917}
1918
1919static s32
1920brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1921 u8 key_idx, bool pairwise, const u8 *mac_addr,
1922 struct key_params *params)
1923{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001924 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001925 struct brcmf_wsec_key key;
1926 s32 val;
1927 s32 wsec;
1928 s32 err = 0;
1929 u8 keybuf[8];
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001930 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001931
1932 WL_TRACE("Enter\n");
1933 WL_CONN("key index (%d)\n", key_idx);
1934 if (!check_sys_up(wiphy))
1935 return -EIO;
1936
1937 if (mac_addr) {
1938 WL_TRACE("Exit");
1939 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
1940 }
1941 memset(&key, 0, sizeof(key));
1942
1943 key.len = (u32) params->key_len;
1944 key.index = (u32) key_idx;
1945
1946 if (key.len > sizeof(key.data)) {
1947 WL_ERR("Too long key length (%u)\n", key.len);
1948 err = -EINVAL;
1949 goto done;
1950 }
1951 memcpy(key.data, params->key, key.len);
1952
1953 key.flags = BRCMF_PRIMARY_KEY;
1954 switch (params->cipher) {
1955 case WLAN_CIPHER_SUITE_WEP40:
1956 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001957 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001958 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1959 break;
1960 case WLAN_CIPHER_SUITE_WEP104:
1961 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001962 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001963 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1964 break;
1965 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001966 if (cfg->conf->mode != WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001967 WL_CONN("Swapping key\n");
1968 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1969 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1970 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1971 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001972 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001973 val = TKIP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001974 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1975 break;
1976 case WLAN_CIPHER_SUITE_AES_CMAC:
1977 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001978 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001979 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1980 break;
1981 case WLAN_CIPHER_SUITE_CCMP:
1982 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001983 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001984 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
1985 break;
1986 default:
1987 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
1988 err = -EINVAL;
1989 goto done;
1990 }
1991
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001992 bssidx = brcmf_find_bssidx(cfg, ndev);
1993 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001994 if (err)
1995 goto done;
1996
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001997 err = brcmf_fil_bsscfg_int_get(ndev, bssidx, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001998 if (err) {
1999 WL_ERR("get wsec error (%d)\n", err);
2000 goto done;
2001 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002002 wsec |= val;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002003 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002004 if (err) {
2005 WL_ERR("set wsec error (%d)\n", err);
2006 goto done;
2007 }
2008
Arend van Spriel5b435de2011-10-05 13:19:03 +02002009done:
2010 WL_TRACE("Exit\n");
2011 return err;
2012}
2013
2014static s32
2015brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2016 u8 key_idx, bool pairwise, const u8 *mac_addr)
2017{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002018 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002019 struct brcmf_wsec_key key;
2020 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002021 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002022
2023 WL_TRACE("Enter\n");
2024 if (!check_sys_up(wiphy))
2025 return -EIO;
2026
2027 memset(&key, 0, sizeof(key));
2028
2029 key.index = (u32) key_idx;
2030 key.flags = BRCMF_PRIMARY_KEY;
2031 key.algo = CRYPTO_ALGO_OFF;
2032
2033 WL_CONN("key index (%d)\n", key_idx);
2034
2035 /* Set the new key/index */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002036 bssidx = brcmf_find_bssidx(cfg, ndev);
2037 err = send_key_to_dongle(cfg, bssidx, ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002038 if (err) {
2039 if (err == -EINVAL) {
2040 if (key.index >= DOT11_MAX_DEFAULT_KEYS)
2041 /* we ignore this key index in this case */
2042 WL_ERR("invalid key index (%d)\n", key_idx);
2043 }
2044 /* Ignore this error, may happen during DISASSOC */
2045 err = -EAGAIN;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002046 }
2047
Arend van Spriel5b435de2011-10-05 13:19:03 +02002048 WL_TRACE("Exit\n");
2049 return err;
2050}
2051
2052static s32
2053brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2054 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2055 void (*callback) (void *cookie, struct key_params * params))
2056{
2057 struct key_params params;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002058 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02002059 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002060 struct brcmf_cfg80211_security *sec;
2061 s32 wsec;
2062 s32 err = 0;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002063 s32 bssidx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002064
2065 WL_TRACE("Enter\n");
2066 WL_CONN("key index (%d)\n", key_idx);
2067 if (!check_sys_up(wiphy))
2068 return -EIO;
2069
2070 memset(&params, 0, sizeof(params));
2071
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002072 bssidx = brcmf_find_bssidx(cfg, ndev);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002073 err = brcmf_fil_bsscfg_int_get(ndev, bssidx, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002074 if (err) {
2075 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
2076 /* Ignore this error, may happen during DISASSOC */
2077 err = -EAGAIN;
2078 goto done;
2079 }
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002080 switch (wsec & ~SES_OW_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002081 case WEP_ENABLED:
Arend van Spriel06bb1232012-09-27 14:17:56 +02002082 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002083 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2084 params.cipher = WLAN_CIPHER_SUITE_WEP40;
2085 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
2086 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2087 params.cipher = WLAN_CIPHER_SUITE_WEP104;
2088 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
2089 }
2090 break;
2091 case TKIP_ENABLED:
2092 params.cipher = WLAN_CIPHER_SUITE_TKIP;
2093 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
2094 break;
2095 case AES_ENABLED:
2096 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
2097 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
2098 break;
2099 default:
2100 WL_ERR("Invalid algo (0x%x)\n", wsec);
2101 err = -EINVAL;
2102 goto done;
2103 }
2104 callback(cookie, &params);
2105
2106done:
2107 WL_TRACE("Exit\n");
2108 return err;
2109}
2110
2111static s32
2112brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2113 struct net_device *ndev, u8 key_idx)
2114{
2115 WL_INFO("Not supported\n");
2116
2117 return -EOPNOTSUPP;
2118}
2119
2120static s32
2121brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02002122 u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002123{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002124 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel06bb1232012-09-27 14:17:56 +02002125 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002126 struct brcmf_scb_val_le scb_val;
2127 int rssi;
2128 s32 rate;
2129 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02002130 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002131 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002132
Hante Meuleman1a873342012-09-27 14:17:54 +02002133 WL_TRACE("Enter, MAC %pM\n", mac);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002134 if (!check_sys_up(wiphy))
2135 return -EIO;
2136
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002137 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002138 memcpy(&sta_info_le, mac, ETH_ALEN);
2139 err = brcmf_fil_iovar_data_get(ndev, "sta_info", &sta_info_le,
2140 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002141 if (err < 0) {
2142 WL_ERR("GET STA INFO failed, %d\n", err);
2143 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002144 }
Hante Meuleman1a873342012-09-27 14:17:54 +02002145 sinfo->filled = STATION_INFO_INACTIVE_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002146 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2147 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002148 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002149 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02002150 }
2151 WL_TRACE("STA idle time : %d ms, connected time :%d sec\n",
2152 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002153 } else if (cfg->conf->mode == WL_MODE_BSS) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002154 if (memcmp(mac, bssid, ETH_ALEN)) {
2155 WL_ERR("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
2156 mac, bssid);
2157 err = -ENOENT;
2158 goto done;
2159 }
2160 /* Report the current tx rate */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002161 err = brcmf_fil_cmd_int_get(ndev, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02002162 if (err) {
2163 WL_ERR("Could not get rate (%d)\n", err);
2164 goto done;
2165 } else {
2166 sinfo->filled |= STATION_INFO_TX_BITRATE;
2167 sinfo->txrate.legacy = rate * 5;
2168 WL_CONN("Rate %d Mbps\n", rate / 2);
2169 }
2170
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002171 if (test_bit(WL_STATUS_CONNECTED, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002172 memset(&scb_val, 0, sizeof(scb_val));
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002173 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_GET_RSSI, &scb_val,
2174 sizeof(struct brcmf_scb_val_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002175 if (err) {
2176 WL_ERR("Could not get rssi (%d)\n", err);
2177 goto done;
2178 } else {
2179 rssi = le32_to_cpu(scb_val.val);
2180 sinfo->filled |= STATION_INFO_SIGNAL;
2181 sinfo->signal = rssi;
2182 WL_CONN("RSSI %d dBm\n", rssi);
2183 }
2184 }
2185 } else
2186 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002187done:
2188 WL_TRACE("Exit\n");
2189 return err;
2190}
2191
2192static s32
2193brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2194 bool enabled, s32 timeout)
2195{
2196 s32 pm;
2197 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002198 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002199
2200 WL_TRACE("Enter\n");
2201
2202 /*
2203 * Powersave enable/disable request is coming from the
2204 * cfg80211 even before the interface is up. In that
2205 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002206 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002207 * FW later while initializing the dongle
2208 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002209 cfg->pwr_save = enabled;
2210 if (!test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002211
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002212 WL_INFO("Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002213 goto done;
2214 }
2215
2216 pm = enabled ? PM_FAST : PM_OFF;
2217 WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
2218
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002219 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002220 if (err) {
2221 if (err == -ENODEV)
2222 WL_ERR("net_device is not ready yet\n");
2223 else
2224 WL_ERR("error (%d)\n", err);
2225 }
2226done:
2227 WL_TRACE("Exit\n");
2228 return err;
2229}
2230
2231static s32
2232brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
2233 const u8 *addr,
2234 const struct cfg80211_bitrate_mask *mask)
2235{
2236 struct brcm_rateset_le rateset_le;
2237 s32 rate;
2238 s32 val;
2239 s32 err_bg;
2240 s32 err_a;
2241 u32 legacy;
2242 s32 err = 0;
2243
2244 WL_TRACE("Enter\n");
2245 if (!check_sys_up(wiphy))
2246 return -EIO;
2247
2248 /* addr param is always NULL. ignore it */
2249 /* Get current rateset */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002250 err = brcmf_fil_cmd_data_get(ndev, BRCM_GET_CURR_RATESET, &rateset_le,
2251 sizeof(rateset_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002252 if (err) {
2253 WL_ERR("could not get current rateset (%d)\n", err);
2254 goto done;
2255 }
2256
2257 legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
2258 if (!legacy)
2259 legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
2260 0xFFFF);
2261
2262 val = wl_g_rates[legacy - 1].bitrate * 100000;
2263
2264 if (val < le32_to_cpu(rateset_le.count))
2265 /* Select rate by rateset index */
2266 rate = rateset_le.rates[val] & 0x7f;
2267 else
2268 /* Specified rate in bps */
2269 rate = val / 500000;
2270
2271 WL_CONN("rate %d mbps\n", rate / 2);
2272
2273 /*
2274 *
2275 * Set rate override,
2276 * Since the is a/b/g-blind, both a/bg_rate are enforced.
2277 */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002278 err_bg = brcmf_fil_iovar_int_set(ndev, "bg_rate", rate);
2279 err_a = brcmf_fil_iovar_int_set(ndev, "a_rate", rate);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002280 if (err_bg && err_a) {
2281 WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
2282 err = err_bg | err_a;
2283 }
2284
2285done:
2286 WL_TRACE("Exit\n");
2287 return err;
2288}
2289
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002290static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002291 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002292{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002293 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002294 struct ieee80211_channel *notify_channel;
2295 struct cfg80211_bss *bss;
2296 struct ieee80211_supported_band *band;
2297 s32 err = 0;
2298 u16 channel;
2299 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002300 u16 notify_capability;
2301 u16 notify_interval;
2302 u8 *notify_ie;
2303 size_t notify_ielen;
2304 s32 notify_signal;
2305
2306 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
2307 WL_ERR("Bss info is larger than buffer. Discarding\n");
2308 return 0;
2309 }
2310
2311 channel = bi->ctl_ch ? bi->ctl_ch :
2312 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2313
2314 if (channel <= CH_MAX_2G_CHANNEL)
2315 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2316 else
2317 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2318
2319 freq = ieee80211_channel_to_frequency(channel, band->band);
2320 notify_channel = ieee80211_get_channel(wiphy, freq);
2321
Arend van Spriel5b435de2011-10-05 13:19:03 +02002322 notify_capability = le16_to_cpu(bi->capability);
2323 notify_interval = le16_to_cpu(bi->beacon_period);
2324 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2325 notify_ielen = le32_to_cpu(bi->ie_length);
2326 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2327
2328 WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
2329 bi->BSSID[0], bi->BSSID[1], bi->BSSID[2],
2330 bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]);
2331 WL_CONN("Channel: %d(%d)\n", channel, freq);
2332 WL_CONN("Capability: %X\n", notify_capability);
2333 WL_CONN("Beacon interval: %d\n", notify_interval);
2334 WL_CONN("Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002335
2336 bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002337 0, notify_capability, notify_interval, notify_ie,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002338 notify_ielen, notify_signal, GFP_KERNEL);
2339
Franky Line78946e2011-11-10 20:30:34 +01002340 if (!bss)
2341 return -ENOMEM;
2342
2343 cfg80211_put_bss(bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002344
2345 return err;
2346}
2347
Roland Vossen6f09be02011-10-18 14:03:02 +02002348static struct brcmf_bss_info_le *
2349next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2350{
2351 if (bss == NULL)
2352 return list->bss_info_le;
2353 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2354 le32_to_cpu(bss->length));
2355}
2356
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002357static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002358{
2359 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002360 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002361 s32 err = 0;
2362 int i;
2363
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002364 bss_list = cfg->bss_list;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002365 if (bss_list->version != BRCMF_BSS_INFO_VERSION) {
2366 WL_ERR("Version %d != WL_BSS_INFO_VERSION\n",
2367 bss_list->version);
2368 return -EOPNOTSUPP;
2369 }
2370 WL_SCAN("scanned AP count (%d)\n", bss_list->count);
2371 for (i = 0; i < bss_list->count && i < WL_AP_MAX; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002372 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002373 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002374 if (err)
2375 break;
2376 }
2377 return err;
2378}
2379
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002380static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002381 struct net_device *ndev, const u8 *bssid)
2382{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002383 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002384 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002385 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002386 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002387 struct cfg80211_bss *bss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002388 u8 *buf = NULL;
2389 s32 err = 0;
2390 u16 channel;
2391 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002392 u16 notify_capability;
2393 u16 notify_interval;
2394 u8 *notify_ie;
2395 size_t notify_ielen;
2396 s32 notify_signal;
2397
2398 WL_TRACE("Enter\n");
2399
2400 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2401 if (buf == NULL) {
2402 err = -ENOMEM;
2403 goto CleanUp;
2404 }
2405
2406 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2407
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002408 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_GET_BSS_INFO, buf,
2409 WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002410 if (err) {
2411 WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
2412 goto CleanUp;
2413 }
2414
Roland Vossend34bf642011-10-18 14:03:01 +02002415 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002416
2417 channel = bi->ctl_ch ? bi->ctl_ch :
2418 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2419
2420 if (channel <= CH_MAX_2G_CHANNEL)
2421 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2422 else
2423 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2424
2425 freq = ieee80211_channel_to_frequency(channel, band->band);
2426 notify_channel = ieee80211_get_channel(wiphy, freq);
2427
Arend van Spriel5b435de2011-10-05 13:19:03 +02002428 notify_capability = le16_to_cpu(bi->capability);
2429 notify_interval = le16_to_cpu(bi->beacon_period);
2430 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2431 notify_ielen = le32_to_cpu(bi->ie_length);
2432 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2433
2434 WL_CONN("channel: %d(%d)\n", channel, freq);
2435 WL_CONN("capability: %X\n", notify_capability);
2436 WL_CONN("beacon interval: %d\n", notify_interval);
2437 WL_CONN("signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002438
Franky Line78946e2011-11-10 20:30:34 +01002439 bss = cfg80211_inform_bss(wiphy, notify_channel, bssid,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002440 0, notify_capability, notify_interval,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002441 notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
2442
Franky Line78946e2011-11-10 20:30:34 +01002443 if (!bss) {
2444 err = -ENOMEM;
2445 goto CleanUp;
2446 }
2447
2448 cfg80211_put_bss(bss);
2449
Arend van Spriel5b435de2011-10-05 13:19:03 +02002450CleanUp:
2451
2452 kfree(buf);
2453
2454 WL_TRACE("Exit\n");
2455
2456 return err;
2457}
2458
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002459static bool brcmf_is_ibssmode(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002460{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002461 return cfg->conf->mode == WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002462}
2463
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002464/*
2465 * Traverse a string of 1-byte tag/1-byte length/variable-length value
2466 * triples, returning a pointer to the substring whose first element
2467 * matches tag
2468 */
2469static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
2470{
2471 struct brcmf_tlv *elt;
2472 int totlen;
2473
2474 elt = (struct brcmf_tlv *) buf;
2475 totlen = buflen;
2476
2477 /* find tagged parameter */
Hante Meuleman04012892012-09-27 14:17:49 +02002478 while (totlen >= TLV_HDR_LEN) {
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002479 int len = elt->len;
2480
2481 /* validate remaining totlen */
Hante Meuleman04012892012-09-27 14:17:49 +02002482 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002483 return elt;
2484
Hante Meuleman04012892012-09-27 14:17:49 +02002485 elt = (struct brcmf_tlv *) ((u8 *) elt + (len + TLV_HDR_LEN));
2486 totlen -= (len + TLV_HDR_LEN);
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002487 }
2488
2489 return NULL;
2490}
2491
Hante Meuleman1a873342012-09-27 14:17:54 +02002492/* Is any of the tlvs the expected entry? If
2493 * not update the tlvs buffer pointer/length.
2494 */
2495static bool
2496brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
2497 u8 *oui, u32 oui_len, u8 type)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002498{
Hante Meuleman1a873342012-09-27 14:17:54 +02002499 /* If the contents match the OUI and the type */
2500 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
2501 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
2502 type == ie[TLV_BODY_OFF + oui_len]) {
2503 return true;
2504 }
2505
2506 if (tlvs == NULL)
2507 return false;
2508 /* point to the next ie */
2509 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
2510 /* calculate the length of the rest of the buffer */
2511 *tlvs_len -= (int)(ie - *tlvs);
2512 /* update the pointer to the start of the buffer */
2513 *tlvs = ie;
2514
2515 return false;
2516}
2517
Franky Lin3cb91f52012-10-10 11:13:08 -07002518static struct brcmf_vs_tlv *
Hante Meuleman1a873342012-09-27 14:17:54 +02002519brcmf_find_wpaie(u8 *parse, u32 len)
2520{
2521 struct brcmf_tlv *ie;
2522
Arend van Spriel04b23122012-10-12 12:28:14 +02002523 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002524 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
2525 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
2526 return (struct brcmf_vs_tlv *)ie;
2527 }
2528 return NULL;
2529}
2530
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002531static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002532{
Arend van Spriel06bb1232012-09-27 14:17:56 +02002533 struct brcmf_cfg80211_profile *profile = cfg->profile;
Roland Vossend34bf642011-10-18 14:03:01 +02002534 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002535 struct brcmf_ssid *ssid;
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002536 struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002537 u16 beacon_interval;
2538 u8 dtim_period;
2539 size_t ie_len;
2540 u8 *ie;
2541 s32 err = 0;
2542
2543 WL_TRACE("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002544 if (brcmf_is_ibssmode(cfg))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002545 return err;
2546
Arend van Spriel06bb1232012-09-27 14:17:56 +02002547 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002548
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002549 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002550 err = brcmf_fil_cmd_data_get(cfg_to_ndev(cfg),
2551 BRCMF_C_GET_BSS_INFO,
2552 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002553 if (err) {
2554 WL_ERR("Could not get bss info %d\n", err);
2555 goto update_bss_info_out;
2556 }
2557
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002558 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2559 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002560 if (err)
2561 goto update_bss_info_out;
2562
2563 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2564 ie_len = le32_to_cpu(bi->ie_length);
2565 beacon_interval = le16_to_cpu(bi->beacon_period);
2566
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002567 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002568 if (tim)
2569 dtim_period = tim->data[1];
2570 else {
2571 /*
2572 * active scan was done so we could not get dtim
2573 * information out of probe response.
2574 * so we speficially query dtim information to dongle.
2575 */
2576 u32 var;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002577 err = brcmf_fil_iovar_int_get(cfg_to_ndev(cfg),
2578 "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002579 if (err) {
2580 WL_ERR("wl dtim_assoc failed (%d)\n", err);
2581 goto update_bss_info_out;
2582 }
2583 dtim_period = (u8)var;
2584 }
2585
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02002586 profile->beacon_interval = beacon_interval;
2587 profile->dtim_period = dtim_period;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002588
2589update_bss_info_out:
2590 WL_TRACE("Exit");
2591 return err;
2592}
2593
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002594static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002595{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002596 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
2597 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002598 struct brcmf_ssid ssid;
2599
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002600 set_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
2601 if (cfg->iscan_on) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002602 iscan->state = WL_ISCAN_STATE_IDLE;
2603
2604 if (iscan->timer_on) {
2605 del_timer_sync(&iscan->timer);
2606 iscan->timer_on = 0;
2607 }
2608
2609 cancel_work_sync(&iscan->work);
2610
2611 /* Abort iscan running in FW */
2612 memset(&ssid, 0, sizeof(ssid));
2613 brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002614
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002615 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002616 /* Indidate scan abort to cfg80211 layer */
2617 WL_INFO("Terminating scan in progress\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002618 cfg80211_scan_done(cfg->scan_request, true);
2619 cfg->scan_request = NULL;
Arend van Spriel108a4be2012-09-19 22:21:07 +02002620 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002621 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002622 if (cfg->escan_on && cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002623 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002624 brcmf_notify_escan_complete(cfg, escan->ndev, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002625 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002626 clear_bit(WL_STATUS_SCANNING, &cfg->status);
2627 clear_bit(WL_STATUS_SCAN_ABORTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002628}
2629
2630static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
2631 bool aborted)
2632{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002633 struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan);
2634 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002635
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002636 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002637 WL_ERR("Scan complete while device not scanning\n");
2638 return;
2639 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002640 if (cfg->scan_request) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002641 WL_SCAN("ISCAN Completed scan: %s\n",
2642 aborted ? "Aborted" : "Done");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002643 cfg80211_scan_done(cfg->scan_request, aborted);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002644 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002645 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002646 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002647 cfg->iscan_kickstart = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002648}
2649
2650static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan)
2651{
2652 if (iscan->state != WL_ISCAN_STATE_IDLE) {
2653 WL_SCAN("wake up iscan\n");
2654 schedule_work(&iscan->work);
2655 return 0;
2656 }
2657
2658 return -EIO;
2659}
2660
2661static s32
2662brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status,
2663 struct brcmf_scan_results **bss_list)
2664{
Arend van Spriel5b435de2011-10-05 13:19:03 +02002665 struct brcmf_scan_results *results;
2666 struct brcmf_scan_results_le *results_le;
2667 struct brcmf_iscan_results *list_buf;
2668 s32 err = 0;
2669
2670 memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX);
2671 list_buf = (struct brcmf_iscan_results *)iscan->scan_buf;
2672 results = &list_buf->results;
2673 results_le = &list_buf->results_le;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002674 results_le->buflen = cpu_to_le32(sizeof(iscan->scan_buf));
2675 results_le->version = 0;
2676 results_le->count = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002677
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002678 err = brcmf_fil_iovar_data_get(iscan->ndev, "iscanresults",
2679 iscan->scan_buf,
2680 sizeof(iscan->scan_buf));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002681 if (err) {
2682 WL_ERR("error (%d)\n", err);
2683 return err;
2684 }
2685 results->buflen = le32_to_cpu(results_le->buflen);
2686 results->version = le32_to_cpu(results_le->version);
2687 results->count = le32_to_cpu(results_le->count);
2688 WL_SCAN("results->count = %d\n", results_le->count);
2689 WL_SCAN("results->buflen = %d\n", results_le->buflen);
2690 *status = le32_to_cpu(list_buf->status_le);
2691 WL_SCAN("status = %d\n", *status);
2692 *bss_list = results;
2693
2694 return err;
2695}
2696
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002697static s32 brcmf_iscan_done(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002698{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002699 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002700 s32 err = 0;
2701
2702 iscan->state = WL_ISCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002703 brcmf_inform_bss(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002704 brcmf_notify_iscan_complete(iscan, false);
2705
2706 return err;
2707}
2708
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002709static s32 brcmf_iscan_pending(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002710{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002711 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002712 s32 err = 0;
2713
2714 /* Reschedule the timer */
2715 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
2716 iscan->timer_on = 1;
2717
2718 return err;
2719}
2720
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002721static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002722{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002723 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002724 s32 err = 0;
2725
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002726 brcmf_inform_bss(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002727 brcmf_run_iscan(iscan, NULL, BRCMF_SCAN_ACTION_CONTINUE);
2728 /* Reschedule the timer */
2729 mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
2730 iscan->timer_on = 1;
2731
2732 return err;
2733}
2734
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002735static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002736{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002737 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002738 s32 err = 0;
2739
2740 iscan->state = WL_ISCAN_STATE_IDLE;
2741 brcmf_notify_iscan_complete(iscan, true);
2742
2743 return err;
2744}
2745
2746static void brcmf_cfg80211_iscan_handler(struct work_struct *work)
2747{
2748 struct brcmf_cfg80211_iscan_ctrl *iscan =
2749 container_of(work, struct brcmf_cfg80211_iscan_ctrl,
2750 work);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002751 struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002752 struct brcmf_cfg80211_iscan_eloop *el = &iscan->el;
2753 u32 status = BRCMF_SCAN_RESULTS_PARTIAL;
2754
2755 if (iscan->timer_on) {
2756 del_timer_sync(&iscan->timer);
2757 iscan->timer_on = 0;
2758 }
2759
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002760 if (brcmf_get_iscan_results(iscan, &status, &cfg->bss_list)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002761 status = BRCMF_SCAN_RESULTS_ABORTED;
2762 WL_ERR("Abort iscan\n");
2763 }
2764
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002765 el->handler[status](cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002766}
2767
2768static void brcmf_iscan_timer(unsigned long data)
2769{
2770 struct brcmf_cfg80211_iscan_ctrl *iscan =
2771 (struct brcmf_cfg80211_iscan_ctrl *)data;
2772
2773 if (iscan) {
2774 iscan->timer_on = 0;
2775 WL_SCAN("timer expired\n");
2776 brcmf_wakeup_iscan(iscan);
2777 }
2778}
2779
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002780static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002781{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002782 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002783
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002784 if (cfg->iscan_on) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002785 iscan->state = WL_ISCAN_STATE_IDLE;
2786 INIT_WORK(&iscan->work, brcmf_cfg80211_iscan_handler);
2787 }
2788
2789 return 0;
2790}
2791
2792static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el)
2793{
2794 memset(el, 0, sizeof(*el));
2795 el->handler[BRCMF_SCAN_RESULTS_SUCCESS] = brcmf_iscan_done;
2796 el->handler[BRCMF_SCAN_RESULTS_PARTIAL] = brcmf_iscan_inprogress;
2797 el->handler[BRCMF_SCAN_RESULTS_PENDING] = brcmf_iscan_pending;
2798 el->handler[BRCMF_SCAN_RESULTS_ABORTED] = brcmf_iscan_aborted;
2799 el->handler[BRCMF_SCAN_RESULTS_NO_MEM] = brcmf_iscan_aborted;
2800}
2801
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002802static s32 brcmf_init_iscan(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002803{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002804 struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002805 int err = 0;
2806
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002807 if (cfg->iscan_on) {
2808 iscan->ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002809 brcmf_init_iscan_eloop(&iscan->el);
2810 iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS;
2811 init_timer(&iscan->timer);
2812 iscan->timer.data = (unsigned long) iscan;
2813 iscan->timer.function = brcmf_iscan_timer;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002814 err = brcmf_invoke_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002815 if (!err)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002816 iscan->data = cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002817 }
2818
2819 return err;
2820}
2821
Hante Meulemane756af52012-09-11 21:18:52 +02002822static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2823{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002824 struct brcmf_cfg80211_info *cfg =
2825 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002826 escan_timeout_work);
2827
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002828 brcmf_notify_escan_complete(cfg,
2829 cfg->escan_info.ndev, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002830}
2831
2832static void brcmf_escan_timeout(unsigned long data)
2833{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002834 struct brcmf_cfg80211_info *cfg =
2835 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002836
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002837 if (cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02002838 WL_ERR("timer expired\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002839 if (cfg->escan_on)
2840 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002841 }
2842}
2843
2844static s32
2845brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
2846 struct brcmf_bss_info_le *bss_info_le)
2847{
2848 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
2849 (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
2850 CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
2851 bss_info_le->SSID_len == bss->SSID_len &&
2852 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
2853 if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
2854 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002855 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2856 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2857
Hante Meulemane756af52012-09-11 21:18:52 +02002858 /* preserve max RSSI if the measurements are
2859 * both on-channel or both off-channel
2860 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002861 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002862 bss->RSSI = bss_info_le->RSSI;
2863 } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
2864 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
2865 /* preserve the on-channel rssi measurement
2866 * if the new measurement is off channel
2867 */
2868 bss->RSSI = bss_info_le->RSSI;
2869 bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
2870 }
2871 return 1;
2872 }
2873 return 0;
2874}
2875
2876static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002877brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +02002878 struct net_device *ndev,
2879 const struct brcmf_event_msg *e, void *data)
2880{
2881 s32 status;
2882 s32 err = 0;
2883 struct brcmf_escan_result_le *escan_result_le;
2884 struct brcmf_bss_info_le *bss_info_le;
2885 struct brcmf_bss_info_le *bss = NULL;
2886 u32 bi_length;
2887 struct brcmf_scan_results *list;
2888 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002889 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002890
2891 status = be32_to_cpu(e->status);
2892
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002893 if (!ndev || !cfg->escan_on ||
2894 !test_bit(WL_STATUS_SCANNING, &cfg->status)) {
Hante Meulemane756af52012-09-11 21:18:52 +02002895 WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002896 ndev, cfg->escan_on,
2897 !test_bit(WL_STATUS_SCANNING, &cfg->status));
Hante Meulemane756af52012-09-11 21:18:52 +02002898 return -EPERM;
2899 }
2900
2901 if (status == BRCMF_E_STATUS_PARTIAL) {
2902 WL_SCAN("ESCAN Partial result\n");
2903 escan_result_le = (struct brcmf_escan_result_le *) data;
2904 if (!escan_result_le) {
2905 WL_ERR("Invalid escan result (NULL pointer)\n");
2906 goto exit;
2907 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002908 if (!cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02002909 WL_SCAN("result without cfg80211 request\n");
2910 goto exit;
2911 }
2912
2913 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
2914 WL_ERR("Invalid bss_count %d: ignoring\n",
2915 escan_result_le->bss_count);
2916 goto exit;
2917 }
2918 bss_info_le = &escan_result_le->bss_info_le;
2919
2920 bi_length = le32_to_cpu(bss_info_le->length);
2921 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2922 WL_ESCAN_RESULTS_FIXED_SIZE)) {
2923 WL_ERR("Invalid bss_info length %d: ignoring\n",
2924 bi_length);
2925 goto exit;
2926 }
2927
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002928 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002929 BIT(NL80211_IFTYPE_ADHOC))) {
2930 if (le16_to_cpu(bss_info_le->capability) &
2931 WLAN_CAPABILITY_IBSS) {
2932 WL_ERR("Ignoring IBSS result\n");
2933 goto exit;
2934 }
2935 }
2936
2937 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002938 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002939 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
2940 WL_ERR("Buffer is too small: ignoring\n");
2941 goto exit;
2942 }
2943
2944 for (i = 0; i < list->count; i++) {
2945 bss = bss ? (struct brcmf_bss_info_le *)
2946 ((unsigned char *)bss +
2947 le32_to_cpu(bss->length)) : list->bss_info_le;
2948 if (brcmf_compare_update_same_bss(bss, bss_info_le))
2949 goto exit;
2950 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002951 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002952 bss_info_le, bi_length);
2953 list->version = le32_to_cpu(bss_info_le->version);
2954 list->buflen += bi_length;
2955 list->count++;
2956 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002957 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2958 if (cfg->scan_request) {
2959 cfg->bss_list = (struct brcmf_scan_results *)
2960 cfg->escan_info.escan_buf;
2961 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002962 aborted = status != BRCMF_E_STATUS_SUCCESS;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002963 brcmf_notify_escan_complete(cfg, ndev, aborted,
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002964 false);
Hante Meulemane756af52012-09-11 21:18:52 +02002965 } else
2966 WL_ERR("Unexpected scan result 0x%x\n", status);
2967 }
2968exit:
2969 return err;
2970}
2971
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002972static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002973{
2974
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002975 if (cfg->escan_on) {
2976 cfg->el.handler[BRCMF_E_ESCAN_RESULT] =
Hante Meulemane756af52012-09-11 21:18:52 +02002977 brcmf_cfg80211_escan_handler;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002978 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meulemane756af52012-09-11 21:18:52 +02002979 /* Init scan_timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002980 init_timer(&cfg->escan_timeout);
2981 cfg->escan_timeout.data = (unsigned long) cfg;
2982 cfg->escan_timeout.function = brcmf_escan_timeout;
2983 INIT_WORK(&cfg->escan_timeout_work,
Hante Meulemane756af52012-09-11 21:18:52 +02002984 brcmf_cfg80211_escan_timeout_worker);
2985 }
2986}
2987
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002988static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002989{
2990 if (ms < 1000 / HZ) {
2991 cond_resched();
2992 mdelay(ms);
2993 } else {
2994 msleep(ms);
2995 }
2996}
2997
2998static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
2999{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003000 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003001
3002 /*
3003 * Check for WL_STATUS_READY before any function call which
3004 * could result is bus access. Don't block the resume for
3005 * any driver error conditions
3006 */
3007 WL_TRACE("Enter\n");
3008
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003009 if (test_bit(WL_STATUS_READY, &cfg->status))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003010 brcmf_invoke_iscan(wiphy_to_cfg(wiphy));
3011
3012 WL_TRACE("Exit\n");
3013 return 0;
3014}
3015
3016static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
3017 struct cfg80211_wowlan *wow)
3018{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003019 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3020 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003021
3022 WL_TRACE("Enter\n");
3023
3024 /*
3025 * Check for WL_STATUS_READY before any function call which
3026 * could result is bus access. Don't block the suspend for
3027 * any driver error conditions
3028 */
3029
3030 /*
3031 * While going to suspend if associated with AP disassociate
3032 * from AP to save power while system is in suspended state
3033 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003034 if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
3035 test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
3036 test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02003037 WL_INFO("Disassociating from AP"
3038 " while entering suspend state\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003039 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003040
3041 /*
3042 * Make sure WPA_Supplicant receives all the event
3043 * generated due to DISASSOC call to the fw to keep
3044 * the state fw and WPA_Supplicant state consistent
3045 */
3046 brcmf_delay(500);
3047 }
3048
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003049 if (test_bit(WL_STATUS_READY, &cfg->status))
3050 brcmf_abort_scanning(cfg);
Arend van Spriel108a4be2012-09-19 22:21:07 +02003051 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003052 clear_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003053
3054 /* Turn off watchdog timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003055 if (test_bit(WL_STATUS_READY, &cfg->status))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003056 brcmf_set_mpc(ndev, 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003057
3058 WL_TRACE("Exit\n");
3059
3060 return 0;
3061}
3062
3063static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003064brcmf_update_pmklist(struct net_device *ndev,
3065 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3066{
3067 int i, j;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003068 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003069
Arend van Spriel40c8e952011-10-12 20:51:20 +02003070 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3071
3072 WL_CONN("No of elements %d\n", pmkid_len);
3073 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02003074 WL_CONN("PMKID[%d]: %pM =\n", i,
3075 &pmk_list->pmkids.pmkid[i].BSSID);
3076 for (j = 0; j < WLAN_PMKID_LEN; j++)
3077 WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]);
3078 }
3079
3080 if (!err)
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003081 brcmf_fil_iovar_data_set(ndev, "pmkid_info", (char *)pmk_list,
3082 sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003083
3084 return err;
3085}
3086
3087static s32
3088brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3089 struct cfg80211_pmksa *pmksa)
3090{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003091 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3092 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003093 s32 err = 0;
3094 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003095 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003096
3097 WL_TRACE("Enter\n");
3098 if (!check_sys_up(wiphy))
3099 return -EIO;
3100
Arend van Spriel40c8e952011-10-12 20:51:20 +02003101 pmkid_len = le32_to_cpu(pmkids->npmkid);
3102 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003103 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3104 break;
3105 if (i < WL_NUM_PMKIDS_MAX) {
3106 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3107 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003108 if (i == pmkid_len) {
3109 pmkid_len++;
3110 pmkids->npmkid = cpu_to_le32(pmkid_len);
3111 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003112 } else
3113 err = -EINVAL;
3114
3115 WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
Arend van Spriel40c8e952011-10-12 20:51:20 +02003116 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003117 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel40c8e952011-10-12 20:51:20 +02003118 WL_CONN("%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003119
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003120 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003121
3122 WL_TRACE("Exit\n");
3123 return err;
3124}
3125
3126static s32
3127brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3128 struct cfg80211_pmksa *pmksa)
3129{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003130 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003131 struct pmkid_list pmkid;
3132 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02003133 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003134
3135 WL_TRACE("Enter\n");
3136 if (!check_sys_up(wiphy))
3137 return -EIO;
3138
3139 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3140 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3141
3142 WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3143 &pmkid.pmkid[0].BSSID);
3144 for (i = 0; i < WLAN_PMKID_LEN; i++)
3145 WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]);
3146
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003147 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003148 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003149 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003150 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003151 ETH_ALEN))
3152 break;
3153
Arend van Spriel40c8e952011-10-12 20:51:20 +02003154 if ((pmkid_len > 0)
3155 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003156 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003157 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003158 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003159 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3160 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003161 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003162 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3163 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003164 WLAN_PMKID_LEN);
3165 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003166 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003167 } else
3168 err = -EINVAL;
3169
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003170 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003171
3172 WL_TRACE("Exit\n");
3173 return err;
3174
3175}
3176
3177static s32
3178brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3179{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003180 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003181 s32 err = 0;
3182
3183 WL_TRACE("Enter\n");
3184 if (!check_sys_up(wiphy))
3185 return -EIO;
3186
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003187 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3188 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003189
3190 WL_TRACE("Exit\n");
3191 return err;
3192
3193}
3194
Arend van Spriele5806072012-09-19 22:21:08 +02003195/*
3196 * PFN result doesn't have all the info which are
3197 * required by the supplicant
3198 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3199 * via wl_inform_single_bss in the required format. Escan does require the
3200 * scan request in the form of cfg80211_scan_request. For timebeing, create
3201 * cfg80211_scan_request one out of the received PNO event.
3202 */
3203static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003204brcmf_notify_sched_scan_results(struct brcmf_cfg80211_info *cfg,
Arend van Spriele5806072012-09-19 22:21:08 +02003205 struct net_device *ndev,
3206 const struct brcmf_event_msg *e, void *data)
3207{
3208 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3209 struct cfg80211_scan_request *request = NULL;
3210 struct cfg80211_ssid *ssid = NULL;
3211 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003212 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003213 int err = 0;
3214 int channel_req = 0;
3215 int band = 0;
3216 struct brcmf_pno_scanresults_le *pfn_result;
3217 u32 result_count;
3218 u32 status;
3219
3220 WL_SCAN("Enter\n");
3221
3222 if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) {
3223 WL_SCAN("PFN NET LOST event. Do Nothing\n");
3224 return 0;
3225 }
3226
3227 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3228 result_count = le32_to_cpu(pfn_result->count);
3229 status = le32_to_cpu(pfn_result->status);
3230
3231 /*
3232 * PFN event is limited to fit 512 bytes so we may get
3233 * multiple NET_FOUND events. For now place a warning here.
3234 */
3235 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3236 WL_SCAN("PFN NET FOUND event. count: %d\n", result_count);
3237 if (result_count > 0) {
3238 int i;
3239
3240 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003241 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3242 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003243 if (!request || !ssid || !channel) {
3244 err = -ENOMEM;
3245 goto out_err;
3246 }
3247
3248 request->wiphy = wiphy;
3249 data += sizeof(struct brcmf_pno_scanresults_le);
3250 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3251
3252 for (i = 0; i < result_count; i++) {
3253 netinfo = &netinfo_start[i];
3254 if (!netinfo) {
3255 WL_ERR("Invalid netinfo ptr. index: %d\n", i);
3256 err = -EINVAL;
3257 goto out_err;
3258 }
3259
3260 WL_SCAN("SSID:%s Channel:%d\n",
3261 netinfo->SSID, netinfo->channel);
3262 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3263 ssid[i].ssid_len = netinfo->SSID_len;
3264 request->n_ssids++;
3265
3266 channel_req = netinfo->channel;
3267 if (channel_req <= CH_MAX_2G_CHANNEL)
3268 band = NL80211_BAND_2GHZ;
3269 else
3270 band = NL80211_BAND_5GHZ;
3271 channel[i].center_freq =
3272 ieee80211_channel_to_frequency(channel_req,
3273 band);
3274 channel[i].band = band;
3275 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3276 request->channels[i] = &channel[i];
3277 request->n_channels++;
3278 }
3279
3280 /* assign parsed ssid array */
3281 if (request->n_ssids)
3282 request->ssids = &ssid[0];
3283
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003284 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003285 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003286 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003287 }
3288
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003289 set_bit(WL_STATUS_SCANNING, &cfg->status);
3290 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003291 if (err) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003292 clear_bit(WL_STATUS_SCANNING, &cfg->status);
Arend van Spriele5806072012-09-19 22:21:08 +02003293 goto out_err;
3294 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003295 cfg->sched_escan = true;
3296 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003297 } else {
3298 WL_ERR("FALSE PNO Event. (pfn_count == 0)\n");
3299 goto out_err;
3300 }
3301
3302 kfree(ssid);
3303 kfree(channel);
3304 kfree(request);
3305 return 0;
3306
3307out_err:
3308 kfree(ssid);
3309 kfree(channel);
3310 kfree(request);
3311 cfg80211_sched_scan_stopped(wiphy);
3312 return err;
3313}
3314
3315#ifndef CONFIG_BRCMISCAN
3316static int brcmf_dev_pno_clean(struct net_device *ndev)
3317{
Arend van Spriele5806072012-09-19 22:21:08 +02003318 int ret;
3319
3320 /* Disable pfn */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003321 ret = brcmf_fil_iovar_int_set(ndev, "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003322 if (ret == 0) {
3323 /* clear pfn */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003324 ret = brcmf_fil_iovar_data_set(ndev, "pfnclear", NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003325 }
3326 if (ret < 0)
3327 WL_ERR("failed code %d\n", ret);
3328
3329 return ret;
3330}
3331
3332static int brcmf_dev_pno_config(struct net_device *ndev)
3333{
3334 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003335
3336 memset(&pfn_param, 0, sizeof(pfn_param));
3337 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3338
3339 /* set extra pno params */
3340 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3341 pfn_param.repeat = BRCMF_PNO_REPEAT;
3342 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3343
3344 /* set up pno scan fr */
3345 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3346
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003347 return brcmf_fil_iovar_data_set(ndev, "pfn_set", &pfn_param,
3348 sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003349}
3350
3351static int
3352brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3353 struct net_device *ndev,
3354 struct cfg80211_sched_scan_request *request)
3355{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003356 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003357 struct brcmf_pno_net_param_le pfn;
3358 int i;
3359 int ret = 0;
3360
3361 WL_SCAN("Enter n_match_sets:%d n_ssids:%d\n",
3362 request->n_match_sets, request->n_ssids);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003363 if (test_bit(WL_STATUS_SCANNING, &cfg->status)) {
3364 WL_ERR("Scanning already : status (%lu)\n", cfg->status);
Arend van Spriele5806072012-09-19 22:21:08 +02003365 return -EAGAIN;
3366 }
3367
3368 if (!request || !request->n_ssids || !request->n_match_sets) {
3369 WL_ERR("Invalid sched scan req!! n_ssids:%d\n",
3370 request->n_ssids);
3371 return -EINVAL;
3372 }
3373
3374 if (request->n_ssids > 0) {
3375 for (i = 0; i < request->n_ssids; i++) {
3376 /* Active scan req for ssids */
3377 WL_SCAN(">>> Active scan req for ssid (%s)\n",
3378 request->ssids[i].ssid);
3379
3380 /*
3381 * match_set ssids is a supert set of n_ssid list,
3382 * so we need not add these set seperately.
3383 */
3384 }
3385 }
3386
3387 if (request->n_match_sets > 0) {
3388 /* clean up everything */
3389 ret = brcmf_dev_pno_clean(ndev);
3390 if (ret < 0) {
3391 WL_ERR("failed error=%d\n", ret);
3392 return ret;
3393 }
3394
3395 /* configure pno */
3396 ret = brcmf_dev_pno_config(ndev);
3397 if (ret < 0) {
3398 WL_ERR("PNO setup failed!! ret=%d\n", ret);
3399 return -EINVAL;
3400 }
3401
3402 /* configure each match set */
3403 for (i = 0; i < request->n_match_sets; i++) {
3404 struct cfg80211_ssid *ssid;
3405 u32 ssid_len;
3406
3407 ssid = &request->match_sets[i].ssid;
3408 ssid_len = ssid->ssid_len;
3409
3410 if (!ssid_len) {
3411 WL_ERR("skip broadcast ssid\n");
3412 continue;
3413 }
3414 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3415 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3416 pfn.wsec = cpu_to_le32(0);
3417 pfn.infra = cpu_to_le32(1);
3418 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3419 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3420 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003421 ret = brcmf_fil_iovar_data_set(ndev, "pfn_add",
3422 &pfn, sizeof(pfn));
Arend van Spriele5806072012-09-19 22:21:08 +02003423 WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
3424 ret == 0 ? "set" : "failed",
3425 ssid->ssid);
3426 }
3427 /* Enable the PNO */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003428 if (brcmf_fil_iovar_int_set(ndev, "pfn", 1) < 0) {
Arend van Spriele5806072012-09-19 22:21:08 +02003429 WL_ERR("PNO enable failed!! ret=%d\n", ret);
3430 return -EINVAL;
3431 }
3432 } else {
3433 return -EINVAL;
3434 }
3435
3436 return 0;
3437}
3438
3439static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3440 struct net_device *ndev)
3441{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003442 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003443
3444 WL_SCAN("enter\n");
3445 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003446 if (cfg->sched_escan)
3447 brcmf_notify_escan_complete(cfg, ndev, true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003448 return 0;
3449}
3450#endif /* CONFIG_BRCMISCAN */
3451
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003452#ifdef CONFIG_NL80211_TESTMODE
3453static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3454{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003455 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3456 struct net_device *ndev = cfg->wdev->netdev;
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003457 struct brcmf_dcmd *dcmd = data;
3458 struct sk_buff *reply;
3459 int ret;
3460
3461 ret = brcmf_netlink_dcmd(ndev, dcmd);
3462 if (ret == 0) {
3463 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3464 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3465 ret = cfg80211_testmode_reply(reply);
3466 }
3467 return ret;
3468}
3469#endif
3470
Hante Meuleman1a873342012-09-27 14:17:54 +02003471static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
3472{
3473 s32 err;
3474
3475 /* set auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003476 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003477 if (err < 0) {
3478 WL_ERR("auth error %d\n", err);
3479 return err;
3480 }
3481 /* set wsec */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003482 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003483 if (err < 0) {
3484 WL_ERR("wsec error %d\n", err);
3485 return err;
3486 }
3487 /* set upper-layer auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003488 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003489 if (err < 0) {
3490 WL_ERR("wpa_auth error %d\n", err);
3491 return err;
3492 }
3493
3494 return 0;
3495}
3496
3497static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3498{
3499 if (is_rsn_ie)
3500 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3501
3502 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3503}
3504
3505static s32
3506brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
3507 bool is_rsn_ie, s32 bssidx)
3508{
3509 u32 auth = 0; /* d11 open authentication */
3510 u16 count;
3511 s32 err = 0;
3512 s32 len = 0;
3513 u32 i;
3514 u32 wsec;
3515 u32 pval = 0;
3516 u32 gval = 0;
3517 u32 wpa_auth = 0;
3518 u32 offset;
3519 u8 *data;
3520 u16 rsn_cap;
3521 u32 wme_bss_disable;
3522
3523 WL_TRACE("Enter\n");
3524 if (wpa_ie == NULL)
3525 goto exit;
3526
3527 len = wpa_ie->len + TLV_HDR_LEN;
3528 data = (u8 *)wpa_ie;
3529 offset = 0;
3530 if (!is_rsn_ie)
3531 offset += VS_IE_FIXED_HDR_LEN;
3532 offset += WPA_IE_VERSION_LEN;
3533
3534 /* check for multicast cipher suite */
3535 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3536 err = -EINVAL;
3537 WL_ERR("no multicast cipher suite\n");
3538 goto exit;
3539 }
3540
3541 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3542 err = -EINVAL;
3543 WL_ERR("ivalid OUI\n");
3544 goto exit;
3545 }
3546 offset += TLV_OUI_LEN;
3547
3548 /* pick up multicast cipher */
3549 switch (data[offset]) {
3550 case WPA_CIPHER_NONE:
3551 gval = 0;
3552 break;
3553 case WPA_CIPHER_WEP_40:
3554 case WPA_CIPHER_WEP_104:
3555 gval = WEP_ENABLED;
3556 break;
3557 case WPA_CIPHER_TKIP:
3558 gval = TKIP_ENABLED;
3559 break;
3560 case WPA_CIPHER_AES_CCM:
3561 gval = AES_ENABLED;
3562 break;
3563 default:
3564 err = -EINVAL;
3565 WL_ERR("Invalid multi cast cipher info\n");
3566 goto exit;
3567 }
3568
3569 offset++;
3570 /* walk thru unicast cipher list and pick up what we recognize */
3571 count = data[offset] + (data[offset + 1] << 8);
3572 offset += WPA_IE_SUITE_COUNT_LEN;
3573 /* Check for unicast suite(s) */
3574 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3575 err = -EINVAL;
3576 WL_ERR("no unicast cipher suite\n");
3577 goto exit;
3578 }
3579 for (i = 0; i < count; i++) {
3580 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3581 err = -EINVAL;
3582 WL_ERR("ivalid OUI\n");
3583 goto exit;
3584 }
3585 offset += TLV_OUI_LEN;
3586 switch (data[offset]) {
3587 case WPA_CIPHER_NONE:
3588 break;
3589 case WPA_CIPHER_WEP_40:
3590 case WPA_CIPHER_WEP_104:
3591 pval |= WEP_ENABLED;
3592 break;
3593 case WPA_CIPHER_TKIP:
3594 pval |= TKIP_ENABLED;
3595 break;
3596 case WPA_CIPHER_AES_CCM:
3597 pval |= AES_ENABLED;
3598 break;
3599 default:
3600 WL_ERR("Ivalid unicast security info\n");
3601 }
3602 offset++;
3603 }
3604 /* walk thru auth management suite list and pick up what we recognize */
3605 count = data[offset] + (data[offset + 1] << 8);
3606 offset += WPA_IE_SUITE_COUNT_LEN;
3607 /* Check for auth key management suite(s) */
3608 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3609 err = -EINVAL;
3610 WL_ERR("no auth key mgmt suite\n");
3611 goto exit;
3612 }
3613 for (i = 0; i < count; i++) {
3614 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3615 err = -EINVAL;
3616 WL_ERR("ivalid OUI\n");
3617 goto exit;
3618 }
3619 offset += TLV_OUI_LEN;
3620 switch (data[offset]) {
3621 case RSN_AKM_NONE:
3622 WL_TRACE("RSN_AKM_NONE\n");
3623 wpa_auth |= WPA_AUTH_NONE;
3624 break;
3625 case RSN_AKM_UNSPECIFIED:
3626 WL_TRACE("RSN_AKM_UNSPECIFIED\n");
3627 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3628 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3629 break;
3630 case RSN_AKM_PSK:
3631 WL_TRACE("RSN_AKM_PSK\n");
3632 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3633 (wpa_auth |= WPA_AUTH_PSK);
3634 break;
3635 default:
3636 WL_ERR("Ivalid key mgmt info\n");
3637 }
3638 offset++;
3639 }
3640
3641 if (is_rsn_ie) {
3642 wme_bss_disable = 1;
3643 if ((offset + RSN_CAP_LEN) <= len) {
3644 rsn_cap = data[offset] + (data[offset + 1] << 8);
3645 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3646 wme_bss_disable = 0;
3647 }
3648 /* set wme_bss_disable to sync RSN Capabilities */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003649 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wme_bss_disable",
3650 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003651 if (err < 0) {
3652 WL_ERR("wme_bss_disable error %d\n", err);
3653 goto exit;
3654 }
3655 }
3656 /* FOR WPS , set SES_OW_ENABLED */
3657 wsec = (pval | gval | SES_OW_ENABLED);
3658
3659 /* set auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003660 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003661 if (err < 0) {
3662 WL_ERR("auth error %d\n", err);
3663 goto exit;
3664 }
3665 /* set wsec */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003666 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003667 if (err < 0) {
3668 WL_ERR("wsec error %d\n", err);
3669 goto exit;
3670 }
3671 /* set upper-layer auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003672 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003673 if (err < 0) {
3674 WL_ERR("wpa_auth error %d\n", err);
3675 goto exit;
3676 }
3677
3678exit:
3679 return err;
3680}
3681
3682static s32
3683brcmf_parse_vndr_ies(u8 *vndr_ie_buf, u32 vndr_ie_len,
3684 struct parsed_vndr_ies *vndr_ies)
3685{
3686 s32 err = 0;
3687 struct brcmf_vs_tlv *vndrie;
3688 struct brcmf_tlv *ie;
3689 struct parsed_vndr_ie_info *parsed_info;
3690 s32 remaining_len;
3691
3692 remaining_len = (s32)vndr_ie_len;
3693 memset(vndr_ies, 0, sizeof(*vndr_ies));
3694
3695 ie = (struct brcmf_tlv *)vndr_ie_buf;
3696 while (ie) {
3697 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3698 goto next;
3699 vndrie = (struct brcmf_vs_tlv *)ie;
3700 /* len should be bigger than OUI length + one */
3701 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
3702 WL_ERR("invalid vndr ie. length is too small %d\n",
3703 vndrie->len);
3704 goto next;
3705 }
3706 /* if wpa or wme ie, do not add ie */
3707 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3708 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3709 (vndrie->oui_type == WME_OUI_TYPE))) {
3710 WL_TRACE("Found WPA/WME oui. Do not add it\n");
3711 goto next;
3712 }
3713
3714 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3715
3716 /* save vndr ie information */
3717 parsed_info->ie_ptr = (char *)vndrie;
3718 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3719 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3720
3721 vndr_ies->count++;
3722
3723 WL_TRACE("** OUI %02x %02x %02x, type 0x%02x\n",
3724 parsed_info->vndrie.oui[0],
3725 parsed_info->vndrie.oui[1],
3726 parsed_info->vndrie.oui[2],
3727 parsed_info->vndrie.oui_type);
3728
3729 if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
3730 break;
3731next:
3732 remaining_len -= ie->len;
3733 if (remaining_len <= 2)
3734 ie = NULL;
3735 else
3736 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len);
3737 }
3738 return err;
3739}
3740
3741static u32
3742brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3743{
3744
3745 __le32 iecount_le;
3746 __le32 pktflag_le;
3747
3748 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3749 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3750
3751 iecount_le = cpu_to_le32(1);
3752 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3753
3754 pktflag_le = cpu_to_le32(pktflag);
3755 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3756
3757 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3758
3759 return ie_len + VNDR_IE_HDR_SIZE;
3760}
3761
Franky Lin3cb91f52012-10-10 11:13:08 -07003762static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003763brcmf_set_management_ie(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02003764 struct net_device *ndev, s32 bssidx, s32 pktflag,
3765 u8 *vndr_ie_buf, u32 vndr_ie_len)
3766{
3767 s32 err = 0;
3768 u8 *iovar_ie_buf;
3769 u8 *curr_ie_buf;
3770 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003771 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003772 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003773 u32 del_add_ie_buf_len = 0;
3774 u32 total_ie_buf_len = 0;
3775 u32 parsed_ie_buf_len = 0;
3776 struct parsed_vndr_ies old_vndr_ies;
3777 struct parsed_vndr_ies new_vndr_ies;
3778 struct parsed_vndr_ie_info *vndrie_info;
3779 s32 i;
3780 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003781 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003782
3783 WL_TRACE("bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag);
3784 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3785 if (!iovar_ie_buf)
3786 return -ENOMEM;
3787 curr_ie_buf = iovar_ie_buf;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003788 if (test_bit(WL_STATUS_AP_CREATING, &cfg->status) ||
3789 test_bit(WL_STATUS_AP_CREATED, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003790 switch (pktflag) {
3791 case VNDR_IE_PRBRSP_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003792 mgmt_ie_buf = cfg->ap_info->probe_res_ie;
3793 mgmt_ie_len = &cfg->ap_info->probe_res_ie_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003794 mgmt_ie_buf_len = sizeof(cfg->ap_info->probe_res_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003795 break;
3796 case VNDR_IE_BEACON_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003797 mgmt_ie_buf = cfg->ap_info->beacon_ie;
3798 mgmt_ie_len = &cfg->ap_info->beacon_ie_len;
3799 mgmt_ie_buf_len = sizeof(cfg->ap_info->beacon_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003800 break;
3801 default:
3802 err = -EPERM;
3803 WL_ERR("not suitable type\n");
3804 goto exit;
3805 }
3806 bssidx = 0;
3807 } else {
3808 err = -EPERM;
3809 WL_ERR("not suitable type\n");
3810 goto exit;
3811 }
3812
3813 if (vndr_ie_len > mgmt_ie_buf_len) {
3814 err = -ENOMEM;
3815 WL_ERR("extra IE size too big\n");
3816 goto exit;
3817 }
3818
3819 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3820 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3821 ptr = curr_ie_buf;
3822 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3823 for (i = 0; i < new_vndr_ies.count; i++) {
3824 vndrie_info = &new_vndr_ies.ie_info[i];
3825 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3826 vndrie_info->ie_len);
3827 parsed_ie_buf_len += vndrie_info->ie_len;
3828 }
3829 }
3830
3831 if (mgmt_ie_buf != NULL) {
3832 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3833 (memcmp(mgmt_ie_buf, curr_ie_buf,
3834 parsed_ie_buf_len) == 0)) {
3835 WL_TRACE("Previous mgmt IE is equals to current IE");
3836 goto exit;
3837 }
3838
3839 /* parse old vndr_ie */
3840 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3841
3842 /* make a command to delete old ie */
3843 for (i = 0; i < old_vndr_ies.count; i++) {
3844 vndrie_info = &old_vndr_ies.ie_info[i];
3845
3846 WL_TRACE("DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3847 vndrie_info->vndrie.id,
3848 vndrie_info->vndrie.len,
3849 vndrie_info->vndrie.oui[0],
3850 vndrie_info->vndrie.oui[1],
3851 vndrie_info->vndrie.oui[2]);
3852
3853 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3854 vndrie_info->ie_ptr,
3855 vndrie_info->ie_len,
3856 "del");
3857 curr_ie_buf += del_add_ie_buf_len;
3858 total_ie_buf_len += del_add_ie_buf_len;
3859 }
3860 }
3861
3862 *mgmt_ie_len = 0;
3863 /* Add if there is any extra IE */
3864 if (mgmt_ie_buf && parsed_ie_buf_len) {
3865 ptr = mgmt_ie_buf;
3866
3867 remained_buf_len = mgmt_ie_buf_len;
3868
3869 /* make a command to add new ie */
3870 for (i = 0; i < new_vndr_ies.count; i++) {
3871 vndrie_info = &new_vndr_ies.ie_info[i];
3872
3873 WL_TRACE("ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3874 vndrie_info->vndrie.id,
3875 vndrie_info->vndrie.len,
3876 vndrie_info->vndrie.oui[0],
3877 vndrie_info->vndrie.oui[1],
3878 vndrie_info->vndrie.oui[2]);
3879
3880 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3881 vndrie_info->ie_ptr,
3882 vndrie_info->ie_len,
3883 "add");
3884 /* verify remained buf size before copy data */
3885 remained_buf_len -= vndrie_info->ie_len;
3886 if (remained_buf_len < 0) {
3887 WL_ERR("no space in mgmt_ie_buf: len left %d",
3888 remained_buf_len);
3889 break;
3890 }
3891
3892 /* save the parsed IE in wl struct */
3893 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3894 vndrie_info->ie_len);
3895 *mgmt_ie_len += vndrie_info->ie_len;
3896
3897 curr_ie_buf += del_add_ie_buf_len;
3898 total_ie_buf_len += del_add_ie_buf_len;
3899 }
3900 }
3901 if (total_ie_buf_len) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003902 err = brcmf_fil_bsscfg_data_set(ndev, bssidx, "vndr_ie",
3903 iovar_ie_buf,
3904 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003905 if (err)
3906 WL_ERR("vndr ie set error : %d\n", err);
3907 }
3908
3909exit:
3910 kfree(iovar_ie_buf);
3911 return err;
3912}
3913
3914static s32
3915brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3916 struct cfg80211_ap_settings *settings)
3917{
3918 s32 ie_offset;
3919 struct brcmf_tlv *ssid_ie;
3920 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003921 s32 err = -EPERM;
3922 struct brcmf_tlv *rsn_ie;
3923 struct brcmf_vs_tlv *wpa_ie;
3924 struct brcmf_join_params join_params;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003925 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02003926 s32 bssidx = 0;
3927
3928 WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
3929 settings->channel_type, settings->beacon_interval,
3930 settings->dtim_period);
3931 WL_TRACE("ssid=%s(%d), auth_type=%d, inactivity_timeout=%d\n",
3932 settings->ssid, settings->ssid_len, settings->auth_type,
3933 settings->inactivity_timeout);
3934
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003935 if (!test_bit(WL_STATUS_AP_CREATING, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003936 WL_ERR("Not in AP creation mode\n");
3937 return -EPERM;
3938 }
3939
3940 memset(&ssid_le, 0, sizeof(ssid_le));
3941 if (settings->ssid == NULL || settings->ssid_len == 0) {
3942 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
3943 ssid_ie = brcmf_parse_tlvs(
3944 (u8 *)&settings->beacon.head[ie_offset],
3945 settings->beacon.head_len - ie_offset,
3946 WLAN_EID_SSID);
3947 if (!ssid_ie)
3948 return -EINVAL;
3949
3950 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
3951 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
3952 WL_TRACE("SSID is (%s) in Head\n", ssid_le.SSID);
3953 } else {
3954 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
3955 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3956 }
3957
3958 brcmf_set_mpc(ndev, 0);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003959 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_DOWN, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003960 if (err < 0) {
3961 WL_ERR("BRCMF_C_DOWN error %d\n", err);
3962 goto exit;
3963 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003964 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003965 if (err < 0) {
3966 WL_ERR("SET INFRA error %d\n", err);
3967 goto exit;
3968 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003969 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_AP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003970 if (err < 0) {
3971 WL_ERR("setting AP mode failed %d\n", err);
3972 goto exit;
3973 }
3974
3975 /* find the RSN_IE */
3976 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
3977 settings->beacon.tail_len, WLAN_EID_RSN);
3978
3979 /* find the WPA_IE */
3980 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
3981 settings->beacon.tail_len);
3982
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003983 kfree(cfg->ap_info->rsn_ie);
3984 cfg->ap_info->rsn_ie = NULL;
3985 kfree(cfg->ap_info->wpa_ie);
3986 cfg->ap_info->wpa_ie = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02003987
3988 if ((wpa_ie != NULL || rsn_ie != NULL)) {
3989 WL_TRACE("WPA(2) IE is found\n");
3990 if (wpa_ie != NULL) {
3991 /* WPA IE */
3992 err = brcmf_configure_wpaie(ndev, wpa_ie, false,
3993 bssidx);
3994 if (err < 0)
3995 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003996 cfg->ap_info->wpa_ie = kmemdup(wpa_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02003997 wpa_ie->len +
3998 TLV_HDR_LEN,
3999 GFP_KERNEL);
4000 } else {
4001 /* RSN IE */
4002 err = brcmf_configure_wpaie(ndev,
4003 (struct brcmf_vs_tlv *)rsn_ie, true, bssidx);
4004 if (err < 0)
4005 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004006 cfg->ap_info->rsn_ie = kmemdup(rsn_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02004007 rsn_ie->len +
4008 TLV_HDR_LEN,
4009 GFP_KERNEL);
4010 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004011 cfg->ap_info->security_mode = true;
Hante Meuleman1a873342012-09-27 14:17:54 +02004012 } else {
4013 WL_TRACE("No WPA(2) IEs found\n");
4014 brcmf_configure_opensecurity(ndev, bssidx);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004015 cfg->ap_info->security_mode = false;
Hante Meuleman1a873342012-09-27 14:17:54 +02004016 }
4017 /* Set Beacon IEs to FW */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004018 err = brcmf_set_management_ie(cfg, ndev, bssidx,
Hante Meuleman1a873342012-09-27 14:17:54 +02004019 VNDR_IE_BEACON_FLAG,
4020 (u8 *)settings->beacon.tail,
4021 settings->beacon.tail_len);
4022 if (err)
4023 WL_ERR("Set Beacon IE Failed\n");
4024 else
4025 WL_TRACE("Applied Vndr IEs for Beacon\n");
4026
4027 /* Set Probe Response IEs to FW */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004028 err = brcmf_set_management_ie(cfg, ndev, bssidx,
Hante Meuleman1a873342012-09-27 14:17:54 +02004029 VNDR_IE_PRBRSP_FLAG,
4030 (u8 *)settings->beacon.proberesp_ies,
4031 settings->beacon.proberesp_ies_len);
4032 if (err)
4033 WL_ERR("Set Probe Resp IE Failed\n");
4034 else
4035 WL_TRACE("Applied Vndr IEs for Probe Resp\n");
4036
4037 if (settings->beacon_interval) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004038 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_BCNPRD,
4039 settings->beacon_interval);
Hante Meuleman1a873342012-09-27 14:17:54 +02004040 if (err < 0) {
4041 WL_ERR("Beacon Interval Set Error, %d\n", err);
4042 goto exit;
4043 }
4044 }
4045 if (settings->dtim_period) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004046 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_DTIMPRD,
4047 settings->dtim_period);
Hante Meuleman1a873342012-09-27 14:17:54 +02004048 if (err < 0) {
4049 WL_ERR("DTIM Interval Set Error, %d\n", err);
4050 goto exit;
4051 }
4052 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004053 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_UP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004054 if (err < 0) {
4055 WL_ERR("BRCMF_C_UP error (%d)\n", err);
4056 goto exit;
4057 }
4058
4059 memset(&join_params, 0, sizeof(join_params));
4060 /* join parameters starts with ssid */
4061 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4062 /* create softap */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004063 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_SSID, &join_params,
4064 sizeof(join_params));
Hante Meuleman1a873342012-09-27 14:17:54 +02004065 if (err < 0) {
4066 WL_ERR("SET SSID error (%d)\n", err);
4067 goto exit;
4068 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004069 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4070 set_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004071
4072exit:
4073 if (err)
4074 brcmf_set_mpc(ndev, 1);
4075 return err;
4076}
4077
4078static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4079{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004080 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004081 s32 err = -EPERM;
4082
4083 WL_TRACE("Enter\n");
4084
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004085 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004086 /* Due to most likely deauths outstanding we sleep */
4087 /* first to make sure they get processed by fw. */
4088 msleep(400);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004089 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_AP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02004090 if (err < 0) {
4091 WL_ERR("setting AP mode failed %d\n", err);
4092 goto exit;
4093 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004094 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_UP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02004095 if (err < 0) {
4096 WL_ERR("BRCMF_C_UP error %d\n", err);
4097 goto exit;
4098 }
4099 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004100 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4101 clear_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004102 }
4103exit:
4104 return err;
4105}
4106
4107static int
4108brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
4109 u8 *mac)
4110{
4111 struct brcmf_scb_val_le scbval;
4112 s32 err;
4113
4114 if (!mac)
4115 return -EFAULT;
4116
4117 WL_TRACE("Enter %pM\n", mac);
4118
4119 if (!check_sys_up(wiphy))
4120 return -EIO;
4121
4122 memcpy(&scbval.ea, mac, ETH_ALEN);
4123 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004124 err = brcmf_fil_cmd_data_set(ndev,
4125 BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
4126 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004127 if (err)
4128 WL_ERR("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
4129
4130 WL_TRACE("Exit\n");
4131 return err;
4132}
4133
Arend van Spriel5b435de2011-10-05 13:19:03 +02004134static struct cfg80211_ops wl_cfg80211_ops = {
4135 .change_virtual_intf = brcmf_cfg80211_change_iface,
4136 .scan = brcmf_cfg80211_scan,
4137 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4138 .join_ibss = brcmf_cfg80211_join_ibss,
4139 .leave_ibss = brcmf_cfg80211_leave_ibss,
4140 .get_station = brcmf_cfg80211_get_station,
4141 .set_tx_power = brcmf_cfg80211_set_tx_power,
4142 .get_tx_power = brcmf_cfg80211_get_tx_power,
4143 .add_key = brcmf_cfg80211_add_key,
4144 .del_key = brcmf_cfg80211_del_key,
4145 .get_key = brcmf_cfg80211_get_key,
4146 .set_default_key = brcmf_cfg80211_config_default_key,
4147 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4148 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
4149 .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
4150 .connect = brcmf_cfg80211_connect,
4151 .disconnect = brcmf_cfg80211_disconnect,
4152 .suspend = brcmf_cfg80211_suspend,
4153 .resume = brcmf_cfg80211_resume,
4154 .set_pmksa = brcmf_cfg80211_set_pmksa,
4155 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004156 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004157 .start_ap = brcmf_cfg80211_start_ap,
4158 .stop_ap = brcmf_cfg80211_stop_ap,
4159 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004160#ifndef CONFIG_BRCMISCAN
4161 /* scheduled scan need e-scan, which is mutual exclusive with i-scan */
4162 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4163 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
4164#endif
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004165#ifdef CONFIG_NL80211_TESTMODE
4166 .testmode_cmd = brcmf_cfg80211_testmode
4167#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02004168};
4169
4170static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
4171{
4172 s32 err = 0;
4173
4174 switch (mode) {
4175 case WL_MODE_BSS:
4176 return NL80211_IFTYPE_STATION;
4177 case WL_MODE_IBSS:
4178 return NL80211_IFTYPE_ADHOC;
4179 default:
4180 return NL80211_IFTYPE_UNSPECIFIED;
4181 }
4182
4183 return err;
4184}
4185
Arend van Spriele5806072012-09-19 22:21:08 +02004186static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
4187{
4188#ifndef CONFIG_BRCMFISCAN
4189 /* scheduled scan settings */
4190 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
4191 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
4192 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
4193 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
4194#endif
4195}
4196
Arend van Spriel5db6e952012-09-27 14:17:53 +02004197static struct wireless_dev *brcmf_alloc_wdev(struct device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004198{
4199 struct wireless_dev *wdev;
4200 s32 err = 0;
4201
4202 wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
4203 if (!wdev)
4204 return ERR_PTR(-ENOMEM);
4205
Arend van Spriel5db6e952012-09-27 14:17:53 +02004206 wdev->wiphy = wiphy_new(&wl_cfg80211_ops,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004207 sizeof(struct brcmf_cfg80211_info));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004208 if (!wdev->wiphy) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004209 WL_ERR("Could not allocate wiphy device\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004210 err = -ENOMEM;
4211 goto wiphy_new_out;
4212 }
4213 set_wiphy_dev(wdev->wiphy, ndev);
4214 wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
4215 wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Hante Meuleman1a873342012-09-27 14:17:54 +02004216 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
4217 BIT(NL80211_IFTYPE_ADHOC) |
4218 BIT(NL80211_IFTYPE_AP);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004219 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
4220 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
4221 * it as 11a by default.
4222 * This will be updated with
4223 * 11n phy tables in
4224 * "ifconfig up"
4225 * if phy has 11n capability
4226 */
4227 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
4228 wdev->wiphy->cipher_suites = __wl_cipher_suites;
4229 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
4230 wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
4231 * save mode
4232 * by default
4233 */
Arend van Spriele5806072012-09-19 22:21:08 +02004234 brcmf_wiphy_pno_params(wdev->wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004235 err = wiphy_register(wdev->wiphy);
4236 if (err < 0) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004237 WL_ERR("Could not register wiphy device (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004238 goto wiphy_register_out;
4239 }
4240 return wdev;
4241
4242wiphy_register_out:
4243 wiphy_free(wdev->wiphy);
4244
4245wiphy_new_out:
4246 kfree(wdev);
4247
4248 return ERR_PTR(err);
4249}
4250
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004251static void brcmf_free_wdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004252{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004253 struct wireless_dev *wdev = cfg->wdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004254
4255 if (!wdev) {
4256 WL_ERR("wdev is invalid\n");
4257 return;
4258 }
4259 wiphy_unregister(wdev->wiphy);
4260 wiphy_free(wdev->wiphy);
4261 kfree(wdev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004262 cfg->wdev = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004263}
4264
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004265static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004266 const struct brcmf_event_msg *e)
4267{
4268 u32 event = be32_to_cpu(e->event_type);
4269 u32 status = be32_to_cpu(e->status);
4270
4271 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
4272 WL_CONN("Processing set ssid\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004273 cfg->link_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004274 return true;
4275 }
4276
4277 return false;
4278}
4279
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004280static bool brcmf_is_linkdown(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004281 const struct brcmf_event_msg *e)
4282{
4283 u32 event = be32_to_cpu(e->event_type);
4284 u16 flags = be16_to_cpu(e->flags);
4285
4286 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
4287 WL_CONN("Processing link down\n");
4288 return true;
4289 }
4290 return false;
4291}
4292
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004293static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004294 const struct brcmf_event_msg *e)
4295{
4296 u32 event = be32_to_cpu(e->event_type);
4297 u32 status = be32_to_cpu(e->status);
4298
4299 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
4300 WL_CONN("Processing Link %s & no network found\n",
4301 be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ?
4302 "up" : "down");
4303 return true;
4304 }
4305
4306 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
4307 WL_CONN("Processing connecting & no network found\n");
4308 return true;
4309 }
4310
4311 return false;
4312}
4313
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004314static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004315{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004316 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004317
4318 kfree(conn_info->req_ie);
4319 conn_info->req_ie = NULL;
4320 conn_info->req_ie_len = 0;
4321 kfree(conn_info->resp_ie);
4322 conn_info->resp_ie = NULL;
4323 conn_info->resp_ie_len = 0;
4324}
4325
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004326static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004327{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004328 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004329 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004330 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004331 u32 req_len;
4332 u32 resp_len;
4333 s32 err = 0;
4334
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004335 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004336
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004337 err = brcmf_fil_iovar_data_get(ndev, "assoc_info", cfg->extra_buf,
4338 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004339 if (err) {
4340 WL_ERR("could not get assoc info (%d)\n", err);
4341 return err;
4342 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004343 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004344 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004345 req_len = le32_to_cpu(assoc_info->req_len);
4346 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004347 if (req_len) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004348 err = brcmf_fil_iovar_data_get(ndev, "assoc_req_ies",
4349 cfg->extra_buf,
4350 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004351 if (err) {
4352 WL_ERR("could not get assoc req (%d)\n", err);
4353 return err;
4354 }
4355 conn_info->req_ie_len = req_len;
4356 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004357 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004358 GFP_KERNEL);
4359 } else {
4360 conn_info->req_ie_len = 0;
4361 conn_info->req_ie = NULL;
4362 }
4363 if (resp_len) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004364 err = brcmf_fil_iovar_data_get(ndev, "assoc_resp_ies",
4365 cfg->extra_buf,
4366 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004367 if (err) {
4368 WL_ERR("could not get assoc resp (%d)\n", err);
4369 return err;
4370 }
4371 conn_info->resp_ie_len = resp_len;
4372 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004373 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004374 GFP_KERNEL);
4375 } else {
4376 conn_info->resp_ie_len = 0;
4377 conn_info->resp_ie = NULL;
4378 }
4379 WL_CONN("req len (%d) resp len (%d)\n",
4380 conn_info->req_ie_len, conn_info->resp_ie_len);
4381
4382 return err;
4383}
4384
4385static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004386brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004387 struct net_device *ndev,
4388 const struct brcmf_event_msg *e)
4389{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004390 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004391 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4392 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004393 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004394 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004395 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004396 u32 freq;
4397 s32 err = 0;
4398 u32 target_channel;
Franky Lina180b832012-10-10 11:13:09 -07004399 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004400
4401 WL_TRACE("Enter\n");
4402
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004403 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004404 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004405 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004406
Franky Lina180b832012-10-10 11:13:09 -07004407 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4408 if (buf == NULL) {
4409 err = -ENOMEM;
4410 goto done;
4411 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004412
Franky Lina180b832012-10-10 11:13:09 -07004413 /* data sent to dongle has to be little endian */
4414 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004415 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_GET_BSS_INFO, buf,
4416 WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004417
4418 if (err)
4419 goto done;
4420
4421 bi = (struct brcmf_bss_info_le *)(buf + 4);
4422 target_channel = bi->ctl_ch ? bi->ctl_ch :
4423 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004424
4425 if (target_channel <= CH_MAX_2G_CHANNEL)
4426 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4427 else
4428 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4429
4430 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4431 notify_channel = ieee80211_get_channel(wiphy, freq);
4432
Franky Lina180b832012-10-10 11:13:09 -07004433done:
4434 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004435 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004436 conn_info->req_ie, conn_info->req_ie_len,
4437 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
4438 WL_CONN("Report roaming result\n");
4439
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004440 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004441 WL_TRACE("Exit\n");
4442 return err;
4443}
4444
4445static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004446brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004447 struct net_device *ndev, const struct brcmf_event_msg *e,
4448 bool completed)
4449{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004450 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004451 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004452 s32 err = 0;
4453
4454 WL_TRACE("Enter\n");
4455
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004456 if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004457 if (completed) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004458 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004459 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004460 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004461 }
4462 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004463 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004464 conn_info->req_ie,
4465 conn_info->req_ie_len,
4466 conn_info->resp_ie,
4467 conn_info->resp_ie_len,
4468 completed ? WLAN_STATUS_SUCCESS :
4469 WLAN_STATUS_AUTH_TIMEOUT,
4470 GFP_KERNEL);
4471 if (completed)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004472 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004473 WL_CONN("Report connect result - connection %s\n",
4474 completed ? "succeeded" : "failed");
4475 }
4476 WL_TRACE("Exit\n");
4477 return err;
4478}
4479
4480static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004481brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004482 struct net_device *ndev,
4483 const struct brcmf_event_msg *e, void *data)
4484{
4485 s32 err = 0;
4486 u32 event = be32_to_cpu(e->event_type);
4487 u32 reason = be32_to_cpu(e->reason);
4488 u32 len = be32_to_cpu(e->datalen);
4489 static int generation;
4490
4491 struct station_info sinfo;
4492
4493 WL_CONN("event %d, reason %d\n", event, reason);
4494 memset(&sinfo, 0, sizeof(sinfo));
4495
4496 sinfo.filled = 0;
4497 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
4498 reason == BRCMF_E_STATUS_SUCCESS) {
4499 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4500 if (!data) {
4501 WL_ERR("No IEs present in ASSOC/REASSOC_IND");
4502 return -EINVAL;
4503 }
4504 sinfo.assoc_req_ies = data;
4505 sinfo.assoc_req_ies_len = len;
4506 generation++;
4507 sinfo.generation = generation;
4508 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_ATOMIC);
4509 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4510 (event == BRCMF_E_DEAUTH_IND) ||
4511 (event == BRCMF_E_DEAUTH)) {
4512 generation++;
4513 sinfo.generation = generation;
4514 cfg80211_del_sta(ndev, e->addr, GFP_ATOMIC);
4515 }
4516 return err;
4517}
4518
4519static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004520brcmf_notify_connect_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004521 struct net_device *ndev,
4522 const struct brcmf_event_msg *e, void *data)
4523{
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004524 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004525 s32 err = 0;
4526
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004527 if (cfg->conf->mode == WL_MODE_AP) {
4528 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
4529 } else if (brcmf_is_linkup(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004530 WL_CONN("Linkup\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004531 if (brcmf_is_ibssmode(cfg)) {
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004532 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004533 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004534 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004535 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
4536 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004537 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004538 brcmf_bss_connect_done(cfg, ndev, e, true);
4539 } else if (brcmf_is_linkdown(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004540 WL_CONN("Linkdown\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004541 if (brcmf_is_ibssmode(cfg)) {
4542 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004543 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004544 &cfg->status))
4545 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004546 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004547 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004548 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004549 &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004550 cfg80211_disconnected(ndev, 0, NULL, 0,
4551 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004552 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004553 }
4554 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004555 brcmf_init_prof(cfg->profile);
4556 } else if (brcmf_is_nonetwork(cfg, e)) {
4557 if (brcmf_is_ibssmode(cfg))
4558 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004559 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004560 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004561 }
4562
4563 return err;
4564}
4565
4566static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004567brcmf_notify_roaming_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004568 struct net_device *ndev,
4569 const struct brcmf_event_msg *e, void *data)
4570{
4571 s32 err = 0;
4572 u32 event = be32_to_cpu(e->event_type);
4573 u32 status = be32_to_cpu(e->status);
4574
4575 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004576 if (test_bit(WL_STATUS_CONNECTED, &cfg->status))
4577 brcmf_bss_roaming_done(cfg, ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004578 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004579 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004580 }
4581
4582 return err;
4583}
4584
4585static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004586brcmf_notify_mic_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004587 struct net_device *ndev,
4588 const struct brcmf_event_msg *e, void *data)
4589{
4590 u16 flags = be16_to_cpu(e->flags);
4591 enum nl80211_key_type key_type;
4592
4593 if (flags & BRCMF_EVENT_MSG_GROUP)
4594 key_type = NL80211_KEYTYPE_GROUP;
4595 else
4596 key_type = NL80211_KEYTYPE_PAIRWISE;
4597
4598 cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1,
4599 NULL, GFP_KERNEL);
4600
4601 return 0;
4602}
4603
4604static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004605brcmf_notify_scan_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004606 struct net_device *ndev,
4607 const struct brcmf_event_msg *e, void *data)
4608{
4609 struct brcmf_channel_info_le channel_inform_le;
4610 struct brcmf_scan_results_le *bss_list_le;
4611 u32 len = WL_SCAN_BUF_MAX;
4612 s32 err = 0;
4613 bool scan_abort = false;
4614 u32 scan_channel;
4615
4616 WL_TRACE("Enter\n");
4617
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004618 if (cfg->iscan_on && cfg->iscan_kickstart) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004619 WL_TRACE("Exit\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004620 return brcmf_wakeup_iscan(cfg_to_iscan(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004621 }
4622
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004623 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004624 WL_ERR("Scan complete while device not scanning\n");
4625 scan_abort = true;
4626 err = -EINVAL;
4627 goto scan_done_out;
4628 }
4629
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004630 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_GET_CHANNEL,
4631 &channel_inform_le,
4632 sizeof(channel_inform_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004633 if (err) {
4634 WL_ERR("scan busy (%d)\n", err);
4635 scan_abort = true;
4636 goto scan_done_out;
4637 }
4638 scan_channel = le32_to_cpu(channel_inform_le.scan_channel);
4639 if (scan_channel)
4640 WL_CONN("channel_inform.scan_channel (%d)\n", scan_channel);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004641 cfg->bss_list = cfg->scan_results;
4642 bss_list_le = (struct brcmf_scan_results_le *) cfg->bss_list;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004643
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004644 memset(cfg->scan_results, 0, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004645 bss_list_le->buflen = cpu_to_le32(len);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004646 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_SCAN_RESULTS,
4647 cfg->scan_results, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004648 if (err) {
4649 WL_ERR("%s Scan_results error (%d)\n", ndev->name, err);
4650 err = -EINVAL;
4651 scan_abort = true;
4652 goto scan_done_out;
4653 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004654 cfg->scan_results->buflen = le32_to_cpu(bss_list_le->buflen);
4655 cfg->scan_results->version = le32_to_cpu(bss_list_le->version);
4656 cfg->scan_results->count = le32_to_cpu(bss_list_le->count);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004657
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004658 err = brcmf_inform_bss(cfg);
Hante Meuleman35aafa92012-09-11 21:18:49 +02004659 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004660 scan_abort = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004661
4662scan_done_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004663 if (cfg->scan_request) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004664 WL_SCAN("calling cfg80211_scan_done\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004665 cfg80211_scan_done(cfg->scan_request, scan_abort);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004666 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004667 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004668 }
4669
4670 WL_TRACE("Exit\n");
4671
4672 return err;
4673}
4674
4675static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4676{
4677 conf->mode = (u32)-1;
4678 conf->frag_threshold = (u32)-1;
4679 conf->rts_threshold = (u32)-1;
4680 conf->retry_short = (u32)-1;
4681 conf->retry_long = (u32)-1;
4682 conf->tx_power = -1;
4683}
4684
4685static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
4686{
4687 memset(el, 0, sizeof(*el));
4688 el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status;
4689 el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status;
Hante Meuleman1a873342012-09-27 14:17:54 +02004690 el->handler[BRCMF_E_DEAUTH_IND] = brcmf_notify_connect_status;
4691 el->handler[BRCMF_E_DEAUTH] = brcmf_notify_connect_status;
4692 el->handler[BRCMF_E_DISASSOC_IND] = brcmf_notify_connect_status;
4693 el->handler[BRCMF_E_ASSOC_IND] = brcmf_notify_connect_status;
4694 el->handler[BRCMF_E_REASSOC_IND] = brcmf_notify_connect_status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004695 el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
4696 el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
4697 el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
Arend van Spriele5806072012-09-19 22:21:08 +02004698 el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004699}
4700
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004701static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004702{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004703 kfree(cfg->scan_results);
4704 cfg->scan_results = NULL;
4705 kfree(cfg->bss_info);
4706 cfg->bss_info = NULL;
4707 kfree(cfg->conf);
4708 cfg->conf = NULL;
4709 kfree(cfg->profile);
4710 cfg->profile = NULL;
4711 kfree(cfg->scan_req_int);
4712 cfg->scan_req_int = NULL;
4713 kfree(cfg->escan_ioctl_buf);
4714 cfg->escan_ioctl_buf = NULL;
4715 kfree(cfg->dcmd_buf);
4716 cfg->dcmd_buf = NULL;
4717 kfree(cfg->extra_buf);
4718 cfg->extra_buf = NULL;
4719 kfree(cfg->iscan);
4720 cfg->iscan = NULL;
4721 kfree(cfg->pmk_list);
4722 cfg->pmk_list = NULL;
4723 if (cfg->ap_info) {
4724 kfree(cfg->ap_info->wpa_ie);
4725 kfree(cfg->ap_info->rsn_ie);
4726 kfree(cfg->ap_info);
4727 cfg->ap_info = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02004728 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004729}
4730
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004731static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004732{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004733 cfg->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL);
4734 if (!cfg->scan_results)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004735 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004736 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4737 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004738 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004739 cfg->profile = kzalloc(sizeof(*cfg->profile), GFP_KERNEL);
4740 if (!cfg->profile)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004741 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004742 cfg->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4743 if (!cfg->bss_info)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004744 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004745 cfg->scan_req_int = kzalloc(sizeof(*cfg->scan_req_int),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004746 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004747 if (!cfg->scan_req_int)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004748 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004749 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4750 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004751 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004752 cfg->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
4753 if (!cfg->dcmd_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004754 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004755 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4756 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004757 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004758 cfg->iscan = kzalloc(sizeof(*cfg->iscan), GFP_KERNEL);
4759 if (!cfg->iscan)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004760 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004761 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4762 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004763 goto init_priv_mem_out;
4764
4765 return 0;
4766
4767init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004768 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004769
4770 return -ENOMEM;
4771}
4772
4773/*
4774* retrieve first queued event from head
4775*/
4776
4777static struct brcmf_cfg80211_event_q *brcmf_deq_event(
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004778 struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004779{
4780 struct brcmf_cfg80211_event_q *e = NULL;
4781
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004782 spin_lock_irq(&cfg->evt_q_lock);
4783 if (!list_empty(&cfg->evt_q_list)) {
4784 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004785 struct brcmf_cfg80211_event_q, evt_q_list);
4786 list_del(&e->evt_q_list);
4787 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004788 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004789
4790 return e;
4791}
4792
4793/*
Arend van Sprielbcbec9e2012-02-09 21:09:06 +01004794* push event to tail of the queue
4795*
4796* remark: this function may not sleep as it is called in atomic context.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004797*/
4798
4799static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004800brcmf_enq_event(struct brcmf_cfg80211_info *cfg, u32 event,
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004801 const struct brcmf_event_msg *msg, void *data)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004802{
4803 struct brcmf_cfg80211_event_q *e;
4804 s32 err = 0;
Arend van Sprielcf440662012-02-09 21:09:07 +01004805 ulong flags;
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004806 u32 data_len;
4807 u32 total_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004808
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004809 total_len = sizeof(struct brcmf_cfg80211_event_q);
4810 if (data)
4811 data_len = be32_to_cpu(msg->datalen);
4812 else
4813 data_len = 0;
4814 total_len += data_len;
4815 e = kzalloc(total_len, GFP_ATOMIC);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004816 if (!e)
4817 return -ENOMEM;
4818
4819 e->etype = event;
4820 memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004821 if (data)
4822 memcpy(&e->edata, data, data_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004823
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004824 spin_lock_irqsave(&cfg->evt_q_lock, flags);
4825 list_add_tail(&e->evt_q_list, &cfg->evt_q_list);
4826 spin_unlock_irqrestore(&cfg->evt_q_lock, flags);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004827
4828 return err;
4829}
4830
4831static void brcmf_put_event(struct brcmf_cfg80211_event_q *e)
4832{
4833 kfree(e);
4834}
4835
4836static void brcmf_cfg80211_event_handler(struct work_struct *work)
4837{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004838 struct brcmf_cfg80211_info *cfg =
4839 container_of(work, struct brcmf_cfg80211_info,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004840 event_work);
4841 struct brcmf_cfg80211_event_q *e;
4842
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004843 e = brcmf_deq_event(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004844 if (unlikely(!e)) {
4845 WL_ERR("event queue empty...\n");
4846 return;
4847 }
4848
4849 do {
4850 WL_INFO("event type (%d)\n", e->etype);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004851 if (cfg->el.handler[e->etype])
4852 cfg->el.handler[e->etype](cfg,
4853 cfg_to_ndev(cfg),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004854 &e->emsg, e->edata);
4855 else
4856 WL_INFO("Unknown Event (%d): ignoring\n", e->etype);
4857 brcmf_put_event(e);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004858 } while ((e = brcmf_deq_event(cfg)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004859
4860}
4861
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004862static void brcmf_init_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004863{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004864 spin_lock_init(&cfg->evt_q_lock);
4865 INIT_LIST_HEAD(&cfg->evt_q_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004866}
4867
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004868static void brcmf_flush_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004869{
4870 struct brcmf_cfg80211_event_q *e;
4871
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004872 spin_lock_irq(&cfg->evt_q_lock);
4873 while (!list_empty(&cfg->evt_q_list)) {
4874 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004875 struct brcmf_cfg80211_event_q, evt_q_list);
4876 list_del(&e->evt_q_list);
4877 kfree(e);
4878 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004879 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004880}
4881
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004882static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004883{
4884 s32 err = 0;
4885
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004886 cfg->scan_request = NULL;
4887 cfg->pwr_save = true;
Hante Meulemane756af52012-09-11 21:18:52 +02004888#ifdef CONFIG_BRCMISCAN
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004889 cfg->iscan_on = true; /* iscan on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004890 we enable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004891 cfg->escan_on = false; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02004892 we disable escan per default */
4893#else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004894 cfg->iscan_on = false; /* iscan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02004895 we disable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004896 cfg->escan_on = true; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02004897 we enable escan per default */
4898#endif
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004899 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004900 we enable roam per default */
4901
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004902 cfg->iscan_kickstart = false;
4903 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02004904 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004905 cfg->dongle_up = false; /* dongle is not up yet */
4906 brcmf_init_eq(cfg);
4907 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004908 if (err)
4909 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004910 INIT_WORK(&cfg->event_work, brcmf_cfg80211_event_handler);
4911 brcmf_init_eloop_handler(&cfg->el);
4912 mutex_init(&cfg->usr_sync);
4913 err = brcmf_init_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004914 if (err)
4915 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004916 brcmf_init_escan(cfg);
4917 brcmf_init_conf(cfg->conf);
4918 brcmf_init_prof(cfg->profile);
4919 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004920
4921 return err;
4922}
4923
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004924static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004925{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004926 cancel_work_sync(&cfg->event_work);
4927 cfg->dongle_up = false; /* dongle down */
4928 brcmf_flush_eq(cfg);
4929 brcmf_link_down(cfg);
4930 brcmf_abort_scanning(cfg);
4931 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004932}
4933
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004934struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct net_device *ndev,
Arend van Sprielb451ec92012-09-27 14:17:52 +02004935 struct device *busdev,
4936 struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004937{
4938 struct wireless_dev *wdev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004939 struct brcmf_cfg80211_info *cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004940 s32 err = 0;
4941
4942 if (!ndev) {
4943 WL_ERR("ndev is invalid\n");
4944 return NULL;
4945 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004946
Arend van Spriel5db6e952012-09-27 14:17:53 +02004947 wdev = brcmf_alloc_wdev(busdev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004948 if (IS_ERR(wdev)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004949 return NULL;
4950 }
4951
4952 wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004953 cfg = wdev_to_cfg(wdev);
4954 cfg->wdev = wdev;
4955 cfg->pub = drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004956 ndev->ieee80211_ptr = wdev;
4957 SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
4958 wdev->netdev = ndev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004959 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004960 if (err) {
4961 WL_ERR("Failed to init iwm_priv (%d)\n", err);
4962 goto cfg80211_attach_out;
4963 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004964
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004965 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004966
4967cfg80211_attach_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004968 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004969 return NULL;
4970}
4971
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004972void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004973{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004974 wl_deinit_priv(cfg);
4975 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004976}
4977
4978void
4979brcmf_cfg80211_event(struct net_device *ndev,
4980 const struct brcmf_event_msg *e, void *data)
4981{
4982 u32 event_type = be32_to_cpu(e->event_type);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004983 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004984
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004985 if (!brcmf_enq_event(cfg, event_type, e, data))
4986 schedule_work(&cfg->event_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004987}
4988
Arend van Spriel5b435de2011-10-05 13:19:03 +02004989static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
4990{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004991 s8 eventmask[BRCMF_EVENTING_MASK_LEN];
4992 s32 err = 0;
4993
4994 WL_TRACE("Enter\n");
4995
4996 /* Setup event_msgs */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004997 err = brcmf_fil_iovar_data_get(ndev, "event_msgs", eventmask,
4998 BRCMF_EVENTING_MASK_LEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004999 if (err) {
5000 WL_ERR("Get event_msgs error (%d)\n", err);
5001 goto dongle_eventmsg_out;
5002 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005003
5004 setbit(eventmask, BRCMF_E_SET_SSID);
5005 setbit(eventmask, BRCMF_E_ROAM);
5006 setbit(eventmask, BRCMF_E_PRUNE);
5007 setbit(eventmask, BRCMF_E_AUTH);
5008 setbit(eventmask, BRCMF_E_REASSOC);
5009 setbit(eventmask, BRCMF_E_REASSOC_IND);
5010 setbit(eventmask, BRCMF_E_DEAUTH_IND);
5011 setbit(eventmask, BRCMF_E_DISASSOC_IND);
5012 setbit(eventmask, BRCMF_E_DISASSOC);
5013 setbit(eventmask, BRCMF_E_JOIN);
5014 setbit(eventmask, BRCMF_E_ASSOC_IND);
5015 setbit(eventmask, BRCMF_E_PSK_SUP);
5016 setbit(eventmask, BRCMF_E_LINK);
5017 setbit(eventmask, BRCMF_E_NDIS_LINK);
5018 setbit(eventmask, BRCMF_E_MIC_ERROR);
5019 setbit(eventmask, BRCMF_E_PMKID_CACHE);
5020 setbit(eventmask, BRCMF_E_TXFAIL);
5021 setbit(eventmask, BRCMF_E_JOIN_START);
5022 setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
Hante Meulemane756af52012-09-11 21:18:52 +02005023 setbit(eventmask, BRCMF_E_ESCAN_RESULT);
Arend van Spriele5806072012-09-19 22:21:08 +02005024 setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005025
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005026 err = brcmf_fil_iovar_data_set(ndev, "event_msgs", eventmask,
5027 BRCMF_EVENTING_MASK_LEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005028 if (err) {
5029 WL_ERR("Set event_msgs error (%d)\n", err);
5030 goto dongle_eventmsg_out;
5031 }
5032
5033dongle_eventmsg_out:
5034 WL_TRACE("Exit\n");
5035 return err;
5036}
5037
5038static s32
5039brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
5040{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005041 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005042 __le32 roamtrigger[2];
5043 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005044
5045 /*
5046 * Setup timeout if Beacons are lost and roam is
5047 * off to report link down
5048 */
5049 if (roamvar) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005050 err = brcmf_fil_iovar_int_set(ndev, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005051 if (err) {
5052 WL_ERR("bcn_timeout error (%d)\n", err);
5053 goto dongle_rom_out;
5054 }
5055 }
5056
5057 /*
5058 * Enable/Disable built-in roaming to allow supplicant
5059 * to take care of roaming
5060 */
5061 WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005062 err = brcmf_fil_iovar_int_set(ndev, "roam_off", roamvar);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005063 if (err) {
5064 WL_ERR("roam_off error (%d)\n", err);
5065 goto dongle_rom_out;
5066 }
5067
Arend van Sprielf588bc02011-10-12 20:51:22 +02005068 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5069 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005070 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_ROAM_TRIGGER,
5071 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005072 if (err) {
5073 WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
5074 goto dongle_rom_out;
5075 }
5076
Arend van Sprielf588bc02011-10-12 20:51:22 +02005077 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5078 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005079 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_ROAM_DELTA,
5080 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005081 if (err) {
5082 WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
5083 goto dongle_rom_out;
5084 }
5085
5086dongle_rom_out:
5087 return err;
5088}
5089
5090static s32
5091brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005092 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005093{
5094 s32 err = 0;
5095
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005096 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME,
5097 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005098 if (err) {
5099 if (err == -EOPNOTSUPP)
5100 WL_INFO("Scan assoc time is not supported\n");
5101 else
5102 WL_ERR("Scan assoc time error (%d)\n", err);
5103 goto dongle_scantime_out;
5104 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005105 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME,
5106 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005107 if (err) {
5108 if (err == -EOPNOTSUPP)
5109 WL_INFO("Scan unassoc time is not supported\n");
5110 else
5111 WL_ERR("Scan unassoc time error (%d)\n", err);
5112 goto dongle_scantime_out;
5113 }
5114
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005115 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME,
5116 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005117 if (err) {
5118 if (err == -EOPNOTSUPP)
5119 WL_INFO("Scan passive time is not supported\n");
5120 else
5121 WL_ERR("Scan passive time error (%d)\n", err);
5122 goto dongle_scantime_out;
5123 }
5124
5125dongle_scantime_out:
5126 return err;
5127}
5128
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005129static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005130{
5131 struct wiphy *wiphy;
5132 s32 phy_list;
5133 s8 phy;
5134 s32 err = 0;
5135
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005136 err = brcmf_fil_cmd_data_get(cfg_to_ndev(cfg), BRCM_GET_PHYLIST,
5137 &phy_list, sizeof(phy_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005138 if (err) {
5139 WL_ERR("error (%d)\n", err);
5140 return err;
5141 }
5142
Hante Meuleman3ba81372012-09-19 22:21:13 +02005143 phy = ((char *)&phy_list)[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005144 WL_INFO("%c phy\n", phy);
5145 if (phy == 'n' || phy == 'a') {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005146 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005147 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
5148 }
5149
5150 return err;
5151}
5152
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005153static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005154{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005155 return wl_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005156}
5157
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005158static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005159{
5160 struct net_device *ndev;
5161 struct wireless_dev *wdev;
5162 s32 power_mode;
5163 s32 err = 0;
5164
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005165 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005166 return err;
5167
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005168 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005169 wdev = ndev->ieee80211_ptr;
5170
5171 brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
5172 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
5173
5174 err = brcmf_dongle_eventmsg(ndev);
5175 if (err)
5176 goto default_conf_out;
5177
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005178 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005179 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005180 if (err)
5181 goto default_conf_out;
5182 WL_INFO("power save set to %s\n",
5183 (power_mode ? "enabled" : "disabled"));
5184
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005185 err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
Arend van Spriel5b435de2011-10-05 13:19:03 +02005186 WL_BEACON_TIMEOUT);
5187 if (err)
5188 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005189 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5190 NULL, NULL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005191 if (err && err != -EINPROGRESS)
5192 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005193 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005194 if (err)
5195 goto default_conf_out;
5196
5197 /* -EINPROGRESS: Call commit handler */
5198
5199default_conf_out:
5200
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005201 cfg->dongle_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005202
5203 return err;
5204
5205}
5206
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005207static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005208{
5209 char buf[10+IFNAMSIZ];
5210 struct dentry *fd;
5211 s32 err = 0;
5212
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005213 sprintf(buf, "netdev:%s", cfg_to_ndev(cfg)->name);
5214 cfg->debugfsdir = debugfs_create_dir(buf,
5215 cfg_to_wiphy(cfg)->debugfsdir);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005216
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005217 fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg->debugfsdir,
5218 (u16 *)&cfg->profile->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005219 if (!fd) {
5220 err = -ENOMEM;
5221 goto err_out;
5222 }
5223
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005224 fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg->debugfsdir,
5225 (u8 *)&cfg->profile->dtim_period);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005226 if (!fd) {
5227 err = -ENOMEM;
5228 goto err_out;
5229 }
5230
5231err_out:
5232 return err;
5233}
5234
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005235static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005236{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005237 debugfs_remove_recursive(cfg->debugfsdir);
5238 cfg->debugfsdir = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005239}
5240
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005241static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005242{
5243 s32 err = 0;
5244
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005245 set_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005246
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005247 brcmf_debugfs_add_netdev_params(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005248
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005249 err = brcmf_config_dongle(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005250 if (err)
5251 return err;
5252
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005253 brcmf_invoke_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005254
5255 return err;
5256}
5257
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005258static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005259{
5260 /*
5261 * While going down, if associated with AP disassociate
5262 * from AP to save power
5263 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005264 if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
5265 test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
5266 test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005267 WL_INFO("Disassociating from AP");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005268 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005269
5270 /* Make sure WPA_Supplicant receives all the event
5271 generated due to DISASSOC call to the fw to keep
5272 the state fw and WPA_Supplicant state consistent
5273 */
5274 brcmf_delay(500);
5275 }
5276
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005277 brcmf_abort_scanning(cfg);
5278 clear_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005279
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005280 brcmf_debugfs_remove_netdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005281
5282 return 0;
5283}
5284
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005285s32 brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005286{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005287 s32 err = 0;
5288
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005289 mutex_lock(&cfg->usr_sync);
5290 err = __brcmf_cfg80211_up(cfg);
5291 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005292
5293 return err;
5294}
5295
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005296s32 brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005297{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005298 s32 err = 0;
5299
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005300 mutex_lock(&cfg->usr_sync);
5301 err = __brcmf_cfg80211_down(cfg);
5302 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005303
5304 return err;
5305}
5306