blob: 5dea1b4fcd6ea64d3151d28d3d53c56e39faa58c [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"
30#include "wl_cfg80211.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070031#include "fwil.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020032
Arend van Spriele5806072012-09-19 22:21:08 +020033#define BRCMF_SCAN_IE_LEN_MAX 2048
34#define BRCMF_PNO_VERSION 2
35#define BRCMF_PNO_TIME 30
36#define BRCMF_PNO_REPEAT 4
37#define BRCMF_PNO_FREQ_EXPO_MAX 3
38#define BRCMF_PNO_MAX_PFN_COUNT 16
39#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
40#define BRCMF_PNO_HIDDEN_BIT 2
41#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
42#define BRCMF_PNO_SCAN_COMPLETE 1
43#define BRCMF_PNO_SCAN_INCOMPLETE 0
44
Arend van Spriel3eacf862012-10-22 13:55:30 -070045#define BRCMF_IFACE_MAX_CNT 2
46
Hante Meuleman1a873342012-09-27 14:17:54 +020047#define TLV_LEN_OFF 1 /* length offset */
Hante Meuleman04012892012-09-27 14:17:49 +020048#define TLV_HDR_LEN 2 /* header length */
Hante Meuleman1a873342012-09-27 14:17:54 +020049#define TLV_BODY_OFF 2 /* body offset */
50#define TLV_OUI_LEN 3 /* oui id length */
51#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
52#define WPA_OUI_TYPE 1
53#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
54#define WME_OUI_TYPE 2
55
56#define VS_IE_FIXED_HDR_LEN 6
57#define WPA_IE_VERSION_LEN 2
58#define WPA_IE_MIN_OUI_LEN 4
59#define WPA_IE_SUITE_COUNT_LEN 2
60
61#define WPA_CIPHER_NONE 0 /* None */
62#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
63#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
64#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
65#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
66
67#define RSN_AKM_NONE 0 /* None (IBSS) */
68#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
69#define RSN_AKM_PSK 2 /* Pre-shared Key */
70#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
71#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
72
73#define VNDR_IE_CMD_LEN 4 /* length of the set command
74 * string :"add", "del" (+ NUL)
75 */
76#define VNDR_IE_COUNT_OFFSET 4
77#define VNDR_IE_PKTFLAG_OFFSET 8
78#define VNDR_IE_VSIE_OFFSET 12
79#define VNDR_IE_HDR_SIZE 12
80#define VNDR_IE_BEACON_FLAG 0x1
81#define VNDR_IE_PRBRSP_FLAG 0x2
82#define MAX_VNDR_IE_NUMBER 5
83
84#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
85#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020086
Arend van Spriel5b435de2011-10-05 13:19:03 +020087#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
88 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
89
Arend van Spriel5b435de2011-10-05 13:19:03 +020090static u32 brcmf_dbg_level = WL_DBG_ERR;
91
Arend van Sprielce81e312012-10-22 13:55:37 -070092static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +020093{
Arend van Sprielc1179032012-10-22 13:55:33 -070094 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
95 WL_INFO("device is not ready : status (%lu)\n",
96 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +020097 return false;
98 }
99 return true;
100}
101
102#define CHAN2G(_channel, _freq, _flags) { \
103 .band = IEEE80211_BAND_2GHZ, \
104 .center_freq = (_freq), \
105 .hw_value = (_channel), \
106 .flags = (_flags), \
107 .max_antenna_gain = 0, \
108 .max_power = 30, \
109}
110
111#define CHAN5G(_channel, _flags) { \
112 .band = IEEE80211_BAND_5GHZ, \
113 .center_freq = 5000 + (5 * (_channel)), \
114 .hw_value = (_channel), \
115 .flags = (_flags), \
116 .max_antenna_gain = 0, \
117 .max_power = 30, \
118}
119
120#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
121#define RATETAB_ENT(_rateid, _flags) \
122 { \
123 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
124 .hw_value = (_rateid), \
125 .flags = (_flags), \
126 }
127
128static struct ieee80211_rate __wl_rates[] = {
129 RATETAB_ENT(BRCM_RATE_1M, 0),
130 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
131 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
132 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
133 RATETAB_ENT(BRCM_RATE_6M, 0),
134 RATETAB_ENT(BRCM_RATE_9M, 0),
135 RATETAB_ENT(BRCM_RATE_12M, 0),
136 RATETAB_ENT(BRCM_RATE_18M, 0),
137 RATETAB_ENT(BRCM_RATE_24M, 0),
138 RATETAB_ENT(BRCM_RATE_36M, 0),
139 RATETAB_ENT(BRCM_RATE_48M, 0),
140 RATETAB_ENT(BRCM_RATE_54M, 0),
141};
142
143#define wl_a_rates (__wl_rates + 4)
144#define wl_a_rates_size 8
145#define wl_g_rates (__wl_rates + 0)
146#define wl_g_rates_size 12
147
148static struct ieee80211_channel __wl_2ghz_channels[] = {
149 CHAN2G(1, 2412, 0),
150 CHAN2G(2, 2417, 0),
151 CHAN2G(3, 2422, 0),
152 CHAN2G(4, 2427, 0),
153 CHAN2G(5, 2432, 0),
154 CHAN2G(6, 2437, 0),
155 CHAN2G(7, 2442, 0),
156 CHAN2G(8, 2447, 0),
157 CHAN2G(9, 2452, 0),
158 CHAN2G(10, 2457, 0),
159 CHAN2G(11, 2462, 0),
160 CHAN2G(12, 2467, 0),
161 CHAN2G(13, 2472, 0),
162 CHAN2G(14, 2484, 0),
163};
164
165static struct ieee80211_channel __wl_5ghz_a_channels[] = {
166 CHAN5G(34, 0), CHAN5G(36, 0),
167 CHAN5G(38, 0), CHAN5G(40, 0),
168 CHAN5G(42, 0), CHAN5G(44, 0),
169 CHAN5G(46, 0), CHAN5G(48, 0),
170 CHAN5G(52, 0), CHAN5G(56, 0),
171 CHAN5G(60, 0), CHAN5G(64, 0),
172 CHAN5G(100, 0), CHAN5G(104, 0),
173 CHAN5G(108, 0), CHAN5G(112, 0),
174 CHAN5G(116, 0), CHAN5G(120, 0),
175 CHAN5G(124, 0), CHAN5G(128, 0),
176 CHAN5G(132, 0), CHAN5G(136, 0),
177 CHAN5G(140, 0), CHAN5G(149, 0),
178 CHAN5G(153, 0), CHAN5G(157, 0),
179 CHAN5G(161, 0), CHAN5G(165, 0),
180 CHAN5G(184, 0), CHAN5G(188, 0),
181 CHAN5G(192, 0), CHAN5G(196, 0),
182 CHAN5G(200, 0), CHAN5G(204, 0),
183 CHAN5G(208, 0), CHAN5G(212, 0),
184 CHAN5G(216, 0),
185};
186
187static struct ieee80211_channel __wl_5ghz_n_channels[] = {
188 CHAN5G(32, 0), CHAN5G(34, 0),
189 CHAN5G(36, 0), CHAN5G(38, 0),
190 CHAN5G(40, 0), CHAN5G(42, 0),
191 CHAN5G(44, 0), CHAN5G(46, 0),
192 CHAN5G(48, 0), CHAN5G(50, 0),
193 CHAN5G(52, 0), CHAN5G(54, 0),
194 CHAN5G(56, 0), CHAN5G(58, 0),
195 CHAN5G(60, 0), CHAN5G(62, 0),
196 CHAN5G(64, 0), CHAN5G(66, 0),
197 CHAN5G(68, 0), CHAN5G(70, 0),
198 CHAN5G(72, 0), CHAN5G(74, 0),
199 CHAN5G(76, 0), CHAN5G(78, 0),
200 CHAN5G(80, 0), CHAN5G(82, 0),
201 CHAN5G(84, 0), CHAN5G(86, 0),
202 CHAN5G(88, 0), CHAN5G(90, 0),
203 CHAN5G(92, 0), CHAN5G(94, 0),
204 CHAN5G(96, 0), CHAN5G(98, 0),
205 CHAN5G(100, 0), CHAN5G(102, 0),
206 CHAN5G(104, 0), CHAN5G(106, 0),
207 CHAN5G(108, 0), CHAN5G(110, 0),
208 CHAN5G(112, 0), CHAN5G(114, 0),
209 CHAN5G(116, 0), CHAN5G(118, 0),
210 CHAN5G(120, 0), CHAN5G(122, 0),
211 CHAN5G(124, 0), CHAN5G(126, 0),
212 CHAN5G(128, 0), CHAN5G(130, 0),
213 CHAN5G(132, 0), CHAN5G(134, 0),
214 CHAN5G(136, 0), CHAN5G(138, 0),
215 CHAN5G(140, 0), CHAN5G(142, 0),
216 CHAN5G(144, 0), CHAN5G(145, 0),
217 CHAN5G(146, 0), CHAN5G(147, 0),
218 CHAN5G(148, 0), CHAN5G(149, 0),
219 CHAN5G(150, 0), CHAN5G(151, 0),
220 CHAN5G(152, 0), CHAN5G(153, 0),
221 CHAN5G(154, 0), CHAN5G(155, 0),
222 CHAN5G(156, 0), CHAN5G(157, 0),
223 CHAN5G(158, 0), CHAN5G(159, 0),
224 CHAN5G(160, 0), CHAN5G(161, 0),
225 CHAN5G(162, 0), CHAN5G(163, 0),
226 CHAN5G(164, 0), CHAN5G(165, 0),
227 CHAN5G(166, 0), CHAN5G(168, 0),
228 CHAN5G(170, 0), CHAN5G(172, 0),
229 CHAN5G(174, 0), CHAN5G(176, 0),
230 CHAN5G(178, 0), CHAN5G(180, 0),
231 CHAN5G(182, 0), CHAN5G(184, 0),
232 CHAN5G(186, 0), CHAN5G(188, 0),
233 CHAN5G(190, 0), CHAN5G(192, 0),
234 CHAN5G(194, 0), CHAN5G(196, 0),
235 CHAN5G(198, 0), CHAN5G(200, 0),
236 CHAN5G(202, 0), CHAN5G(204, 0),
237 CHAN5G(206, 0), CHAN5G(208, 0),
238 CHAN5G(210, 0), CHAN5G(212, 0),
239 CHAN5G(214, 0), CHAN5G(216, 0),
240 CHAN5G(218, 0), CHAN5G(220, 0),
241 CHAN5G(222, 0), CHAN5G(224, 0),
242 CHAN5G(226, 0), CHAN5G(228, 0),
243};
244
245static struct ieee80211_supported_band __wl_band_2ghz = {
246 .band = IEEE80211_BAND_2GHZ,
247 .channels = __wl_2ghz_channels,
248 .n_channels = ARRAY_SIZE(__wl_2ghz_channels),
249 .bitrates = wl_g_rates,
250 .n_bitrates = wl_g_rates_size,
251};
252
253static struct ieee80211_supported_band __wl_band_5ghz_a = {
254 .band = IEEE80211_BAND_5GHZ,
255 .channels = __wl_5ghz_a_channels,
256 .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
257 .bitrates = wl_a_rates,
258 .n_bitrates = wl_a_rates_size,
259};
260
261static struct ieee80211_supported_band __wl_band_5ghz_n = {
262 .band = IEEE80211_BAND_5GHZ,
263 .channels = __wl_5ghz_n_channels,
264 .n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
265 .bitrates = wl_a_rates,
266 .n_bitrates = wl_a_rates_size,
267};
268
269static const u32 __wl_cipher_suites[] = {
270 WLAN_CIPHER_SUITE_WEP40,
271 WLAN_CIPHER_SUITE_WEP104,
272 WLAN_CIPHER_SUITE_TKIP,
273 WLAN_CIPHER_SUITE_CCMP,
274 WLAN_CIPHER_SUITE_AES_CMAC,
275};
276
Alwin Beukersf8e4b412011-10-12 20:51:28 +0200277/* tag_ID/length/value_buffer tuple */
278struct brcmf_tlv {
279 u8 id;
280 u8 len;
281 u8 data[1];
282};
283
Hante Meuleman1a873342012-09-27 14:17:54 +0200284/* Vendor specific ie. id = 221, oui and type defines exact ie */
285struct brcmf_vs_tlv {
286 u8 id;
287 u8 len;
288 u8 oui[3];
289 u8 oui_type;
290};
291
292struct parsed_vndr_ie_info {
293 u8 *ie_ptr;
294 u32 ie_len; /* total length including id & length field */
295 struct brcmf_vs_tlv vndrie;
296};
297
298struct parsed_vndr_ies {
299 u32 count;
300 struct parsed_vndr_ie_info ie_info[MAX_VNDR_IE_NUMBER];
301};
302
Alwin Beukersef6ac172011-10-12 20:51:26 +0200303/* Quarter dBm units to mW
304 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
305 * Table is offset so the last entry is largest mW value that fits in
306 * a u16.
307 */
308
309#define QDBM_OFFSET 153 /* Offset for first entry */
310#define QDBM_TABLE_LEN 40 /* Table size */
311
312/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
313 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
314 */
315#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
316
317/* Largest mW value that will round down to the last table entry,
318 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
319 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
320 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
321 */
322#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
323
324static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
325/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
326/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
327/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
328/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
329/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
330/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
331};
332
333static u16 brcmf_qdbm_to_mw(u8 qdbm)
334{
335 uint factor = 1;
336 int idx = qdbm - QDBM_OFFSET;
337
338 if (idx >= QDBM_TABLE_LEN)
339 /* clamp to max u16 mW value */
340 return 0xFFFF;
341
342 /* scale the qdBm index up to the range of the table 0-40
343 * where an offset of 40 qdBm equals a factor of 10 mW.
344 */
345 while (idx < 0) {
346 idx += 40;
347 factor *= 10;
348 }
349
350 /* return the mW value scaled down to the correct factor of 10,
351 * adding in factor/2 to get proper rounding.
352 */
353 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
354}
355
356static u8 brcmf_mw_to_qdbm(u16 mw)
357{
358 u8 qdbm;
359 int offset;
360 uint mw_uint = mw;
361 uint boundary;
362
363 /* handle boundary case */
364 if (mw_uint <= 1)
365 return 0;
366
367 offset = QDBM_OFFSET;
368
369 /* move mw into the range of the table */
370 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
371 mw_uint *= 10;
372 offset -= 40;
373 }
374
375 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
376 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
377 nqdBm_to_mW_map[qdbm]) / 2;
378 if (mw_uint < boundary)
379 break;
380 }
381
382 qdbm += (u8) offset;
383
384 return qdbm;
385}
386
Arend van Spriel6e186162012-10-22 10:36:22 -0700387static u16 channel_to_chanspec(struct ieee80211_channel *ch)
388{
389 u16 chanspec;
390
391 chanspec = ieee80211_frequency_to_channel(ch->center_freq);
392 chanspec &= WL_CHANSPEC_CHAN_MASK;
393
394 if (ch->band == IEEE80211_BAND_2GHZ)
395 chanspec |= WL_CHANSPEC_BAND_2G;
396 else
397 chanspec |= WL_CHANSPEC_BAND_5G;
398
399 if (ch->flags & IEEE80211_CHAN_NO_HT40) {
400 chanspec |= WL_CHANSPEC_BW_20;
401 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
402 } else {
403 chanspec |= WL_CHANSPEC_BW_40;
404 if (ch->flags & IEEE80211_CHAN_NO_HT40PLUS)
405 chanspec |= WL_CHANSPEC_CTL_SB_LOWER;
406 else
407 chanspec |= WL_CHANSPEC_CTL_SB_UPPER;
408 }
409 return chanspec;
410}
411
Arend van Spriel5b435de2011-10-05 13:19:03 +0200412static void convert_key_from_CPU(struct brcmf_wsec_key *key,
413 struct brcmf_wsec_key_le *key_le)
414{
415 key_le->index = cpu_to_le32(key->index);
416 key_le->len = cpu_to_le32(key->len);
417 key_le->algo = cpu_to_le32(key->algo);
418 key_le->flags = cpu_to_le32(key->flags);
419 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
420 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
421 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
422 memcpy(key_le->data, key->data, sizeof(key->data));
423 memcpy(key_le->ea, key->ea, sizeof(key->ea));
424}
425
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200426static int
Arend van Spriel2eaba7e2012-10-22 10:36:26 -0700427send_key_to_dongle(struct net_device *ndev, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200428{
429 int err;
430 struct brcmf_wsec_key_le key_le;
431
432 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200433
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700434 brcmf_netdev_wait_pend8021x(ndev);
435
Arend van Sprielac24be62012-10-22 10:36:23 -0700436 err = brcmf_fil_bsscfg_data_set(netdev_priv(ndev), "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700437 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200438
Arend van Spriel5b435de2011-10-05 13:19:03 +0200439 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200440 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200441 return err;
442}
443
444static s32
445brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
446 enum nl80211_iftype type, u32 *flags,
447 struct vif_params *params)
448{
Arend van Sprielc1179032012-10-22 13:55:33 -0700449 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100450 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200451 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200452 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200453 s32 err = 0;
454
Hante Meuleman1a873342012-09-27 14:17:54 +0200455 WL_TRACE("Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200456
457 switch (type) {
458 case NL80211_IFTYPE_MONITOR:
459 case NL80211_IFTYPE_WDS:
460 WL_ERR("type (%d) : currently we do not support this type\n",
461 type);
462 return -EOPNOTSUPP;
463 case NL80211_IFTYPE_ADHOC:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100464 vif->mode = WL_MODE_IBSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200465 infra = 0;
466 break;
467 case NL80211_IFTYPE_STATION:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100468 vif->mode = WL_MODE_BSS;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200469 infra = 1;
470 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200471 case NL80211_IFTYPE_AP:
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100472 vif->mode = WL_MODE_AP;
Hante Meuleman1a873342012-09-27 14:17:54 +0200473 ap = 1;
474 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200475 default:
476 err = -EINVAL;
477 goto done;
478 }
479
Hante Meuleman1a873342012-09-27 14:17:54 +0200480 if (ap) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100481 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +0200482 WL_INFO("IF Type = AP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200483 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100484 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200485 if (err) {
486 WL_ERR("WLC_SET_INFRA error (%d)\n", err);
487 err = -EAGAIN;
488 goto done;
489 }
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100490 WL_INFO("IF Type = %s\n", (vif->mode == WL_MODE_IBSS) ?
Hante Meuleman1a873342012-09-27 14:17:54 +0200491 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200492 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200493 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200494
495done:
496 WL_TRACE("Exit\n");
497
498 return err;
499}
500
Arend van Spriel5b435de2011-10-05 13:19:03 +0200501static void brcmf_set_mpc(struct net_device *ndev, int mpc)
502{
Arend van Sprielc1179032012-10-22 13:55:33 -0700503 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200504 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200505
Arend van Sprielce81e312012-10-22 13:55:37 -0700506 if (check_vif_up(ifp->vif)) {
Arend van Sprielc1179032012-10-22 13:55:33 -0700507 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200508 if (err) {
509 WL_ERR("fail to set mpc\n");
510 return;
511 }
512 WL_INFO("MPC : %d\n", mpc);
513 }
514}
515
Hante Meulemane756af52012-09-11 21:18:52 +0200516static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le,
517 struct cfg80211_scan_request *request)
518{
519 u32 n_ssids;
520 u32 n_channels;
521 s32 i;
522 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200523 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200524 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200525 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200526
Arend van Sprielba40d162012-10-22 13:55:38 -0700527 memset(params_le->bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200528 params_le->bss_type = DOT11_BSSTYPE_ANY;
529 params_le->scan_type = 0;
530 params_le->channel_num = 0;
531 params_le->nprobes = cpu_to_le32(-1);
532 params_le->active_time = cpu_to_le32(-1);
533 params_le->passive_time = cpu_to_le32(-1);
534 params_le->home_time = cpu_to_le32(-1);
535 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
536
537 /* if request is null exit so it will be all channel broadcast scan */
538 if (!request)
539 return;
540
541 n_ssids = request->n_ssids;
542 n_channels = request->n_channels;
543 /* Copy channel array if applicable */
544 WL_SCAN("### List of channelspecs to scan ### %d\n", n_channels);
545 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]);
Hante Meulemane756af52012-09-11 21:18:52 +0200548 WL_SCAN("Chan : %d, Channel spec: %x\n",
Arend van Spriel6e186162012-10-22 10:36:22 -0700549 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 {
553 WL_SCAN("Scanning all channels\n");
554 }
555 /* Copy ssid array if applicable */
556 WL_SCAN("### List of SSIDs to scan ### %d\n", n_ssids);
557 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)
Hante Meulemane756af52012-09-11 21:18:52 +0200569 WL_SCAN("%d: Broadcast scan\n", i);
570 else
571 WL_SCAN("%d: scan for %s size =%d\n", i,
Arend van Spriel029591f2012-09-19 22:21:06 +0200572 ssid_le.SSID, ssid_le.SSID_len);
573 memcpy(ptr, &ssid_le, sizeof(ssid_le));
574 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200575 }
576 } else {
577 WL_SCAN("Broadcast scan %p\n", request->ssids);
578 if ((request->ssids) && request->ssids->ssid_len) {
579 WL_SCAN("SSID %s len=%d\n", params_le->ssid_le.SSID,
580 request->ssids->ssid_len);
581 params_le->ssid_le.SSID_len =
582 cpu_to_le32(request->ssids->ssid_len);
583 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
584 request->ssids->ssid_len);
585 }
586 }
587 /* Adding mask to channel numbers */
588 params_le->channel_num =
589 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
590 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
591}
592
593static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200594brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
Hante Meulemane756af52012-09-11 21:18:52 +0200595 struct net_device *ndev,
596 bool aborted, bool fw_abort)
597{
598 struct brcmf_scan_params_le params_le;
599 struct cfg80211_scan_request *scan_request;
600 s32 err = 0;
601
602 WL_SCAN("Enter\n");
603
604 /* clear scan request, because the FW abort can cause a second call */
605 /* to this functon and might cause a double cfg80211_scan_done */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200606 scan_request = cfg->scan_request;
607 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200608
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200609 if (timer_pending(&cfg->escan_timeout))
610 del_timer_sync(&cfg->escan_timeout);
Hante Meulemane756af52012-09-11 21:18:52 +0200611
612 if (fw_abort) {
613 /* Do a scan abort to stop the driver's scan engine */
614 WL_SCAN("ABORT scan in firmware\n");
615 memset(&params_le, 0, sizeof(params_le));
Arend van Sprielba40d162012-10-22 13:55:38 -0700616 memset(params_le.bssid, 0xFF, ETH_ALEN);
Hante Meulemane756af52012-09-11 21:18:52 +0200617 params_le.bss_type = DOT11_BSSTYPE_ANY;
618 params_le.scan_type = 0;
619 params_le.channel_num = cpu_to_le32(1);
620 params_le.nprobes = cpu_to_le32(1);
621 params_le.active_time = cpu_to_le32(-1);
622 params_le.passive_time = cpu_to_le32(-1);
623 params_le.home_time = cpu_to_le32(-1);
624 /* Scan is aborted by setting channel_list[0] to -1 */
625 params_le.channel_list[0] = cpu_to_le16(-1);
626 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielac24be62012-10-22 10:36:23 -0700627 err = brcmf_fil_cmd_data_set(netdev_priv(ndev), BRCMF_C_SCAN,
628 &params_le, sizeof(params_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200629 if (err)
630 WL_ERR("Scan abort failed\n");
631 }
Arend van Spriele5806072012-09-19 22:21:08 +0200632 /*
633 * e-scan can be initiated by scheduled scan
634 * which takes precedence.
635 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200636 if (cfg->sched_escan) {
Arend van Spriele5806072012-09-19 22:21:08 +0200637 WL_SCAN("scheduled scan completed\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200638 cfg->sched_escan = false;
Arend van Spriele5806072012-09-19 22:21:08 +0200639 if (!aborted)
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200640 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriele5806072012-09-19 22:21:08 +0200641 brcmf_set_mpc(ndev, 1);
642 } else if (scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +0200643 WL_SCAN("ESCAN Completed scan: %s\n",
644 aborted ? "Aborted" : "Done");
645 cfg80211_scan_done(scan_request, aborted);
646 brcmf_set_mpc(ndev, 1);
647 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700648 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meulemane756af52012-09-11 21:18:52 +0200649 WL_ERR("Scan complete while device not scanning\n");
650 return -EPERM;
651 }
652
653 return err;
654}
655
656static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200657brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct net_device *ndev,
Hante Meulemane756af52012-09-11 21:18:52 +0200658 struct cfg80211_scan_request *request, u16 action)
659{
660 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
661 offsetof(struct brcmf_escan_params_le, params_le);
662 struct brcmf_escan_params_le *params;
663 s32 err = 0;
664
665 WL_SCAN("E-SCAN START\n");
666
667 if (request != NULL) {
668 /* Allocate space for populating ssids in struct */
669 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
670
671 /* Allocate space for populating ssids in struct */
672 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
673 }
674
675 params = kzalloc(params_size, GFP_KERNEL);
676 if (!params) {
677 err = -ENOMEM;
678 goto exit;
679 }
680 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
681 brcmf_escan_prep(&params->params_le, request);
682 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
683 params->action = cpu_to_le16(action);
684 params->sync_id = cpu_to_le16(0x1234);
685
Arend van Sprielac24be62012-10-22 10:36:23 -0700686 err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "escan",
687 params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200688 if (err) {
689 if (err == -EBUSY)
690 WL_INFO("system busy : escan canceled\n");
691 else
692 WL_ERR("error (%d)\n", err);
693 }
694
695 kfree(params);
696exit:
697 return err;
698}
699
700static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200701brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Hante Meulemane756af52012-09-11 21:18:52 +0200702 struct net_device *ndev, struct cfg80211_scan_request *request)
703{
704 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700705 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200706 struct brcmf_scan_results *results;
707
708 WL_SCAN("Enter\n");
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200709 cfg->escan_info.ndev = ndev;
710 cfg->escan_info.wiphy = wiphy;
711 cfg->escan_info.escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700712 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielac24be62012-10-22 10:36:23 -0700713 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700714 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200715 if (err) {
716 WL_ERR("error (%d)\n", err);
717 return err;
718 }
719 brcmf_set_mpc(ndev, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200720 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +0200721 results->version = 0;
722 results->count = 0;
723 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
724
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200725 err = brcmf_run_escan(cfg, ndev, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200726 if (err)
727 brcmf_set_mpc(ndev, 1);
728 return err;
729}
730
731static s32
732brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev,
733 struct cfg80211_scan_request *request,
734 struct cfg80211_ssid *this_ssid)
735{
Arend van Sprielc1179032012-10-22 13:55:33 -0700736 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200737 struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev);
Hante Meulemane756af52012-09-11 21:18:52 +0200738 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -0800739 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700740 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200741 bool escan_req;
742 bool spec_scan;
743 s32 err;
744 u32 SSID_len;
745
746 WL_SCAN("START ESCAN\n");
747
Arend van Sprielc1179032012-10-22 13:55:33 -0700748 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
749 WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200750 return -EAGAIN;
751 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700752 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
753 WL_ERR("Scanning being aborted: status (%lu)\n",
754 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200755 return -EAGAIN;
756 }
Arend van Sprielc1179032012-10-22 13:55:33 -0700757 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
758 WL_ERR("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +0200759 return -EAGAIN;
760 }
761
762 /* Arm scan timeout timer */
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200763 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemane756af52012-09-11 21:18:52 +0200764 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
765
766 escan_req = false;
767 if (request) {
768 /* scan bss */
769 ssids = request->ssids;
770 escan_req = true;
771 } else {
772 /* scan in ibss */
773 /* we don't do escan in ibss */
774 ssids = this_ssid;
775 }
776
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200777 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -0700778 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +0200779 if (escan_req) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200780 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -0800781 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +0200782 goto scan_out;
783 } else {
784 WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
785 ssids->ssid, ssids->ssid_len);
786 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
787 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
788 sr->ssid_le.SSID_len = cpu_to_le32(0);
789 spec_scan = false;
790 if (SSID_len) {
791 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
792 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
793 spec_scan = true;
794 } else
795 WL_SCAN("Broadcast scan\n");
796
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700797 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -0700798 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700799 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200800 if (err) {
801 WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
802 goto scan_out;
803 }
804 brcmf_set_mpc(ndev, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -0700805 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -0700806 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +0200807 if (err) {
808 if (err == -EBUSY)
809 WL_INFO("BUSY: scan for \"%s\" canceled\n",
810 sr->ssid_le.SSID);
811 else
812 WL_ERR("WLC_SCAN error (%d)\n", err);
813
814 brcmf_set_mpc(ndev, 1);
815 goto scan_out;
816 }
817 }
818
819 return 0;
820
821scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -0700822 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200823 if (timer_pending(&cfg->escan_timeout))
824 del_timer_sync(&cfg->escan_timeout);
825 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +0200826 return err;
827}
828
Arend van Spriel5b435de2011-10-05 13:19:03 +0200829static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700830brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200831{
Johannes Bergfd014282012-06-18 19:17:03 +0200832 struct net_device *ndev = request->wdev->netdev;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200833 s32 err = 0;
834
835 WL_TRACE("Enter\n");
836
Arend van Sprielce81e312012-10-22 13:55:37 -0700837 if (!check_vif_up(container_of(request->wdev,
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700838 struct brcmf_cfg80211_vif, wdev)))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200839 return -EIO;
840
Hante Meulemanf07998952012-11-05 16:22:13 -0800841 err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +0200842
Arend van Spriel5b435de2011-10-05 13:19:03 +0200843 if (err)
844 WL_ERR("scan error (%d)\n", err);
845
846 WL_TRACE("Exit\n");
847 return err;
848}
849
850static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
851{
852 s32 err = 0;
853
Arend van Sprielac24be62012-10-22 10:36:23 -0700854 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
855 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200856 if (err)
857 WL_ERR("Error (%d)\n", err);
858
859 return err;
860}
861
862static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
863{
864 s32 err = 0;
865
Arend van Sprielac24be62012-10-22 10:36:23 -0700866 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
867 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200868 if (err)
869 WL_ERR("Error (%d)\n", err);
870
871 return err;
872}
873
874static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
875{
876 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -0800877 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200878
Arend van Sprielac24be62012-10-22 10:36:23 -0700879 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200880 if (err) {
881 WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
882 return err;
883 }
884 return err;
885}
886
887static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
888{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200889 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
890 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700891 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200892 s32 err = 0;
893
894 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -0700895 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200896 return -EIO;
897
898 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200899 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
900 cfg->conf->rts_threshold = wiphy->rts_threshold;
901 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200902 if (!err)
903 goto done;
904 }
905 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200906 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
907 cfg->conf->frag_threshold = wiphy->frag_threshold;
908 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200909 if (!err)
910 goto done;
911 }
912 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200913 && (cfg->conf->retry_long != wiphy->retry_long)) {
914 cfg->conf->retry_long = wiphy->retry_long;
915 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200916 if (!err)
917 goto done;
918 }
919 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200920 && (cfg->conf->retry_short != wiphy->retry_short)) {
921 cfg->conf->retry_short = wiphy->retry_short;
922 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200923 if (!err)
924 goto done;
925 }
926
927done:
928 WL_TRACE("Exit\n");
929 return err;
930}
931
Arend van Spriel5b435de2011-10-05 13:19:03 +0200932static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
933{
934 memset(prof, 0, sizeof(*prof));
935}
936
937static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
938 size_t *join_params_size)
939{
940 u16 chanspec = 0;
941
942 if (ch != 0) {
943 if (ch <= CH_MAX_2G_CHANNEL)
944 chanspec |= WL_CHANSPEC_BAND_2G;
945 else
946 chanspec |= WL_CHANSPEC_BAND_5G;
947
948 chanspec |= WL_CHANSPEC_BW_20;
949 chanspec |= WL_CHANSPEC_CTL_SB_NONE;
950
951 *join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
952 sizeof(u16);
953
954 chanspec |= (ch & WL_CHANSPEC_CHAN_MASK);
955 join_params->params_le.chanspec_list[0] = cpu_to_le16(chanspec);
956 join_params->params_le.chanspec_num = cpu_to_le32(1);
957
958 WL_CONN("join_params->params.chanspec_list[0]= %#X,"
959 "channel %d, chanspec %#X\n",
960 chanspec, ch, chanspec);
961 }
962}
963
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100964static void brcmf_link_down(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200965{
Arend van Spriel5b435de2011-10-05 13:19:03 +0200966 s32 err = 0;
967
968 WL_TRACE("Enter\n");
969
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100970 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200971 WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100972 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -0700973 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200974 if (err)
975 WL_ERR("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100976 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200977 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +0100978 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200979 WL_TRACE("Exit\n");
980}
981
982static s32
983brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
984 struct cfg80211_ibss_params *params)
985{
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200986 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -0700987 struct brcmf_if *ifp = netdev_priv(ndev);
988 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200989 struct brcmf_join_params join_params;
990 size_t join_params_size = 0;
991 s32 err = 0;
992 s32 wsec = 0;
993 s32 bcnprd;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200994
995 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -0700996 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +0200997 return -EIO;
998
999 if (params->ssid)
1000 WL_CONN("SSID: %s\n", params->ssid);
1001 else {
1002 WL_CONN("SSID: NULL, Not supported\n");
1003 return -EOPNOTSUPP;
1004 }
1005
Arend van Sprielc1179032012-10-22 13:55:33 -07001006 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001007
1008 if (params->bssid)
Andy Shevchenko040a7832012-07-12 10:43:44 +03001009 WL_CONN("BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001010 else
1011 WL_CONN("No BSSID specified\n");
1012
Johannes Berg683b6d32012-11-08 21:25:48 +01001013 if (params->chandef.chan)
1014 WL_CONN("channel: %d\n", params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001015 else
1016 WL_CONN("no channel specified\n");
1017
1018 if (params->channel_fixed)
1019 WL_CONN("fixed channel required\n");
1020 else
1021 WL_CONN("no fixed channel required\n");
1022
1023 if (params->ie && params->ie_len)
1024 WL_CONN("ie len: %d\n", params->ie_len);
1025 else
1026 WL_CONN("no ie specified\n");
1027
1028 if (params->beacon_interval)
1029 WL_CONN("beacon interval: %d\n", params->beacon_interval);
1030 else
1031 WL_CONN("no beacon interval specified\n");
1032
1033 if (params->basic_rates)
1034 WL_CONN("basic rates: %08X\n", params->basic_rates);
1035 else
1036 WL_CONN("no basic rates specified\n");
1037
1038 if (params->privacy)
1039 WL_CONN("privacy required\n");
1040 else
1041 WL_CONN("no privacy required\n");
1042
1043 /* Configure Privacy for starter */
1044 if (params->privacy)
1045 wsec |= WEP_ENABLED;
1046
Arend van Sprielc1179032012-10-22 13:55:33 -07001047 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001048 if (err) {
1049 WL_ERR("wsec failed (%d)\n", err);
1050 goto done;
1051 }
1052
1053 /* Configure Beacon Interval for starter */
1054 if (params->beacon_interval)
1055 bcnprd = params->beacon_interval;
1056 else
1057 bcnprd = 100;
1058
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001059 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001060 if (err) {
1061 WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
1062 goto done;
1063 }
1064
1065 /* Configure required join parameter */
1066 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1067
1068 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001069 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1070 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1071 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1072 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001073 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001074
1075 /* BSSID */
1076 if (params->bssid) {
1077 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1078 join_params_size = sizeof(join_params.ssid_le) +
1079 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001080 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001081 } else {
Arend van Sprielba40d162012-10-22 13:55:38 -07001082 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001083 memset(profile->bssid, 0, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001084 }
1085
Arend van Spriel5b435de2011-10-05 13:19:03 +02001086 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001087 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001088 u32 target_channel;
1089
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001090 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001091 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001092 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001093 if (params->channel_fixed) {
1094 /* adding chanspec */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001095 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001096 &join_params, &join_params_size);
1097 }
1098
1099 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001100 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001101 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001102 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001103 if (err) {
1104 WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
1105 goto done;
1106 }
1107 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001108 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001109
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001110 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001111
1112
Arend van Sprielc1179032012-10-22 13:55:33 -07001113 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001114 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001115 if (err) {
1116 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1117 goto done;
1118 }
1119
1120done:
1121 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001122 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001123 WL_TRACE("Exit\n");
1124 return err;
1125}
1126
1127static s32
1128brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1129{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001130 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001131 s32 err = 0;
1132
1133 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001134 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001135 return -EIO;
1136
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001137 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001138
1139 WL_TRACE("Exit\n");
1140
1141 return err;
1142}
1143
1144static s32 brcmf_set_wpa_version(struct net_device *ndev,
1145 struct cfg80211_connect_params *sme)
1146{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001147 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001148 struct brcmf_cfg80211_security *sec;
1149 s32 val = 0;
1150 s32 err = 0;
1151
1152 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1153 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1154 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1155 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1156 else
1157 val = WPA_AUTH_DISABLED;
1158 WL_CONN("setting wpa_auth to 0x%0x\n", val);
Arend van Sprielac24be62012-10-22 10:36:23 -07001159 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001160 if (err) {
1161 WL_ERR("set wpa_auth failed (%d)\n", err);
1162 return err;
1163 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001164 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001165 sec->wpa_versions = sme->crypto.wpa_versions;
1166 return err;
1167}
1168
1169static s32 brcmf_set_auth_type(struct net_device *ndev,
1170 struct cfg80211_connect_params *sme)
1171{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001172 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001173 struct brcmf_cfg80211_security *sec;
1174 s32 val = 0;
1175 s32 err = 0;
1176
1177 switch (sme->auth_type) {
1178 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1179 val = 0;
1180 WL_CONN("open system\n");
1181 break;
1182 case NL80211_AUTHTYPE_SHARED_KEY:
1183 val = 1;
1184 WL_CONN("shared key\n");
1185 break;
1186 case NL80211_AUTHTYPE_AUTOMATIC:
1187 val = 2;
1188 WL_CONN("automatic\n");
1189 break;
1190 case NL80211_AUTHTYPE_NETWORK_EAP:
1191 WL_CONN("network eap\n");
1192 default:
1193 val = 2;
1194 WL_ERR("invalid auth type (%d)\n", sme->auth_type);
1195 break;
1196 }
1197
Arend van Sprielac24be62012-10-22 10:36:23 -07001198 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001199 if (err) {
1200 WL_ERR("set auth failed (%d)\n", err);
1201 return err;
1202 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001203 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001204 sec->auth_type = sme->auth_type;
1205 return err;
1206}
1207
1208static s32
1209brcmf_set_set_cipher(struct net_device *ndev,
1210 struct cfg80211_connect_params *sme)
1211{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001212 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001213 struct brcmf_cfg80211_security *sec;
1214 s32 pval = 0;
1215 s32 gval = 0;
1216 s32 err = 0;
1217
1218 if (sme->crypto.n_ciphers_pairwise) {
1219 switch (sme->crypto.ciphers_pairwise[0]) {
1220 case WLAN_CIPHER_SUITE_WEP40:
1221 case WLAN_CIPHER_SUITE_WEP104:
1222 pval = WEP_ENABLED;
1223 break;
1224 case WLAN_CIPHER_SUITE_TKIP:
1225 pval = TKIP_ENABLED;
1226 break;
1227 case WLAN_CIPHER_SUITE_CCMP:
1228 pval = AES_ENABLED;
1229 break;
1230 case WLAN_CIPHER_SUITE_AES_CMAC:
1231 pval = AES_ENABLED;
1232 break;
1233 default:
1234 WL_ERR("invalid cipher pairwise (%d)\n",
1235 sme->crypto.ciphers_pairwise[0]);
1236 return -EINVAL;
1237 }
1238 }
1239 if (sme->crypto.cipher_group) {
1240 switch (sme->crypto.cipher_group) {
1241 case WLAN_CIPHER_SUITE_WEP40:
1242 case WLAN_CIPHER_SUITE_WEP104:
1243 gval = WEP_ENABLED;
1244 break;
1245 case WLAN_CIPHER_SUITE_TKIP:
1246 gval = TKIP_ENABLED;
1247 break;
1248 case WLAN_CIPHER_SUITE_CCMP:
1249 gval = AES_ENABLED;
1250 break;
1251 case WLAN_CIPHER_SUITE_AES_CMAC:
1252 gval = AES_ENABLED;
1253 break;
1254 default:
1255 WL_ERR("invalid cipher group (%d)\n",
1256 sme->crypto.cipher_group);
1257 return -EINVAL;
1258 }
1259 }
1260
1261 WL_CONN("pval (%d) gval (%d)\n", pval, gval);
Arend van Sprielac24be62012-10-22 10:36:23 -07001262 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "wsec", pval | gval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001263 if (err) {
1264 WL_ERR("error (%d)\n", err);
1265 return err;
1266 }
1267
Arend van Spriel06bb1232012-09-27 14:17:56 +02001268 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001269 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1270 sec->cipher_group = sme->crypto.cipher_group;
1271
1272 return err;
1273}
1274
1275static s32
1276brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1277{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001278 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001279 struct brcmf_cfg80211_security *sec;
1280 s32 val = 0;
1281 s32 err = 0;
1282
1283 if (sme->crypto.n_akm_suites) {
Arend van Sprielac24be62012-10-22 10:36:23 -07001284 err = brcmf_fil_iovar_int_get(netdev_priv(ndev),
1285 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001286 if (err) {
1287 WL_ERR("could not get wpa_auth (%d)\n", err);
1288 return err;
1289 }
1290 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1291 switch (sme->crypto.akm_suites[0]) {
1292 case WLAN_AKM_SUITE_8021X:
1293 val = WPA_AUTH_UNSPECIFIED;
1294 break;
1295 case WLAN_AKM_SUITE_PSK:
1296 val = WPA_AUTH_PSK;
1297 break;
1298 default:
1299 WL_ERR("invalid cipher group (%d)\n",
1300 sme->crypto.cipher_group);
1301 return -EINVAL;
1302 }
1303 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1304 switch (sme->crypto.akm_suites[0]) {
1305 case WLAN_AKM_SUITE_8021X:
1306 val = WPA2_AUTH_UNSPECIFIED;
1307 break;
1308 case WLAN_AKM_SUITE_PSK:
1309 val = WPA2_AUTH_PSK;
1310 break;
1311 default:
1312 WL_ERR("invalid cipher group (%d)\n",
1313 sme->crypto.cipher_group);
1314 return -EINVAL;
1315 }
1316 }
1317
1318 WL_CONN("setting wpa_auth to %d\n", val);
Arend van Sprielac24be62012-10-22 10:36:23 -07001319 err = brcmf_fil_iovar_int_set(netdev_priv(ndev),
1320 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321 if (err) {
1322 WL_ERR("could not set wpa_auth (%d)\n", err);
1323 return err;
1324 }
1325 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001326 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001327 sec->wpa_auth = sme->crypto.akm_suites[0];
1328
1329 return err;
1330}
1331
1332static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001333brcmf_set_sharedkey(struct net_device *ndev,
1334 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001335{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001336 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337 struct brcmf_cfg80211_security *sec;
1338 struct brcmf_wsec_key key;
1339 s32 val;
1340 s32 err = 0;
1341
1342 WL_CONN("key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001343
Roland Vossena718e2f2011-10-12 20:51:24 +02001344 if (sme->key_len == 0)
1345 return 0;
1346
Arend van Spriel06bb1232012-09-27 14:17:56 +02001347 sec = &profile->sec;
Roland Vossena718e2f2011-10-12 20:51:24 +02001348 WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n",
1349 sec->wpa_versions, sec->cipher_pairwise);
1350
1351 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1352 return 0;
1353
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001354 if (!(sec->cipher_pairwise &
1355 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1356 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001357
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001358 memset(&key, 0, sizeof(key));
1359 key.len = (u32) sme->key_len;
1360 key.index = (u32) sme->key_idx;
1361 if (key.len > sizeof(key.data)) {
1362 WL_ERR("Too long key length (%u)\n", key.len);
1363 return -EINVAL;
1364 }
1365 memcpy(key.data, sme->key, key.len);
1366 key.flags = BRCMF_PRIMARY_KEY;
1367 switch (sec->cipher_pairwise) {
1368 case WLAN_CIPHER_SUITE_WEP40:
1369 key.algo = CRYPTO_ALGO_WEP1;
1370 break;
1371 case WLAN_CIPHER_SUITE_WEP104:
1372 key.algo = CRYPTO_ALGO_WEP128;
1373 break;
1374 default:
1375 WL_ERR("Invalid algorithm (%d)\n",
1376 sme->crypto.ciphers_pairwise[0]);
1377 return -EINVAL;
1378 }
1379 /* Set the new key/index */
1380 WL_CONN("key length (%d) key index (%d) algo (%d)\n",
1381 key.len, key.index, key.algo);
1382 WL_CONN("key \"%s\"\n", key.data);
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001383 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001384 if (err)
1385 return err;
1386
1387 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
1388 WL_CONN("set auth_type to shared key\n");
1389 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001390 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001391 if (err)
1392 WL_ERR("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001393 }
1394 return err;
1395}
1396
1397static s32
1398brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
1399 struct cfg80211_connect_params *sme)
1400{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001401 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001402 struct brcmf_if *ifp = netdev_priv(ndev);
1403 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001404 struct ieee80211_channel *chan = sme->channel;
1405 struct brcmf_join_params join_params;
1406 size_t join_params_size;
1407 struct brcmf_ssid ssid;
1408
1409 s32 err = 0;
1410
1411 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001412 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001413 return -EIO;
1414
1415 if (!sme->ssid) {
1416 WL_ERR("Invalid ssid\n");
1417 return -EOPNOTSUPP;
1418 }
1419
Arend van Sprielc1179032012-10-22 13:55:33 -07001420 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001421
1422 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001423 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001424 ieee80211_frequency_to_channel(chan->center_freq);
1425 WL_CONN("channel (%d), center_req (%d)\n",
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001426 cfg->channel, chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001427 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001428 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001429
1430 WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
1431
1432 err = brcmf_set_wpa_version(ndev, sme);
1433 if (err) {
1434 WL_ERR("wl_set_wpa_version failed (%d)\n", err);
1435 goto done;
1436 }
1437
1438 err = brcmf_set_auth_type(ndev, sme);
1439 if (err) {
1440 WL_ERR("wl_set_auth_type failed (%d)\n", err);
1441 goto done;
1442 }
1443
1444 err = brcmf_set_set_cipher(ndev, sme);
1445 if (err) {
1446 WL_ERR("wl_set_set_cipher failed (%d)\n", err);
1447 goto done;
1448 }
1449
1450 err = brcmf_set_key_mgmt(ndev, sme);
1451 if (err) {
1452 WL_ERR("wl_set_key_mgmt failed (%d)\n", err);
1453 goto done;
1454 }
1455
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001456 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001457 if (err) {
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001458 WL_ERR("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001459 goto done;
1460 }
1461
1462 memset(&join_params, 0, sizeof(join_params));
1463 join_params_size = sizeof(join_params.ssid_le);
1464
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001465 profile->ssid.SSID_len = min_t(u32,
1466 sizeof(ssid.SSID), (u32)sme->ssid_len);
1467 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
1468 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1469 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001470
Arend van Sprielba40d162012-10-22 13:55:38 -07001471 memset(join_params.params_le.bssid, 0xFF, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001472
1473 if (ssid.SSID_len < IEEE80211_MAX_SSID_LEN)
1474 WL_CONN("ssid \"%s\", len (%d)\n",
1475 ssid.SSID, ssid.SSID_len);
1476
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001477 brcmf_ch_to_chanspec(cfg->channel,
Arend van Spriel5b435de2011-10-05 13:19:03 +02001478 &join_params, &join_params_size);
Arend van Sprielc1179032012-10-22 13:55:33 -07001479 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001480 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001481 if (err)
1482 WL_ERR("WLC_SET_SSID failed (%d)\n", err);
1483
1484done:
1485 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001486 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001487 WL_TRACE("Exit\n");
1488 return err;
1489}
1490
1491static s32
1492brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1493 u16 reason_code)
1494{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001495 struct brcmf_if *ifp = netdev_priv(ndev);
1496 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001497 struct brcmf_scb_val_le scbval;
1498 s32 err = 0;
1499
1500 WL_TRACE("Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001501 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001502 return -EIO;
1503
Arend van Sprielc1179032012-10-22 13:55:33 -07001504 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001505
Arend van Spriel06bb1232012-09-27 14:17:56 +02001506 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001507 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001508 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001509 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001510 if (err)
1511 WL_ERR("error (%d)\n", err);
1512
Arend van Spriel5b435de2011-10-05 13:19:03 +02001513 WL_TRACE("Exit\n");
1514 return err;
1515}
1516
1517static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001518brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001519 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001520{
1521
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001522 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001523 struct net_device *ndev = cfg_to_ndev(cfg);
1524 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001525 u16 txpwrmw;
1526 s32 err = 0;
1527 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001528 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001529
1530 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001531 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001532 return -EIO;
1533
1534 switch (type) {
1535 case NL80211_TX_POWER_AUTOMATIC:
1536 break;
1537 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001538 case NL80211_TX_POWER_FIXED:
1539 if (dbm < 0) {
1540 WL_ERR("TX_POWER_FIXED - dbm is negative\n");
1541 err = -EINVAL;
1542 goto done;
1543 }
1544 break;
1545 }
1546 /* Make sure radio is off or on as far as software is concerned */
1547 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001548 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001549 if (err)
1550 WL_ERR("WLC_SET_RADIO error (%d)\n", err);
1551
1552 if (dbm > 0xffff)
1553 txpwrmw = 0xffff;
1554 else
1555 txpwrmw = (u16) dbm;
Arend van Sprielac24be62012-10-22 10:36:23 -07001556 err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
1557 (s32)brcmf_mw_to_qdbm(txpwrmw));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001558 if (err)
1559 WL_ERR("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001560 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001561
1562done:
1563 WL_TRACE("Exit\n");
1564 return err;
1565}
1566
Johannes Bergc8442112012-10-24 10:17:18 +02001567static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
1568 struct wireless_dev *wdev,
1569 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001570{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001571 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001572 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001573 s32 txpwrdbm;
1574 u8 result;
1575 s32 err = 0;
1576
1577 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001578 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001579 return -EIO;
1580
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001581 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001582 if (err) {
1583 WL_ERR("error (%d)\n", err);
1584 goto done;
1585 }
1586
1587 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02001588 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001589
1590done:
1591 WL_TRACE("Exit\n");
1592 return err;
1593}
1594
1595static s32
1596brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
1597 u8 key_idx, bool unicast, bool multicast)
1598{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001599 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001600 u32 index;
1601 u32 wsec;
1602 s32 err = 0;
1603
1604 WL_TRACE("Enter\n");
1605 WL_CONN("key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001606 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001607 return -EIO;
1608
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001609 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001610 if (err) {
1611 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
1612 goto done;
1613 }
1614
1615 if (wsec & WEP_ENABLED) {
1616 /* Just select a new current key */
1617 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001618 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001619 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001620 if (err)
1621 WL_ERR("error (%d)\n", err);
1622 }
1623done:
1624 WL_TRACE("Exit\n");
1625 return err;
1626}
1627
1628static s32
1629brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
1630 u8 key_idx, const u8 *mac_addr, struct key_params *params)
1631{
1632 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001633 s32 err = 0;
1634
1635 memset(&key, 0, sizeof(key));
1636 key.index = (u32) key_idx;
1637 /* Instead of bcast for ea address for default wep keys,
1638 driver needs it to be Null */
1639 if (!is_multicast_ether_addr(mac_addr))
1640 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
1641 key.len = (u32) params->key_len;
1642 /* check for key index change */
1643 if (key.len == 0) {
1644 /* key delete */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001645 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001646 if (err)
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001647 WL_ERR("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001648 } else {
1649 if (key.len > sizeof(key.data)) {
1650 WL_ERR("Invalid key length (%d)\n", key.len);
1651 return -EINVAL;
1652 }
1653
1654 WL_CONN("Setting the key index %d\n", key.index);
1655 memcpy(key.data, params->key, key.len);
1656
1657 if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
1658 u8 keybuf[8];
1659 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1660 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1661 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1662 }
1663
1664 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
1665 if (params->seq && params->seq_len == 6) {
1666 /* rx iv */
1667 u8 *ivptr;
1668 ivptr = (u8 *) params->seq;
1669 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
1670 (ivptr[3] << 8) | ivptr[2];
1671 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
1672 key.iv_initialized = true;
1673 }
1674
1675 switch (params->cipher) {
1676 case WLAN_CIPHER_SUITE_WEP40:
1677 key.algo = CRYPTO_ALGO_WEP1;
1678 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1679 break;
1680 case WLAN_CIPHER_SUITE_WEP104:
1681 key.algo = CRYPTO_ALGO_WEP128;
1682 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1683 break;
1684 case WLAN_CIPHER_SUITE_TKIP:
1685 key.algo = CRYPTO_ALGO_TKIP;
1686 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1687 break;
1688 case WLAN_CIPHER_SUITE_AES_CMAC:
1689 key.algo = CRYPTO_ALGO_AES_CCM;
1690 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1691 break;
1692 case WLAN_CIPHER_SUITE_CCMP:
1693 key.algo = CRYPTO_ALGO_AES_CCM;
1694 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
1695 break;
1696 default:
1697 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
1698 return -EINVAL;
1699 }
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001700 err = send_key_to_dongle(ndev, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001701 if (err)
1702 WL_ERR("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001703 }
1704 return err;
1705}
1706
1707static s32
1708brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
1709 u8 key_idx, bool pairwise, const u8 *mac_addr,
1710 struct key_params *params)
1711{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001712 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001713 struct brcmf_wsec_key key;
1714 s32 val;
1715 s32 wsec;
1716 s32 err = 0;
1717 u8 keybuf[8];
1718
1719 WL_TRACE("Enter\n");
1720 WL_CONN("key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001721 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001722 return -EIO;
1723
1724 if (mac_addr) {
1725 WL_TRACE("Exit");
1726 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
1727 }
1728 memset(&key, 0, sizeof(key));
1729
1730 key.len = (u32) params->key_len;
1731 key.index = (u32) key_idx;
1732
1733 if (key.len > sizeof(key.data)) {
1734 WL_ERR("Too long key length (%u)\n", key.len);
1735 err = -EINVAL;
1736 goto done;
1737 }
1738 memcpy(key.data, params->key, key.len);
1739
1740 key.flags = BRCMF_PRIMARY_KEY;
1741 switch (params->cipher) {
1742 case WLAN_CIPHER_SUITE_WEP40:
1743 key.algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001744 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001745 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1746 break;
1747 case WLAN_CIPHER_SUITE_WEP104:
1748 key.algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001749 val = WEP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001750 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1751 break;
1752 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel128ce3b2012-11-28 21:44:12 +01001753 if (ifp->vif->mode != WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001754 WL_CONN("Swapping key\n");
1755 memcpy(keybuf, &key.data[24], sizeof(keybuf));
1756 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
1757 memcpy(&key.data[16], keybuf, sizeof(keybuf));
1758 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001759 key.algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001760 val = TKIP_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001761 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1762 break;
1763 case WLAN_CIPHER_SUITE_AES_CMAC:
1764 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001765 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001766 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1767 break;
1768 case WLAN_CIPHER_SUITE_CCMP:
1769 key.algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001770 val = AES_ENABLED;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001771 WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
1772 break;
1773 default:
1774 WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
1775 err = -EINVAL;
1776 goto done;
1777 }
1778
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001779 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001780 if (err)
1781 goto done;
1782
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001783 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001784 if (err) {
1785 WL_ERR("get wsec error (%d)\n", err);
1786 goto done;
1787 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001788 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001789 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001790 if (err) {
1791 WL_ERR("set wsec error (%d)\n", err);
1792 goto done;
1793 }
1794
Arend van Spriel5b435de2011-10-05 13:19:03 +02001795done:
1796 WL_TRACE("Exit\n");
1797 return err;
1798}
1799
1800static s32
1801brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
1802 u8 key_idx, bool pairwise, const u8 *mac_addr)
1803{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001804 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001805 struct brcmf_wsec_key key;
1806 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001807
1808 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001809 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001810 return -EIO;
1811
Hante Meuleman256c3742012-11-05 16:22:28 -08001812 if (key_idx >= DOT11_MAX_DEFAULT_KEYS) {
1813 /* we ignore this key index in this case */
1814 WL_ERR("invalid key index (%d)\n", key_idx);
1815 return -EINVAL;
1816 }
1817
Arend van Spriel5b435de2011-10-05 13:19:03 +02001818 memset(&key, 0, sizeof(key));
1819
1820 key.index = (u32) key_idx;
1821 key.flags = BRCMF_PRIMARY_KEY;
1822 key.algo = CRYPTO_ALGO_OFF;
1823
1824 WL_CONN("key index (%d)\n", key_idx);
1825
1826 /* Set the new key/index */
Arend van Spriel2eaba7e2012-10-22 10:36:26 -07001827 err = send_key_to_dongle(ndev, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001828
Arend van Spriel5b435de2011-10-05 13:19:03 +02001829 WL_TRACE("Exit\n");
1830 return err;
1831}
1832
1833static s32
1834brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
1835 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
1836 void (*callback) (void *cookie, struct key_params * params))
1837{
1838 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001839 struct brcmf_if *ifp = netdev_priv(ndev);
1840 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001841 struct brcmf_cfg80211_security *sec;
1842 s32 wsec;
1843 s32 err = 0;
1844
1845 WL_TRACE("Enter\n");
1846 WL_CONN("key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07001847 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001848 return -EIO;
1849
1850 memset(&params, 0, sizeof(params));
1851
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001852 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001853 if (err) {
1854 WL_ERR("WLC_GET_WSEC error (%d)\n", err);
1855 /* Ignore this error, may happen during DISASSOC */
1856 err = -EAGAIN;
1857 goto done;
1858 }
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001859 switch (wsec & ~SES_OW_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001860 case WEP_ENABLED:
Arend van Spriel06bb1232012-09-27 14:17:56 +02001861 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001862 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
1863 params.cipher = WLAN_CIPHER_SUITE_WEP40;
1864 WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
1865 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
1866 params.cipher = WLAN_CIPHER_SUITE_WEP104;
1867 WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
1868 }
1869 break;
1870 case TKIP_ENABLED:
1871 params.cipher = WLAN_CIPHER_SUITE_TKIP;
1872 WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
1873 break;
1874 case AES_ENABLED:
1875 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
1876 WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
1877 break;
1878 default:
1879 WL_ERR("Invalid algo (0x%x)\n", wsec);
1880 err = -EINVAL;
1881 goto done;
1882 }
1883 callback(cookie, &params);
1884
1885done:
1886 WL_TRACE("Exit\n");
1887 return err;
1888}
1889
1890static s32
1891brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
1892 struct net_device *ndev, u8 key_idx)
1893{
1894 WL_INFO("Not supported\n");
1895
1896 return -EOPNOTSUPP;
1897}
1898
1899static s32
1900brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman1a873342012-09-27 14:17:54 +02001901 u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001902{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001903 struct brcmf_if *ifp = netdev_priv(ndev);
1904 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001905 struct brcmf_scb_val_le scb_val;
1906 int rssi;
1907 s32 rate;
1908 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02001909 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001910 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001911
Hante Meuleman1a873342012-09-27 14:17:54 +02001912 WL_TRACE("Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07001913 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001914 return -EIO;
1915
Arend van Spriel128ce3b2012-11-28 21:44:12 +01001916 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001917 memcpy(&sta_info_le, mac, ETH_ALEN);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001918 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07001919 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001920 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02001921 if (err < 0) {
1922 WL_ERR("GET STA INFO failed, %d\n", err);
1923 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02001924 }
Hante Meuleman1a873342012-09-27 14:17:54 +02001925 sinfo->filled = STATION_INFO_INACTIVE_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001926 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
1927 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001928 sinfo->filled |= STATION_INFO_CONNECTED_TIME;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001929 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02001930 }
1931 WL_TRACE("STA idle time : %d ms, connected time :%d sec\n",
1932 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel128ce3b2012-11-28 21:44:12 +01001933 } else if (ifp->vif->mode == WL_MODE_BSS) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001934 if (memcmp(mac, bssid, ETH_ALEN)) {
1935 WL_ERR("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
1936 mac, bssid);
1937 err = -ENOENT;
1938 goto done;
1939 }
1940 /* Report the current tx rate */
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001941 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02001942 if (err) {
1943 WL_ERR("Could not get rate (%d)\n", err);
1944 goto done;
1945 } else {
1946 sinfo->filled |= STATION_INFO_TX_BITRATE;
1947 sinfo->txrate.legacy = rate * 5;
1948 WL_CONN("Rate %d Mbps\n", rate / 2);
1949 }
1950
Arend van Sprielc1179032012-10-22 13:55:33 -07001951 if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
1952 &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02001953 memset(&scb_val, 0, sizeof(scb_val));
Arend van Sprielc1179032012-10-22 13:55:33 -07001954 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
1955 &scb_val, sizeof(scb_val));
Hante Meuleman1a873342012-09-27 14:17:54 +02001956 if (err) {
1957 WL_ERR("Could not get rssi (%d)\n", err);
1958 goto done;
1959 } else {
1960 rssi = le32_to_cpu(scb_val.val);
1961 sinfo->filled |= STATION_INFO_SIGNAL;
1962 sinfo->signal = rssi;
1963 WL_CONN("RSSI %d dBm\n", rssi);
1964 }
1965 }
1966 } else
1967 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001968done:
1969 WL_TRACE("Exit\n");
1970 return err;
1971}
1972
1973static s32
1974brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
1975 bool enabled, s32 timeout)
1976{
1977 s32 pm;
1978 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001979 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07001980 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001981
1982 WL_TRACE("Enter\n");
1983
1984 /*
1985 * Powersave enable/disable request is coming from the
1986 * cfg80211 even before the interface is up. In that
1987 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001988 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02001989 * FW later while initializing the dongle
1990 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001991 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07001992 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001993
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001994 WL_INFO("Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001995 goto done;
1996 }
1997
1998 pm = enabled ? PM_FAST : PM_OFF;
1999 WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
2000
Arend van Sprielc1179032012-10-22 13:55:33 -07002001 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002002 if (err) {
2003 if (err == -ENODEV)
2004 WL_ERR("net_device is not ready yet\n");
2005 else
2006 WL_ERR("error (%d)\n", err);
2007 }
2008done:
2009 WL_TRACE("Exit\n");
2010 return err;
2011}
2012
2013static s32
2014brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev,
2015 const u8 *addr,
2016 const struct cfg80211_bitrate_mask *mask)
2017{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002018 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002019 struct brcm_rateset_le rateset_le;
2020 s32 rate;
2021 s32 val;
2022 s32 err_bg;
2023 s32 err_a;
2024 u32 legacy;
2025 s32 err = 0;
2026
2027 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002028 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002029 return -EIO;
2030
2031 /* addr param is always NULL. ignore it */
2032 /* Get current rateset */
Hante Meulemanb87e2c42012-11-14 18:46:23 -08002033 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CURR_RATESET,
Arend van Sprielac24be62012-10-22 10:36:23 -07002034 &rateset_le, sizeof(rateset_le));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002035 if (err) {
2036 WL_ERR("could not get current rateset (%d)\n", err);
2037 goto done;
2038 }
2039
2040 legacy = ffs(mask->control[IEEE80211_BAND_2GHZ].legacy & 0xFFFF);
2041 if (!legacy)
2042 legacy = ffs(mask->control[IEEE80211_BAND_5GHZ].legacy &
2043 0xFFFF);
2044
2045 val = wl_g_rates[legacy - 1].bitrate * 100000;
2046
2047 if (val < le32_to_cpu(rateset_le.count))
2048 /* Select rate by rateset index */
2049 rate = rateset_le.rates[val] & 0x7f;
2050 else
2051 /* Specified rate in bps */
2052 rate = val / 500000;
2053
2054 WL_CONN("rate %d mbps\n", rate / 2);
2055
2056 /*
2057 *
2058 * Set rate override,
2059 * Since the is a/b/g-blind, both a/bg_rate are enforced.
2060 */
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002061 err_bg = brcmf_fil_iovar_int_set(ifp, "bg_rate", rate);
2062 err_a = brcmf_fil_iovar_int_set(ifp, "a_rate", rate);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002063 if (err_bg && err_a) {
2064 WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
2065 err = err_bg | err_a;
2066 }
2067
2068done:
2069 WL_TRACE("Exit\n");
2070 return err;
2071}
2072
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002073static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002074 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002075{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002076 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002077 struct ieee80211_channel *notify_channel;
2078 struct cfg80211_bss *bss;
2079 struct ieee80211_supported_band *band;
2080 s32 err = 0;
2081 u16 channel;
2082 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002083 u16 notify_capability;
2084 u16 notify_interval;
2085 u8 *notify_ie;
2086 size_t notify_ielen;
2087 s32 notify_signal;
2088
2089 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
2090 WL_ERR("Bss info is larger than buffer. Discarding\n");
2091 return 0;
2092 }
2093
2094 channel = bi->ctl_ch ? bi->ctl_ch :
2095 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
2096
2097 if (channel <= CH_MAX_2G_CHANNEL)
2098 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2099 else
2100 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2101
2102 freq = ieee80211_channel_to_frequency(channel, band->band);
2103 notify_channel = ieee80211_get_channel(wiphy, freq);
2104
Arend van Spriel5b435de2011-10-05 13:19:03 +02002105 notify_capability = le16_to_cpu(bi->capability);
2106 notify_interval = le16_to_cpu(bi->beacon_period);
2107 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2108 notify_ielen = le32_to_cpu(bi->ie_length);
2109 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2110
2111 WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
2112 bi->BSSID[0], bi->BSSID[1], bi->BSSID[2],
2113 bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]);
2114 WL_CONN("Channel: %d(%d)\n", channel, freq);
2115 WL_CONN("Capability: %X\n", notify_capability);
2116 WL_CONN("Beacon interval: %d\n", notify_interval);
2117 WL_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 Spriel5b435de2011-10-05 13:19:03 +02002150 WL_ERR("Version %d != WL_BSS_INFO_VERSION\n",
2151 bss_list->version);
2152 return -EOPNOTSUPP;
2153 }
2154 WL_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
2182 WL_TRACE("Enter\n");
2183
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) {
2195 WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
2196 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
2218 WL_CONN("channel: %d(%d)\n", channel, freq);
2219 WL_CONN("capability: %X\n", notify_capability);
2220 WL_CONN("beacon interval: %d\n", notify_interval);
2221 WL_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
2238 WL_TRACE("Exit\n");
2239
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
2329 WL_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) {
2339 WL_ERR("Could not get bss info %d\n", err);
2340 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) {
2364 WL_ERR("wl dtim_assoc failed (%d)\n", err);
2365 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:
2371 WL_TRACE("Exit");
2372 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) {
Hante Meulemane756af52012-09-11 21:18:52 +02002404 WL_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)) {
2460 WL_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) {
2466 WL_SCAN("ESCAN Partial result\n");
2467 escan_result_le = (struct brcmf_escan_result_le *) data;
2468 if (!escan_result_le) {
2469 WL_ERR("Invalid escan result (NULL pointer)\n");
2470 goto exit;
2471 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002472 if (!cfg->scan_request) {
Hante Meulemane756af52012-09-11 21:18:52 +02002473 WL_SCAN("result without cfg80211 request\n");
2474 goto exit;
2475 }
2476
2477 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
2478 WL_ERR("Invalid bss_count %d: ignoring\n",
2479 escan_result_le->bss_count);
2480 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)) {
2487 WL_ERR("Invalid bss_info length %d: ignoring\n",
2488 bi_length);
2489 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) {
2496 WL_ERR("Ignoring IBSS result\n");
2497 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) {
2504 WL_ERR("Buffer is too small: ignoring\n");
2505 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
2530 WL_ERR("Unexpected scan result 0x%x\n", status);
2531 }
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 Spriel5b435de2011-10-05 13:19:03 +02002561 WL_TRACE("Enter\n");
2562
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
2573 WL_TRACE("Enter\n");
2574
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 Spriel5b435de2011-10-05 13:19:03 +02002607 WL_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
2622 WL_CONN("No of elements %d\n", pmkid_len);
2623 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002624 WL_CONN("PMKID[%d]: %pM =\n", i,
2625 &pmk_list->pmkids.pmkid[i].BSSID);
2626 for (j = 0; j < WLAN_PMKID_LEN; j++)
2627 WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]);
2628 }
2629
2630 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07002631 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
2632 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002633
2634 return err;
2635}
2636
2637static s32
2638brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2639 struct cfg80211_pmksa *pmksa)
2640{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002641 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002642 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002643 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002644 s32 err = 0;
2645 int i;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002646 int pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002647
2648 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002649 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002650 return -EIO;
2651
Arend van Spriel40c8e952011-10-12 20:51:20 +02002652 pmkid_len = le32_to_cpu(pmkids->npmkid);
2653 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002654 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
2655 break;
2656 if (i < WL_NUM_PMKIDS_MAX) {
2657 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
2658 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002659 if (i == pmkid_len) {
2660 pmkid_len++;
2661 pmkids->npmkid = cpu_to_le32(pmkid_len);
2662 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002663 } else
2664 err = -EINVAL;
2665
2666 WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
Arend van Spriel40c8e952011-10-12 20:51:20 +02002667 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002668 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel40c8e952011-10-12 20:51:20 +02002669 WL_CONN("%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002670
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002671 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002672
2673 WL_TRACE("Exit\n");
2674 return err;
2675}
2676
2677static s32
2678brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
2679 struct cfg80211_pmksa *pmksa)
2680{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002681 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002682 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002683 struct pmkid_list pmkid;
2684 s32 err = 0;
Arend van Spriel40c8e952011-10-12 20:51:20 +02002685 int i, pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002686
2687 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002688 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002689 return -EIO;
2690
2691 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
2692 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
2693
2694 WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
2695 &pmkid.pmkid[0].BSSID);
2696 for (i = 0; i < WLAN_PMKID_LEN; i++)
2697 WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]);
2698
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002699 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02002700 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002701 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002702 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002703 ETH_ALEN))
2704 break;
2705
Arend van Spriel40c8e952011-10-12 20:51:20 +02002706 if ((pmkid_len > 0)
2707 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002708 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002709 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02002710 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002711 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
2712 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002713 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002714 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
2715 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002716 WLAN_PMKID_LEN);
2717 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002718 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002719 } else
2720 err = -EINVAL;
2721
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002722 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002723
2724 WL_TRACE("Exit\n");
2725 return err;
2726
2727}
2728
2729static s32
2730brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
2731{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002732 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002733 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002734 s32 err = 0;
2735
2736 WL_TRACE("Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002737 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002738 return -EIO;
2739
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002740 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
2741 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002742
2743 WL_TRACE("Exit\n");
2744 return err;
2745
2746}
2747
Arend van Spriele5806072012-09-19 22:21:08 +02002748/*
2749 * PFN result doesn't have all the info which are
2750 * required by the supplicant
2751 * (For e.g IEs) Do a target Escan so that sched scan results are reported
2752 * via wl_inform_single_bss in the required format. Escan does require the
2753 * scan request in the form of cfg80211_scan_request. For timebeing, create
2754 * cfg80211_scan_request one out of the received PNO event.
2755 */
2756static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002757brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02002758 const struct brcmf_event_msg *e, void *data)
2759{
Arend van Spriel19937322012-11-05 16:22:32 -08002760 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
2761 struct net_device *ndev = ifp->ndev;
Arend van Spriele5806072012-09-19 22:21:08 +02002762 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
2763 struct cfg80211_scan_request *request = NULL;
2764 struct cfg80211_ssid *ssid = NULL;
2765 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002766 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002767 int err = 0;
2768 int channel_req = 0;
2769 int band = 0;
2770 struct brcmf_pno_scanresults_le *pfn_result;
2771 u32 result_count;
2772 u32 status;
2773
2774 WL_SCAN("Enter\n");
2775
Arend van Spriel5c36b992012-11-14 18:46:05 -08002776 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriele5806072012-09-19 22:21:08 +02002777 WL_SCAN("PFN NET LOST event. Do Nothing\n");
2778 return 0;
2779 }
2780
2781 pfn_result = (struct brcmf_pno_scanresults_le *)data;
2782 result_count = le32_to_cpu(pfn_result->count);
2783 status = le32_to_cpu(pfn_result->status);
2784
2785 /*
2786 * PFN event is limited to fit 512 bytes so we may get
2787 * multiple NET_FOUND events. For now place a warning here.
2788 */
2789 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
2790 WL_SCAN("PFN NET FOUND event. count: %d\n", result_count);
2791 if (result_count > 0) {
2792 int i;
2793
2794 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03002795 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
2796 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02002797 if (!request || !ssid || !channel) {
2798 err = -ENOMEM;
2799 goto out_err;
2800 }
2801
2802 request->wiphy = wiphy;
2803 data += sizeof(struct brcmf_pno_scanresults_le);
2804 netinfo_start = (struct brcmf_pno_net_info_le *)data;
2805
2806 for (i = 0; i < result_count; i++) {
2807 netinfo = &netinfo_start[i];
2808 if (!netinfo) {
2809 WL_ERR("Invalid netinfo ptr. index: %d\n", i);
2810 err = -EINVAL;
2811 goto out_err;
2812 }
2813
2814 WL_SCAN("SSID:%s Channel:%d\n",
2815 netinfo->SSID, netinfo->channel);
2816 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
2817 ssid[i].ssid_len = netinfo->SSID_len;
2818 request->n_ssids++;
2819
2820 channel_req = netinfo->channel;
2821 if (channel_req <= CH_MAX_2G_CHANNEL)
2822 band = NL80211_BAND_2GHZ;
2823 else
2824 band = NL80211_BAND_5GHZ;
2825 channel[i].center_freq =
2826 ieee80211_channel_to_frequency(channel_req,
2827 band);
2828 channel[i].band = band;
2829 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
2830 request->channels[i] = &channel[i];
2831 request->n_channels++;
2832 }
2833
2834 /* assign parsed ssid array */
2835 if (request->n_ssids)
2836 request->ssids = &ssid[0];
2837
Arend van Sprielc1179032012-10-22 13:55:33 -07002838 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02002839 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002840 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02002841 }
2842
Arend van Sprielc1179032012-10-22 13:55:33 -07002843 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002844 err = brcmf_do_escan(cfg, wiphy, ndev, request);
Arend van Spriele5806072012-09-19 22:21:08 +02002845 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07002846 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02002847 goto out_err;
2848 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002849 cfg->sched_escan = true;
2850 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02002851 } else {
2852 WL_ERR("FALSE PNO Event. (pfn_count == 0)\n");
2853 goto out_err;
2854 }
2855
2856 kfree(ssid);
2857 kfree(channel);
2858 kfree(request);
2859 return 0;
2860
2861out_err:
2862 kfree(ssid);
2863 kfree(channel);
2864 kfree(request);
2865 cfg80211_sched_scan_stopped(wiphy);
2866 return err;
2867}
2868
Arend van Spriele5806072012-09-19 22:21:08 +02002869static int brcmf_dev_pno_clean(struct net_device *ndev)
2870{
Arend van Spriele5806072012-09-19 22:21:08 +02002871 int ret;
2872
2873 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002874 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002875 if (ret == 0) {
2876 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07002877 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
2878 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002879 }
2880 if (ret < 0)
2881 WL_ERR("failed code %d\n", ret);
2882
2883 return ret;
2884}
2885
2886static int brcmf_dev_pno_config(struct net_device *ndev)
2887{
2888 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02002889
2890 memset(&pfn_param, 0, sizeof(pfn_param));
2891 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
2892
2893 /* set extra pno params */
2894 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
2895 pfn_param.repeat = BRCMF_PNO_REPEAT;
2896 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
2897
2898 /* set up pno scan fr */
2899 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
2900
Arend van Sprielac24be62012-10-22 10:36:23 -07002901 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
2902 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02002903}
2904
2905static int
2906brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
2907 struct net_device *ndev,
2908 struct cfg80211_sched_scan_request *request)
2909{
Arend van Sprielc1179032012-10-22 13:55:33 -07002910 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002911 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02002912 struct brcmf_pno_net_param_le pfn;
2913 int i;
2914 int ret = 0;
2915
2916 WL_SCAN("Enter n_match_sets:%d n_ssids:%d\n",
2917 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07002918 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2919 WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02002920 return -EAGAIN;
2921 }
2922
2923 if (!request || !request->n_ssids || !request->n_match_sets) {
2924 WL_ERR("Invalid sched scan req!! n_ssids:%d\n",
Yuanhan Liu9495b312012-10-18 18:10:56 +08002925 request ? request->n_ssids : 0);
Arend van Spriele5806072012-09-19 22:21:08 +02002926 return -EINVAL;
2927 }
2928
2929 if (request->n_ssids > 0) {
2930 for (i = 0; i < request->n_ssids; i++) {
2931 /* Active scan req for ssids */
2932 WL_SCAN(">>> Active scan req for ssid (%s)\n",
2933 request->ssids[i].ssid);
2934
2935 /*
2936 * match_set ssids is a supert set of n_ssid list,
2937 * so we need not add these set seperately.
2938 */
2939 }
2940 }
2941
2942 if (request->n_match_sets > 0) {
2943 /* clean up everything */
2944 ret = brcmf_dev_pno_clean(ndev);
2945 if (ret < 0) {
2946 WL_ERR("failed error=%d\n", ret);
2947 return ret;
2948 }
2949
2950 /* configure pno */
2951 ret = brcmf_dev_pno_config(ndev);
2952 if (ret < 0) {
2953 WL_ERR("PNO setup failed!! ret=%d\n", ret);
2954 return -EINVAL;
2955 }
2956
2957 /* configure each match set */
2958 for (i = 0; i < request->n_match_sets; i++) {
2959 struct cfg80211_ssid *ssid;
2960 u32 ssid_len;
2961
2962 ssid = &request->match_sets[i].ssid;
2963 ssid_len = ssid->ssid_len;
2964
2965 if (!ssid_len) {
2966 WL_ERR("skip broadcast ssid\n");
2967 continue;
2968 }
2969 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
2970 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
2971 pfn.wsec = cpu_to_le32(0);
2972 pfn.infra = cpu_to_le32(1);
2973 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
2974 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
2975 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07002976 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07002977 sizeof(pfn));
Arend van Spriele5806072012-09-19 22:21:08 +02002978 WL_SCAN(">>> PNO filter %s for ssid (%s)\n",
2979 ret == 0 ? "set" : "failed",
2980 ssid->ssid);
2981 }
2982 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07002983 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriele5806072012-09-19 22:21:08 +02002984 WL_ERR("PNO enable failed!! ret=%d\n", ret);
2985 return -EINVAL;
2986 }
2987 } else {
2988 return -EINVAL;
2989 }
2990
2991 return 0;
2992}
2993
2994static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
2995 struct net_device *ndev)
2996{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002997 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02002998
2999 WL_SCAN("enter\n");
3000 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003001 if (cfg->sched_escan)
3002 brcmf_notify_escan_complete(cfg, ndev, true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003003 return 0;
3004}
Arend van Spriele5806072012-09-19 22:21:08 +02003005
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003006#ifdef CONFIG_NL80211_TESTMODE
3007static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len)
3008{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003009 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003010 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003011 struct brcmf_dcmd *dcmd = data;
3012 struct sk_buff *reply;
3013 int ret;
3014
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003015 WL_TRACE("cmd %x set %d buf %p len %d\n", dcmd->cmd, dcmd->set,
3016 dcmd->buf, dcmd->len);
3017
3018 if (dcmd->set)
Arend van Sprielac24be62012-10-22 10:36:23 -07003019 ret = brcmf_fil_cmd_data_set(netdev_priv(ndev), dcmd->cmd,
3020 dcmd->buf, dcmd->len);
Hante Meulemanf368a5b2012-10-22 10:36:16 -07003021 else
Arend van Sprielac24be62012-10-22 10:36:23 -07003022 ret = brcmf_fil_cmd_data_get(netdev_priv(ndev), dcmd->cmd,
3023 dcmd->buf, dcmd->len);
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003024 if (ret == 0) {
3025 reply = cfg80211_testmode_alloc_reply_skb(wiphy, sizeof(*dcmd));
3026 nla_put(reply, NL80211_ATTR_TESTDATA, sizeof(*dcmd), dcmd);
3027 ret = cfg80211_testmode_reply(reply);
3028 }
3029 return ret;
3030}
3031#endif
3032
Hante Meuleman1a873342012-09-27 14:17:54 +02003033static s32 brcmf_configure_opensecurity(struct net_device *ndev, s32 bssidx)
3034{
Arend van Sprielac24be62012-10-22 10:36:23 -07003035 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003036 s32 err;
3037
3038 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003039 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003040 if (err < 0) {
3041 WL_ERR("auth error %d\n", err);
3042 return err;
3043 }
3044 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003045 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003046 if (err < 0) {
3047 WL_ERR("wsec error %d\n", err);
3048 return err;
3049 }
3050 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003051 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003052 if (err < 0) {
3053 WL_ERR("wpa_auth error %d\n", err);
3054 return err;
3055 }
3056
3057 return 0;
3058}
3059
3060static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3061{
3062 if (is_rsn_ie)
3063 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3064
3065 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3066}
3067
3068static s32
3069brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,
Arend van Spriel34778522012-11-05 16:22:19 -08003070 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003071{
Arend van Sprielac24be62012-10-22 10:36:23 -07003072 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003073 u32 auth = 0; /* d11 open authentication */
3074 u16 count;
3075 s32 err = 0;
3076 s32 len = 0;
3077 u32 i;
3078 u32 wsec;
3079 u32 pval = 0;
3080 u32 gval = 0;
3081 u32 wpa_auth = 0;
3082 u32 offset;
3083 u8 *data;
3084 u16 rsn_cap;
3085 u32 wme_bss_disable;
3086
3087 WL_TRACE("Enter\n");
3088 if (wpa_ie == NULL)
3089 goto exit;
3090
3091 len = wpa_ie->len + TLV_HDR_LEN;
3092 data = (u8 *)wpa_ie;
3093 offset = 0;
3094 if (!is_rsn_ie)
3095 offset += VS_IE_FIXED_HDR_LEN;
3096 offset += WPA_IE_VERSION_LEN;
3097
3098 /* check for multicast cipher suite */
3099 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3100 err = -EINVAL;
3101 WL_ERR("no multicast cipher suite\n");
3102 goto exit;
3103 }
3104
3105 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3106 err = -EINVAL;
3107 WL_ERR("ivalid OUI\n");
3108 goto exit;
3109 }
3110 offset += TLV_OUI_LEN;
3111
3112 /* pick up multicast cipher */
3113 switch (data[offset]) {
3114 case WPA_CIPHER_NONE:
3115 gval = 0;
3116 break;
3117 case WPA_CIPHER_WEP_40:
3118 case WPA_CIPHER_WEP_104:
3119 gval = WEP_ENABLED;
3120 break;
3121 case WPA_CIPHER_TKIP:
3122 gval = TKIP_ENABLED;
3123 break;
3124 case WPA_CIPHER_AES_CCM:
3125 gval = AES_ENABLED;
3126 break;
3127 default:
3128 err = -EINVAL;
3129 WL_ERR("Invalid multi cast cipher info\n");
3130 goto exit;
3131 }
3132
3133 offset++;
3134 /* walk thru unicast cipher list and pick up what we recognize */
3135 count = data[offset] + (data[offset + 1] << 8);
3136 offset += WPA_IE_SUITE_COUNT_LEN;
3137 /* Check for unicast suite(s) */
3138 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3139 err = -EINVAL;
3140 WL_ERR("no unicast cipher suite\n");
3141 goto exit;
3142 }
3143 for (i = 0; i < count; i++) {
3144 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3145 err = -EINVAL;
3146 WL_ERR("ivalid OUI\n");
3147 goto exit;
3148 }
3149 offset += TLV_OUI_LEN;
3150 switch (data[offset]) {
3151 case WPA_CIPHER_NONE:
3152 break;
3153 case WPA_CIPHER_WEP_40:
3154 case WPA_CIPHER_WEP_104:
3155 pval |= WEP_ENABLED;
3156 break;
3157 case WPA_CIPHER_TKIP:
3158 pval |= TKIP_ENABLED;
3159 break;
3160 case WPA_CIPHER_AES_CCM:
3161 pval |= AES_ENABLED;
3162 break;
3163 default:
3164 WL_ERR("Ivalid unicast security info\n");
3165 }
3166 offset++;
3167 }
3168 /* walk thru auth management suite list and pick up what we recognize */
3169 count = data[offset] + (data[offset + 1] << 8);
3170 offset += WPA_IE_SUITE_COUNT_LEN;
3171 /* Check for auth key management suite(s) */
3172 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3173 err = -EINVAL;
3174 WL_ERR("no auth key mgmt suite\n");
3175 goto exit;
3176 }
3177 for (i = 0; i < count; i++) {
3178 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3179 err = -EINVAL;
3180 WL_ERR("ivalid OUI\n");
3181 goto exit;
3182 }
3183 offset += TLV_OUI_LEN;
3184 switch (data[offset]) {
3185 case RSN_AKM_NONE:
3186 WL_TRACE("RSN_AKM_NONE\n");
3187 wpa_auth |= WPA_AUTH_NONE;
3188 break;
3189 case RSN_AKM_UNSPECIFIED:
3190 WL_TRACE("RSN_AKM_UNSPECIFIED\n");
3191 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3192 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3193 break;
3194 case RSN_AKM_PSK:
3195 WL_TRACE("RSN_AKM_PSK\n");
3196 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3197 (wpa_auth |= WPA_AUTH_PSK);
3198 break;
3199 default:
3200 WL_ERR("Ivalid key mgmt info\n");
3201 }
3202 offset++;
3203 }
3204
3205 if (is_rsn_ie) {
3206 wme_bss_disable = 1;
3207 if ((offset + RSN_CAP_LEN) <= len) {
3208 rsn_cap = data[offset] + (data[offset + 1] << 8);
3209 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3210 wme_bss_disable = 0;
3211 }
3212 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003213 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003214 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003215 if (err < 0) {
3216 WL_ERR("wme_bss_disable error %d\n", err);
3217 goto exit;
3218 }
3219 }
3220 /* FOR WPS , set SES_OW_ENABLED */
3221 wsec = (pval | gval | SES_OW_ENABLED);
3222
3223 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003224 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003225 if (err < 0) {
3226 WL_ERR("auth error %d\n", err);
3227 goto exit;
3228 }
3229 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003230 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003231 if (err < 0) {
3232 WL_ERR("wsec error %d\n", err);
3233 goto exit;
3234 }
3235 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003236 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003237 if (err < 0) {
3238 WL_ERR("wpa_auth error %d\n", err);
3239 goto exit;
3240 }
3241
3242exit:
3243 return err;
3244}
3245
3246static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003247brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003248 struct parsed_vndr_ies *vndr_ies)
3249{
3250 s32 err = 0;
3251 struct brcmf_vs_tlv *vndrie;
3252 struct brcmf_tlv *ie;
3253 struct parsed_vndr_ie_info *parsed_info;
3254 s32 remaining_len;
3255
3256 remaining_len = (s32)vndr_ie_len;
3257 memset(vndr_ies, 0, sizeof(*vndr_ies));
3258
3259 ie = (struct brcmf_tlv *)vndr_ie_buf;
3260 while (ie) {
3261 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3262 goto next;
3263 vndrie = (struct brcmf_vs_tlv *)ie;
3264 /* len should be bigger than OUI length + one */
3265 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
3266 WL_ERR("invalid vndr ie. length is too small %d\n",
3267 vndrie->len);
3268 goto next;
3269 }
3270 /* if wpa or wme ie, do not add ie */
3271 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3272 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3273 (vndrie->oui_type == WME_OUI_TYPE))) {
3274 WL_TRACE("Found WPA/WME oui. Do not add it\n");
3275 goto next;
3276 }
3277
3278 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3279
3280 /* save vndr ie information */
3281 parsed_info->ie_ptr = (char *)vndrie;
3282 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3283 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3284
3285 vndr_ies->count++;
3286
3287 WL_TRACE("** OUI %02x %02x %02x, type 0x%02x\n",
3288 parsed_info->vndrie.oui[0],
3289 parsed_info->vndrie.oui[1],
3290 parsed_info->vndrie.oui[2],
3291 parsed_info->vndrie.oui_type);
3292
3293 if (vndr_ies->count >= MAX_VNDR_IE_NUMBER)
3294 break;
3295next:
3296 remaining_len -= ie->len;
3297 if (remaining_len <= 2)
3298 ie = NULL;
3299 else
3300 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len);
3301 }
3302 return err;
3303}
3304
3305static u32
3306brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3307{
3308
3309 __le32 iecount_le;
3310 __le32 pktflag_le;
3311
3312 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3313 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3314
3315 iecount_le = cpu_to_le32(1);
3316 memcpy(&iebuf[VNDR_IE_COUNT_OFFSET], &iecount_le, sizeof(iecount_le));
3317
3318 pktflag_le = cpu_to_le32(pktflag);
3319 memcpy(&iebuf[VNDR_IE_PKTFLAG_OFFSET], &pktflag_le, sizeof(pktflag_le));
3320
3321 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3322
3323 return ie_len + VNDR_IE_HDR_SIZE;
3324}
3325
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003326static
Arend van Spriel1332e262012-11-05 16:22:18 -08003327s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3328 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003329{
Arend van Spriel1332e262012-11-05 16:22:18 -08003330 struct brcmf_if *ifp;
3331 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003332 s32 err = 0;
3333 u8 *iovar_ie_buf;
3334 u8 *curr_ie_buf;
3335 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003336 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003337 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003338 u32 del_add_ie_buf_len = 0;
3339 u32 total_ie_buf_len = 0;
3340 u32 parsed_ie_buf_len = 0;
3341 struct parsed_vndr_ies old_vndr_ies;
3342 struct parsed_vndr_ies new_vndr_ies;
3343 struct parsed_vndr_ie_info *vndrie_info;
3344 s32 i;
3345 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003346 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003347
Arend van Spriel1332e262012-11-05 16:22:18 -08003348 if (!vif)
3349 return -ENODEV;
3350 ifp = vif->ifp;
3351 saved_ie = &vif->saved_ie;
3352
3353 WL_TRACE("bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003354 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3355 if (!iovar_ie_buf)
3356 return -ENOMEM;
3357 curr_ie_buf = iovar_ie_buf;
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003358 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003359 switch (pktflag) {
3360 case VNDR_IE_PRBRSP_FLAG:
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003361 mgmt_ie_buf = saved_ie->probe_res_ie;
3362 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3363 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003364 break;
3365 case VNDR_IE_BEACON_FLAG:
Arend van Spriel8ff5dc92012-10-22 13:55:41 -07003366 mgmt_ie_buf = saved_ie->beacon_ie;
3367 mgmt_ie_len = &saved_ie->beacon_ie_len;
3368 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
Hante Meuleman1a873342012-09-27 14:17:54 +02003369 break;
3370 default:
3371 err = -EPERM;
3372 WL_ERR("not suitable type\n");
3373 goto exit;
3374 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003375 } else {
3376 err = -EPERM;
3377 WL_ERR("not suitable type\n");
3378 goto exit;
3379 }
3380
3381 if (vndr_ie_len > mgmt_ie_buf_len) {
3382 err = -ENOMEM;
3383 WL_ERR("extra IE size too big\n");
3384 goto exit;
3385 }
3386
3387 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3388 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3389 ptr = curr_ie_buf;
3390 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3391 for (i = 0; i < new_vndr_ies.count; i++) {
3392 vndrie_info = &new_vndr_ies.ie_info[i];
3393 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3394 vndrie_info->ie_len);
3395 parsed_ie_buf_len += vndrie_info->ie_len;
3396 }
3397 }
3398
3399 if (mgmt_ie_buf != NULL) {
3400 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3401 (memcmp(mgmt_ie_buf, curr_ie_buf,
3402 parsed_ie_buf_len) == 0)) {
3403 WL_TRACE("Previous mgmt IE is equals to current IE");
3404 goto exit;
3405 }
3406
3407 /* parse old vndr_ie */
3408 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3409
3410 /* make a command to delete old ie */
3411 for (i = 0; i < old_vndr_ies.count; i++) {
3412 vndrie_info = &old_vndr_ies.ie_info[i];
3413
3414 WL_TRACE("DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3415 vndrie_info->vndrie.id,
3416 vndrie_info->vndrie.len,
3417 vndrie_info->vndrie.oui[0],
3418 vndrie_info->vndrie.oui[1],
3419 vndrie_info->vndrie.oui[2]);
3420
3421 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3422 vndrie_info->ie_ptr,
3423 vndrie_info->ie_len,
3424 "del");
3425 curr_ie_buf += del_add_ie_buf_len;
3426 total_ie_buf_len += del_add_ie_buf_len;
3427 }
3428 }
3429
3430 *mgmt_ie_len = 0;
3431 /* Add if there is any extra IE */
3432 if (mgmt_ie_buf && parsed_ie_buf_len) {
3433 ptr = mgmt_ie_buf;
3434
3435 remained_buf_len = mgmt_ie_buf_len;
3436
3437 /* make a command to add new ie */
3438 for (i = 0; i < new_vndr_ies.count; i++) {
3439 vndrie_info = &new_vndr_ies.ie_info[i];
3440
3441 WL_TRACE("ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3442 vndrie_info->vndrie.id,
3443 vndrie_info->vndrie.len,
3444 vndrie_info->vndrie.oui[0],
3445 vndrie_info->vndrie.oui[1],
3446 vndrie_info->vndrie.oui[2]);
3447
3448 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3449 vndrie_info->ie_ptr,
3450 vndrie_info->ie_len,
3451 "add");
3452 /* verify remained buf size before copy data */
3453 remained_buf_len -= vndrie_info->ie_len;
3454 if (remained_buf_len < 0) {
3455 WL_ERR("no space in mgmt_ie_buf: len left %d",
3456 remained_buf_len);
3457 break;
3458 }
3459
3460 /* save the parsed IE in wl struct */
3461 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3462 vndrie_info->ie_len);
3463 *mgmt_ie_len += vndrie_info->ie_len;
3464
3465 curr_ie_buf += del_add_ie_buf_len;
3466 total_ie_buf_len += del_add_ie_buf_len;
3467 }
3468 }
3469 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003470 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003471 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003472 if (err)
3473 WL_ERR("vndr ie set error : %d\n", err);
3474 }
3475
3476exit:
3477 kfree(iovar_ie_buf);
3478 return err;
3479}
3480
3481static s32
3482brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3483 struct cfg80211_ap_settings *settings)
3484{
3485 s32 ie_offset;
Arend van Sprielac24be62012-10-22 10:36:23 -07003486 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003487 struct brcmf_tlv *ssid_ie;
3488 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003489 s32 err = -EPERM;
3490 struct brcmf_tlv *rsn_ie;
3491 struct brcmf_vs_tlv *wpa_ie;
3492 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02003493 s32 bssidx = 0;
3494
3495 WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n",
Johannes Berg683b6d32012-11-08 21:25:48 +01003496 cfg80211_get_chandef_type(&settings->chandef),
3497 settings->beacon_interval,
Hante Meuleman1a873342012-09-27 14:17:54 +02003498 settings->dtim_period);
Arend van Spriel3f40b832012-11-05 16:22:27 -08003499 WL_TRACE("ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
Hante Meuleman1a873342012-09-27 14:17:54 +02003500 settings->ssid, settings->ssid_len, settings->auth_type,
3501 settings->inactivity_timeout);
3502
Arend van Sprielc1179032012-10-22 13:55:33 -07003503 if (!test_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003504 WL_ERR("Not in AP creation mode\n");
3505 return -EPERM;
3506 }
3507
3508 memset(&ssid_le, 0, sizeof(ssid_le));
3509 if (settings->ssid == NULL || settings->ssid_len == 0) {
3510 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
3511 ssid_ie = brcmf_parse_tlvs(
3512 (u8 *)&settings->beacon.head[ie_offset],
3513 settings->beacon.head_len - ie_offset,
3514 WLAN_EID_SSID);
3515 if (!ssid_ie)
3516 return -EINVAL;
3517
3518 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
3519 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
3520 WL_TRACE("SSID is (%s) in Head\n", ssid_le.SSID);
3521 } else {
3522 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
3523 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
3524 }
3525
3526 brcmf_set_mpc(ndev, 0);
Arend van Sprielac24be62012-10-22 10:36:23 -07003527 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003528 if (err < 0) {
3529 WL_ERR("BRCMF_C_DOWN error %d\n", err);
3530 goto exit;
3531 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003532 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003533 if (err < 0) {
3534 WL_ERR("SET INFRA error %d\n", err);
3535 goto exit;
3536 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003537 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003538 if (err < 0) {
3539 WL_ERR("setting AP mode failed %d\n", err);
3540 goto exit;
3541 }
3542
3543 /* find the RSN_IE */
3544 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
3545 settings->beacon.tail_len, WLAN_EID_RSN);
3546
3547 /* find the WPA_IE */
3548 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
3549 settings->beacon.tail_len);
3550
Hante Meuleman1a873342012-09-27 14:17:54 +02003551 if ((wpa_ie != NULL || rsn_ie != NULL)) {
3552 WL_TRACE("WPA(2) IE is found\n");
3553 if (wpa_ie != NULL) {
3554 /* WPA IE */
Arend van Spriel34778522012-11-05 16:22:19 -08003555 err = brcmf_configure_wpaie(ndev, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02003556 if (err < 0)
3557 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003558 } else {
3559 /* RSN IE */
3560 err = brcmf_configure_wpaie(ndev,
Arend van Spriel34778522012-11-05 16:22:19 -08003561 (struct brcmf_vs_tlv *)rsn_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02003562 if (err < 0)
3563 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003564 }
Hante Meuleman1a873342012-09-27 14:17:54 +02003565 } else {
3566 WL_TRACE("No WPA(2) IEs found\n");
3567 brcmf_configure_opensecurity(ndev, bssidx);
Hante Meuleman1a873342012-09-27 14:17:54 +02003568 }
3569 /* Set Beacon IEs to FW */
Arend van Spriel1332e262012-11-05 16:22:18 -08003570 err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
3571 VNDR_IE_BEACON_FLAG,
3572 settings->beacon.tail,
3573 settings->beacon.tail_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003574 if (err)
3575 WL_ERR("Set Beacon IE Failed\n");
3576 else
3577 WL_TRACE("Applied Vndr IEs for Beacon\n");
3578
3579 /* Set Probe Response IEs to FW */
Arend van Spriel1332e262012-11-05 16:22:18 -08003580 err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev),
3581 VNDR_IE_PRBRSP_FLAG,
3582 settings->beacon.proberesp_ies,
3583 settings->beacon.proberesp_ies_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003584 if (err)
3585 WL_ERR("Set Probe Resp IE Failed\n");
3586 else
3587 WL_TRACE("Applied Vndr IEs for Probe Resp\n");
3588
3589 if (settings->beacon_interval) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003590 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003591 settings->beacon_interval);
Hante Meuleman1a873342012-09-27 14:17:54 +02003592 if (err < 0) {
3593 WL_ERR("Beacon Interval Set Error, %d\n", err);
3594 goto exit;
3595 }
3596 }
3597 if (settings->dtim_period) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003598 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003599 settings->dtim_period);
Hante Meuleman1a873342012-09-27 14:17:54 +02003600 if (err < 0) {
3601 WL_ERR("DTIM Interval Set Error, %d\n", err);
3602 goto exit;
3603 }
3604 }
Arend van Sprielac24be62012-10-22 10:36:23 -07003605 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02003606 if (err < 0) {
3607 WL_ERR("BRCMF_C_UP error (%d)\n", err);
3608 goto exit;
3609 }
3610
3611 memset(&join_params, 0, sizeof(join_params));
3612 /* join parameters starts with ssid */
3613 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
3614 /* create softap */
Arend van Sprielac24be62012-10-22 10:36:23 -07003615 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
3616 &join_params, sizeof(join_params));
Hante Meuleman1a873342012-09-27 14:17:54 +02003617 if (err < 0) {
3618 WL_ERR("SET SSID error (%d)\n", err);
3619 goto exit;
3620 }
Arend van Sprielc1179032012-10-22 13:55:33 -07003621 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3622 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003623
3624exit:
3625 if (err)
3626 brcmf_set_mpc(ndev, 1);
3627 return err;
3628}
3629
3630static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
3631{
Arend van Sprielc1179032012-10-22 13:55:33 -07003632 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003633 s32 err = -EPERM;
3634
3635 WL_TRACE("Enter\n");
3636
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003637 if (ifp->vif->mode == WL_MODE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003638 /* Due to most likely deauths outstanding we sleep */
3639 /* first to make sure they get processed by fw. */
3640 msleep(400);
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003641 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003642 if (err < 0) {
3643 WL_ERR("setting AP mode failed %d\n", err);
3644 goto exit;
3645 }
Arend van Spriel128ce3b2012-11-28 21:44:12 +01003646 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003647 if (err < 0) {
3648 WL_ERR("BRCMF_C_UP error %d\n", err);
3649 goto exit;
3650 }
3651 brcmf_set_mpc(ndev, 1);
Arend van Sprielc1179032012-10-22 13:55:33 -07003652 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
3653 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02003654 }
3655exit:
3656 return err;
3657}
3658
3659static int
3660brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
3661 u8 *mac)
3662{
3663 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003664 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02003665 s32 err;
3666
3667 if (!mac)
3668 return -EFAULT;
3669
3670 WL_TRACE("Enter %pM\n", mac);
3671
Arend van Sprielce81e312012-10-22 13:55:37 -07003672 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02003673 return -EIO;
3674
3675 memcpy(&scbval.ea, mac, ETH_ALEN);
3676 scbval.val = cpu_to_le32(WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003677 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003678 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02003679 if (err)
3680 WL_ERR("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
3681
3682 WL_TRACE("Exit\n");
3683 return err;
3684}
3685
Arend van Spriel5b435de2011-10-05 13:19:03 +02003686static struct cfg80211_ops wl_cfg80211_ops = {
3687 .change_virtual_intf = brcmf_cfg80211_change_iface,
3688 .scan = brcmf_cfg80211_scan,
3689 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
3690 .join_ibss = brcmf_cfg80211_join_ibss,
3691 .leave_ibss = brcmf_cfg80211_leave_ibss,
3692 .get_station = brcmf_cfg80211_get_station,
3693 .set_tx_power = brcmf_cfg80211_set_tx_power,
3694 .get_tx_power = brcmf_cfg80211_get_tx_power,
3695 .add_key = brcmf_cfg80211_add_key,
3696 .del_key = brcmf_cfg80211_del_key,
3697 .get_key = brcmf_cfg80211_get_key,
3698 .set_default_key = brcmf_cfg80211_config_default_key,
3699 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
3700 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
3701 .set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
3702 .connect = brcmf_cfg80211_connect,
3703 .disconnect = brcmf_cfg80211_disconnect,
3704 .suspend = brcmf_cfg80211_suspend,
3705 .resume = brcmf_cfg80211_resume,
3706 .set_pmksa = brcmf_cfg80211_set_pmksa,
3707 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003708 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02003709 .start_ap = brcmf_cfg80211_start_ap,
3710 .stop_ap = brcmf_cfg80211_stop_ap,
3711 .del_station = brcmf_cfg80211_del_station,
Arend van Spriele5806072012-09-19 22:21:08 +02003712 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
3713 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02003714#ifdef CONFIG_NL80211_TESTMODE
3715 .testmode_cmd = brcmf_cfg80211_testmode
3716#endif
Arend van Spriel5b435de2011-10-05 13:19:03 +02003717};
3718
3719static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
3720{
3721 s32 err = 0;
3722
3723 switch (mode) {
3724 case WL_MODE_BSS:
3725 return NL80211_IFTYPE_STATION;
3726 case WL_MODE_IBSS:
3727 return NL80211_IFTYPE_ADHOC;
3728 default:
3729 return NL80211_IFTYPE_UNSPECIFIED;
3730 }
3731
3732 return err;
3733}
3734
Arend van Spriele5806072012-09-19 22:21:08 +02003735static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
3736{
Arend van Spriele5806072012-09-19 22:21:08 +02003737 /* scheduled scan settings */
3738 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
3739 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
3740 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
3741 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
Arend van Spriele5806072012-09-19 22:21:08 +02003742}
3743
Arend van Spriel3eacf862012-10-22 13:55:30 -07003744static struct wiphy *brcmf_setup_wiphy(struct device *phydev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003745{
Arend van Spriel3eacf862012-10-22 13:55:30 -07003746 struct wiphy *wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003747 s32 err = 0;
3748
Arend van Spriel3eacf862012-10-22 13:55:30 -07003749 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
3750 if (!wiphy) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08003751 WL_ERR("Could not allocate wiphy device\n");
Arend van Spriel3eacf862012-10-22 13:55:30 -07003752 return ERR_PTR(-ENOMEM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003753 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003754 set_wiphy_dev(wiphy, phydev);
3755 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
3756 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
3757 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
3758 BIT(NL80211_IFTYPE_ADHOC) |
3759 BIT(NL80211_IFTYPE_AP);
3760 wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
3761 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a; /* Set
Arend van Spriel5b435de2011-10-05 13:19:03 +02003762 * it as 11a by default.
3763 * This will be updated with
3764 * 11n phy tables in
3765 * "ifconfig up"
3766 * if phy has 11n capability
3767 */
Arend van Spriel3eacf862012-10-22 13:55:30 -07003768 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
3769 wiphy->cipher_suites = __wl_cipher_suites;
3770 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
3771 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; /* enable power
Arend van Spriel5b435de2011-10-05 13:19:03 +02003772 * save mode
3773 * by default
3774 */
Arend van Spriel3eacf862012-10-22 13:55:30 -07003775 brcmf_wiphy_pno_params(wiphy);
3776 err = wiphy_register(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003777 if (err < 0) {
Joe Perchesbfeb4db2012-01-15 00:38:45 -08003778 WL_ERR("Could not register wiphy device (%d)\n", err);
Arend van Spriel3eacf862012-10-22 13:55:30 -07003779 wiphy_free(wiphy);
3780 return ERR_PTR(err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003781 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003782 return wiphy;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003783}
3784
Arend van Spriel3eacf862012-10-22 13:55:30 -07003785static
3786struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
3787 struct net_device *netdev,
3788 s32 mode, bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003789{
Arend van Spriel3eacf862012-10-22 13:55:30 -07003790 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003791
Arend van Spriel3eacf862012-10-22 13:55:30 -07003792 if (cfg->vif_cnt == BRCMF_IFACE_MAX_CNT)
3793 return ERR_PTR(-ENOSPC);
3794
3795 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
3796 if (!vif)
3797 return ERR_PTR(-ENOMEM);
3798
3799 vif->wdev.wiphy = cfg->wiphy;
3800 vif->wdev.netdev = netdev;
3801 vif->wdev.iftype = brcmf_mode_to_nl80211_iftype(mode);
3802
3803 if (netdev) {
3804 vif->ifp = netdev_priv(netdev);
3805 netdev->ieee80211_ptr = &vif->wdev;
3806 SET_NETDEV_DEV(netdev, wiphy_dev(cfg->wiphy));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003807 }
Arend van Spriel3eacf862012-10-22 13:55:30 -07003808
3809 vif->mode = mode;
3810 vif->pm_block = pm_block;
3811 vif->roam_off = -1;
3812
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07003813 brcmf_init_prof(&vif->profile);
3814
Arend van Spriel3eacf862012-10-22 13:55:30 -07003815 list_add_tail(&vif->list, &cfg->vif_list);
3816 cfg->vif_cnt++;
3817 return vif;
3818}
3819
3820static void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
3821{
3822 struct brcmf_cfg80211_info *cfg;
3823 struct wiphy *wiphy;
3824
3825 wiphy = vif->wdev.wiphy;
3826 cfg = wiphy_priv(wiphy);
3827 list_del(&vif->list);
3828 cfg->vif_cnt--;
3829
3830 kfree(vif);
3831 if (!cfg->vif_cnt) {
3832 wiphy_unregister(wiphy);
3833 wiphy_free(wiphy);
3834 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003835}
3836
Arend van Spriel903e0ee2012-11-28 21:44:11 +01003837static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003838{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003839 u32 event = e->event_code;
3840 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003841
3842 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
3843 WL_CONN("Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003844 return true;
3845 }
3846
3847 return false;
3848}
3849
Arend van Spriel903e0ee2012-11-28 21:44:11 +01003850static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003851{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003852 u32 event = e->event_code;
3853 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003854
3855 if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
3856 WL_CONN("Processing link down\n");
3857 return true;
3858 }
3859 return false;
3860}
3861
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003862static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003863 const struct brcmf_event_msg *e)
3864{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003865 u32 event = e->event_code;
3866 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003867
3868 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
3869 WL_CONN("Processing Link %s & no network found\n",
Arend van Spriel5c36b992012-11-14 18:46:05 -08003870 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003871 return true;
3872 }
3873
3874 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
3875 WL_CONN("Processing connecting & no network found\n");
3876 return true;
3877 }
3878
3879 return false;
3880}
3881
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003882static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003883{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003884 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003885
3886 kfree(conn_info->req_ie);
3887 conn_info->req_ie = NULL;
3888 conn_info->req_ie_len = 0;
3889 kfree(conn_info->resp_ie);
3890 conn_info->resp_ie = NULL;
3891 conn_info->resp_ie_len = 0;
3892}
3893
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003894static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003895{
Arend van Sprielac24be62012-10-22 10:36:23 -07003896 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003897 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003898 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003899 u32 req_len;
3900 u32 resp_len;
3901 s32 err = 0;
3902
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003903 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003904
Arend van Sprielac24be62012-10-22 10:36:23 -07003905 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
3906 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003907 if (err) {
3908 WL_ERR("could not get assoc info (%d)\n", err);
3909 return err;
3910 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003911 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003912 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02003913 req_len = le32_to_cpu(assoc_info->req_len);
3914 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003915 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003916 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003917 cfg->extra_buf,
3918 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003919 if (err) {
3920 WL_ERR("could not get assoc req (%d)\n", err);
3921 return err;
3922 }
3923 conn_info->req_ie_len = req_len;
3924 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003925 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003926 GFP_KERNEL);
3927 } else {
3928 conn_info->req_ie_len = 0;
3929 conn_info->req_ie = NULL;
3930 }
3931 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07003932 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003933 cfg->extra_buf,
3934 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003935 if (err) {
3936 WL_ERR("could not get assoc resp (%d)\n", err);
3937 return err;
3938 }
3939 conn_info->resp_ie_len = resp_len;
3940 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003941 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003942 GFP_KERNEL);
3943 } else {
3944 conn_info->resp_ie_len = 0;
3945 conn_info->resp_ie = NULL;
3946 }
3947 WL_CONN("req len (%d) resp len (%d)\n",
3948 conn_info->req_ie_len, conn_info->resp_ie_len);
3949
3950 return err;
3951}
3952
3953static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003954brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003955 struct net_device *ndev,
3956 const struct brcmf_event_msg *e)
3957{
Arend van Sprielc1179032012-10-22 13:55:33 -07003958 struct brcmf_if *ifp = netdev_priv(ndev);
3959 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003960 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
3961 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07003962 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003963 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07003964 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003965 u32 freq;
3966 s32 err = 0;
3967 u32 target_channel;
Franky Lina180b832012-10-10 11:13:09 -07003968 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003969
3970 WL_TRACE("Enter\n");
3971
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003972 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02003973 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003974 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003975
Franky Lina180b832012-10-10 11:13:09 -07003976 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
3977 if (buf == NULL) {
3978 err = -ENOMEM;
3979 goto done;
3980 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003981
Franky Lina180b832012-10-10 11:13:09 -07003982 /* data sent to dongle has to be little endian */
3983 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07003984 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07003985 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07003986
3987 if (err)
3988 goto done;
3989
3990 bi = (struct brcmf_bss_info_le *)(buf + 4);
3991 target_channel = bi->ctl_ch ? bi->ctl_ch :
3992 CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003993
3994 if (target_channel <= CH_MAX_2G_CHANNEL)
3995 band = wiphy->bands[IEEE80211_BAND_2GHZ];
3996 else
3997 band = wiphy->bands[IEEE80211_BAND_5GHZ];
3998
3999 freq = ieee80211_channel_to_frequency(target_channel, band->band);
4000 notify_channel = ieee80211_get_channel(wiphy, freq);
4001
Franky Lina180b832012-10-10 11:13:09 -07004002done:
4003 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004004 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004005 conn_info->req_ie, conn_info->req_ie_len,
4006 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
4007 WL_CONN("Report roaming result\n");
4008
Arend van Sprielc1179032012-10-22 13:55:33 -07004009 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004010 WL_TRACE("Exit\n");
4011 return err;
4012}
4013
4014static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004015brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004016 struct net_device *ndev, const struct brcmf_event_msg *e,
4017 bool completed)
4018{
Arend van Sprielc1179032012-10-22 13:55:33 -07004019 struct brcmf_if *ifp = netdev_priv(ndev);
4020 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004021 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004022 s32 err = 0;
4023
4024 WL_TRACE("Enter\n");
4025
Arend van Sprielc1179032012-10-22 13:55:33 -07004026 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4027 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004028 if (completed) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004029 brcmf_get_assoc_ies(cfg);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004030 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004031 brcmf_update_bss_info(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004032 }
4033 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004034 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004035 conn_info->req_ie,
4036 conn_info->req_ie_len,
4037 conn_info->resp_ie,
4038 conn_info->resp_ie_len,
4039 completed ? WLAN_STATUS_SUCCESS :
4040 WLAN_STATUS_AUTH_TIMEOUT,
4041 GFP_KERNEL);
4042 if (completed)
Arend van Sprielc1179032012-10-22 13:55:33 -07004043 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4044 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004045 WL_CONN("Report connect result - connection %s\n",
4046 completed ? "succeeded" : "failed");
4047 }
4048 WL_TRACE("Exit\n");
4049 return err;
4050}
4051
4052static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004053brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004054 struct net_device *ndev,
4055 const struct brcmf_event_msg *e, void *data)
4056{
4057 s32 err = 0;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004058 u32 event = e->event_code;
4059 u32 reason = e->reason;
4060 u32 len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004061 static int generation;
4062
4063 struct station_info sinfo;
4064
4065 WL_CONN("event %d, reason %d\n", event, reason);
4066 memset(&sinfo, 0, sizeof(sinfo));
4067
4068 sinfo.filled = 0;
4069 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
4070 reason == BRCMF_E_STATUS_SUCCESS) {
4071 sinfo.filled = STATION_INFO_ASSOC_REQ_IES;
4072 if (!data) {
4073 WL_ERR("No IEs present in ASSOC/REASSOC_IND");
4074 return -EINVAL;
4075 }
4076 sinfo.assoc_req_ies = data;
4077 sinfo.assoc_req_ies_len = len;
4078 generation++;
4079 sinfo.generation = generation;
4080 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_ATOMIC);
4081 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4082 (event == BRCMF_E_DEAUTH_IND) ||
4083 (event == BRCMF_E_DEAUTH)) {
4084 generation++;
4085 sinfo.generation = generation;
4086 cfg80211_del_sta(ndev, e->addr, GFP_ATOMIC);
4087 }
4088 return err;
4089}
4090
4091static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004092brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004093 const struct brcmf_event_msg *e, void *data)
4094{
Arend van Spriel19937322012-11-05 16:22:32 -08004095 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4096 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004097 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004098 s32 err = 0;
4099
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004100 if (ifp->vif->mode == WL_MODE_AP) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004101 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004102 } else if (brcmf_is_linkup(e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004103 WL_CONN("Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004104 if (brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004105 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004106 wl_inform_ibss(cfg, ndev, e->addr);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004107 cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004108 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4109 &ifp->vif->sme_state);
4110 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4111 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004112 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004113 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004114 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004115 WL_CONN("Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004116 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004117 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Sprielc1179032012-10-22 13:55:33 -07004118 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED,
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004119 &ifp->vif->sme_state))
Arend van Spriel5b435de2011-10-05 13:19:03 +02004120 cfg80211_disconnected(ndev, 0, NULL, 0,
Arend van Sprielc1179032012-10-22 13:55:33 -07004121 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004122 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004123 brcmf_link_down(ifp->vif);
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004124 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004125 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004126 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07004127 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4128 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004129 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004130 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004131 }
4132
4133 return err;
4134}
4135
4136static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004137brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004138 const struct brcmf_event_msg *e, void *data)
4139{
Arend van Spriel19937322012-11-05 16:22:32 -08004140 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004141 s32 err = 0;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004142 u32 event = e->event_code;
4143 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004144
4145 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004146 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08004147 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004148 else
Arend van Spriel19937322012-11-05 16:22:32 -08004149 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004150 }
4151
4152 return err;
4153}
4154
4155static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004156brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004157 const struct brcmf_event_msg *e, void *data)
4158{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004159 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004160 enum nl80211_key_type key_type;
4161
4162 if (flags & BRCMF_EVENT_MSG_GROUP)
4163 key_type = NL80211_KEYTYPE_GROUP;
4164 else
4165 key_type = NL80211_KEYTYPE_PAIRWISE;
4166
Arend van Spriel19937322012-11-05 16:22:32 -08004167 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004168 NULL, GFP_KERNEL);
4169
4170 return 0;
4171}
4172
Arend van Spriel5b435de2011-10-05 13:19:03 +02004173static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
4174{
Arend van Spriel5b435de2011-10-05 13:19:03 +02004175 conf->frag_threshold = (u32)-1;
4176 conf->rts_threshold = (u32)-1;
4177 conf->retry_short = (u32)-1;
4178 conf->retry_long = (u32)-1;
4179 conf->tx_power = -1;
4180}
4181
Arend van Spriel5c36b992012-11-14 18:46:05 -08004182static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004183{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004184 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
4185 brcmf_notify_connect_status);
4186 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
4187 brcmf_notify_connect_status);
4188 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
4189 brcmf_notify_connect_status);
4190 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
4191 brcmf_notify_connect_status);
4192 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
4193 brcmf_notify_connect_status);
4194 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
4195 brcmf_notify_connect_status);
4196 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
4197 brcmf_notify_roaming_status);
4198 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
4199 brcmf_notify_mic_status);
4200 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
4201 brcmf_notify_connect_status);
4202 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
4203 brcmf_notify_sched_scan_results);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004204}
4205
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004206static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004207{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004208 kfree(cfg->conf);
4209 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004210 kfree(cfg->escan_ioctl_buf);
4211 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004212 kfree(cfg->extra_buf);
4213 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004214 kfree(cfg->pmk_list);
4215 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004216}
4217
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004218static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004219{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004220 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
4221 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004222 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004223 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
4224 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02004225 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004226 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4227 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004228 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004229 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
4230 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004231 goto init_priv_mem_out;
4232
4233 return 0;
4234
4235init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004236 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004237
4238 return -ENOMEM;
4239}
4240
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004241static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004242{
4243 s32 err = 0;
4244
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004245 cfg->scan_request = NULL;
4246 cfg->pwr_save = true;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004247 cfg->roam_on = true; /* roam on & off switch.
Arend van Spriel5b435de2011-10-05 13:19:03 +02004248 we enable roam per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004249 cfg->active_scan = true; /* we do active scan for
Arend van Spriel5b435de2011-10-05 13:19:03 +02004250 specific scan per default */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004251 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004252 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004253 if (err)
4254 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004255 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004256 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004257 brcmf_init_escan(cfg);
4258 brcmf_init_conf(cfg->conf);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004259
4260 return err;
4261}
4262
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004263static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004264{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004265 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004266 brcmf_abort_scanning(cfg);
4267 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004268}
4269
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004270struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004271{
Arend van Spriel1ed9baf2012-10-22 10:36:20 -07004272 struct net_device *ndev = drvr->iflist[0]->ndev;
4273 struct device *busdev = drvr->dev;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004274 struct brcmf_cfg80211_info *cfg;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004275 struct wiphy *wiphy;
4276 struct brcmf_cfg80211_vif *vif;
4277 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004278 s32 err = 0;
4279
4280 if (!ndev) {
4281 WL_ERR("ndev is invalid\n");
4282 return NULL;
4283 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004284
Arend van Spriel3eacf862012-10-22 13:55:30 -07004285 ifp = netdev_priv(ndev);
4286 wiphy = brcmf_setup_wiphy(busdev);
4287 if (IS_ERR(wiphy))
4288 return NULL;
4289
4290 cfg = wiphy_priv(wiphy);
4291 cfg->wiphy = wiphy;
4292 cfg->pub = drvr;
4293 INIT_LIST_HEAD(&cfg->vif_list);
4294
4295 vif = brcmf_alloc_vif(cfg, ndev, WL_MODE_BSS, false);
4296 if (IS_ERR(vif)) {
4297 wiphy_free(wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004298 return NULL;
4299 }
4300
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004301 err = wl_init_priv(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004302 if (err) {
4303 WL_ERR("Failed to init iwm_priv (%d)\n", err);
4304 goto cfg80211_attach_out;
4305 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004306
Arend van Spriel3eacf862012-10-22 13:55:30 -07004307 ifp->vif = vif;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004308 return cfg;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004309
4310cfg80211_attach_out:
Arend van Spriel3eacf862012-10-22 13:55:30 -07004311 brcmf_free_vif(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004312 return NULL;
4313}
4314
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004315void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004316{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004317 struct brcmf_cfg80211_vif *vif;
4318 struct brcmf_cfg80211_vif *tmp;
4319
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004320 wl_deinit_priv(cfg);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004321 list_for_each_entry_safe(vif, tmp, &cfg->vif_list, list) {
4322 brcmf_free_vif(vif);
4323 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004324}
4325
Arend van Spriel5b435de2011-10-05 13:19:03 +02004326static s32
4327brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
4328{
Arend van Sprielac24be62012-10-22 10:36:23 -07004329 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004330 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02004331 __le32 roamtrigger[2];
4332 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02004333
4334 /*
4335 * Setup timeout if Beacons are lost and roam is
4336 * off to report link down
4337 */
4338 if (roamvar) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004339 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004340 if (err) {
4341 WL_ERR("bcn_timeout error (%d)\n", err);
4342 goto dongle_rom_out;
4343 }
4344 }
4345
4346 /*
4347 * Enable/Disable built-in roaming to allow supplicant
4348 * to take care of roaming
4349 */
4350 WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
Arend van Sprielac24be62012-10-22 10:36:23 -07004351 err = brcmf_fil_iovar_int_set(ifp, "roam_off", roamvar);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004352 if (err) {
4353 WL_ERR("roam_off error (%d)\n", err);
4354 goto dongle_rom_out;
4355 }
4356
Arend van Sprielf588bc02011-10-12 20:51:22 +02004357 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
4358 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004359 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004360 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004361 if (err) {
4362 WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
4363 goto dongle_rom_out;
4364 }
4365
Arend van Sprielf588bc02011-10-12 20:51:22 +02004366 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
4367 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07004368 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004369 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004370 if (err) {
4371 WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
4372 goto dongle_rom_out;
4373 }
4374
4375dongle_rom_out:
4376 return err;
4377}
4378
4379static s32
4380brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02004381 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004382{
Arend van Sprielac24be62012-10-22 10:36:23 -07004383 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004384 s32 err = 0;
4385
Arend van Sprielac24be62012-10-22 10:36:23 -07004386 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004387 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004388 if (err) {
4389 if (err == -EOPNOTSUPP)
4390 WL_INFO("Scan assoc time is not supported\n");
4391 else
4392 WL_ERR("Scan assoc time error (%d)\n", err);
4393 goto dongle_scantime_out;
4394 }
Arend van Sprielac24be62012-10-22 10:36:23 -07004395 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004396 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004397 if (err) {
4398 if (err == -EOPNOTSUPP)
4399 WL_INFO("Scan unassoc time is not supported\n");
4400 else
4401 WL_ERR("Scan unassoc time error (%d)\n", err);
4402 goto dongle_scantime_out;
4403 }
4404
Arend van Sprielac24be62012-10-22 10:36:23 -07004405 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004406 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004407 if (err) {
4408 if (err == -EOPNOTSUPP)
4409 WL_INFO("Scan passive time is not supported\n");
4410 else
4411 WL_ERR("Scan passive time error (%d)\n", err);
4412 goto dongle_scantime_out;
4413 }
4414
4415dongle_scantime_out:
4416 return err;
4417}
4418
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004419static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004420{
Arend van Sprielac24be62012-10-22 10:36:23 -07004421 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004422 struct wiphy *wiphy;
4423 s32 phy_list;
4424 s8 phy;
4425 s32 err = 0;
4426
Hante Meulemanb87e2c42012-11-14 18:46:23 -08004427 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004428 &phy_list, sizeof(phy_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02004429 if (err) {
4430 WL_ERR("error (%d)\n", err);
4431 return err;
4432 }
4433
Hante Meuleman3ba81372012-09-19 22:21:13 +02004434 phy = ((char *)&phy_list)[0];
Arend van Spriel5b435de2011-10-05 13:19:03 +02004435 WL_INFO("%c phy\n", phy);
4436 if (phy == 'n' || phy == 'a') {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004437 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004438 wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
4439 }
4440
4441 return err;
4442}
4443
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004444static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004445{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004446 return wl_update_wiphybands(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004447}
4448
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004449static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004450{
4451 struct net_device *ndev;
4452 struct wireless_dev *wdev;
4453 s32 power_mode;
4454 s32 err = 0;
4455
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004456 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004457 return err;
4458
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004459 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004460 wdev = ndev->ieee80211_ptr;
4461
4462 brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
4463 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
4464
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004465 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Arend van Sprielac24be62012-10-22 10:36:23 -07004466 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PM,
4467 power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004468 if (err)
4469 goto default_conf_out;
4470 WL_INFO("power save set to %s\n",
4471 (power_mode ? "enabled" : "disabled"));
4472
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004473 err = brcmf_dongle_roam(ndev, (cfg->roam_on ? 0 : 1),
Arend van Spriel5b435de2011-10-05 13:19:03 +02004474 WL_BEACON_TIMEOUT);
4475 if (err)
4476 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07004477 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
4478 NULL, NULL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004479 if (err && err != -EINPROGRESS)
4480 goto default_conf_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004481 err = brcmf_dongle_probecap(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004482 if (err)
4483 goto default_conf_out;
4484
4485 /* -EINPROGRESS: Call commit handler */
4486
4487default_conf_out:
4488
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004489 cfg->dongle_up = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004490
4491 return err;
4492
4493}
4494
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004495static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004496{
Arend van Sprielc1179032012-10-22 13:55:33 -07004497 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004498 if (ifp->idx)
4499 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004500
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004501 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004502}
4503
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004504static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004505{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004506 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07004507
Arend van Spriel5b435de2011-10-05 13:19:03 +02004508 /*
4509 * While going down, if associated with AP disassociate
4510 * from AP to save power
4511 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004512 if (check_vif_up(ifp->vif)) {
4513 brcmf_link_down(ifp->vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004514
4515 /* Make sure WPA_Supplicant receives all the event
4516 generated due to DISASSOC call to the fw to keep
4517 the state fw and WPA_Supplicant state consistent
4518 */
4519 brcmf_delay(500);
4520 }
4521
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004522 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07004523 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004524
Arend van Spriel5b435de2011-10-05 13:19:03 +02004525 return 0;
4526}
4527
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004528s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004529{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004530 struct brcmf_if *ifp = netdev_priv(ndev);
4531 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004532 s32 err = 0;
4533
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004534 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004535 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004536 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004537
4538 return err;
4539}
4540
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004541s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004542{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004543 struct brcmf_if *ifp = netdev_priv(ndev);
4544 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004545 s32 err = 0;
4546
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004547 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08004548 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004549 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004550
4551 return err;
4552}
4553