blob: 30e9c127f9c175b043a5818758b43452b480d7c8 [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
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003461 WL_TRACE("cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
3462 dcmd->buf, dcmd->len);
3463
3464 if (dcmd->set)
3465 ret = brcmf_fil_cmd_data_set(ndev, dcmd->cmd, dcmd->buf,
3466 dcmd->len);
3467 else
3468 ret = brcmf_fil_cmd_data_get(ndev, dcmd->cmd, dcmd->buf,
3469 dcmd->len);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003470 if (ret == 0) {
3471 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3472 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3473 ret = cfg80211_testmode_reply(reply);
3474 }
3475 return ret;
3476}
3477#endif
3478
Hante Meuleman1a873342012-09-27 14:17:54 +02003479static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
3480{
3481 s32 err;
3482
3483 /* set auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003484 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003485 if (err < 0) {
3486 WL_ERR("auth error %d\n", err);
3487 return err;
3488 }
3489 /* set wsec */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003490 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003491 if (err < 0) {
3492 WL_ERR("wsec error %d\n", err);
3493 return err;
3494 }
3495 /* set upper-layer auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003496 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003497 if (err < 0) {
3498 WL_ERR("wpa_auth error %d\n", err);
3499 return err;
3500 }
3501
3502 return 0;
3503}
3504
3505static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3506{
3507 if (is_rsn_ie)
3508 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3509
3510 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3511}
3512
3513static s32
3514brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
3515 bool is_rsn_ie, s32 bssidx)
3516{
3517 u32 auth = 0; /* d11 open authentication */
3518 u16 count;
3519 s32 err = 0;
3520 s32 len = 0;
3521 u32 i;
3522 u32 wsec;
3523 u32 pval = 0;
3524 u32 gval = 0;
3525 u32 wpa_auth = 0;
3526 u32 offset;
3527 u8 *data;
3528 u16 rsn_cap;
3529 u32 wme_bss_disable;
3530
3531 WL_TRACE("Enter\n");
3532 if (wpa_ie == NULL)
3533 goto exit;
3534
3535 len = wpa_ie->len + TLV_HDR_LEN;
3536 data = (u8 *)wpa_ie;
3537 offset = 0;
3538 if (!is_rsn_ie)
3539 offset += VS_IE_FIXED_HDR_LEN;
3540 offset += WPA_IE_VERSION_LEN;
3541
3542 /* check for multicast cipher suite */
3543 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3544 err = -EINVAL;
3545 WL_ERR("no multicast cipher suite\n");
3546 goto exit;
3547 }
3548
3549 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3550 err = -EINVAL;
3551 WL_ERR("ivalid OUI\n");
3552 goto exit;
3553 }
3554 offset += TLV_OUI_LEN;
3555
3556 /* pick up multicast cipher */
3557 switch (data[offset]) {
3558 case WPA_CIPHER_NONE:
3559 gval = 0;
3560 break;
3561 case WPA_CIPHER_WEP_40:
3562 case WPA_CIPHER_WEP_104:
3563 gval = WEP_ENABLED;
3564 break;
3565 case WPA_CIPHER_TKIP:
3566 gval = TKIP_ENABLED;
3567 break;
3568 case WPA_CIPHER_AES_CCM:
3569 gval = AES_ENABLED;
3570 break;
3571 default:
3572 err = -EINVAL;
3573 WL_ERR("Invalid multi cast cipher info\n");
3574 goto exit;
3575 }
3576
3577 offset++;
3578 /* walk thru unicast cipher list and pick up what we recognize */
3579 count = data[offset] + (data[offset + 1] << 8);
3580 offset += WPA_IE_SUITE_COUNT_LEN;
3581 /* Check for unicast suite(s) */
3582 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3583 err = -EINVAL;
3584 WL_ERR("no unicast cipher suite\n");
3585 goto exit;
3586 }
3587 for (i = 0; i < count; i++) {
3588 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3589 err = -EINVAL;
3590 WL_ERR("ivalid OUI\n");
3591 goto exit;
3592 }
3593 offset += TLV_OUI_LEN;
3594 switch (data[offset]) {
3595 case WPA_CIPHER_NONE:
3596 break;
3597 case WPA_CIPHER_WEP_40:
3598 case WPA_CIPHER_WEP_104:
3599 pval |= WEP_ENABLED;
3600 break;
3601 case WPA_CIPHER_TKIP:
3602 pval |= TKIP_ENABLED;
3603 break;
3604 case WPA_CIPHER_AES_CCM:
3605 pval |= AES_ENABLED;
3606 break;
3607 default:
3608 WL_ERR("Ivalid unicast security info\n");
3609 }
3610 offset++;
3611 }
3612 /* walk thru auth management suite list and pick up what we recognize */
3613 count = data[offset] + (data[offset + 1] << 8);
3614 offset += WPA_IE_SUITE_COUNT_LEN;
3615 /* Check for auth key management suite(s) */
3616 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3617 err = -EINVAL;
3618 WL_ERR("no auth key mgmt suite\n");
3619 goto exit;
3620 }
3621 for (i = 0; i < count; i++) {
3622 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3623 err = -EINVAL;
3624 WL_ERR("ivalid OUI\n");
3625 goto exit;
3626 }
3627 offset += TLV_OUI_LEN;
3628 switch (data[offset]) {
3629 case RSN_AKM_NONE:
3630 WL_TRACE("RSN_AKM_NONE\n");
3631 wpa_auth |= WPA_AUTH_NONE;
3632 break;
3633 case RSN_AKM_UNSPECIFIED:
3634 WL_TRACE("RSN_AKM_UNSPECIFIED\n");
3635 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3636 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3637 break;
3638 case RSN_AKM_PSK:
3639 WL_TRACE("RSN_AKM_PSK\n");
3640 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3641 (wpa_auth |= WPA_AUTH_PSK);
3642 break;
3643 default:
3644 WL_ERR("Ivalid key mgmt info\n");
3645 }
3646 offset++;
3647 }
3648
3649 if (is_rsn_ie) {
3650 wme_bss_disable = 1;
3651 if ((offset + RSN_CAP_LEN) <= len) {
3652 rsn_cap = data[offset] + (data[offset + 1] << 8);
3653 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3654 wme_bss_disable = 0;
3655 }
3656 /* set wme_bss_disable to sync RSN Capabilities */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003657 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wme_bss_disable",
3658 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003659 if (err < 0) {
3660 WL_ERR("wme_bss_disable error %d\n", err);
3661 goto exit;
3662 }
3663 }
3664 /* FOR WPS , set SES_OW_ENABLED */
3665 wsec = (pval | gval | SES_OW_ENABLED);
3666
3667 /* set auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003668 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003669 if (err < 0) {
3670 WL_ERR("auth error %d\n", err);
3671 goto exit;
3672 }
3673 /* set wsec */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003674 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003675 if (err < 0) {
3676 WL_ERR("wsec error %d\n", err);
3677 goto exit;
3678 }
3679 /* set upper-layer auth */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003680 err = brcmf_fil_bsscfg_int_set(ndev, bssidx, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003681 if (err < 0) {
3682 WL_ERR("wpa_auth error %d\n", err);
3683 goto exit;
3684 }
3685
3686exit:
3687 return err;
3688}
3689
3690static s32
3691brcmf_parse_vndr_ies(u8 *vndr_ie_buf, u32 vndr_ie_len,
3692 struct parsed_vndr_ies *vndr_ies)
3693{
3694 s32 err = 0;
3695 struct brcmf_vs_tlv *vndrie;
3696 struct brcmf_tlv *ie;
3697 struct parsed_vndr_ie_info *parsed_info;
3698 s32 remaining_len;
3699
3700 remaining_len = (s32)vndr_ie_len;
3701 memset(vndr_ies, 0, sizeof(*vndr_ies));
3702
3703 ie = (struct brcmf_tlv *)vndr_ie_buf;
3704 while (ie) {
3705 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3706 goto next;
3707 vndrie = (struct brcmf_vs_tlv *)ie;
3708 /* len should be bigger than OUI length + one */
3709 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
3710 WL_ERR("invalid vndr ie. length is too small %d\n",
3711 vndrie->len);
3712 goto next;
3713 }
3714 /* if wpa or wme ie, do not add ie */
3715 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3716 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3717 (vndrie->oui_type == WME_OUI_TYPE))) {
3718 WL_TRACE("Found WPA/WME oui. Do not add it\n");
3719 goto next;
3720 }
3721
3722 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3723
3724 /* save vndr ie information */
3725 parsed_info->ie_ptr = (char *)vndrie;
3726 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3727 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3728
3729 vndr_ies->count++;
3730
3731 WL_TRACE("** OUI %02x %02x %02x, type 0x%02x\n",
3732 parsed_info->vndrie.oui[0],
3733 parsed_info->vndrie.oui[1],
3734 parsed_info->vndrie.oui[2],
3735 parsed_info->vndrie.oui_type);
3736
3737 if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
3738 break;
3739next:
3740 remaining_len -= ie->len;
3741 if (remaining_len <= 2)
3742 ie = NULL;
3743 else
3744 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len);
3745 }
3746 return err;
3747}
3748
3749static u32
3750brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3751{
3752
3753 __le32 iecount_le;
3754 __le32 pktflag_le;
3755
3756 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3757 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3758
3759 iecount_le = cpu_to_le32(1);
3760 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3761
3762 pktflag_le = cpu_to_le32(pktflag);
3763 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3764
3765 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3766
3767 return ie_len + VNDR_IE_HDR_SIZE;
3768}
3769
Franky Lin3cb91f52012-10-10 11:13:08 -07003770static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003771brcmf_set_management_ie(struct brcmf_cfg80211_info *cfg,
Arend van Spriel1d4fd8d2012-10-22 10:36:19 -07003772 struct net_device *ndev, s32 pktflag,
Hante Meuleman1a873342012-09-27 14:17:54 +02003773 u8 *vndr_ie_buf, u32 vndr_ie_len)
3774{
3775 s32 err = 0;
3776 u8 *iovar_ie_buf;
3777 u8 *curr_ie_buf;
3778 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003779 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003780 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003781 u32 del_add_ie_buf_len = 0;
3782 u32 total_ie_buf_len = 0;
3783 u32 parsed_ie_buf_len = 0;
3784 struct parsed_vndr_ies old_vndr_ies;
3785 struct parsed_vndr_ies new_vndr_ies;
3786 struct parsed_vndr_ie_info *vndrie_info;
3787 s32 i;
Arend van Spriel1d4fd8d2012-10-22 10:36:19 -07003788 s32 bssidx = brcmf_ndev_bssidx(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003789 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003790 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003791
3792 WL_TRACE("bssidx %d, pktflag : 0x%02X\n", bssidx, pktflag);
3793 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3794 if (!iovar_ie_buf)
3795 return -ENOMEM;
3796 curr_ie_buf = iovar_ie_buf;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003797 if (test_bit(WL_STATUS_AP_CREATING, &cfg->status) ||
3798 test_bit(WL_STATUS_AP_CREATED, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003799 switch (pktflag) {
3800 case VNDR_IE_PRBRSP_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003801 mgmt_ie_buf = cfg->ap_info->probe_res_ie;
3802 mgmt_ie_len = &cfg->ap_info->probe_res_ie_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003803 mgmt_ie_buf_len = sizeof(cfg->ap_info->probe_res_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003804 break;
3805 case VNDR_IE_BEACON_FLAG:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003806 mgmt_ie_buf = cfg->ap_info->beacon_ie;
3807 mgmt_ie_len = &cfg->ap_info->beacon_ie_len;
3808 mgmt_ie_buf_len = sizeof(cfg->ap_info->beacon_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003809 break;
3810 default:
3811 err = -EPERM;
3812 WL_ERR("not suitable type\n");
3813 goto exit;
3814 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003815 } else {
3816 err = -EPERM;
3817 WL_ERR("not suitable type\n");
3818 goto exit;
3819 }
3820
3821 if (vndr_ie_len > mgmt_ie_buf_len) {
3822 err = -ENOMEM;
3823 WL_ERR("extra IE size too big\n");
3824 goto exit;
3825 }
3826
3827 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3828 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3829 ptr = curr_ie_buf;
3830 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3831 for (i = 0; i < new_vndr_ies.count; i++) {
3832 vndrie_info = &new_vndr_ies.ie_info[i];
3833 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3834 vndrie_info->ie_len);
3835 parsed_ie_buf_len += vndrie_info->ie_len;
3836 }
3837 }
3838
3839 if (mgmt_ie_buf != NULL) {
3840 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3841 (memcmp(mgmt_ie_buf, curr_ie_buf,
3842 parsed_ie_buf_len) == 0)) {
3843 WL_TRACE("Previous mgmt IE is equals to current IE");
3844 goto exit;
3845 }
3846
3847 /* parse old vndr_ie */
3848 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3849
3850 /* make a command to delete old ie */
3851 for (i = 0; i < old_vndr_ies.count; i++) {
3852 vndrie_info = &old_vndr_ies.ie_info[i];
3853
3854 WL_TRACE("DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3855 vndrie_info->vndrie.id,
3856 vndrie_info->vndrie.len,
3857 vndrie_info->vndrie.oui[0],
3858 vndrie_info->vndrie.oui[1],
3859 vndrie_info->vndrie.oui[2]);
3860
3861 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3862 vndrie_info->ie_ptr,
3863 vndrie_info->ie_len,
3864 "del");
3865 curr_ie_buf += del_add_ie_buf_len;
3866 total_ie_buf_len += del_add_ie_buf_len;
3867 }
3868 }
3869
3870 *mgmt_ie_len = 0;
3871 /* Add if there is any extra IE */
3872 if (mgmt_ie_buf && parsed_ie_buf_len) {
3873 ptr = mgmt_ie_buf;
3874
3875 remained_buf_len = mgmt_ie_buf_len;
3876
3877 /* make a command to add new ie */
3878 for (i = 0; i < new_vndr_ies.count; i++) {
3879 vndrie_info = &new_vndr_ies.ie_info[i];
3880
3881 WL_TRACE("ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3882 vndrie_info->vndrie.id,
3883 vndrie_info->vndrie.len,
3884 vndrie_info->vndrie.oui[0],
3885 vndrie_info->vndrie.oui[1],
3886 vndrie_info->vndrie.oui[2]);
3887
3888 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3889 vndrie_info->ie_ptr,
3890 vndrie_info->ie_len,
3891 "add");
3892 /* verify remained buf size before copy data */
3893 remained_buf_len -= vndrie_info->ie_len;
3894 if (remained_buf_len < 0) {
3895 WL_ERR("no space in mgmt_ie_buf: len left %d",
3896 remained_buf_len);
3897 break;
3898 }
3899
3900 /* save the parsed IE in wl struct */
3901 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3902 vndrie_info->ie_len);
3903 *mgmt_ie_len += vndrie_info->ie_len;
3904
3905 curr_ie_buf += del_add_ie_buf_len;
3906 total_ie_buf_len += del_add_ie_buf_len;
3907 }
3908 }
3909 if (total_ie_buf_len) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003910 err = brcmf_fil_bsscfg_data_set(ndev, bssidx, "vndr_ie",
3911 iovar_ie_buf,
3912 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003913 if (err)
3914 WL_ERR("vndr ie set error : %d\n", err);
3915 }
3916
3917exit:
3918 kfree(iovar_ie_buf);
3919 return err;
3920}
3921
3922static s32
3923brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3924 struct cfg80211_ap_settings *settings)
3925{
3926 s32 ie_offset;
3927 struct brcmf_tlv *ssid_ie;
3928 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003929 s32 err = -EPERM;
3930 struct brcmf_tlv *rsn_ie;
3931 struct brcmf_vs_tlv *wpa_ie;
3932 struct brcmf_join_params join_params;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003933 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02003934 s32 bssidx = 0;
3935
3936 WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
3937 settings->channel_type, settings->beacon_interval,
3938 settings->dtim_period);
3939 WL_TRACE("ssid=%s(%d), auth_type=%d, inactivity_timeout=%d\n",
3940 settings->ssid, settings->ssid_len, settings->auth_type,
3941 settings->inactivity_timeout);
3942
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003943 if (!test_bit(WL_STATUS_AP_CREATING, &cfg->status)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003944 WL_ERR("Not in AP creation mode\n");
3945 return -EPERM;
3946 }
3947
3948 memset(&ssid_le, 0, sizeof(ssid_le));
3949 if (settings->ssid == NULL || settings->ssid_len == 0) {
3950 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
3951 ssid_ie = brcmf_parse_tlvs(
3952 (u8 *)&settings->beacon.head[ie_offset],
3953 settings->beacon.head_len - ie_offset,
3954 WLAN_EID_SSID);
3955 if (!ssid_ie)
3956 return -EINVAL;
3957
3958 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
3959 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
3960 WL_TRACE("SSID is (%s) in Head\n", ssid_le.SSID);
3961 } else {
3962 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
3963 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3964 }
3965
3966 brcmf_set_mpc(ndev, 0);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003967 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_DOWN, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003968 if (err < 0) {
3969 WL_ERR("BRCMF_C_DOWN error %d\n", err);
3970 goto exit;
3971 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003972 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003973 if (err < 0) {
3974 WL_ERR("SET INFRA error %d\n", err);
3975 goto exit;
3976 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003977 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_AP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003978 if (err < 0) {
3979 WL_ERR("setting AP mode failed %d\n", err);
3980 goto exit;
3981 }
3982
3983 /* find the RSN_IE */
3984 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
3985 settings->beacon.tail_len, WLAN_EID_RSN);
3986
3987 /* find the WPA_IE */
3988 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
3989 settings->beacon.tail_len);
3990
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003991 kfree(cfg->ap_info->rsn_ie);
3992 cfg->ap_info->rsn_ie = NULL;
3993 kfree(cfg->ap_info->wpa_ie);
3994 cfg->ap_info->wpa_ie = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02003995
3996 if ((wpa_ie != NULL || rsn_ie != NULL)) {
3997 WL_TRACE("WPA(2) IE is found\n");
3998 if (wpa_ie != NULL) {
3999 /* WPA IE */
4000 err = brcmf_configure_wpaie(ndev, wpa_ie, false,
4001 bssidx);
4002 if (err < 0)
4003 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004004 cfg->ap_info->wpa_ie = kmemdup(wpa_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02004005 wpa_ie->len +
4006 TLV_HDR_LEN,
4007 GFP_KERNEL);
4008 } else {
4009 /* RSN IE */
4010 err = brcmf_configure_wpaie(ndev,
4011 (struct brcmf_vs_tlv *)rsn_ie, true, bssidx);
4012 if (err < 0)
4013 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004014 cfg->ap_info->rsn_ie = kmemdup(rsn_ie,
Hante Meuleman1a873342012-09-27 14:17:54 +02004015 rsn_ie->len +
4016 TLV_HDR_LEN,
4017 GFP_KERNEL);
4018 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004019 cfg->ap_info->security_mode = true;
Hante Meuleman1a873342012-09-27 14:17:54 +02004020 } else {
4021 WL_TRACE("No WPA(2) IEs found\n");
4022 brcmf_configure_opensecurity(ndev, bssidx);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004023 cfg->ap_info->security_mode = false;
Hante Meuleman1a873342012-09-27 14:17:54 +02004024 }
4025 /* Set Beacon IEs to FW */
Arend van Spriel1d4fd8d2012-10-22 10:36:19 -07004026 err = brcmf_set_management_ie(cfg, ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02004027 VNDR_IE_BEACON_FLAG,
4028 (u8 *)settings->beacon.tail,
4029 settings->beacon.tail_len);
4030 if (err)
4031 WL_ERR("Set Beacon IE Failed\n");
4032 else
4033 WL_TRACE("Applied Vndr IEs for Beacon\n");
4034
4035 /* Set Probe Response IEs to FW */
Arend van Spriel1d4fd8d2012-10-22 10:36:19 -07004036 err = brcmf_set_management_ie(cfg, ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02004037 VNDR_IE_PRBRSP_FLAG,
4038 (u8 *)settings->beacon.proberesp_ies,
4039 settings->beacon.proberesp_ies_len);
4040 if (err)
4041 WL_ERR("Set Probe Resp IE Failed\n");
4042 else
4043 WL_TRACE("Applied Vndr IEs for Probe Resp\n");
4044
4045 if (settings->beacon_interval) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004046 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_BCNPRD,
4047 settings->beacon_interval);
Hante Meuleman1a873342012-09-27 14:17:54 +02004048 if (err < 0) {
4049 WL_ERR("Beacon Interval Set Error, %d\n", err);
4050 goto exit;
4051 }
4052 }
4053 if (settings->dtim_period) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004054 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_DTIMPRD,
4055 settings->dtim_period);
Hante Meuleman1a873342012-09-27 14:17:54 +02004056 if (err < 0) {
4057 WL_ERR("DTIM Interval Set Error, %d\n", err);
4058 goto exit;
4059 }
4060 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004061 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_UP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004062 if (err < 0) {
4063 WL_ERR("BRCMF_C_UP error (%d)\n", err);
4064 goto exit;
4065 }
4066
4067 memset(&join_params, 0, sizeof(join_params));
4068 /* join parameters starts with ssid */
4069 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4070 /* create softap */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004071 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_SSID, &join_params,
4072 sizeof(join_params));
Hante Meuleman1a873342012-09-27 14:17:54 +02004073 if (err < 0) {
4074 WL_ERR("SET SSID error (%d)\n", err);
4075 goto exit;
4076 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004077 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4078 set_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004079
4080exit:
4081 if (err)
4082 brcmf_set_mpc(ndev, 1);
4083 return err;
4084}
4085
4086static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4087{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004088 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004089 s32 err = -EPERM;
4090
4091 WL_TRACE("Enter\n");
4092
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004093 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004094 /* Due to most likely deauths outstanding we sleep */
4095 /* first to make sure they get processed by fw. */
4096 msleep(400);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004097 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_AP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02004098 if (err < 0) {
4099 WL_ERR("setting AP mode failed %d\n", err);
4100 goto exit;
4101 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004102 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_UP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02004103 if (err < 0) {
4104 WL_ERR("BRCMF_C_UP error %d\n", err);
4105 goto exit;
4106 }
4107 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004108 clear_bit(WL_STATUS_AP_CREATING, &cfg->status);
4109 clear_bit(WL_STATUS_AP_CREATED, &cfg->status);
Hante Meuleman1a873342012-09-27 14:17:54 +02004110 }
4111exit:
4112 return err;
4113}
4114
4115static int
4116brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
4117 u8 *mac)
4118{
4119 struct brcmf_scb_val_le scbval;
4120 s32 err;
4121
4122 if (!mac)
4123 return -EFAULT;
4124
4125 WL_TRACE("Enter %pM\n", mac);
4126
4127 if (!check_sys_up(wiphy))
4128 return -EIO;
4129
4130 memcpy(&scbval.ea, mac, ETH_ALEN);
4131 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004132 err = brcmf_fil_cmd_data_set(ndev,
4133 BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
4134 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004135 if (err)
4136 WL_ERR("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
4137
4138 WL_TRACE("Exit\n");
4139 return err;
4140}
4141
Arend van Spriel5b435de2011-10-05 13:19:03 +02004142static struct cfg80211_ops wl_cfg80211_ops = {
4143 .change_virtual_intf = brcmf_cfg80211_change_iface,
4144 .scan = brcmf_cfg80211_scan,
4145 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4146 .join_ibss = brcmf_cfg80211_join_ibss,
4147 .leave_ibss = brcmf_cfg80211_leave_ibss,
4148 .get_station = brcmf_cfg80211_get_station,
4149 .set_tx_power = brcmf_cfg80211_set_tx_power,
4150 .get_tx_power = brcmf_cfg80211_get_tx_power,
4151 .add_key = brcmf_cfg80211_add_key,
4152 .del_key = brcmf_cfg80211_del_key,
4153 .get_key = brcmf_cfg80211_get_key,
4154 .set_default_key = brcmf_cfg80211_config_default_key,
4155 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4156 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
4157 .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
4158 .connect = brcmf_cfg80211_connect,
4159 .disconnect = brcmf_cfg80211_disconnect,
4160 .suspend = brcmf_cfg80211_suspend,
4161 .resume = brcmf_cfg80211_resume,
4162 .set_pmksa = brcmf_cfg80211_set_pmksa,
4163 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004164 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004165 .start_ap = brcmf_cfg80211_start_ap,
4166 .stop_ap = brcmf_cfg80211_stop_ap,
4167 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004168#ifndef CONFIG_BRCMISCAN
4169 /* scheduled scan need e-scan, which is mutual exclusive with i-scan */
4170 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4171 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
4172#endif
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004173#ifdef CONFIG_NL80211_TESTMODE
4174 .testmode_cmd = brcmf_cfg80211_testmode
4175#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02004176};
4177
4178static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
4179{
4180 s32 err = 0;
4181
4182 switch (mode) {
4183 case WL_MODE_BSS:
4184 return NL80211_IFTYPE_STATION;
4185 case WL_MODE_IBSS:
4186 return NL80211_IFTYPE_ADHOC;
4187 default:
4188 return NL80211_IFTYPE_UNSPECIFIED;
4189 }
4190
4191 return err;
4192}
4193
Arend van Spriele5806072012-09-19 22:21:08 +02004194static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
4195{
4196#ifndef CONFIG_BRCMFISCAN
4197 /* scheduled scan settings */
4198 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
4199 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
4200 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
4201 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
4202#endif
4203}
4204
Arend van Spriel5db6e952012-09-27 14:17:53 +02004205static struct wireless_dev *brcmf_alloc_wdev(struct device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004206{
4207 struct wireless_dev *wdev;
4208 s32 err = 0;
4209
4210 wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
4211 if (!wdev)
4212 return ERR_PTR(-ENOMEM);
4213
Arend van Spriel5db6e952012-09-27 14:17:53 +02004214 wdev->wiphy = wiphy_new(&wl_cfg80211_ops,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004215 sizeof(struct brcmf_cfg80211_info));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004216 if (!wdev->wiphy) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004217 WL_ERR("Could not allocate wiphy device\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004218 err = -ENOMEM;
4219 goto wiphy_new_out;
4220 }
4221 set_wiphy_dev(wdev->wiphy, ndev);
4222 wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
4223 wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Hante Meuleman1a873342012-09-27 14:17:54 +02004224 wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
4225 BIT(NL80211_IFTYPE_ADHOC) |
4226 BIT(NL80211_IFTYPE_AP);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004227 wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
4228 wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
4229 * it as 11a by default.
4230 * This will be updated with
4231 * 11n phy tables in
4232 * "ifconfig up"
4233 * if phy has 11n capability
4234 */
4235 wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
4236 wdev->wiphy->cipher_suites = __wl_cipher_suites;
4237 wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
4238 wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
4239 * save mode
4240 * by default
4241 */
Arend van Spriele5806072012-09-19 22:21:08 +02004242 brcmf_wiphy_pno_params(wdev->wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004243 err = wiphy_register(wdev->wiphy);
4244 if (err < 0) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08004245 WL_ERR("Could not register wiphy device (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004246 goto wiphy_register_out;
4247 }
4248 return wdev;
4249
4250wiphy_register_out:
4251 wiphy_free(wdev->wiphy);
4252
4253wiphy_new_out:
4254 kfree(wdev);
4255
4256 return ERR_PTR(err);
4257}
4258
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004259static void brcmf_free_wdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004260{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004261 struct wireless_dev *wdev = cfg->wdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004262
4263 if (!wdev) {
4264 WL_ERR("wdev is invalid\n");
4265 return;
4266 }
4267 wiphy_unregister(wdev->wiphy);
4268 wiphy_free(wdev->wiphy);
4269 kfree(wdev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004270 cfg->wdev = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004271}
4272
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004273static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004274 const struct brcmf_event_msg *e)
4275{
4276 u32 event = be32_to_cpu(e->event_type);
4277 u32 status = be32_to_cpu(e->status);
4278
4279 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
4280 WL_CONN("Processing set ssid\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004281 cfg->link_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004282 return true;
4283 }
4284
4285 return false;
4286}
4287
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004288static bool brcmf_is_linkdown(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004289 const struct brcmf_event_msg *e)
4290{
4291 u32 event = be32_to_cpu(e->event_type);
4292 u16 flags = be16_to_cpu(e->flags);
4293
4294 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
4295 WL_CONN("Processing link down\n");
4296 return true;
4297 }
4298 return false;
4299}
4300
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004301static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004302 const struct brcmf_event_msg *e)
4303{
4304 u32 event = be32_to_cpu(e->event_type);
4305 u32 status = be32_to_cpu(e->status);
4306
4307 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
4308 WL_CONN("Processing Link %s & no network found\n",
4309 be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ?
4310 "up" : "down");
4311 return true;
4312 }
4313
4314 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
4315 WL_CONN("Processing connecting & no network found\n");
4316 return true;
4317 }
4318
4319 return false;
4320}
4321
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004322static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004323{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004324 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004325
4326 kfree(conn_info->req_ie);
4327 conn_info->req_ie = NULL;
4328 conn_info->req_ie_len = 0;
4329 kfree(conn_info->resp_ie);
4330 conn_info->resp_ie = NULL;
4331 conn_info->resp_ie_len = 0;
4332}
4333
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004334static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004335{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004336 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004337 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004338 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004339 u32 req_len;
4340 u32 resp_len;
4341 s32 err = 0;
4342
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004343 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004344
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004345 err = brcmf_fil_iovar_data_get(ndev, "assoc_info", cfg->extra_buf,
4346 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004347 if (err) {
4348 WL_ERR("could not get assoc info (%d)\n", err);
4349 return err;
4350 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004351 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004352 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004353 req_len = le32_to_cpu(assoc_info->req_len);
4354 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004355 if (req_len) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004356 err = brcmf_fil_iovar_data_get(ndev, "assoc_req_ies",
4357 cfg->extra_buf,
4358 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004359 if (err) {
4360 WL_ERR("could not get assoc req (%d)\n", err);
4361 return err;
4362 }
4363 conn_info->req_ie_len = req_len;
4364 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004365 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004366 GFP_KERNEL);
4367 } else {
4368 conn_info->req_ie_len = 0;
4369 conn_info->req_ie = NULL;
4370 }
4371 if (resp_len) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004372 err = brcmf_fil_iovar_data_get(ndev, "assoc_resp_ies",
4373 cfg->extra_buf,
4374 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004375 if (err) {
4376 WL_ERR("could not get assoc resp (%d)\n", err);
4377 return err;
4378 }
4379 conn_info->resp_ie_len = resp_len;
4380 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004381 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004382 GFP_KERNEL);
4383 } else {
4384 conn_info->resp_ie_len = 0;
4385 conn_info->resp_ie = NULL;
4386 }
4387 WL_CONN("req len (%d) resp len (%d)\n",
4388 conn_info->req_ie_len, conn_info->resp_ie_len);
4389
4390 return err;
4391}
4392
4393static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004394brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004395 struct net_device *ndev,
4396 const struct brcmf_event_msg *e)
4397{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004398 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004399 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4400 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004401 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004402 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004403 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004404 u32 freq;
4405 s32 err = 0;
4406 u32 target_channel;
Franky Lina180b832012-10-10 11:13:09 -07004407 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004408
4409 WL_TRACE("Enter\n");
4410
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004411 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004412 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004413 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004414
Franky Lina180b832012-10-10 11:13:09 -07004415 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4416 if (buf == NULL) {
4417 err = -ENOMEM;
4418 goto done;
4419 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004420
Franky Lina180b832012-10-10 11:13:09 -07004421 /* data sent to dongle has to be little endian */
4422 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004423 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_GET_BSS_INFO, buf,
4424 WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004425
4426 if (err)
4427 goto done;
4428
4429 bi = (struct brcmf_bss_info_le *)(buf + 4);
4430 target_channel = bi->ctl_ch ? bi->ctl_ch :
4431 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004432
4433 if (target_channel <= CH_MAX_2G_CHANNEL)
4434 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4435 else
4436 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4437
4438 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4439 notify_channel = ieee80211_get_channel(wiphy, freq);
4440
Franky Lina180b832012-10-10 11:13:09 -07004441done:
4442 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004443 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004444 conn_info->req_ie, conn_info->req_ie_len,
4445 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
4446 WL_CONN("Report roaming result\n");
4447
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004448 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004449 WL_TRACE("Exit\n");
4450 return err;
4451}
4452
4453static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004454brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004455 struct net_device *ndev, const struct brcmf_event_msg *e,
4456 bool completed)
4457{
Arend van Spriel06bb1232012-09-27 14:17:56 +02004458 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004459 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004460 s32 err = 0;
4461
4462 WL_TRACE("Enter\n");
4463
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004464 if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004465 if (completed) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004466 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004467 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004468 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004469 }
4470 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004471 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004472 conn_info->req_ie,
4473 conn_info->req_ie_len,
4474 conn_info->resp_ie,
4475 conn_info->resp_ie_len,
4476 completed ? WLAN_STATUS_SUCCESS :
4477 WLAN_STATUS_AUTH_TIMEOUT,
4478 GFP_KERNEL);
4479 if (completed)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004480 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004481 WL_CONN("Report connect result - connection %s\n",
4482 completed ? "succeeded" : "failed");
4483 }
4484 WL_TRACE("Exit\n");
4485 return err;
4486}
4487
4488static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004489brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004490 struct net_device *ndev,
4491 const struct brcmf_event_msg *e, void *data)
4492{
4493 s32 err = 0;
4494 u32 event = be32_to_cpu(e->event_type);
4495 u32 reason = be32_to_cpu(e->reason);
4496 u32 len = be32_to_cpu(e->datalen);
4497 static int generation;
4498
4499 struct station_info sinfo;
4500
4501 WL_CONN("event %d, reason %d\n", event, reason);
4502 memset(&sinfo, 0, sizeof(sinfo));
4503
4504 sinfo.filled = 0;
4505 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
4506 reason == BRCMF_E_STATUS_SUCCESS) {
4507 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4508 if (!data) {
4509 WL_ERR("No IEs present in ASSOC/REASSOC_IND");
4510 return -EINVAL;
4511 }
4512 sinfo.assoc_req_ies = data;
4513 sinfo.assoc_req_ies_len = len;
4514 generation++;
4515 sinfo.generation = generation;
4516 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_ATOMIC);
4517 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4518 (event == BRCMF_E_DEAUTH_IND) ||
4519 (event == BRCMF_E_DEAUTH)) {
4520 generation++;
4521 sinfo.generation = generation;
4522 cfg80211_del_sta(ndev, e->addr, GFP_ATOMIC);
4523 }
4524 return err;
4525}
4526
4527static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004528brcmf_notify_connect_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004529 struct net_device *ndev,
4530 const struct brcmf_event_msg *e, void *data)
4531{
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004532 struct brcmf_cfg80211_profile *profile = cfg->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004533 s32 err = 0;
4534
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004535 if (cfg->conf->mode == WL_MODE_AP) {
4536 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
4537 } else if (brcmf_is_linkup(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004538 WL_CONN("Linkup\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004539 if (brcmf_is_ibssmode(cfg)) {
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004540 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004541 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004542 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004543 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
4544 set_bit(WL_STATUS_CONNECTED, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004545 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004546 brcmf_bss_connect_done(cfg, ndev, e, true);
4547 } else if (brcmf_is_linkdown(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004548 WL_CONN("Linkdown\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004549 if (brcmf_is_ibssmode(cfg)) {
4550 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004551 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004552 &cfg->status))
4553 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004554 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004555 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004556 if (test_and_clear_bit(WL_STATUS_CONNECTED,
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004557 &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004558 cfg80211_disconnected(ndev, 0, NULL, 0,
4559 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004560 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004561 }
4562 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004563 brcmf_init_prof(cfg->profile);
4564 } else if (brcmf_is_nonetwork(cfg, e)) {
4565 if (brcmf_is_ibssmode(cfg))
4566 clear_bit(WL_STATUS_CONNECTING, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004567 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004568 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004569 }
4570
4571 return err;
4572}
4573
4574static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004575brcmf_notify_roaming_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004576 struct net_device *ndev,
4577 const struct brcmf_event_msg *e, void *data)
4578{
4579 s32 err = 0;
4580 u32 event = be32_to_cpu(e->event_type);
4581 u32 status = be32_to_cpu(e->status);
4582
4583 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004584 if (test_bit(WL_STATUS_CONNECTED, &cfg->status))
4585 brcmf_bss_roaming_done(cfg, ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004586 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004587 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004588 }
4589
4590 return err;
4591}
4592
4593static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004594brcmf_notify_mic_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004595 struct net_device *ndev,
4596 const struct brcmf_event_msg *e, void *data)
4597{
4598 u16 flags = be16_to_cpu(e->flags);
4599 enum nl80211_key_type key_type;
4600
4601 if (flags & BRCMF_EVENT_MSG_GROUP)
4602 key_type = NL80211_KEYTYPE_GROUP;
4603 else
4604 key_type = NL80211_KEYTYPE_PAIRWISE;
4605
4606 cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1,
4607 NULL, GFP_KERNEL);
4608
4609 return 0;
4610}
4611
4612static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004613brcmf_notify_scan_status(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004614 struct net_device *ndev,
4615 const struct brcmf_event_msg *e, void *data)
4616{
4617 struct brcmf_channel_info_le channel_inform_le;
4618 struct brcmf_scan_results_le *bss_list_le;
4619 u32 len = WL_SCAN_BUF_MAX;
4620 s32 err = 0;
4621 bool scan_abort = false;
4622 u32 scan_channel;
4623
4624 WL_TRACE("Enter\n");
4625
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004626 if (cfg->iscan_on && cfg->iscan_kickstart) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004627 WL_TRACE("Exit\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004628 return brcmf_wakeup_iscan(cfg_to_iscan(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004629 }
4630
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004631 if (!test_and_clear_bit(WL_STATUS_SCANNING, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004632 WL_ERR("Scan complete while device not scanning\n");
4633 scan_abort = true;
4634 err = -EINVAL;
4635 goto scan_done_out;
4636 }
4637
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004638 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_GET_CHANNEL,
4639 &channel_inform_le,
4640 sizeof(channel_inform_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004641 if (err) {
4642 WL_ERR("scan busy (%d)\n", err);
4643 scan_abort = true;
4644 goto scan_done_out;
4645 }
4646 scan_channel = le32_to_cpu(channel_inform_le.scan_channel);
4647 if (scan_channel)
4648 WL_CONN("channel_inform.scan_channel (%d)\n", scan_channel);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004649 cfg->bss_list = cfg->scan_results;
4650 bss_list_le = (struct brcmf_scan_results_le *) cfg->bss_list;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004651
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004652 memset(cfg->scan_results, 0, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004653 bss_list_le->buflen = cpu_to_le32(len);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004654 err = brcmf_fil_cmd_data_get(ndev, BRCMF_C_SCAN_RESULTS,
4655 cfg->scan_results, len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004656 if (err) {
4657 WL_ERR("%s Scan_results error (%d)\n", ndev->name, err);
4658 err = -EINVAL;
4659 scan_abort = true;
4660 goto scan_done_out;
4661 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004662 cfg->scan_results->buflen = le32_to_cpu(bss_list_le->buflen);
4663 cfg->scan_results->version = le32_to_cpu(bss_list_le->version);
4664 cfg->scan_results->count = le32_to_cpu(bss_list_le->count);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004665
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004666 err = brcmf_inform_bss(cfg);
Hante Meuleman35aafa92012-09-11 21:18:49 +02004667 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004668 scan_abort = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004669
4670scan_done_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004671 if (cfg->scan_request) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004672 WL_SCAN("calling cfg80211_scan_done\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004673 cfg80211_scan_done(cfg->scan_request, scan_abort);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004674 brcmf_set_mpc(ndev, 1);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004675 cfg->scan_request = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004676 }
4677
4678 WL_TRACE("Exit\n");
4679
4680 return err;
4681}
4682
4683static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4684{
4685 conf->mode = (u32)-1;
4686 conf->frag_threshold = (u32)-1;
4687 conf->rts_threshold = (u32)-1;
4688 conf->retry_short = (u32)-1;
4689 conf->retry_long = (u32)-1;
4690 conf->tx_power = -1;
4691}
4692
4693static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
4694{
4695 memset(el, 0, sizeof(*el));
4696 el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status;
4697 el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status;
Hante Meuleman1a873342012-09-27 14:17:54 +02004698 el->handler[BRCMF_E_DEAUTH_IND] = brcmf_notify_connect_status;
4699 el->handler[BRCMF_E_DEAUTH] = brcmf_notify_connect_status;
4700 el->handler[BRCMF_E_DISASSOC_IND] = brcmf_notify_connect_status;
4701 el->handler[BRCMF_E_ASSOC_IND] = brcmf_notify_connect_status;
4702 el->handler[BRCMF_E_REASSOC_IND] = brcmf_notify_connect_status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004703 el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
4704 el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
4705 el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
Arend van Spriele5806072012-09-19 22:21:08 +02004706 el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004707}
4708
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004709static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004710{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004711 kfree(cfg->scan_results);
4712 cfg->scan_results = NULL;
4713 kfree(cfg->bss_info);
4714 cfg->bss_info = NULL;
4715 kfree(cfg->conf);
4716 cfg->conf = NULL;
4717 kfree(cfg->profile);
4718 cfg->profile = NULL;
4719 kfree(cfg->scan_req_int);
4720 cfg->scan_req_int = NULL;
4721 kfree(cfg->escan_ioctl_buf);
4722 cfg->escan_ioctl_buf = NULL;
4723 kfree(cfg->dcmd_buf);
4724 cfg->dcmd_buf = NULL;
4725 kfree(cfg->extra_buf);
4726 cfg->extra_buf = NULL;
4727 kfree(cfg->iscan);
4728 cfg->iscan = NULL;
4729 kfree(cfg->pmk_list);
4730 cfg->pmk_list = NULL;
4731 if (cfg->ap_info) {
4732 kfree(cfg->ap_info->wpa_ie);
4733 kfree(cfg->ap_info->rsn_ie);
4734 kfree(cfg->ap_info);
4735 cfg->ap_info = NULL;
Hante Meuleman1a873342012-09-27 14:17:54 +02004736 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004737}
4738
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004739static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004740{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004741 cfg->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL);
4742 if (!cfg->scan_results)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004743 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004744 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4745 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004746 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004747 cfg->profile = kzalloc(sizeof(*cfg->profile), GFP_KERNEL);
4748 if (!cfg->profile)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004749 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004750 cfg->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4751 if (!cfg->bss_info)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004752 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004753 cfg->scan_req_int = kzalloc(sizeof(*cfg->scan_req_int),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004754 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004755 if (!cfg->scan_req_int)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004756 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004757 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4758 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004759 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004760 cfg->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL);
4761 if (!cfg->dcmd_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004762 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004763 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4764 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004765 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004766 cfg->iscan = kzalloc(sizeof(*cfg->iscan), GFP_KERNEL);
4767 if (!cfg->iscan)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004768 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004769 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4770 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004771 goto init_priv_mem_out;
4772
4773 return 0;
4774
4775init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004776 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004777
4778 return -ENOMEM;
4779}
4780
4781/*
4782* retrieve first queued event from head
4783*/
4784
4785static struct brcmf_cfg80211_event_q *brcmf_deq_event(
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004786 struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004787{
4788 struct brcmf_cfg80211_event_q *e = NULL;
4789
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004790 spin_lock_irq(&cfg->evt_q_lock);
4791 if (!list_empty(&cfg->evt_q_list)) {
4792 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004793 struct brcmf_cfg80211_event_q, evt_q_list);
4794 list_del(&e->evt_q_list);
4795 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004796 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004797
4798 return e;
4799}
4800
4801/*
Arend van Sprielbcbec9e2012-02-09 21:09:06 +01004802* push event to tail of the queue
4803*
4804* remark: this function may not sleep as it is called in atomic context.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004805*/
4806
4807static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004808brcmf_enq_event(struct brcmf_cfg80211_info *cfg, u32 event,
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004809 const struct brcmf_event_msg *msg, void *data)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004810{
4811 struct brcmf_cfg80211_event_q *e;
4812 s32 err = 0;
Arend van Sprielcf440662012-02-09 21:09:07 +01004813 ulong flags;
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004814 u32 data_len;
4815 u32 total_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004816
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004817 total_len = sizeof(struct brcmf_cfg80211_event_q);
4818 if (data)
4819 data_len = be32_to_cpu(msg->datalen);
4820 else
4821 data_len = 0;
4822 total_len += data_len;
4823 e = kzalloc(total_len, GFP_ATOMIC);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004824 if (!e)
4825 return -ENOMEM;
4826
4827 e->etype = event;
4828 memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004829 if (data)
4830 memcpy(&e->edata, data, data_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004831
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004832 spin_lock_irqsave(&cfg->evt_q_lock, flags);
4833 list_add_tail(&e->evt_q_list, &cfg->evt_q_list);
4834 spin_unlock_irqrestore(&cfg->evt_q_lock, flags);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004835
4836 return err;
4837}
4838
4839static void brcmf_put_event(struct brcmf_cfg80211_event_q *e)
4840{
4841 kfree(e);
4842}
4843
4844static void brcmf_cfg80211_event_handler(struct work_struct *work)
4845{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004846 struct brcmf_cfg80211_info *cfg =
4847 container_of(work, struct brcmf_cfg80211_info,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004848 event_work);
4849 struct brcmf_cfg80211_event_q *e;
4850
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004851 e = brcmf_deq_event(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004852 if (unlikely(!e)) {
4853 WL_ERR("event queue empty...\n");
4854 return;
4855 }
4856
4857 do {
4858 WL_INFO("event type (%d)\n", e->etype);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004859 if (cfg->el.handler[e->etype])
4860 cfg->el.handler[e->etype](cfg,
4861 cfg_to_ndev(cfg),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004862 &e->emsg, e->edata);
4863 else
4864 WL_INFO("Unknown Event (%d): ignoring\n", e->etype);
4865 brcmf_put_event(e);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004866 } while ((e = brcmf_deq_event(cfg)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004867
4868}
4869
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004870static void brcmf_init_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004871{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004872 spin_lock_init(&cfg->evt_q_lock);
4873 INIT_LIST_HEAD(&cfg->evt_q_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004874}
4875
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004876static void brcmf_flush_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004877{
4878 struct brcmf_cfg80211_event_q *e;
4879
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004880 spin_lock_irq(&cfg->evt_q_lock);
4881 while (!list_empty(&cfg->evt_q_list)) {
4882 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004883 struct brcmf_cfg80211_event_q, evt_q_list);
4884 list_del(&e->evt_q_list);
4885 kfree(e);
4886 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004887 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004888}
4889
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004890static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004891{
4892 s32 err = 0;
4893
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004894 cfg->scan_request = NULL;
4895 cfg->pwr_save = true;
Hante Meulemane756af52012-09-11 21:18:52 +02004896#ifdef CONFIG_BRCMISCAN
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004897 cfg->iscan_on = true; /* iscan on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004898 we enable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004899 cfg->escan_on = false; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02004900 we disable escan per default */
4901#else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004902 cfg->iscan_on = false; /* iscan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02004903 we disable iscan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004904 cfg->escan_on = true; /* escan on & off switch.
Hante Meulemane756af52012-09-11 21:18:52 +02004905 we enable escan per default */
4906#endif
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004907 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004908 we enable roam per default */
4909
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004910 cfg->iscan_kickstart = false;
4911 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02004912 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004913 cfg->dongle_up = false; /* dongle is not up yet */
4914 brcmf_init_eq(cfg);
4915 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004916 if (err)
4917 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004918 INIT_WORK(&cfg->event_work, brcmf_cfg80211_event_handler);
4919 brcmf_init_eloop_handler(&cfg->el);
4920 mutex_init(&cfg->usr_sync);
4921 err = brcmf_init_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004922 if (err)
4923 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004924 brcmf_init_escan(cfg);
4925 brcmf_init_conf(cfg->conf);
4926 brcmf_init_prof(cfg->profile);
4927 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004928
4929 return err;
4930}
4931
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004932static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004933{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004934 cancel_work_sync(&cfg->event_work);
4935 cfg->dongle_up = false; /* dongle down */
4936 brcmf_flush_eq(cfg);
4937 brcmf_link_down(cfg);
4938 brcmf_abort_scanning(cfg);
4939 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004940}
4941
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004942struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004943{
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004944 struct net_device *ndev = drvr->iflist[0]->ndev;
4945 struct device *busdev = drvr->dev;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004946 struct wireless_dev *wdev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004947 struct brcmf_cfg80211_info *cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004948 s32 err = 0;
4949
4950 if (!ndev) {
4951 WL_ERR("ndev is invalid\n");
4952 return NULL;
4953 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004954
Arend van Spriel5db6e952012-09-27 14:17:53 +02004955 wdev = brcmf_alloc_wdev(busdev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004956 if (IS_ERR(wdev)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004957 return NULL;
4958 }
4959
4960 wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004961 cfg = wdev_to_cfg(wdev);
4962 cfg->wdev = wdev;
4963 cfg->pub = drvr;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004964 ndev->ieee80211_ptr = wdev;
4965 SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
4966 wdev->netdev = ndev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004967 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004968 if (err) {
4969 WL_ERR("Failed to init iwm_priv (%d)\n", err);
4970 goto cfg80211_attach_out;
4971 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004972
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004973 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004974
4975cfg80211_attach_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004976 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004977 return NULL;
4978}
4979
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004980void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004981{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004982 wl_deinit_priv(cfg);
4983 brcmf_free_wdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004984}
4985
4986void
4987brcmf_cfg80211_event(struct net_device *ndev,
4988 const struct brcmf_event_msg *e, void *data)
4989{
4990 u32 event_type = be32_to_cpu(e->event_type);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004991 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004992
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004993 if (!brcmf_enq_event(cfg, event_type, e, data))
4994 schedule_work(&cfg->event_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004995}
4996
Arend van Spriel5b435de2011-10-05 13:19:03 +02004997static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
4998{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004999 s8 eventmask[BRCMF_EVENTING_MASK_LEN];
5000 s32 err = 0;
5001
5002 WL_TRACE("Enter\n");
5003
5004 /* Setup event_msgs */
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005005 err = brcmf_fil_iovar_data_get(ndev, "event_msgs", eventmask,
5006 BRCMF_EVENTING_MASK_LEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005007 if (err) {
5008 WL_ERR("Get event_msgs error (%d)\n", err);
5009 goto dongle_eventmsg_out;
5010 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005011
5012 setbit(eventmask, BRCMF_E_SET_SSID);
5013 setbit(eventmask, BRCMF_E_ROAM);
5014 setbit(eventmask, BRCMF_E_PRUNE);
5015 setbit(eventmask, BRCMF_E_AUTH);
5016 setbit(eventmask, BRCMF_E_REASSOC);
5017 setbit(eventmask, BRCMF_E_REASSOC_IND);
5018 setbit(eventmask, BRCMF_E_DEAUTH_IND);
5019 setbit(eventmask, BRCMF_E_DISASSOC_IND);
5020 setbit(eventmask, BRCMF_E_DISASSOC);
5021 setbit(eventmask, BRCMF_E_JOIN);
5022 setbit(eventmask, BRCMF_E_ASSOC_IND);
5023 setbit(eventmask, BRCMF_E_PSK_SUP);
5024 setbit(eventmask, BRCMF_E_LINK);
5025 setbit(eventmask, BRCMF_E_NDIS_LINK);
5026 setbit(eventmask, BRCMF_E_MIC_ERROR);
5027 setbit(eventmask, BRCMF_E_PMKID_CACHE);
5028 setbit(eventmask, BRCMF_E_TXFAIL);
5029 setbit(eventmask, BRCMF_E_JOIN_START);
5030 setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
Hante Meulemane756af52012-09-11 21:18:52 +02005031 setbit(eventmask, BRCMF_E_ESCAN_RESULT);
Arend van Spriele5806072012-09-19 22:21:08 +02005032 setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005033
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005034 err = brcmf_fil_iovar_data_set(ndev, "event_msgs", eventmask,
5035 BRCMF_EVENTING_MASK_LEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005036 if (err) {
5037 WL_ERR("Set event_msgs error (%d)\n", err);
5038 goto dongle_eventmsg_out;
5039 }
5040
5041dongle_eventmsg_out:
5042 WL_TRACE("Exit\n");
5043 return err;
5044}
5045
5046static s32
5047brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
5048{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005049 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005050 __le32 roamtrigger[2];
5051 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005052
5053 /*
5054 * Setup timeout if Beacons are lost and roam is
5055 * off to report link down
5056 */
5057 if (roamvar) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005058 err = brcmf_fil_iovar_int_set(ndev, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005059 if (err) {
5060 WL_ERR("bcn_timeout error (%d)\n", err);
5061 goto dongle_rom_out;
5062 }
5063 }
5064
5065 /*
5066 * Enable/Disable built-in roaming to allow supplicant
5067 * to take care of roaming
5068 */
5069 WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005070 err = brcmf_fil_iovar_int_set(ndev, "roam_off", roamvar);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005071 if (err) {
5072 WL_ERR("roam_off error (%d)\n", err);
5073 goto dongle_rom_out;
5074 }
5075
Arend van Sprielf588bc02011-10-12 20:51:22 +02005076 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5077 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005078 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_ROAM_TRIGGER,
5079 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005080 if (err) {
5081 WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
5082 goto dongle_rom_out;
5083 }
5084
Arend van Sprielf588bc02011-10-12 20:51:22 +02005085 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5086 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005087 err = brcmf_fil_cmd_data_set(ndev, BRCMF_C_SET_ROAM_DELTA,
5088 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005089 if (err) {
5090 WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
5091 goto dongle_rom_out;
5092 }
5093
5094dongle_rom_out:
5095 return err;
5096}
5097
5098static s32
5099brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005100 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005101{
5102 s32 err = 0;
5103
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005104 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME,
5105 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005106 if (err) {
5107 if (err == -EOPNOTSUPP)
5108 WL_INFO("Scan assoc time is not supported\n");
5109 else
5110 WL_ERR("Scan assoc time error (%d)\n", err);
5111 goto dongle_scantime_out;
5112 }
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005113 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME,
5114 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005115 if (err) {
5116 if (err == -EOPNOTSUPP)
5117 WL_INFO("Scan unassoc time is not supported\n");
5118 else
5119 WL_ERR("Scan unassoc time error (%d)\n", err);
5120 goto dongle_scantime_out;
5121 }
5122
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005123 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME,
5124 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005125 if (err) {
5126 if (err == -EOPNOTSUPP)
5127 WL_INFO("Scan passive time is not supported\n");
5128 else
5129 WL_ERR("Scan passive time error (%d)\n", err);
5130 goto dongle_scantime_out;
5131 }
5132
5133dongle_scantime_out:
5134 return err;
5135}
5136
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005137static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005138{
5139 struct wiphy *wiphy;
5140 s32 phy_list;
5141 s8 phy;
5142 s32 err = 0;
5143
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005144 err = brcmf_fil_cmd_data_get(cfg_to_ndev(cfg), BRCM_GET_PHYLIST,
5145 &phy_list, sizeof(phy_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005146 if (err) {
5147 WL_ERR("error (%d)\n", err);
5148 return err;
5149 }
5150
Hante Meuleman3ba81372012-09-19 22:21:13 +02005151 phy = ((char *)&phy_list)[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005152 WL_INFO("%c phy\n", phy);
5153 if (phy == 'n' || phy == 'a') {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005154 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005155 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
5156 }
5157
5158 return err;
5159}
5160
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005161static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005162{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005163 return wl_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005164}
5165
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005166static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005167{
5168 struct net_device *ndev;
5169 struct wireless_dev *wdev;
5170 s32 power_mode;
5171 s32 err = 0;
5172
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005173 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005174 return err;
5175
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005176 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005177 wdev = ndev->ieee80211_ptr;
5178
5179 brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
5180 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
5181
5182 err = brcmf_dongle_eventmsg(ndev);
5183 if (err)
5184 goto default_conf_out;
5185
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005186 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005187 err = brcmf_fil_cmd_int_set(ndev, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005188 if (err)
5189 goto default_conf_out;
5190 WL_INFO("power save set to %s\n",
5191 (power_mode ? "enabled" : "disabled"));
5192
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005193 err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
Arend van Spriel5b435de2011-10-05 13:19:03 +02005194 WL_BEACON_TIMEOUT);
5195 if (err)
5196 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005197 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5198 NULL, NULL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005199 if (err && err != -EINPROGRESS)
5200 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005201 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005202 if (err)
5203 goto default_conf_out;
5204
5205 /* -EINPROGRESS: Call commit handler */
5206
5207default_conf_out:
5208
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005209 cfg->dongle_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210
5211 return err;
5212
5213}
5214
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005215static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005216{
5217 char buf[10+IFNAMSIZ];
5218 struct dentry *fd;
5219 s32 err = 0;
5220
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005221 sprintf(buf, "netdev:%s", cfg_to_ndev(cfg)->name);
5222 cfg->debugfsdir = debugfs_create_dir(buf,
5223 cfg_to_wiphy(cfg)->debugfsdir);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005224
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005225 fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg->debugfsdir,
5226 (u16 *)&cfg->profile->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005227 if (!fd) {
5228 err = -ENOMEM;
5229 goto err_out;
5230 }
5231
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005232 fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg->debugfsdir,
5233 (u8 *)&cfg->profile->dtim_period);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005234 if (!fd) {
5235 err = -ENOMEM;
5236 goto err_out;
5237 }
5238
5239err_out:
5240 return err;
5241}
5242
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005243static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005244{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005245 debugfs_remove_recursive(cfg->debugfsdir);
5246 cfg->debugfsdir = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005247}
5248
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005249static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005250{
5251 s32 err = 0;
5252
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005253 set_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005254
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005255 brcmf_debugfs_add_netdev_params(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005256
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005257 err = brcmf_config_dongle(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005258 if (err)
5259 return err;
5260
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005261 brcmf_invoke_iscan(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005262
5263 return err;
5264}
5265
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005266static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005267{
5268 /*
5269 * While going down, if associated with AP disassociate
5270 * from AP to save power
5271 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005272 if ((test_bit(WL_STATUS_CONNECTED, &cfg->status) ||
5273 test_bit(WL_STATUS_CONNECTING, &cfg->status)) &&
5274 test_bit(WL_STATUS_READY, &cfg->status)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005275 WL_INFO("Disassociating from AP");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005276 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005277
5278 /* Make sure WPA_Supplicant receives all the event
5279 generated due to DISASSOC call to the fw to keep
5280 the state fw and WPA_Supplicant state consistent
5281 */
5282 brcmf_delay(500);
5283 }
5284
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005285 brcmf_abort_scanning(cfg);
5286 clear_bit(WL_STATUS_READY, &cfg->status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005287
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005288 brcmf_debugfs_remove_netdev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005289
5290 return 0;
5291}
5292
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005293s32 brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005294{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005295 s32 err = 0;
5296
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005297 mutex_lock(&cfg->usr_sync);
5298 err = __brcmf_cfg80211_up(cfg);
5299 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005300
5301 return err;
5302}
5303
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005304s32 brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005305{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005306 s32 err = 0;
5307
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005308 mutex_lock(&cfg->usr_sync);
5309 err = __brcmf_cfg80211_down(cfg);
5310 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005311
5312 return err;
5313}
5314