blob: 75464ad4fbd188ce1fb135302539dfa02caa3fbb [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>
Arend van Spriel5b435de2011-10-05 13:19:03 +020022#include <linux/etherdevice.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020024#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020025
26#include <brcmu_utils.h>
27#include <defs.h>
28#include <brcmu_wifi.h>
29#include "dhd.h"
Arend van Spriel16886732012-12-05 15:26:04 +010030#include "dhd_dbg.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020031#include "wl_cfg80211.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070032#include "fwil.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020033
Arend van Spriele5806072012-09-19 22:21:08 +020034#define BRCMF_SCAN_IE_LEN_MAX 2048
35#define BRCMF_PNO_VERSION 2
36#define BRCMF_PNO_TIME 30
37#define BRCMF_PNO_REPEAT 4
38#define BRCMF_PNO_FREQ_EXPO_MAX 3
39#define BRCMF_PNO_MAX_PFN_COUNT 16
40#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
41#define BRCMF_PNO_HIDDEN_BIT 2
42#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
43#define BRCMF_PNO_SCAN_COMPLETE 1
44#define BRCMF_PNO_SCAN_INCOMPLETE 0
45
Arend van Spriel3eacf862012-10-22 13:55:30 -070046#define BRCMF_IFACE_MAX_CNT 2
47
Hante Meuleman1a873342012-09-27 14:17:54 +020048#define TLV_LEN_OFF 1 /* length offset */
Hante Meuleman04012892012-09-27 14:17:49 +020049#define TLV_HDR_LEN 2 /* header length */
Hante Meuleman1a873342012-09-27 14:17:54 +020050#define TLV_BODY_OFF 2 /* body offset */
51#define TLV_OUI_LEN 3 /* oui id length */
52#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
53#define WPA_OUI_TYPE 1
54#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
55#define WME_OUI_TYPE 2
56
57#define VS_IE_FIXED_HDR_LEN 6
58#define WPA_IE_VERSION_LEN 2
59#define WPA_IE_MIN_OUI_LEN 4
60#define WPA_IE_SUITE_COUNT_LEN 2
61
62#define WPA_CIPHER_NONE 0 /* None */
63#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
64#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
65#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
66#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
67
68#define RSN_AKM_NONE 0 /* None (IBSS) */
69#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
70#define RSN_AKM_PSK 2 /* Pre-shared Key */
71#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
72#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
73
74#define VNDR_IE_CMD_LEN 4 /* length of the set command
75 * string :"add", "del" (+ NUL)
76 */
77#define VNDR_IE_COUNT_OFFSET 4
78#define VNDR_IE_PKTFLAG_OFFSET 8
79#define VNDR_IE_VSIE_OFFSET 12
80#define VNDR_IE_HDR_SIZE 12
81#define VNDR_IE_BEACON_FLAG 0x1
82#define VNDR_IE_PRBRSP_FLAG 0x2
83#define MAX_VNDR_IE_NUMBER 5
84
85#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
86#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020087
Arend van Spriel5b435de2011-10-05 13:19:03 +020088#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
89 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
90
Arend van Sprielce81e312012-10-22 13:55:37 -070091static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +020092{
Arend van Sprielc1179032012-10-22 13:55:33 -070093 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +010094 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
95 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +020096 return false;
97 }
98 return true;
99}
100
101#define CHAN2G(_channel, _freq, _flags) { \
102 .band = IEEE80211_BAND_2GHZ, \
103 .center_freq = (_freq), \
104 .hw_value = (_channel), \
105 .flags = (_flags), \
106 .max_antenna_gain = 0, \
107 .max_power = 30, \
108}
109
110#define CHAN5G(_channel, _flags) { \
111 .band = IEEE80211_BAND_5GHZ, \
112 .center_freq = 5000 + (5 * (_channel)), \
113 .hw_value = (_channel), \
114 .flags = (_flags), \
115 .max_antenna_gain = 0, \
116 .max_power = 30, \
117}
118
119#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
120#define RATETAB_ENT(_rateid, _flags) \
121 { \
122 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
123 .hw_value = (_rateid), \
124 .flags = (_flags), \
125 }
126
127static struct ieee80211_rate __wl_rates[] = {
128 RATETAB_ENT(BRCM_RATE_1M, 0),
129 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
130 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
131 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
132 RATETAB_ENT(BRCM_RATE_6M, 0),
133 RATETAB_ENT(BRCM_RATE_9M, 0),
134 RATETAB_ENT(BRCM_RATE_12M, 0),
135 RATETAB_ENT(BRCM_RATE_18M, 0),
136 RATETAB_ENT(BRCM_RATE_24M, 0),
137 RATETAB_ENT(BRCM_RATE_36M, 0),
138 RATETAB_ENT(BRCM_RATE_48M, 0),
139 RATETAB_ENT(BRCM_RATE_54M, 0),
140};
141
142#define wl_a_rates (__wl_rates + 4)
143#define wl_a_rates_size 8
144#define wl_g_rates (__wl_rates + 0)
145#define wl_g_rates_size 12
146
147static struct ieee80211_channel __wl_2ghz_channels[] = {
148 CHAN2G(1, 2412, 0),
149 CHAN2G(2, 2417, 0),
150 CHAN2G(3, 2422, 0),
151 CHAN2G(4, 2427, 0),
152 CHAN2G(5, 2432, 0),
153 CHAN2G(6, 2437, 0),
154 CHAN2G(7, 2442, 0),
155 CHAN2G(8, 2447, 0),
156 CHAN2G(9, 2452, 0),
157 CHAN2G(10, 2457, 0),
158 CHAN2G(11, 2462, 0),
159 CHAN2G(12, 2467, 0),
160 CHAN2G(13, 2472, 0),
161 CHAN2G(14, 2484, 0),
162};
163
164static struct ieee80211_channel __wl_5ghz_a_channels[] = {
165 CHAN5G(34, 0), CHAN5G(36, 0),
166 CHAN5G(38, 0), CHAN5G(40, 0),
167 CHAN5G(42, 0), CHAN5G(44, 0),
168 CHAN5G(46, 0), CHAN5G(48, 0),
169 CHAN5G(52, 0), CHAN5G(56, 0),
170 CHAN5G(60, 0), CHAN5G(64, 0),
171 CHAN5G(100, 0), CHAN5G(104, 0),
172 CHAN5G(108, 0), CHAN5G(112, 0),
173 CHAN5G(116, 0), CHAN5G(120, 0),
174 CHAN5G(124, 0), CHAN5G(128, 0),
175 CHAN5G(132, 0), CHAN5G(136, 0),
176 CHAN5G(140, 0), CHAN5G(149, 0),
177 CHAN5G(153, 0), CHAN5G(157, 0),
178 CHAN5G(161, 0), CHAN5G(165, 0),
179 CHAN5G(184, 0), CHAN5G(188, 0),
180 CHAN5G(192, 0), CHAN5G(196, 0),
181 CHAN5G(200, 0), CHAN5G(204, 0),
182 CHAN5G(208, 0), CHAN5G(212, 0),
183 CHAN5G(216, 0),
184};
185
186static struct ieee80211_channel __wl_5ghz_n_channels[] = {
187 CHAN5G(32, 0), CHAN5G(34, 0),
188 CHAN5G(36, 0), CHAN5G(38, 0),
189 CHAN5G(40, 0), CHAN5G(42, 0),
190 CHAN5G(44, 0), CHAN5G(46, 0),
191 CHAN5G(48, 0), CHAN5G(50, 0),
192 CHAN5G(52, 0), CHAN5G(54, 0),
193 CHAN5G(56, 0), CHAN5G(58, 0),
194 CHAN5G(60, 0), CHAN5G(62, 0),
195 CHAN5G(64, 0), CHAN5G(66, 0),
196 CHAN5G(68, 0), CHAN5G(70, 0),
197 CHAN5G(72, 0), CHAN5G(74, 0),
198 CHAN5G(76, 0), CHAN5G(78, 0),
199 CHAN5G(80, 0), CHAN5G(82, 0),
200 CHAN5G(84, 0), CHAN5G(86, 0),
201 CHAN5G(88, 0), CHAN5G(90, 0),
202 CHAN5G(92, 0), CHAN5G(94, 0),
203 CHAN5G(96, 0), CHAN5G(98, 0),
204 CHAN5G(100, 0), CHAN5G(102, 0),
205 CHAN5G(104, 0), CHAN5G(106, 0),
206 CHAN5G(108, 0), CHAN5G(110, 0),
207 CHAN5G(112, 0), CHAN5G(114, 0),
208 CHAN5G(116, 0), CHAN5G(118, 0),
209 CHAN5G(120, 0), CHAN5G(122, 0),
210 CHAN5G(124, 0), CHAN5G(126, 0),
211 CHAN5G(128, 0), CHAN5G(130, 0),
212 CHAN5G(132, 0), CHAN5G(134, 0),
213 CHAN5G(136, 0), CHAN5G(138, 0),
214 CHAN5G(140, 0), CHAN5G(142, 0),
215 CHAN5G(144, 0), CHAN5G(145, 0),
216 CHAN5G(146, 0), CHAN5G(147, 0),
217 CHAN5G(148, 0), CHAN5G(149, 0),
218 CHAN5G(150, 0), CHAN5G(151, 0),
219 CHAN5G(152, 0), CHAN5G(153, 0),
220 CHAN5G(154, 0), CHAN5G(155, 0),
221 CHAN5G(156, 0), CHAN5G(157, 0),
222 CHAN5G(158, 0), CHAN5G(159, 0),
223 CHAN5G(160, 0), CHAN5G(161, 0),
224 CHAN5G(162, 0), CHAN5G(163, 0),
225 CHAN5G(164, 0), CHAN5G(165, 0),
226 CHAN5G(166, 0), CHAN5G(168, 0),
227 CHAN5G(170, 0), CHAN5G(172, 0),
228 CHAN5G(174, 0), CHAN5G(176, 0),
229 CHAN5G(178, 0), CHAN5G(180, 0),
230 CHAN5G(182, 0), CHAN5G(184, 0),
231 CHAN5G(186, 0), CHAN5G(188, 0),
232 CHAN5G(190, 0), CHAN5G(192, 0),
233 CHAN5G(194, 0), CHAN5G(196, 0),
234 CHAN5G(198, 0), CHAN5G(200, 0),
235 CHAN5G(202, 0), CHAN5G(204, 0),
236 CHAN5G(206, 0), CHAN5G(208, 0),
237 CHAN5G(210, 0), CHAN5G(212, 0),
238 CHAN5G(214, 0), CHAN5G(216, 0),
239 CHAN5G(218, 0), CHAN5G(220, 0),
240 CHAN5G(222, 0), CHAN5G(224, 0),
241 CHAN5G(226, 0), CHAN5G(228, 0),
242};
243
244static struct ieee80211_supported_band __wl_band_2ghz = {
245 .band = IEEE80211_BAND_2GHZ,
246 .channels = __wl_2ghz_channels,
247 .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
248 .bitrates = wl_g_rates,
249 .n_bitrates = wl_g_rates_size,
250};
251
252static struct ieee80211_supported_band __wl_band_5ghz_a = {
253 .band = IEEE80211_BAND_5GHZ,
254 .channels = __wl_5ghz_a_channels,
255 .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
256 .bitrates = wl_a_rates,
257 .n_bitrates = wl_a_rates_size,
258};
259
260static struct ieee80211_supported_band __wl_band_5ghz_n = {
261 .band = IEEE80211_BAND_5GHZ,
262 .channels = __wl_5ghz_n_channels,
263 .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
264 .bitrates = wl_a_rates,
265 .n_bitrates = wl_a_rates_size,
266};
267
268static const u32 __wl_cipher_suites[] = {
269 WLAN_CIPHER_SUITE_WEP40,
270 WLAN_CIPHER_SUITE_WEP104,
271 WLAN_CIPHER_SUITE_TKIP,
272 WLAN_CIPHER_SUITE_CCMP,
273 WLAN_CIPHER_SUITE_AES_CMAC,
274};
275
Alwin Beukersf8e4b412011-10-12 20:51:28 +0200276/* tag_ID/length/value_buffer tuple */
277struct brcmf_tlv {
278 u8 id;
279 u8 len;
280 u8 data[1];
281};
282
Hante Meuleman1a873342012-09-27 14:17:54 +0200283/* Vendor specific ie. id = 221, oui and type defines exact ie */
284struct brcmf_vs_tlv {
285 u8 id;
286 u8 len;
287 u8 oui[3];
288 u8 oui_type;
289};
290
291struct parsed_vndr_ie_info {
292 u8 *ie_ptr;
293 u32 ie_len; /* total length including id & length field */
294 struct brcmf_vs_tlv vndrie;
295};
296
297struct parsed_vndr_ies {
298 u32 count;
299 struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER];
300};
301
Alwin Beukersef6ac172011-10-12 20:51:26 +0200302/* Quarter dBm units to mW
303 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
304 * Table is offset so the last entry is largest mW value that fits in
305 * a u16.
306 */
307
308#define QDBM_OFFSET 153 /* Offset for first entry */
309#define QDBM_TABLE_LEN 40 /* Table size */
310
311/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
312 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
313 */
314#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
315
316/* Largest mW value that will round down to the last table entry,
317 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
318 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
319 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
320 */
321#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
322
323static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
324/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
325/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
326/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
327/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
328/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
329/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
330};
331
332static u16 brcmf_qdbm_to_mw(u8 qdbm)
333{
334 uint factor = 1;
335 int idx = qdbm - QDBM_OFFSET;
336
337 if (idx >= QDBM_TABLE_LEN)
338 /* clamp to max u16 mW value */
339 return 0xFFFF;
340
341 /* scale the qdBm index up to the range of the table 0-40
342 * where an offset of 40 qdBm equals a factor of 10 mW.
343 */
344 while (idx < 0) {
345 idx += 40;
346 factor *= 10;
347 }
348
349 /* return the mW value scaled down to the correct factor of 10,
350 * adding in factor/2 to get proper rounding.
351 */
352 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
353}
354
355static u8 brcmf_mw_to_qdbm(u16 mw)
356{
357 u8 qdbm;
358 int offset;
359 uint mw_uint = mw;
360 uint boundary;
361
362 /* handle boundary case */
363 if (mw_uint <= 1)
364 return 0;
365
366 offset = QDBM_OFFSET;
367
368 /* move mw into the range of the table */
369 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
370 mw_uint *= 10;
371 offset -= 40;
372 }
373
374 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
375 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
376 nqdBm_to_mW_map[qdbm]) / 2;
377 if (mw_uint < boundary)
378 break;
379 }
380
381 qdbm += (u8) offset;
382
383 return qdbm;
384}
385
Arend van Spriel6e186162012-10-22 10:36:22 -0700386static u16 channel_to_chanspec(struct ieee80211_channel *ch)
387{
388 u16 chanspec;
389
390 chanspec = ieee80211_frequency_to_channel(ch->center_freq);
391 chanspec &= WL_CHANSPEC_CHAN_MASK;
392
393 if (ch->band == IEEE80211_BAND_2GHZ)
394 chanspec |= WL_CHANSPEC_BAND_2G;
395 else
396 chanspec |= WL_CHANSPEC_BAND_5G;
397
398 if (ch->flags & IEEE80211_CHAN_NO_HT40) {
399 chanspec |= WL_CHANSPEC_BW_20;
400 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
401 } else {
402 chanspec |= WL_CHANSPEC_BW_40;
403 if (ch->flags & IEEE80211_CHAN_NO_HT40PLUS)
404 chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
405 else
406 chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
407 }
408 return chanspec;
409}
410
Arend van Spriel5b435de2011-10-05 13:19:03 +0200411static void convert_key_from_CPU(struct brcmf_wsec_key *key,
412 struct brcmf_wsec_key_le *key_le)
413{
414 key_le->index = cpu_to_le32(key->index);
415 key_le->len = cpu_to_le32(key->len);
416 key_le->algo = cpu_to_le32(key->algo);
417 key_le->flags = cpu_to_le32(key->flags);
418 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
419 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
420 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
421 memcpy(key_le->data, key->data, sizeof(key->data));
422 memcpy(key_le->ea, key->ea, sizeof(key->ea));
423}
424
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200425static int
Arend van Spriel2eaba7e2012-10-22 10:36:26 -0700426send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200427{
428 int err;
429 struct brcmf_wsec_key_le key_le;
430
431 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200432
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700433 brcmf_netdev_wait_pend8021x(ndev);
434
Arend van Sprielac24be62012-10-22 10:36:23 -0700435 err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700436 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200437
Arend van Spriel5b435de2011-10-05 13:19:03 +0200438 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100439 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200440 return err;
441}
442
443static s32
444brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
445 enum nl80211_iftype type, u32 *flags,
446 struct vif_params *params)
447{
Arend van Sprielc1179032012-10-22 13:55:33 -0700448 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100449 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200450 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200451 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200452 s32 err = 0;
453
Arend van Sprield96b8012012-12-05 15:26:02 +0100454 brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200455
456 switch (type) {
457 case NL80211_IFTYPE_MONITOR:
458 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100459 brcmf_err("type (%d) : currently we do not support this type\n",
460 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200461 return -EOPNOTSUPP;
462 case NL80211_IFTYPE_ADHOC:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100463 vif->mode = WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200464 infra = 0;
465 break;
466 case NL80211_IFTYPE_STATION:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100467 vif->mode = WL_MODE_BSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200468 infra = 1;
469 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200470 case NL80211_IFTYPE_AP:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100471 vif->mode = WL_MODE_AP;
Hante Meuleman1a873342012-09-27 14:17:54 +0200472 ap = 1;
473 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200474 default:
475 err = -EINVAL;
476 goto done;
477 }
478
Hante Meuleman1a873342012-09-27 14:17:54 +0200479 if (ap) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100480 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100481 brcmf_dbg(INFO, "IF Type = AP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200482 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100483 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200484 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100485 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200486 err = -EAGAIN;
487 goto done;
488 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100489 brcmf_dbg(INFO, "IF Type = %s\n", (vif->mode == WL_MODE_IBSS) ?
490 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200491 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200492 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200493
494done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100495 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200496
497 return err;
498}
499
Arend van Spriel5b435de2011-10-05 13:19:03 +0200500static void brcmf_set_mpc(struct net_device *ndev, int mpc)
501{
Arend van Sprielc1179032012-10-22 13:55:33 -0700502 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200503 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200504
Arend van Sprielce81e312012-10-22 13:55:37 -0700505 if (check_vif_up(ifp->vif)) {
Arend van Sprielc1179032012-10-22 13:55:33 -0700506 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200507 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100508 brcmf_err("fail to set mpc\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200509 return;
510 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100511 brcmf_dbg(INFO, "MPC : %d\n", mpc);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200512 }
513}
514
Hante Meulemane756af52012-09-11 21:18:52 +0200515static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
516 struct cfg80211_scan_request *request)
517{
518 u32 n_ssids;
519 u32 n_channels;
520 s32 i;
521 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200522 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200523 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200524 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200525
Arend van Sprielba40d162012-10-22 13:55:38 -0700526 memset(params_le->bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200527 params_le->bss_type = DOT11_BSSTYPE_ANY;
528 params_le->scan_type = 0;
529 params_le->channel_num = 0;
530 params_le->nprobes = cpu_to_le32(-1);
531 params_le->active_time = cpu_to_le32(-1);
532 params_le->passive_time = cpu_to_le32(-1);
533 params_le->home_time = cpu_to_le32(-1);
534 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
535
536 /* if request is null exit so it will be all channel broadcast scan */
537 if (!request)
538 return;
539
540 n_ssids = request->n_ssids;
541 n_channels = request->n_channels;
542 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100543 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
544 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200545 if (n_channels > 0) {
546 for (i = 0; i < n_channels; i++) {
Arend van Spriel6e186162012-10-22 10:36:22 -0700547 chanspec = channel_to_chanspec(request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100548 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
549 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200550 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200551 }
552 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100553 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200554 }
555 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100556 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200557 if (n_ssids > 0) {
558 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
559 n_channels * sizeof(u16);
560 offset = roundup(offset, sizeof(u32));
561 ptr = (char *)params_le + offset;
562 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200563 memset(&ssid_le, 0, sizeof(ssid_le));
564 ssid_le.SSID_len =
565 cpu_to_le32(request->ssids[i].ssid_len);
566 memcpy(ssid_le.SSID, request->ssids[i].ssid,
567 request->ssids[i].ssid_len);
568 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100569 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200570 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100571 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
572 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200573 memcpy(ptr, &ssid_le, sizeof(ssid_le));
574 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200575 }
576 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100577 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200578 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100579 brcmf_dbg(SCAN, "SSID %s len=%d\n",
580 params_le->ssid_le.SSID,
581 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200582 params_le->ssid_le.SSID_len =
583 cpu_to_le32(request->ssids->ssid_len);
584 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
585 request->ssids->ssid_len);
586 }
587 }
588 /* Adding mask to channel numbers */
589 params_le->channel_num =
590 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
591 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
592}
593
594static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200595brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +0200596 struct net_device *ndev,
597 bool aborted, bool fw_abort)
598{
599 struct brcmf_scan_params_le params_le;
600 struct cfg80211_scan_request *scan_request;
601 s32 err = 0;
602
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100603 brcmf_dbg(SCAN, "Enter\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200604
605 /* clear scan request, because the FW abort can cause a second call */
606 /* to this functon and might cause a double cfg80211_scan_done */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200607 scan_request = cfg->scan_request;
608 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200609
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200610 if (timer_pending(&cfg->escan_timeout))
611 del_timer_sync(&cfg->escan_timeout);
Hante Meulemane756af52012-09-11 21:18:52 +0200612
613 if (fw_abort) {
614 /* Do a scan abort to stop the driver's scan engine */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100615 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200616 memset(&params_le, 0, sizeof(params_le));
Arend van Sprielba40d162012-10-22 13:55:38 -0700617 memset(params_le.bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200618 params_le.bss_type = DOT11_BSSTYPE_ANY;
619 params_le.scan_type = 0;
620 params_le.channel_num = cpu_to_le32(1);
621 params_le.nprobes = cpu_to_le32(1);
622 params_le.active_time = cpu_to_le32(-1);
623 params_le.passive_time = cpu_to_le32(-1);
624 params_le.home_time = cpu_to_le32(-1);
625 /* Scan is aborted by setting channel_list[0] to -1 */
626 params_le.channel_list[0] = cpu_to_le16(-1);
627 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielac24be62012-10-22 10:36:23 -0700628 err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN,
629 &params_le, sizeof(params_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200630 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100631 brcmf_err("Scan abort failed\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200632 }
Arend van Spriele5806072012-09-19 22:21:08 +0200633 /*
634 * e-scan can be initiated by scheduled scan
635 * which takes precedence.
636 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200637 if (cfg->sched_escan) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100638 brcmf_dbg(SCAN, "scheduled scan completed\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200639 cfg->sched_escan = false;
Arend van Spriele5806072012-09-19 22:21:08 +0200640 if (!aborted)
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200641 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriele5806072012-09-19 22:21:08 +0200642 brcmf_set_mpc(ndev, 1);
643 } else if (scan_request) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100644 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
645 aborted ? "Aborted" : "Done");
Hante Meulemane756af52012-09-11 21:18:52 +0200646 cfg80211_scan_done(scan_request, aborted);
647 brcmf_set_mpc(ndev, 1);
648 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700649 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100650 brcmf_err("Scan complete while device not scanning\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200651 return -EPERM;
652 }
653
654 return err;
655}
656
657static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200658brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
Hante Meulemane756af52012-09-11 21:18:52 +0200659 struct cfg80211_scan_request *request, u16 action)
660{
661 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
662 offsetof(struct brcmf_escan_params_le, params_le);
663 struct brcmf_escan_params_le *params;
664 s32 err = 0;
665
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100666 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200667
668 if (request != NULL) {
669 /* Allocate space for populating ssids in struct */
670 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
671
672 /* Allocate space for populating ssids in struct */
673 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
674 }
675
676 params = kzalloc(params_size, GFP_KERNEL);
677 if (!params) {
678 err = -ENOMEM;
679 goto exit;
680 }
681 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
682 brcmf_escan_prep(&params->params_le, request);
683 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
684 params->action = cpu_to_le16(action);
685 params->sync_id = cpu_to_le16(0x1234);
686
Arend van Sprielac24be62012-10-22 10:36:23 -0700687 err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan",
688 params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200689 if (err) {
690 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100691 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200692 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100693 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200694 }
695
696 kfree(params);
697exit:
698 return err;
699}
700
701static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200702brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Hante Meulemane756af52012-09-11 21:18:52 +0200703 struct net_device *ndev, struct cfg80211_scan_request *request)
704{
705 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700706 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200707 struct brcmf_scan_results *results;
708
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100709 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200710 cfg->escan_info.ndev = ndev;
711 cfg->escan_info.wiphy = wiphy;
712 cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700713 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielac24be62012-10-22 10:36:23 -0700714 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700715 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200716 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100717 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200718 return err;
719 }
720 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200721 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +0200722 results->version = 0;
723 results->count = 0;
724 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
725
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200726 err = brcmf_run_escan(cfg, ndev, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200727 if (err)
728 brcmf_set_mpc(ndev, 1);
729 return err;
730}
731
732static s32
733brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
734 struct cfg80211_scan_request *request,
735 struct cfg80211_ssid *this_ssid)
736{
Arend van Sprielc1179032012-10-22 13:55:33 -0700737 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200738 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Hante Meulemane756af52012-09-11 21:18:52 +0200739 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -0800740 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700741 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200742 bool escan_req;
743 bool spec_scan;
744 s32 err;
745 u32 SSID_len;
746
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100747 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200748
Arend van Sprielc1179032012-10-22 13:55:33 -0700749 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100750 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200751 return -EAGAIN;
752 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700753 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100754 brcmf_err("Scanning being aborted: status (%lu)\n",
755 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200756 return -EAGAIN;
757 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700758 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100759 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +0200760 return -EAGAIN;
761 }
762
763 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200764 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +0200765 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
766
767 escan_req = false;
768 if (request) {
769 /* scan bss */
770 ssids = request->ssids;
771 escan_req = true;
772 } else {
773 /* scan in ibss */
774 /* we don't do escan in ibss */
775 ssids = this_ssid;
776 }
777
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200778 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -0700779 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200780 if (escan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200781 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -0800782 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +0200783 goto scan_out;
784 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100785 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
786 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200787 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
788 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
789 sr->ssid_le.SSID_len = cpu_to_le32(0);
790 spec_scan = false;
791 if (SSID_len) {
792 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
793 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
794 spec_scan = true;
795 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100796 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200797
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700798 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -0700799 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700800 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200801 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100802 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200803 goto scan_out;
804 }
805 brcmf_set_mpc(ndev, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -0700806 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -0700807 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200808 if (err) {
809 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100810 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
811 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +0200812 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100813 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200814
815 brcmf_set_mpc(ndev, 1);
816 goto scan_out;
817 }
818 }
819
820 return 0;
821
822scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -0700823 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200824 if (timer_pending(&cfg->escan_timeout))
825 del_timer_sync(&cfg->escan_timeout);
826 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200827 return err;
828}
829
Arend van Spriel5b435de2011-10-05 13:19:03 +0200830static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700831brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200832{
Johannes Bergfd014282012-06-18 19:17:03 +0200833 struct net_device *ndev = request->wdev->netdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200834 s32 err = 0;
835
Arend van Sprield96b8012012-12-05 15:26:02 +0100836 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200837
Arend van Sprielce81e312012-10-22 13:55:37 -0700838 if (!check_vif_up(container_of(request->wdev,
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700839 struct brcmf_cfg80211_vif, wdev)))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200840 return -EIO;
841
Hante Meulemanf07998952012-11-05 16:22:13 -0800842 err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +0200843
Arend van Spriel5b435de2011-10-05 13:19:03 +0200844 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100845 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200846
Arend van Sprield96b8012012-12-05 15:26:02 +0100847 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200848 return err;
849}
850
851static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
852{
853 s32 err = 0;
854
Arend van Sprielac24be62012-10-22 10:36:23 -0700855 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
856 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200857 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100858 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200859
860 return err;
861}
862
863static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
864{
865 s32 err = 0;
866
Arend van Sprielac24be62012-10-22 10:36:23 -0700867 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
868 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200869 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100870 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200871
872 return err;
873}
874
875static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
876{
877 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -0800878 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200879
Arend van Sprielac24be62012-10-22 10:36:23 -0700880 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200881 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100882 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200883 return err;
884 }
885 return err;
886}
887
888static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
889{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200890 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
891 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700892 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200893 s32 err = 0;
894
Arend van Sprield96b8012012-12-05 15:26:02 +0100895 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -0700896 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200897 return -EIO;
898
899 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200900 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
901 cfg->conf->rts_threshold = wiphy->rts_threshold;
902 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200903 if (!err)
904 goto done;
905 }
906 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200907 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
908 cfg->conf->frag_threshold = wiphy->frag_threshold;
909 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200910 if (!err)
911 goto done;
912 }
913 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200914 && (cfg->conf->retry_long != wiphy->retry_long)) {
915 cfg->conf->retry_long = wiphy->retry_long;
916 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200917 if (!err)
918 goto done;
919 }
920 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200921 && (cfg->conf->retry_short != wiphy->retry_short)) {
922 cfg->conf->retry_short = wiphy->retry_short;
923 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200924 if (!err)
925 goto done;
926 }
927
928done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100929 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200930 return err;
931}
932
Arend van Spriel5b435de2011-10-05 13:19:03 +0200933static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
934{
935 memset(prof, 0, sizeof(*prof));
936}
937
938static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
939 size_t *join_params_size)
940{
941 u16 chanspec = 0;
942
943 if (ch != 0) {
944 if (ch <= CH_MAX_2G_CHANNEL)
945 chanspec |= WL_CHANSPEC_BAND_2G;
946 else
947 chanspec |= WL_CHANSPEC_BAND_5G;
948
949 chanspec |= WL_CHANSPEC_BW_20;
950 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
951
952 *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
953 sizeof(u16);
954
955 chanspec |= (ch & WL_CHANSPEC_CHAN_MASK);
956 join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec);
957 join_params->params_le.chanspec_num = cpu_to_le32(1);
958
Arend van Spriel16886732012-12-05 15:26:04 +0100959 brcmf_dbg(CONN, "channel %d, chanspec %#X\n", ch, chanspec);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200960 }
961}
962
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100963static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200964{
Arend van Spriel5b435de2011-10-05 13:19:03 +0200965 s32 err = 0;
966
Arend van Sprield96b8012012-12-05 15:26:02 +0100967 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200968
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100969 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100970 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100971 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -0700972 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200973 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100974 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100975 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200976 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100977 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +0100978 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200979}
980
981static s32
982brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
983 struct cfg80211_ibss_params *params)
984{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200985 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700986 struct brcmf_if *ifp = netdev_priv(ndev);
987 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200988 struct brcmf_join_params join_params;
989 size_t join_params_size = 0;
990 s32 err = 0;
991 s32 wsec = 0;
992 s32 bcnprd;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200993
Arend van Sprield96b8012012-12-05 15:26:02 +0100994 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -0700995 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200996 return -EIO;
997
998 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +0100999 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001000 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001001 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001002 return -EOPNOTSUPP;
1003 }
1004
Arend van Sprielc1179032012-10-22 13:55:33 -07001005 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001006
1007 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001008 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001009 else
Arend van Spriel16886732012-12-05 15:26:04 +01001010 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001011
Johannes Berg683b6d32012-11-08 21:25:48 +01001012 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001013 brcmf_dbg(CONN, "channel: %d\n",
1014 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001015 else
Arend van Spriel16886732012-12-05 15:26:04 +01001016 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001017
1018 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001019 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001020 else
Arend van Spriel16886732012-12-05 15:26:04 +01001021 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001022
1023 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001024 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001025 else
Arend van Spriel16886732012-12-05 15:26:04 +01001026 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001027
1028 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001029 brcmf_dbg(CONN, "beacon interval: %d\n",
1030 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001031 else
Arend van Spriel16886732012-12-05 15:26:04 +01001032 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001033
1034 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001035 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001036 else
Arend van Spriel16886732012-12-05 15:26:04 +01001037 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001038
1039 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001040 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001041 else
Arend van Spriel16886732012-12-05 15:26:04 +01001042 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001043
1044 /* Configure Privacy for starter */
1045 if (params->privacy)
1046 wsec |= WEP_ENABLED;
1047
Arend van Sprielc1179032012-10-22 13:55:33 -07001048 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001049 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001050 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001051 goto done;
1052 }
1053
1054 /* Configure Beacon Interval for starter */
1055 if (params->beacon_interval)
1056 bcnprd = params->beacon_interval;
1057 else
1058 bcnprd = 100;
1059
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001060 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001061 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001062 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001063 goto done;
1064 }
1065
1066 /* Configure required join parameter */
1067 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1068
1069 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001070 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1071 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1072 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1073 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001074 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001075
1076 /* BSSID */
1077 if (params->bssid) {
1078 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1079 join_params_size = sizeof(join_params.ssid_le) +
1080 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001081 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001082 } else {
Arend van Sprielba40d162012-10-22 13:55:38 -07001083 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001084 memset(profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001085 }
1086
Arend van Spriel5b435de2011-10-05 13:19:03 +02001087 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001088 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001089 u32 target_channel;
1090
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001091 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001092 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001093 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001094 if (params->channel_fixed) {
1095 /* adding chanspec */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001096 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001097 &join_params, &join_params_size);
1098 }
1099
1100 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001101 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001102 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001103 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001104 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001105 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001106 goto done;
1107 }
1108 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001109 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001110
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001111 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001112
1113
Arend van Sprielc1179032012-10-22 13:55:33 -07001114 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001115 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001116 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001117 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001118 goto done;
1119 }
1120
1121done:
1122 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001123 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001124 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001125 return err;
1126}
1127
1128static s32
1129brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1130{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001131 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001132 s32 err = 0;
1133
Arend van Sprield96b8012012-12-05 15:26:02 +01001134 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001135 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001136 return -EIO;
1137
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001138 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001139
Arend van Sprield96b8012012-12-05 15:26:02 +01001140 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001141
1142 return err;
1143}
1144
1145static s32 brcmf_set_wpa_version(struct net_device *ndev,
1146 struct cfg80211_connect_params *sme)
1147{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001148 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001149 struct brcmf_cfg80211_security *sec;
1150 s32 val = 0;
1151 s32 err = 0;
1152
1153 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1154 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1155 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1156 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1157 else
1158 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001159 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Arend van Sprielac24be62012-10-22 10:36:23 -07001160 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001161 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001162 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001163 return err;
1164 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001165 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001166 sec->wpa_versions = sme->crypto.wpa_versions;
1167 return err;
1168}
1169
1170static s32 brcmf_set_auth_type(struct net_device *ndev,
1171 struct cfg80211_connect_params *sme)
1172{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001173 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001174 struct brcmf_cfg80211_security *sec;
1175 s32 val = 0;
1176 s32 err = 0;
1177
1178 switch (sme->auth_type) {
1179 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1180 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001181 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001182 break;
1183 case NL80211_AUTHTYPE_SHARED_KEY:
1184 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001185 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001186 break;
1187 case NL80211_AUTHTYPE_AUTOMATIC:
1188 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001189 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001190 break;
1191 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001192 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001193 default:
1194 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001195 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001196 break;
1197 }
1198
Arend van Sprielac24be62012-10-22 10:36:23 -07001199 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001200 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001201 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001202 return err;
1203 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001204 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001205 sec->auth_type = sme->auth_type;
1206 return err;
1207}
1208
1209static s32
1210brcmf_set_set_cipher(struct net_device *ndev,
1211 struct cfg80211_connect_params *sme)
1212{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001213 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001214 struct brcmf_cfg80211_security *sec;
1215 s32 pval = 0;
1216 s32 gval = 0;
1217 s32 err = 0;
1218
1219 if (sme->crypto.n_ciphers_pairwise) {
1220 switch (sme->crypto.ciphers_pairwise[0]) {
1221 case WLAN_CIPHER_SUITE_WEP40:
1222 case WLAN_CIPHER_SUITE_WEP104:
1223 pval = WEP_ENABLED;
1224 break;
1225 case WLAN_CIPHER_SUITE_TKIP:
1226 pval = TKIP_ENABLED;
1227 break;
1228 case WLAN_CIPHER_SUITE_CCMP:
1229 pval = AES_ENABLED;
1230 break;
1231 case WLAN_CIPHER_SUITE_AES_CMAC:
1232 pval = AES_ENABLED;
1233 break;
1234 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001235 brcmf_err("invalid cipher pairwise (%d)\n",
1236 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001237 return -EINVAL;
1238 }
1239 }
1240 if (sme->crypto.cipher_group) {
1241 switch (sme->crypto.cipher_group) {
1242 case WLAN_CIPHER_SUITE_WEP40:
1243 case WLAN_CIPHER_SUITE_WEP104:
1244 gval = WEP_ENABLED;
1245 break;
1246 case WLAN_CIPHER_SUITE_TKIP:
1247 gval = TKIP_ENABLED;
1248 break;
1249 case WLAN_CIPHER_SUITE_CCMP:
1250 gval = AES_ENABLED;
1251 break;
1252 case WLAN_CIPHER_SUITE_AES_CMAC:
1253 gval = AES_ENABLED;
1254 break;
1255 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001256 brcmf_err("invalid cipher group (%d)\n",
1257 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001258 return -EINVAL;
1259 }
1260 }
1261
Arend van Spriel16886732012-12-05 15:26:04 +01001262 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Arend van Sprielac24be62012-10-22 10:36:23 -07001263 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wsec", pval | gval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001264 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001265 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001266 return err;
1267 }
1268
Arend van Spriel06bb1232012-09-27 14:17:56 +02001269 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001270 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1271 sec->cipher_group = sme->crypto.cipher_group;
1272
1273 return err;
1274}
1275
1276static s32
1277brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1278{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001279 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001280 struct brcmf_cfg80211_security *sec;
1281 s32 val = 0;
1282 s32 err = 0;
1283
1284 if (sme->crypto.n_akm_suites) {
Arend van Sprielac24be62012-10-22 10:36:23 -07001285 err = brcmf_fil_iovar_int_get(netdev_priv(ndev),
1286 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001287 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001288 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001289 return err;
1290 }
1291 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1292 switch (sme->crypto.akm_suites[0]) {
1293 case WLAN_AKM_SUITE_8021X:
1294 val = WPA_AUTH_UNSPECIFIED;
1295 break;
1296 case WLAN_AKM_SUITE_PSK:
1297 val = WPA_AUTH_PSK;
1298 break;
1299 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001300 brcmf_err("invalid cipher group (%d)\n",
1301 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001302 return -EINVAL;
1303 }
1304 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1305 switch (sme->crypto.akm_suites[0]) {
1306 case WLAN_AKM_SUITE_8021X:
1307 val = WPA2_AUTH_UNSPECIFIED;
1308 break;
1309 case WLAN_AKM_SUITE_PSK:
1310 val = WPA2_AUTH_PSK;
1311 break;
1312 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001313 brcmf_err("invalid cipher group (%d)\n",
1314 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001315 return -EINVAL;
1316 }
1317 }
1318
Arend van Spriel16886732012-12-05 15:26:04 +01001319 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Arend van Sprielac24be62012-10-22 10:36:23 -07001320 err = brcmf_fil_iovar_int_set(netdev_priv(ndev),
1321 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001322 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001323 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001324 return err;
1325 }
1326 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001327 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001328 sec->wpa_auth = sme->crypto.akm_suites[0];
1329
1330 return err;
1331}
1332
1333static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001334brcmf_set_sharedkey(struct net_device *ndev,
1335 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001336{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001337 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001338 struct brcmf_cfg80211_security *sec;
1339 struct brcmf_wsec_key key;
1340 s32 val;
1341 s32 err = 0;
1342
Arend van Spriel16886732012-12-05 15:26:04 +01001343 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001344
Roland Vossena718e2f2011-10-12 20:51:24 +02001345 if (sme->key_len == 0)
1346 return 0;
1347
Arend van Spriel06bb1232012-09-27 14:17:56 +02001348 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001349 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1350 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001351
1352 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1353 return 0;
1354
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001355 if (!(sec->cipher_pairwise &
1356 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1357 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001358
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001359 memset(&key, 0, sizeof(key));
1360 key.len = (u32) sme->key_len;
1361 key.index = (u32) sme->key_idx;
1362 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001363 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001364 return -EINVAL;
1365 }
1366 memcpy(key.data, sme->key, key.len);
1367 key.flags = BRCMF_PRIMARY_KEY;
1368 switch (sec->cipher_pairwise) {
1369 case WLAN_CIPHER_SUITE_WEP40:
1370 key.algo = CRYPTO_ALGO_WEP1;
1371 break;
1372 case WLAN_CIPHER_SUITE_WEP104:
1373 key.algo = CRYPTO_ALGO_WEP128;
1374 break;
1375 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001376 brcmf_err("Invalid algorithm (%d)\n",
1377 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001378 return -EINVAL;
1379 }
1380 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001381 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1382 key.len, key.index, key.algo);
1383 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001384 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001385 if (err)
1386 return err;
1387
1388 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001389 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001390 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001391 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001392 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001393 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001394 }
1395 return err;
1396}
1397
1398static s32
1399brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
1400 struct cfg80211_connect_params *sme)
1401{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001402 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001403 struct brcmf_if *ifp = netdev_priv(ndev);
1404 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001405 struct ieee80211_channel *chan = sme->channel;
1406 struct brcmf_join_params join_params;
1407 size_t join_params_size;
1408 struct brcmf_ssid ssid;
1409
1410 s32 err = 0;
1411
Arend van Sprield96b8012012-12-05 15:26:02 +01001412 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001413 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001414 return -EIO;
1415
1416 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001417 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001418 return -EOPNOTSUPP;
1419 }
1420
Arend van Sprielc1179032012-10-22 13:55:33 -07001421 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001422
1423 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001424 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001425 ieee80211_frequency_to_channel(chan->center_freq);
Arend van Spriel16886732012-12-05 15:26:04 +01001426 brcmf_dbg(CONN, "channel (%d), center_req (%d)\n",
1427 cfg->channel, chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001428 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001429 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001430
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001431 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001432
1433 err = brcmf_set_wpa_version(ndev, sme);
1434 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001435 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001436 goto done;
1437 }
1438
1439 err = brcmf_set_auth_type(ndev, sme);
1440 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001441 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001442 goto done;
1443 }
1444
1445 err = brcmf_set_set_cipher(ndev, sme);
1446 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001447 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001448 goto done;
1449 }
1450
1451 err = brcmf_set_key_mgmt(ndev, sme);
1452 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001453 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001454 goto done;
1455 }
1456
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001457 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001458 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001459 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001460 goto done;
1461 }
1462
1463 memset(&join_params, 0, sizeof(join_params));
1464 join_params_size = sizeof(join_params.ssid_le);
1465
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001466 profile->ssid.SSID_len = min_t(u32,
1467 sizeof(ssid.SSID), (u32)sme->ssid_len);
1468 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
1469 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1470 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001471
Arend van Sprielba40d162012-10-22 13:55:38 -07001472 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001473
1474 if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
Arend van Spriel16886732012-12-05 15:26:04 +01001475 brcmf_dbg(CONN, "ssid \"%s\", len (%d)\n",
1476 ssid.SSID, ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001477
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001478 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001479 &join_params, &join_params_size);
Arend van Sprielc1179032012-10-22 13:55:33 -07001480 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001481 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001482 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001483 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001484
1485done:
1486 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001487 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001488 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001489 return err;
1490}
1491
1492static s32
1493brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1494 u16 reason_code)
1495{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001496 struct brcmf_if *ifp = netdev_priv(ndev);
1497 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001498 struct brcmf_scb_val_le scbval;
1499 s32 err = 0;
1500
Arend van Sprield96b8012012-12-05 15:26:02 +01001501 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001502 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001503 return -EIO;
1504
Arend van Sprielc1179032012-10-22 13:55:33 -07001505 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001506
Arend van Spriel06bb1232012-09-27 14:17:56 +02001507 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001508 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001509 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001510 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001511 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001512 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001513
Arend van Sprield96b8012012-12-05 15:26:02 +01001514 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001515 return err;
1516}
1517
1518static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001519brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001520 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001521{
1522
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001523 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001524 struct net_device *ndev = cfg_to_ndev(cfg);
1525 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001526 u16 txpwrmw;
1527 s32 err = 0;
1528 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001529 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001530
Arend van Sprield96b8012012-12-05 15:26:02 +01001531 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001532 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001533 return -EIO;
1534
1535 switch (type) {
1536 case NL80211_TX_POWER_AUTOMATIC:
1537 break;
1538 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001539 case NL80211_TX_POWER_FIXED:
1540 if (dbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001541 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001542 err = -EINVAL;
1543 goto done;
1544 }
1545 break;
1546 }
1547 /* Make sure radio is off or on as far as software is concerned */
1548 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001549 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001550 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001551 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001552
1553 if (dbm > 0xffff)
1554 txpwrmw = 0xffff;
1555 else
1556 txpwrmw = (u16) dbm;
Arend van Sprielac24be62012-10-22 10:36:23 -07001557 err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
1558 (s32)brcmf_mw_to_qdbm(txpwrmw));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001559 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001560 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001561 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001562
1563done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001564 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001565 return err;
1566}
1567
Johannes Bergc8442112012-10-24 10:17:18 +02001568static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
1569 struct wireless_dev *wdev,
1570 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001571{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001572 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001573 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001574 s32 txpwrdbm;
1575 u8 result;
1576 s32 err = 0;
1577
Arend van Sprield96b8012012-12-05 15:26:02 +01001578 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001579 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001580 return -EIO;
1581
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001582 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001583 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001584 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001585 goto done;
1586 }
1587
1588 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001589 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001590
1591done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001592 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001593 return err;
1594}
1595
1596static s32
1597brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1598 u8 key_idx, bool unicast, bool multicast)
1599{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001600 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001601 u32 index;
1602 u32 wsec;
1603 s32 err = 0;
1604
Arend van Sprield96b8012012-12-05 15:26:02 +01001605 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01001606 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001607 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001608 return -EIO;
1609
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001610 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001611 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001612 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001613 goto done;
1614 }
1615
1616 if (wsec & WEP_ENABLED) {
1617 /* Just select a new current key */
1618 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001619 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001620 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001621 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001622 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001623 }
1624done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001625 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001626 return err;
1627}
1628
1629static s32
1630brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
1631 u8 key_idx, const u8 *mac_addr, struct key_params *params)
1632{
1633 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001634 s32 err = 0;
1635
1636 memset(&key, 0, sizeof(key));
1637 key.index = (u32) key_idx;
1638 /* Instead of bcast for ea address for default wep keys,
1639 driver needs it to be Null */
1640 if (!is_multicast_ether_addr(mac_addr))
1641 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
1642 key.len = (u32) params->key_len;
1643 /* check for key index change */
1644 if (key.len == 0) {
1645 /* key delete */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001646 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001647 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001648 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001649 } else {
1650 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001651 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001652 return -EINVAL;
1653 }
1654
Arend van Spriel16886732012-12-05 15:26:04 +01001655 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001656 memcpy(key.data, params->key, key.len);
1657
1658 if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
1659 u8 keybuf[8];
1660 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1661 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1662 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1663 }
1664
1665 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
1666 if (params->seq && params->seq_len == 6) {
1667 /* rx iv */
1668 u8 *ivptr;
1669 ivptr = (u8 *) params->seq;
1670 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
1671 (ivptr[3] << 8) | ivptr[2];
1672 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
1673 key.iv_initialized = true;
1674 }
1675
1676 switch (params->cipher) {
1677 case WLAN_CIPHER_SUITE_WEP40:
1678 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01001679 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001680 break;
1681 case WLAN_CIPHER_SUITE_WEP104:
1682 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01001683 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001684 break;
1685 case WLAN_CIPHER_SUITE_TKIP:
1686 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01001687 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001688 break;
1689 case WLAN_CIPHER_SUITE_AES_CMAC:
1690 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01001691 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001692 break;
1693 case WLAN_CIPHER_SUITE_CCMP:
1694 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01001695 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001696 break;
1697 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001698 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001699 return -EINVAL;
1700 }
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001701 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001702 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001703 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001704 }
1705 return err;
1706}
1707
1708static s32
1709brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1710 u8 key_idx, bool pairwise, const u8 *mac_addr,
1711 struct key_params *params)
1712{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001713 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001714 struct brcmf_wsec_key key;
1715 s32 val;
1716 s32 wsec;
1717 s32 err = 0;
1718 u8 keybuf[8];
1719
Arend van Sprield96b8012012-12-05 15:26:02 +01001720 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01001721 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001722 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001723 return -EIO;
1724
1725 if (mac_addr) {
Arend van Sprield96b8012012-12-05 15:26:02 +01001726 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001727 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
1728 }
1729 memset(&key, 0, sizeof(key));
1730
1731 key.len = (u32) params->key_len;
1732 key.index = (u32) key_idx;
1733
1734 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001735 brcmf_err("Too long key length (%u)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001736 err = -EINVAL;
1737 goto done;
1738 }
1739 memcpy(key.data, params->key, key.len);
1740
1741 key.flags = BRCMF_PRIMARY_KEY;
1742 switch (params->cipher) {
1743 case WLAN_CIPHER_SUITE_WEP40:
1744 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001745 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001746 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001747 break;
1748 case WLAN_CIPHER_SUITE_WEP104:
1749 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001750 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001751 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001752 break;
1753 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel128ce3b2012-11-28 21:44:12 +01001754 if (ifp->vif->mode != WL_MODE_AP) {
Arend van Spriel16886732012-12-05 15:26:04 +01001755 brcmf_dbg(CONN, "Swapping key\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02001756 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1757 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1758 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1759 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001760 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001761 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001762 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001763 break;
1764 case WLAN_CIPHER_SUITE_AES_CMAC:
1765 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001766 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001767 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001768 break;
1769 case WLAN_CIPHER_SUITE_CCMP:
1770 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001771 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001772 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001773 break;
1774 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001775 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001776 err = -EINVAL;
1777 goto done;
1778 }
1779
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001780 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001781 if (err)
1782 goto done;
1783
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001784 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001785 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001786 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001787 goto done;
1788 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001789 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001790 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001791 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001792 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001793 goto done;
1794 }
1795
Arend van Spriel5b435de2011-10-05 13:19:03 +02001796done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001797 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001798 return err;
1799}
1800
1801static s32
1802brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1803 u8 key_idx, bool pairwise, const u8 *mac_addr)
1804{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001805 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001806 struct brcmf_wsec_key key;
1807 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001808
Arend van Sprield96b8012012-12-05 15:26:02 +01001809 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001810 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001811 return -EIO;
1812
Hante Meuleman256c3742012-11-05 16:22:28 -08001813 if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
1814 /* we ignore this key index in this case */
Arend van Spriel57d6e912012-12-05 15:26:00 +01001815 brcmf_err("invalid key index (%d)\n", key_idx);
Hante Meuleman256c3742012-11-05 16:22:28 -08001816 return -EINVAL;
1817 }
1818
Arend van Spriel5b435de2011-10-05 13:19:03 +02001819 memset(&key, 0, sizeof(key));
1820
1821 key.index = (u32) key_idx;
1822 key.flags = BRCMF_PRIMARY_KEY;
1823 key.algo = CRYPTO_ALGO_OFF;
1824
Arend van Spriel16886732012-12-05 15:26:04 +01001825 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001826
1827 /* Set the new key/index */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001828 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001829
Arend van Sprield96b8012012-12-05 15:26:02 +01001830 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001831 return err;
1832}
1833
1834static s32
1835brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1836 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
1837 void (*callback) (void *cookie, struct key_params * params))
1838{
1839 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001840 struct brcmf_if *ifp = netdev_priv(ndev);
1841 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001842 struct brcmf_cfg80211_security *sec;
1843 s32 wsec;
1844 s32 err = 0;
1845
Arend van Sprield96b8012012-12-05 15:26:02 +01001846 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01001847 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001848 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001849 return -EIO;
1850
1851 memset(&params, 0, sizeof(params));
1852
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001853 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001854 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001855 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001856 /* Ignore this error, may happen during DISASSOC */
1857 err = -EAGAIN;
1858 goto done;
1859 }
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001860 switch (wsec & ~SES_OW_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001861 case WEP_ENABLED:
Arend van Spriel06bb1232012-09-27 14:17:56 +02001862 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001863 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
1864 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01001865 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001866 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
1867 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01001868 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001869 }
1870 break;
1871 case TKIP_ENABLED:
1872 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01001873 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001874 break;
1875 case AES_ENABLED:
1876 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01001877 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001878 break;
1879 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001880 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001881 err = -EINVAL;
1882 goto done;
1883 }
1884 callback(cookie, &params);
1885
1886done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001887 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001888 return err;
1889}
1890
1891static s32
1892brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
1893 struct net_device *ndev, u8 key_idx)
1894{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001895 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001896
1897 return -EOPNOTSUPP;
1898}
1899
1900static s32
1901brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02001902 u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001903{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001904 struct brcmf_if *ifp = netdev_priv(ndev);
1905 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001906 struct brcmf_scb_val_le scb_val;
1907 int rssi;
1908 s32 rate;
1909 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02001910 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001911 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001912
Arend van Sprield96b8012012-12-05 15:26:02 +01001913 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07001914 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001915 return -EIO;
1916
Arend van Spriel128ce3b2012-11-28 21:44:12 +01001917 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001918 memcpy(&sta_info_le, mac, ETH_ALEN);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001919 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07001920 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001921 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02001922 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001923 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02001924 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02001925 }
Hante Meuleman1a873342012-09-27 14:17:54 +02001926 sinfo->filled = STATION_INFO_INACTIVE_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001927 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
1928 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001929 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001930 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02001931 }
Arend van Sprield96b8012012-12-05 15:26:02 +01001932 brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
1933 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel128ce3b2012-11-28 21:44:12 +01001934 } else if (ifp->vif->mode == WL_MODE_BSS) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001935 if (memcmp(mac, bssid, ETH_ALEN)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001936 brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
1937 mac, bssid);
Hante Meuleman1a873342012-09-27 14:17:54 +02001938 err = -ENOENT;
1939 goto done;
1940 }
1941 /* Report the current tx rate */
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001942 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02001943 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001944 brcmf_err("Could not get rate (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02001945 goto done;
1946 } else {
1947 sinfo->filled |= STATION_INFO_TX_BITRATE;
1948 sinfo->txrate.legacy = rate * 5;
Arend van Spriel16886732012-12-05 15:26:04 +01001949 brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
Hante Meuleman1a873342012-09-27 14:17:54 +02001950 }
1951
Arend van Sprielc1179032012-10-22 13:55:33 -07001952 if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
1953 &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001954 memset(&scb_val, 0, sizeof(scb_val));
Arend van Sprielc1179032012-10-22 13:55:33 -07001955 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
1956 &scb_val, sizeof(scb_val));
Hante Meuleman1a873342012-09-27 14:17:54 +02001957 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001958 brcmf_err("Could not get rssi (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02001959 goto done;
1960 } else {
1961 rssi = le32_to_cpu(scb_val.val);
1962 sinfo->filled |= STATION_INFO_SIGNAL;
1963 sinfo->signal = rssi;
Arend van Spriel16886732012-12-05 15:26:04 +01001964 brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
Hante Meuleman1a873342012-09-27 14:17:54 +02001965 }
1966 }
1967 } else
1968 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001969done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001970 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 return err;
1972}
1973
1974static s32
1975brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
1976 bool enabled, s32 timeout)
1977{
1978 s32 pm;
1979 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001980 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07001981 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001982
Arend van Sprield96b8012012-12-05 15:26:02 +01001983 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001984
1985 /*
1986 * Powersave enable/disable request is coming from the
1987 * cfg80211 even before the interface is up. In that
1988 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001989 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02001990 * FW later while initializing the dongle
1991 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001992 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07001993 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001994
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001995 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001996 goto done;
1997 }
1998
1999 pm = enabled ? PM_FAST : PM_OFF;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002000 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002001
Arend van Sprielc1179032012-10-22 13:55:33 -07002002 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002003 if (err) {
2004 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002005 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002006 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002007 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002008 }
2009done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002010 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002011 return err;
2012}
2013
2014static s32
2015brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
2016 const u8 *addr,
2017 const struct cfg80211_bitrate_mask *mask)
2018{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002019 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002020 struct brcm_rateset_le rateset_le;
2021 s32 rate;
2022 s32 val;
2023 s32 err_bg;
2024 s32 err_a;
2025 u32 legacy;
2026 s32 err = 0;
2027
Arend van Sprield96b8012012-12-05 15:26:02 +01002028 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002029 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002030 return -EIO;
2031
2032 /* addr param is always NULL. ignore it */
2033 /* Get current rateset */
Hante Meulemanb87e2c42012-11-14 18:46:23 -08002034 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CURR_RATESET,
Arend van Sprielac24be62012-10-22 10:36:23 -07002035 &rateset_le, sizeof(rateset_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002036 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002037 brcmf_err("could not get current rateset (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002038 goto done;
2039 }
2040
2041 legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
2042 if (!legacy)
2043 legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
2044 0xFFFF);
2045
2046 val = wl_g_rates[legacy - 1].bitrate * 100000;
2047
2048 if (val < le32_to_cpu(rateset_le.count))
2049 /* Select rate by rateset index */
2050 rate = rateset_le.rates[val] & 0x7f;
2051 else
2052 /* Specified rate in bps */
2053 rate = val / 500000;
2054
Arend van Spriel16886732012-12-05 15:26:04 +01002055 brcmf_dbg(CONN, "rate %d mbps\n", rate / 2);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002056
2057 /*
2058 *
2059 * Set rate override,
2060 * Since the is a/b/g-blind, both a/bg_rate are enforced.
2061 */
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002062 err_bg = brcmf_fil_iovar_int_set(ifp, "bg_rate", rate);
2063 err_a = brcmf_fil_iovar_int_set(ifp, "a_rate", rate);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002064 if (err_bg && err_a) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002065 brcmf_err("could not set fixed rate (%d) (%d)\n", err_bg,
2066 err_a);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002067 err = err_bg | err_a;
2068 }
2069
2070done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002071 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002072 return err;
2073}
2074
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002075static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002076 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002077{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002078 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002079 struct ieee80211_channel *notify_channel;
2080 struct cfg80211_bss *bss;
2081 struct ieee80211_supported_band *band;
2082 s32 err = 0;
2083 u16 channel;
2084 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002085 u16 notify_capability;
2086 u16 notify_interval;
2087 u8 *notify_ie;
2088 size_t notify_ielen;
2089 s32 notify_signal;
2090
2091 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002092 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002093 return 0;
2094 }
2095
2096 channel = bi->ctl_ch ? bi->ctl_ch :
2097 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2098
2099 if (channel <= CH_MAX_2G_CHANNEL)
2100 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2101 else
2102 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2103
2104 freq = ieee80211_channel_to_frequency(channel, band->band);
2105 notify_channel = ieee80211_get_channel(wiphy, freq);
2106
Arend van Spriel5b435de2011-10-05 13:19:03 +02002107 notify_capability = le16_to_cpu(bi->capability);
2108 notify_interval = le16_to_cpu(bi->beacon_period);
2109 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2110 notify_ielen = le32_to_cpu(bi->ie_length);
2111 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2112
Arend van Spriel16886732012-12-05 15:26:04 +01002113 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2114 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2115 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2116 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2117 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002118
2119 bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002120 0, notify_capability, notify_interval, notify_ie,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002121 notify_ielen, notify_signal, GFP_KERNEL);
2122
Franky Line78946e2011-11-10 20:30:34 +01002123 if (!bss)
2124 return -ENOMEM;
2125
2126 cfg80211_put_bss(bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002127
2128 return err;
2129}
2130
Roland Vossen6f09be02011-10-18 14:03:02 +02002131static struct brcmf_bss_info_le *
2132next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2133{
2134 if (bss == NULL)
2135 return list->bss_info_le;
2136 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2137 le32_to_cpu(bss->length));
2138}
2139
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002140static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002141{
2142 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002143 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002144 s32 err = 0;
2145 int i;
2146
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002147 bss_list = cfg->bss_list;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002148 if (bss_list->count != 0 &&
2149 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002150 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2151 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002152 return -EOPNOTSUPP;
2153 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002154 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002155 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002156 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002157 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002158 if (err)
2159 break;
2160 }
2161 return err;
2162}
2163
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002164static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002165 struct net_device *ndev, const u8 *bssid)
2166{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002167 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002168 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002169 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002170 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002171 struct cfg80211_bss *bss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002172 u8 *buf = NULL;
2173 s32 err = 0;
2174 u16 channel;
2175 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002176 u16 notify_capability;
2177 u16 notify_interval;
2178 u8 *notify_ie;
2179 size_t notify_ielen;
2180 s32 notify_signal;
2181
Arend van Sprield96b8012012-12-05 15:26:02 +01002182 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002183
2184 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2185 if (buf == NULL) {
2186 err = -ENOMEM;
2187 goto CleanUp;
2188 }
2189
2190 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2191
Arend van Sprielac24be62012-10-22 10:36:23 -07002192 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2193 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002194 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002195 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002196 goto CleanUp;
2197 }
2198
Roland Vossend34bf642011-10-18 14:03:01 +02002199 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002200
2201 channel = bi->ctl_ch ? bi->ctl_ch :
2202 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2203
2204 if (channel <= CH_MAX_2G_CHANNEL)
2205 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2206 else
2207 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2208
2209 freq = ieee80211_channel_to_frequency(channel, band->band);
2210 notify_channel = ieee80211_get_channel(wiphy, freq);
2211
Arend van Spriel5b435de2011-10-05 13:19:03 +02002212 notify_capability = le16_to_cpu(bi->capability);
2213 notify_interval = le16_to_cpu(bi->beacon_period);
2214 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2215 notify_ielen = le32_to_cpu(bi->ie_length);
2216 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2217
Arend van Spriel16886732012-12-05 15:26:04 +01002218 brcmf_dbg(CONN, "channel: %d(%d)\n", channel, freq);
2219 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2220 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2221 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002222
Franky Line78946e2011-11-10 20:30:34 +01002223 bss = cfg80211_inform_bss(wiphy, notify_channel, bssid,
Johannes Berg8e6cffb2012-03-13 13:57:03 +01002224 0, notify_capability, notify_interval,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002225 notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
2226
Franky Line78946e2011-11-10 20:30:34 +01002227 if (!bss) {
2228 err = -ENOMEM;
2229 goto CleanUp;
2230 }
2231
2232 cfg80211_put_bss(bss);
2233
Arend van Spriel5b435de2011-10-05 13:19:03 +02002234CleanUp:
2235
2236 kfree(buf);
2237
Arend van Sprield96b8012012-12-05 15:26:02 +01002238 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002239
2240 return err;
2241}
2242
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002243static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002244{
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002245 return vif->mode == WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002246}
2247
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002248/*
2249 * Traverse a string of 1-byte tag/1-byte length/variable-length value
2250 * triples, returning a pointer to the substring whose first element
2251 * matches tag
2252 */
2253static struct brcmf_tlv *brcmf_parse_tlvs(void *buf, int buflen, uint key)
2254{
2255 struct brcmf_tlv *elt;
2256 int totlen;
2257
2258 elt = (struct brcmf_tlv *) buf;
2259 totlen = buflen;
2260
2261 /* find tagged parameter */
Hante Meuleman04012892012-09-27 14:17:49 +02002262 while (totlen >= TLV_HDR_LEN) {
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002263 int len = elt->len;
2264
2265 /* validate remaining totlen */
Hante Meuleman04012892012-09-27 14:17:49 +02002266 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002267 return elt;
2268
Hante Meuleman04012892012-09-27 14:17:49 +02002269 elt = (struct brcmf_tlv *) ((u8 *) elt + (len + TLV_HDR_LEN));
2270 totlen -= (len + TLV_HDR_LEN);
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002271 }
2272
2273 return NULL;
2274}
2275
Hante Meuleman1a873342012-09-27 14:17:54 +02002276/* Is any of the tlvs the expected entry? If
2277 * not update the tlvs buffer pointer/length.
2278 */
2279static bool
2280brcmf_tlv_has_ie(u8 *ie, u8 **tlvs, u32 *tlvs_len,
2281 u8 *oui, u32 oui_len, u8 type)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002282{
Hante Meuleman1a873342012-09-27 14:17:54 +02002283 /* If the contents match the OUI and the type */
2284 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
2285 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
2286 type == ie[TLV_BODY_OFF + oui_len]) {
2287 return true;
2288 }
2289
2290 if (tlvs == NULL)
2291 return false;
2292 /* point to the next ie */
2293 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
2294 /* calculate the length of the rest of the buffer */
2295 *tlvs_len -= (int)(ie - *tlvs);
2296 /* update the pointer to the start of the buffer */
2297 *tlvs = ie;
2298
2299 return false;
2300}
2301
Franky Lin3cb91f52012-10-10 11:13:08 -07002302static struct brcmf_vs_tlv *
Hante Meuleman1a873342012-09-27 14:17:54 +02002303brcmf_find_wpaie(u8 *parse, u32 len)
2304{
2305 struct brcmf_tlv *ie;
2306
Arend van Spriel04b23122012-10-12 12:28:14 +02002307 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002308 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
2309 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
2310 return (struct brcmf_vs_tlv *)ie;
2311 }
2312 return NULL;
2313}
2314
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002315static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002316{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07002317 struct net_device *ndev = cfg_to_ndev(cfg);
2318 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
2319 struct brcmf_if *ifp = netdev_priv(ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002320 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002321 struct brcmf_ssid *ssid;
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002322 struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002323 u16 beacon_interval;
2324 u8 dtim_period;
2325 size_t ie_len;
2326 u8 *ie;
2327 s32 err = 0;
2328
Arend van Sprield96b8012012-12-05 15:26:02 +01002329 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002330 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002331 return err;
2332
Arend van Spriel06bb1232012-09-27 14:17:56 +02002333 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002334
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002335 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002336 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002337 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002338 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002339 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002340 goto update_bss_info_out;
2341 }
2342
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002343 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2344 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002345 if (err)
2346 goto update_bss_info_out;
2347
2348 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2349 ie_len = le32_to_cpu(bi->ie_length);
2350 beacon_interval = le16_to_cpu(bi->beacon_period);
2351
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002352 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002353 if (tim)
2354 dtim_period = tim->data[1];
2355 else {
2356 /*
2357 * active scan was done so we could not get dtim
2358 * information out of probe response.
2359 * so we speficially query dtim information to dongle.
2360 */
2361 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002362 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002363 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002364 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002365 goto update_bss_info_out;
2366 }
2367 dtim_period = (u8)var;
2368 }
2369
Arend van Spriel5b435de2011-10-05 13:19:03 +02002370update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002371 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002372 return err;
2373}
2374
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002375static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002376{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002377 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002378
Arend van Sprielc1179032012-10-22 13:55:33 -07002379 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002380 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002381 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002382 brcmf_notify_escan_complete(cfg, escan->ndev, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002383 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002384 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2385 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002386}
2387
Hante Meulemane756af52012-09-11 21:18:52 +02002388static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2389{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002390 struct brcmf_cfg80211_info *cfg =
2391 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002392 escan_timeout_work);
2393
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002394 brcmf_notify_escan_complete(cfg,
2395 cfg->escan_info.ndev, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002396}
2397
2398static void brcmf_escan_timeout(unsigned long data)
2399{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002400 struct brcmf_cfg80211_info *cfg =
2401 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002402
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002403 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002404 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002405 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002406 }
2407}
2408
2409static s32
2410brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss,
2411 struct brcmf_bss_info_le *bss_info_le)
2412{
2413 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
2414 (CHSPEC_BAND(le16_to_cpu(bss_info_le->chanspec)) ==
2415 CHSPEC_BAND(le16_to_cpu(bss->chanspec))) &&
2416 bss_info_le->SSID_len == bss->SSID_len &&
2417 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
2418 if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) ==
2419 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002420 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2421 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2422
Hante Meulemane756af52012-09-11 21:18:52 +02002423 /* preserve max RSSI if the measurements are
2424 * both on-channel or both off-channel
2425 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002426 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002427 bss->RSSI = bss_info_le->RSSI;
2428 } else if ((bss->flags & WLC_BSS_RSSI_ON_CHANNEL) &&
2429 (bss_info_le->flags & WLC_BSS_RSSI_ON_CHANNEL) == 0) {
2430 /* preserve the on-channel rssi measurement
2431 * if the new measurement is off channel
2432 */
2433 bss->RSSI = bss_info_le->RSSI;
2434 bss->flags |= WLC_BSS_RSSI_ON_CHANNEL;
2435 }
2436 return 1;
2437 }
2438 return 0;
2439}
2440
2441static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002442brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002443 const struct brcmf_event_msg *e, void *data)
2444{
Arend van Spriel19937322012-11-05 16:22:32 -08002445 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
2446 struct net_device *ndev = ifp->ndev;
Hante Meulemane756af52012-09-11 21:18:52 +02002447 s32 status;
2448 s32 err = 0;
2449 struct brcmf_escan_result_le *escan_result_le;
2450 struct brcmf_bss_info_le *bss_info_le;
2451 struct brcmf_bss_info_le *bss = NULL;
2452 u32 bi_length;
2453 struct brcmf_scan_results *list;
2454 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002455 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002456
Arend van Spriel5c36b992012-11-14 18:46:05 -08002457 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002458
Hante Meulemanf07998952012-11-05 16:22:13 -08002459 if (!ndev || !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002460 brcmf_err("scan not ready ndev %p drv_status %x\n", ndev,
2461 !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status));
Hante Meulemane756af52012-09-11 21:18:52 +02002462 return -EPERM;
2463 }
2464
2465 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002466 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002467 escan_result_le = (struct brcmf_escan_result_le *) data;
2468 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002469 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002470 goto exit;
2471 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002472 if (!cfg->scan_request) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002473 brcmf_dbg(SCAN, "result without cfg80211 request\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002474 goto exit;
2475 }
2476
2477 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002478 brcmf_err("Invalid bss_count %d: ignoring\n",
2479 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002480 goto exit;
2481 }
2482 bss_info_le = &escan_result_le->bss_info_le;
2483
2484 bi_length = le32_to_cpu(bss_info_le->length);
2485 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2486 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002487 brcmf_err("Invalid bss_info length %d: ignoring\n",
2488 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002489 goto exit;
2490 }
2491
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002492 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002493 BIT(NL80211_IFTYPE_ADHOC))) {
2494 if (le16_to_cpu(bss_info_le->capability) &
2495 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002496 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002497 goto exit;
2498 }
2499 }
2500
2501 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002502 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002503 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002504 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002505 goto exit;
2506 }
2507
2508 for (i = 0; i < list->count; i++) {
2509 bss = bss ? (struct brcmf_bss_info_le *)
2510 ((unsigned char *)bss +
2511 le32_to_cpu(bss->length)) : list->bss_info_le;
2512 if (brcmf_compare_update_same_bss(bss, bss_info_le))
2513 goto exit;
2514 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002515 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002516 bss_info_le, bi_length);
2517 list->version = le32_to_cpu(bss_info_le->version);
2518 list->buflen += bi_length;
2519 list->count++;
2520 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002521 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2522 if (cfg->scan_request) {
2523 cfg->bss_list = (struct brcmf_scan_results *)
2524 cfg->escan_info.escan_buf;
2525 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002526 aborted = status != BRCMF_E_STATUS_SUCCESS;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002527 brcmf_notify_escan_complete(cfg, ndev, aborted,
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002528 false);
Hante Meulemane756af52012-09-11 21:18:52 +02002529 } else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002530 brcmf_err("Unexpected scan result 0x%x\n", status);
Hante Meulemane756af52012-09-11 21:18:52 +02002531 }
2532exit:
2533 return err;
2534}
2535
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002536static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002537{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002538 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
2539 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08002540 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2541 /* Init scan_timeout timer */
2542 init_timer(&cfg->escan_timeout);
2543 cfg->escan_timeout.data = (unsigned long) cfg;
2544 cfg->escan_timeout.function = brcmf_escan_timeout;
2545 INIT_WORK(&cfg->escan_timeout_work,
2546 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002547}
2548
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002549static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002550{
2551 if (ms < 1000 / HZ) {
2552 cond_resched();
2553 mdelay(ms);
2554 } else {
2555 msleep(ms);
2556 }
2557}
2558
2559static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
2560{
Arend van Sprield96b8012012-12-05 15:26:02 +01002561 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002562
Arend van Spriel5b435de2011-10-05 13:19:03 +02002563 return 0;
2564}
2565
2566static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
2567 struct cfg80211_wowlan *wow)
2568{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002569 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2570 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel7d641072012-10-22 13:55:39 -07002571 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002572
Arend van Sprield96b8012012-12-05 15:26:02 +01002573 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002574
2575 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002576 * if the primary net_device is not READY there is nothing
2577 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02002578 */
Arend van Spriel7d641072012-10-22 13:55:39 -07002579 vif = ((struct brcmf_if *)netdev_priv(ndev))->vif;
2580 if (!check_vif_up(vif))
2581 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002582
Arend van Spriel7d641072012-10-22 13:55:39 -07002583 list_for_each_entry(vif, &cfg->vif_list, list) {
2584 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
2585 continue;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002586 /*
Arend van Spriel7d641072012-10-22 13:55:39 -07002587 * While going to suspend if associated with AP disassociate
2588 * from AP to save power while system is in suspended state
Arend van Spriel5b435de2011-10-05 13:19:03 +02002589 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01002590 brcmf_link_down(vif);
Arend van Spriel7d641072012-10-22 13:55:39 -07002591
Arend van Spriel903e0ee2012-11-28 21:44:11 +01002592 /* Make sure WPA_Supplicant receives all the event
2593 * generated due to DISASSOC call to the fw to keep
2594 * the state fw and WPA_Supplicant state consistent
2595 */
2596 brcmf_delay(500);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002597 }
2598
Arend van Spriel7d641072012-10-22 13:55:39 -07002599 /* end any scanning */
2600 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002601 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002602
2603 /* Turn off watchdog timer */
Arend van Spriel7d641072012-10-22 13:55:39 -07002604 brcmf_set_mpc(ndev, 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002605
Arend van Spriel7d641072012-10-22 13:55:39 -07002606exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01002607 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07002608 /* clear any scanning activity */
2609 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002610 return 0;
2611}
2612
2613static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002614brcmf_update_pmklist(struct net_device *ndev,
2615 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
2616{
2617 int i, j;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002618 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002619
Arend van Spriel40c8e952011-10-12 20:51:20 +02002620 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
2621
Arend van Spriel16886732012-12-05 15:26:04 +01002622 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002623 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01002624 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
2625 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002626 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01002627 brcmf_dbg(CONN, "%02x\n",
2628 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002629 }
2630
2631 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07002632 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
2633 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002634
2635 return err;
2636}
2637
2638static s32
2639brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2640 struct cfg80211_pmksa *pmksa)
2641{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002642 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002643 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002644 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002645 s32 err = 0;
2646 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002647 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002648
Arend van Sprield96b8012012-12-05 15:26:02 +01002649 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002650 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002651 return -EIO;
2652
Arend van Spriel40c8e952011-10-12 20:51:20 +02002653 pmkid_len = le32_to_cpu(pmkids->npmkid);
2654 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002655 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
2656 break;
2657 if (i < WL_NUM_PMKIDS_MAX) {
2658 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
2659 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002660 if (i == pmkid_len) {
2661 pmkid_len++;
2662 pmkids->npmkid = cpu_to_le32(pmkid_len);
2663 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002664 } else
2665 err = -EINVAL;
2666
Arend van Spriel16886732012-12-05 15:26:04 +01002667 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
2668 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002669 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01002670 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002671
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002672 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002673
Arend van Sprield96b8012012-12-05 15:26:02 +01002674 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002675 return err;
2676}
2677
2678static s32
2679brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2680 struct cfg80211_pmksa *pmksa)
2681{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002682 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002683 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002684 struct pmkid_list pmkid;
2685 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002686 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002687
Arend van Sprield96b8012012-12-05 15:26:02 +01002688 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002689 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002690 return -EIO;
2691
2692 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
2693 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
2694
Arend van Spriel16886732012-12-05 15:26:04 +01002695 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
2696 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002697 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01002698 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002699
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002700 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002701 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002702 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002703 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002704 ETH_ALEN))
2705 break;
2706
Arend van Spriel40c8e952011-10-12 20:51:20 +02002707 if ((pmkid_len > 0)
2708 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002709 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002710 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02002711 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002712 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
2713 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002714 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002715 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
2716 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002717 WLAN_PMKID_LEN);
2718 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002719 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002720 } else
2721 err = -EINVAL;
2722
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002723 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002724
Arend van Sprield96b8012012-12-05 15:26:02 +01002725 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002726 return err;
2727
2728}
2729
2730static s32
2731brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
2732{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002733 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002734 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002735 s32 err = 0;
2736
Arend van Sprield96b8012012-12-05 15:26:02 +01002737 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002738 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002739 return -EIO;
2740
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002741 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
2742 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002743
Arend van Sprield96b8012012-12-05 15:26:02 +01002744 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002745 return err;
2746
2747}
2748
Arend van Spriele5806072012-09-19 22:21:08 +02002749/*
2750 * PFN result doesn't have all the info which are
2751 * required by the supplicant
2752 * (For e.g IEs) Do a target Escan so that sched scan results are reported
2753 * via wl_inform_single_bss in the required format. Escan does require the
2754 * scan request in the form of cfg80211_scan_request. For timebeing, create
2755 * cfg80211_scan_request one out of the received PNO event.
2756 */
2757static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002758brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02002759 const struct brcmf_event_msg *e, void *data)
2760{
Arend van Spriel19937322012-11-05 16:22:32 -08002761 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
2762 struct net_device *ndev = ifp->ndev;
Arend van Spriele5806072012-09-19 22:21:08 +02002763 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
2764 struct cfg80211_scan_request *request = NULL;
2765 struct cfg80211_ssid *ssid = NULL;
2766 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002767 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002768 int err = 0;
2769 int channel_req = 0;
2770 int band = 0;
2771 struct brcmf_pno_scanresults_le *pfn_result;
2772 u32 result_count;
2773 u32 status;
2774
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002775 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02002776
Arend van Spriel5c36b992012-11-14 18:46:05 -08002777 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002778 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02002779 return 0;
2780 }
2781
2782 pfn_result = (struct brcmf_pno_scanresults_le *)data;
2783 result_count = le32_to_cpu(pfn_result->count);
2784 status = le32_to_cpu(pfn_result->status);
2785
2786 /*
2787 * PFN event is limited to fit 512 bytes so we may get
2788 * multiple NET_FOUND events. For now place a warning here.
2789 */
2790 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002791 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02002792 if (result_count > 0) {
2793 int i;
2794
2795 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03002796 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
2797 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02002798 if (!request || !ssid || !channel) {
2799 err = -ENOMEM;
2800 goto out_err;
2801 }
2802
2803 request->wiphy = wiphy;
2804 data += sizeof(struct brcmf_pno_scanresults_le);
2805 netinfo_start = (struct brcmf_pno_net_info_le *)data;
2806
2807 for (i = 0; i < result_count; i++) {
2808 netinfo = &netinfo_start[i];
2809 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002810 brcmf_err("Invalid netinfo ptr. index: %d\n",
2811 i);
Arend van Spriele5806072012-09-19 22:21:08 +02002812 err = -EINVAL;
2813 goto out_err;
2814 }
2815
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002816 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
2817 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02002818 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
2819 ssid[i].ssid_len = netinfo->SSID_len;
2820 request->n_ssids++;
2821
2822 channel_req = netinfo->channel;
2823 if (channel_req <= CH_MAX_2G_CHANNEL)
2824 band = NL80211_BAND_2GHZ;
2825 else
2826 band = NL80211_BAND_5GHZ;
2827 channel[i].center_freq =
2828 ieee80211_channel_to_frequency(channel_req,
2829 band);
2830 channel[i].band = band;
2831 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
2832 request->channels[i] = &channel[i];
2833 request->n_channels++;
2834 }
2835
2836 /* assign parsed ssid array */
2837 if (request->n_ssids)
2838 request->ssids = &ssid[0];
2839
Arend van Sprielc1179032012-10-22 13:55:33 -07002840 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02002841 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002842 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002843 }
2844
Arend van Sprielc1179032012-10-22 13:55:33 -07002845 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002846 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriele5806072012-09-19 22:21:08 +02002847 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07002848 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02002849 goto out_err;
2850 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002851 cfg->sched_escan = true;
2852 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02002853 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002854 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02002855 goto out_err;
2856 }
2857
2858 kfree(ssid);
2859 kfree(channel);
2860 kfree(request);
2861 return 0;
2862
2863out_err:
2864 kfree(ssid);
2865 kfree(channel);
2866 kfree(request);
2867 cfg80211_sched_scan_stopped(wiphy);
2868 return err;
2869}
2870
Arend van Spriele5806072012-09-19 22:21:08 +02002871static int brcmf_dev_pno_clean(struct net_device *ndev)
2872{
Arend van Spriele5806072012-09-19 22:21:08 +02002873 int ret;
2874
2875 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002876 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002877 if (ret == 0) {
2878 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002879 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
2880 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002881 }
2882 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002883 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02002884
2885 return ret;
2886}
2887
2888static int brcmf_dev_pno_config(struct net_device *ndev)
2889{
2890 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02002891
2892 memset(&pfn_param, 0, sizeof(pfn_param));
2893 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
2894
2895 /* set extra pno params */
2896 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
2897 pfn_param.repeat = BRCMF_PNO_REPEAT;
2898 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
2899
2900 /* set up pno scan fr */
2901 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
2902
Arend van Sprielac24be62012-10-22 10:36:23 -07002903 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
2904 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02002905}
2906
2907static int
2908brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
2909 struct net_device *ndev,
2910 struct cfg80211_sched_scan_request *request)
2911{
Arend van Sprielc1179032012-10-22 13:55:33 -07002912 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002913 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02002914 struct brcmf_pno_net_param_le pfn;
2915 int i;
2916 int ret = 0;
2917
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002918 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
2919 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07002920 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002921 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02002922 return -EAGAIN;
2923 }
2924
2925 if (!request || !request->n_ssids || !request->n_match_sets) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002926 brcmf_err("Invalid sched scan req!! n_ssids:%d\n",
2927 request ? request->n_ssids : 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002928 return -EINVAL;
2929 }
2930
2931 if (request->n_ssids > 0) {
2932 for (i = 0; i < request->n_ssids; i++) {
2933 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002934 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
2935 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02002936
2937 /*
2938 * match_set ssids is a supert set of n_ssid list,
2939 * so we need not add these set seperately.
2940 */
2941 }
2942 }
2943
2944 if (request->n_match_sets > 0) {
2945 /* clean up everything */
2946 ret = brcmf_dev_pno_clean(ndev);
2947 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002948 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02002949 return ret;
2950 }
2951
2952 /* configure pno */
2953 ret = brcmf_dev_pno_config(ndev);
2954 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002955 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02002956 return -EINVAL;
2957 }
2958
2959 /* configure each match set */
2960 for (i = 0; i < request->n_match_sets; i++) {
2961 struct cfg80211_ssid *ssid;
2962 u32 ssid_len;
2963
2964 ssid = &request->match_sets[i].ssid;
2965 ssid_len = ssid->ssid_len;
2966
2967 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002968 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02002969 continue;
2970 }
2971 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
2972 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
2973 pfn.wsec = cpu_to_le32(0);
2974 pfn.infra = cpu_to_le32(1);
2975 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
2976 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
2977 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07002978 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07002979 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002980 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
2981 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02002982 }
2983 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07002984 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002985 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02002986 return -EINVAL;
2987 }
2988 } else {
2989 return -EINVAL;
2990 }
2991
2992 return 0;
2993}
2994
2995static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
2996 struct net_device *ndev)
2997{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002998 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02002999
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003000 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003001 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003002 if (cfg->sched_escan)
3003 brcmf_notify_escan_complete(cfg, ndev, true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003004 return 0;
3005}
Arend van Spriele5806072012-09-19 22:21:08 +02003006
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003007#ifdef CONFIG_NL80211_TESTMODE
3008static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3009{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003010 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003011 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003012 struct brcmf_dcmd *dcmd = data;
3013 struct sk_buff *reply;
3014 int ret;
3015
Arend van Sprield96b8012012-12-05 15:26:02 +01003016 brcmf_dbg(TRACE, "cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
3017 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003018
3019 if (dcmd->set)
Arend van Sprielac24be62012-10-22 10:36:23 -07003020 ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
3021 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003022 else
Arend van Sprielac24be62012-10-22 10:36:23 -07003023 ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
3024 dcmd->buf, dcmd->len);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003025 if (ret == 0) {
3026 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3027 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3028 ret = cfg80211_testmode_reply(reply);
3029 }
3030 return ret;
3031}
3032#endif
3033
Hante Meuleman1a873342012-09-27 14:17:54 +02003034static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
3035{
Arend van Sprielac24be62012-10-22 10:36:23 -07003036 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003037 s32 err;
3038
3039 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003040 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003041 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003042 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003043 return err;
3044 }
3045 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003046 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003047 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003048 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003049 return err;
3050 }
3051 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003052 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003053 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003054 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003055 return err;
3056 }
3057
3058 return 0;
3059}
3060
3061static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3062{
3063 if (is_rsn_ie)
3064 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3065
3066 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3067}
3068
3069static s32
3070brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
Arend van Spriel34778522012-11-05 16:22:19 -08003071 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003072{
Arend van Sprielac24be62012-10-22 10:36:23 -07003073 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003074 u32 auth = 0; /* d11 open authentication */
3075 u16 count;
3076 s32 err = 0;
3077 s32 len = 0;
3078 u32 i;
3079 u32 wsec;
3080 u32 pval = 0;
3081 u32 gval = 0;
3082 u32 wpa_auth = 0;
3083 u32 offset;
3084 u8 *data;
3085 u16 rsn_cap;
3086 u32 wme_bss_disable;
3087
Arend van Sprield96b8012012-12-05 15:26:02 +01003088 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003089 if (wpa_ie == NULL)
3090 goto exit;
3091
3092 len = wpa_ie->len + TLV_HDR_LEN;
3093 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003094 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003095 if (!is_rsn_ie)
3096 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003097 else
3098 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003099
3100 /* check for multicast cipher suite */
3101 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3102 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003103 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003104 goto exit;
3105 }
3106
3107 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3108 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003109 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003110 goto exit;
3111 }
3112 offset += TLV_OUI_LEN;
3113
3114 /* pick up multicast cipher */
3115 switch (data[offset]) {
3116 case WPA_CIPHER_NONE:
3117 gval = 0;
3118 break;
3119 case WPA_CIPHER_WEP_40:
3120 case WPA_CIPHER_WEP_104:
3121 gval = WEP_ENABLED;
3122 break;
3123 case WPA_CIPHER_TKIP:
3124 gval = TKIP_ENABLED;
3125 break;
3126 case WPA_CIPHER_AES_CCM:
3127 gval = AES_ENABLED;
3128 break;
3129 default:
3130 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003131 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003132 goto exit;
3133 }
3134
3135 offset++;
3136 /* walk thru unicast cipher list and pick up what we recognize */
3137 count = data[offset] + (data[offset + 1] << 8);
3138 offset += WPA_IE_SUITE_COUNT_LEN;
3139 /* Check for unicast suite(s) */
3140 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3141 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003142 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003143 goto exit;
3144 }
3145 for (i = 0; i < count; i++) {
3146 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3147 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003148 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003149 goto exit;
3150 }
3151 offset += TLV_OUI_LEN;
3152 switch (data[offset]) {
3153 case WPA_CIPHER_NONE:
3154 break;
3155 case WPA_CIPHER_WEP_40:
3156 case WPA_CIPHER_WEP_104:
3157 pval |= WEP_ENABLED;
3158 break;
3159 case WPA_CIPHER_TKIP:
3160 pval |= TKIP_ENABLED;
3161 break;
3162 case WPA_CIPHER_AES_CCM:
3163 pval |= AES_ENABLED;
3164 break;
3165 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003166 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003167 }
3168 offset++;
3169 }
3170 /* walk thru auth management suite list and pick up what we recognize */
3171 count = data[offset] + (data[offset + 1] << 8);
3172 offset += WPA_IE_SUITE_COUNT_LEN;
3173 /* Check for auth key management suite(s) */
3174 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3175 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003176 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003177 goto exit;
3178 }
3179 for (i = 0; i < count; i++) {
3180 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3181 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003182 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003183 goto exit;
3184 }
3185 offset += TLV_OUI_LEN;
3186 switch (data[offset]) {
3187 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003188 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003189 wpa_auth |= WPA_AUTH_NONE;
3190 break;
3191 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003192 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003193 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3194 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3195 break;
3196 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003197 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003198 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3199 (wpa_auth |= WPA_AUTH_PSK);
3200 break;
3201 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003202 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003203 }
3204 offset++;
3205 }
3206
3207 if (is_rsn_ie) {
3208 wme_bss_disable = 1;
3209 if ((offset + RSN_CAP_LEN) <= len) {
3210 rsn_cap = data[offset] + (data[offset + 1] << 8);
3211 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3212 wme_bss_disable = 0;
3213 }
3214 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003215 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003216 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003217 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003218 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003219 goto exit;
3220 }
3221 }
3222 /* FOR WPS , set SES_OW_ENABLED */
3223 wsec = (pval | gval | SES_OW_ENABLED);
3224
3225 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003226 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003227 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003228 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003229 goto exit;
3230 }
3231 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003232 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003233 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003234 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003235 goto exit;
3236 }
3237 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003238 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003239 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003240 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003241 goto exit;
3242 }
3243
3244exit:
3245 return err;
3246}
3247
3248static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003249brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003250 struct parsed_vndr_ies *vndr_ies)
3251{
3252 s32 err = 0;
3253 struct brcmf_vs_tlv *vndrie;
3254 struct brcmf_tlv *ie;
3255 struct parsed_vndr_ie_info *parsed_info;
3256 s32 remaining_len;
3257
3258 remaining_len = (s32)vndr_ie_len;
3259 memset(vndr_ies, 0, sizeof(*vndr_ies));
3260
3261 ie = (struct brcmf_tlv *)vndr_ie_buf;
3262 while (ie) {
3263 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3264 goto next;
3265 vndrie = (struct brcmf_vs_tlv *)ie;
3266 /* len should be bigger than OUI length + one */
3267 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003268 brcmf_err("invalid vndr ie. length is too small %d\n",
3269 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003270 goto next;
3271 }
3272 /* if wpa or wme ie, do not add ie */
3273 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3274 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3275 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003276 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003277 goto next;
3278 }
3279
3280 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3281
3282 /* save vndr ie information */
3283 parsed_info->ie_ptr = (char *)vndrie;
3284 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3285 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3286
3287 vndr_ies->count++;
3288
Arend van Sprield96b8012012-12-05 15:26:02 +01003289 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3290 parsed_info->vndrie.oui[0],
3291 parsed_info->vndrie.oui[1],
3292 parsed_info->vndrie.oui[2],
3293 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003294
3295 if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
3296 break;
3297next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003298 remaining_len -= (ie->len + TLV_HDR_LEN);
3299 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003300 ie = NULL;
3301 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003302 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3303 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003304 }
3305 return err;
3306}
3307
3308static u32
3309brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3310{
3311
3312 __le32 iecount_le;
3313 __le32 pktflag_le;
3314
3315 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3316 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3317
3318 iecount_le = cpu_to_le32(1);
3319 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3320
3321 pktflag_le = cpu_to_le32(pktflag);
3322 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3323
3324 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3325
3326 return ie_len + VNDR_IE_HDR_SIZE;
3327}
3328
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003329static
Arend van Spriel1332e262012-11-05 16:22:18 -08003330s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3331 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003332{
Arend van Spriel1332e262012-11-05 16:22:18 -08003333 struct brcmf_if *ifp;
3334 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003335 s32 err = 0;
3336 u8 *iovar_ie_buf;
3337 u8 *curr_ie_buf;
3338 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003339 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003340 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003341 u32 del_add_ie_buf_len = 0;
3342 u32 total_ie_buf_len = 0;
3343 u32 parsed_ie_buf_len = 0;
3344 struct parsed_vndr_ies old_vndr_ies;
3345 struct parsed_vndr_ies new_vndr_ies;
3346 struct parsed_vndr_ie_info *vndrie_info;
3347 s32 i;
3348 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003349 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003350
Arend van Spriel1332e262012-11-05 16:22:18 -08003351 if (!vif)
3352 return -ENODEV;
3353 ifp = vif->ifp;
3354 saved_ie = &vif->saved_ie;
3355
Arend van Sprield96b8012012-12-05 15:26:02 +01003356 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003357 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3358 if (!iovar_ie_buf)
3359 return -ENOMEM;
3360 curr_ie_buf = iovar_ie_buf;
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003361 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003362 switch (pktflag) {
3363 case VNDR_IE_PRBRSP_FLAG:
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003364 mgmt_ie_buf = saved_ie->probe_res_ie;
3365 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3366 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003367 break;
3368 case VNDR_IE_BEACON_FLAG:
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003369 mgmt_ie_buf = saved_ie->beacon_ie;
3370 mgmt_ie_len = &saved_ie->beacon_ie_len;
3371 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003372 break;
3373 default:
3374 err = -EPERM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003375 brcmf_err("not suitable type\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003376 goto exit;
3377 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003378 } else {
3379 err = -EPERM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003380 brcmf_err("not suitable type\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003381 goto exit;
3382 }
3383
3384 if (vndr_ie_len > mgmt_ie_buf_len) {
3385 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003386 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003387 goto exit;
3388 }
3389
3390 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3391 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3392 ptr = curr_ie_buf;
3393 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3394 for (i = 0; i < new_vndr_ies.count; i++) {
3395 vndrie_info = &new_vndr_ies.ie_info[i];
3396 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3397 vndrie_info->ie_len);
3398 parsed_ie_buf_len += vndrie_info->ie_len;
3399 }
3400 }
3401
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003402 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003403 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3404 (memcmp(mgmt_ie_buf, curr_ie_buf,
3405 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003406 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003407 goto exit;
3408 }
3409
3410 /* parse old vndr_ie */
3411 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3412
3413 /* make a command to delete old ie */
3414 for (i = 0; i < old_vndr_ies.count; i++) {
3415 vndrie_info = &old_vndr_ies.ie_info[i];
3416
Arend van Sprield96b8012012-12-05 15:26:02 +01003417 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3418 vndrie_info->vndrie.id,
3419 vndrie_info->vndrie.len,
3420 vndrie_info->vndrie.oui[0],
3421 vndrie_info->vndrie.oui[1],
3422 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003423
3424 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3425 vndrie_info->ie_ptr,
3426 vndrie_info->ie_len,
3427 "del");
3428 curr_ie_buf += del_add_ie_buf_len;
3429 total_ie_buf_len += del_add_ie_buf_len;
3430 }
3431 }
3432
3433 *mgmt_ie_len = 0;
3434 /* Add if there is any extra IE */
3435 if (mgmt_ie_buf && parsed_ie_buf_len) {
3436 ptr = mgmt_ie_buf;
3437
3438 remained_buf_len = mgmt_ie_buf_len;
3439
3440 /* make a command to add new ie */
3441 for (i = 0; i < new_vndr_ies.count; i++) {
3442 vndrie_info = &new_vndr_ies.ie_info[i];
3443
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003444 /* verify remained buf size before copy data */
3445 if (remained_buf_len < (vndrie_info->vndrie.len +
3446 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003447 brcmf_err("no space in mgmt_ie_buf: len left %d",
3448 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003449 break;
3450 }
3451 remained_buf_len -= (vndrie_info->ie_len +
3452 VNDR_IE_VSIE_OFFSET);
3453
Arend van Sprield96b8012012-12-05 15:26:02 +01003454 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3455 vndrie_info->vndrie.id,
3456 vndrie_info->vndrie.len,
3457 vndrie_info->vndrie.oui[0],
3458 vndrie_info->vndrie.oui[1],
3459 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003460
3461 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3462 vndrie_info->ie_ptr,
3463 vndrie_info->ie_len,
3464 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003465
3466 /* save the parsed IE in wl struct */
3467 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3468 vndrie_info->ie_len);
3469 *mgmt_ie_len += vndrie_info->ie_len;
3470
3471 curr_ie_buf += del_add_ie_buf_len;
3472 total_ie_buf_len += del_add_ie_buf_len;
3473 }
3474 }
3475 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003476 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003477 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003478 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003479 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003480 }
3481
3482exit:
3483 kfree(iovar_ie_buf);
3484 return err;
3485}
3486
3487static s32
3488brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3489 struct cfg80211_ap_settings *settings)
3490{
3491 s32 ie_offset;
Arend van Sprielac24be62012-10-22 10:36:23 -07003492 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003493 struct brcmf_tlv *ssid_ie;
3494 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003495 s32 err = -EPERM;
3496 struct brcmf_tlv *rsn_ie;
3497 struct brcmf_vs_tlv *wpa_ie;
3498 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02003499 s32 bssidx = 0;
3500
Arend van Sprield96b8012012-12-05 15:26:02 +01003501 brcmf_dbg(TRACE, "channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
3502 cfg80211_get_chandef_type(&settings->chandef),
3503 settings->beacon_interval,
3504 settings->dtim_period);
3505 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
3506 settings->ssid, settings->ssid_len, settings->auth_type,
3507 settings->inactivity_timeout);
Hante Meuleman1a873342012-09-27 14:17:54 +02003508
Arend van Sprielc1179032012-10-22 13:55:33 -07003509 if (!test_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003510 brcmf_err("Not in AP creation mode\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003511 return -EPERM;
3512 }
3513
3514 memset(&ssid_le, 0, sizeof(ssid_le));
3515 if (settings->ssid == NULL || settings->ssid_len == 0) {
3516 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
3517 ssid_ie = brcmf_parse_tlvs(
3518 (u8 *)&settings->beacon.head[ie_offset],
3519 settings->beacon.head_len - ie_offset,
3520 WLAN_EID_SSID);
3521 if (!ssid_ie)
3522 return -EINVAL;
3523
3524 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
3525 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01003526 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02003527 } else {
3528 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
3529 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3530 }
3531
3532 brcmf_set_mpc(ndev, 0);
Arend van Sprielac24be62012-10-22 10:36:23 -07003533 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003534 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003535 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003536 goto exit;
3537 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003538 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003539 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003540 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003541 goto exit;
3542 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003543 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003544 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003545 brcmf_err("setting AP mode failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003546 goto exit;
3547 }
3548
3549 /* find the RSN_IE */
3550 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
3551 settings->beacon.tail_len, WLAN_EID_RSN);
3552
3553 /* find the WPA_IE */
3554 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
3555 settings->beacon.tail_len);
3556
Hante Meuleman1a873342012-09-27 14:17:54 +02003557 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003558 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003559 if (wpa_ie != NULL) {
3560 /* WPA IE */
Arend van Spriel34778522012-11-05 16:22:19 -08003561 err = brcmf_configure_wpaie(ndev, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02003562 if (err < 0)
3563 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003564 } else {
3565 /* RSN IE */
3566 err = brcmf_configure_wpaie(ndev,
Arend van Spriel34778522012-11-05 16:22:19 -08003567 (struct brcmf_vs_tlv *)rsn_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02003568 if (err < 0)
3569 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003570 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003571 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01003572 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003573 brcmf_configure_opensecurity(ndev, bssidx);
Hante Meuleman1a873342012-09-27 14:17:54 +02003574 }
3575 /* Set Beacon IEs to FW */
Arend van Spriel1332e262012-11-05 16:22:18 -08003576 err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
3577 VNDR_IE_BEACON_FLAG,
3578 settings->beacon.tail,
3579 settings->beacon.tail_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003580 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003581 brcmf_err("Set Beacon IE Failed\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003582 else
Arend van Sprield96b8012012-12-05 15:26:02 +01003583 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003584
3585 /* Set Probe Response IEs to FW */
Arend van Spriel1332e262012-11-05 16:22:18 -08003586 err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
3587 VNDR_IE_PRBRSP_FLAG,
3588 settings->beacon.proberesp_ies,
3589 settings->beacon.proberesp_ies_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003590 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003591 brcmf_err("Set Probe Resp IE Failed\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003592 else
Arend van Sprield96b8012012-12-05 15:26:02 +01003593 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003594
3595 if (settings->beacon_interval) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003596 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003597 settings->beacon_interval);
Hante Meuleman1a873342012-09-27 14:17:54 +02003598 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003599 brcmf_err("Beacon Interval Set Error, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003600 goto exit;
3601 }
3602 }
3603 if (settings->dtim_period) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003604 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003605 settings->dtim_period);
Hante Meuleman1a873342012-09-27 14:17:54 +02003606 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003607 brcmf_err("DTIM Interval Set Error, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003608 goto exit;
3609 }
3610 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003611 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003612 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003613 brcmf_err("BRCMF_C_UP error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003614 goto exit;
3615 }
3616
3617 memset(&join_params, 0, sizeof(join_params));
3618 /* join parameters starts with ssid */
3619 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
3620 /* create softap */
Arend van Sprielac24be62012-10-22 10:36:23 -07003621 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
3622 &join_params, sizeof(join_params));
Hante Meuleman1a873342012-09-27 14:17:54 +02003623 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003624 brcmf_err("SET SSID error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003625 goto exit;
3626 }
Arend van Sprielc1179032012-10-22 13:55:33 -07003627 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3628 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003629
3630exit:
3631 if (err)
3632 brcmf_set_mpc(ndev, 1);
3633 return err;
3634}
3635
3636static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
3637{
Arend van Sprielc1179032012-10-22 13:55:33 -07003638 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003639 s32 err = -EPERM;
3640
Arend van Sprield96b8012012-12-05 15:26:02 +01003641 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003642
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003643 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003644 /* Due to most likely deauths outstanding we sleep */
3645 /* first to make sure they get processed by fw. */
3646 msleep(400);
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003647 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003648 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003649 brcmf_err("setting AP mode failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003650 goto exit;
3651 }
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003652 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003653 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003654 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003655 goto exit;
3656 }
3657 brcmf_set_mpc(ndev, 1);
Arend van Sprielc1179032012-10-22 13:55:33 -07003658 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3659 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003660 }
3661exit:
3662 return err;
3663}
3664
3665static int
3666brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
3667 u8 *mac)
3668{
3669 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003670 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003671 s32 err;
3672
3673 if (!mac)
3674 return -EFAULT;
3675
Arend van Sprield96b8012012-12-05 15:26:02 +01003676 brcmf_dbg(TRACE, "Enter %pM\n", mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02003677
Arend van Sprielce81e312012-10-22 13:55:37 -07003678 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02003679 return -EIO;
3680
3681 memcpy(&scbval.ea, mac, ETH_ALEN);
3682 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003683 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003684 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02003685 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003686 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003687
Arend van Sprield96b8012012-12-05 15:26:02 +01003688 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003689 return err;
3690}
3691
Arend van Spriel5b435de2011-10-05 13:19:03 +02003692static struct cfg80211_ops wl_cfg80211_ops = {
3693 .change_virtual_intf = brcmf_cfg80211_change_iface,
3694 .scan = brcmf_cfg80211_scan,
3695 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
3696 .join_ibss = brcmf_cfg80211_join_ibss,
3697 .leave_ibss = brcmf_cfg80211_leave_ibss,
3698 .get_station = brcmf_cfg80211_get_station,
3699 .set_tx_power = brcmf_cfg80211_set_tx_power,
3700 .get_tx_power = brcmf_cfg80211_get_tx_power,
3701 .add_key = brcmf_cfg80211_add_key,
3702 .del_key = brcmf_cfg80211_del_key,
3703 .get_key = brcmf_cfg80211_get_key,
3704 .set_default_key = brcmf_cfg80211_config_default_key,
3705 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
3706 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
3707 .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
3708 .connect = brcmf_cfg80211_connect,
3709 .disconnect = brcmf_cfg80211_disconnect,
3710 .suspend = brcmf_cfg80211_suspend,
3711 .resume = brcmf_cfg80211_resume,
3712 .set_pmksa = brcmf_cfg80211_set_pmksa,
3713 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003714 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02003715 .start_ap = brcmf_cfg80211_start_ap,
3716 .stop_ap = brcmf_cfg80211_stop_ap,
3717 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02003718 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
3719 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003720#ifdef CONFIG_NL80211_TESTMODE
3721 .testmode_cmd = brcmf_cfg80211_testmode
3722#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02003723};
3724
3725static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
3726{
3727 s32 err = 0;
3728
3729 switch (mode) {
3730 case WL_MODE_BSS:
3731 return NL80211_IFTYPE_STATION;
3732 case WL_MODE_IBSS:
3733 return NL80211_IFTYPE_ADHOC;
3734 default:
3735 return NL80211_IFTYPE_UNSPECIFIED;
3736 }
3737
3738 return err;
3739}
3740
Arend van Spriele5806072012-09-19 22:21:08 +02003741static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
3742{
Arend van Spriele5806072012-09-19 22:21:08 +02003743 /* scheduled scan settings */
3744 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
3745 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
3746 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
3747 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
Arend van Spriele5806072012-09-19 22:21:08 +02003748}
3749
Arend van Spriel3eacf862012-10-22 13:55:30 -07003750static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003751{
Arend van Spriel3eacf862012-10-22 13:55:30 -07003752 struct wiphy *wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003753 s32 err = 0;
3754
Arend van Spriel3eacf862012-10-22 13:55:30 -07003755 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
3756 if (!wiphy) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003757 brcmf_err("Could not allocate wiphy device\n");
Arend van Spriel3eacf862012-10-22 13:55:30 -07003758 return ERR_PTR(-ENOMEM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003759 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003760 set_wiphy_dev(wiphy, phydev);
3761 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
3762 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
3763 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3764 BIT(NL80211_IFTYPE_ADHOC) |
3765 BIT(NL80211_IFTYPE_AP);
3766 wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
3767 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
Arend van Spriel5b435de2011-10-05 13:19:03 +02003768 * it as 11a by default.
3769 * This will be updated with
3770 * 11n phy tables in
3771 * "ifconfig up"
3772 * if phy has 11n capability
3773 */
Arend van Spriel3eacf862012-10-22 13:55:30 -07003774 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3775 wiphy->cipher_suites = __wl_cipher_suites;
3776 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
3777 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
Arend van Spriel5b435de2011-10-05 13:19:03 +02003778 * save mode
3779 * by default
3780 */
Arend van Spriel3eacf862012-10-22 13:55:30 -07003781 brcmf_wiphy_pno_params(wiphy);
3782 err = wiphy_register(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003783 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003784 brcmf_err("Could not register wiphy device (%d)\n", err);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003785 wiphy_free(wiphy);
3786 return ERR_PTR(err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003787 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003788 return wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003789}
3790
Arend van Spriel3eacf862012-10-22 13:55:30 -07003791static
3792struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
3793 struct net_device *netdev,
3794 s32 mode, bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003795{
Arend van Spriel3eacf862012-10-22 13:55:30 -07003796 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003797
Arend van Spriel3eacf862012-10-22 13:55:30 -07003798 if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT)
3799 return ERR_PTR(-ENOSPC);
3800
3801 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
3802 if (!vif)
3803 return ERR_PTR(-ENOMEM);
3804
3805 vif->wdev.wiphy = cfg->wiphy;
3806 vif->wdev.netdev = netdev;
3807 vif->wdev.iftype = brcmf_mode_to_nl80211_iftype(mode);
3808
3809 if (netdev) {
3810 vif->ifp = netdev_priv(netdev);
3811 netdev->ieee80211_ptr = &vif->wdev;
3812 SET_NETDEV_DEV(netdev, wiphy_dev(cfg->wiphy));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003813 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003814
3815 vif->mode = mode;
3816 vif->pm_block = pm_block;
3817 vif->roam_off = -1;
3818
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07003819 brcmf_init_prof(&vif->profile);
3820
Arend van Spriel3eacf862012-10-22 13:55:30 -07003821 list_add_tail(&vif->list, &cfg->vif_list);
3822 cfg->vif_cnt++;
3823 return vif;
3824}
3825
3826static void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
3827{
3828 struct brcmf_cfg80211_info *cfg;
3829 struct wiphy *wiphy;
3830
3831 wiphy = vif->wdev.wiphy;
3832 cfg = wiphy_priv(wiphy);
3833 list_del(&vif->list);
3834 cfg->vif_cnt--;
3835
3836 kfree(vif);
3837 if (!cfg->vif_cnt) {
3838 wiphy_unregister(wiphy);
3839 wiphy_free(wiphy);
3840 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003841}
3842
Arend van Spriel903e0ee2012-11-28 21:44:11 +01003843static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003844{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003845 u32 event = e->event_code;
3846 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003847
3848 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01003849 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003850 return true;
3851 }
3852
3853 return false;
3854}
3855
Arend van Spriel903e0ee2012-11-28 21:44:11 +01003856static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003857{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003858 u32 event = e->event_code;
3859 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003860
3861 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
Arend van Spriel16886732012-12-05 15:26:04 +01003862 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003863 return true;
3864 }
3865 return false;
3866}
3867
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003868static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003869 const struct brcmf_event_msg *e)
3870{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003871 u32 event = e->event_code;
3872 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003873
3874 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01003875 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
3876 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003877 return true;
3878 }
3879
3880 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01003881 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003882 return true;
3883 }
3884
3885 return false;
3886}
3887
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003888static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003889{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003890 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003891
3892 kfree(conn_info->req_ie);
3893 conn_info->req_ie = NULL;
3894 conn_info->req_ie_len = 0;
3895 kfree(conn_info->resp_ie);
3896 conn_info->resp_ie = NULL;
3897 conn_info->resp_ie_len = 0;
3898}
3899
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003900static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003901{
Arend van Sprielac24be62012-10-22 10:36:23 -07003902 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003903 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003904 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003905 u32 req_len;
3906 u32 resp_len;
3907 s32 err = 0;
3908
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003909 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003910
Arend van Sprielac24be62012-10-22 10:36:23 -07003911 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
3912 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003913 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003914 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003915 return err;
3916 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003917 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003918 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003919 req_len = le32_to_cpu(assoc_info->req_len);
3920 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003921 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003922 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003923 cfg->extra_buf,
3924 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003925 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003926 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003927 return err;
3928 }
3929 conn_info->req_ie_len = req_len;
3930 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003931 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003932 GFP_KERNEL);
3933 } else {
3934 conn_info->req_ie_len = 0;
3935 conn_info->req_ie = NULL;
3936 }
3937 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003938 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003939 cfg->extra_buf,
3940 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003941 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003942 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003943 return err;
3944 }
3945 conn_info->resp_ie_len = resp_len;
3946 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003947 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003948 GFP_KERNEL);
3949 } else {
3950 conn_info->resp_ie_len = 0;
3951 conn_info->resp_ie = NULL;
3952 }
Arend van Spriel16886732012-12-05 15:26:04 +01003953 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
3954 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003955
3956 return err;
3957}
3958
3959static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003960brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003961 struct net_device *ndev,
3962 const struct brcmf_event_msg *e)
3963{
Arend van Sprielc1179032012-10-22 13:55:33 -07003964 struct brcmf_if *ifp = netdev_priv(ndev);
3965 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003966 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
3967 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07003968 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003969 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07003970 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003971 u32 freq;
3972 s32 err = 0;
3973 u32 target_channel;
Franky Lina180b832012-10-10 11:13:09 -07003974 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003975
Arend van Sprield96b8012012-12-05 15:26:02 +01003976 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003977
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003978 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02003979 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003980 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003981
Franky Lina180b832012-10-10 11:13:09 -07003982 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
3983 if (buf == NULL) {
3984 err = -ENOMEM;
3985 goto done;
3986 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003987
Franky Lina180b832012-10-10 11:13:09 -07003988 /* data sent to dongle has to be little endian */
3989 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07003990 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07003991 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07003992
3993 if (err)
3994 goto done;
3995
3996 bi = (struct brcmf_bss_info_le *)(buf + 4);
3997 target_channel = bi->ctl_ch ? bi->ctl_ch :
3998 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003999
4000 if (target_channel <= CH_MAX_2G_CHANNEL)
4001 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4002 else
4003 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4004
4005 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4006 notify_channel = ieee80211_get_channel(wiphy, freq);
4007
Franky Lina180b832012-10-10 11:13:09 -07004008done:
4009 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004010 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004011 conn_info->req_ie, conn_info->req_ie_len,
4012 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004013 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004014
Arend van Sprielc1179032012-10-22 13:55:33 -07004015 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004016 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004017 return err;
4018}
4019
4020static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004021brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004022 struct net_device *ndev, const struct brcmf_event_msg *e,
4023 bool completed)
4024{
Arend van Sprielc1179032012-10-22 13:55:33 -07004025 struct brcmf_if *ifp = netdev_priv(ndev);
4026 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004027 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004028 s32 err = 0;
4029
Arend van Sprield96b8012012-12-05 15:26:02 +01004030 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004031
Arend van Sprielc1179032012-10-22 13:55:33 -07004032 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4033 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004034 if (completed) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004035 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004036 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004037 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004038 }
4039 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004040 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004041 conn_info->req_ie,
4042 conn_info->req_ie_len,
4043 conn_info->resp_ie,
4044 conn_info->resp_ie_len,
4045 completed ? WLAN_STATUS_SUCCESS :
4046 WLAN_STATUS_AUTH_TIMEOUT,
4047 GFP_KERNEL);
4048 if (completed)
Arend van Sprielc1179032012-10-22 13:55:33 -07004049 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4050 &ifp->vif->sme_state);
Arend van Spriel16886732012-12-05 15:26:04 +01004051 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4052 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004053 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004054 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004055 return err;
4056}
4057
4058static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004059brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004060 struct net_device *ndev,
4061 const struct brcmf_event_msg *e, void *data)
4062{
4063 s32 err = 0;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004064 u32 event = e->event_code;
4065 u32 reason = e->reason;
4066 u32 len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004067 static int generation;
4068
4069 struct station_info sinfo;
4070
Arend van Spriel16886732012-12-05 15:26:04 +01004071 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Hante Meuleman1a873342012-09-27 14:17:54 +02004072 memset(&sinfo, 0, sizeof(sinfo));
4073
4074 sinfo.filled = 0;
4075 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
4076 reason == BRCMF_E_STATUS_SUCCESS) {
4077 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4078 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004079 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004080 return -EINVAL;
4081 }
4082 sinfo.assoc_req_ies = data;
4083 sinfo.assoc_req_ies_len = len;
4084 generation++;
4085 sinfo.generation = generation;
4086 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_ATOMIC);
4087 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4088 (event == BRCMF_E_DEAUTH_IND) ||
4089 (event == BRCMF_E_DEAUTH)) {
4090 generation++;
4091 sinfo.generation = generation;
4092 cfg80211_del_sta(ndev, e->addr, GFP_ATOMIC);
4093 }
4094 return err;
4095}
4096
4097static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004098brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004099 const struct brcmf_event_msg *e, void *data)
4100{
Arend van Spriel19937322012-11-05 16:22:32 -08004101 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4102 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004103 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004104 s32 err = 0;
4105
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004106 if (ifp->vif->mode == WL_MODE_AP) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004107 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004108 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004109 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004110 if (brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004111 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004112 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004113 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004114 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4115 &ifp->vif->sme_state);
4116 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4117 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004118 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004119 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004120 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004121 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004122 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004123 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Sprielc1179032012-10-22 13:55:33 -07004124 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004125 &ifp->vif->sme_state))
Arend van Spriel5b435de2011-10-05 13:19:03 +02004126 cfg80211_disconnected(ndev, 0, NULL, 0,
Arend van Sprielc1179032012-10-22 13:55:33 -07004127 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004128 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004129 brcmf_link_down(ifp->vif);
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004130 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004131 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004132 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07004133 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4134 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004135 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004136 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004137 }
4138
4139 return err;
4140}
4141
4142static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004143brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004144 const struct brcmf_event_msg *e, void *data)
4145{
Arend van Spriel19937322012-11-05 16:22:32 -08004146 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004147 s32 err = 0;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004148 u32 event = e->event_code;
4149 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004150
4151 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004152 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08004153 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004154 else
Arend van Spriel19937322012-11-05 16:22:32 -08004155 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004156 }
4157
4158 return err;
4159}
4160
4161static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004162brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004163 const struct brcmf_event_msg *e, void *data)
4164{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004165 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004166 enum nl80211_key_type key_type;
4167
4168 if (flags & BRCMF_EVENT_MSG_GROUP)
4169 key_type = NL80211_KEYTYPE_GROUP;
4170 else
4171 key_type = NL80211_KEYTYPE_PAIRWISE;
4172
Arend van Spriel19937322012-11-05 16:22:32 -08004173 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004174 NULL, GFP_KERNEL);
4175
4176 return 0;
4177}
4178
Arend van Spriel5b435de2011-10-05 13:19:03 +02004179static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4180{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004181 conf->frag_threshold = (u32)-1;
4182 conf->rts_threshold = (u32)-1;
4183 conf->retry_short = (u32)-1;
4184 conf->retry_long = (u32)-1;
4185 conf->tx_power = -1;
4186}
4187
Arend van Spriel5c36b992012-11-14 18:46:05 -08004188static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004189{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004190 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
4191 brcmf_notify_connect_status);
4192 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
4193 brcmf_notify_connect_status);
4194 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
4195 brcmf_notify_connect_status);
4196 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
4197 brcmf_notify_connect_status);
4198 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
4199 brcmf_notify_connect_status);
4200 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
4201 brcmf_notify_connect_status);
4202 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
4203 brcmf_notify_roaming_status);
4204 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
4205 brcmf_notify_mic_status);
4206 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
4207 brcmf_notify_connect_status);
4208 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
4209 brcmf_notify_sched_scan_results);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004210}
4211
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004212static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004213{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004214 kfree(cfg->conf);
4215 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004216 kfree(cfg->escan_ioctl_buf);
4217 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004218 kfree(cfg->extra_buf);
4219 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004220 kfree(cfg->pmk_list);
4221 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004222}
4223
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004224static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004225{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004226 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4227 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004228 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004229 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4230 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004231 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004232 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4233 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004234 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004235 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4236 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004237 goto init_priv_mem_out;
4238
4239 return 0;
4240
4241init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004242 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004243
4244 return -ENOMEM;
4245}
4246
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004247static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004248{
4249 s32 err = 0;
4250
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004251 cfg->scan_request = NULL;
4252 cfg->pwr_save = true;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004253 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004254 we enable roam per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004255 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02004256 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004257 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004258 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004259 if (err)
4260 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004261 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004262 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004263 brcmf_init_escan(cfg);
4264 brcmf_init_conf(cfg->conf);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004265
4266 return err;
4267}
4268
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004269static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004270{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004271 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004272 brcmf_abort_scanning(cfg);
4273 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004274}
4275
Arend van Sprield9cb2592012-12-05 15:25:54 +01004276struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
4277 struct device *busdev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004278{
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004279 struct net_device *ndev = drvr->iflist[0]->ndev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004280 struct brcmf_cfg80211_info *cfg;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004281 struct wiphy *wiphy;
4282 struct brcmf_cfg80211_vif *vif;
4283 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004284 s32 err = 0;
4285
4286 if (!ndev) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004287 brcmf_err("ndev is invalid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004288 return NULL;
4289 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004290
Arend van Spriel3eacf862012-10-22 13:55:30 -07004291 ifp = netdev_priv(ndev);
4292 wiphy = brcmf_setup_wiphy(busdev);
4293 if (IS_ERR(wiphy))
4294 return NULL;
4295
4296 cfg = wiphy_priv(wiphy);
4297 cfg->wiphy = wiphy;
4298 cfg->pub = drvr;
4299 INIT_LIST_HEAD(&cfg->vif_list);
4300
4301 vif = brcmf_alloc_vif(cfg, ndev, WL_MODE_BSS, false);
4302 if (IS_ERR(vif)) {
4303 wiphy_free(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004304 return NULL;
4305 }
4306
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004307 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004308 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004309 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004310 goto cfg80211_attach_out;
4311 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004312
Arend van Spriel3eacf862012-10-22 13:55:30 -07004313 ifp->vif = vif;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004314 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004315
4316cfg80211_attach_out:
Arend van Spriel3eacf862012-10-22 13:55:30 -07004317 brcmf_free_vif(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004318 return NULL;
4319}
4320
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004321void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004322{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004323 struct brcmf_cfg80211_vif *vif;
4324 struct brcmf_cfg80211_vif *tmp;
4325
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004326 wl_deinit_priv(cfg);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004327 list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) {
4328 brcmf_free_vif(vif);
4329 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004330}
4331
Arend van Spriel5b435de2011-10-05 13:19:03 +02004332static s32
4333brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
4334{
Arend van Sprielac24be62012-10-22 10:36:23 -07004335 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004336 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02004337 __le32 roamtrigger[2];
4338 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02004339
4340 /*
4341 * Setup timeout if Beacons are lost and roam is
4342 * off to report link down
4343 */
4344 if (roamvar) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004345 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004346 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004347 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004348 goto dongle_rom_out;
4349 }
4350 }
4351
4352 /*
4353 * Enable/Disable built-in roaming to allow supplicant
4354 * to take care of roaming
4355 */
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004356 brcmf_dbg(INFO, "Internal Roaming = %s\n", roamvar ? "Off" : "On");
Arend van Sprielac24be62012-10-22 10:36:23 -07004357 err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004358 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004359 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004360 goto dongle_rom_out;
4361 }
4362
Arend van Sprielf588bc02011-10-12 20:51:22 +02004363 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
4364 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004365 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004366 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004367 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004368 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004369 goto dongle_rom_out;
4370 }
4371
Arend van Sprielf588bc02011-10-12 20:51:22 +02004372 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
4373 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004374 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004375 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004376 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004377 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004378 goto dongle_rom_out;
4379 }
4380
4381dongle_rom_out:
4382 return err;
4383}
4384
4385static s32
4386brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02004387 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004388{
Arend van Sprielac24be62012-10-22 10:36:23 -07004389 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004390 s32 err = 0;
4391
Arend van Sprielac24be62012-10-22 10:36:23 -07004392 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004393 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004394 if (err) {
4395 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004396 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004397 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01004398 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004399 goto dongle_scantime_out;
4400 }
Arend van Sprielac24be62012-10-22 10:36:23 -07004401 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004402 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004403 if (err) {
4404 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004405 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004406 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01004407 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004408 goto dongle_scantime_out;
4409 }
4410
Arend van Sprielac24be62012-10-22 10:36:23 -07004411 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004412 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004413 if (err) {
4414 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004415 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004416 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01004417 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004418 goto dongle_scantime_out;
4419 }
4420
4421dongle_scantime_out:
4422 return err;
4423}
4424
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004425static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004426{
Arend van Sprielac24be62012-10-22 10:36:23 -07004427 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004428 struct wiphy *wiphy;
4429 s32 phy_list;
4430 s8 phy;
4431 s32 err = 0;
4432
Hante Meulemanb87e2c42012-11-14 18:46:23 -08004433 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004434 &phy_list, sizeof(phy_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004435 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004436 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004437 return err;
4438 }
4439
Hante Meuleman3ba81372012-09-19 22:21:13 +02004440 phy = ((char *)&phy_list)[0];
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004441 brcmf_dbg(INFO, "%c phy\n", phy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004442 if (phy == 'n' || phy == 'a') {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004443 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004444 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
4445 }
4446
4447 return err;
4448}
4449
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004450static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004451{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004452 return wl_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004453}
4454
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004455static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004456{
4457 struct net_device *ndev;
4458 struct wireless_dev *wdev;
4459 s32 power_mode;
4460 s32 err = 0;
4461
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004462 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004463 return err;
4464
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004465 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004466 wdev = ndev->ieee80211_ptr;
4467
4468 brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
4469 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
4470
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004471 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Arend van Sprielac24be62012-10-22 10:36:23 -07004472 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PM,
4473 power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004474 if (err)
4475 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01004476 brcmf_dbg(INFO, "power save set to %s\n",
4477 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004478
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004479 err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004480 WL_BEACON_TIMEOUT);
4481 if (err)
4482 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07004483 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
4484 NULL, NULL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004485 if (err && err != -EINPROGRESS)
4486 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004487 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004488 if (err)
4489 goto default_conf_out;
4490
4491 /* -EINPROGRESS: Call commit handler */
4492
4493default_conf_out:
4494
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004495 cfg->dongle_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004496
4497 return err;
4498
4499}
4500
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004501static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004502{
Arend van Sprielc1179032012-10-22 13:55:33 -07004503 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004504 if (ifp->idx)
4505 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004506
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004507 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004508}
4509
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004510static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004511{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004512 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07004513
Arend van Spriel5b435de2011-10-05 13:19:03 +02004514 /*
4515 * While going down, if associated with AP disassociate
4516 * from AP to save power
4517 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004518 if (check_vif_up(ifp->vif)) {
4519 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004520
4521 /* Make sure WPA_Supplicant receives all the event
4522 generated due to DISASSOC call to the fw to keep
4523 the state fw and WPA_Supplicant state consistent
4524 */
4525 brcmf_delay(500);
4526 }
4527
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004528 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07004529 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004530
Arend van Spriel5b435de2011-10-05 13:19:03 +02004531 return 0;
4532}
4533
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004534s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004535{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004536 struct brcmf_if *ifp = netdev_priv(ndev);
4537 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004538 s32 err = 0;
4539
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004540 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004541 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004542 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004543
4544 return err;
4545}
4546
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004547s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004548{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004549 struct brcmf_if *ifp = netdev_priv(ndev);
4550 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004551 s32 err = 0;
4552
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004553 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004554 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004555 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004556
4557 return err;
4558}
4559