blob: 4387ca506b3b0fdb82705f8d5ab3aa7ba8455682 [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
Arend van Spriel3eacf862012-10-22 13:55:30 -070052#define BRCMF_IFACE_MAX_CNT 2
53
Hante Meuleman1a873342012-09-27 14:17:54 +020054#define TLV_LEN_OFF 1 /* length offset */
Hante Meuleman04012892012-09-27 14:17:49 +020055#define TLV_HDR_LEN 2 /* header length */
Hante Meuleman1a873342012-09-27 14:17:54 +020056#define TLV_BODY_OFF 2 /* body offset */
57#define TLV_OUI_LEN 3 /* oui id length */
58#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
59#define WPA_OUI_TYPE 1
60#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
61#define WME_OUI_TYPE 2
62
63#define VS_IE_FIXED_HDR_LEN 6
64#define WPA_IE_VERSION_LEN 2
65#define WPA_IE_MIN_OUI_LEN 4
66#define WPA_IE_SUITE_COUNT_LEN 2
67
68#define WPA_CIPHER_NONE 0 /* None */
69#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
70#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
71#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
72#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
73
74#define RSN_AKM_NONE 0 /* None (IBSS) */
75#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
76#define RSN_AKM_PSK 2 /* Pre-shared Key */
77#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
78#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
79
80#define VNDR_IE_CMD_LEN 4 /* length of the set command
81 * string :"add", "del" (+ NUL)
82 */
83#define VNDR_IE_COUNT_OFFSET 4
84#define VNDR_IE_PKTFLAG_OFFSET 8
85#define VNDR_IE_VSIE_OFFSET 12
86#define VNDR_IE_HDR_SIZE 12
87#define VNDR_IE_BEACON_FLAG 0x1
88#define VNDR_IE_PRBRSP_FLAG 0x2
89#define MAX_VNDR_IE_NUMBER 5
90
91#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
92#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020093
Arend van Spriel5b435de2011-10-05 13:19:03 +020094#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
95 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
96
Arend van Spriel5b435de2011-10-05 13:19:03 +020097static u32 brcmf_dbg_level = WL_DBG_ERR;
98
Arend van Sprielce81e312012-10-22 13:55:37 -070099static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200100{
Arend van Sprielc1179032012-10-22 13:55:33 -0700101 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
102 WL_INFO("device is not ready : status (%lu)\n",
103 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200104 return false;
105 }
106 return true;
107}
108
109#define CHAN2G(_channel, _freq, _flags) { \
110 .band = IEEE80211_BAND_2GHZ, \
111 .center_freq = (_freq), \
112 .hw_value = (_channel), \
113 .flags = (_flags), \
114 .max_antenna_gain = 0, \
115 .max_power = 30, \
116}
117
118#define CHAN5G(_channel, _flags) { \
119 .band = IEEE80211_BAND_5GHZ, \
120 .center_freq = 5000 + (5 * (_channel)), \
121 .hw_value = (_channel), \
122 .flags = (_flags), \
123 .max_antenna_gain = 0, \
124 .max_power = 30, \
125}
126
127#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
128#define RATETAB_ENT(_rateid, _flags) \
129 { \
130 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
131 .hw_value = (_rateid), \
132 .flags = (_flags), \
133 }
134
135static struct ieee80211_rate __wl_rates[] = {
136 RATETAB_ENT(BRCM_RATE_1M, 0),
137 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
138 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
139 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
140 RATETAB_ENT(BRCM_RATE_6M, 0),
141 RATETAB_ENT(BRCM_RATE_9M, 0),
142 RATETAB_ENT(BRCM_RATE_12M, 0),
143 RATETAB_ENT(BRCM_RATE_18M, 0),
144 RATETAB_ENT(BRCM_RATE_24M, 0),
145 RATETAB_ENT(BRCM_RATE_36M, 0),
146 RATETAB_ENT(BRCM_RATE_48M, 0),
147 RATETAB_ENT(BRCM_RATE_54M, 0),
148};
149
150#define wl_a_rates (__wl_rates + 4)
151#define wl_a_rates_size 8
152#define wl_g_rates (__wl_rates + 0)
153#define wl_g_rates_size 12
154
155static struct ieee80211_channel __wl_2ghz_channels[] = {
156 CHAN2G(1, 2412, 0),
157 CHAN2G(2, 2417, 0),
158 CHAN2G(3, 2422, 0),
159 CHAN2G(4, 2427, 0),
160 CHAN2G(5, 2432, 0),
161 CHAN2G(6, 2437, 0),
162 CHAN2G(7, 2442, 0),
163 CHAN2G(8, 2447, 0),
164 CHAN2G(9, 2452, 0),
165 CHAN2G(10, 2457, 0),
166 CHAN2G(11, 2462, 0),
167 CHAN2G(12, 2467, 0),
168 CHAN2G(13, 2472, 0),
169 CHAN2G(14, 2484, 0),
170};
171
172static struct ieee80211_channel __wl_5ghz_a_channels[] = {
173 CHAN5G(34, 0), CHAN5G(36, 0),
174 CHAN5G(38, 0), CHAN5G(40, 0),
175 CHAN5G(42, 0), CHAN5G(44, 0),
176 CHAN5G(46, 0), CHAN5G(48, 0),
177 CHAN5G(52, 0), CHAN5G(56, 0),
178 CHAN5G(60, 0), CHAN5G(64, 0),
179 CHAN5G(100, 0), CHAN5G(104, 0),
180 CHAN5G(108, 0), CHAN5G(112, 0),
181 CHAN5G(116, 0), CHAN5G(120, 0),
182 CHAN5G(124, 0), CHAN5G(128, 0),
183 CHAN5G(132, 0), CHAN5G(136, 0),
184 CHAN5G(140, 0), CHAN5G(149, 0),
185 CHAN5G(153, 0), CHAN5G(157, 0),
186 CHAN5G(161, 0), CHAN5G(165, 0),
187 CHAN5G(184, 0), CHAN5G(188, 0),
188 CHAN5G(192, 0), CHAN5G(196, 0),
189 CHAN5G(200, 0), CHAN5G(204, 0),
190 CHAN5G(208, 0), CHAN5G(212, 0),
191 CHAN5G(216, 0),
192};
193
194static struct ieee80211_channel __wl_5ghz_n_channels[] = {
195 CHAN5G(32, 0), CHAN5G(34, 0),
196 CHAN5G(36, 0), CHAN5G(38, 0),
197 CHAN5G(40, 0), CHAN5G(42, 0),
198 CHAN5G(44, 0), CHAN5G(46, 0),
199 CHAN5G(48, 0), CHAN5G(50, 0),
200 CHAN5G(52, 0), CHAN5G(54, 0),
201 CHAN5G(56, 0), CHAN5G(58, 0),
202 CHAN5G(60, 0), CHAN5G(62, 0),
203 CHAN5G(64, 0), CHAN5G(66, 0),
204 CHAN5G(68, 0), CHAN5G(70, 0),
205 CHAN5G(72, 0), CHAN5G(74, 0),
206 CHAN5G(76, 0), CHAN5G(78, 0),
207 CHAN5G(80, 0), CHAN5G(82, 0),
208 CHAN5G(84, 0), CHAN5G(86, 0),
209 CHAN5G(88, 0), CHAN5G(90, 0),
210 CHAN5G(92, 0), CHAN5G(94, 0),
211 CHAN5G(96, 0), CHAN5G(98, 0),
212 CHAN5G(100, 0), CHAN5G(102, 0),
213 CHAN5G(104, 0), CHAN5G(106, 0),
214 CHAN5G(108, 0), CHAN5G(110, 0),
215 CHAN5G(112, 0), CHAN5G(114, 0),
216 CHAN5G(116, 0), CHAN5G(118, 0),
217 CHAN5G(120, 0), CHAN5G(122, 0),
218 CHAN5G(124, 0), CHAN5G(126, 0),
219 CHAN5G(128, 0), CHAN5G(130, 0),
220 CHAN5G(132, 0), CHAN5G(134, 0),
221 CHAN5G(136, 0), CHAN5G(138, 0),
222 CHAN5G(140, 0), CHAN5G(142, 0),
223 CHAN5G(144, 0), CHAN5G(145, 0),
224 CHAN5G(146, 0), CHAN5G(147, 0),
225 CHAN5G(148, 0), CHAN5G(149, 0),
226 CHAN5G(150, 0), CHAN5G(151, 0),
227 CHAN5G(152, 0), CHAN5G(153, 0),
228 CHAN5G(154, 0), CHAN5G(155, 0),
229 CHAN5G(156, 0), CHAN5G(157, 0),
230 CHAN5G(158, 0), CHAN5G(159, 0),
231 CHAN5G(160, 0), CHAN5G(161, 0),
232 CHAN5G(162, 0), CHAN5G(163, 0),
233 CHAN5G(164, 0), CHAN5G(165, 0),
234 CHAN5G(166, 0), CHAN5G(168, 0),
235 CHAN5G(170, 0), CHAN5G(172, 0),
236 CHAN5G(174, 0), CHAN5G(176, 0),
237 CHAN5G(178, 0), CHAN5G(180, 0),
238 CHAN5G(182, 0), CHAN5G(184, 0),
239 CHAN5G(186, 0), CHAN5G(188, 0),
240 CHAN5G(190, 0), CHAN5G(192, 0),
241 CHAN5G(194, 0), CHAN5G(196, 0),
242 CHAN5G(198, 0), CHAN5G(200, 0),
243 CHAN5G(202, 0), CHAN5G(204, 0),
244 CHAN5G(206, 0), CHAN5G(208, 0),
245 CHAN5G(210, 0), CHAN5G(212, 0),
246 CHAN5G(214, 0), CHAN5G(216, 0),
247 CHAN5G(218, 0), CHAN5G(220, 0),
248 CHAN5G(222, 0), CHAN5G(224, 0),
249 CHAN5G(226, 0), CHAN5G(228, 0),
250};
251
252static struct ieee80211_supported_band __wl_band_2ghz = {
253 .band = IEEE80211_BAND_2GHZ,
254 .channels = __wl_2ghz_channels,
255 .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
256 .bitrates = wl_g_rates,
257 .n_bitrates = wl_g_rates_size,
258};
259
260static struct ieee80211_supported_band __wl_band_5ghz_a = {
261 .band = IEEE80211_BAND_5GHZ,
262 .channels = __wl_5ghz_a_channels,
263 .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
264 .bitrates = wl_a_rates,
265 .n_bitrates = wl_a_rates_size,
266};
267
268static struct ieee80211_supported_band __wl_band_5ghz_n = {
269 .band = IEEE80211_BAND_5GHZ,
270 .channels = __wl_5ghz_n_channels,
271 .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
272 .bitrates = wl_a_rates,
273 .n_bitrates = wl_a_rates_size,
274};
275
276static const u32 __wl_cipher_suites[] = {
277 WLAN_CIPHER_SUITE_WEP40,
278 WLAN_CIPHER_SUITE_WEP104,
279 WLAN_CIPHER_SUITE_TKIP,
280 WLAN_CIPHER_SUITE_CCMP,
281 WLAN_CIPHER_SUITE_AES_CMAC,
282};
283
Alwin Beukersf8e4b412011-10-12 20:51:28 +0200284/* tag_ID/length/value_buffer tuple */
285struct brcmf_tlv {
286 u8 id;
287 u8 len;
288 u8 data[1];
289};
290
Hante Meuleman1a873342012-09-27 14:17:54 +0200291/* Vendor specific ie. id = 221, oui and type defines exact ie */
292struct brcmf_vs_tlv {
293 u8 id;
294 u8 len;
295 u8 oui[3];
296 u8 oui_type;
297};
298
299struct parsed_vndr_ie_info {
300 u8 *ie_ptr;
301 u32 ie_len; /* total length including id & length field */
302 struct brcmf_vs_tlv vndrie;
303};
304
305struct parsed_vndr_ies {
306 u32 count;
307 struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER];
308};
309
Alwin Beukersef6ac172011-10-12 20:51:26 +0200310/* Quarter dBm units to mW
311 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
312 * Table is offset so the last entry is largest mW value that fits in
313 * a u16.
314 */
315
316#define QDBM_OFFSET 153 /* Offset for first entry */
317#define QDBM_TABLE_LEN 40 /* Table size */
318
319/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
320 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
321 */
322#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
323
324/* Largest mW value that will round down to the last table entry,
325 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
326 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
327 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
328 */
329#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
330
331static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
332/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
333/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
334/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
335/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
336/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
337/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
338};
339
340static u16 brcmf_qdbm_to_mw(u8 qdbm)
341{
342 uint factor = 1;
343 int idx = qdbm - QDBM_OFFSET;
344
345 if (idx >= QDBM_TABLE_LEN)
346 /* clamp to max u16 mW value */
347 return 0xFFFF;
348
349 /* scale the qdBm index up to the range of the table 0-40
350 * where an offset of 40 qdBm equals a factor of 10 mW.
351 */
352 while (idx < 0) {
353 idx += 40;
354 factor *= 10;
355 }
356
357 /* return the mW value scaled down to the correct factor of 10,
358 * adding in factor/2 to get proper rounding.
359 */
360 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
361}
362
363static u8 brcmf_mw_to_qdbm(u16 mw)
364{
365 u8 qdbm;
366 int offset;
367 uint mw_uint = mw;
368 uint boundary;
369
370 /* handle boundary case */
371 if (mw_uint <= 1)
372 return 0;
373
374 offset = QDBM_OFFSET;
375
376 /* move mw into the range of the table */
377 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
378 mw_uint *= 10;
379 offset -= 40;
380 }
381
382 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
383 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
384 nqdBm_to_mW_map[qdbm]) / 2;
385 if (mw_uint < boundary)
386 break;
387 }
388
389 qdbm += (u8) offset;
390
391 return qdbm;
392}
393
Arend van Spriel6e186162012-10-22 10:36:22 -0700394static u16 channel_to_chanspec(struct ieee80211_channel *ch)
395{
396 u16 chanspec;
397
398 chanspec = ieee80211_frequency_to_channel(ch->center_freq);
399 chanspec &= WL_CHANSPEC_CHAN_MASK;
400
401 if (ch->band == IEEE80211_BAND_2GHZ)
402 chanspec |= WL_CHANSPEC_BAND_2G;
403 else
404 chanspec |= WL_CHANSPEC_BAND_5G;
405
406 if (ch->flags & IEEE80211_CHAN_NO_HT40) {
407 chanspec |= WL_CHANSPEC_BW_20;
408 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
409 } else {
410 chanspec |= WL_CHANSPEC_BW_40;
411 if (ch->flags & IEEE80211_CHAN_NO_HT40PLUS)
412 chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
413 else
414 chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
415 }
416 return chanspec;
417}
418
Arend van Spriel5b435de2011-10-05 13:19:03 +0200419static void convert_key_from_CPU(struct brcmf_wsec_key *key,
420 struct brcmf_wsec_key_le *key_le)
421{
422 key_le->index = cpu_to_le32(key->index);
423 key_le->len = cpu_to_le32(key->len);
424 key_le->algo = cpu_to_le32(key->algo);
425 key_le->flags = cpu_to_le32(key->flags);
426 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
427 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
428 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
429 memcpy(key_le->data, key->data, sizeof(key->data));
430 memcpy(key_le->ea, key->ea, sizeof(key->ea));
431}
432
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200433static int
Arend van Spriel2eaba7e2012-10-22 10:36:26 -0700434send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200435{
436 int err;
437 struct brcmf_wsec_key_le key_le;
438
439 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200440
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700441 brcmf_netdev_wait_pend8021x(ndev);
442
Arend van Sprielac24be62012-10-22 10:36:23 -0700443 err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700444 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200445
Arend van Spriel5b435de2011-10-05 13:19:03 +0200446 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200447 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200448 return err;
449}
450
451static s32
452brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
453 enum nl80211_iftype type, u32 *flags,
454 struct vif_params *params)
455{
Arend van Sprielc1179032012-10-22 13:55:33 -0700456 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200457 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200458 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200459 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200460 s32 err = 0;
461
Hante Meuleman1a873342012-09-27 14:17:54 +0200462 WL_TRACE("Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200463
464 switch (type) {
465 case NL80211_IFTYPE_MONITOR:
466 case NL80211_IFTYPE_WDS:
467 WL_ERR("type (%d) : currently we do not support this type\n",
468 type);
469 return -EOPNOTSUPP;
470 case NL80211_IFTYPE_ADHOC:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200471 cfg->conf->mode = WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200472 infra = 0;
473 break;
474 case NL80211_IFTYPE_STATION:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200475 cfg->conf->mode = WL_MODE_BSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200476 infra = 1;
477 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200478 case NL80211_IFTYPE_AP:
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200479 cfg->conf->mode = WL_MODE_AP;
Hante Meuleman1a873342012-09-27 14:17:54 +0200480 ap = 1;
481 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200482 default:
483 err = -EINVAL;
484 goto done;
485 }
486
Hante Meuleman1a873342012-09-27 14:17:54 +0200487 if (ap) {
Arend van Sprielc1179032012-10-22 13:55:33 -0700488 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +0200489 WL_INFO("IF Type = AP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200490 } else {
Arend van Sprielac24be62012-10-22 10:36:23 -0700491 err = brcmf_fil_cmd_int_set(netdev_priv(ndev),
492 BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200493 if (err) {
494 WL_ERR("WLC_SET_INFRA error (%d)\n", err);
495 err = -EAGAIN;
496 goto done;
497 }
498 WL_INFO("IF Type = %s\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200499 (cfg->conf->mode == WL_MODE_IBSS) ?
Hante Meuleman1a873342012-09-27 14:17:54 +0200500 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200501 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200502 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200503
504done:
505 WL_TRACE("Exit\n");
506
507 return err;
508}
509
Arend van Spriel5b435de2011-10-05 13:19:03 +0200510static void brcmf_set_mpc(struct net_device *ndev, int mpc)
511{
Arend van Sprielc1179032012-10-22 13:55:33 -0700512 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200513 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200514
Arend van Sprielce81e312012-10-22 13:55:37 -0700515 if (check_vif_up(ifp->vif)) {
Arend van Sprielc1179032012-10-22 13:55:33 -0700516 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200517 if (err) {
518 WL_ERR("fail to set mpc\n");
519 return;
520 }
521 WL_INFO("MPC : %d\n", mpc);
522 }
523}
524
Hante Meulemane756af52012-09-11 21:18:52 +0200525static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
526 struct cfg80211_scan_request *request)
527{
528 u32 n_ssids;
529 u32 n_channels;
530 s32 i;
531 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200532 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200533 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200534 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200535
Arend van Sprielba40d162012-10-22 13:55:38 -0700536 memset(params_le->bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200537 params_le->bss_type = DOT11_BSSTYPE_ANY;
538 params_le->scan_type = 0;
539 params_le->channel_num = 0;
540 params_le->nprobes = cpu_to_le32(-1);
541 params_le->active_time = cpu_to_le32(-1);
542 params_le->passive_time = cpu_to_le32(-1);
543 params_le->home_time = cpu_to_le32(-1);
544 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
545
546 /* if request is null exit so it will be all channel broadcast scan */
547 if (!request)
548 return;
549
550 n_ssids = request->n_ssids;
551 n_channels = request->n_channels;
552 /* Copy channel array if applicable */
553 WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
554 if (n_channels > 0) {
555 for (i = 0; i < n_channels; i++) {
Arend van Spriel6e186162012-10-22 10:36:22 -0700556 chanspec = channel_to_chanspec(request->channels[i]);
Hante Meulemane756af52012-09-11 21:18:52 +0200557 WL_SCAN("Chan : %d, Channel spec: %x\n",
Arend van Spriel6e186162012-10-22 10:36:22 -0700558 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200559 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200560 }
561 } else {
562 WL_SCAN("Scanning all channels\n");
563 }
564 /* Copy ssid array if applicable */
565 WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids);
566 if (n_ssids > 0) {
567 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
568 n_channels * sizeof(u16);
569 offset = roundup(offset, sizeof(u32));
570 ptr = (char *)params_le + offset;
571 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200572 memset(&ssid_le, 0, sizeof(ssid_le));
573 ssid_le.SSID_len =
574 cpu_to_le32(request->ssids[i].ssid_len);
575 memcpy(ssid_le.SSID, request->ssids[i].ssid,
576 request->ssids[i].ssid_len);
577 if (!ssid_le.SSID_len)
Hante Meulemane756af52012-09-11 21:18:52 +0200578 WL_SCAN("%d: Broadcast scan\n", i);
579 else
580 WL_SCAN("%d: scan for %s size =%d\n", i,
Arend van Spriel029591f2012-09-19 22:21:06 +0200581 ssid_le.SSID, ssid_le.SSID_len);
582 memcpy(ptr, &ssid_le, sizeof(ssid_le));
583 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200584 }
585 } else {
586 WL_SCAN("Broadcast scan %p\n", request->ssids);
587 if ((request->ssids) && request->ssids->ssid_len) {
588 WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID,
589 request->ssids->ssid_len);
590 params_le->ssid_le.SSID_len =
591 cpu_to_le32(request->ssids->ssid_len);
592 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
593 request->ssids->ssid_len);
594 }
595 }
596 /* Adding mask to channel numbers */
597 params_le->channel_num =
598 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
599 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
600}
601
602static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200603brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +0200604 struct net_device *ndev,
605 bool aborted, bool fw_abort)
606{
607 struct brcmf_scan_params_le params_le;
608 struct cfg80211_scan_request *scan_request;
609 s32 err = 0;
610
611 WL_SCAN("Enter\n");
612
613 /* clear scan request, because the FW abort can cause a second call */
614 /* to this functon and might cause a double cfg80211_scan_done */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200615 scan_request = cfg->scan_request;
616 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200617
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200618 if (timer_pending(&cfg->escan_timeout))
619 del_timer_sync(&cfg->escan_timeout);
Hante Meulemane756af52012-09-11 21:18:52 +0200620
621 if (fw_abort) {
622 /* Do a scan abort to stop the driver's scan engine */
623 WL_SCAN("ABORT scan in firmware\n");
624 memset(&params_le, 0, sizeof(params_le));
Arend van Sprielba40d162012-10-22 13:55:38 -0700625 memset(params_le.bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200626 params_le.bss_type = DOT11_BSSTYPE_ANY;
627 params_le.scan_type = 0;
628 params_le.channel_num = cpu_to_le32(1);
629 params_le.nprobes = cpu_to_le32(1);
630 params_le.active_time = cpu_to_le32(-1);
631 params_le.passive_time = cpu_to_le32(-1);
632 params_le.home_time = cpu_to_le32(-1);
633 /* Scan is aborted by setting channel_list[0] to -1 */
634 params_le.channel_list[0] = cpu_to_le16(-1);
635 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielac24be62012-10-22 10:36:23 -0700636 err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN,
637 &params_le, sizeof(params_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200638 if (err)
639 WL_ERR("Scan abort failed\n");
640 }
Arend van Spriele5806072012-09-19 22:21:08 +0200641 /*
642 * e-scan can be initiated by scheduled scan
643 * which takes precedence.
644 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200645 if (cfg->sched_escan) {
Arend van Spriele5806072012-09-19 22:21:08 +0200646 WL_SCAN("scheduled scan completed\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200647 cfg->sched_escan = false;
Arend van Spriele5806072012-09-19 22:21:08 +0200648 if (!aborted)
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200649 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriele5806072012-09-19 22:21:08 +0200650 brcmf_set_mpc(ndev, 1);
651 } else if (scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +0200652 WL_SCAN("ESCAN Completed scan: %s\n",
653 aborted ? "Aborted" : "Done");
654 cfg80211_scan_done(scan_request, aborted);
655 brcmf_set_mpc(ndev, 1);
656 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700657 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meulemane756af52012-09-11 21:18:52 +0200658 WL_ERR("Scan complete while device not scanning\n");
659 return -EPERM;
660 }
661
662 return err;
663}
664
665static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200666brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
Hante Meulemane756af52012-09-11 21:18:52 +0200667 struct cfg80211_scan_request *request, u16 action)
668{
669 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
670 offsetof(struct brcmf_escan_params_le, params_le);
671 struct brcmf_escan_params_le *params;
672 s32 err = 0;
673
674 WL_SCAN("E-SCAN START\n");
675
676 if (request != NULL) {
677 /* Allocate space for populating ssids in struct */
678 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
679
680 /* Allocate space for populating ssids in struct */
681 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
682 }
683
684 params = kzalloc(params_size, GFP_KERNEL);
685 if (!params) {
686 err = -ENOMEM;
687 goto exit;
688 }
689 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
690 brcmf_escan_prep(&params->params_le, request);
691 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
692 params->action = cpu_to_le16(action);
693 params->sync_id = cpu_to_le16(0x1234);
694
Arend van Sprielac24be62012-10-22 10:36:23 -0700695 err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan",
696 params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200697 if (err) {
698 if (err == -EBUSY)
699 WL_INFO("system busy : escan canceled\n");
700 else
701 WL_ERR("error (%d)\n", err);
702 }
703
704 kfree(params);
705exit:
706 return err;
707}
708
709static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200710brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Hante Meulemane756af52012-09-11 21:18:52 +0200711 struct net_device *ndev, struct cfg80211_scan_request *request)
712{
713 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700714 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200715 struct brcmf_scan_results *results;
716
717 WL_SCAN("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200718 cfg->escan_info.ndev = ndev;
719 cfg->escan_info.wiphy = wiphy;
720 cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700721 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielac24be62012-10-22 10:36:23 -0700722 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700723 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200724 if (err) {
725 WL_ERR("error (%d)\n", err);
726 return err;
727 }
728 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200729 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +0200730 results->version = 0;
731 results->count = 0;
732 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
733
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200734 err = brcmf_run_escan(cfg, ndev, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200735 if (err)
736 brcmf_set_mpc(ndev, 1);
737 return err;
738}
739
740static s32
741brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
742 struct cfg80211_scan_request *request,
743 struct cfg80211_ssid *this_ssid)
744{
Arend van Sprielc1179032012-10-22 13:55:33 -0700745 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200746 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Hante Meulemane756af52012-09-11 21:18:52 +0200747 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -0800748 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700749 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200750 bool escan_req;
751 bool spec_scan;
752 s32 err;
753 u32 SSID_len;
754
755 WL_SCAN("START ESCAN\n");
756
Arend van Sprielc1179032012-10-22 13:55:33 -0700757 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
758 WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200759 return -EAGAIN;
760 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700761 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
762 WL_ERR("Scanning being aborted: status (%lu)\n",
763 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200764 return -EAGAIN;
765 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700766 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
767 WL_ERR("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +0200768 return -EAGAIN;
769 }
770
771 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200772 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +0200773 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
774
775 escan_req = false;
776 if (request) {
777 /* scan bss */
778 ssids = request->ssids;
779 escan_req = true;
780 } else {
781 /* scan in ibss */
782 /* we don't do escan in ibss */
783 ssids = this_ssid;
784 }
785
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200786 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -0700787 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200788 if (escan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200789 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -0800790 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +0200791 goto scan_out;
792 } else {
793 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
794 ssids->ssid, ssids->ssid_len);
795 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
796 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
797 sr->ssid_le.SSID_len = cpu_to_le32(0);
798 spec_scan = false;
799 if (SSID_len) {
800 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
801 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
802 spec_scan = true;
803 } else
804 WL_SCAN("Broadcast scan\n");
805
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700806 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -0700807 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700808 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200809 if (err) {
810 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
811 goto scan_out;
812 }
813 brcmf_set_mpc(ndev, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -0700814 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -0700815 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200816 if (err) {
817 if (err == -EBUSY)
818 WL_INFO("BUSY: scan for \"%s\" canceled\n",
819 sr->ssid_le.SSID);
820 else
821 WL_ERR("WLC_SCAN error (%d)\n", err);
822
823 brcmf_set_mpc(ndev, 1);
824 goto scan_out;
825 }
826 }
827
828 return 0;
829
830scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -0700831 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200832 if (timer_pending(&cfg->escan_timeout))
833 del_timer_sync(&cfg->escan_timeout);
834 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200835 return err;
836}
837
Arend van Spriel5b435de2011-10-05 13:19:03 +0200838static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700839brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200840{
Johannes Bergfd014282012-06-18 19:17:03 +0200841 struct net_device *ndev = request->wdev->netdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200842 s32 err = 0;
843
844 WL_TRACE("Enter\n");
845
Arend van Sprielce81e312012-10-22 13:55:37 -0700846 if (!check_vif_up(container_of(request->wdev,
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700847 struct brcmf_cfg80211_vif, wdev)))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200848 return -EIO;
849
Hante Meulemanf07998952012-11-05 16:22:13 -0800850 err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +0200851
Arend van Spriel5b435de2011-10-05 13:19:03 +0200852 if (err)
853 WL_ERR("scan error (%d)\n", err);
854
855 WL_TRACE("Exit\n");
856 return err;
857}
858
859static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
860{
861 s32 err = 0;
862
Arend van Sprielac24be62012-10-22 10:36:23 -0700863 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
864 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200865 if (err)
866 WL_ERR("Error (%d)\n", err);
867
868 return err;
869}
870
871static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
872{
873 s32 err = 0;
874
Arend van Sprielac24be62012-10-22 10:36:23 -0700875 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
876 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200877 if (err)
878 WL_ERR("Error (%d)\n", err);
879
880 return err;
881}
882
883static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
884{
885 s32 err = 0;
886 u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL);
887
Arend van Sprielac24be62012-10-22 10:36:23 -0700888 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200889 if (err) {
890 WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
891 return err;
892 }
893 return err;
894}
895
896static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
897{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200898 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
899 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700900 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200901 s32 err = 0;
902
903 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -0700904 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200905 return -EIO;
906
907 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200908 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
909 cfg->conf->rts_threshold = wiphy->rts_threshold;
910 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200911 if (!err)
912 goto done;
913 }
914 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200915 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
916 cfg->conf->frag_threshold = wiphy->frag_threshold;
917 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200918 if (!err)
919 goto done;
920 }
921 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200922 && (cfg->conf->retry_long != wiphy->retry_long)) {
923 cfg->conf->retry_long = wiphy->retry_long;
924 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200925 if (!err)
926 goto done;
927 }
928 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200929 && (cfg->conf->retry_short != wiphy->retry_short)) {
930 cfg->conf->retry_short = wiphy->retry_short;
931 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200932 if (!err)
933 goto done;
934 }
935
936done:
937 WL_TRACE("Exit\n");
938 return err;
939}
940
Arend van Spriel5b435de2011-10-05 13:19:03 +0200941static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
942{
943 memset(prof, 0, sizeof(*prof));
944}
945
946static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
947 size_t *join_params_size)
948{
949 u16 chanspec = 0;
950
951 if (ch != 0) {
952 if (ch <= CH_MAX_2G_CHANNEL)
953 chanspec |= WL_CHANSPEC_BAND_2G;
954 else
955 chanspec |= WL_CHANSPEC_BAND_5G;
956
957 chanspec |= WL_CHANSPEC_BW_20;
958 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
959
960 *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
961 sizeof(u16);
962
963 chanspec |= (ch & WL_CHANSPEC_CHAN_MASK);
964 join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec);
965 join_params->params_le.chanspec_num = cpu_to_le32(1);
966
967 WL_CONN("join_params->params.chanspec_list[0]= %#X,"
968 "channel %d, chanspec %#X\n",
969 chanspec, ch, chanspec);
970 }
971}
972
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200973static void brcmf_link_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200974{
975 struct net_device *ndev = NULL;
976 s32 err = 0;
977
978 WL_TRACE("Enter\n");
979
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200980 if (cfg->link_up) {
981 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200982 WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Sprielac24be62012-10-22 10:36:23 -0700983 err = brcmf_fil_cmd_data_set(netdev_priv(ndev),
984 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200985 if (err)
986 WL_ERR("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200987 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200988 }
989 WL_TRACE("Exit\n");
990}
991
992static s32
993brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
994 struct cfg80211_ibss_params *params)
995{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200996 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700997 struct brcmf_if *ifp = netdev_priv(ndev);
998 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200999 struct brcmf_join_params join_params;
1000 size_t join_params_size = 0;
1001 s32 err = 0;
1002 s32 wsec = 0;
1003 s32 bcnprd;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001004
1005 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001006 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001007 return -EIO;
1008
1009 if (params->ssid)
1010 WL_CONN("SSID: %s\n", params->ssid);
1011 else {
1012 WL_CONN("SSID: NULL, Not supported\n");
1013 return -EOPNOTSUPP;
1014 }
1015
Arend van Sprielc1179032012-10-22 13:55:33 -07001016 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001017
1018 if (params->bssid)
Andy Shevchenko040a7832012-07-12 10:43:44 +03001019 WL_CONN("BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001020 else
1021 WL_CONN("No BSSID specified\n");
1022
1023 if (params->channel)
1024 WL_CONN("channel: %d\n", params->channel->center_freq);
1025 else
1026 WL_CONN("no channel specified\n");
1027
1028 if (params->channel_fixed)
1029 WL_CONN("fixed channel required\n");
1030 else
1031 WL_CONN("no fixed channel required\n");
1032
1033 if (params->ie && params->ie_len)
1034 WL_CONN("ie len: %d\n", params->ie_len);
1035 else
1036 WL_CONN("no ie specified\n");
1037
1038 if (params->beacon_interval)
1039 WL_CONN("beacon interval: %d\n", params->beacon_interval);
1040 else
1041 WL_CONN("no beacon interval specified\n");
1042
1043 if (params->basic_rates)
1044 WL_CONN("basic rates: %08X\n", params->basic_rates);
1045 else
1046 WL_CONN("no basic rates specified\n");
1047
1048 if (params->privacy)
1049 WL_CONN("privacy required\n");
1050 else
1051 WL_CONN("no privacy required\n");
1052
1053 /* Configure Privacy for starter */
1054 if (params->privacy)
1055 wsec |= WEP_ENABLED;
1056
Arend van Sprielc1179032012-10-22 13:55:33 -07001057 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001058 if (err) {
1059 WL_ERR("wsec failed (%d)\n", err);
1060 goto done;
1061 }
1062
1063 /* Configure Beacon Interval for starter */
1064 if (params->beacon_interval)
1065 bcnprd = params->beacon_interval;
1066 else
1067 bcnprd = 100;
1068
Arend van Sprielc1179032012-10-22 13:55:33 -07001069 err = brcmf_fil_cmd_int_set(ifp, BRCM_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001070 if (err) {
1071 WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
1072 goto done;
1073 }
1074
1075 /* Configure required join parameter */
1076 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1077
1078 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001079 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1080 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1081 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1082 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001083 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001084
1085 /* BSSID */
1086 if (params->bssid) {
1087 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1088 join_params_size = sizeof(join_params.ssid_le) +
1089 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001090 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001091 } else {
Arend van Sprielba40d162012-10-22 13:55:38 -07001092 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001093 memset(profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001094 }
1095
Arend van Spriel5b435de2011-10-05 13:19:03 +02001096 /* Channel */
1097 if (params->channel) {
1098 u32 target_channel;
1099
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001100 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001101 ieee80211_frequency_to_channel(
1102 params->channel->center_freq);
1103 if (params->channel_fixed) {
1104 /* adding chanspec */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001105 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001106 &join_params, &join_params_size);
1107 }
1108
1109 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001110 target_channel = cfg->channel;
Arend van Sprielc1179032012-10-22 13:55:33 -07001111 err = brcmf_fil_cmd_int_set(ifp, BRCM_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001112 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001113 if (err) {
1114 WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
1115 goto done;
1116 }
1117 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001118 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001119
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001120 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001121
1122
Arend van Sprielc1179032012-10-22 13:55:33 -07001123 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001124 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001125 if (err) {
1126 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1127 goto done;
1128 }
1129
1130done:
1131 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001132 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001133 WL_TRACE("Exit\n");
1134 return err;
1135}
1136
1137static s32
1138brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1139{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001140 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001141 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001142 s32 err = 0;
1143
1144 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001145 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001146 return -EIO;
1147
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001148 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001149
1150 WL_TRACE("Exit\n");
1151
1152 return err;
1153}
1154
1155static s32 brcmf_set_wpa_version(struct net_device *ndev,
1156 struct cfg80211_connect_params *sme)
1157{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001158 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001159 struct brcmf_cfg80211_security *sec;
1160 s32 val = 0;
1161 s32 err = 0;
1162
1163 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1164 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1165 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1166 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1167 else
1168 val = WPA_AUTH_DISABLED;
1169 WL_CONN("setting wpa_auth to 0x%0x\n", val);
Arend van Sprielac24be62012-10-22 10:36:23 -07001170 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001171 if (err) {
1172 WL_ERR("set wpa_auth failed (%d)\n", err);
1173 return err;
1174 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001175 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001176 sec->wpa_versions = sme->crypto.wpa_versions;
1177 return err;
1178}
1179
1180static s32 brcmf_set_auth_type(struct net_device *ndev,
1181 struct cfg80211_connect_params *sme)
1182{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001183 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184 struct brcmf_cfg80211_security *sec;
1185 s32 val = 0;
1186 s32 err = 0;
1187
1188 switch (sme->auth_type) {
1189 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1190 val = 0;
1191 WL_CONN("open system\n");
1192 break;
1193 case NL80211_AUTHTYPE_SHARED_KEY:
1194 val = 1;
1195 WL_CONN("shared key\n");
1196 break;
1197 case NL80211_AUTHTYPE_AUTOMATIC:
1198 val = 2;
1199 WL_CONN("automatic\n");
1200 break;
1201 case NL80211_AUTHTYPE_NETWORK_EAP:
1202 WL_CONN("network eap\n");
1203 default:
1204 val = 2;
1205 WL_ERR("invalid auth type (%d)\n", sme->auth_type);
1206 break;
1207 }
1208
Arend van Sprielac24be62012-10-22 10:36:23 -07001209 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001210 if (err) {
1211 WL_ERR("set auth failed (%d)\n", err);
1212 return err;
1213 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001214 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001215 sec->auth_type = sme->auth_type;
1216 return err;
1217}
1218
1219static s32
1220brcmf_set_set_cipher(struct net_device *ndev,
1221 struct cfg80211_connect_params *sme)
1222{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001223 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001224 struct brcmf_cfg80211_security *sec;
1225 s32 pval = 0;
1226 s32 gval = 0;
1227 s32 err = 0;
1228
1229 if (sme->crypto.n_ciphers_pairwise) {
1230 switch (sme->crypto.ciphers_pairwise[0]) {
1231 case WLAN_CIPHER_SUITE_WEP40:
1232 case WLAN_CIPHER_SUITE_WEP104:
1233 pval = WEP_ENABLED;
1234 break;
1235 case WLAN_CIPHER_SUITE_TKIP:
1236 pval = TKIP_ENABLED;
1237 break;
1238 case WLAN_CIPHER_SUITE_CCMP:
1239 pval = AES_ENABLED;
1240 break;
1241 case WLAN_CIPHER_SUITE_AES_CMAC:
1242 pval = AES_ENABLED;
1243 break;
1244 default:
1245 WL_ERR("invalid cipher pairwise (%d)\n",
1246 sme->crypto.ciphers_pairwise[0]);
1247 return -EINVAL;
1248 }
1249 }
1250 if (sme->crypto.cipher_group) {
1251 switch (sme->crypto.cipher_group) {
1252 case WLAN_CIPHER_SUITE_WEP40:
1253 case WLAN_CIPHER_SUITE_WEP104:
1254 gval = WEP_ENABLED;
1255 break;
1256 case WLAN_CIPHER_SUITE_TKIP:
1257 gval = TKIP_ENABLED;
1258 break;
1259 case WLAN_CIPHER_SUITE_CCMP:
1260 gval = AES_ENABLED;
1261 break;
1262 case WLAN_CIPHER_SUITE_AES_CMAC:
1263 gval = AES_ENABLED;
1264 break;
1265 default:
1266 WL_ERR("invalid cipher group (%d)\n",
1267 sme->crypto.cipher_group);
1268 return -EINVAL;
1269 }
1270 }
1271
1272 WL_CONN("pval (%d) gval (%d)\n", pval, gval);
Arend van Sprielac24be62012-10-22 10:36:23 -07001273 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wsec", pval | gval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001274 if (err) {
1275 WL_ERR("error (%d)\n", err);
1276 return err;
1277 }
1278
Arend van Spriel06bb1232012-09-27 14:17:56 +02001279 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001280 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1281 sec->cipher_group = sme->crypto.cipher_group;
1282
1283 return err;
1284}
1285
1286static s32
1287brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1288{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001289 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001290 struct brcmf_cfg80211_security *sec;
1291 s32 val = 0;
1292 s32 err = 0;
1293
1294 if (sme->crypto.n_akm_suites) {
Arend van Sprielac24be62012-10-22 10:36:23 -07001295 err = brcmf_fil_iovar_int_get(netdev_priv(ndev),
1296 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001297 if (err) {
1298 WL_ERR("could not get wpa_auth (%d)\n", err);
1299 return err;
1300 }
1301 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1302 switch (sme->crypto.akm_suites[0]) {
1303 case WLAN_AKM_SUITE_8021X:
1304 val = WPA_AUTH_UNSPECIFIED;
1305 break;
1306 case WLAN_AKM_SUITE_PSK:
1307 val = WPA_AUTH_PSK;
1308 break;
1309 default:
1310 WL_ERR("invalid cipher group (%d)\n",
1311 sme->crypto.cipher_group);
1312 return -EINVAL;
1313 }
1314 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1315 switch (sme->crypto.akm_suites[0]) {
1316 case WLAN_AKM_SUITE_8021X:
1317 val = WPA2_AUTH_UNSPECIFIED;
1318 break;
1319 case WLAN_AKM_SUITE_PSK:
1320 val = WPA2_AUTH_PSK;
1321 break;
1322 default:
1323 WL_ERR("invalid cipher group (%d)\n",
1324 sme->crypto.cipher_group);
1325 return -EINVAL;
1326 }
1327 }
1328
1329 WL_CONN("setting wpa_auth to %d\n", val);
Arend van Sprielac24be62012-10-22 10:36:23 -07001330 err = brcmf_fil_iovar_int_set(netdev_priv(ndev),
1331 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001332 if (err) {
1333 WL_ERR("could not set wpa_auth (%d)\n", err);
1334 return err;
1335 }
1336 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001337 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001338 sec->wpa_auth = sme->crypto.akm_suites[0];
1339
1340 return err;
1341}
1342
1343static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001344brcmf_set_sharedkey(struct net_device *ndev,
1345 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001346{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001347 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001348 struct brcmf_cfg80211_security *sec;
1349 struct brcmf_wsec_key key;
1350 s32 val;
1351 s32 err = 0;
1352
1353 WL_CONN("key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001354
Roland Vossena718e2f2011-10-12 20:51:24 +02001355 if (sme->key_len == 0)
1356 return 0;
1357
Arend van Spriel06bb1232012-09-27 14:17:56 +02001358 sec = &profile->sec;
Roland Vossena718e2f2011-10-12 20:51:24 +02001359 WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n",
1360 sec->wpa_versions, sec->cipher_pairwise);
1361
1362 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1363 return 0;
1364
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001365 if (!(sec->cipher_pairwise &
1366 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1367 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001368
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001369 memset(&key, 0, sizeof(key));
1370 key.len = (u32) sme->key_len;
1371 key.index = (u32) sme->key_idx;
1372 if (key.len > sizeof(key.data)) {
1373 WL_ERR("Too long key length (%u)\n", key.len);
1374 return -EINVAL;
1375 }
1376 memcpy(key.data, sme->key, key.len);
1377 key.flags = BRCMF_PRIMARY_KEY;
1378 switch (sec->cipher_pairwise) {
1379 case WLAN_CIPHER_SUITE_WEP40:
1380 key.algo = CRYPTO_ALGO_WEP1;
1381 break;
1382 case WLAN_CIPHER_SUITE_WEP104:
1383 key.algo = CRYPTO_ALGO_WEP128;
1384 break;
1385 default:
1386 WL_ERR("Invalid algorithm (%d)\n",
1387 sme->crypto.ciphers_pairwise[0]);
1388 return -EINVAL;
1389 }
1390 /* Set the new key/index */
1391 WL_CONN("key length (%d) key index (%d) algo (%d)\n",
1392 key.len, key.index, key.algo);
1393 WL_CONN("key \"%s\"\n", key.data);
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001394 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001395 if (err)
1396 return err;
1397
1398 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
1399 WL_CONN("set auth_type to shared key\n");
1400 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001401 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001402 if (err)
1403 WL_ERR("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001404 }
1405 return err;
1406}
1407
1408static s32
1409brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
1410 struct cfg80211_connect_params *sme)
1411{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001412 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001413 struct brcmf_if *ifp = netdev_priv(ndev);
1414 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415 struct ieee80211_channel *chan = sme->channel;
1416 struct brcmf_join_params join_params;
1417 size_t join_params_size;
1418 struct brcmf_ssid ssid;
1419
1420 s32 err = 0;
1421
1422 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001423 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001424 return -EIO;
1425
1426 if (!sme->ssid) {
1427 WL_ERR("Invalid ssid\n");
1428 return -EOPNOTSUPP;
1429 }
1430
Arend van Sprielc1179032012-10-22 13:55:33 -07001431 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001432
1433 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001434 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001435 ieee80211_frequency_to_channel(chan->center_freq);
1436 WL_CONN("channel (%d), center_req (%d)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001437 cfg->channel, chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001438 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001439 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001440
1441 WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
1442
1443 err = brcmf_set_wpa_version(ndev, sme);
1444 if (err) {
1445 WL_ERR("wl_set_wpa_version failed (%d)\n", err);
1446 goto done;
1447 }
1448
1449 err = brcmf_set_auth_type(ndev, sme);
1450 if (err) {
1451 WL_ERR("wl_set_auth_type failed (%d)\n", err);
1452 goto done;
1453 }
1454
1455 err = brcmf_set_set_cipher(ndev, sme);
1456 if (err) {
1457 WL_ERR("wl_set_set_cipher failed (%d)\n", err);
1458 goto done;
1459 }
1460
1461 err = brcmf_set_key_mgmt(ndev, sme);
1462 if (err) {
1463 WL_ERR("wl_set_key_mgmt failed (%d)\n", err);
1464 goto done;
1465 }
1466
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001467 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001468 if (err) {
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001469 WL_ERR("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001470 goto done;
1471 }
1472
1473 memset(&join_params, 0, sizeof(join_params));
1474 join_params_size = sizeof(join_params.ssid_le);
1475
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001476 profile->ssid.SSID_len = min_t(u32,
1477 sizeof(ssid.SSID), (u32)sme->ssid_len);
1478 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
1479 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1480 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001481
Arend van Sprielba40d162012-10-22 13:55:38 -07001482 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001483
1484 if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
1485 WL_CONN("ssid \"%s\", len (%d)\n",
1486 ssid.SSID, ssid.SSID_len);
1487
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001488 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001489 &join_params, &join_params_size);
Arend van Sprielc1179032012-10-22 13:55:33 -07001490 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001491 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001492 if (err)
1493 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1494
1495done:
1496 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001497 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001498 WL_TRACE("Exit\n");
1499 return err;
1500}
1501
1502static s32
1503brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1504 u16 reason_code)
1505{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001506 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001507 struct brcmf_if *ifp = netdev_priv(ndev);
1508 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001509 struct brcmf_scb_val_le scbval;
1510 s32 err = 0;
1511
1512 WL_TRACE("Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001513 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001514 return -EIO;
1515
Arend van Sprielc1179032012-10-22 13:55:33 -07001516 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001517
Arend van Spriel06bb1232012-09-27 14:17:56 +02001518 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001519 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001520 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001521 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001522 if (err)
1523 WL_ERR("error (%d)\n", err);
1524
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001525 cfg->link_up = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001526
1527 WL_TRACE("Exit\n");
1528 return err;
1529}
1530
1531static s32
1532brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001533 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001534{
1535
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001536 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001537 struct net_device *ndev = cfg_to_ndev(cfg);
1538 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001539 u16 txpwrmw;
1540 s32 err = 0;
1541 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001542 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001543
1544 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001545 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001546 return -EIO;
1547
1548 switch (type) {
1549 case NL80211_TX_POWER_AUTOMATIC:
1550 break;
1551 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001552 case NL80211_TX_POWER_FIXED:
1553 if (dbm < 0) {
1554 WL_ERR("TX_POWER_FIXED - dbm is negative\n");
1555 err = -EINVAL;
1556 goto done;
1557 }
1558 break;
1559 }
1560 /* Make sure radio is off or on as far as software is concerned */
1561 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001562 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001563 if (err)
1564 WL_ERR("WLC_SET_RADIO error (%d)\n", err);
1565
1566 if (dbm > 0xffff)
1567 txpwrmw = 0xffff;
1568 else
1569 txpwrmw = (u16) dbm;
Arend van Sprielac24be62012-10-22 10:36:23 -07001570 err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
1571 (s32)brcmf_mw_to_qdbm(txpwrmw));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001572 if (err)
1573 WL_ERR("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001574 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001575
1576done:
1577 WL_TRACE("Exit\n");
1578 return err;
1579}
1580
1581static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
1582{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001583 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001584 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001585 s32 txpwrdbm;
1586 u8 result;
1587 s32 err = 0;
1588
1589 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001590 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001591 return -EIO;
1592
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001593 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001594 if (err) {
1595 WL_ERR("error (%d)\n", err);
1596 goto done;
1597 }
1598
1599 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001600 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001601
1602done:
1603 WL_TRACE("Exit\n");
1604 return err;
1605}
1606
1607static s32
1608brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1609 u8 key_idx, bool unicast, bool multicast)
1610{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001611 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001612 u32 index;
1613 u32 wsec;
1614 s32 err = 0;
1615
1616 WL_TRACE("Enter\n");
1617 WL_CONN("key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001618 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001619 return -EIO;
1620
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001621 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001622 if (err) {
1623 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
1624 goto done;
1625 }
1626
1627 if (wsec & WEP_ENABLED) {
1628 /* Just select a new current key */
1629 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001630 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001631 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001632 if (err)
1633 WL_ERR("error (%d)\n", err);
1634 }
1635done:
1636 WL_TRACE("Exit\n");
1637 return err;
1638}
1639
1640static s32
1641brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
1642 u8 key_idx, const u8 *mac_addr, struct key_params *params)
1643{
1644 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001645 s32 err = 0;
1646
1647 memset(&key, 0, sizeof(key));
1648 key.index = (u32) key_idx;
1649 /* Instead of bcast for ea address for default wep keys,
1650 driver needs it to be Null */
1651 if (!is_multicast_ether_addr(mac_addr))
1652 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
1653 key.len = (u32) params->key_len;
1654 /* check for key index change */
1655 if (key.len == 0) {
1656 /* key delete */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001657 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001658 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001659 WL_ERR("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001660 } else {
1661 if (key.len > sizeof(key.data)) {
1662 WL_ERR("Invalid key length (%d)\n", key.len);
1663 return -EINVAL;
1664 }
1665
1666 WL_CONN("Setting the key index %d\n", key.index);
1667 memcpy(key.data, params->key, key.len);
1668
1669 if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
1670 u8 keybuf[8];
1671 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1672 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1673 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1674 }
1675
1676 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
1677 if (params->seq && params->seq_len == 6) {
1678 /* rx iv */
1679 u8 *ivptr;
1680 ivptr = (u8 *) params->seq;
1681 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
1682 (ivptr[3] << 8) | ivptr[2];
1683 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
1684 key.iv_initialized = true;
1685 }
1686
1687 switch (params->cipher) {
1688 case WLAN_CIPHER_SUITE_WEP40:
1689 key.algo = CRYPTO_ALGO_WEP1;
1690 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1691 break;
1692 case WLAN_CIPHER_SUITE_WEP104:
1693 key.algo = CRYPTO_ALGO_WEP128;
1694 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1695 break;
1696 case WLAN_CIPHER_SUITE_TKIP:
1697 key.algo = CRYPTO_ALGO_TKIP;
1698 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1699 break;
1700 case WLAN_CIPHER_SUITE_AES_CMAC:
1701 key.algo = CRYPTO_ALGO_AES_CCM;
1702 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1703 break;
1704 case WLAN_CIPHER_SUITE_CCMP:
1705 key.algo = CRYPTO_ALGO_AES_CCM;
1706 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
1707 break;
1708 default:
1709 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
1710 return -EINVAL;
1711 }
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001712 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001713 if (err)
1714 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001715 }
1716 return err;
1717}
1718
1719static s32
1720brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1721 u8 key_idx, bool pairwise, const u8 *mac_addr,
1722 struct key_params *params)
1723{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001724 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001725 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001726 struct brcmf_wsec_key key;
1727 s32 val;
1728 s32 wsec;
1729 s32 err = 0;
1730 u8 keybuf[8];
1731
1732 WL_TRACE("Enter\n");
1733 WL_CONN("key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001734 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001735 return -EIO;
1736
1737 if (mac_addr) {
1738 WL_TRACE("Exit");
1739 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
1740 }
1741 memset(&key, 0, sizeof(key));
1742
1743 key.len = (u32) params->key_len;
1744 key.index = (u32) key_idx;
1745
1746 if (key.len > sizeof(key.data)) {
1747 WL_ERR("Too long key length (%u)\n", key.len);
1748 err = -EINVAL;
1749 goto done;
1750 }
1751 memcpy(key.data, params->key, key.len);
1752
1753 key.flags = BRCMF_PRIMARY_KEY;
1754 switch (params->cipher) {
1755 case WLAN_CIPHER_SUITE_WEP40:
1756 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001757 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001758 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1759 break;
1760 case WLAN_CIPHER_SUITE_WEP104:
1761 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001762 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001763 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1764 break;
1765 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001766 if (cfg->conf->mode != WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001767 WL_CONN("Swapping key\n");
1768 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1769 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1770 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1771 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001772 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001773 val = TKIP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001774 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1775 break;
1776 case WLAN_CIPHER_SUITE_AES_CMAC:
1777 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001778 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001779 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1780 break;
1781 case WLAN_CIPHER_SUITE_CCMP:
1782 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001783 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001784 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
1785 break;
1786 default:
1787 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
1788 err = -EINVAL;
1789 goto done;
1790 }
1791
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001792 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001793 if (err)
1794 goto done;
1795
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001796 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001797 if (err) {
1798 WL_ERR("get wsec error (%d)\n", err);
1799 goto done;
1800 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001801 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001802 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001803 if (err) {
1804 WL_ERR("set wsec error (%d)\n", err);
1805 goto done;
1806 }
1807
Arend van Spriel5b435de2011-10-05 13:19:03 +02001808done:
1809 WL_TRACE("Exit\n");
1810 return err;
1811}
1812
1813static s32
1814brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1815 u8 key_idx, bool pairwise, const u8 *mac_addr)
1816{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001817 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001818 struct brcmf_wsec_key key;
1819 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001820
1821 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001822 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001823 return -EIO;
1824
Hante Meuleman256c3742012-11-05 16:22:28 -08001825 if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
1826 /* we ignore this key index in this case */
1827 WL_ERR("invalid key index (%d)\n", key_idx);
1828 return -EINVAL;
1829 }
1830
Arend van Spriel5b435de2011-10-05 13:19:03 +02001831 memset(&key, 0, sizeof(key));
1832
1833 key.index = (u32) key_idx;
1834 key.flags = BRCMF_PRIMARY_KEY;
1835 key.algo = CRYPTO_ALGO_OFF;
1836
1837 WL_CONN("key index (%d)\n", key_idx);
1838
1839 /* Set the new key/index */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001840 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001841
Arend van Spriel5b435de2011-10-05 13:19:03 +02001842 WL_TRACE("Exit\n");
1843 return err;
1844}
1845
1846static s32
1847brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1848 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
1849 void (*callback) (void *cookie, struct key_params * params))
1850{
1851 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001852 struct brcmf_if *ifp = netdev_priv(ndev);
1853 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001854 struct brcmf_cfg80211_security *sec;
1855 s32 wsec;
1856 s32 err = 0;
1857
1858 WL_TRACE("Enter\n");
1859 WL_CONN("key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001860 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001861 return -EIO;
1862
1863 memset(&params, 0, sizeof(params));
1864
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001865 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001866 if (err) {
1867 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
1868 /* Ignore this error, may happen during DISASSOC */
1869 err = -EAGAIN;
1870 goto done;
1871 }
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001872 switch (wsec & ~SES_OW_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001873 case WEP_ENABLED:
Arend van Spriel06bb1232012-09-27 14:17:56 +02001874 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001875 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
1876 params.cipher = WLAN_CIPHER_SUITE_WEP40;
1877 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1878 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
1879 params.cipher = WLAN_CIPHER_SUITE_WEP104;
1880 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1881 }
1882 break;
1883 case TKIP_ENABLED:
1884 params.cipher = WLAN_CIPHER_SUITE_TKIP;
1885 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1886 break;
1887 case AES_ENABLED:
1888 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
1889 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1890 break;
1891 default:
1892 WL_ERR("Invalid algo (0x%x)\n", wsec);
1893 err = -EINVAL;
1894 goto done;
1895 }
1896 callback(cookie, &params);
1897
1898done:
1899 WL_TRACE("Exit\n");
1900 return err;
1901}
1902
1903static s32
1904brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
1905 struct net_device *ndev, u8 key_idx)
1906{
1907 WL_INFO("Not supported\n");
1908
1909 return -EOPNOTSUPP;
1910}
1911
1912static s32
1913brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02001914 u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001915{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001916 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001917 struct brcmf_if *ifp = netdev_priv(ndev);
1918 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001919 struct brcmf_scb_val_le scb_val;
1920 int rssi;
1921 s32 rate;
1922 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02001923 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001924 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001925
Hante Meuleman1a873342012-09-27 14:17:54 +02001926 WL_TRACE("Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07001927 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001928 return -EIO;
1929
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001930 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001931 memcpy(&sta_info_le, mac, ETH_ALEN);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001932 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07001933 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001934 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02001935 if (err < 0) {
1936 WL_ERR("GET STA INFO failed, %d\n", err);
1937 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02001938 }
Hante Meuleman1a873342012-09-27 14:17:54 +02001939 sinfo->filled = STATION_INFO_INACTIVE_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001940 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
1941 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001942 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001943 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02001944 }
1945 WL_TRACE("STA idle time : %d ms, connected time :%d sec\n",
1946 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001947 } else if (cfg->conf->mode == WL_MODE_BSS) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001948 if (memcmp(mac, bssid, ETH_ALEN)) {
1949 WL_ERR("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
1950 mac, bssid);
1951 err = -ENOENT;
1952 goto done;
1953 }
1954 /* Report the current tx rate */
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001955 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02001956 if (err) {
1957 WL_ERR("Could not get rate (%d)\n", err);
1958 goto done;
1959 } else {
1960 sinfo->filled |= STATION_INFO_TX_BITRATE;
1961 sinfo->txrate.legacy = rate * 5;
1962 WL_CONN("Rate %d Mbps\n", rate / 2);
1963 }
1964
Arend van Sprielc1179032012-10-22 13:55:33 -07001965 if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
1966 &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001967 memset(&scb_val, 0, sizeof(scb_val));
Arend van Sprielc1179032012-10-22 13:55:33 -07001968 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
1969 &scb_val, sizeof(scb_val));
Hante Meuleman1a873342012-09-27 14:17:54 +02001970 if (err) {
1971 WL_ERR("Could not get rssi (%d)\n", err);
1972 goto done;
1973 } else {
1974 rssi = le32_to_cpu(scb_val.val);
1975 sinfo->filled |= STATION_INFO_SIGNAL;
1976 sinfo->signal = rssi;
1977 WL_CONN("RSSI %d dBm\n", rssi);
1978 }
1979 }
1980 } else
1981 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001982done:
1983 WL_TRACE("Exit\n");
1984 return err;
1985}
1986
1987static s32
1988brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
1989 bool enabled, s32 timeout)
1990{
1991 s32 pm;
1992 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001993 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07001994 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001995
1996 WL_TRACE("Enter\n");
1997
1998 /*
1999 * Powersave enable/disable request is coming from the
2000 * cfg80211 even before the interface is up. In that
2001 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002002 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002003 * FW later while initializing the dongle
2004 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002005 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002006 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002007
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002008 WL_INFO("Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002009 goto done;
2010 }
2011
2012 pm = enabled ? PM_FAST : PM_OFF;
2013 WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
2014
Arend van Sprielc1179032012-10-22 13:55:33 -07002015 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002016 if (err) {
2017 if (err == -ENODEV)
2018 WL_ERR("net_device is not ready yet\n");
2019 else
2020 WL_ERR("error (%d)\n", err);
2021 }
2022done:
2023 WL_TRACE("Exit\n");
2024 return err;
2025}
2026
2027static s32
2028brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
2029 const u8 *addr,
2030 const struct cfg80211_bitrate_mask *mask)
2031{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002032 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002033 struct brcm_rateset_le rateset_le;
2034 s32 rate;
2035 s32 val;
2036 s32 err_bg;
2037 s32 err_a;
2038 u32 legacy;
2039 s32 err = 0;
2040
2041 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002042 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002043 return -EIO;
2044
2045 /* addr param is always NULL. ignore it */
2046 /* Get current rateset */
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002047 err = brcmf_fil_cmd_data_get(ifp, BRCM_GET_CURR_RATESET,
Arend van Sprielac24be62012-10-22 10:36:23 -07002048 &rateset_le, sizeof(rateset_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002049 if (err) {
2050 WL_ERR("could not get current rateset (%d)\n", err);
2051 goto done;
2052 }
2053
2054 legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
2055 if (!legacy)
2056 legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
2057 0xFFFF);
2058
2059 val = wl_g_rates[legacy - 1].bitrate * 100000;
2060
2061 if (val < le32_to_cpu(rateset_le.count))
2062 /* Select rate by rateset index */
2063 rate = rateset_le.rates[val] & 0x7f;
2064 else
2065 /* Specified rate in bps */
2066 rate = val / 500000;
2067
2068 WL_CONN("rate %d mbps\n", rate / 2);
2069
2070 /*
2071 *
2072 * Set rate override,
2073 * Since the is a/b/g-blind, both a/bg_rate are enforced.
2074 */
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002075 err_bg = brcmf_fil_iovar_int_set(ifp, "bg_rate", rate);
2076 err_a = brcmf_fil_iovar_int_set(ifp, "a_rate", rate);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002077 if (err_bg && err_a) {
2078 WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
2079 err = err_bg | err_a;
2080 }
2081
2082done:
2083 WL_TRACE("Exit\n");
2084 return err;
2085}
2086
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002087static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002088 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002089{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002090 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002091 struct ieee80211_channel *notify_channel;
2092 struct cfg80211_bss *bss;
2093 struct ieee80211_supported_band *band;
2094 s32 err = 0;
2095 u16 channel;
2096 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002097 u16 notify_capability;
2098 u16 notify_interval;
2099 u8 *notify_ie;
2100 size_t notify_ielen;
2101 s32 notify_signal;
2102
2103 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
2104 WL_ERR("Bss info is larger than buffer. Discarding\n");
2105 return 0;
2106 }
2107
2108 channel = bi->ctl_ch ? bi->ctl_ch :
2109 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2110
2111 if (channel <= CH_MAX_2G_CHANNEL)
2112 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2113 else
2114 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2115
2116 freq = ieee80211_channel_to_frequency(channel, band->band);
2117 notify_channel = ieee80211_get_channel(wiphy, freq);
2118
Arend van Spriel5b435de2011-10-05 13:19:03 +02002119 notify_capability = le16_to_cpu(bi->capability);
2120 notify_interval = le16_to_cpu(bi->beacon_period);
2121 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2122 notify_ielen = le32_to_cpu(bi->ie_length);
2123 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2124
2125 WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
2126 bi->BSSID[0], bi->BSSID[1], bi->BSSID[2],
2127 bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]);
2128 WL_CONN("Channel: %d(%d)\n", channel, freq);
2129 WL_CONN("Capability: %X\n", notify_capability);
2130 WL_CONN("Beacon interval: %d\n", notify_interval);
2131 WL_CONN("Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002132
2133 bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002134 0, notify_capability, notify_interval, notify_ie,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002135 notify_ielen, notify_signal, GFP_KERNEL);
2136
Franky Line78946e2011-11-10 20:30:34 +01002137 if (!bss)
2138 return -ENOMEM;
2139
2140 cfg80211_put_bss(bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002141
2142 return err;
2143}
2144
Roland Vossen6f09be02011-10-18 14:03:02 +02002145static struct brcmf_bss_info_le *
2146next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2147{
2148 if (bss == NULL)
2149 return list->bss_info_le;
2150 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2151 le32_to_cpu(bss->length));
2152}
2153
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002154static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002155{
2156 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002157 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002158 s32 err = 0;
2159 int i;
2160
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002161 bss_list = cfg->bss_list;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002162 if (bss_list->count != 0 &&
2163 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002164 WL_ERR("Version %d != WL_BSS_INFO_VERSION\n",
2165 bss_list->version);
2166 return -EOPNOTSUPP;
2167 }
2168 WL_SCAN("scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002169 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002170 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002171 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002172 if (err)
2173 break;
2174 }
2175 return err;
2176}
2177
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002178static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002179 struct net_device *ndev, const u8 *bssid)
2180{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002181 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002182 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002183 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002184 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002185 struct cfg80211_bss *bss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002186 u8 *buf = NULL;
2187 s32 err = 0;
2188 u16 channel;
2189 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002190 u16 notify_capability;
2191 u16 notify_interval;
2192 u8 *notify_ie;
2193 size_t notify_ielen;
2194 s32 notify_signal;
2195
2196 WL_TRACE("Enter\n");
2197
2198 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2199 if (buf == NULL) {
2200 err = -ENOMEM;
2201 goto CleanUp;
2202 }
2203
2204 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2205
Arend van Sprielac24be62012-10-22 10:36:23 -07002206 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2207 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002208 if (err) {
2209 WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
2210 goto CleanUp;
2211 }
2212
Roland Vossend34bf642011-10-18 14:03:01 +02002213 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002214
2215 channel = bi->ctl_ch ? bi->ctl_ch :
2216 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2217
2218 if (channel <= CH_MAX_2G_CHANNEL)
2219 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2220 else
2221 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2222
2223 freq = ieee80211_channel_to_frequency(channel, band->band);
2224 notify_channel = ieee80211_get_channel(wiphy, freq);
2225
Arend van Spriel5b435de2011-10-05 13:19:03 +02002226 notify_capability = le16_to_cpu(bi->capability);
2227 notify_interval = le16_to_cpu(bi->beacon_period);
2228 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2229 notify_ielen = le32_to_cpu(bi->ie_length);
2230 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2231
2232 WL_CONN("channel: %d(%d)\n", channel, freq);
2233 WL_CONN("capability: %X\n", notify_capability);
2234 WL_CONN("beacon interval: %d\n", notify_interval);
2235 WL_CONN("signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236
Franky Line78946e2011-11-10 20:30:34 +01002237 bss = cfg80211_inform_bss(wiphy, notify_channel, bssid,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002238 0, notify_capability, notify_interval,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002239 notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
2240
Franky Line78946e2011-11-10 20:30:34 +01002241 if (!bss) {
2242 err = -ENOMEM;
2243 goto CleanUp;
2244 }
2245
2246 cfg80211_put_bss(bss);
2247
Arend van Spriel5b435de2011-10-05 13:19:03 +02002248CleanUp:
2249
2250 kfree(buf);
2251
2252 WL_TRACE("Exit\n");
2253
2254 return err;
2255}
2256
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002257static bool brcmf_is_ibssmode(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002258{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002259 return cfg->conf->mode == WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002260}
2261
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002262/*
2263 * Traverse a string of 1-byte tag/1-byte length/variable-length value
2264 * triples, returning a pointer to the substring whose first element
2265 * matches tag
2266 */
2267static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
2268{
2269 struct brcmf_tlv *elt;
2270 int totlen;
2271
2272 elt = (struct brcmf_tlv *) buf;
2273 totlen = buflen;
2274
2275 /* find tagged parameter */
Hante Meuleman04012892012-09-27 14:17:49 +02002276 while (totlen >= TLV_HDR_LEN) {
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002277 int len = elt->len;
2278
2279 /* validate remaining totlen */
Hante Meuleman04012892012-09-27 14:17:49 +02002280 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002281 return elt;
2282
Hante Meuleman04012892012-09-27 14:17:49 +02002283 elt = (struct brcmf_tlv *) ((u8 *) elt + (len + TLV_HDR_LEN));
2284 totlen -= (len + TLV_HDR_LEN);
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002285 }
2286
2287 return NULL;
2288}
2289
Hante Meuleman1a873342012-09-27 14:17:54 +02002290/* Is any of the tlvs the expected entry? If
2291 * not update the tlvs buffer pointer/length.
2292 */
2293static bool
2294brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
2295 u8 *oui, u32 oui_len, u8 type)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002296{
Hante Meuleman1a873342012-09-27 14:17:54 +02002297 /* If the contents match the OUI and the type */
2298 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
2299 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
2300 type == ie[TLV_BODY_OFF + oui_len]) {
2301 return true;
2302 }
2303
2304 if (tlvs == NULL)
2305 return false;
2306 /* point to the next ie */
2307 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
2308 /* calculate the length of the rest of the buffer */
2309 *tlvs_len -= (int)(ie - *tlvs);
2310 /* update the pointer to the start of the buffer */
2311 *tlvs = ie;
2312
2313 return false;
2314}
2315
Franky Lin3cb91f52012-10-10 11:13:08 -07002316static struct brcmf_vs_tlv *
Hante Meuleman1a873342012-09-27 14:17:54 +02002317brcmf_find_wpaie(u8 *parse, u32 len)
2318{
2319 struct brcmf_tlv *ie;
2320
Arend van Spriel04b23122012-10-12 12:28:14 +02002321 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002322 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
2323 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
2324 return (struct brcmf_vs_tlv *)ie;
2325 }
2326 return NULL;
2327}
2328
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002329static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002330{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07002331 struct net_device *ndev = cfg_to_ndev(cfg);
2332 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
2333 struct brcmf_if *ifp = netdev_priv(ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002334 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002335 struct brcmf_ssid *ssid;
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002336 struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002337 u16 beacon_interval;
2338 u8 dtim_period;
2339 size_t ie_len;
2340 u8 *ie;
2341 s32 err = 0;
2342
2343 WL_TRACE("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002344 if (brcmf_is_ibssmode(cfg))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002345 return err;
2346
Arend van Spriel06bb1232012-09-27 14:17:56 +02002347 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002348
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002349 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002350 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002351 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002352 if (err) {
2353 WL_ERR("Could not get bss info %d\n", err);
2354 goto update_bss_info_out;
2355 }
2356
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002357 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2358 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002359 if (err)
2360 goto update_bss_info_out;
2361
2362 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2363 ie_len = le32_to_cpu(bi->ie_length);
2364 beacon_interval = le16_to_cpu(bi->beacon_period);
2365
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002366 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002367 if (tim)
2368 dtim_period = tim->data[1];
2369 else {
2370 /*
2371 * active scan was done so we could not get dtim
2372 * information out of probe response.
2373 * so we speficially query dtim information to dongle.
2374 */
2375 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002376 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002377 if (err) {
2378 WL_ERR("wl dtim_assoc failed (%d)\n", err);
2379 goto update_bss_info_out;
2380 }
2381 dtim_period = (u8)var;
2382 }
2383
Arend van Spriel5b435de2011-10-05 13:19:03 +02002384update_bss_info_out:
2385 WL_TRACE("Exit");
2386 return err;
2387}
2388
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002389static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002390{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002391 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002392
Arend van Sprielc1179032012-10-22 13:55:33 -07002393 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002394 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002395 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002396 brcmf_notify_escan_complete(cfg, escan->ndev, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002397 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002398 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2399 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002400}
2401
Hante Meulemane756af52012-09-11 21:18:52 +02002402static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2403{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002404 struct brcmf_cfg80211_info *cfg =
2405 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002406 escan_timeout_work);
2407
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002408 brcmf_notify_escan_complete(cfg,
2409 cfg->escan_info.ndev, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002410}
2411
2412static void brcmf_escan_timeout(unsigned long data)
2413{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002414 struct brcmf_cfg80211_info *cfg =
2415 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002416
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002417 if (cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02002418 WL_ERR("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002419 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002420 }
2421}
2422
2423static s32
2424brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
2425 struct brcmf_bss_info_le *bss_info_le)
2426{
2427 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
2428 (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
2429 CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
2430 bss_info_le->SSID_len == bss->SSID_len &&
2431 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
2432 if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
2433 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002434 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2435 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2436
Hante Meulemane756af52012-09-11 21:18:52 +02002437 /* preserve max RSSI if the measurements are
2438 * both on-channel or both off-channel
2439 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002440 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002441 bss->RSSI = bss_info_le->RSSI;
2442 } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
2443 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
2444 /* preserve the on-channel rssi measurement
2445 * if the new measurement is off channel
2446 */
2447 bss->RSSI = bss_info_le->RSSI;
2448 bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
2449 }
2450 return 1;
2451 }
2452 return 0;
2453}
2454
2455static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002456brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002457 const struct brcmf_event_msg *e, void *data)
2458{
Arend van Spriel19937322012-11-05 16:22:32 -08002459 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
2460 struct net_device *ndev = ifp->ndev;
Hante Meulemane756af52012-09-11 21:18:52 +02002461 s32 status;
2462 s32 err = 0;
2463 struct brcmf_escan_result_le *escan_result_le;
2464 struct brcmf_bss_info_le *bss_info_le;
2465 struct brcmf_bss_info_le *bss = NULL;
2466 u32 bi_length;
2467 struct brcmf_scan_results *list;
2468 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002469 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002470
2471 status = be32_to_cpu(e->status);
2472
Hante Meulemanf07998952012-11-05 16:22:13 -08002473 if (!ndev || !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2474 WL_ERR("scan not ready ndev %p drv_status %x\n", ndev,
2475 !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status));
Hante Meulemane756af52012-09-11 21:18:52 +02002476 return -EPERM;
2477 }
2478
2479 if (status == BRCMF_E_STATUS_PARTIAL) {
2480 WL_SCAN("ESCAN Partial result\n");
2481 escan_result_le = (struct brcmf_escan_result_le *) data;
2482 if (!escan_result_le) {
2483 WL_ERR("Invalid escan result (NULL pointer)\n");
2484 goto exit;
2485 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002486 if (!cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02002487 WL_SCAN("result without cfg80211 request\n");
2488 goto exit;
2489 }
2490
2491 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
2492 WL_ERR("Invalid bss_count %d: ignoring\n",
2493 escan_result_le->bss_count);
2494 goto exit;
2495 }
2496 bss_info_le = &escan_result_le->bss_info_le;
2497
2498 bi_length = le32_to_cpu(bss_info_le->length);
2499 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2500 WL_ESCAN_RESULTS_FIXED_SIZE)) {
2501 WL_ERR("Invalid bss_info length %d: ignoring\n",
2502 bi_length);
2503 goto exit;
2504 }
2505
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002506 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002507 BIT(NL80211_IFTYPE_ADHOC))) {
2508 if (le16_to_cpu(bss_info_le->capability) &
2509 WLAN_CAPABILITY_IBSS) {
2510 WL_ERR("Ignoring IBSS result\n");
2511 goto exit;
2512 }
2513 }
2514
2515 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002516 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002517 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
2518 WL_ERR("Buffer is too small: ignoring\n");
2519 goto exit;
2520 }
2521
2522 for (i = 0; i < list->count; i++) {
2523 bss = bss ? (struct brcmf_bss_info_le *)
2524 ((unsigned char *)bss +
2525 le32_to_cpu(bss->length)) : list->bss_info_le;
2526 if (brcmf_compare_update_same_bss(bss, bss_info_le))
2527 goto exit;
2528 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002529 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002530 bss_info_le, bi_length);
2531 list->version = le32_to_cpu(bss_info_le->version);
2532 list->buflen += bi_length;
2533 list->count++;
2534 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002535 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2536 if (cfg->scan_request) {
2537 cfg->bss_list = (struct brcmf_scan_results *)
2538 cfg->escan_info.escan_buf;
2539 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002540 aborted = status != BRCMF_E_STATUS_SUCCESS;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002541 brcmf_notify_escan_complete(cfg, ndev, aborted,
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002542 false);
Hante Meulemane756af52012-09-11 21:18:52 +02002543 } else
2544 WL_ERR("Unexpected scan result 0x%x\n", status);
2545 }
2546exit:
2547 return err;
2548}
2549
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002550static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002551{
2552
Hante Meulemanf07998952012-11-05 16:22:13 -08002553 cfg->el.handler[BRCMF_E_ESCAN_RESULT] =
2554 brcmf_cfg80211_escan_handler;
2555 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2556 /* Init scan_timeout timer */
2557 init_timer(&cfg->escan_timeout);
2558 cfg->escan_timeout.data = (unsigned long) cfg;
2559 cfg->escan_timeout.function = brcmf_escan_timeout;
2560 INIT_WORK(&cfg->escan_timeout_work,
2561 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002562}
2563
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002564static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002565{
2566 if (ms < 1000 / HZ) {
2567 cond_resched();
2568 mdelay(ms);
2569 } else {
2570 msleep(ms);
2571 }
2572}
2573
2574static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
2575{
Arend van Spriel5b435de2011-10-05 13:19:03 +02002576 WL_TRACE("Enter\n");
2577
Arend van Spriel5b435de2011-10-05 13:19:03 +02002578 return 0;
2579}
2580
2581static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
2582 struct cfg80211_wowlan *wow)
2583{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002584 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2585 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel7d641072012-10-22 13:55:39 -07002586 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002587
2588 WL_TRACE("Enter\n");
2589
2590 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002591 * if the primary net_device is not READY there is nothing
2592 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02002593 */
Arend van Spriel7d641072012-10-22 13:55:39 -07002594 vif = ((struct brcmf_if *)netdev_priv(ndev))->vif;
2595 if (!check_vif_up(vif))
2596 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002597
Arend van Spriel7d641072012-10-22 13:55:39 -07002598 list_for_each_entry(vif, &cfg->vif_list, list) {
2599 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
2600 continue;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002601 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002602 * While going to suspend if associated with AP disassociate
2603 * from AP to save power while system is in suspended state
Arend van Spriel5b435de2011-10-05 13:19:03 +02002604 */
Arend van Spriel7d641072012-10-22 13:55:39 -07002605 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state) ||
2606 test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) {
2607 WL_INFO("Disassociating from AP before suspend\n");
2608 brcmf_link_down(cfg);
2609
2610 /* Make sure WPA_Supplicant receives all the event
2611 * generated due to DISASSOC call to the fw to keep
2612 * the state fw and WPA_Supplicant state consistent
2613 */
2614 brcmf_delay(500);
2615 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002616 }
2617
Arend van Spriel7d641072012-10-22 13:55:39 -07002618 /* end any scanning */
2619 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002620 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002621
2622 /* Turn off watchdog timer */
Arend van Spriel7d641072012-10-22 13:55:39 -07002623 brcmf_set_mpc(ndev, 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002624
Arend van Spriel7d641072012-10-22 13:55:39 -07002625exit:
Arend van Spriel5b435de2011-10-05 13:19:03 +02002626 WL_TRACE("Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07002627 /* clear any scanning activity */
2628 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002629 return 0;
2630}
2631
2632static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002633brcmf_update_pmklist(struct net_device *ndev,
2634 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
2635{
2636 int i, j;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002637 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002638
Arend van Spriel40c8e952011-10-12 20:51:20 +02002639 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
2640
2641 WL_CONN("No of elements %d\n", pmkid_len);
2642 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002643 WL_CONN("PMKID[%d]: %pM =\n", i,
2644 &pmk_list->pmkids.pmkid[i].BSSID);
2645 for (j = 0; j < WLAN_PMKID_LEN; j++)
2646 WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]);
2647 }
2648
2649 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07002650 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
2651 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002652
2653 return err;
2654}
2655
2656static s32
2657brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2658 struct cfg80211_pmksa *pmksa)
2659{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002660 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002661 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002662 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002663 s32 err = 0;
2664 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002665 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002666
2667 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002668 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002669 return -EIO;
2670
Arend van Spriel40c8e952011-10-12 20:51:20 +02002671 pmkid_len = le32_to_cpu(pmkids->npmkid);
2672 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002673 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
2674 break;
2675 if (i < WL_NUM_PMKIDS_MAX) {
2676 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
2677 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002678 if (i == pmkid_len) {
2679 pmkid_len++;
2680 pmkids->npmkid = cpu_to_le32(pmkid_len);
2681 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002682 } else
2683 err = -EINVAL;
2684
2685 WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
Arend van Spriel40c8e952011-10-12 20:51:20 +02002686 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002687 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel40c8e952011-10-12 20:51:20 +02002688 WL_CONN("%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002689
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002690 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002691
2692 WL_TRACE("Exit\n");
2693 return err;
2694}
2695
2696static s32
2697brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2698 struct cfg80211_pmksa *pmksa)
2699{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002700 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002701 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002702 struct pmkid_list pmkid;
2703 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002704 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002705
2706 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002707 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002708 return -EIO;
2709
2710 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
2711 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
2712
2713 WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
2714 &pmkid.pmkid[0].BSSID);
2715 for (i = 0; i < WLAN_PMKID_LEN; i++)
2716 WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]);
2717
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002718 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002719 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002720 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002721 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002722 ETH_ALEN))
2723 break;
2724
Arend van Spriel40c8e952011-10-12 20:51:20 +02002725 if ((pmkid_len > 0)
2726 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002727 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002728 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02002729 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002730 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
2731 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002732 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002733 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
2734 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002735 WLAN_PMKID_LEN);
2736 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002737 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002738 } else
2739 err = -EINVAL;
2740
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002741 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002742
2743 WL_TRACE("Exit\n");
2744 return err;
2745
2746}
2747
2748static s32
2749brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
2750{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002751 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002752 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002753 s32 err = 0;
2754
2755 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002756 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002757 return -EIO;
2758
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002759 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
2760 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002761
2762 WL_TRACE("Exit\n");
2763 return err;
2764
2765}
2766
Arend van Spriele5806072012-09-19 22:21:08 +02002767/*
2768 * PFN result doesn't have all the info which are
2769 * required by the supplicant
2770 * (For e.g IEs) Do a target Escan so that sched scan results are reported
2771 * via wl_inform_single_bss in the required format. Escan does require the
2772 * scan request in the form of cfg80211_scan_request. For timebeing, create
2773 * cfg80211_scan_request one out of the received PNO event.
2774 */
2775static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002776brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02002777 const struct brcmf_event_msg *e, void *data)
2778{
Arend van Spriel19937322012-11-05 16:22:32 -08002779 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
2780 struct net_device *ndev = ifp->ndev;
Arend van Spriele5806072012-09-19 22:21:08 +02002781 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
2782 struct cfg80211_scan_request *request = NULL;
2783 struct cfg80211_ssid *ssid = NULL;
2784 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002785 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002786 int err = 0;
2787 int channel_req = 0;
2788 int band = 0;
2789 struct brcmf_pno_scanresults_le *pfn_result;
2790 u32 result_count;
2791 u32 status;
2792
2793 WL_SCAN("Enter\n");
2794
2795 if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) {
2796 WL_SCAN("PFN NET LOST event. Do Nothing\n");
2797 return 0;
2798 }
2799
2800 pfn_result = (struct brcmf_pno_scanresults_le *)data;
2801 result_count = le32_to_cpu(pfn_result->count);
2802 status = le32_to_cpu(pfn_result->status);
2803
2804 /*
2805 * PFN event is limited to fit 512 bytes so we may get
2806 * multiple NET_FOUND events. For now place a warning here.
2807 */
2808 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
2809 WL_SCAN("PFN NET FOUND event. count: %d\n", result_count);
2810 if (result_count > 0) {
2811 int i;
2812
2813 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03002814 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
2815 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02002816 if (!request || !ssid || !channel) {
2817 err = -ENOMEM;
2818 goto out_err;
2819 }
2820
2821 request->wiphy = wiphy;
2822 data += sizeof(struct brcmf_pno_scanresults_le);
2823 netinfo_start = (struct brcmf_pno_net_info_le *)data;
2824
2825 for (i = 0; i < result_count; i++) {
2826 netinfo = &netinfo_start[i];
2827 if (!netinfo) {
2828 WL_ERR("Invalid netinfo ptr. index: %d\n", i);
2829 err = -EINVAL;
2830 goto out_err;
2831 }
2832
2833 WL_SCAN("SSID:%s Channel:%d\n",
2834 netinfo->SSID, netinfo->channel);
2835 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
2836 ssid[i].ssid_len = netinfo->SSID_len;
2837 request->n_ssids++;
2838
2839 channel_req = netinfo->channel;
2840 if (channel_req <= CH_MAX_2G_CHANNEL)
2841 band = NL80211_BAND_2GHZ;
2842 else
2843 band = NL80211_BAND_5GHZ;
2844 channel[i].center_freq =
2845 ieee80211_channel_to_frequency(channel_req,
2846 band);
2847 channel[i].band = band;
2848 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
2849 request->channels[i] = &channel[i];
2850 request->n_channels++;
2851 }
2852
2853 /* assign parsed ssid array */
2854 if (request->n_ssids)
2855 request->ssids = &ssid[0];
2856
Arend van Sprielc1179032012-10-22 13:55:33 -07002857 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02002858 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002859 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002860 }
2861
Arend van Sprielc1179032012-10-22 13:55:33 -07002862 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002863 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriele5806072012-09-19 22:21:08 +02002864 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07002865 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02002866 goto out_err;
2867 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002868 cfg->sched_escan = true;
2869 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02002870 } else {
2871 WL_ERR("FALSE PNO Event. (pfn_count == 0)\n");
2872 goto out_err;
2873 }
2874
2875 kfree(ssid);
2876 kfree(channel);
2877 kfree(request);
2878 return 0;
2879
2880out_err:
2881 kfree(ssid);
2882 kfree(channel);
2883 kfree(request);
2884 cfg80211_sched_scan_stopped(wiphy);
2885 return err;
2886}
2887
Arend van Spriele5806072012-09-19 22:21:08 +02002888static int brcmf_dev_pno_clean(struct net_device *ndev)
2889{
Arend van Spriele5806072012-09-19 22:21:08 +02002890 int ret;
2891
2892 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002893 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002894 if (ret == 0) {
2895 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002896 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
2897 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002898 }
2899 if (ret < 0)
2900 WL_ERR("failed code %d\n", ret);
2901
2902 return ret;
2903}
2904
2905static int brcmf_dev_pno_config(struct net_device *ndev)
2906{
2907 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02002908
2909 memset(&pfn_param, 0, sizeof(pfn_param));
2910 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
2911
2912 /* set extra pno params */
2913 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
2914 pfn_param.repeat = BRCMF_PNO_REPEAT;
2915 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
2916
2917 /* set up pno scan fr */
2918 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
2919
Arend van Sprielac24be62012-10-22 10:36:23 -07002920 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
2921 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02002922}
2923
2924static int
2925brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
2926 struct net_device *ndev,
2927 struct cfg80211_sched_scan_request *request)
2928{
Arend van Sprielc1179032012-10-22 13:55:33 -07002929 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002930 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02002931 struct brcmf_pno_net_param_le pfn;
2932 int i;
2933 int ret = 0;
2934
2935 WL_SCAN("Enter n_match_sets:%d n_ssids:%d\n",
2936 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07002937 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2938 WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02002939 return -EAGAIN;
2940 }
2941
2942 if (!request || !request->n_ssids || !request->n_match_sets) {
2943 WL_ERR("Invalid sched scan req!! n_ssids:%d\n",
Yuanhan Liu9495b312012-10-18 18:10:56 +08002944 request ? request->n_ssids : 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002945 return -EINVAL;
2946 }
2947
2948 if (request->n_ssids > 0) {
2949 for (i = 0; i < request->n_ssids; i++) {
2950 /* Active scan req for ssids */
2951 WL_SCAN(">>> Active scan req for ssid (%s)\n",
2952 request->ssids[i].ssid);
2953
2954 /*
2955 * match_set ssids is a supert set of n_ssid list,
2956 * so we need not add these set seperately.
2957 */
2958 }
2959 }
2960
2961 if (request->n_match_sets > 0) {
2962 /* clean up everything */
2963 ret = brcmf_dev_pno_clean(ndev);
2964 if (ret < 0) {
2965 WL_ERR("failed error=%d\n", ret);
2966 return ret;
2967 }
2968
2969 /* configure pno */
2970 ret = brcmf_dev_pno_config(ndev);
2971 if (ret < 0) {
2972 WL_ERR("PNO setup failed!! ret=%d\n", ret);
2973 return -EINVAL;
2974 }
2975
2976 /* configure each match set */
2977 for (i = 0; i < request->n_match_sets; i++) {
2978 struct cfg80211_ssid *ssid;
2979 u32 ssid_len;
2980
2981 ssid = &request->match_sets[i].ssid;
2982 ssid_len = ssid->ssid_len;
2983
2984 if (!ssid_len) {
2985 WL_ERR("skip broadcast ssid\n");
2986 continue;
2987 }
2988 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
2989 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
2990 pfn.wsec = cpu_to_le32(0);
2991 pfn.infra = cpu_to_le32(1);
2992 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
2993 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
2994 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07002995 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07002996 sizeof(pfn));
Arend van Spriele5806072012-09-19 22:21:08 +02002997 WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
2998 ret == 0 ? "set" : "failed",
2999 ssid->ssid);
3000 }
3001 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003002 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriele5806072012-09-19 22:21:08 +02003003 WL_ERR("PNO enable failed!! ret=%d\n", ret);
3004 return -EINVAL;
3005 }
3006 } else {
3007 return -EINVAL;
3008 }
3009
3010 return 0;
3011}
3012
3013static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3014 struct net_device *ndev)
3015{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003016 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003017
3018 WL_SCAN("enter\n");
3019 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003020 if (cfg->sched_escan)
3021 brcmf_notify_escan_complete(cfg, ndev, true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003022 return 0;
3023}
Arend van Spriele5806072012-09-19 22:21:08 +02003024
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003025#ifdef CONFIG_NL80211_TESTMODE
3026static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3027{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003028 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003029 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003030 struct brcmf_dcmd *dcmd = data;
3031 struct sk_buff *reply;
3032 int ret;
3033
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003034 WL_TRACE("cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
3035 dcmd->buf, dcmd->len);
3036
3037 if (dcmd->set)
Arend van Sprielac24be62012-10-22 10:36:23 -07003038 ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
3039 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003040 else
Arend van Sprielac24be62012-10-22 10:36:23 -07003041 ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
3042 dcmd->buf, dcmd->len);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003043 if (ret == 0) {
3044 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3045 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3046 ret = cfg80211_testmode_reply(reply);
3047 }
3048 return ret;
3049}
3050#endif
3051
Hante Meuleman1a873342012-09-27 14:17:54 +02003052static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
3053{
Arend van Sprielac24be62012-10-22 10:36:23 -07003054 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003055 s32 err;
3056
3057 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003058 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003059 if (err < 0) {
3060 WL_ERR("auth error %d\n", err);
3061 return err;
3062 }
3063 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003064 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003065 if (err < 0) {
3066 WL_ERR("wsec error %d\n", err);
3067 return err;
3068 }
3069 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003070 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003071 if (err < 0) {
3072 WL_ERR("wpa_auth error %d\n", err);
3073 return err;
3074 }
3075
3076 return 0;
3077}
3078
3079static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3080{
3081 if (is_rsn_ie)
3082 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3083
3084 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3085}
3086
3087static s32
3088brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
Arend van Spriel34778522012-11-05 16:22:19 -08003089 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003090{
Arend van Sprielac24be62012-10-22 10:36:23 -07003091 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003092 u32 auth = 0; /* d11 open authentication */
3093 u16 count;
3094 s32 err = 0;
3095 s32 len = 0;
3096 u32 i;
3097 u32 wsec;
3098 u32 pval = 0;
3099 u32 gval = 0;
3100 u32 wpa_auth = 0;
3101 u32 offset;
3102 u8 *data;
3103 u16 rsn_cap;
3104 u32 wme_bss_disable;
3105
3106 WL_TRACE("Enter\n");
3107 if (wpa_ie == NULL)
3108 goto exit;
3109
3110 len = wpa_ie->len + TLV_HDR_LEN;
3111 data = (u8 *)wpa_ie;
3112 offset = 0;
3113 if (!is_rsn_ie)
3114 offset += VS_IE_FIXED_HDR_LEN;
3115 offset += WPA_IE_VERSION_LEN;
3116
3117 /* check for multicast cipher suite */
3118 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3119 err = -EINVAL;
3120 WL_ERR("no multicast cipher suite\n");
3121 goto exit;
3122 }
3123
3124 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3125 err = -EINVAL;
3126 WL_ERR("ivalid OUI\n");
3127 goto exit;
3128 }
3129 offset += TLV_OUI_LEN;
3130
3131 /* pick up multicast cipher */
3132 switch (data[offset]) {
3133 case WPA_CIPHER_NONE:
3134 gval = 0;
3135 break;
3136 case WPA_CIPHER_WEP_40:
3137 case WPA_CIPHER_WEP_104:
3138 gval = WEP_ENABLED;
3139 break;
3140 case WPA_CIPHER_TKIP:
3141 gval = TKIP_ENABLED;
3142 break;
3143 case WPA_CIPHER_AES_CCM:
3144 gval = AES_ENABLED;
3145 break;
3146 default:
3147 err = -EINVAL;
3148 WL_ERR("Invalid multi cast cipher info\n");
3149 goto exit;
3150 }
3151
3152 offset++;
3153 /* walk thru unicast cipher list and pick up what we recognize */
3154 count = data[offset] + (data[offset + 1] << 8);
3155 offset += WPA_IE_SUITE_COUNT_LEN;
3156 /* Check for unicast suite(s) */
3157 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3158 err = -EINVAL;
3159 WL_ERR("no unicast cipher suite\n");
3160 goto exit;
3161 }
3162 for (i = 0; i < count; i++) {
3163 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3164 err = -EINVAL;
3165 WL_ERR("ivalid OUI\n");
3166 goto exit;
3167 }
3168 offset += TLV_OUI_LEN;
3169 switch (data[offset]) {
3170 case WPA_CIPHER_NONE:
3171 break;
3172 case WPA_CIPHER_WEP_40:
3173 case WPA_CIPHER_WEP_104:
3174 pval |= WEP_ENABLED;
3175 break;
3176 case WPA_CIPHER_TKIP:
3177 pval |= TKIP_ENABLED;
3178 break;
3179 case WPA_CIPHER_AES_CCM:
3180 pval |= AES_ENABLED;
3181 break;
3182 default:
3183 WL_ERR("Ivalid unicast security info\n");
3184 }
3185 offset++;
3186 }
3187 /* walk thru auth management suite list and pick up what we recognize */
3188 count = data[offset] + (data[offset + 1] << 8);
3189 offset += WPA_IE_SUITE_COUNT_LEN;
3190 /* Check for auth key management suite(s) */
3191 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3192 err = -EINVAL;
3193 WL_ERR("no auth key mgmt suite\n");
3194 goto exit;
3195 }
3196 for (i = 0; i < count; i++) {
3197 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3198 err = -EINVAL;
3199 WL_ERR("ivalid OUI\n");
3200 goto exit;
3201 }
3202 offset += TLV_OUI_LEN;
3203 switch (data[offset]) {
3204 case RSN_AKM_NONE:
3205 WL_TRACE("RSN_AKM_NONE\n");
3206 wpa_auth |= WPA_AUTH_NONE;
3207 break;
3208 case RSN_AKM_UNSPECIFIED:
3209 WL_TRACE("RSN_AKM_UNSPECIFIED\n");
3210 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3211 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3212 break;
3213 case RSN_AKM_PSK:
3214 WL_TRACE("RSN_AKM_PSK\n");
3215 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3216 (wpa_auth |= WPA_AUTH_PSK);
3217 break;
3218 default:
3219 WL_ERR("Ivalid key mgmt info\n");
3220 }
3221 offset++;
3222 }
3223
3224 if (is_rsn_ie) {
3225 wme_bss_disable = 1;
3226 if ((offset + RSN_CAP_LEN) <= len) {
3227 rsn_cap = data[offset] + (data[offset + 1] << 8);
3228 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3229 wme_bss_disable = 0;
3230 }
3231 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003232 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003233 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003234 if (err < 0) {
3235 WL_ERR("wme_bss_disable error %d\n", err);
3236 goto exit;
3237 }
3238 }
3239 /* FOR WPS , set SES_OW_ENABLED */
3240 wsec = (pval | gval | SES_OW_ENABLED);
3241
3242 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003243 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003244 if (err < 0) {
3245 WL_ERR("auth error %d\n", err);
3246 goto exit;
3247 }
3248 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003249 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003250 if (err < 0) {
3251 WL_ERR("wsec error %d\n", err);
3252 goto exit;
3253 }
3254 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003255 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003256 if (err < 0) {
3257 WL_ERR("wpa_auth error %d\n", err);
3258 goto exit;
3259 }
3260
3261exit:
3262 return err;
3263}
3264
3265static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003266brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003267 struct parsed_vndr_ies *vndr_ies)
3268{
3269 s32 err = 0;
3270 struct brcmf_vs_tlv *vndrie;
3271 struct brcmf_tlv *ie;
3272 struct parsed_vndr_ie_info *parsed_info;
3273 s32 remaining_len;
3274
3275 remaining_len = (s32)vndr_ie_len;
3276 memset(vndr_ies, 0, sizeof(*vndr_ies));
3277
3278 ie = (struct brcmf_tlv *)vndr_ie_buf;
3279 while (ie) {
3280 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3281 goto next;
3282 vndrie = (struct brcmf_vs_tlv *)ie;
3283 /* len should be bigger than OUI length + one */
3284 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
3285 WL_ERR("invalid vndr ie. length is too small %d\n",
3286 vndrie->len);
3287 goto next;
3288 }
3289 /* if wpa or wme ie, do not add ie */
3290 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3291 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3292 (vndrie->oui_type == WME_OUI_TYPE))) {
3293 WL_TRACE("Found WPA/WME oui. Do not add it\n");
3294 goto next;
3295 }
3296
3297 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3298
3299 /* save vndr ie information */
3300 parsed_info->ie_ptr = (char *)vndrie;
3301 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3302 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3303
3304 vndr_ies->count++;
3305
3306 WL_TRACE("** OUI %02x %02x %02x, type 0x%02x\n",
3307 parsed_info->vndrie.oui[0],
3308 parsed_info->vndrie.oui[1],
3309 parsed_info->vndrie.oui[2],
3310 parsed_info->vndrie.oui_type);
3311
3312 if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
3313 break;
3314next:
3315 remaining_len -= ie->len;
3316 if (remaining_len <= 2)
3317 ie = NULL;
3318 else
3319 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len);
3320 }
3321 return err;
3322}
3323
3324static u32
3325brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3326{
3327
3328 __le32 iecount_le;
3329 __le32 pktflag_le;
3330
3331 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3332 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3333
3334 iecount_le = cpu_to_le32(1);
3335 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3336
3337 pktflag_le = cpu_to_le32(pktflag);
3338 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3339
3340 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3341
3342 return ie_len + VNDR_IE_HDR_SIZE;
3343}
3344
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003345static
Arend van Spriel1332e262012-11-05 16:22:18 -08003346s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3347 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003348{
Arend van Spriel1332e262012-11-05 16:22:18 -08003349 struct brcmf_if *ifp;
3350 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003351 s32 err = 0;
3352 u8 *iovar_ie_buf;
3353 u8 *curr_ie_buf;
3354 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003355 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003356 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003357 u32 del_add_ie_buf_len = 0;
3358 u32 total_ie_buf_len = 0;
3359 u32 parsed_ie_buf_len = 0;
3360 struct parsed_vndr_ies old_vndr_ies;
3361 struct parsed_vndr_ies new_vndr_ies;
3362 struct parsed_vndr_ie_info *vndrie_info;
3363 s32 i;
3364 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003365 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003366
Arend van Spriel1332e262012-11-05 16:22:18 -08003367 if (!vif)
3368 return -ENODEV;
3369 ifp = vif->ifp;
3370 saved_ie = &vif->saved_ie;
3371
3372 WL_TRACE("bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003373 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3374 if (!iovar_ie_buf)
3375 return -ENOMEM;
3376 curr_ie_buf = iovar_ie_buf;
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003377 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003378 switch (pktflag) {
3379 case VNDR_IE_PRBRSP_FLAG:
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003380 mgmt_ie_buf = saved_ie->probe_res_ie;
3381 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3382 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003383 break;
3384 case VNDR_IE_BEACON_FLAG:
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003385 mgmt_ie_buf = saved_ie->beacon_ie;
3386 mgmt_ie_len = &saved_ie->beacon_ie_len;
3387 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003388 break;
3389 default:
3390 err = -EPERM;
3391 WL_ERR("not suitable type\n");
3392 goto exit;
3393 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003394 } else {
3395 err = -EPERM;
3396 WL_ERR("not suitable type\n");
3397 goto exit;
3398 }
3399
3400 if (vndr_ie_len > mgmt_ie_buf_len) {
3401 err = -ENOMEM;
3402 WL_ERR("extra IE size too big\n");
3403 goto exit;
3404 }
3405
3406 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3407 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3408 ptr = curr_ie_buf;
3409 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3410 for (i = 0; i < new_vndr_ies.count; i++) {
3411 vndrie_info = &new_vndr_ies.ie_info[i];
3412 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3413 vndrie_info->ie_len);
3414 parsed_ie_buf_len += vndrie_info->ie_len;
3415 }
3416 }
3417
3418 if (mgmt_ie_buf != NULL) {
3419 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3420 (memcmp(mgmt_ie_buf, curr_ie_buf,
3421 parsed_ie_buf_len) == 0)) {
3422 WL_TRACE("Previous mgmt IE is equals to current IE");
3423 goto exit;
3424 }
3425
3426 /* parse old vndr_ie */
3427 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3428
3429 /* make a command to delete old ie */
3430 for (i = 0; i < old_vndr_ies.count; i++) {
3431 vndrie_info = &old_vndr_ies.ie_info[i];
3432
3433 WL_TRACE("DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3434 vndrie_info->vndrie.id,
3435 vndrie_info->vndrie.len,
3436 vndrie_info->vndrie.oui[0],
3437 vndrie_info->vndrie.oui[1],
3438 vndrie_info->vndrie.oui[2]);
3439
3440 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3441 vndrie_info->ie_ptr,
3442 vndrie_info->ie_len,
3443 "del");
3444 curr_ie_buf += del_add_ie_buf_len;
3445 total_ie_buf_len += del_add_ie_buf_len;
3446 }
3447 }
3448
3449 *mgmt_ie_len = 0;
3450 /* Add if there is any extra IE */
3451 if (mgmt_ie_buf && parsed_ie_buf_len) {
3452 ptr = mgmt_ie_buf;
3453
3454 remained_buf_len = mgmt_ie_buf_len;
3455
3456 /* make a command to add new ie */
3457 for (i = 0; i < new_vndr_ies.count; i++) {
3458 vndrie_info = &new_vndr_ies.ie_info[i];
3459
3460 WL_TRACE("ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3461 vndrie_info->vndrie.id,
3462 vndrie_info->vndrie.len,
3463 vndrie_info->vndrie.oui[0],
3464 vndrie_info->vndrie.oui[1],
3465 vndrie_info->vndrie.oui[2]);
3466
3467 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3468 vndrie_info->ie_ptr,
3469 vndrie_info->ie_len,
3470 "add");
3471 /* verify remained buf size before copy data */
3472 remained_buf_len -= vndrie_info->ie_len;
3473 if (remained_buf_len < 0) {
3474 WL_ERR("no space in mgmt_ie_buf: len left %d",
3475 remained_buf_len);
3476 break;
3477 }
3478
3479 /* save the parsed IE in wl struct */
3480 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3481 vndrie_info->ie_len);
3482 *mgmt_ie_len += vndrie_info->ie_len;
3483
3484 curr_ie_buf += del_add_ie_buf_len;
3485 total_ie_buf_len += del_add_ie_buf_len;
3486 }
3487 }
3488 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003489 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003490 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003491 if (err)
3492 WL_ERR("vndr ie set error : %d\n", err);
3493 }
3494
3495exit:
3496 kfree(iovar_ie_buf);
3497 return err;
3498}
3499
3500static s32
3501brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3502 struct cfg80211_ap_settings *settings)
3503{
3504 s32 ie_offset;
Arend van Sprielac24be62012-10-22 10:36:23 -07003505 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003506 struct brcmf_tlv *ssid_ie;
3507 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003508 s32 err = -EPERM;
3509 struct brcmf_tlv *rsn_ie;
3510 struct brcmf_vs_tlv *wpa_ie;
3511 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02003512 s32 bssidx = 0;
3513
3514 WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
3515 settings->channel_type, settings->beacon_interval,
3516 settings->dtim_period);
Arend van Spriel3f40b832012-11-05 16:22:27 -08003517 WL_TRACE("ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
Hante Meuleman1a873342012-09-27 14:17:54 +02003518 settings->ssid, settings->ssid_len, settings->auth_type,
3519 settings->inactivity_timeout);
3520
Arend van Sprielc1179032012-10-22 13:55:33 -07003521 if (!test_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003522 WL_ERR("Not in AP creation mode\n");
3523 return -EPERM;
3524 }
3525
3526 memset(&ssid_le, 0, sizeof(ssid_le));
3527 if (settings->ssid == NULL || settings->ssid_len == 0) {
3528 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
3529 ssid_ie = brcmf_parse_tlvs(
3530 (u8 *)&settings->beacon.head[ie_offset],
3531 settings->beacon.head_len - ie_offset,
3532 WLAN_EID_SSID);
3533 if (!ssid_ie)
3534 return -EINVAL;
3535
3536 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
3537 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
3538 WL_TRACE("SSID is (%s) in Head\n", ssid_le.SSID);
3539 } else {
3540 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
3541 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3542 }
3543
3544 brcmf_set_mpc(ndev, 0);
Arend van Sprielac24be62012-10-22 10:36:23 -07003545 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003546 if (err < 0) {
3547 WL_ERR("BRCMF_C_DOWN error %d\n", err);
3548 goto exit;
3549 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003550 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003551 if (err < 0) {
3552 WL_ERR("SET INFRA error %d\n", err);
3553 goto exit;
3554 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003555 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003556 if (err < 0) {
3557 WL_ERR("setting AP mode failed %d\n", err);
3558 goto exit;
3559 }
3560
3561 /* find the RSN_IE */
3562 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
3563 settings->beacon.tail_len, WLAN_EID_RSN);
3564
3565 /* find the WPA_IE */
3566 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
3567 settings->beacon.tail_len);
3568
Hante Meuleman1a873342012-09-27 14:17:54 +02003569 if ((wpa_ie != NULL || rsn_ie != NULL)) {
3570 WL_TRACE("WPA(2) IE is found\n");
3571 if (wpa_ie != NULL) {
3572 /* WPA IE */
Arend van Spriel34778522012-11-05 16:22:19 -08003573 err = brcmf_configure_wpaie(ndev, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02003574 if (err < 0)
3575 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003576 } else {
3577 /* RSN IE */
3578 err = brcmf_configure_wpaie(ndev,
Arend van Spriel34778522012-11-05 16:22:19 -08003579 (struct brcmf_vs_tlv *)rsn_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02003580 if (err < 0)
3581 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003582 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003583 } else {
3584 WL_TRACE("No WPA(2) IEs found\n");
3585 brcmf_configure_opensecurity(ndev, bssidx);
Hante Meuleman1a873342012-09-27 14:17:54 +02003586 }
3587 /* Set Beacon IEs to FW */
Arend van Spriel1332e262012-11-05 16:22:18 -08003588 err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
3589 VNDR_IE_BEACON_FLAG,
3590 settings->beacon.tail,
3591 settings->beacon.tail_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003592 if (err)
3593 WL_ERR("Set Beacon IE Failed\n");
3594 else
3595 WL_TRACE("Applied Vndr IEs for Beacon\n");
3596
3597 /* Set Probe Response IEs to FW */
Arend van Spriel1332e262012-11-05 16:22:18 -08003598 err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
3599 VNDR_IE_PRBRSP_FLAG,
3600 settings->beacon.proberesp_ies,
3601 settings->beacon.proberesp_ies_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003602 if (err)
3603 WL_ERR("Set Probe Resp IE Failed\n");
3604 else
3605 WL_TRACE("Applied Vndr IEs for Probe Resp\n");
3606
3607 if (settings->beacon_interval) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003608 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003609 settings->beacon_interval);
Hante Meuleman1a873342012-09-27 14:17:54 +02003610 if (err < 0) {
3611 WL_ERR("Beacon Interval Set Error, %d\n", err);
3612 goto exit;
3613 }
3614 }
3615 if (settings->dtim_period) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003616 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003617 settings->dtim_period);
Hante Meuleman1a873342012-09-27 14:17:54 +02003618 if (err < 0) {
3619 WL_ERR("DTIM Interval Set Error, %d\n", err);
3620 goto exit;
3621 }
3622 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003623 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003624 if (err < 0) {
3625 WL_ERR("BRCMF_C_UP error (%d)\n", err);
3626 goto exit;
3627 }
3628
3629 memset(&join_params, 0, sizeof(join_params));
3630 /* join parameters starts with ssid */
3631 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
3632 /* create softap */
Arend van Sprielac24be62012-10-22 10:36:23 -07003633 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
3634 &join_params, sizeof(join_params));
Hante Meuleman1a873342012-09-27 14:17:54 +02003635 if (err < 0) {
3636 WL_ERR("SET SSID error (%d)\n", err);
3637 goto exit;
3638 }
Arend van Sprielc1179032012-10-22 13:55:33 -07003639 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3640 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003641
3642exit:
3643 if (err)
3644 brcmf_set_mpc(ndev, 1);
3645 return err;
3646}
3647
3648static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
3649{
Arend van Sprielc1179032012-10-22 13:55:33 -07003650 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003651 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02003652 s32 err = -EPERM;
3653
3654 WL_TRACE("Enter\n");
3655
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003656 if (cfg->conf->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003657 /* Due to most likely deauths outstanding we sleep */
3658 /* first to make sure they get processed by fw. */
3659 msleep(400);
Arend van Sprielac24be62012-10-22 10:36:23 -07003660 err = brcmf_fil_cmd_int_set(netdev_priv(ndev),
3661 BRCMF_C_SET_AP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003662 if (err < 0) {
3663 WL_ERR("setting AP mode failed %d\n", err);
3664 goto exit;
3665 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003666 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_UP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003667 if (err < 0) {
3668 WL_ERR("BRCMF_C_UP error %d\n", err);
3669 goto exit;
3670 }
3671 brcmf_set_mpc(ndev, 1);
Arend van Sprielc1179032012-10-22 13:55:33 -07003672 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3673 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003674 }
3675exit:
3676 return err;
3677}
3678
3679static int
3680brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
3681 u8 *mac)
3682{
3683 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003684 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003685 s32 err;
3686
3687 if (!mac)
3688 return -EFAULT;
3689
3690 WL_TRACE("Enter %pM\n", mac);
3691
Arend van Sprielce81e312012-10-22 13:55:37 -07003692 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02003693 return -EIO;
3694
3695 memcpy(&scbval.ea, mac, ETH_ALEN);
3696 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003697 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003698 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02003699 if (err)
3700 WL_ERR("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
3701
3702 WL_TRACE("Exit\n");
3703 return err;
3704}
3705
Arend van Spriel5b435de2011-10-05 13:19:03 +02003706static struct cfg80211_ops wl_cfg80211_ops = {
3707 .change_virtual_intf = brcmf_cfg80211_change_iface,
3708 .scan = brcmf_cfg80211_scan,
3709 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
3710 .join_ibss = brcmf_cfg80211_join_ibss,
3711 .leave_ibss = brcmf_cfg80211_leave_ibss,
3712 .get_station = brcmf_cfg80211_get_station,
3713 .set_tx_power = brcmf_cfg80211_set_tx_power,
3714 .get_tx_power = brcmf_cfg80211_get_tx_power,
3715 .add_key = brcmf_cfg80211_add_key,
3716 .del_key = brcmf_cfg80211_del_key,
3717 .get_key = brcmf_cfg80211_get_key,
3718 .set_default_key = brcmf_cfg80211_config_default_key,
3719 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
3720 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
3721 .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
3722 .connect = brcmf_cfg80211_connect,
3723 .disconnect = brcmf_cfg80211_disconnect,
3724 .suspend = brcmf_cfg80211_suspend,
3725 .resume = brcmf_cfg80211_resume,
3726 .set_pmksa = brcmf_cfg80211_set_pmksa,
3727 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003728 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02003729 .start_ap = brcmf_cfg80211_start_ap,
3730 .stop_ap = brcmf_cfg80211_stop_ap,
3731 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02003732 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
3733 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003734#ifdef CONFIG_NL80211_TESTMODE
3735 .testmode_cmd = brcmf_cfg80211_testmode
3736#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02003737};
3738
3739static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
3740{
3741 s32 err = 0;
3742
3743 switch (mode) {
3744 case WL_MODE_BSS:
3745 return NL80211_IFTYPE_STATION;
3746 case WL_MODE_IBSS:
3747 return NL80211_IFTYPE_ADHOC;
3748 default:
3749 return NL80211_IFTYPE_UNSPECIFIED;
3750 }
3751
3752 return err;
3753}
3754
Arend van Spriele5806072012-09-19 22:21:08 +02003755static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
3756{
Arend van Spriele5806072012-09-19 22:21:08 +02003757 /* scheduled scan settings */
3758 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
3759 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
3760 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
3761 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
Arend van Spriele5806072012-09-19 22:21:08 +02003762}
3763
Arend van Spriel3eacf862012-10-22 13:55:30 -07003764static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003765{
Arend van Spriel3eacf862012-10-22 13:55:30 -07003766 struct wiphy *wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003767 s32 err = 0;
3768
Arend van Spriel3eacf862012-10-22 13:55:30 -07003769 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
3770 if (!wiphy) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08003771 WL_ERR("Could not allocate wiphy device\n");
Arend van Spriel3eacf862012-10-22 13:55:30 -07003772 return ERR_PTR(-ENOMEM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003773 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003774 set_wiphy_dev(wiphy, phydev);
3775 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
3776 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
3777 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3778 BIT(NL80211_IFTYPE_ADHOC) |
3779 BIT(NL80211_IFTYPE_AP);
3780 wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
3781 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
Arend van Spriel5b435de2011-10-05 13:19:03 +02003782 * it as 11a by default.
3783 * This will be updated with
3784 * 11n phy tables in
3785 * "ifconfig up"
3786 * if phy has 11n capability
3787 */
Arend van Spriel3eacf862012-10-22 13:55:30 -07003788 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3789 wiphy->cipher_suites = __wl_cipher_suites;
3790 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
3791 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
Arend van Spriel5b435de2011-10-05 13:19:03 +02003792 * save mode
3793 * by default
3794 */
Arend van Spriel3eacf862012-10-22 13:55:30 -07003795 brcmf_wiphy_pno_params(wiphy);
3796 err = wiphy_register(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003797 if (err < 0) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08003798 WL_ERR("Could not register wiphy device (%d)\n", err);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003799 wiphy_free(wiphy);
3800 return ERR_PTR(err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003801 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003802 return wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003803}
3804
Arend van Spriel3eacf862012-10-22 13:55:30 -07003805static
3806struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
3807 struct net_device *netdev,
3808 s32 mode, bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003809{
Arend van Spriel3eacf862012-10-22 13:55:30 -07003810 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003811
Arend van Spriel3eacf862012-10-22 13:55:30 -07003812 if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT)
3813 return ERR_PTR(-ENOSPC);
3814
3815 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
3816 if (!vif)
3817 return ERR_PTR(-ENOMEM);
3818
3819 vif->wdev.wiphy = cfg->wiphy;
3820 vif->wdev.netdev = netdev;
3821 vif->wdev.iftype = brcmf_mode_to_nl80211_iftype(mode);
3822
3823 if (netdev) {
3824 vif->ifp = netdev_priv(netdev);
3825 netdev->ieee80211_ptr = &vif->wdev;
3826 SET_NETDEV_DEV(netdev, wiphy_dev(cfg->wiphy));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003827 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003828
3829 vif->mode = mode;
3830 vif->pm_block = pm_block;
3831 vif->roam_off = -1;
3832
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07003833 brcmf_init_prof(&vif->profile);
3834
Arend van Spriel3eacf862012-10-22 13:55:30 -07003835 list_add_tail(&vif->list, &cfg->vif_list);
3836 cfg->vif_cnt++;
3837 return vif;
3838}
3839
3840static void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
3841{
3842 struct brcmf_cfg80211_info *cfg;
3843 struct wiphy *wiphy;
3844
3845 wiphy = vif->wdev.wiphy;
3846 cfg = wiphy_priv(wiphy);
3847 list_del(&vif->list);
3848 cfg->vif_cnt--;
3849
3850 kfree(vif);
3851 if (!cfg->vif_cnt) {
3852 wiphy_unregister(wiphy);
3853 wiphy_free(wiphy);
3854 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003855}
3856
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003857static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003858 const struct brcmf_event_msg *e)
3859{
3860 u32 event = be32_to_cpu(e->event_type);
3861 u32 status = be32_to_cpu(e->status);
3862
3863 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
3864 WL_CONN("Processing set ssid\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003865 cfg->link_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003866 return true;
3867 }
3868
3869 return false;
3870}
3871
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003872static bool brcmf_is_linkdown(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003873 const struct brcmf_event_msg *e)
3874{
3875 u32 event = be32_to_cpu(e->event_type);
3876 u16 flags = be16_to_cpu(e->flags);
3877
3878 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
3879 WL_CONN("Processing link down\n");
3880 return true;
3881 }
3882 return false;
3883}
3884
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003885static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003886 const struct brcmf_event_msg *e)
3887{
3888 u32 event = be32_to_cpu(e->event_type);
3889 u32 status = be32_to_cpu(e->status);
3890
3891 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
3892 WL_CONN("Processing Link %s & no network found\n",
3893 be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ?
3894 "up" : "down");
3895 return true;
3896 }
3897
3898 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
3899 WL_CONN("Processing connecting & no network found\n");
3900 return true;
3901 }
3902
3903 return false;
3904}
3905
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003906static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003907{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003908 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003909
3910 kfree(conn_info->req_ie);
3911 conn_info->req_ie = NULL;
3912 conn_info->req_ie_len = 0;
3913 kfree(conn_info->resp_ie);
3914 conn_info->resp_ie = NULL;
3915 conn_info->resp_ie_len = 0;
3916}
3917
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003918static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003919{
Arend van Sprielac24be62012-10-22 10:36:23 -07003920 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003921 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003922 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003923 u32 req_len;
3924 u32 resp_len;
3925 s32 err = 0;
3926
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003927 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003928
Arend van Sprielac24be62012-10-22 10:36:23 -07003929 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
3930 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003931 if (err) {
3932 WL_ERR("could not get assoc info (%d)\n", err);
3933 return err;
3934 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003935 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003936 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003937 req_len = le32_to_cpu(assoc_info->req_len);
3938 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003939 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003940 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003941 cfg->extra_buf,
3942 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003943 if (err) {
3944 WL_ERR("could not get assoc req (%d)\n", err);
3945 return err;
3946 }
3947 conn_info->req_ie_len = req_len;
3948 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003949 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003950 GFP_KERNEL);
3951 } else {
3952 conn_info->req_ie_len = 0;
3953 conn_info->req_ie = NULL;
3954 }
3955 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003956 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003957 cfg->extra_buf,
3958 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003959 if (err) {
3960 WL_ERR("could not get assoc resp (%d)\n", err);
3961 return err;
3962 }
3963 conn_info->resp_ie_len = resp_len;
3964 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003965 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003966 GFP_KERNEL);
3967 } else {
3968 conn_info->resp_ie_len = 0;
3969 conn_info->resp_ie = NULL;
3970 }
3971 WL_CONN("req len (%d) resp len (%d)\n",
3972 conn_info->req_ie_len, conn_info->resp_ie_len);
3973
3974 return err;
3975}
3976
3977static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003978brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003979 struct net_device *ndev,
3980 const struct brcmf_event_msg *e)
3981{
Arend van Sprielc1179032012-10-22 13:55:33 -07003982 struct brcmf_if *ifp = netdev_priv(ndev);
3983 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003984 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
3985 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07003986 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003987 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07003988 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003989 u32 freq;
3990 s32 err = 0;
3991 u32 target_channel;
Franky Lina180b832012-10-10 11:13:09 -07003992 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003993
3994 WL_TRACE("Enter\n");
3995
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003996 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02003997 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003998 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003999
Franky Lina180b832012-10-10 11:13:09 -07004000 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4001 if (buf == NULL) {
4002 err = -ENOMEM;
4003 goto done;
4004 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004005
Franky Lina180b832012-10-10 11:13:09 -07004006 /* data sent to dongle has to be little endian */
4007 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004008 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004009 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004010
4011 if (err)
4012 goto done;
4013
4014 bi = (struct brcmf_bss_info_le *)(buf + 4);
4015 target_channel = bi->ctl_ch ? bi->ctl_ch :
4016 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004017
4018 if (target_channel <= CH_MAX_2G_CHANNEL)
4019 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4020 else
4021 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4022
4023 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4024 notify_channel = ieee80211_get_channel(wiphy, freq);
4025
Franky Lina180b832012-10-10 11:13:09 -07004026done:
4027 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004028 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004029 conn_info->req_ie, conn_info->req_ie_len,
4030 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
4031 WL_CONN("Report roaming result\n");
4032
Arend van Sprielc1179032012-10-22 13:55:33 -07004033 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004034 WL_TRACE("Exit\n");
4035 return err;
4036}
4037
4038static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004039brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004040 struct net_device *ndev, const struct brcmf_event_msg *e,
4041 bool completed)
4042{
Arend van Sprielc1179032012-10-22 13:55:33 -07004043 struct brcmf_if *ifp = netdev_priv(ndev);
4044 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004045 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004046 s32 err = 0;
4047
4048 WL_TRACE("Enter\n");
4049
Arend van Sprielc1179032012-10-22 13:55:33 -07004050 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4051 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004052 if (completed) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004053 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004054 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004055 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004056 }
4057 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004058 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004059 conn_info->req_ie,
4060 conn_info->req_ie_len,
4061 conn_info->resp_ie,
4062 conn_info->resp_ie_len,
4063 completed ? WLAN_STATUS_SUCCESS :
4064 WLAN_STATUS_AUTH_TIMEOUT,
4065 GFP_KERNEL);
4066 if (completed)
Arend van Sprielc1179032012-10-22 13:55:33 -07004067 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4068 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004069 WL_CONN("Report connect result - connection %s\n",
4070 completed ? "succeeded" : "failed");
4071 }
4072 WL_TRACE("Exit\n");
4073 return err;
4074}
4075
4076static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004077brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004078 struct net_device *ndev,
4079 const struct brcmf_event_msg *e, void *data)
4080{
4081 s32 err = 0;
4082 u32 event = be32_to_cpu(e->event_type);
4083 u32 reason = be32_to_cpu(e->reason);
4084 u32 len = be32_to_cpu(e->datalen);
4085 static int generation;
4086
4087 struct station_info sinfo;
4088
4089 WL_CONN("event %d, reason %d\n", event, reason);
4090 memset(&sinfo, 0, sizeof(sinfo));
4091
4092 sinfo.filled = 0;
4093 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
4094 reason == BRCMF_E_STATUS_SUCCESS) {
4095 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4096 if (!data) {
4097 WL_ERR("No IEs present in ASSOC/REASSOC_IND");
4098 return -EINVAL;
4099 }
4100 sinfo.assoc_req_ies = data;
4101 sinfo.assoc_req_ies_len = len;
4102 generation++;
4103 sinfo.generation = generation;
4104 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_ATOMIC);
4105 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4106 (event == BRCMF_E_DEAUTH_IND) ||
4107 (event == BRCMF_E_DEAUTH)) {
4108 generation++;
4109 sinfo.generation = generation;
4110 cfg80211_del_sta(ndev, e->addr, GFP_ATOMIC);
4111 }
4112 return err;
4113}
4114
4115static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004116brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004117 const struct brcmf_event_msg *e, void *data)
4118{
Arend van Spriel19937322012-11-05 16:22:32 -08004119 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4120 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004121 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004122 s32 err = 0;
4123
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004124 if (cfg->conf->mode == WL_MODE_AP) {
4125 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
4126 } else if (brcmf_is_linkup(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004127 WL_CONN("Linkup\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004128 if (brcmf_is_ibssmode(cfg)) {
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004129 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004130 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004131 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004132 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4133 &ifp->vif->sme_state);
4134 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4135 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004136 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004137 brcmf_bss_connect_done(cfg, ndev, e, true);
4138 } else if (brcmf_is_linkdown(cfg, e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004139 WL_CONN("Linkdown\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004140 if (brcmf_is_ibssmode(cfg)) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004141 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4142 &ifp->vif->sme_state);
4143 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
4144 &ifp->vif->sme_state))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004145 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004146 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004147 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Sprielc1179032012-10-22 13:55:33 -07004148 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
4149 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004150 cfg80211_disconnected(ndev, 0, NULL, 0,
Arend van Sprielc1179032012-10-22 13:55:33 -07004151 GFP_KERNEL);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004152 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004153 }
4154 }
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004155 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004156 } else if (brcmf_is_nonetwork(cfg, e)) {
4157 if (brcmf_is_ibssmode(cfg))
Arend van Sprielc1179032012-10-22 13:55:33 -07004158 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4159 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004160 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004161 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004162 }
4163
4164 return err;
4165}
4166
4167static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004168brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004169 const struct brcmf_event_msg *e, void *data)
4170{
Arend van Spriel19937322012-11-05 16:22:32 -08004171 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004172 s32 err = 0;
4173 u32 event = be32_to_cpu(e->event_type);
4174 u32 status = be32_to_cpu(e->status);
4175
4176 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004177 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08004178 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004179 else
Arend van Spriel19937322012-11-05 16:22:32 -08004180 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004181 }
4182
4183 return err;
4184}
4185
4186static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004187brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004188 const struct brcmf_event_msg *e, void *data)
4189{
4190 u16 flags = be16_to_cpu(e->flags);
4191 enum nl80211_key_type key_type;
4192
4193 if (flags & BRCMF_EVENT_MSG_GROUP)
4194 key_type = NL80211_KEYTYPE_GROUP;
4195 else
4196 key_type = NL80211_KEYTYPE_PAIRWISE;
4197
Arend van Spriel19937322012-11-05 16:22:32 -08004198 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004199 NULL, GFP_KERNEL);
4200
4201 return 0;
4202}
4203
Arend van Spriel5b435de2011-10-05 13:19:03 +02004204static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4205{
4206 conf->mode = (u32)-1;
4207 conf->frag_threshold = (u32)-1;
4208 conf->rts_threshold = (u32)-1;
4209 conf->retry_short = (u32)-1;
4210 conf->retry_long = (u32)-1;
4211 conf->tx_power = -1;
4212}
4213
4214static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
4215{
4216 memset(el, 0, sizeof(*el));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004217 el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status;
Hante Meuleman1a873342012-09-27 14:17:54 +02004218 el->handler[BRCMF_E_DEAUTH_IND] = brcmf_notify_connect_status;
4219 el->handler[BRCMF_E_DEAUTH] = brcmf_notify_connect_status;
4220 el->handler[BRCMF_E_DISASSOC_IND] = brcmf_notify_connect_status;
4221 el->handler[BRCMF_E_ASSOC_IND] = brcmf_notify_connect_status;
4222 el->handler[BRCMF_E_REASSOC_IND] = brcmf_notify_connect_status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004223 el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
4224 el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
4225 el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
Arend van Spriele5806072012-09-19 22:21:08 +02004226 el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004227}
4228
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004229static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004230{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004231 kfree(cfg->conf);
4232 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004233 kfree(cfg->escan_ioctl_buf);
4234 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004235 kfree(cfg->extra_buf);
4236 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004237 kfree(cfg->pmk_list);
4238 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004239}
4240
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004241static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004242{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004243 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4244 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004245 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004246 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4247 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004248 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004249 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4250 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004251 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004252 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4253 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004254 goto init_priv_mem_out;
4255
4256 return 0;
4257
4258init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004259 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004260
4261 return -ENOMEM;
4262}
4263
4264/*
4265* retrieve first queued event from head
4266*/
4267
4268static struct brcmf_cfg80211_event_q *brcmf_deq_event(
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004269 struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004270{
4271 struct brcmf_cfg80211_event_q *e = NULL;
4272
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004273 spin_lock_irq(&cfg->evt_q_lock);
4274 if (!list_empty(&cfg->evt_q_list)) {
4275 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004276 struct brcmf_cfg80211_event_q, evt_q_list);
4277 list_del(&e->evt_q_list);
4278 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004279 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004280
4281 return e;
4282}
4283
4284/*
Arend van Sprielbcbec9e2012-02-09 21:09:06 +01004285* push event to tail of the queue
4286*
4287* remark: this function may not sleep as it is called in atomic context.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004288*/
4289
4290static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004291brcmf_enq_event(struct brcmf_if *ifp, u32 event,
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004292 const struct brcmf_event_msg *msg, void *data)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004293{
Arend van Spriel19937322012-11-05 16:22:32 -08004294 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004295 struct brcmf_cfg80211_event_q *e;
4296 s32 err = 0;
Arend van Sprielcf440662012-02-09 21:09:07 +01004297 ulong flags;
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004298 u32 data_len;
4299 u32 total_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004300
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004301 total_len = sizeof(struct brcmf_cfg80211_event_q);
4302 if (data)
4303 data_len = be32_to_cpu(msg->datalen);
4304 else
4305 data_len = 0;
4306 total_len += data_len;
4307 e = kzalloc(total_len, GFP_ATOMIC);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004308 if (!e)
4309 return -ENOMEM;
4310
4311 e->etype = event;
Arend van Spriel19937322012-11-05 16:22:32 -08004312 e->ifp = ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004313 memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
Hante Meulemanc4fdb052012-09-11 21:18:47 +02004314 if (data)
4315 memcpy(&e->edata, data, data_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004316
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004317 spin_lock_irqsave(&cfg->evt_q_lock, flags);
4318 list_add_tail(&e->evt_q_list, &cfg->evt_q_list);
4319 spin_unlock_irqrestore(&cfg->evt_q_lock, flags);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004320
4321 return err;
4322}
4323
4324static void brcmf_put_event(struct brcmf_cfg80211_event_q *e)
4325{
4326 kfree(e);
4327}
4328
4329static void brcmf_cfg80211_event_handler(struct work_struct *work)
4330{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004331 struct brcmf_cfg80211_info *cfg =
4332 container_of(work, struct brcmf_cfg80211_info,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004333 event_work);
4334 struct brcmf_cfg80211_event_q *e;
4335
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004336 e = brcmf_deq_event(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004337 if (unlikely(!e)) {
4338 WL_ERR("event queue empty...\n");
4339 return;
4340 }
4341
4342 do {
4343 WL_INFO("event type (%d)\n", e->etype);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004344 if (cfg->el.handler[e->etype])
Arend van Spriel19937322012-11-05 16:22:32 -08004345 cfg->el.handler[e->etype](e->ifp, &e->emsg, e->edata);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004346 else
4347 WL_INFO("Unknown Event (%d): ignoring\n", e->etype);
4348 brcmf_put_event(e);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004349 } while ((e = brcmf_deq_event(cfg)));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004350
4351}
4352
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004353static void brcmf_init_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004354{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004355 spin_lock_init(&cfg->evt_q_lock);
4356 INIT_LIST_HEAD(&cfg->evt_q_list);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004357}
4358
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004359static void brcmf_flush_eq(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004360{
4361 struct brcmf_cfg80211_event_q *e;
4362
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004363 spin_lock_irq(&cfg->evt_q_lock);
4364 while (!list_empty(&cfg->evt_q_list)) {
4365 e = list_first_entry(&cfg->evt_q_list,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004366 struct brcmf_cfg80211_event_q, evt_q_list);
4367 list_del(&e->evt_q_list);
4368 kfree(e);
4369 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004370 spin_unlock_irq(&cfg->evt_q_lock);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004371}
4372
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004373static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004374{
4375 s32 err = 0;
4376
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004377 cfg->scan_request = NULL;
4378 cfg->pwr_save = true;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004379 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004380 we enable roam per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004381 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02004382 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004383 cfg->dongle_up = false; /* dongle is not up yet */
4384 brcmf_init_eq(cfg);
4385 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004386 if (err)
4387 return err;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004388 INIT_WORK(&cfg->event_work, brcmf_cfg80211_event_handler);
4389 brcmf_init_eloop_handler(&cfg->el);
4390 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004391 brcmf_init_escan(cfg);
4392 brcmf_init_conf(cfg->conf);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004393 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004394
4395 return err;
4396}
4397
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004398static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004399{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004400 cancel_work_sync(&cfg->event_work);
4401 cfg->dongle_up = false; /* dongle down */
4402 brcmf_flush_eq(cfg);
4403 brcmf_link_down(cfg);
4404 brcmf_abort_scanning(cfg);
4405 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004406}
4407
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004408struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004409{
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004410 struct net_device *ndev = drvr->iflist[0]->ndev;
4411 struct device *busdev = drvr->dev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004412 struct brcmf_cfg80211_info *cfg;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004413 struct wiphy *wiphy;
4414 struct brcmf_cfg80211_vif *vif;
4415 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004416 s32 err = 0;
4417
4418 if (!ndev) {
4419 WL_ERR("ndev is invalid\n");
4420 return NULL;
4421 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004422
Arend van Spriel3eacf862012-10-22 13:55:30 -07004423 ifp = netdev_priv(ndev);
4424 wiphy = brcmf_setup_wiphy(busdev);
4425 if (IS_ERR(wiphy))
4426 return NULL;
4427
4428 cfg = wiphy_priv(wiphy);
4429 cfg->wiphy = wiphy;
4430 cfg->pub = drvr;
4431 INIT_LIST_HEAD(&cfg->vif_list);
4432
4433 vif = brcmf_alloc_vif(cfg, ndev, WL_MODE_BSS, false);
4434 if (IS_ERR(vif)) {
4435 wiphy_free(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004436 return NULL;
4437 }
4438
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004439 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004440 if (err) {
4441 WL_ERR("Failed to init iwm_priv (%d)\n", err);
4442 goto cfg80211_attach_out;
4443 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004444
Arend van Spriel3eacf862012-10-22 13:55:30 -07004445 ifp->vif = vif;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004446 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004447
4448cfg80211_attach_out:
Arend van Spriel3eacf862012-10-22 13:55:30 -07004449 brcmf_free_vif(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004450 return NULL;
4451}
4452
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004453void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004454{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004455 struct brcmf_cfg80211_vif *vif;
4456 struct brcmf_cfg80211_vif *tmp;
4457
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004458 wl_deinit_priv(cfg);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004459 list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) {
4460 brcmf_free_vif(vif);
4461 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004462}
4463
Arend van Spriel19937322012-11-05 16:22:32 -08004464void brcmf_cfg80211_event(struct brcmf_if *ifp,
4465 const struct brcmf_event_msg *e, void *data)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004466{
Arend van Spriel19937322012-11-05 16:22:32 -08004467 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004468 u32 event_type = be32_to_cpu(e->event_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004469
Arend van Spriel19937322012-11-05 16:22:32 -08004470 if (!brcmf_enq_event(ifp, event_type, e, data))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004471 schedule_work(&cfg->event_work);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004472}
4473
Arend van Spriel5b435de2011-10-05 13:19:03 +02004474static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
4475{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004476 s8 eventmask[BRCMF_EVENTING_MASK_LEN];
4477 s32 err = 0;
4478
4479 WL_TRACE("Enter\n");
4480
4481 /* Setup event_msgs */
Arend van Sprielac24be62012-10-22 10:36:23 -07004482 err = brcmf_fil_iovar_data_get(netdev_priv(ndev), "event_msgs",
4483 eventmask, BRCMF_EVENTING_MASK_LEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004484 if (err) {
4485 WL_ERR("Get event_msgs error (%d)\n", err);
4486 goto dongle_eventmsg_out;
4487 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004488
4489 setbit(eventmask, BRCMF_E_SET_SSID);
4490 setbit(eventmask, BRCMF_E_ROAM);
4491 setbit(eventmask, BRCMF_E_PRUNE);
4492 setbit(eventmask, BRCMF_E_AUTH);
4493 setbit(eventmask, BRCMF_E_REASSOC);
4494 setbit(eventmask, BRCMF_E_REASSOC_IND);
4495 setbit(eventmask, BRCMF_E_DEAUTH_IND);
4496 setbit(eventmask, BRCMF_E_DISASSOC_IND);
4497 setbit(eventmask, BRCMF_E_DISASSOC);
4498 setbit(eventmask, BRCMF_E_JOIN);
4499 setbit(eventmask, BRCMF_E_ASSOC_IND);
4500 setbit(eventmask, BRCMF_E_PSK_SUP);
4501 setbit(eventmask, BRCMF_E_LINK);
4502 setbit(eventmask, BRCMF_E_NDIS_LINK);
4503 setbit(eventmask, BRCMF_E_MIC_ERROR);
4504 setbit(eventmask, BRCMF_E_PMKID_CACHE);
4505 setbit(eventmask, BRCMF_E_TXFAIL);
4506 setbit(eventmask, BRCMF_E_JOIN_START);
Hante Meulemane756af52012-09-11 21:18:52 +02004507 setbit(eventmask, BRCMF_E_ESCAN_RESULT);
Arend van Spriele5806072012-09-19 22:21:08 +02004508 setbit(eventmask, BRCMF_E_PFN_NET_FOUND);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004509
Arend van Sprielac24be62012-10-22 10:36:23 -07004510 err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "event_msgs",
4511 eventmask, BRCMF_EVENTING_MASK_LEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004512 if (err) {
4513 WL_ERR("Set event_msgs error (%d)\n", err);
4514 goto dongle_eventmsg_out;
4515 }
4516
4517dongle_eventmsg_out:
4518 WL_TRACE("Exit\n");
4519 return err;
4520}
4521
4522static s32
4523brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
4524{
Arend van Sprielac24be62012-10-22 10:36:23 -07004525 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004526 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02004527 __le32 roamtrigger[2];
4528 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02004529
4530 /*
4531 * Setup timeout if Beacons are lost and roam is
4532 * off to report link down
4533 */
4534 if (roamvar) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004535 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004536 if (err) {
4537 WL_ERR("bcn_timeout error (%d)\n", err);
4538 goto dongle_rom_out;
4539 }
4540 }
4541
4542 /*
4543 * Enable/Disable built-in roaming to allow supplicant
4544 * to take care of roaming
4545 */
4546 WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
Arend van Sprielac24be62012-10-22 10:36:23 -07004547 err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004548 if (err) {
4549 WL_ERR("roam_off error (%d)\n", err);
4550 goto dongle_rom_out;
4551 }
4552
Arend van Sprielf588bc02011-10-12 20:51:22 +02004553 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
4554 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004555 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004556 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004557 if (err) {
4558 WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
4559 goto dongle_rom_out;
4560 }
4561
Arend van Sprielf588bc02011-10-12 20:51:22 +02004562 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
4563 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004564 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004565 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004566 if (err) {
4567 WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
4568 goto dongle_rom_out;
4569 }
4570
4571dongle_rom_out:
4572 return err;
4573}
4574
4575static s32
4576brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02004577 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004578{
Arend van Sprielac24be62012-10-22 10:36:23 -07004579 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004580 s32 err = 0;
4581
Arend van Sprielac24be62012-10-22 10:36:23 -07004582 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004583 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004584 if (err) {
4585 if (err == -EOPNOTSUPP)
4586 WL_INFO("Scan assoc time is not supported\n");
4587 else
4588 WL_ERR("Scan assoc time error (%d)\n", err);
4589 goto dongle_scantime_out;
4590 }
Arend van Sprielac24be62012-10-22 10:36:23 -07004591 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004592 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004593 if (err) {
4594 if (err == -EOPNOTSUPP)
4595 WL_INFO("Scan unassoc time is not supported\n");
4596 else
4597 WL_ERR("Scan unassoc time error (%d)\n", err);
4598 goto dongle_scantime_out;
4599 }
4600
Arend van Sprielac24be62012-10-22 10:36:23 -07004601 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004602 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004603 if (err) {
4604 if (err == -EOPNOTSUPP)
4605 WL_INFO("Scan passive time is not supported\n");
4606 else
4607 WL_ERR("Scan passive time error (%d)\n", err);
4608 goto dongle_scantime_out;
4609 }
4610
4611dongle_scantime_out:
4612 return err;
4613}
4614
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004615static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004616{
Arend van Sprielac24be62012-10-22 10:36:23 -07004617 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004618 struct wiphy *wiphy;
4619 s32 phy_list;
4620 s8 phy;
4621 s32 err = 0;
4622
Arend van Sprielac24be62012-10-22 10:36:23 -07004623 err = brcmf_fil_cmd_data_get(ifp, BRCM_GET_PHYLIST,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004624 &phy_list, sizeof(phy_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004625 if (err) {
4626 WL_ERR("error (%d)\n", err);
4627 return err;
4628 }
4629
Hante Meuleman3ba81372012-09-19 22:21:13 +02004630 phy = ((char *)&phy_list)[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +02004631 WL_INFO("%c phy\n", phy);
4632 if (phy == 'n' || phy == 'a') {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004633 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004634 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
4635 }
4636
4637 return err;
4638}
4639
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004640static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004641{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004642 return wl_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004643}
4644
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004645static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004646{
4647 struct net_device *ndev;
4648 struct wireless_dev *wdev;
4649 s32 power_mode;
4650 s32 err = 0;
4651
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004652 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004653 return err;
4654
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004655 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004656 wdev = ndev->ieee80211_ptr;
4657
4658 brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
4659 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
4660
4661 err = brcmf_dongle_eventmsg(ndev);
4662 if (err)
4663 goto default_conf_out;
4664
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004665 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Arend van Sprielac24be62012-10-22 10:36:23 -07004666 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PM,
4667 power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004668 if (err)
4669 goto default_conf_out;
4670 WL_INFO("power save set to %s\n",
4671 (power_mode ? "enabled" : "disabled"));
4672
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004673 err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004674 WL_BEACON_TIMEOUT);
4675 if (err)
4676 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07004677 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
4678 NULL, NULL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004679 if (err && err != -EINPROGRESS)
4680 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004681 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004682 if (err)
4683 goto default_conf_out;
4684
4685 /* -EINPROGRESS: Call commit handler */
4686
4687default_conf_out:
4688
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004689 cfg->dongle_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004690
4691 return err;
4692
4693}
4694
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004695static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004696{
Arend van Sprielc1179032012-10-22 13:55:33 -07004697 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004698
Arend van Sprielc1179032012-10-22 13:55:33 -07004699 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004700
Hante Meulemanf07998952012-11-05 16:22:13 -08004701 return brcmf_config_dongle(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004702}
4703
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004704static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004705{
Arend van Sprielc1179032012-10-22 13:55:33 -07004706 struct net_device *ndev = cfg_to_ndev(cfg);
4707 struct brcmf_if *ifp = netdev_priv(ndev);
4708
Arend van Spriel5b435de2011-10-05 13:19:03 +02004709 /*
4710 * While going down, if associated with AP disassociate
4711 * from AP to save power
4712 */
Arend van Sprielc1179032012-10-22 13:55:33 -07004713 if ((test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state) ||
4714 test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) &&
Arend van Sprielce81e312012-10-22 13:55:37 -07004715 check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004716 WL_INFO("Disassociating from AP");
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004717 brcmf_link_down(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004718
4719 /* Make sure WPA_Supplicant receives all the event
4720 generated due to DISASSOC call to the fw to keep
4721 the state fw and WPA_Supplicant state consistent
4722 */
4723 brcmf_delay(500);
4724 }
4725
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004726 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07004727 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004728
Arend van Spriel5b435de2011-10-05 13:19:03 +02004729 return 0;
4730}
4731
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004732s32 brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004733{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004734 s32 err = 0;
4735
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004736 mutex_lock(&cfg->usr_sync);
4737 err = __brcmf_cfg80211_up(cfg);
4738 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004739
4740 return err;
4741}
4742
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004743s32 brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004744{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004745 s32 err = 0;
4746
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004747 mutex_lock(&cfg->usr_sync);
4748 err = __brcmf_cfg80211_down(cfg);
4749 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004750
4751 return err;
4752}
4753