blob: a668a351b0c5a7d66ab9d0c5c483a04f2cdcd8dc [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
19#include <linux/kernel.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <linux/etherdevice.h>
Hante Meuleman68ca3952014-02-25 20:30:26 +010021#include <linux/module.h>
Franky Lin1bacb042014-06-21 12:11:16 +020022#include <linux/vmalloc.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>
Hante Meuleman122d3d02014-10-28 14:56:18 +010029#include "core.h"
Hante Meulemana8e8ed32014-10-28 14:56:13 +010030#include "debug.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020031#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010032#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010033#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020034#include "btcoex.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010035#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020036#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070037#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020038#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020039#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010040#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010041#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020042
Arend van Spriele5806072012-09-19 22:21:08 +020043#define BRCMF_SCAN_IE_LEN_MAX 2048
44#define BRCMF_PNO_VERSION 2
45#define BRCMF_PNO_TIME 30
46#define BRCMF_PNO_REPEAT 4
47#define BRCMF_PNO_FREQ_EXPO_MAX 3
48#define BRCMF_PNO_MAX_PFN_COUNT 16
49#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
50#define BRCMF_PNO_HIDDEN_BIT 2
51#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
52#define BRCMF_PNO_SCAN_COMPLETE 1
53#define BRCMF_PNO_SCAN_INCOMPLETE 0
54
Hante Meuleman1a873342012-09-27 14:17:54 +020055#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010059#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020060
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
75#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
76#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
77
78#define VNDR_IE_CMD_LEN 4 /* length of the set command
79 * string :"add", "del" (+ NUL)
80 */
81#define VNDR_IE_COUNT_OFFSET 4
82#define VNDR_IE_PKTFLAG_OFFSET 8
83#define VNDR_IE_VSIE_OFFSET 12
84#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010085#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020086
87#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
88#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020089
Hante Meuleman89286dc2013-02-08 15:53:46 +010090#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
91#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
92#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
93
Arend van Spriel5b435de2011-10-05 13:19:03 +020094#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
95 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
96
Arend van Sprielce81e312012-10-22 13:55:37 -070097static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +020098{
Arend van Sprielc1179032012-10-22 13:55:33 -070099 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100100 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
101 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200102 return false;
103 }
104 return true;
105}
106
Arend van Spriel5b435de2011-10-05 13:19:03 +0200107#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
108#define RATETAB_ENT(_rateid, _flags) \
109 { \
110 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
111 .hw_value = (_rateid), \
112 .flags = (_flags), \
113 }
114
115static struct ieee80211_rate __wl_rates[] = {
116 RATETAB_ENT(BRCM_RATE_1M, 0),
117 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
118 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
119 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
120 RATETAB_ENT(BRCM_RATE_6M, 0),
121 RATETAB_ENT(BRCM_RATE_9M, 0),
122 RATETAB_ENT(BRCM_RATE_12M, 0),
123 RATETAB_ENT(BRCM_RATE_18M, 0),
124 RATETAB_ENT(BRCM_RATE_24M, 0),
125 RATETAB_ENT(BRCM_RATE_36M, 0),
126 RATETAB_ENT(BRCM_RATE_48M, 0),
127 RATETAB_ENT(BRCM_RATE_54M, 0),
128};
129
Arend van Spriel5b435de2011-10-05 13:19:03 +0200130#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200131#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
132#define wl_a_rates (__wl_rates + 4)
133#define wl_a_rates_size (wl_g_rates_size - 4)
134
135#define CHAN2G(_channel, _freq) { \
136 .band = IEEE80211_BAND_2GHZ, \
137 .center_freq = (_freq), \
138 .hw_value = (_channel), \
139 .flags = IEEE80211_CHAN_DISABLED, \
140 .max_antenna_gain = 0, \
141 .max_power = 30, \
142}
143
144#define CHAN5G(_channel) { \
145 .band = IEEE80211_BAND_5GHZ, \
146 .center_freq = 5000 + (5 * (_channel)), \
147 .hw_value = (_channel), \
148 .flags = IEEE80211_CHAN_DISABLED, \
149 .max_antenna_gain = 0, \
150 .max_power = 30, \
151}
152
153static struct ieee80211_channel __wl_2ghz_channels[] = {
154 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
155 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
156 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
157 CHAN2G(13, 2472), CHAN2G(14, 2484)
158};
159
160static struct ieee80211_channel __wl_5ghz_channels[] = {
161 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
162 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
163 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
164 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
165 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
166 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
167};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200168
Arend van Sprielb48d8912014-07-12 08:49:41 +0200169/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200170 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200171 */
172static const struct ieee80211_supported_band __wl_band_2ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200173 .band = IEEE80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200174 .bitrates = wl_g_rates,
175 .n_bitrates = wl_g_rates_size,
176};
177
Arend van Spriel58de92d2015-04-14 20:10:24 +0200178static const struct ieee80211_supported_band __wl_band_5ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200179 .band = IEEE80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200180 .bitrates = wl_a_rates,
181 .n_bitrates = wl_a_rates_size,
182};
183
Hante Meulemand48200b2013-04-03 12:40:29 +0200184/* This is to override regulatory domains defined in cfg80211 module (reg.c)
185 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200186 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
187 * With respect to these flags, wpa_supplicant doesn't * start p2p
188 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200189 * domain are to be done here.
190 */
191static const struct ieee80211_regdomain brcmf_regdom = {
192 .n_reg_rules = 4,
193 .alpha2 = "99",
194 .reg_rules = {
195 /* IEEE 802.11b/g, channels 1..11 */
196 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
197 /* If any */
198 /* IEEE 802.11 channel 14 - Only JP enables
199 * this and for 802.11b only
200 */
201 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
202 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200203 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200204 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200205 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200206};
207
208static const u32 __wl_cipher_suites[] = {
209 WLAN_CIPHER_SUITE_WEP40,
210 WLAN_CIPHER_SUITE_WEP104,
211 WLAN_CIPHER_SUITE_TKIP,
212 WLAN_CIPHER_SUITE_CCMP,
213 WLAN_CIPHER_SUITE_AES_CMAC,
214};
215
Hante Meuleman1a873342012-09-27 14:17:54 +0200216/* Vendor specific ie. id = 221, oui and type defines exact ie */
217struct brcmf_vs_tlv {
218 u8 id;
219 u8 len;
220 u8 oui[3];
221 u8 oui_type;
222};
223
224struct parsed_vndr_ie_info {
225 u8 *ie_ptr;
226 u32 ie_len; /* total length including id & length field */
227 struct brcmf_vs_tlv vndrie;
228};
229
230struct parsed_vndr_ies {
231 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100232 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200233};
234
Hante Meuleman68ca3952014-02-25 20:30:26 +0100235static int brcmf_roamoff;
236module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
237MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
238
Alwin Beukersef6ac172011-10-12 20:51:26 +0200239/* Quarter dBm units to mW
240 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
241 * Table is offset so the last entry is largest mW value that fits in
242 * a u16.
243 */
244
245#define QDBM_OFFSET 153 /* Offset for first entry */
246#define QDBM_TABLE_LEN 40 /* Table size */
247
248/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
249 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
250 */
251#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
252
253/* Largest mW value that will round down to the last table entry,
254 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
255 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
256 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
257 */
258#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
259
260static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
261/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
262/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
263/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
264/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
265/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
266/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
267};
268
269static u16 brcmf_qdbm_to_mw(u8 qdbm)
270{
271 uint factor = 1;
272 int idx = qdbm - QDBM_OFFSET;
273
274 if (idx >= QDBM_TABLE_LEN)
275 /* clamp to max u16 mW value */
276 return 0xFFFF;
277
278 /* scale the qdBm index up to the range of the table 0-40
279 * where an offset of 40 qdBm equals a factor of 10 mW.
280 */
281 while (idx < 0) {
282 idx += 40;
283 factor *= 10;
284 }
285
286 /* return the mW value scaled down to the correct factor of 10,
287 * adding in factor/2 to get proper rounding.
288 */
289 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
290}
291
292static u8 brcmf_mw_to_qdbm(u16 mw)
293{
294 u8 qdbm;
295 int offset;
296 uint mw_uint = mw;
297 uint boundary;
298
299 /* handle boundary case */
300 if (mw_uint <= 1)
301 return 0;
302
303 offset = QDBM_OFFSET;
304
305 /* move mw into the range of the table */
306 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
307 mw_uint *= 10;
308 offset -= 40;
309 }
310
311 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
312 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
313 nqdBm_to_mW_map[qdbm]) / 2;
314 if (mw_uint < boundary)
315 break;
316 }
317
318 qdbm += (u8) offset;
319
320 return qdbm;
321}
322
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200323static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
324 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200325{
326 struct brcmu_chan ch_inf;
327 s32 primary_offset;
328
329 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
330 ch->chan->center_freq, ch->center_freq1, ch->width);
331 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
332 primary_offset = ch->center_freq1 - ch->chan->center_freq;
333 switch (ch->width) {
334 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100335 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200336 ch_inf.bw = BRCMU_CHAN_BW_20;
337 WARN_ON(primary_offset != 0);
338 break;
339 case NL80211_CHAN_WIDTH_40:
340 ch_inf.bw = BRCMU_CHAN_BW_40;
341 if (primary_offset < 0)
342 ch_inf.sb = BRCMU_CHAN_SB_U;
343 else
344 ch_inf.sb = BRCMU_CHAN_SB_L;
345 break;
346 case NL80211_CHAN_WIDTH_80:
347 ch_inf.bw = BRCMU_CHAN_BW_80;
348 if (primary_offset < 0) {
349 if (primary_offset < -CH_10MHZ_APART)
350 ch_inf.sb = BRCMU_CHAN_SB_UU;
351 else
352 ch_inf.sb = BRCMU_CHAN_SB_UL;
353 } else {
354 if (primary_offset > CH_10MHZ_APART)
355 ch_inf.sb = BRCMU_CHAN_SB_LL;
356 else
357 ch_inf.sb = BRCMU_CHAN_SB_LU;
358 }
359 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100360 case NL80211_CHAN_WIDTH_80P80:
361 case NL80211_CHAN_WIDTH_160:
362 case NL80211_CHAN_WIDTH_5:
363 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200364 default:
365 WARN_ON_ONCE(1);
366 }
367 switch (ch->chan->band) {
368 case IEEE80211_BAND_2GHZ:
369 ch_inf.band = BRCMU_CHAN_BAND_2G;
370 break;
371 case IEEE80211_BAND_5GHZ:
372 ch_inf.band = BRCMU_CHAN_BAND_5G;
373 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100374 case IEEE80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200375 default:
376 WARN_ON_ONCE(1);
377 }
378 d11inf->encchspec(&ch_inf);
379
380 return ch_inf.chspec;
381}
382
Franky Lin83cf17a2013-04-11 13:28:50 +0200383u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
384 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700385{
Franky Lin83cf17a2013-04-11 13:28:50 +0200386 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700387
Franky Lin83cf17a2013-04-11 13:28:50 +0200388 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
389 ch_inf.bw = BRCMU_CHAN_BW_20;
390 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700391
Franky Lin83cf17a2013-04-11 13:28:50 +0200392 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700393}
394
Hante Meuleman89286dc2013-02-08 15:53:46 +0100395/* Traverse a string of 1-byte tag/1-byte length/variable-length value
396 * triples, returning a pointer to the substring whose first element
397 * matches tag
398 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100399const struct brcmf_tlv *
400brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100401{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100402 const struct brcmf_tlv *elt = buf;
403 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100404
405 /* find tagged parameter */
406 while (totlen >= TLV_HDR_LEN) {
407 int len = elt->len;
408
409 /* validate remaining totlen */
410 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
411 return elt;
412
413 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
414 totlen -= (len + TLV_HDR_LEN);
415 }
416
417 return NULL;
418}
419
420/* Is any of the tlvs the expected entry? If
421 * not update the tlvs buffer pointer/length.
422 */
423static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100424brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
425 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100426{
427 /* If the contents match the OUI and the type */
428 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
429 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
430 type == ie[TLV_BODY_OFF + oui_len]) {
431 return true;
432 }
433
434 if (tlvs == NULL)
435 return false;
436 /* point to the next ie */
437 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
438 /* calculate the length of the rest of the buffer */
439 *tlvs_len -= (int)(ie - *tlvs);
440 /* update the pointer to the start of the buffer */
441 *tlvs = ie;
442
443 return false;
444}
445
446static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100447brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100448{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100449 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100450
451 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100452 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100453 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
454 return (struct brcmf_vs_tlv *)ie;
455 }
456 return NULL;
457}
458
459static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100460brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100461{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100462 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100463
464 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
465 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
466 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
467 return (struct brcmf_vs_tlv *)ie;
468 }
469 return NULL;
470}
471
472
Arend van Spriel5b435de2011-10-05 13:19:03 +0200473static void convert_key_from_CPU(struct brcmf_wsec_key *key,
474 struct brcmf_wsec_key_le *key_le)
475{
476 key_le->index = cpu_to_le32(key->index);
477 key_le->len = cpu_to_le32(key->len);
478 key_le->algo = cpu_to_le32(key->algo);
479 key_le->flags = cpu_to_le32(key->flags);
480 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
481 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
482 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
483 memcpy(key_le->data, key->data, sizeof(key->data));
484 memcpy(key_le->ea, key->ea, sizeof(key->ea));
485}
486
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200487static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100488send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200489{
490 int err;
491 struct brcmf_wsec_key_le key_le;
492
493 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200494
Hante Meuleman118eb302014-12-21 12:43:49 +0100495 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700496
Hante Meuleman118eb302014-12-21 12:43:49 +0100497 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700498 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200499
Arend van Spriel5b435de2011-10-05 13:19:03 +0200500 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100501 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200502 return err;
503}
504
Hante Meulemanb3657452013-05-27 21:09:53 +0200505static s32
506brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
507{
508 s32 err;
509 u32 mode;
510
511 if (enable)
512 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
513 else
514 mode = 0;
515
516 /* Try to set and enable ARP offload feature, this may fail, then it */
517 /* is simply not supported and err 0 will be returned */
518 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
519 if (err) {
520 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
521 mode, err);
522 err = 0;
523 } else {
524 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
525 if (err) {
526 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
527 enable, err);
528 err = 0;
529 } else
530 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
531 enable, mode);
532 }
533
534 return err;
535}
536
Hante Meuleman8851cce2014-07-30 13:20:02 +0200537static void
538brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
539{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200540 struct brcmf_cfg80211_vif *vif;
541 struct brcmf_if *ifp;
542
543 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
544 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200545
546 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
547 (wdev->iftype == NL80211_IFTYPE_AP) ||
548 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
549 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
550 ADDR_DIRECT);
551 else
552 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
553 ADDR_INDIRECT);
554}
555
Hante Meulemana44aa402014-12-03 21:05:33 +0100556static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
557{
558 struct brcmf_mbss_ssid_le mbss_ssid_le;
559 int bsscfgidx;
560 int err;
561
562 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
563 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
564 if (bsscfgidx < 0)
565 return bsscfgidx;
566
567 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
568 mbss_ssid_le.SSID_len = cpu_to_le32(5);
569 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
570
571 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
572 sizeof(mbss_ssid_le));
573 if (err < 0)
574 brcmf_err("setting ssid failed %d\n", err);
575
576 return err;
577}
578
579/**
580 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
581 *
582 * @wiphy: wiphy device of new interface.
583 * @name: name of the new interface.
584 * @flags: not used.
585 * @params: contains mac address for AP device.
586 */
587static
588struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
589 u32 *flags, struct vif_params *params)
590{
591 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
592 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
593 struct brcmf_cfg80211_vif *vif;
594 int err;
595
596 if (brcmf_cfg80211_vif_event_armed(cfg))
597 return ERR_PTR(-EBUSY);
598
599 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
600
601 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
602 if (IS_ERR(vif))
603 return (struct wireless_dev *)vif;
604
605 brcmf_cfg80211_arm_vif_event(cfg, vif);
606
607 err = brcmf_cfg80211_request_ap_if(ifp);
608 if (err) {
609 brcmf_cfg80211_arm_vif_event(cfg, NULL);
610 goto fail;
611 }
612
613 /* wait for firmware event */
614 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
615 msecs_to_jiffies(1500));
616 brcmf_cfg80211_arm_vif_event(cfg, NULL);
617 if (!err) {
618 brcmf_err("timeout occurred\n");
619 err = -EIO;
620 goto fail;
621 }
622
623 /* interface created in firmware */
624 ifp = vif->ifp;
625 if (!ifp) {
626 brcmf_err("no if pointer provided\n");
627 err = -ENOENT;
628 goto fail;
629 }
630
631 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
632 err = brcmf_net_attach(ifp, true);
633 if (err) {
634 brcmf_err("Registering netdevice failed\n");
635 goto fail;
636 }
637
638 return &ifp->vif->wdev;
639
640fail:
641 brcmf_free_vif(vif);
642 return ERR_PTR(err);
643}
644
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100645static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
646{
647 enum nl80211_iftype iftype;
648
649 iftype = vif->wdev.iftype;
650 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
651}
652
653static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
654{
655 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
656}
657
Arend van Spriel9f440b72013-02-08 15:53:36 +0100658static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
659 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100660 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100661 enum nl80211_iftype type,
662 u32 *flags,
663 struct vif_params *params)
664{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200665 struct wireless_dev *wdev;
666
Arend van Spriel9f440b72013-02-08 15:53:36 +0100667 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
668 switch (type) {
669 case NL80211_IFTYPE_ADHOC:
670 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100671 case NL80211_IFTYPE_AP_VLAN:
672 case NL80211_IFTYPE_WDS:
673 case NL80211_IFTYPE_MONITOR:
674 case NL80211_IFTYPE_MESH_POINT:
675 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100676 case NL80211_IFTYPE_AP:
677 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
678 if (!IS_ERR(wdev))
679 brcmf_cfg80211_update_proto_addr_mode(wdev);
680 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100681 case NL80211_IFTYPE_P2P_CLIENT:
682 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200683 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100684 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200685 if (!IS_ERR(wdev))
686 brcmf_cfg80211_update_proto_addr_mode(wdev);
687 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100688 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100689 default:
690 return ERR_PTR(-EINVAL);
691 }
692}
693
Daniel Kim5e787f72014-06-21 12:11:18 +0200694static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
695{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200696 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200697 brcmf_set_mpc(ifp, mpc);
698}
699
Arend van Sprielf96aa072013-04-05 10:57:48 +0200700void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100701{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100702 s32 err = 0;
703
704 if (check_vif_up(ifp->vif)) {
705 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
706 if (err) {
707 brcmf_err("fail to set mpc\n");
708 return;
709 }
710 brcmf_dbg(INFO, "MPC : %d\n", mpc);
711 }
712}
713
Arend van Spriela0f472a2013-04-05 10:57:49 +0200714s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
715 struct brcmf_if *ifp, bool aborted,
716 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100717{
718 struct brcmf_scan_params_le params_le;
719 struct cfg80211_scan_request *scan_request;
720 s32 err = 0;
721
722 brcmf_dbg(SCAN, "Enter\n");
723
724 /* clear scan request, because the FW abort can cause a second call */
725 /* to this functon and might cause a double cfg80211_scan_done */
726 scan_request = cfg->scan_request;
727 cfg->scan_request = NULL;
728
729 if (timer_pending(&cfg->escan_timeout))
730 del_timer_sync(&cfg->escan_timeout);
731
732 if (fw_abort) {
733 /* Do a scan abort to stop the driver's scan engine */
734 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
735 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800736 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100737 params_le.bss_type = DOT11_BSSTYPE_ANY;
738 params_le.scan_type = 0;
739 params_le.channel_num = cpu_to_le32(1);
740 params_le.nprobes = cpu_to_le32(1);
741 params_le.active_time = cpu_to_le32(-1);
742 params_le.passive_time = cpu_to_le32(-1);
743 params_le.home_time = cpu_to_le32(-1);
744 /* Scan is aborted by setting channel_list[0] to -1 */
745 params_le.channel_list[0] = cpu_to_le16(-1);
746 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200747 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100748 &params_le, sizeof(params_le));
749 if (err)
750 brcmf_err("Scan abort failed\n");
751 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200752
Daniel Kim5e787f72014-06-21 12:11:18 +0200753 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200754
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100755 /*
756 * e-scan can be initiated by scheduled scan
757 * which takes precedence.
758 */
759 if (cfg->sched_escan) {
760 brcmf_dbg(SCAN, "scheduled scan completed\n");
761 cfg->sched_escan = false;
762 if (!aborted)
763 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100764 } else if (scan_request) {
765 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
766 aborted ? "Aborted" : "Done");
767 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100768 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100769 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
770 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100771
772 return err;
773}
774
Arend van Spriel9f440b72013-02-08 15:53:36 +0100775static
776int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
777{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100778 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
779 struct net_device *ndev = wdev->netdev;
780
781 /* vif event pending in firmware */
782 if (brcmf_cfg80211_vif_event_armed(cfg))
783 return -EBUSY;
784
785 if (ndev) {
786 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200787 cfg->escan_info.ifp == netdev_priv(ndev))
788 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
789 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100790
791 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
792 }
793
Arend van Spriel9f440b72013-02-08 15:53:36 +0100794 switch (wdev->iftype) {
795 case NL80211_IFTYPE_ADHOC:
796 case NL80211_IFTYPE_STATION:
797 case NL80211_IFTYPE_AP:
798 case NL80211_IFTYPE_AP_VLAN:
799 case NL80211_IFTYPE_WDS:
800 case NL80211_IFTYPE_MONITOR:
801 case NL80211_IFTYPE_MESH_POINT:
802 return -EOPNOTSUPP;
803 case NL80211_IFTYPE_P2P_CLIENT:
804 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200805 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100806 return brcmf_p2p_del_vif(wiphy, wdev);
807 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100808 default:
809 return -EINVAL;
810 }
811 return -EOPNOTSUPP;
812}
813
Arend van Spriel5b435de2011-10-05 13:19:03 +0200814static s32
815brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
816 enum nl80211_iftype type, u32 *flags,
817 struct vif_params *params)
818{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100819 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700820 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100821 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200822 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200823 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200824 s32 err = 0;
825
Arend van Sprield96b8012012-12-05 15:26:02 +0100826 brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200827
828 switch (type) {
829 case NL80211_IFTYPE_MONITOR:
830 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100831 brcmf_err("type (%d) : currently we do not support this type\n",
832 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200833 return -EOPNOTSUPP;
834 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200835 infra = 0;
836 break;
837 case NL80211_IFTYPE_STATION:
Hante Meuleman1bc7c652013-02-08 15:53:56 +0100838 /* Ignore change for p2p IF. Unclear why supplicant does this */
839 if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
840 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
841 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
842 /* WAR: It is unexpected to get a change of VIF for P2P
843 * IF, but it happens. The request can not be handled
844 * but returning EPERM causes a crash. Returning 0
845 * without setting ieee80211_ptr->iftype causes trace
846 * (WARN_ON) but it works with wpa_supplicant
847 */
848 return 0;
849 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200850 infra = 1;
851 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200852 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100853 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200854 ap = 1;
855 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200856 default:
857 err = -EINVAL;
858 goto done;
859 }
860
Hante Meuleman1a873342012-09-27 14:17:54 +0200861 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100862 if (type == NL80211_IFTYPE_P2P_GO) {
863 brcmf_dbg(INFO, "IF Type = P2P GO\n");
864 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
865 }
866 if (!err) {
867 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
868 brcmf_dbg(INFO, "IF Type = AP\n");
869 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200870 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100871 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200872 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100873 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200874 err = -EAGAIN;
875 goto done;
876 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100877 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100878 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200879 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200880 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200881
Hante Meuleman8851cce2014-07-30 13:20:02 +0200882 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
883
Arend van Spriel5b435de2011-10-05 13:19:03 +0200884done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100885 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200886
887 return err;
888}
889
Franky Lin83cf17a2013-04-11 13:28:50 +0200890static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
891 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200892 struct cfg80211_scan_request *request)
893{
894 u32 n_ssids;
895 u32 n_channels;
896 s32 i;
897 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200898 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200899 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200900 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200901
Joe Perches93803b32015-03-02 19:54:49 -0800902 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200903 params_le->bss_type = DOT11_BSSTYPE_ANY;
904 params_le->scan_type = 0;
905 params_le->channel_num = 0;
906 params_le->nprobes = cpu_to_le32(-1);
907 params_le->active_time = cpu_to_le32(-1);
908 params_le->passive_time = cpu_to_le32(-1);
909 params_le->home_time = cpu_to_le32(-1);
910 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
911
912 /* if request is null exit so it will be all channel broadcast scan */
913 if (!request)
914 return;
915
916 n_ssids = request->n_ssids;
917 n_channels = request->n_channels;
918 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100919 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
920 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200921 if (n_channels > 0) {
922 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200923 chanspec = channel_to_chanspec(&cfg->d11inf,
924 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100925 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
926 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200927 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200928 }
929 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100930 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200931 }
932 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100933 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200934 if (n_ssids > 0) {
935 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
936 n_channels * sizeof(u16);
937 offset = roundup(offset, sizeof(u32));
938 ptr = (char *)params_le + offset;
939 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200940 memset(&ssid_le, 0, sizeof(ssid_le));
941 ssid_le.SSID_len =
942 cpu_to_le32(request->ssids[i].ssid_len);
943 memcpy(ssid_le.SSID, request->ssids[i].ssid,
944 request->ssids[i].ssid_len);
945 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100946 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200947 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100948 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
949 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200950 memcpy(ptr, &ssid_le, sizeof(ssid_le));
951 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200952 }
953 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100954 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200955 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100956 brcmf_dbg(SCAN, "SSID %s len=%d\n",
957 params_le->ssid_le.SSID,
958 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200959 params_le->ssid_le.SSID_len =
960 cpu_to_le32(request->ssids->ssid_len);
961 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
962 request->ssids->ssid_len);
963 }
964 }
965 /* Adding mask to channel numbers */
966 params_le->channel_num =
967 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
968 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
969}
970
971static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200972brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +0200973 struct cfg80211_scan_request *request, u16 action)
974{
975 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
976 offsetof(struct brcmf_escan_params_le, params_le);
977 struct brcmf_escan_params_le *params;
978 s32 err = 0;
979
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100980 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200981
982 if (request != NULL) {
983 /* Allocate space for populating ssids in struct */
984 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
985
986 /* Allocate space for populating ssids in struct */
987 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
988 }
989
990 params = kzalloc(params_size, GFP_KERNEL);
991 if (!params) {
992 err = -ENOMEM;
993 goto exit;
994 }
995 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200996 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200997 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
998 params->action = cpu_to_le16(action);
999 params->sync_id = cpu_to_le16(0x1234);
1000
Arend van Spriela0f472a2013-04-05 10:57:49 +02001001 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +02001002 if (err) {
1003 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001004 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001005 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001006 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001007 }
1008
1009 kfree(params);
1010exit:
1011 return err;
1012}
1013
1014static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001015brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +02001016 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001017{
1018 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001019 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001020 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001021 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001022
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001023 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001024 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001025 escan->wiphy = wiphy;
1026 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001027 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001028 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001029 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001030 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001031 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001032 return err;
1033 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001034 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001035 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001036 results->version = 0;
1037 results->count = 0;
1038 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1039
Arend van Spriela0f472a2013-04-05 10:57:49 +02001040 err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001041 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001042 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001043 return err;
1044}
1045
1046static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001047brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001048 struct cfg80211_scan_request *request,
1049 struct cfg80211_ssid *this_ssid)
1050{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001051 struct brcmf_if *ifp = vif->ifp;
1052 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001053 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -08001054 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001055 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001056 bool escan_req;
1057 bool spec_scan;
1058 s32 err;
1059 u32 SSID_len;
1060
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001061 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001062
Arend van Sprielc1179032012-10-22 13:55:33 -07001063 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001064 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001065 return -EAGAIN;
1066 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001067 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001068 brcmf_err("Scanning being aborted: status (%lu)\n",
1069 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001070 return -EAGAIN;
1071 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001072 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1073 brcmf_err("Scanning suppressed: status (%lu)\n",
1074 cfg->scan_status);
1075 return -EAGAIN;
1076 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001077 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001078 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001079 return -EAGAIN;
1080 }
1081
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001082 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001083 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1084 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001085
Hante Meulemane756af52012-09-11 21:18:52 +02001086 escan_req = false;
1087 if (request) {
1088 /* scan bss */
1089 ssids = request->ssids;
1090 escan_req = true;
1091 } else {
1092 /* scan in ibss */
1093 /* we don't do escan in ibss */
1094 ssids = this_ssid;
1095 }
1096
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001097 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001098 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001099 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001100 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001101 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001102 if (err)
1103 goto scan_out;
1104
Arend van Spriela0f472a2013-04-05 10:57:49 +02001105 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001106 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001107 goto scan_out;
1108 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001109 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1110 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001111 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1112 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1113 sr->ssid_le.SSID_len = cpu_to_le32(0);
1114 spec_scan = false;
1115 if (SSID_len) {
1116 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1117 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1118 spec_scan = true;
1119 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001120 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001121
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001122 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001123 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001124 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001125 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001126 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001127 goto scan_out;
1128 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001129 brcmf_scan_config_mpc(ifp, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -07001130 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -07001131 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001132 if (err) {
1133 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001134 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
1135 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001136 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001137 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001138
Daniel Kim5e787f72014-06-21 12:11:18 +02001139 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001140 goto scan_out;
1141 }
1142 }
1143
Hante Meuleman661fa952015-02-06 18:36:47 +01001144 /* Arm scan timeout timer */
1145 mod_timer(&cfg->escan_timeout, jiffies +
1146 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1147
Hante Meulemane756af52012-09-11 21:18:52 +02001148 return 0;
1149
1150scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001151 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001152 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001153 return err;
1154}
1155
Arend van Spriel5b435de2011-10-05 13:19:03 +02001156static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001157brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001158{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001159 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001160 s32 err = 0;
1161
Arend van Sprield96b8012012-12-05 15:26:02 +01001162 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001163 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1164 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001165 return -EIO;
1166
Arend van Spriela0f472a2013-04-05 10:57:49 +02001167 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001168
Arend van Spriel5b435de2011-10-05 13:19:03 +02001169 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001170 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001171
Arend van Sprield96b8012012-12-05 15:26:02 +01001172 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001173 return err;
1174}
1175
1176static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1177{
1178 s32 err = 0;
1179
Arend van Sprielac24be62012-10-22 10:36:23 -07001180 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1181 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001182 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001183 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184
1185 return err;
1186}
1187
1188static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1189{
1190 s32 err = 0;
1191
Arend van Sprielac24be62012-10-22 10:36:23 -07001192 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1193 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001194 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001195 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001196
1197 return err;
1198}
1199
1200static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1201{
1202 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001203 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001204
Arend van Sprielac24be62012-10-22 10:36:23 -07001205 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001206 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001207 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001208 return err;
1209 }
1210 return err;
1211}
1212
1213static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1214{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001215 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1216 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001217 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001218 s32 err = 0;
1219
Arend van Sprield96b8012012-12-05 15:26:02 +01001220 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001221 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001222 return -EIO;
1223
1224 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001225 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1226 cfg->conf->rts_threshold = wiphy->rts_threshold;
1227 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001228 if (!err)
1229 goto done;
1230 }
1231 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001232 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1233 cfg->conf->frag_threshold = wiphy->frag_threshold;
1234 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001235 if (!err)
1236 goto done;
1237 }
1238 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001239 && (cfg->conf->retry_long != wiphy->retry_long)) {
1240 cfg->conf->retry_long = wiphy->retry_long;
1241 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001242 if (!err)
1243 goto done;
1244 }
1245 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001246 && (cfg->conf->retry_short != wiphy->retry_short)) {
1247 cfg->conf->retry_short = wiphy->retry_short;
1248 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001249 if (!err)
1250 goto done;
1251 }
1252
1253done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001254 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001255 return err;
1256}
1257
Arend van Spriel5b435de2011-10-05 13:19:03 +02001258static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1259{
1260 memset(prof, 0, sizeof(*prof));
1261}
1262
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001263static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1264{
1265 u16 reason;
1266
1267 switch (e->event_code) {
1268 case BRCMF_E_DEAUTH:
1269 case BRCMF_E_DEAUTH_IND:
1270 case BRCMF_E_DISASSOC_IND:
1271 reason = e->reason;
1272 break;
1273 case BRCMF_E_LINK:
1274 default:
1275 reason = 0;
1276 break;
1277 }
1278 return reason;
1279}
1280
1281static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001282{
Piotr Haber61730d42013-04-23 12:53:12 +02001283 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001284 s32 err = 0;
1285
Arend van Sprield96b8012012-12-05 15:26:02 +01001286 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001287
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001288 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001289 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001290 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001291 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001292 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001293 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001294 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001295 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001296 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
Johannes Berg80279fb2015-05-22 16:22:20 +02001297 true, GFP_KERNEL);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001298
Arend van Spriel5b435de2011-10-05 13:19:03 +02001299 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001300 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001301 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1302 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001303 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001304}
1305
1306static s32
1307brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1308 struct cfg80211_ibss_params *params)
1309{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001310 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001311 struct brcmf_if *ifp = netdev_priv(ndev);
1312 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001313 struct brcmf_join_params join_params;
1314 size_t join_params_size = 0;
1315 s32 err = 0;
1316 s32 wsec = 0;
1317 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001318 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001319
Arend van Sprield96b8012012-12-05 15:26:02 +01001320 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001321 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001322 return -EIO;
1323
1324 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001325 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001326 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001327 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001328 return -EOPNOTSUPP;
1329 }
1330
Arend van Sprielc1179032012-10-22 13:55:33 -07001331 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001332
1333 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001334 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001335 else
Arend van Spriel16886732012-12-05 15:26:04 +01001336 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337
Johannes Berg683b6d32012-11-08 21:25:48 +01001338 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001339 brcmf_dbg(CONN, "channel: %d\n",
1340 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001341 else
Arend van Spriel16886732012-12-05 15:26:04 +01001342 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001343
1344 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001345 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001346 else
Arend van Spriel16886732012-12-05 15:26:04 +01001347 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001348
1349 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001350 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001351 else
Arend van Spriel16886732012-12-05 15:26:04 +01001352 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001353
1354 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001355 brcmf_dbg(CONN, "beacon interval: %d\n",
1356 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001357 else
Arend van Spriel16886732012-12-05 15:26:04 +01001358 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001359
1360 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001361 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001362 else
Arend van Spriel16886732012-12-05 15:26:04 +01001363 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001364
1365 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001366 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001367 else
Arend van Spriel16886732012-12-05 15:26:04 +01001368 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001369
1370 /* Configure Privacy for starter */
1371 if (params->privacy)
1372 wsec |= WEP_ENABLED;
1373
Arend van Sprielc1179032012-10-22 13:55:33 -07001374 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001375 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001376 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001377 goto done;
1378 }
1379
1380 /* Configure Beacon Interval for starter */
1381 if (params->beacon_interval)
1382 bcnprd = params->beacon_interval;
1383 else
1384 bcnprd = 100;
1385
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001386 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001387 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001388 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001389 goto done;
1390 }
1391
1392 /* Configure required join parameter */
1393 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1394
1395 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001396 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1397 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1398 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1399 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001400 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001401
1402 /* BSSID */
1403 if (params->bssid) {
1404 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1405 join_params_size = sizeof(join_params.ssid_le) +
1406 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001407 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001408 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001409 eth_broadcast_addr(join_params.params_le.bssid);
1410 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001411 }
1412
Arend van Spriel5b435de2011-10-05 13:19:03 +02001413 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001414 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415 u32 target_channel;
1416
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001417 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001418 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001419 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001420 if (params->channel_fixed) {
1421 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001422 chanspec = chandef_to_chanspec(&cfg->d11inf,
1423 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001424 join_params.params_le.chanspec_list[0] =
1425 cpu_to_le16(chanspec);
1426 join_params.params_le.chanspec_num = cpu_to_le32(1);
1427 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001428 }
1429
1430 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001431 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001432 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001433 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001434 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001435 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001436 goto done;
1437 }
1438 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001439 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001440
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001441 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001442
1443
Arend van Sprielc1179032012-10-22 13:55:33 -07001444 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001445 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001446 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001447 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001448 goto done;
1449 }
1450
1451done:
1452 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001453 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001454 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001455 return err;
1456}
1457
1458static s32
1459brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1460{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001461 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001462
Arend van Sprield96b8012012-12-05 15:26:02 +01001463 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001464 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001465 return -EIO;
1466
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001467 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001468
Arend van Sprield96b8012012-12-05 15:26:02 +01001469 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001470
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001471 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001472}
1473
1474static s32 brcmf_set_wpa_version(struct net_device *ndev,
1475 struct cfg80211_connect_params *sme)
1476{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001477 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001478 struct brcmf_cfg80211_security *sec;
1479 s32 val = 0;
1480 s32 err = 0;
1481
1482 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1483 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1484 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1485 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1486 else
1487 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001488 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001489 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001490 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001491 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001492 return err;
1493 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001494 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001495 sec->wpa_versions = sme->crypto.wpa_versions;
1496 return err;
1497}
1498
1499static s32 brcmf_set_auth_type(struct net_device *ndev,
1500 struct cfg80211_connect_params *sme)
1501{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001502 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001503 struct brcmf_cfg80211_security *sec;
1504 s32 val = 0;
1505 s32 err = 0;
1506
1507 switch (sme->auth_type) {
1508 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1509 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001510 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001511 break;
1512 case NL80211_AUTHTYPE_SHARED_KEY:
1513 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001514 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001515 break;
1516 case NL80211_AUTHTYPE_AUTOMATIC:
1517 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001518 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001519 break;
1520 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001521 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001522 default:
1523 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001524 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001525 break;
1526 }
1527
Hante Meuleman89286dc2013-02-08 15:53:46 +01001528 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001529 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001530 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001531 return err;
1532 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001533 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001534 sec->auth_type = sme->auth_type;
1535 return err;
1536}
1537
1538static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001539brcmf_set_wsec_mode(struct net_device *ndev,
1540 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001541{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001542 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001543 struct brcmf_cfg80211_security *sec;
1544 s32 pval = 0;
1545 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001546 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001547 s32 err = 0;
1548
1549 if (sme->crypto.n_ciphers_pairwise) {
1550 switch (sme->crypto.ciphers_pairwise[0]) {
1551 case WLAN_CIPHER_SUITE_WEP40:
1552 case WLAN_CIPHER_SUITE_WEP104:
1553 pval = WEP_ENABLED;
1554 break;
1555 case WLAN_CIPHER_SUITE_TKIP:
1556 pval = TKIP_ENABLED;
1557 break;
1558 case WLAN_CIPHER_SUITE_CCMP:
1559 pval = AES_ENABLED;
1560 break;
1561 case WLAN_CIPHER_SUITE_AES_CMAC:
1562 pval = AES_ENABLED;
1563 break;
1564 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001565 brcmf_err("invalid cipher pairwise (%d)\n",
1566 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001567 return -EINVAL;
1568 }
1569 }
1570 if (sme->crypto.cipher_group) {
1571 switch (sme->crypto.cipher_group) {
1572 case WLAN_CIPHER_SUITE_WEP40:
1573 case WLAN_CIPHER_SUITE_WEP104:
1574 gval = WEP_ENABLED;
1575 break;
1576 case WLAN_CIPHER_SUITE_TKIP:
1577 gval = TKIP_ENABLED;
1578 break;
1579 case WLAN_CIPHER_SUITE_CCMP:
1580 gval = AES_ENABLED;
1581 break;
1582 case WLAN_CIPHER_SUITE_AES_CMAC:
1583 gval = AES_ENABLED;
1584 break;
1585 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001586 brcmf_err("invalid cipher group (%d)\n",
1587 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001588 return -EINVAL;
1589 }
1590 }
1591
Arend van Spriel16886732012-12-05 15:26:04 +01001592 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001593 /* In case of privacy, but no security and WPS then simulate */
1594 /* setting AES. WPS-2.0 allows no security */
1595 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1596 sme->privacy)
1597 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001598
1599 if (mfp)
1600 wsec = pval | gval | MFP_CAPABLE;
1601 else
1602 wsec = pval | gval;
1603 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001604 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001605 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001606 return err;
1607 }
1608
Arend van Spriel06bb1232012-09-27 14:17:56 +02001609 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001610 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1611 sec->cipher_group = sme->crypto.cipher_group;
1612
1613 return err;
1614}
1615
1616static s32
1617brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1618{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001619 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001620 struct brcmf_cfg80211_security *sec;
1621 s32 val = 0;
1622 s32 err = 0;
1623
1624 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001625 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1626 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001627 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001628 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001629 return err;
1630 }
1631 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1632 switch (sme->crypto.akm_suites[0]) {
1633 case WLAN_AKM_SUITE_8021X:
1634 val = WPA_AUTH_UNSPECIFIED;
1635 break;
1636 case WLAN_AKM_SUITE_PSK:
1637 val = WPA_AUTH_PSK;
1638 break;
1639 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001640 brcmf_err("invalid cipher group (%d)\n",
1641 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001642 return -EINVAL;
1643 }
1644 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1645 switch (sme->crypto.akm_suites[0]) {
1646 case WLAN_AKM_SUITE_8021X:
1647 val = WPA2_AUTH_UNSPECIFIED;
1648 break;
1649 case WLAN_AKM_SUITE_PSK:
1650 val = WPA2_AUTH_PSK;
1651 break;
1652 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001653 brcmf_err("invalid cipher group (%d)\n",
1654 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001655 return -EINVAL;
1656 }
1657 }
1658
Arend van Spriel16886732012-12-05 15:26:04 +01001659 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001660 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1661 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001662 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001663 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001664 return err;
1665 }
1666 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001667 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001668 sec->wpa_auth = sme->crypto.akm_suites[0];
1669
1670 return err;
1671}
1672
1673static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001674brcmf_set_sharedkey(struct net_device *ndev,
1675 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001676{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001677 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001678 struct brcmf_cfg80211_security *sec;
1679 struct brcmf_wsec_key key;
1680 s32 val;
1681 s32 err = 0;
1682
Arend van Spriel16886732012-12-05 15:26:04 +01001683 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001684
Roland Vossena718e2f2011-10-12 20:51:24 +02001685 if (sme->key_len == 0)
1686 return 0;
1687
Arend van Spriel06bb1232012-09-27 14:17:56 +02001688 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001689 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1690 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001691
1692 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1693 return 0;
1694
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001695 if (!(sec->cipher_pairwise &
1696 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1697 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001698
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001699 memset(&key, 0, sizeof(key));
1700 key.len = (u32) sme->key_len;
1701 key.index = (u32) sme->key_idx;
1702 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001703 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001704 return -EINVAL;
1705 }
1706 memcpy(key.data, sme->key, key.len);
1707 key.flags = BRCMF_PRIMARY_KEY;
1708 switch (sec->cipher_pairwise) {
1709 case WLAN_CIPHER_SUITE_WEP40:
1710 key.algo = CRYPTO_ALGO_WEP1;
1711 break;
1712 case WLAN_CIPHER_SUITE_WEP104:
1713 key.algo = CRYPTO_ALGO_WEP128;
1714 break;
1715 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001716 brcmf_err("Invalid algorithm (%d)\n",
1717 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001718 return -EINVAL;
1719 }
1720 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001721 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1722 key.len, key.index, key.algo);
1723 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001724 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001725 if (err)
1726 return err;
1727
1728 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001729 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001730 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001731 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001732 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001733 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001734 }
1735 return err;
1736}
1737
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001738static
1739enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1740 enum nl80211_auth_type type)
1741{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001742 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1743 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1744 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1745 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001746 }
1747 return type;
1748}
1749
Arend van Spriel5b435de2011-10-05 13:19:03 +02001750static s32
1751brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001752 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001753{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001754 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001755 struct brcmf_if *ifp = netdev_priv(ndev);
1756 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001757 struct ieee80211_channel *chan = sme->channel;
1758 struct brcmf_join_params join_params;
1759 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001760 const struct brcmf_tlv *rsn_ie;
1761 const struct brcmf_vs_tlv *wpa_ie;
1762 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001763 u32 ie_len;
1764 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001765 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001766 s32 err = 0;
1767
Arend van Sprield96b8012012-12-05 15:26:02 +01001768 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001769 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001770 return -EIO;
1771
1772 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001773 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001774 return -EOPNOTSUPP;
1775 }
1776
Hante Meuleman89286dc2013-02-08 15:53:46 +01001777 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1778 /* A normal (non P2P) connection request setup. */
1779 ie = NULL;
1780 ie_len = 0;
1781 /* find the WPA_IE */
1782 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1783 if (wpa_ie) {
1784 ie = wpa_ie;
1785 ie_len = wpa_ie->len + TLV_HDR_LEN;
1786 } else {
1787 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001788 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1789 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001790 WLAN_EID_RSN);
1791 if (rsn_ie) {
1792 ie = rsn_ie;
1793 ie_len = rsn_ie->len + TLV_HDR_LEN;
1794 }
1795 }
1796 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1797 }
1798
1799 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1800 sme->ie, sme->ie_len);
1801 if (err)
1802 brcmf_err("Set Assoc REQ IE Failed\n");
1803 else
1804 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1805
Arend van Sprielc1179032012-10-22 13:55:33 -07001806 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001807
1808 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001809 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001810 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001811 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001812 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1813 cfg->channel, chan->center_freq, chanspec);
1814 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001815 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001816 chanspec = 0;
1817 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001818
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001819 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001820
1821 err = brcmf_set_wpa_version(ndev, sme);
1822 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001823 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001824 goto done;
1825 }
1826
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001827 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001828 err = brcmf_set_auth_type(ndev, sme);
1829 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001830 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001831 goto done;
1832 }
1833
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001834 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001835 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001836 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001837 goto done;
1838 }
1839
1840 err = brcmf_set_key_mgmt(ndev, sme);
1841 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001842 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001843 goto done;
1844 }
1845
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001846 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001847 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001848 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001849 goto done;
1850 }
1851
Hante Meuleman89286dc2013-02-08 15:53:46 +01001852 profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
1853 (u32)sme->ssid_len);
1854 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1855 if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1856 profile->ssid.SSID[profile->ssid.SSID_len] = 0;
1857 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
1858 profile->ssid.SSID_len);
1859 }
1860
1861 /* Join with specific BSSID and cached SSID
1862 * If SSID is zero join based on BSSID only
1863 */
1864 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1865 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1866 if (cfg->channel)
1867 join_params_size += sizeof(u16);
1868 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1869 if (ext_join_params == NULL) {
1870 err = -ENOMEM;
1871 goto done;
1872 }
1873 ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
1874 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
1875 profile->ssid.SSID_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001876
Hante Meuleman89286dc2013-02-08 15:53:46 +01001877 /* Set up join scan parameters */
1878 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001879 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1880
1881 if (sme->bssid)
1882 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1883 else
Joe Perches93803b32015-03-02 19:54:49 -08001884 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001885
1886 if (cfg->channel) {
1887 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1888
1889 ext_join_params->assoc_le.chanspec_list[0] =
1890 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001891 /* Increase dwell time to receive probe response or detect
1892 * beacon from target AP at a noisy air only during connect
1893 * command.
1894 */
1895 ext_join_params->scan_le.active_time =
1896 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1897 ext_join_params->scan_le.passive_time =
1898 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1899 /* To sync with presence period of VSDB GO send probe request
1900 * more frequently. Probe request will be stopped when it gets
1901 * probe response from target AP/GO.
1902 */
1903 ext_join_params->scan_le.nprobes =
1904 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1905 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1906 } else {
1907 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1908 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1909 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001910 }
1911
1912 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1913 join_params_size);
1914 kfree(ext_join_params);
1915 if (!err)
1916 /* This is it. join command worked, we are done */
1917 goto done;
1918
1919 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001920 memset(&join_params, 0, sizeof(join_params));
1921 join_params_size = sizeof(join_params.ssid_le);
1922
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001923 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001924 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001925
Hante Meuleman89286dc2013-02-08 15:53:46 +01001926 if (sme->bssid)
1927 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1928 else
Joe Perches93803b32015-03-02 19:54:49 -08001929 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001930
Hante Meuleman17012612013-02-06 18:40:44 +01001931 if (cfg->channel) {
1932 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1933 join_params.params_le.chanspec_num = cpu_to_le32(1);
1934 join_params_size += sizeof(join_params.params_le);
1935 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001936 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001937 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001938 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001939 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001940
1941done:
1942 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001943 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001944 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001945 return err;
1946}
1947
1948static s32
1949brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1950 u16 reason_code)
1951{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001952 struct brcmf_if *ifp = netdev_priv(ndev);
1953 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001954 struct brcmf_scb_val_le scbval;
1955 s32 err = 0;
1956
Arend van Sprield96b8012012-12-05 15:26:02 +01001957 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001958 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001959 return -EIO;
1960
Arend van Sprielc1179032012-10-22 13:55:33 -07001961 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001962 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001963 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001964
Arend van Spriel06bb1232012-09-27 14:17:56 +02001965 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001966 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001967 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001968 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001969 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001970 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971
Arend van Sprield96b8012012-12-05 15:26:02 +01001972 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001973 return err;
1974}
1975
1976static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001977brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001978 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001979{
1980
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001981 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001982 struct net_device *ndev = cfg_to_ndev(cfg);
1983 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001984 u16 txpwrmw;
1985 s32 err = 0;
1986 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001987 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001988
Arend van Sprield96b8012012-12-05 15:26:02 +01001989 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001990 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001991 return -EIO;
1992
1993 switch (type) {
1994 case NL80211_TX_POWER_AUTOMATIC:
1995 break;
1996 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001997 case NL80211_TX_POWER_FIXED:
1998 if (dbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001999 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002000 err = -EINVAL;
2001 goto done;
2002 }
2003 break;
2004 }
2005 /* Make sure radio is off or on as far as software is concerned */
2006 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07002007 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002008 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002009 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002010
2011 if (dbm > 0xffff)
2012 txpwrmw = 0xffff;
2013 else
2014 txpwrmw = (u16) dbm;
Arend van Sprielac24be62012-10-22 10:36:23 -07002015 err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
2016 (s32)brcmf_mw_to_qdbm(txpwrmw));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002017 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002018 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002019 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002020
2021done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002022 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002023 return err;
2024}
2025
Johannes Bergc8442112012-10-24 10:17:18 +02002026static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
2027 struct wireless_dev *wdev,
2028 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002029{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002030 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002031 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002032 s32 txpwrdbm;
2033 u8 result;
2034 s32 err = 0;
2035
Arend van Sprield96b8012012-12-05 15:26:02 +01002036 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002037 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002038 return -EIO;
2039
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002040 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002041 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002042 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002043 goto done;
2044 }
2045
2046 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02002047 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002048
2049done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002050 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002051 return err;
2052}
2053
2054static s32
2055brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
2056 u8 key_idx, bool unicast, bool multicast)
2057{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002058 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002059 u32 index;
2060 u32 wsec;
2061 s32 err = 0;
2062
Arend van Sprield96b8012012-12-05 15:26:02 +01002063 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002064 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002065 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002066 return -EIO;
2067
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002068 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002069 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002070 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002071 goto done;
2072 }
2073
2074 if (wsec & WEP_ENABLED) {
2075 /* Just select a new current key */
2076 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002077 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002078 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002079 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002080 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002081 }
2082done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002083 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002084 return err;
2085}
2086
2087static s32
2088brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2089 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2090{
Hante Meuleman992f6062013-04-02 21:06:17 +02002091 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002092 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002093 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002094 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002095
2096 memset(&key, 0, sizeof(key));
2097 key.index = (u32) key_idx;
2098 /* Instead of bcast for ea address for default wep keys,
2099 driver needs it to be Null */
2100 if (!is_multicast_ether_addr(mac_addr))
2101 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2102 key.len = (u32) params->key_len;
2103 /* check for key index change */
2104 if (key.len == 0) {
2105 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002106 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002107 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002108 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002109 } else {
2110 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002111 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002112 return -EINVAL;
2113 }
2114
Arend van Spriel16886732012-12-05 15:26:04 +01002115 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002116 memcpy(key.data, params->key, key.len);
2117
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002118 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002119 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2120 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002121 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2122 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2123 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2124 }
2125
2126 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2127 if (params->seq && params->seq_len == 6) {
2128 /* rx iv */
2129 u8 *ivptr;
2130 ivptr = (u8 *) params->seq;
2131 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2132 (ivptr[3] << 8) | ivptr[2];
2133 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2134 key.iv_initialized = true;
2135 }
2136
2137 switch (params->cipher) {
2138 case WLAN_CIPHER_SUITE_WEP40:
2139 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002140 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002141 break;
2142 case WLAN_CIPHER_SUITE_WEP104:
2143 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002144 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002145 break;
2146 case WLAN_CIPHER_SUITE_TKIP:
2147 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002148 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002149 break;
2150 case WLAN_CIPHER_SUITE_AES_CMAC:
2151 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002152 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002153 break;
2154 case WLAN_CIPHER_SUITE_CCMP:
2155 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002156 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002157 break;
2158 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002159 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002160 return -EINVAL;
2161 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002162 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002163 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002164 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002165 }
2166 return err;
2167}
2168
2169static s32
2170brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2171 u8 key_idx, bool pairwise, const u8 *mac_addr,
2172 struct key_params *params)
2173{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002174 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002175 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002176 s32 val;
2177 s32 wsec;
2178 s32 err = 0;
2179 u8 keybuf[8];
2180
Arend van Sprield96b8012012-12-05 15:26:02 +01002181 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002182 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002183 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002184 return -EIO;
2185
Hante Meuleman118eb302014-12-21 12:43:49 +01002186 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2187 /* we ignore this key index in this case */
2188 brcmf_err("invalid key index (%d)\n", key_idx);
2189 return -EINVAL;
2190 }
2191
Daniel Kim787eb032014-01-29 15:32:23 +01002192 if (mac_addr &&
2193 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2194 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002195 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002196 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2197 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002198
Hante Meuleman118eb302014-12-21 12:43:49 +01002199 key = &ifp->vif->profile.key[key_idx];
2200 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002201
Hante Meuleman118eb302014-12-21 12:43:49 +01002202 if (params->key_len > sizeof(key->data)) {
2203 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002204 err = -EINVAL;
2205 goto done;
2206 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002207 key->len = params->key_len;
2208 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002209
Hante Meuleman118eb302014-12-21 12:43:49 +01002210 memcpy(key->data, params->key, key->len);
2211
2212 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002213 switch (params->cipher) {
2214 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002215 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002216 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002217 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002218 break;
2219 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002220 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002221 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002222 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002223 break;
2224 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002225 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002226 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002227 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2228 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2229 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002230 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002231 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002232 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002233 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002234 break;
2235 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002236 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002237 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002238 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002239 break;
2240 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002241 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002242 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002243 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002244 break;
2245 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002246 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002247 err = -EINVAL;
2248 goto done;
2249 }
2250
Hante Meuleman118eb302014-12-21 12:43:49 +01002251 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002252 if (err)
2253 goto done;
2254
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002255 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002256 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002257 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002258 goto done;
2259 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002260 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002261 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002262 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002263 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002264 goto done;
2265 }
2266
Arend van Spriel5b435de2011-10-05 13:19:03 +02002267done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002268 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002269 return err;
2270}
2271
2272static s32
2273brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2274 u8 key_idx, bool pairwise, const u8 *mac_addr)
2275{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002276 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002277 struct brcmf_wsec_key key;
2278 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002279
Arend van Sprield96b8012012-12-05 15:26:02 +01002280 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002281 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002282 return -EIO;
2283
Hante Meuleman118eb302014-12-21 12:43:49 +01002284 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002285 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002286 return -EINVAL;
2287 }
2288
Arend van Spriel5b435de2011-10-05 13:19:03 +02002289 memset(&key, 0, sizeof(key));
2290
2291 key.index = (u32) key_idx;
2292 key.flags = BRCMF_PRIMARY_KEY;
2293 key.algo = CRYPTO_ALGO_OFF;
2294
Arend van Spriel16886732012-12-05 15:26:04 +01002295 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002296
2297 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002298 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002299
Arend van Sprield96b8012012-12-05 15:26:02 +01002300 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002301 return err;
2302}
2303
2304static s32
2305brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2306 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2307 void (*callback) (void *cookie, struct key_params * params))
2308{
2309 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002310 struct brcmf_if *ifp = netdev_priv(ndev);
2311 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002312 struct brcmf_cfg80211_security *sec;
2313 s32 wsec;
2314 s32 err = 0;
2315
Arend van Sprield96b8012012-12-05 15:26:02 +01002316 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002317 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002318 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002319 return -EIO;
2320
2321 memset(&params, 0, sizeof(params));
2322
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002323 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002324 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002325 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002326 /* Ignore this error, may happen during DISASSOC */
2327 err = -EAGAIN;
2328 goto done;
2329 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002330 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002331 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002332 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2333 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002334 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002335 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2336 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002337 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002338 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002339 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002340 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002341 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002342 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002343 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002344 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002345 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002346 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002347 err = -EINVAL;
2348 goto done;
2349 }
2350 callback(cookie, &params);
2351
2352done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002353 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002354 return err;
2355}
2356
2357static s32
2358brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2359 struct net_device *ndev, u8 key_idx)
2360{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002361 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002362
2363 return -EOPNOTSUPP;
2364}
2365
Hante Meuleman118eb302014-12-21 12:43:49 +01002366static void
2367brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2368{
2369 s32 err;
2370 u8 key_idx;
2371 struct brcmf_wsec_key *key;
2372 s32 wsec;
2373
2374 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2375 key = &ifp->vif->profile.key[key_idx];
2376 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2377 (key->algo == CRYPTO_ALGO_WEP128))
2378 break;
2379 }
2380 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2381 return;
2382
2383 err = send_key_to_dongle(ifp, key);
2384 if (err) {
2385 brcmf_err("Setting WEP key failed (%d)\n", err);
2386 return;
2387 }
2388 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2389 if (err) {
2390 brcmf_err("get wsec error (%d)\n", err);
2391 return;
2392 }
2393 wsec |= WEP_ENABLED;
2394 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2395 if (err)
2396 brcmf_err("set wsec error (%d)\n", err);
2397}
2398
Arend van Spriel5b435de2011-10-05 13:19:03 +02002399static s32
2400brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002401 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002402{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002403 struct brcmf_if *ifp = netdev_priv(ndev);
2404 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002405 struct brcmf_scb_val_le scb_val;
2406 int rssi;
2407 s32 rate;
2408 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02002409 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002410 struct brcmf_sta_info_le sta_info_le;
Hante Meuleman9ee66d12014-01-29 15:32:11 +01002411 u32 beacon_period;
2412 u32 dtim_period;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002413
Arend van Sprield96b8012012-12-05 15:26:02 +01002414 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002415 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002416 return -EIO;
2417
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002418 if (brcmf_is_apmode(ifp->vif)) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002419 memcpy(&sta_info_le, mac, ETH_ALEN);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002420 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002421 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002422 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002423 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002424 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002425 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002426 }
Johannes Berg319090b2014-11-17 14:08:11 +01002427 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002428 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2429 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Johannes Berg319090b2014-11-17 14:08:11 +01002430 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002431 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02002432 }
Arend van Sprield96b8012012-12-05 15:26:02 +01002433 brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
2434 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002435 } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002436 if (memcmp(mac, bssid, ETH_ALEN)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002437 brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
2438 mac, bssid);
Hante Meuleman1a873342012-09-27 14:17:54 +02002439 err = -ENOENT;
2440 goto done;
2441 }
2442 /* Report the current tx rate */
Hante Meuleman89286dc2013-02-08 15:53:46 +01002443 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02002444 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002445 brcmf_err("Could not get rate (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002446 goto done;
2447 } else {
Johannes Berg319090b2014-11-17 14:08:11 +01002448 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman1a873342012-09-27 14:17:54 +02002449 sinfo->txrate.legacy = rate * 5;
Arend van Spriel16886732012-12-05 15:26:04 +01002450 brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
Hante Meuleman1a873342012-09-27 14:17:54 +02002451 }
2452
Arend van Sprielc1179032012-10-22 13:55:33 -07002453 if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
2454 &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002455 memset(&scb_val, 0, sizeof(scb_val));
Arend van Sprielc1179032012-10-22 13:55:33 -07002456 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
2457 &scb_val, sizeof(scb_val));
Hante Meuleman1a873342012-09-27 14:17:54 +02002458 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002459 brcmf_err("Could not get rssi (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002460 goto done;
2461 } else {
2462 rssi = le32_to_cpu(scb_val.val);
Johannes Berg319090b2014-11-17 14:08:11 +01002463 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
Hante Meuleman1a873342012-09-27 14:17:54 +02002464 sinfo->signal = rssi;
Arend van Spriel16886732012-12-05 15:26:04 +01002465 brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
Hante Meuleman1a873342012-09-27 14:17:54 +02002466 }
Hante Meuleman9ee66d12014-01-29 15:32:11 +01002467 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
2468 &beacon_period);
2469 if (err) {
2470 brcmf_err("Could not get beacon period (%d)\n",
2471 err);
2472 goto done;
2473 } else {
2474 sinfo->bss_param.beacon_interval =
2475 beacon_period;
2476 brcmf_dbg(CONN, "Beacon peroid %d\n",
2477 beacon_period);
2478 }
2479 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
2480 &dtim_period);
2481 if (err) {
2482 brcmf_err("Could not get DTIM period (%d)\n",
2483 err);
2484 goto done;
2485 } else {
2486 sinfo->bss_param.dtim_period = dtim_period;
2487 brcmf_dbg(CONN, "DTIM peroid %d\n",
2488 dtim_period);
2489 }
Johannes Berg319090b2014-11-17 14:08:11 +01002490 sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
Hante Meuleman1a873342012-09-27 14:17:54 +02002491 }
2492 } else
2493 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002494done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002495 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002496 return err;
2497}
2498
2499static s32
2500brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2501 bool enabled, s32 timeout)
2502{
2503 s32 pm;
2504 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002505 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002506 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002507
Arend van Sprield96b8012012-12-05 15:26:02 +01002508 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002509
2510 /*
2511 * Powersave enable/disable request is coming from the
2512 * cfg80211 even before the interface is up. In that
2513 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002514 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002515 * FW later while initializing the dongle
2516 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002517 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002518 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002519
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002520 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002521 goto done;
2522 }
2523
2524 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002525 /* Do not enable the power save after assoc if it is a p2p interface */
2526 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2527 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2528 pm = PM_OFF;
2529 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002530 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002531
Arend van Sprielc1179032012-10-22 13:55:33 -07002532 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002533 if (err) {
2534 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002535 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002536 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002537 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002538 }
2539done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002540 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002541 return err;
2542}
2543
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002544static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002545 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002546{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002547 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002548 struct ieee80211_channel *notify_channel;
2549 struct cfg80211_bss *bss;
2550 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002551 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002552 u16 channel;
2553 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002554 u16 notify_capability;
2555 u16 notify_interval;
2556 u8 *notify_ie;
2557 size_t notify_ielen;
2558 s32 notify_signal;
2559
2560 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002561 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002562 return 0;
2563 }
2564
Franky Lin83cf17a2013-04-11 13:28:50 +02002565 if (!bi->ctl_ch) {
2566 ch.chspec = le16_to_cpu(bi->chanspec);
2567 cfg->d11inf.decchspec(&ch);
2568 bi->ctl_ch = ch.chnum;
2569 }
2570 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002571
2572 if (channel <= CH_MAX_2G_CHANNEL)
2573 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2574 else
2575 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2576
2577 freq = ieee80211_channel_to_frequency(channel, band->band);
2578 notify_channel = ieee80211_get_channel(wiphy, freq);
2579
Arend van Spriel5b435de2011-10-05 13:19:03 +02002580 notify_capability = le16_to_cpu(bi->capability);
2581 notify_interval = le16_to_cpu(bi->beacon_period);
2582 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2583 notify_ielen = le32_to_cpu(bi->ie_length);
2584 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2585
Arend van Spriel16886732012-12-05 15:26:04 +01002586 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2587 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2588 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2589 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2590 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002591
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002592 bss = cfg80211_inform_bss(wiphy, notify_channel,
2593 CFG80211_BSS_FTYPE_UNKNOWN,
2594 (const u8 *)bi->BSSID,
2595 0, notify_capability,
2596 notify_interval, notify_ie,
2597 notify_ielen, notify_signal,
2598 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002599
Franky Line78946e2011-11-10 20:30:34 +01002600 if (!bss)
2601 return -ENOMEM;
2602
Johannes Berg5b112d32013-02-01 01:49:58 +01002603 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002604
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002605 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002606}
2607
Roland Vossen6f09be02011-10-18 14:03:02 +02002608static struct brcmf_bss_info_le *
2609next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2610{
2611 if (bss == NULL)
2612 return list->bss_info_le;
2613 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2614 le32_to_cpu(bss->length));
2615}
2616
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002617static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002618{
2619 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002620 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002621 s32 err = 0;
2622 int i;
2623
Hante Meulemanef8596e2014-09-30 10:23:13 +02002624 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002625 if (bss_list->count != 0 &&
2626 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002627 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2628 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002629 return -EOPNOTSUPP;
2630 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002631 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002632 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002633 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002634 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002635 if (err)
2636 break;
2637 }
2638 return err;
2639}
2640
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002641static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002642 struct net_device *ndev, const u8 *bssid)
2643{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002644 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002645 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002646 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002647 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002648 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002649 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002650 u8 *buf = NULL;
2651 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002652 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002653 u16 notify_capability;
2654 u16 notify_interval;
2655 u8 *notify_ie;
2656 size_t notify_ielen;
2657 s32 notify_signal;
2658
Arend van Sprield96b8012012-12-05 15:26:02 +01002659 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002660
2661 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2662 if (buf == NULL) {
2663 err = -ENOMEM;
2664 goto CleanUp;
2665 }
2666
2667 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2668
Arend van Sprielac24be62012-10-22 10:36:23 -07002669 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2670 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002671 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002672 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002673 goto CleanUp;
2674 }
2675
Roland Vossend34bf642011-10-18 14:03:01 +02002676 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002677
Franky Lin83cf17a2013-04-11 13:28:50 +02002678 ch.chspec = le16_to_cpu(bi->chanspec);
2679 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002680
Franky Lin83cf17a2013-04-11 13:28:50 +02002681 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002682 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2683 else
2684 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2685
Franky Lin83cf17a2013-04-11 13:28:50 +02002686 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002687 notify_channel = ieee80211_get_channel(wiphy, freq);
2688
Arend van Spriel5b435de2011-10-05 13:19:03 +02002689 notify_capability = le16_to_cpu(bi->capability);
2690 notify_interval = le16_to_cpu(bi->beacon_period);
2691 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2692 notify_ielen = le32_to_cpu(bi->ie_length);
2693 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2694
Franky Lin83cf17a2013-04-11 13:28:50 +02002695 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002696 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2697 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2698 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002699
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002700 bss = cfg80211_inform_bss(wiphy, notify_channel,
2701 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2702 notify_capability, notify_interval,
2703 notify_ie, notify_ielen, notify_signal,
2704 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002705
Franky Line78946e2011-11-10 20:30:34 +01002706 if (!bss) {
2707 err = -ENOMEM;
2708 goto CleanUp;
2709 }
2710
Johannes Berg5b112d32013-02-01 01:49:58 +01002711 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002712
Arend van Spriel5b435de2011-10-05 13:19:03 +02002713CleanUp:
2714
2715 kfree(buf);
2716
Arend van Sprield96b8012012-12-05 15:26:02 +01002717 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002718
2719 return err;
2720}
2721
Hante Meuleman89286dc2013-02-08 15:53:46 +01002722static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2723 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002724{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002725 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002726 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002727 struct brcmf_ssid *ssid;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002728 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002729 u16 beacon_interval;
2730 u8 dtim_period;
2731 size_t ie_len;
2732 u8 *ie;
2733 s32 err = 0;
2734
Arend van Sprield96b8012012-12-05 15:26:02 +01002735 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002736 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002737 return err;
2738
Arend van Spriel06bb1232012-09-27 14:17:56 +02002739 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002740
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002741 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002742 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002743 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002744 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002745 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002746 goto update_bss_info_out;
2747 }
2748
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002749 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2750 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002751 if (err)
2752 goto update_bss_info_out;
2753
2754 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2755 ie_len = le32_to_cpu(bi->ie_length);
2756 beacon_interval = le16_to_cpu(bi->beacon_period);
2757
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002758 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002759 if (tim)
2760 dtim_period = tim->data[1];
2761 else {
2762 /*
2763 * active scan was done so we could not get dtim
2764 * information out of probe response.
2765 * so we speficially query dtim information to dongle.
2766 */
2767 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002768 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002769 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002770 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002771 goto update_bss_info_out;
2772 }
2773 dtim_period = (u8)var;
2774 }
2775
Arend van Spriel5b435de2011-10-05 13:19:03 +02002776update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002777 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002778 return err;
2779}
2780
Hante Meuleman18e2f612013-02-08 15:53:49 +01002781void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002782{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002783 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002784
Arend van Sprielc1179032012-10-22 13:55:33 -07002785 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002786 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002787 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002788 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002789 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002790 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2791 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002792}
2793
Hante Meulemane756af52012-09-11 21:18:52 +02002794static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2795{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002796 struct brcmf_cfg80211_info *cfg =
2797 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002798 escan_timeout_work);
2799
Hante Meulemanef8596e2014-09-30 10:23:13 +02002800 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002801 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002802}
2803
2804static void brcmf_escan_timeout(unsigned long data)
2805{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002806 struct brcmf_cfg80211_info *cfg =
2807 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002808
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002809 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002810 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002811 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002812 }
2813}
2814
2815static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002816brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2817 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002818 struct brcmf_bss_info_le *bss_info_le)
2819{
Franky Lin83cf17a2013-04-11 13:28:50 +02002820 struct brcmu_chan ch_bss, ch_bss_info_le;
2821
2822 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2823 cfg->d11inf.decchspec(&ch_bss);
2824 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2825 cfg->d11inf.decchspec(&ch_bss_info_le);
2826
Hante Meulemane756af52012-09-11 21:18:52 +02002827 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002828 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002829 bss_info_le->SSID_len == bss->SSID_len &&
2830 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002831 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2832 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002833 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2834 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2835
Hante Meulemane756af52012-09-11 21:18:52 +02002836 /* preserve max RSSI if the measurements are
2837 * both on-channel or both off-channel
2838 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002839 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002840 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002841 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2842 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002843 /* preserve the on-channel rssi measurement
2844 * if the new measurement is off channel
2845 */
2846 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002847 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002848 }
2849 return 1;
2850 }
2851 return 0;
2852}
2853
2854static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002855brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002856 const struct brcmf_event_msg *e, void *data)
2857{
Arend van Spriel19937322012-11-05 16:22:32 -08002858 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002859 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002860 struct brcmf_escan_result_le *escan_result_le;
2861 struct brcmf_bss_info_le *bss_info_le;
2862 struct brcmf_bss_info_le *bss = NULL;
2863 u32 bi_length;
2864 struct brcmf_scan_results *list;
2865 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002866 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002867
Arend van Spriel5c36b992012-11-14 18:46:05 -08002868 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002869
Arend van Spriela0f472a2013-04-05 10:57:49 +02002870 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2871 brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002872 return -EPERM;
2873 }
2874
2875 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002876 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002877 escan_result_le = (struct brcmf_escan_result_le *) data;
2878 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002879 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002880 goto exit;
2881 }
Hante Meulemane756af52012-09-11 21:18:52 +02002882 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002883 brcmf_err("Invalid bss_count %d: ignoring\n",
2884 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002885 goto exit;
2886 }
2887 bss_info_le = &escan_result_le->bss_info_le;
2888
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002889 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2890 goto exit;
2891
2892 if (!cfg->scan_request) {
2893 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2894 goto exit;
2895 }
2896
Hante Meulemane756af52012-09-11 21:18:52 +02002897 bi_length = le32_to_cpu(bss_info_le->length);
2898 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2899 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002900 brcmf_err("Invalid bss_info length %d: ignoring\n",
2901 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002902 goto exit;
2903 }
2904
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002905 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002906 BIT(NL80211_IFTYPE_ADHOC))) {
2907 if (le16_to_cpu(bss_info_le->capability) &
2908 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002909 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002910 goto exit;
2911 }
2912 }
2913
2914 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002915 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002916 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002917 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002918 goto exit;
2919 }
2920
2921 for (i = 0; i < list->count; i++) {
2922 bss = bss ? (struct brcmf_bss_info_le *)
2923 ((unsigned char *)bss +
2924 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002925 if (brcmf_compare_update_same_bss(cfg, bss,
2926 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002927 goto exit;
2928 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002929 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002930 bss_info_le, bi_length);
2931 list->version = le32_to_cpu(bss_info_le->version);
2932 list->buflen += bi_length;
2933 list->count++;
2934 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002935 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002936 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2937 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002938 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002939 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002940 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02002941 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02002942 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002943 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2944 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002945 }
2946exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002947 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02002948}
2949
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002950static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002951{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002952 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
2953 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08002954 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2955 /* Init scan_timeout timer */
2956 init_timer(&cfg->escan_timeout);
2957 cfg->escan_timeout.data = (unsigned long) cfg;
2958 cfg->escan_timeout.function = brcmf_escan_timeout;
2959 INIT_WORK(&cfg->escan_timeout_work,
2960 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002961}
2962
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002963static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002964{
2965 if (ms < 1000 / HZ) {
2966 cond_resched();
2967 mdelay(ms);
2968 } else {
2969 msleep(ms);
2970 }
2971}
2972
Hante Meulemanb9a82f82014-10-28 14:56:06 +01002973static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
2974 u8 *pattern, u32 patternsize, u8 *mask,
2975 u32 packet_offset)
2976{
2977 struct brcmf_fil_wowl_pattern_le *filter;
2978 u32 masksize;
2979 u32 patternoffset;
2980 u8 *buf;
2981 u32 bufsize;
2982 s32 ret;
2983
2984 masksize = (patternsize + 7) / 8;
2985 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
2986
2987 bufsize = sizeof(*filter) + patternsize + masksize;
2988 buf = kzalloc(bufsize, GFP_KERNEL);
2989 if (!buf)
2990 return -ENOMEM;
2991 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
2992
2993 memcpy(filter->cmd, cmd, 4);
2994 filter->masksize = cpu_to_le32(masksize);
2995 filter->offset = cpu_to_le32(packet_offset);
2996 filter->patternoffset = cpu_to_le32(patternoffset);
2997 filter->patternsize = cpu_to_le32(patternsize);
2998 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
2999
3000 if ((mask) && (masksize))
3001 memcpy(buf + sizeof(*filter), mask, masksize);
3002 if ((pattern) && (patternsize))
3003 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3004
3005 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3006
3007 kfree(buf);
3008 return ret;
3009}
3010
Arend van Spriel5b435de2011-10-05 13:19:03 +02003011static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3012{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003013 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3014 struct net_device *ndev = cfg_to_ndev(cfg);
3015 struct brcmf_if *ifp = netdev_priv(ndev);
3016
Arend van Sprield96b8012012-12-05 15:26:02 +01003017 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003018
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003019 if (cfg->wowl_enabled) {
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003020 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003021 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3022 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003023 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003024 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003025 cfg->wowl_enabled = false;
3026 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003027 return 0;
3028}
3029
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003030static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3031 struct brcmf_if *ifp,
3032 struct cfg80211_wowlan *wowl)
3033{
3034 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003035 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003036
3037 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3038
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003039 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003040 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3041 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3042
3043 wowl_config = 0;
3044 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003045 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003046 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003047 wowl_config |= BRCMF_WOWL_MAGIC;
3048 if ((wowl->patterns) && (wowl->n_patterns)) {
3049 wowl_config |= BRCMF_WOWL_NET;
3050 for (i = 0; i < wowl->n_patterns; i++) {
3051 brcmf_config_wowl_pattern(ifp, "add",
3052 (u8 *)wowl->patterns[i].pattern,
3053 wowl->patterns[i].pattern_len,
3054 (u8 *)wowl->patterns[i].mask,
3055 wowl->patterns[i].pkt_offset);
3056 }
3057 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003058 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3059 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3060 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3061 cfg->wowl_enabled = true;
3062}
3063
Arend van Spriel5b435de2011-10-05 13:19:03 +02003064static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003065 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003066{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003067 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3068 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003069 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003070 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003071
Arend van Sprield96b8012012-12-05 15:26:02 +01003072 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003073
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003074 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003075 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003076 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003077 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003078 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003079
Arend van Spriel7d641072012-10-22 13:55:39 -07003080 /* end any scanning */
3081 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003082 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003083
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003084 if (wowl == NULL) {
3085 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3086 list_for_each_entry(vif, &cfg->vif_list, list) {
3087 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3088 continue;
3089 /* While going to suspend if associated with AP
3090 * disassociate from AP to save power while system is
3091 * in suspended state
3092 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003093 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003094 /* Make sure WPA_Supplicant receives all the event
3095 * generated due to DISASSOC call to the fw to keep
3096 * the state fw and WPA_Supplicant state consistent
3097 */
3098 brcmf_delay(500);
3099 }
3100 /* Configure MPC */
3101 brcmf_set_mpc(ifp, 1);
3102
3103 } else {
3104 /* Configure WOWL paramaters */
3105 brcmf_configure_wowl(cfg, ifp, wowl);
3106 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003107
Arend van Spriel7d641072012-10-22 13:55:39 -07003108exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003109 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003110 /* clear any scanning activity */
3111 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003112 return 0;
3113}
3114
3115static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003116brcmf_update_pmklist(struct net_device *ndev,
3117 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3118{
3119 int i, j;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003120 u32 pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003121
Arend van Spriel40c8e952011-10-12 20:51:20 +02003122 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3123
Arend van Spriel16886732012-12-05 15:26:04 +01003124 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003125 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01003126 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
3127 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003128 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01003129 brcmf_dbg(CONN, "%02x\n",
3130 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003131 }
3132
3133 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07003134 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
3135 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003136
3137 return err;
3138}
3139
3140static s32
3141brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3142 struct cfg80211_pmksa *pmksa)
3143{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003144 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003145 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003146 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003147 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003148 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003149
Arend van Sprield96b8012012-12-05 15:26:02 +01003150 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003151 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003152 return -EIO;
3153
Arend van Spriel40c8e952011-10-12 20:51:20 +02003154 pmkid_len = le32_to_cpu(pmkids->npmkid);
3155 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003156 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3157 break;
3158 if (i < WL_NUM_PMKIDS_MAX) {
3159 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3160 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003161 if (i == pmkid_len) {
3162 pmkid_len++;
3163 pmkids->npmkid = cpu_to_le32(pmkid_len);
3164 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003165 } else
3166 err = -EINVAL;
3167
Arend van Spriel16886732012-12-05 15:26:04 +01003168 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
3169 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003170 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003171 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003172
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003173 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003174
Arend van Sprield96b8012012-12-05 15:26:02 +01003175 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003176 return err;
3177}
3178
3179static s32
3180brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3181 struct cfg80211_pmksa *pmksa)
3182{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003183 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003184 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003185 struct pmkid_list pmkid;
3186 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003187 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003188
Arend van Sprield96b8012012-12-05 15:26:02 +01003189 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003190 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003191 return -EIO;
3192
3193 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3194 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3195
Arend van Spriel16886732012-12-05 15:26:04 +01003196 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3197 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003198 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003199 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003200
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003201 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003202 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003203 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003204 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003205 ETH_ALEN))
3206 break;
3207
Arend van Spriel40c8e952011-10-12 20:51:20 +02003208 if ((pmkid_len > 0)
3209 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003210 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003211 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003212 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003213 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3214 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003215 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003216 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3217 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003218 WLAN_PMKID_LEN);
3219 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003220 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003221 } else
3222 err = -EINVAL;
3223
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003224 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003225
Arend van Sprield96b8012012-12-05 15:26:02 +01003226 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003227 return err;
3228
3229}
3230
3231static s32
3232brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3233{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003234 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003235 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003236 s32 err = 0;
3237
Arend van Sprield96b8012012-12-05 15:26:02 +01003238 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003239 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003240 return -EIO;
3241
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003242 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3243 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003244
Arend van Sprield96b8012012-12-05 15:26:02 +01003245 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003246 return err;
3247
3248}
3249
Arend van Spriele5806072012-09-19 22:21:08 +02003250/*
3251 * PFN result doesn't have all the info which are
3252 * required by the supplicant
3253 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3254 * via wl_inform_single_bss in the required format. Escan does require the
3255 * scan request in the form of cfg80211_scan_request. For timebeing, create
3256 * cfg80211_scan_request one out of the received PNO event.
3257 */
3258static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003259brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003260 const struct brcmf_event_msg *e, void *data)
3261{
Arend van Spriel19937322012-11-05 16:22:32 -08003262 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003263 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3264 struct cfg80211_scan_request *request = NULL;
3265 struct cfg80211_ssid *ssid = NULL;
3266 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003267 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003268 int err = 0;
3269 int channel_req = 0;
3270 int band = 0;
3271 struct brcmf_pno_scanresults_le *pfn_result;
3272 u32 result_count;
3273 u32 status;
3274
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003275 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003276
Arend van Spriel5c36b992012-11-14 18:46:05 -08003277 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003278 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003279 return 0;
3280 }
3281
3282 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3283 result_count = le32_to_cpu(pfn_result->count);
3284 status = le32_to_cpu(pfn_result->status);
3285
3286 /*
3287 * PFN event is limited to fit 512 bytes so we may get
3288 * multiple NET_FOUND events. For now place a warning here.
3289 */
3290 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003291 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003292 if (result_count > 0) {
3293 int i;
3294
3295 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003296 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3297 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003298 if (!request || !ssid || !channel) {
3299 err = -ENOMEM;
3300 goto out_err;
3301 }
3302
3303 request->wiphy = wiphy;
3304 data += sizeof(struct brcmf_pno_scanresults_le);
3305 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3306
3307 for (i = 0; i < result_count; i++) {
3308 netinfo = &netinfo_start[i];
3309 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003310 brcmf_err("Invalid netinfo ptr. index: %d\n",
3311 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003312 err = -EINVAL;
3313 goto out_err;
3314 }
3315
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003316 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3317 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003318 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3319 ssid[i].ssid_len = netinfo->SSID_len;
3320 request->n_ssids++;
3321
3322 channel_req = netinfo->channel;
3323 if (channel_req <= CH_MAX_2G_CHANNEL)
3324 band = NL80211_BAND_2GHZ;
3325 else
3326 band = NL80211_BAND_5GHZ;
3327 channel[i].center_freq =
3328 ieee80211_channel_to_frequency(channel_req,
3329 band);
3330 channel[i].band = band;
3331 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3332 request->channels[i] = &channel[i];
3333 request->n_channels++;
3334 }
3335
3336 /* assign parsed ssid array */
3337 if (request->n_ssids)
3338 request->ssids = &ssid[0];
3339
Arend van Sprielc1179032012-10-22 13:55:33 -07003340 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003341 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003342 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003343 }
3344
Arend van Sprielc1179032012-10-22 13:55:33 -07003345 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003346 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003347 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003348 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003349 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003350 goto out_err;
3351 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003352 cfg->sched_escan = true;
3353 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003354 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003355 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003356 goto out_err;
3357 }
3358
3359 kfree(ssid);
3360 kfree(channel);
3361 kfree(request);
3362 return 0;
3363
3364out_err:
3365 kfree(ssid);
3366 kfree(channel);
3367 kfree(request);
3368 cfg80211_sched_scan_stopped(wiphy);
3369 return err;
3370}
3371
Arend van Spriele5806072012-09-19 22:21:08 +02003372static int brcmf_dev_pno_clean(struct net_device *ndev)
3373{
Arend van Spriele5806072012-09-19 22:21:08 +02003374 int ret;
3375
3376 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003377 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003378 if (ret == 0) {
3379 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003380 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3381 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003382 }
3383 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003384 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003385
3386 return ret;
3387}
3388
3389static int brcmf_dev_pno_config(struct net_device *ndev)
3390{
3391 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003392
3393 memset(&pfn_param, 0, sizeof(pfn_param));
3394 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3395
3396 /* set extra pno params */
3397 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3398 pfn_param.repeat = BRCMF_PNO_REPEAT;
3399 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3400
3401 /* set up pno scan fr */
3402 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3403
Arend van Sprielac24be62012-10-22 10:36:23 -07003404 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3405 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003406}
3407
3408static int
3409brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3410 struct net_device *ndev,
3411 struct cfg80211_sched_scan_request *request)
3412{
Arend van Sprielc1179032012-10-22 13:55:33 -07003413 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003414 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003415 struct brcmf_pno_net_param_le pfn;
3416 int i;
3417 int ret = 0;
3418
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003419 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003420 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003421 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003422 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003423 return -EAGAIN;
3424 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003425 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3426 brcmf_err("Scanning suppressed: status (%lu)\n",
3427 cfg->scan_status);
3428 return -EAGAIN;
3429 }
Arend van Spriele5806072012-09-19 22:21:08 +02003430
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003431 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003432 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003433 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003434 return -EINVAL;
3435 }
3436
3437 if (request->n_ssids > 0) {
3438 for (i = 0; i < request->n_ssids; i++) {
3439 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003440 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3441 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003442
3443 /*
3444 * match_set ssids is a supert set of n_ssid list,
3445 * so we need not add these set seperately.
3446 */
3447 }
3448 }
3449
3450 if (request->n_match_sets > 0) {
3451 /* clean up everything */
3452 ret = brcmf_dev_pno_clean(ndev);
3453 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003454 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003455 return ret;
3456 }
3457
3458 /* configure pno */
3459 ret = brcmf_dev_pno_config(ndev);
3460 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003461 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003462 return -EINVAL;
3463 }
3464
3465 /* configure each match set */
3466 for (i = 0; i < request->n_match_sets; i++) {
3467 struct cfg80211_ssid *ssid;
3468 u32 ssid_len;
3469
3470 ssid = &request->match_sets[i].ssid;
3471 ssid_len = ssid->ssid_len;
3472
3473 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003474 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003475 continue;
3476 }
3477 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3478 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3479 pfn.wsec = cpu_to_le32(0);
3480 pfn.infra = cpu_to_le32(1);
3481 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3482 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3483 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003484 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003485 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003486 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3487 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003488 }
3489 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003490 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003491 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003492 return -EINVAL;
3493 }
3494 } else {
3495 return -EINVAL;
3496 }
3497
3498 return 0;
3499}
3500
3501static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3502 struct net_device *ndev)
3503{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003504 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003505
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003506 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003507 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003508 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003509 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003510 return 0;
3511}
Arend van Spriele5806072012-09-19 22:21:08 +02003512
Hante Meuleman1f170112013-02-06 18:40:38 +01003513static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003514{
3515 s32 err;
3516
3517 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003518 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003519 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003520 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003521 return err;
3522 }
3523 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003524 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003525 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003526 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003527 return err;
3528 }
3529 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003530 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003531 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003532 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003533 return err;
3534 }
3535
3536 return 0;
3537}
3538
3539static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3540{
3541 if (is_rsn_ie)
3542 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3543
3544 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3545}
3546
3547static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003548brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003549 const struct brcmf_vs_tlv *wpa_ie,
3550 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003551{
3552 u32 auth = 0; /* d11 open authentication */
3553 u16 count;
3554 s32 err = 0;
3555 s32 len = 0;
3556 u32 i;
3557 u32 wsec;
3558 u32 pval = 0;
3559 u32 gval = 0;
3560 u32 wpa_auth = 0;
3561 u32 offset;
3562 u8 *data;
3563 u16 rsn_cap;
3564 u32 wme_bss_disable;
3565
Arend van Sprield96b8012012-12-05 15:26:02 +01003566 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003567 if (wpa_ie == NULL)
3568 goto exit;
3569
3570 len = wpa_ie->len + TLV_HDR_LEN;
3571 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003572 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003573 if (!is_rsn_ie)
3574 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003575 else
3576 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003577
3578 /* check for multicast cipher suite */
3579 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3580 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003581 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003582 goto exit;
3583 }
3584
3585 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3586 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003587 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003588 goto exit;
3589 }
3590 offset += TLV_OUI_LEN;
3591
3592 /* pick up multicast cipher */
3593 switch (data[offset]) {
3594 case WPA_CIPHER_NONE:
3595 gval = 0;
3596 break;
3597 case WPA_CIPHER_WEP_40:
3598 case WPA_CIPHER_WEP_104:
3599 gval = WEP_ENABLED;
3600 break;
3601 case WPA_CIPHER_TKIP:
3602 gval = TKIP_ENABLED;
3603 break;
3604 case WPA_CIPHER_AES_CCM:
3605 gval = AES_ENABLED;
3606 break;
3607 default:
3608 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003609 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003610 goto exit;
3611 }
3612
3613 offset++;
3614 /* walk thru unicast cipher list and pick up what we recognize */
3615 count = data[offset] + (data[offset + 1] << 8);
3616 offset += WPA_IE_SUITE_COUNT_LEN;
3617 /* Check for unicast suite(s) */
3618 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3619 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003620 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003621 goto exit;
3622 }
3623 for (i = 0; i < count; i++) {
3624 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3625 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003626 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003627 goto exit;
3628 }
3629 offset += TLV_OUI_LEN;
3630 switch (data[offset]) {
3631 case WPA_CIPHER_NONE:
3632 break;
3633 case WPA_CIPHER_WEP_40:
3634 case WPA_CIPHER_WEP_104:
3635 pval |= WEP_ENABLED;
3636 break;
3637 case WPA_CIPHER_TKIP:
3638 pval |= TKIP_ENABLED;
3639 break;
3640 case WPA_CIPHER_AES_CCM:
3641 pval |= AES_ENABLED;
3642 break;
3643 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003644 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003645 }
3646 offset++;
3647 }
3648 /* walk thru auth management suite list and pick up what we recognize */
3649 count = data[offset] + (data[offset + 1] << 8);
3650 offset += WPA_IE_SUITE_COUNT_LEN;
3651 /* Check for auth key management suite(s) */
3652 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3653 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003654 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003655 goto exit;
3656 }
3657 for (i = 0; i < count; i++) {
3658 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3659 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003660 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003661 goto exit;
3662 }
3663 offset += TLV_OUI_LEN;
3664 switch (data[offset]) {
3665 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003666 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003667 wpa_auth |= WPA_AUTH_NONE;
3668 break;
3669 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003670 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003671 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3672 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3673 break;
3674 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003675 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003676 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3677 (wpa_auth |= WPA_AUTH_PSK);
3678 break;
3679 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003680 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003681 }
3682 offset++;
3683 }
3684
3685 if (is_rsn_ie) {
3686 wme_bss_disable = 1;
3687 if ((offset + RSN_CAP_LEN) <= len) {
3688 rsn_cap = data[offset] + (data[offset + 1] << 8);
3689 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3690 wme_bss_disable = 0;
3691 }
3692 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003693 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003694 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003695 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003696 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003697 goto exit;
3698 }
3699 }
3700 /* FOR WPS , set SES_OW_ENABLED */
3701 wsec = (pval | gval | SES_OW_ENABLED);
3702
3703 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003704 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003705 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003706 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003707 goto exit;
3708 }
3709 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003710 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003711 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003712 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003713 goto exit;
3714 }
3715 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003716 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003717 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003718 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003719 goto exit;
3720 }
3721
3722exit:
3723 return err;
3724}
3725
3726static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003727brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003728 struct parsed_vndr_ies *vndr_ies)
3729{
Hante Meuleman1a873342012-09-27 14:17:54 +02003730 struct brcmf_vs_tlv *vndrie;
3731 struct brcmf_tlv *ie;
3732 struct parsed_vndr_ie_info *parsed_info;
3733 s32 remaining_len;
3734
3735 remaining_len = (s32)vndr_ie_len;
3736 memset(vndr_ies, 0, sizeof(*vndr_ies));
3737
3738 ie = (struct brcmf_tlv *)vndr_ie_buf;
3739 while (ie) {
3740 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3741 goto next;
3742 vndrie = (struct brcmf_vs_tlv *)ie;
3743 /* len should be bigger than OUI length + one */
3744 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003745 brcmf_err("invalid vndr ie. length is too small %d\n",
3746 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003747 goto next;
3748 }
3749 /* if wpa or wme ie, do not add ie */
3750 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3751 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3752 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003753 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003754 goto next;
3755 }
3756
3757 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3758
3759 /* save vndr ie information */
3760 parsed_info->ie_ptr = (char *)vndrie;
3761 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3762 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3763
3764 vndr_ies->count++;
3765
Arend van Sprield96b8012012-12-05 15:26:02 +01003766 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3767 parsed_info->vndrie.oui[0],
3768 parsed_info->vndrie.oui[1],
3769 parsed_info->vndrie.oui[2],
3770 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003771
Arend van Spriel9f440b72013-02-08 15:53:36 +01003772 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003773 break;
3774next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003775 remaining_len -= (ie->len + TLV_HDR_LEN);
3776 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003777 ie = NULL;
3778 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003779 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3780 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003781 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003782 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003783}
3784
3785static u32
3786brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3787{
3788
Hante Meuleman1a873342012-09-27 14:17:54 +02003789 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3790 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3791
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303792 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003793
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303794 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003795
3796 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3797
3798 return ie_len + VNDR_IE_HDR_SIZE;
3799}
3800
Arend van Spriel1332e262012-11-05 16:22:18 -08003801s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3802 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003803{
Arend van Spriel1332e262012-11-05 16:22:18 -08003804 struct brcmf_if *ifp;
3805 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003806 s32 err = 0;
3807 u8 *iovar_ie_buf;
3808 u8 *curr_ie_buf;
3809 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003810 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003811 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003812 u32 del_add_ie_buf_len = 0;
3813 u32 total_ie_buf_len = 0;
3814 u32 parsed_ie_buf_len = 0;
3815 struct parsed_vndr_ies old_vndr_ies;
3816 struct parsed_vndr_ies new_vndr_ies;
3817 struct parsed_vndr_ie_info *vndrie_info;
3818 s32 i;
3819 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003820 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003821
Arend van Spriel1332e262012-11-05 16:22:18 -08003822 if (!vif)
3823 return -ENODEV;
3824 ifp = vif->ifp;
3825 saved_ie = &vif->saved_ie;
3826
Arend van Sprield96b8012012-12-05 15:26:02 +01003827 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003828 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3829 if (!iovar_ie_buf)
3830 return -ENOMEM;
3831 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003832 switch (pktflag) {
3833 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3834 mgmt_ie_buf = saved_ie->probe_req_ie;
3835 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3836 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3837 break;
3838 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3839 mgmt_ie_buf = saved_ie->probe_res_ie;
3840 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3841 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3842 break;
3843 case BRCMF_VNDR_IE_BEACON_FLAG:
3844 mgmt_ie_buf = saved_ie->beacon_ie;
3845 mgmt_ie_len = &saved_ie->beacon_ie_len;
3846 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3847 break;
3848 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3849 mgmt_ie_buf = saved_ie->assoc_req_ie;
3850 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3851 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3852 break;
3853 default:
3854 err = -EPERM;
3855 brcmf_err("not suitable type\n");
3856 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003857 }
3858
3859 if (vndr_ie_len > mgmt_ie_buf_len) {
3860 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003861 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003862 goto exit;
3863 }
3864
3865 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3866 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3867 ptr = curr_ie_buf;
3868 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3869 for (i = 0; i < new_vndr_ies.count; i++) {
3870 vndrie_info = &new_vndr_ies.ie_info[i];
3871 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3872 vndrie_info->ie_len);
3873 parsed_ie_buf_len += vndrie_info->ie_len;
3874 }
3875 }
3876
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003877 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003878 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3879 (memcmp(mgmt_ie_buf, curr_ie_buf,
3880 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003881 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003882 goto exit;
3883 }
3884
3885 /* parse old vndr_ie */
3886 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3887
3888 /* make a command to delete old ie */
3889 for (i = 0; i < old_vndr_ies.count; i++) {
3890 vndrie_info = &old_vndr_ies.ie_info[i];
3891
Arend van Sprield96b8012012-12-05 15:26:02 +01003892 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3893 vndrie_info->vndrie.id,
3894 vndrie_info->vndrie.len,
3895 vndrie_info->vndrie.oui[0],
3896 vndrie_info->vndrie.oui[1],
3897 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003898
3899 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3900 vndrie_info->ie_ptr,
3901 vndrie_info->ie_len,
3902 "del");
3903 curr_ie_buf += del_add_ie_buf_len;
3904 total_ie_buf_len += del_add_ie_buf_len;
3905 }
3906 }
3907
3908 *mgmt_ie_len = 0;
3909 /* Add if there is any extra IE */
3910 if (mgmt_ie_buf && parsed_ie_buf_len) {
3911 ptr = mgmt_ie_buf;
3912
3913 remained_buf_len = mgmt_ie_buf_len;
3914
3915 /* make a command to add new ie */
3916 for (i = 0; i < new_vndr_ies.count; i++) {
3917 vndrie_info = &new_vndr_ies.ie_info[i];
3918
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003919 /* verify remained buf size before copy data */
3920 if (remained_buf_len < (vndrie_info->vndrie.len +
3921 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003922 brcmf_err("no space in mgmt_ie_buf: len left %d",
3923 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003924 break;
3925 }
3926 remained_buf_len -= (vndrie_info->ie_len +
3927 VNDR_IE_VSIE_OFFSET);
3928
Arend van Sprield96b8012012-12-05 15:26:02 +01003929 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3930 vndrie_info->vndrie.id,
3931 vndrie_info->vndrie.len,
3932 vndrie_info->vndrie.oui[0],
3933 vndrie_info->vndrie.oui[1],
3934 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003935
3936 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3937 vndrie_info->ie_ptr,
3938 vndrie_info->ie_len,
3939 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003940
3941 /* save the parsed IE in wl struct */
3942 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3943 vndrie_info->ie_len);
3944 *mgmt_ie_len += vndrie_info->ie_len;
3945
3946 curr_ie_buf += del_add_ie_buf_len;
3947 total_ie_buf_len += del_add_ie_buf_len;
3948 }
3949 }
3950 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003951 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003952 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003953 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003954 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003955 }
3956
3957exit:
3958 kfree(iovar_ie_buf);
3959 return err;
3960}
3961
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01003962s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
3963{
3964 s32 pktflags[] = {
3965 BRCMF_VNDR_IE_PRBREQ_FLAG,
3966 BRCMF_VNDR_IE_PRBRSP_FLAG,
3967 BRCMF_VNDR_IE_BEACON_FLAG
3968 };
3969 int i;
3970
3971 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
3972 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
3973
3974 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
3975 return 0;
3976}
3977
Hante Meuleman1a873342012-09-27 14:17:54 +02003978static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01003979brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
3980 struct cfg80211_beacon_data *beacon)
3981{
3982 s32 err;
3983
3984 /* Set Beacon IEs to FW */
3985 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
3986 beacon->tail, beacon->tail_len);
3987 if (err) {
3988 brcmf_err("Set Beacon IE Failed\n");
3989 return err;
3990 }
3991 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
3992
3993 /* Set Probe Response IEs to FW */
3994 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
3995 beacon->proberesp_ies,
3996 beacon->proberesp_ies_len);
3997 if (err)
3998 brcmf_err("Set Probe Resp IE Failed\n");
3999 else
4000 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4001
4002 return err;
4003}
4004
4005static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004006brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4007 struct cfg80211_ap_settings *settings)
4008{
4009 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004010 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004011 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004012 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004013 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004014 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004015 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004016 const struct brcmf_tlv *rsn_ie;
4017 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004018 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004019 enum nl80211_iftype dev_role;
4020 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004021 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004022 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004023 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004024
Arend van Spriel06c01582014-05-12 10:47:37 +02004025 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4026 settings->chandef.chan->hw_value,
4027 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004028 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004029 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4030 settings->ssid, settings->ssid_len, settings->auth_type,
4031 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004032 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004033 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004034
Arend van Spriel98027762014-12-21 12:43:53 +01004035 /* store current 11d setting */
4036 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4037 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4038 settings->beacon.tail_len,
4039 WLAN_EID_COUNTRY);
4040 is_11d = country_ie ? 1 : 0;
4041
Hante Meuleman1a873342012-09-27 14:17:54 +02004042 memset(&ssid_le, 0, sizeof(ssid_le));
4043 if (settings->ssid == NULL || settings->ssid_len == 0) {
4044 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4045 ssid_ie = brcmf_parse_tlvs(
4046 (u8 *)&settings->beacon.head[ie_offset],
4047 settings->beacon.head_len - ie_offset,
4048 WLAN_EID_SSID);
4049 if (!ssid_ie)
4050 return -EINVAL;
4051
4052 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4053 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004054 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004055 } else {
4056 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4057 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4058 }
4059
Hante Meulemana44aa402014-12-03 21:05:33 +01004060 if (!mbss) {
4061 brcmf_set_mpc(ifp, 0);
4062 brcmf_configure_arp_offload(ifp, false);
4063 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004064
4065 /* find the RSN_IE */
4066 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4067 settings->beacon.tail_len, WLAN_EID_RSN);
4068
4069 /* find the WPA_IE */
4070 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4071 settings->beacon.tail_len);
4072
Hante Meuleman1a873342012-09-27 14:17:54 +02004073 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004074 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004075 if (wpa_ie != NULL) {
4076 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004077 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004078 if (err < 0)
4079 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004080 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004081 struct brcmf_vs_tlv *tmp_ie;
4082
4083 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4084
Hante Meuleman1a873342012-09-27 14:17:54 +02004085 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004086 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004087 if (err < 0)
4088 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004089 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004090 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004091 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004092 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004093 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004094
Hante Meulemana0f07952013-02-08 15:53:47 +01004095 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004096
Hante Meulemana44aa402014-12-03 21:05:33 +01004097 if (!mbss) {
4098 chanspec = chandef_to_chanspec(&cfg->d11inf,
4099 &settings->chandef);
4100 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004101 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004102 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4103 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004104 goto exit;
4105 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004106
Arend van Spriel98027762014-12-21 12:43:53 +01004107 if (is_11d != ifp->vif->is_11d) {
4108 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4109 is_11d);
4110 if (err < 0) {
4111 brcmf_err("Regulatory Set Error, %d\n", err);
4112 goto exit;
4113 }
4114 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004115 if (settings->beacon_interval) {
4116 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4117 settings->beacon_interval);
4118 if (err < 0) {
4119 brcmf_err("Beacon Interval Set Error, %d\n",
4120 err);
4121 goto exit;
4122 }
4123 }
4124 if (settings->dtim_period) {
4125 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4126 settings->dtim_period);
4127 if (err < 0) {
4128 brcmf_err("DTIM Interval Set Error, %d\n", err);
4129 goto exit;
4130 }
4131 }
4132
4133 if (dev_role == NL80211_IFTYPE_AP) {
4134 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4135 if (err < 0) {
4136 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4137 goto exit;
4138 }
4139 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4140 }
4141
4142 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004143 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004144 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004145 goto exit;
4146 }
Arend van Spriel98027762014-12-21 12:43:53 +01004147 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4148 /* Multiple-BSS should use same 11d configuration */
4149 err = -EINVAL;
4150 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004151 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004152 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004153 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4154 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4155
Hante Meulemana0f07952013-02-08 15:53:47 +01004156 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4157 if (err < 0) {
4158 brcmf_err("setting AP mode failed %d\n", err);
4159 goto exit;
4160 }
4161 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4162 if (err < 0) {
4163 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4164 goto exit;
4165 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004166 /* On DOWN the firmware removes the WEP keys, reconfigure
4167 * them if they were set.
4168 */
4169 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004170
4171 memset(&join_params, 0, sizeof(join_params));
4172 /* join parameters starts with ssid */
4173 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4174 /* create softap */
4175 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4176 &join_params, sizeof(join_params));
4177 if (err < 0) {
4178 brcmf_err("SET SSID error (%d)\n", err);
4179 goto exit;
4180 }
4181 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4182 } else {
4183 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4184 sizeof(ssid_le));
4185 if (err < 0) {
4186 brcmf_err("setting ssid failed %d\n", err);
4187 goto exit;
4188 }
4189 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4190 bss_enable.enable = cpu_to_le32(1);
4191 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4192 sizeof(bss_enable));
4193 if (err < 0) {
4194 brcmf_err("bss_enable config failed %d\n", err);
4195 goto exit;
4196 }
4197
4198 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4199 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004200 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4201 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02004202
4203exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004204 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004205 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004206 brcmf_configure_arp_offload(ifp, true);
4207 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004208 return err;
4209}
4210
4211static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4212{
Arend van Sprielc1179032012-10-22 13:55:33 -07004213 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004214 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004215 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004216 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004217
Arend van Sprield96b8012012-12-05 15:26:02 +01004218 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004219
Hante Meuleman426d0a52013-02-08 15:53:53 +01004220 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004221 /* Due to most likely deauths outstanding we sleep */
4222 /* first to make sure they get processed by fw. */
4223 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004224
Hante Meulemana44aa402014-12-03 21:05:33 +01004225 if (ifp->vif->mbss) {
4226 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4227 return err;
4228 }
4229
Hante Meuleman5c33a942013-04-02 21:06:18 +02004230 memset(&join_params, 0, sizeof(join_params));
4231 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4232 &join_params, sizeof(join_params));
4233 if (err < 0)
4234 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004235 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004236 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004237 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004238 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4239 if (err < 0)
4240 brcmf_err("setting AP mode failed %d\n", err);
4241 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4242 if (err < 0)
4243 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004244 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4245 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004246 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4247 ifp->vif->is_11d);
4248 if (err < 0)
4249 brcmf_err("restoring REGULATORY setting failed %d\n",
4250 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004251 /* Bring device back up so it can be used again */
4252 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4253 if (err < 0)
4254 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004255 } else {
4256 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4257 bss_enable.enable = cpu_to_le32(0);
4258 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4259 sizeof(bss_enable));
4260 if (err < 0)
4261 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004262 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004263 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004264 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004265 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4266 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
4267
Hante Meuleman1a873342012-09-27 14:17:54 +02004268 return err;
4269}
4270
Hante Meulemana0f07952013-02-08 15:53:47 +01004271static s32
4272brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4273 struct cfg80211_beacon_data *info)
4274{
Hante Meulemana0f07952013-02-08 15:53:47 +01004275 struct brcmf_if *ifp = netdev_priv(ndev);
4276 s32 err;
4277
4278 brcmf_dbg(TRACE, "Enter\n");
4279
Hante Meulemana0f07952013-02-08 15:53:47 +01004280 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4281
4282 return err;
4283}
4284
Hante Meuleman1a873342012-09-27 14:17:54 +02004285static int
4286brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004287 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004288{
Hante Meulemana0f07952013-02-08 15:53:47 +01004289 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004290 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004291 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004292 s32 err;
4293
Jouni Malinen89c771e2014-10-10 20:52:40 +03004294 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004295 return -EFAULT;
4296
Jouni Malinen89c771e2014-10-10 20:52:40 +03004297 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004298
Hante Meulemana0f07952013-02-08 15:53:47 +01004299 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4300 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004301 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004302 return -EIO;
4303
Jouni Malinen89c771e2014-10-10 20:52:40 +03004304 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004305 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004306 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004307 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004308 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004309 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004310
Arend van Sprield96b8012012-12-05 15:26:02 +01004311 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004312 return err;
4313}
4314
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004315static int
4316brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4317 const u8 *mac, struct station_parameters *params)
4318{
4319 struct brcmf_if *ifp = netdev_priv(ndev);
4320 s32 err;
4321
4322 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4323 params->sta_flags_mask, params->sta_flags_set);
4324
4325 /* Ignore all 00 MAC */
4326 if (is_zero_ether_addr(mac))
4327 return 0;
4328
4329 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4330 return 0;
4331
4332 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4333 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4334 (void *)mac, ETH_ALEN);
4335 else
4336 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4337 (void *)mac, ETH_ALEN);
4338 if (err < 0)
4339 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4340
4341 return err;
4342}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004343
4344static void
4345brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4346 struct wireless_dev *wdev,
4347 u16 frame_type, bool reg)
4348{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004349 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004350 u16 mgmt_type;
4351
4352 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4353
4354 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004355 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004356 if (reg)
4357 vif->mgmt_rx_reg |= BIT(mgmt_type);
4358 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004359 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004360}
4361
4362
4363static int
4364brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004365 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004366{
4367 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004368 struct ieee80211_channel *chan = params->chan;
4369 const u8 *buf = params->buf;
4370 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004371 const struct ieee80211_mgmt *mgmt;
4372 struct brcmf_cfg80211_vif *vif;
4373 s32 err = 0;
4374 s32 ie_offset;
4375 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004376 struct brcmf_fil_action_frame_le *action_frame;
4377 struct brcmf_fil_af_params_le *af_params;
4378 bool ack;
4379 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004380 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004381
4382 brcmf_dbg(TRACE, "Enter\n");
4383
4384 *cookie = 0;
4385
4386 mgmt = (const struct ieee80211_mgmt *)buf;
4387
Hante Meulemana0f07952013-02-08 15:53:47 +01004388 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4389 brcmf_err("Driver only allows MGMT packet type\n");
4390 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004391 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004392
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004393 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4394
Hante Meulemana0f07952013-02-08 15:53:47 +01004395 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4396 /* Right now the only reason to get a probe response */
4397 /* is for p2p listen response or for p2p GO from */
4398 /* wpa_supplicant. Unfortunately the probe is send */
4399 /* on primary ndev, while dongle wants it on the p2p */
4400 /* vif. Since this is only reason for a probe */
4401 /* response to be sent, the vif is taken from cfg. */
4402 /* If ever desired to send proberesp for non p2p */
4403 /* response then data should be checked for */
4404 /* "DIRECT-". Note in future supplicant will take */
4405 /* dedicated p2p wdev to do this and then this 'hack'*/
4406 /* is not needed anymore. */
4407 ie_offset = DOT11_MGMT_HDR_LEN +
4408 DOT11_BCN_PRB_FIXED_LEN;
4409 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004410 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4411 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4412 err = brcmf_vif_set_mgmt_ie(vif,
4413 BRCMF_VNDR_IE_PRBRSP_FLAG,
4414 &buf[ie_offset],
4415 ie_len);
4416 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4417 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004418 } else if (ieee80211_is_action(mgmt->frame_control)) {
4419 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4420 if (af_params == NULL) {
4421 brcmf_err("unable to allocate frame\n");
4422 err = -ENOMEM;
4423 goto exit;
4424 }
4425 action_frame = &af_params->action_frame;
4426 /* Add the packet Id */
4427 action_frame->packet_id = cpu_to_le32(*cookie);
4428 /* Add BSSID */
4429 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4430 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4431 /* Add the length exepted for 802.11 header */
4432 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004433 /* Add the channel. Use the one specified as parameter if any or
4434 * the current one (got from the firmware) otherwise
4435 */
4436 if (chan)
4437 freq = chan->center_freq;
4438 else
4439 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4440 &freq);
4441 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004442 af_params->channel = cpu_to_le32(chan_nr);
4443
4444 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4445 le16_to_cpu(action_frame->len));
4446
4447 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004448 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004449
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004450 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004451 af_params);
4452
4453 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4454 GFP_KERNEL);
4455 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004456 } else {
4457 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4458 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4459 }
4460
Hante Meuleman18e2f612013-02-08 15:53:49 +01004461exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004462 return err;
4463}
4464
4465
4466static int
4467brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4468 struct wireless_dev *wdev,
4469 u64 cookie)
4470{
4471 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4472 struct brcmf_cfg80211_vif *vif;
4473 int err = 0;
4474
4475 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4476
4477 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4478 if (vif == NULL) {
4479 brcmf_err("No p2p device available for probe response\n");
4480 err = -ENODEV;
4481 goto exit;
4482 }
4483 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4484exit:
4485 return err;
4486}
4487
Piotr Haber61730d42013-04-23 12:53:12 +02004488static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4489 struct wireless_dev *wdev,
4490 enum nl80211_crit_proto_id proto,
4491 u16 duration)
4492{
4493 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4494 struct brcmf_cfg80211_vif *vif;
4495
4496 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4497
4498 /* only DHCP support for now */
4499 if (proto != NL80211_CRIT_PROTO_DHCP)
4500 return -EINVAL;
4501
4502 /* suppress and abort scanning */
4503 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4504 brcmf_abort_scanning(cfg);
4505
4506 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4507}
4508
4509static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4510 struct wireless_dev *wdev)
4511{
4512 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4513 struct brcmf_cfg80211_vif *vif;
4514
4515 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4516
4517 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4518 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4519}
4520
Hante Meuleman70b7d942014-07-30 13:20:07 +02004521static s32
4522brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4523 const struct brcmf_event_msg *e, void *data)
4524{
4525 switch (e->reason) {
4526 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4527 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4528 break;
4529 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4530 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4531 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4532 break;
4533 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4534 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4535 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4536 break;
4537 }
4538
4539 return 0;
4540}
4541
Arend van Spriel89c2f382013-08-10 12:27:25 +02004542static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4543{
4544 int ret;
4545
4546 switch (oper) {
4547 case NL80211_TDLS_DISCOVERY_REQ:
4548 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4549 break;
4550 case NL80211_TDLS_SETUP:
4551 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4552 break;
4553 case NL80211_TDLS_TEARDOWN:
4554 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4555 break;
4556 default:
4557 brcmf_err("unsupported operation: %d\n", oper);
4558 ret = -EOPNOTSUPP;
4559 }
4560 return ret;
4561}
4562
4563static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004564 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004565 enum nl80211_tdls_operation oper)
4566{
4567 struct brcmf_if *ifp;
4568 struct brcmf_tdls_iovar_le info;
4569 int ret = 0;
4570
4571 ret = brcmf_convert_nl80211_tdls_oper(oper);
4572 if (ret < 0)
4573 return ret;
4574
4575 ifp = netdev_priv(ndev);
4576 memset(&info, 0, sizeof(info));
4577 info.mode = (u8)ret;
4578 if (peer)
4579 memcpy(info.ea, peer, ETH_ALEN);
4580
4581 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4582 &info, sizeof(info));
4583 if (ret < 0)
4584 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4585
4586 return ret;
4587}
4588
Arend van Spriel5b435de2011-10-05 13:19:03 +02004589static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004590 .add_virtual_intf = brcmf_cfg80211_add_iface,
4591 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004592 .change_virtual_intf = brcmf_cfg80211_change_iface,
4593 .scan = brcmf_cfg80211_scan,
4594 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4595 .join_ibss = brcmf_cfg80211_join_ibss,
4596 .leave_ibss = brcmf_cfg80211_leave_ibss,
4597 .get_station = brcmf_cfg80211_get_station,
4598 .set_tx_power = brcmf_cfg80211_set_tx_power,
4599 .get_tx_power = brcmf_cfg80211_get_tx_power,
4600 .add_key = brcmf_cfg80211_add_key,
4601 .del_key = brcmf_cfg80211_del_key,
4602 .get_key = brcmf_cfg80211_get_key,
4603 .set_default_key = brcmf_cfg80211_config_default_key,
4604 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4605 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004606 .connect = brcmf_cfg80211_connect,
4607 .disconnect = brcmf_cfg80211_disconnect,
4608 .suspend = brcmf_cfg80211_suspend,
4609 .resume = brcmf_cfg80211_resume,
4610 .set_pmksa = brcmf_cfg80211_set_pmksa,
4611 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004612 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004613 .start_ap = brcmf_cfg80211_start_ap,
4614 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004615 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004616 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004617 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004618 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4619 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004620 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4621 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4622 .remain_on_channel = brcmf_p2p_remain_on_channel,
4623 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004624 .start_p2p_device = brcmf_p2p_start_device,
4625 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004626 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4627 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004628 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004629};
4630
Arend van Spriel3eacf862012-10-22 13:55:30 -07004631struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004632 enum nl80211_iftype type,
4633 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004634{
Hante Meulemana44aa402014-12-03 21:05:33 +01004635 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004636 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004637 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004638
Arend van Spriel33a6b152013-02-08 15:53:39 +01004639 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004640 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004641 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4642 if (!vif)
4643 return ERR_PTR(-ENOMEM);
4644
4645 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004646 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004647
Arend van Spriel3eacf862012-10-22 13:55:30 -07004648 vif->pm_block = pm_block;
4649 vif->roam_off = -1;
4650
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004651 brcmf_init_prof(&vif->profile);
4652
Hante Meulemana44aa402014-12-03 21:05:33 +01004653 if (type == NL80211_IFTYPE_AP) {
4654 mbss = false;
4655 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4656 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4657 mbss = true;
4658 break;
4659 }
4660 }
4661 vif->mbss = mbss;
4662 }
4663
Arend van Spriel3eacf862012-10-22 13:55:30 -07004664 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004665 return vif;
4666}
4667
Arend van Spriel427dec52014-01-06 12:40:47 +01004668void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004669{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004670 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004671 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004672}
4673
Arend van Spriel9df4d542014-01-06 12:40:49 +01004674void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4675{
4676 struct brcmf_cfg80211_vif *vif;
4677 struct brcmf_if *ifp;
4678
4679 ifp = netdev_priv(ndev);
4680 vif = ifp->vif;
4681
4682 brcmf_free_vif(vif);
4683 free_netdev(ndev);
4684}
4685
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004686static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004687{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004688 u32 event = e->event_code;
4689 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004690
4691 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004692 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004693 return true;
4694 }
4695
4696 return false;
4697}
4698
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004699static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004700{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004701 u32 event = e->event_code;
4702 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004703
Hante Meuleman68ca3952014-02-25 20:30:26 +01004704 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4705 (event == BRCMF_E_DISASSOC_IND) ||
4706 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004707 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004708 return true;
4709 }
4710 return false;
4711}
4712
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004713static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004714 const struct brcmf_event_msg *e)
4715{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004716 u32 event = e->event_code;
4717 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004718
4719 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004720 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4721 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004722 return true;
4723 }
4724
4725 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004726 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004727 return true;
4728 }
4729
4730 return false;
4731}
4732
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004733static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004734{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004735 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004736
4737 kfree(conn_info->req_ie);
4738 conn_info->req_ie = NULL;
4739 conn_info->req_ie_len = 0;
4740 kfree(conn_info->resp_ie);
4741 conn_info->resp_ie = NULL;
4742 conn_info->resp_ie_len = 0;
4743}
4744
Hante Meuleman89286dc2013-02-08 15:53:46 +01004745static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4746 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004747{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004748 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004749 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004750 u32 req_len;
4751 u32 resp_len;
4752 s32 err = 0;
4753
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004754 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004755
Arend van Sprielac24be62012-10-22 10:36:23 -07004756 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4757 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004758 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004759 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004760 return err;
4761 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004762 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004763 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004764 req_len = le32_to_cpu(assoc_info->req_len);
4765 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004766 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004767 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004768 cfg->extra_buf,
4769 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004770 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004771 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004772 return err;
4773 }
4774 conn_info->req_ie_len = req_len;
4775 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004776 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004777 GFP_KERNEL);
4778 } else {
4779 conn_info->req_ie_len = 0;
4780 conn_info->req_ie = NULL;
4781 }
4782 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004783 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004784 cfg->extra_buf,
4785 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004786 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004787 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004788 return err;
4789 }
4790 conn_info->resp_ie_len = resp_len;
4791 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004792 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004793 GFP_KERNEL);
4794 } else {
4795 conn_info->resp_ie_len = 0;
4796 conn_info->resp_ie = NULL;
4797 }
Arend van Spriel16886732012-12-05 15:26:04 +01004798 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4799 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004800
4801 return err;
4802}
4803
4804static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004805brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004806 struct net_device *ndev,
4807 const struct brcmf_event_msg *e)
4808{
Arend van Sprielc1179032012-10-22 13:55:33 -07004809 struct brcmf_if *ifp = netdev_priv(ndev);
4810 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004811 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4812 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004813 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004814 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004815 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004816 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004817 u32 freq;
4818 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004819 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004820
Arend van Sprield96b8012012-12-05 15:26:02 +01004821 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004822
Hante Meuleman89286dc2013-02-08 15:53:46 +01004823 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004824 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004825 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004826
Franky Lina180b832012-10-10 11:13:09 -07004827 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4828 if (buf == NULL) {
4829 err = -ENOMEM;
4830 goto done;
4831 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004832
Franky Lina180b832012-10-10 11:13:09 -07004833 /* data sent to dongle has to be little endian */
4834 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004835 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004836 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004837
4838 if (err)
4839 goto done;
4840
4841 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004842 ch.chspec = le16_to_cpu(bi->chanspec);
4843 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004844
Franky Lin83cf17a2013-04-11 13:28:50 +02004845 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004846 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4847 else
4848 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4849
Franky Lin83cf17a2013-04-11 13:28:50 +02004850 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004851 notify_channel = ieee80211_get_channel(wiphy, freq);
4852
Franky Lina180b832012-10-10 11:13:09 -07004853done:
4854 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004855 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004856 conn_info->req_ie, conn_info->req_ie_len,
4857 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004858 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004859
Arend van Sprielc1179032012-10-22 13:55:33 -07004860 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004861 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004862 return err;
4863}
4864
4865static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004866brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004867 struct net_device *ndev, const struct brcmf_event_msg *e,
4868 bool completed)
4869{
Arend van Sprielc1179032012-10-22 13:55:33 -07004870 struct brcmf_if *ifp = netdev_priv(ndev);
4871 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004872 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004873
Arend van Sprield96b8012012-12-05 15:26:02 +01004874 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004875
Arend van Sprielc1179032012-10-22 13:55:33 -07004876 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4877 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004878 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004879 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004880 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004881 brcmf_update_bss_info(cfg, ifp);
4882 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4883 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004884 }
4885 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004886 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004887 conn_info->req_ie,
4888 conn_info->req_ie_len,
4889 conn_info->resp_ie,
4890 conn_info->resp_ie_len,
4891 completed ? WLAN_STATUS_SUCCESS :
4892 WLAN_STATUS_AUTH_TIMEOUT,
4893 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004894 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4895 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004896 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004897 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004898 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004899}
4900
4901static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004902brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004903 struct net_device *ndev,
4904 const struct brcmf_event_msg *e, void *data)
4905{
Hante Meulemana44aa402014-12-03 21:05:33 +01004906 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01004907 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004908 u32 event = e->event_code;
4909 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004910 struct station_info sinfo;
4911
Arend van Spriel16886732012-12-05 15:26:04 +01004912 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004913 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4914 ndev != cfg_to_ndev(cfg)) {
4915 brcmf_dbg(CONN, "AP mode link down\n");
4916 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01004917 if (ifp->vif->mbss)
4918 brcmf_remove_interface(ifp->drvr, ifp->bssidx);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004919 return 0;
4920 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004921
Hante Meuleman1a873342012-09-27 14:17:54 +02004922 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004923 (reason == BRCMF_E_STATUS_SUCCESS)) {
4924 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004925 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004926 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004927 return -EINVAL;
4928 }
4929 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004930 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004931 generation++;
4932 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004933 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004934 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4935 (event == BRCMF_E_DEAUTH_IND) ||
4936 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004937 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004938 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004939 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004940}
4941
4942static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004943brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004944 const struct brcmf_event_msg *e, void *data)
4945{
Arend van Spriel19937322012-11-05 16:22:32 -08004946 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4947 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004948 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004949 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004950 s32 err = 0;
4951
Hante Meuleman8851cce2014-07-30 13:20:02 +02004952 if ((e->event_code == BRCMF_E_DEAUTH) ||
4953 (e->event_code == BRCMF_E_DEAUTH_IND) ||
4954 (e->event_code == BRCMF_E_DISASSOC_IND) ||
4955 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
4956 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4957 }
4958
Arend van Spriel967fe2c2014-03-15 17:18:21 +01004959 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004960 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004961 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004962 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004963 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004964 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004965 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004966 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004967 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004968 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4969 &ifp->vif->sme_state);
4970 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4971 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004972 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004973 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004974 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004975 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004976 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004977 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004978 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01004979 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004980 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004981 if (ndev != cfg_to_ndev(cfg))
4982 complete(&cfg->vif_disabled);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004983 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004984 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07004985 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4986 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004987 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004988 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004989 }
4990
4991 return err;
4992}
4993
4994static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004995brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004996 const struct brcmf_event_msg *e, void *data)
4997{
Arend van Spriel19937322012-11-05 16:22:32 -08004998 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004999 u32 event = e->event_code;
5000 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005001
5002 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005003 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005004 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005005 else
Arend van Spriel19937322012-11-05 16:22:32 -08005006 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005007 }
5008
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005009 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005010}
5011
5012static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005013brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005014 const struct brcmf_event_msg *e, void *data)
5015{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005016 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005017 enum nl80211_key_type key_type;
5018
5019 if (flags & BRCMF_EVENT_MSG_GROUP)
5020 key_type = NL80211_KEYTYPE_GROUP;
5021 else
5022 key_type = NL80211_KEYTYPE_PAIRWISE;
5023
Arend van Spriel19937322012-11-05 16:22:32 -08005024 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005025 NULL, GFP_KERNEL);
5026
5027 return 0;
5028}
5029
Arend van Sprield3c0b632013-02-08 15:53:37 +01005030static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5031 const struct brcmf_event_msg *e, void *data)
5032{
5033 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5034 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5035 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5036 struct brcmf_cfg80211_vif *vif;
5037
5038 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
5039 ifevent->action, ifevent->flags, ifevent->ifidx,
5040 ifevent->bssidx);
5041
Arend van Sprield3c0b632013-02-08 15:53:37 +01005042 mutex_lock(&event->vif_event_lock);
5043 event->action = ifevent->action;
5044 vif = event->vif;
5045
5046 switch (ifevent->action) {
5047 case BRCMF_E_IF_ADD:
5048 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005049 if (!cfg->vif_event.vif) {
5050 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005051 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005052 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005053
5054 ifp->vif = vif;
5055 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005056 if (ifp->ndev) {
5057 vif->wdev.netdev = ifp->ndev;
5058 ifp->ndev->ieee80211_ptr = &vif->wdev;
5059 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5060 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005061 mutex_unlock(&event->vif_event_lock);
5062 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005063 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005064
5065 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005066 mutex_unlock(&event->vif_event_lock);
5067 /* event may not be upon user request */
5068 if (brcmf_cfg80211_vif_event_armed(cfg))
5069 wake_up(&event->vif_wq);
5070 return 0;
5071
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005072 case BRCMF_E_IF_CHANGE:
5073 mutex_unlock(&event->vif_event_lock);
5074 wake_up(&event->vif_wq);
5075 return 0;
5076
Arend van Sprield3c0b632013-02-08 15:53:37 +01005077 default:
5078 mutex_unlock(&event->vif_event_lock);
5079 break;
5080 }
5081 return -EINVAL;
5082}
5083
Arend van Spriel5b435de2011-10-05 13:19:03 +02005084static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5085{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005086 conf->frag_threshold = (u32)-1;
5087 conf->rts_threshold = (u32)-1;
5088 conf->retry_short = (u32)-1;
5089 conf->retry_long = (u32)-1;
5090 conf->tx_power = -1;
5091}
5092
Arend van Spriel5c36b992012-11-14 18:46:05 -08005093static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005094{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005095 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5096 brcmf_notify_connect_status);
5097 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5098 brcmf_notify_connect_status);
5099 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5100 brcmf_notify_connect_status);
5101 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5102 brcmf_notify_connect_status);
5103 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5104 brcmf_notify_connect_status);
5105 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5106 brcmf_notify_connect_status);
5107 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5108 brcmf_notify_roaming_status);
5109 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5110 brcmf_notify_mic_status);
5111 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5112 brcmf_notify_connect_status);
5113 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5114 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005115 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5116 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005117 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005118 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005119 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5120 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005121 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5122 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005123 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5124 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005125 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5126 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005127}
5128
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005129static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005130{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005131 kfree(cfg->conf);
5132 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005133 kfree(cfg->escan_ioctl_buf);
5134 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005135 kfree(cfg->extra_buf);
5136 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005137 kfree(cfg->pmk_list);
5138 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005139}
5140
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005141static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005142{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005143 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5144 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005145 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005146 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5147 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005148 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005149 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5150 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005151 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005152 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5153 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005154 goto init_priv_mem_out;
5155
5156 return 0;
5157
5158init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005159 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005160
5161 return -ENOMEM;
5162}
5163
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005164static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005165{
5166 s32 err = 0;
5167
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005168 cfg->scan_request = NULL;
5169 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005170 cfg->active_scan = true; /* we do active scan per default */
5171 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005172 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005173 if (err)
5174 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005175 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005176 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005177 brcmf_init_escan(cfg);
5178 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005179 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005180 return err;
5181}
5182
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005183static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005184{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005185 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005186 brcmf_abort_scanning(cfg);
5187 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005188}
5189
Arend van Sprield3c0b632013-02-08 15:53:37 +01005190static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5191{
5192 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005193 mutex_init(&event->vif_event_lock);
5194}
5195
Arend van Spriel5b435de2011-10-05 13:19:03 +02005196static s32
Hante Meuleman68ca3952014-02-25 20:30:26 +01005197brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005198{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005199 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005200 __le32 roamtrigger[2];
5201 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005202
5203 /*
5204 * Setup timeout if Beacons are lost and roam is
5205 * off to report link down
5206 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005207 if (brcmf_roamoff) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005208 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005209 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005210 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005211 goto dongle_rom_out;
5212 }
5213 }
5214
5215 /*
5216 * Enable/Disable built-in roaming to allow supplicant
5217 * to take care of roaming
5218 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005219 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5220 brcmf_roamoff ? "Off" : "On");
5221 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005222 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005223 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005224 goto dongle_rom_out;
5225 }
5226
Arend van Sprielf588bc02011-10-12 20:51:22 +02005227 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5228 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005229 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005230 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005231 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005232 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005233 goto dongle_rom_out;
5234 }
5235
Arend van Sprielf588bc02011-10-12 20:51:22 +02005236 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5237 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005238 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005239 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005240 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005241 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005242 goto dongle_rom_out;
5243 }
5244
5245dongle_rom_out:
5246 return err;
5247}
5248
5249static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01005250brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005251 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005252{
5253 s32 err = 0;
5254
Arend van Sprielac24be62012-10-22 10:36:23 -07005255 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005256 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005257 if (err) {
5258 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005259 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005260 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005261 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005262 goto dongle_scantime_out;
5263 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005264 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005265 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005266 if (err) {
5267 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005268 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005269 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005270 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005271 goto dongle_scantime_out;
5272 }
5273
Arend van Sprielac24be62012-10-22 10:36:23 -07005274 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005275 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005276 if (err) {
5277 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005278 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005279 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005280 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005281 goto dongle_scantime_out;
5282 }
5283
5284dongle_scantime_out:
5285 return err;
5286}
5287
Arend van Sprielb48d8912014-07-12 08:49:41 +02005288static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5289 struct brcmu_chan *ch)
5290{
5291 u32 ht40_flag;
5292
5293 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5294 if (ch->sb == BRCMU_CHAN_SB_U) {
5295 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5296 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5297 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5298 } else {
5299 /* It should be one of
5300 * IEEE80211_CHAN_NO_HT40 or
5301 * IEEE80211_CHAN_NO_HT40PLUS
5302 */
5303 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5304 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5305 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5306 }
5307}
5308
5309static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5310 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005311{
5312 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005313 struct ieee80211_supported_band *band;
5314 struct ieee80211_channel *channel;
5315 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005316 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005317 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005318 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005319 u8 *pbuf;
5320 u32 i, j;
5321 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005322 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005323 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005324
5325 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5326
5327 if (pbuf == NULL)
5328 return -ENOMEM;
5329
5330 list = (struct brcmf_chanspec_list *)pbuf;
5331
5332 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5333 BRCMF_DCMD_MEDLEN);
5334 if (err) {
5335 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005336 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005337 }
5338
Arend van Sprielb48d8912014-07-12 08:49:41 +02005339 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005340 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5341 if (band)
5342 for (i = 0; i < band->n_channels; i++)
5343 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5344 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5345 if (band)
5346 for (i = 0; i < band->n_channels; i++)
5347 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005348
5349 total = le32_to_cpu(list->count);
5350 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005351 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5352 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005353
Franky Lin83cf17a2013-04-11 13:28:50 +02005354 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005355 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005356 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005357 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005358 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005359 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005360 continue;
5361 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005362 if (!band)
5363 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005364 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005365 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005366 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005367 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005368 ch.bw == BRCMU_CHAN_BW_80)
5369 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005370
5371 channel = band->channels;
5372 index = band->n_channels;
5373 for (j = 0; j < band->n_channels; j++) {
5374 if (channel[j].hw_value == ch.chnum) {
5375 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005376 break;
5377 }
5378 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005379 channel[index].center_freq =
5380 ieee80211_channel_to_frequency(ch.chnum, band->band);
5381 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005382
Arend van Sprielb48d8912014-07-12 08:49:41 +02005383 /* assuming the chanspecs order is HT20,
5384 * HT40 upper, HT40 lower, and VHT80.
5385 */
5386 if (ch.bw == BRCMU_CHAN_BW_80) {
5387 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5388 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5389 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5390 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005391 /* enable the channel and disable other bandwidths
5392 * for now as mentioned order assure they are enabled
5393 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005394 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005395 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5396 IEEE80211_CHAN_NO_80MHZ;
5397 ch.bw = BRCMU_CHAN_BW_20;
5398 cfg->d11inf.encchspec(&ch);
5399 chaninfo = ch.chspec;
5400 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5401 &chaninfo);
5402 if (!err) {
5403 if (chaninfo & WL_CHAN_RADAR)
5404 channel[index].flags |=
5405 (IEEE80211_CHAN_RADAR |
5406 IEEE80211_CHAN_NO_IR);
5407 if (chaninfo & WL_CHAN_PASSIVE)
5408 channel[index].flags |=
5409 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005410 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005411 }
5412 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005413
Arend van Sprielb48d8912014-07-12 08:49:41 +02005414fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005415 kfree(pbuf);
5416 return err;
5417}
5418
Arend van Sprielb48d8912014-07-12 08:49:41 +02005419static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005420{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005421 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5422 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005423 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005424 struct brcmf_chanspec_list *list;
5425 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005426 u32 val;
5427 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005428 struct brcmu_chan ch;
5429 u32 num_chan;
5430 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005431
5432 /* verify support for bw_cap command */
5433 val = WLC_BAND_5G;
5434 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5435
5436 if (!err) {
5437 /* only set 2G bandwidth using bw_cap command */
5438 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5439 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5440 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5441 sizeof(band_bwcap));
5442 } else {
5443 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5444 val = WLC_N_BW_40ALL;
5445 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5446 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005447
5448 if (!err) {
5449 /* update channel info in 2G band */
5450 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5451
5452 if (pbuf == NULL)
5453 return -ENOMEM;
5454
5455 ch.band = BRCMU_CHAN_BAND_2G;
5456 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005457 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005458 ch.chnum = 0;
5459 cfg->d11inf.encchspec(&ch);
5460
5461 /* pass encoded chanspec in query */
5462 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5463
5464 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5465 BRCMF_DCMD_MEDLEN);
5466 if (err) {
5467 brcmf_err("get chanspecs error (%d)\n", err);
5468 kfree(pbuf);
5469 return err;
5470 }
5471
5472 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5473 list = (struct brcmf_chanspec_list *)pbuf;
5474 num_chan = le32_to_cpu(list->count);
5475 for (i = 0; i < num_chan; i++) {
5476 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5477 cfg->d11inf.decchspec(&ch);
5478 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5479 continue;
5480 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5481 continue;
5482 for (j = 0; j < band->n_channels; j++) {
5483 if (band->channels[j].hw_value == ch.chnum)
5484 break;
5485 }
5486 if (WARN_ON(j == band->n_channels))
5487 continue;
5488
5489 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5490 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005491 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005492 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005493 return err;
5494}
5495
Arend van Spriel2375d972014-01-06 12:40:41 +01005496static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5497{
5498 u32 band, mimo_bwcap;
5499 int err;
5500
5501 band = WLC_BAND_2G;
5502 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5503 if (!err) {
5504 bw_cap[IEEE80211_BAND_2GHZ] = band;
5505 band = WLC_BAND_5G;
5506 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5507 if (!err) {
5508 bw_cap[IEEE80211_BAND_5GHZ] = band;
5509 return;
5510 }
5511 WARN_ON(1);
5512 return;
5513 }
5514 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5515 mimo_bwcap = 0;
5516 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5517 if (err)
5518 /* assume 20MHz if firmware does not give a clue */
5519 mimo_bwcap = WLC_N_BW_20ALL;
5520
5521 switch (mimo_bwcap) {
5522 case WLC_N_BW_40ALL:
5523 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5524 /* fall-thru */
5525 case WLC_N_BW_20IN2G_40IN5G:
5526 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5527 /* fall-thru */
5528 case WLC_N_BW_20ALL:
5529 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5530 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5531 break;
5532 default:
5533 brcmf_err("invalid mimo_bw_cap value\n");
5534 }
5535}
Hante Meulemand48200b2013-04-03 12:40:29 +02005536
Arend van Spriel18d6c532014-05-12 10:47:35 +02005537static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5538 u32 bw_cap[2], u32 nchain)
5539{
5540 band->ht_cap.ht_supported = true;
5541 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5542 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5543 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5544 }
5545 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5546 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5547 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5548 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5549 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5550 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5551}
5552
5553static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5554{
5555 u16 mcs_map;
5556 int i;
5557
5558 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5559 mcs_map = (mcs_map << 2) | supp;
5560
5561 return cpu_to_le16(mcs_map);
5562}
5563
5564static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
5565 u32 bw_cap[2], u32 nchain)
5566{
5567 __le16 mcs_map;
5568
5569 /* not allowed in 2.4G band */
5570 if (band->band == IEEE80211_BAND_2GHZ)
5571 return;
5572
5573 band->vht_cap.vht_supported = true;
5574 /* 80MHz is mandatory */
5575 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5576 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5577 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5578 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5579 }
5580 /* all support 256-QAM */
5581 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5582 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5583 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
5584}
5585
Arend van Sprielb48d8912014-07-12 08:49:41 +02005586static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005587{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005588 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005589 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005590 u32 nmode = 0;
5591 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005592 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005593 u32 rxchain;
5594 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005595 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005596 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005597 struct ieee80211_supported_band *band;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005598
Arend van Spriel18d6c532014-05-12 10:47:35 +02005599 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005600 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5601 if (err) {
5602 brcmf_err("nmode error (%d)\n", err);
5603 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005604 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005605 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005606 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5607 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5608 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005609
Daniel Kim4aca7a12014-02-25 20:30:36 +01005610 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5611 if (err) {
5612 brcmf_err("rxchain error (%d)\n", err);
5613 nchain = 1;
5614 } else {
5615 for (nchain = 0; rxchain; nchain++)
5616 rxchain = rxchain & (rxchain - 1);
5617 }
5618 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5619
Arend van Sprielb48d8912014-07-12 08:49:41 +02005620 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005621 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005622 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005623 return err;
5624 }
5625
Arend van Sprielb48d8912014-07-12 08:49:41 +02005626 wiphy = cfg_to_wiphy(cfg);
5627 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5628 band = wiphy->bands[i];
5629 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005630 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005631
Arend van Spriel18d6c532014-05-12 10:47:35 +02005632 if (nmode)
5633 brcmf_update_ht_cap(band, bw_cap, nchain);
5634 if (vhtmode)
5635 brcmf_update_vht_cap(band, bw_cap, nchain);
Hante Meulemand48200b2013-04-03 12:40:29 +02005636 }
5637
Arend van Sprielb48d8912014-07-12 08:49:41 +02005638 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005639}
5640
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005641static const struct ieee80211_txrx_stypes
5642brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5643 [NL80211_IFTYPE_STATION] = {
5644 .tx = 0xffff,
5645 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5646 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5647 },
5648 [NL80211_IFTYPE_P2P_CLIENT] = {
5649 .tx = 0xffff,
5650 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5651 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5652 },
5653 [NL80211_IFTYPE_P2P_GO] = {
5654 .tx = 0xffff,
5655 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5656 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5657 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5658 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5659 BIT(IEEE80211_STYPE_AUTH >> 4) |
5660 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5661 BIT(IEEE80211_STYPE_ACTION >> 4)
5662 },
5663 [NL80211_IFTYPE_P2P_DEVICE] = {
5664 .tx = 0xffff,
5665 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5666 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5667 }
5668};
5669
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005670static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5671{
5672 struct ieee80211_iface_combination *combo = NULL;
5673 struct ieee80211_iface_limit *limits = NULL;
5674 int i = 0, max_iface_cnt;
5675
5676 combo = kzalloc(sizeof(*combo), GFP_KERNEL);
5677 if (!combo)
5678 goto err;
5679
5680 limits = kzalloc(sizeof(*limits) * 4, GFP_KERNEL);
5681 if (!limits)
5682 goto err;
5683
5684 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5685 BIT(NL80211_IFTYPE_ADHOC) |
5686 BIT(NL80211_IFTYPE_AP);
5687
5688 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5689 combo->num_different_channels = 2;
5690 else
5691 combo->num_different_channels = 1;
5692
5693 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
5694 limits[i].max = 1;
5695 limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5696 limits[i].max = 4;
5697 limits[i++].types = BIT(NL80211_IFTYPE_AP);
5698 max_iface_cnt = 5;
5699 } else {
5700 limits[i].max = 2;
5701 limits[i++].types = BIT(NL80211_IFTYPE_STATION) |
5702 BIT(NL80211_IFTYPE_AP);
5703 max_iface_cnt = 2;
5704 }
5705
5706 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P)) {
5707 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5708 BIT(NL80211_IFTYPE_P2P_GO) |
5709 BIT(NL80211_IFTYPE_P2P_DEVICE);
5710 limits[i].max = 1;
5711 limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5712 BIT(NL80211_IFTYPE_P2P_GO);
5713 limits[i].max = 1;
5714 limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5715 max_iface_cnt += 2;
5716 }
5717 combo->max_interfaces = max_iface_cnt;
5718 combo->limits = limits;
5719 combo->n_limits = i;
5720
5721 wiphy->iface_combinations = combo;
5722 wiphy->n_iface_combinations = 1;
5723 return 0;
5724
5725err:
5726 kfree(limits);
5727 kfree(combo);
5728 return -ENOMEM;
5729}
5730
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005731static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
5732{
5733 /* scheduled scan settings */
5734 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
5735 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
5736 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5737 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
5738}
5739
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005740#ifdef CONFIG_PM
5741static const struct wiphy_wowlan_support brcmf_wowlan_support = {
5742 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01005743 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
5744 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
5745 .pattern_min_len = 1,
5746 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005747};
5748#endif
5749
5750static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
5751{
5752#ifdef CONFIG_PM
5753 /* wowl settings */
5754 wiphy->wowlan = &brcmf_wowlan_support;
5755#endif
5756}
5757
Arend van Sprielb48d8912014-07-12 08:49:41 +02005758static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005759{
Arend van Spriel58de92d2015-04-14 20:10:24 +02005760 struct ieee80211_supported_band *band;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005761 __le32 bandlist[3];
5762 u32 n_bands;
5763 int err, i;
5764
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005765 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
5766 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5767 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005768
5769 err = brcmf_setup_ifmodes(wiphy, ifp);
5770 if (err)
5771 return err;
5772
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005773 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
5774 wiphy->cipher_suites = __wl_cipher_suites;
5775 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
5776 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
5777 WIPHY_FLAG_OFFCHAN_TX |
5778 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
5779 WIPHY_FLAG_SUPPORTS_TDLS;
5780 if (!brcmf_roamoff)
5781 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
5782 wiphy->mgmt_stypes = brcmf_txrx_stypes;
5783 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02005784 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
5785 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005786
5787 /* vendor commands/events support */
5788 wiphy->vendor_commands = brcmf_vendor_cmds;
5789 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
5790
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005791 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
5792 brcmf_wiphy_wowl_params(wiphy);
5793
Arend van Spriel58de92d2015-04-14 20:10:24 +02005794 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
5795 sizeof(bandlist));
5796 if (err) {
5797 brcmf_err("could not obtain band info: err=%d\n", err);
5798 return err;
5799 }
5800 /* first entry in bandlist is number of bands */
5801 n_bands = le32_to_cpu(bandlist[0]);
5802 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
5803 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
5804 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
5805 GFP_KERNEL);
5806 if (!band)
5807 return -ENOMEM;
5808
5809 band->channels = kmemdup(&__wl_2ghz_channels,
5810 sizeof(__wl_2ghz_channels),
5811 GFP_KERNEL);
5812 if (!band->channels) {
5813 kfree(band);
5814 return -ENOMEM;
5815 }
5816
5817 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
5818 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
5819 }
5820 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
5821 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
5822 GFP_KERNEL);
5823 if (!band)
5824 return -ENOMEM;
5825
5826 band->channels = kmemdup(&__wl_5ghz_channels,
5827 sizeof(__wl_5ghz_channels),
5828 GFP_KERNEL);
5829 if (!band->channels) {
5830 kfree(band);
5831 return -ENOMEM;
5832 }
5833
5834 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
5835 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
5836 }
5837 }
5838 err = brcmf_setup_wiphybands(wiphy);
5839 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005840}
5841
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005842static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005843{
5844 struct net_device *ndev;
5845 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005846 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005847 s32 power_mode;
5848 s32 err = 0;
5849
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005850 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005851 return err;
5852
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005853 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005854 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01005855 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005856
Hante Meuleman40a23292013-01-02 15:22:51 +01005857 /* make sure RF is ready for work */
5858 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
5859
5860 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
5861 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005862
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005863 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01005864 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005865 if (err)
5866 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005867 brcmf_dbg(INFO, "power save set to %s\n",
5868 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005869
Hante Meuleman68ca3952014-02-25 20:30:26 +01005870 err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005871 if (err)
5872 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005873 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5874 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01005875 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005876 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005877
Hante Meulemanb3657452013-05-27 21:09:53 +02005878 brcmf_configure_arp_offload(ifp, true);
5879
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005880 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01005881default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005882
5883 return err;
5884
5885}
5886
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005887static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005888{
Arend van Sprielc1179032012-10-22 13:55:33 -07005889 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005890
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005891 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005892}
5893
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005894static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005895{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005896 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07005897
Arend van Spriel5b435de2011-10-05 13:19:03 +02005898 /*
5899 * While going down, if associated with AP disassociate
5900 * from AP to save power
5901 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005902 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005903 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005904
5905 /* Make sure WPA_Supplicant receives all the event
5906 generated due to DISASSOC call to the fw to keep
5907 the state fw and WPA_Supplicant state consistent
5908 */
5909 brcmf_delay(500);
5910 }
5911
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005912 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07005913 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005914
Arend van Spriel5b435de2011-10-05 13:19:03 +02005915 return 0;
5916}
5917
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005918s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005919{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005920 struct brcmf_if *ifp = netdev_priv(ndev);
5921 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005922 s32 err = 0;
5923
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005924 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005925 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005926 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005927
5928 return err;
5929}
5930
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005931s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005932{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005933 struct brcmf_if *ifp = netdev_priv(ndev);
5934 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005935 s32 err = 0;
5936
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005937 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005938 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005939 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005940
5941 return err;
5942}
5943
Arend van Spriela7965fb2013-04-11 17:08:37 +02005944enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
5945{
5946 struct wireless_dev *wdev = &ifp->vif->wdev;
5947
5948 return wdev->iftype;
5949}
5950
Hante Meulemanbfe81972014-10-28 14:56:16 +01005951bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
5952 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01005953{
5954 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005955
5956 list_for_each_entry(vif, &cfg->vif_list, list) {
5957 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02005958 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005959 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02005960 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005961}
Arend van Sprield3c0b632013-02-08 15:53:37 +01005962
5963static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
5964 u8 action)
5965{
5966 u8 evt_action;
5967
5968 mutex_lock(&event->vif_event_lock);
5969 evt_action = event->action;
5970 mutex_unlock(&event->vif_event_lock);
5971 return evt_action == action;
5972}
5973
5974void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
5975 struct brcmf_cfg80211_vif *vif)
5976{
5977 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5978
5979 mutex_lock(&event->vif_event_lock);
5980 event->vif = vif;
5981 event->action = 0;
5982 mutex_unlock(&event->vif_event_lock);
5983}
5984
5985bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
5986{
5987 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5988 bool armed;
5989
5990 mutex_lock(&event->vif_event_lock);
5991 armed = event->vif != NULL;
5992 mutex_unlock(&event->vif_event_lock);
5993
5994 return armed;
5995}
5996int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
5997 u8 action, ulong timeout)
5998{
5999 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6000
6001 return wait_event_timeout(event->vif_wq,
6002 vif_event_equals(event, action), timeout);
6003}
6004
Arend van Spriel63db1a42014-12-21 12:43:51 +01006005static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6006 struct regulatory_request *req)
6007{
6008 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6009 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6010 struct brcmf_fil_country_le ccreq;
6011 int i;
6012
6013 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6014 req->alpha2[0], req->alpha2[1]);
6015
6016 /* ignore non-ISO3166 country codes */
6017 for (i = 0; i < sizeof(req->alpha2); i++)
6018 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6019 brcmf_err("not a ISO3166 code\n");
6020 return;
6021 }
6022 memset(&ccreq, 0, sizeof(ccreq));
6023 ccreq.rev = cpu_to_le32(-1);
6024 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006025 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6026 brcmf_err("firmware rejected country setting\n");
6027 return;
6028 }
6029 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006030}
6031
Arend van Sprielb48d8912014-07-12 08:49:41 +02006032static void brcmf_free_wiphy(struct wiphy *wiphy)
6033{
Arend van Spriel58de92d2015-04-14 20:10:24 +02006034 if (!wiphy)
6035 return;
6036
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006037 if (wiphy->iface_combinations)
6038 kfree(wiphy->iface_combinations->limits);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006039 kfree(wiphy->iface_combinations);
6040 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6041 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6042 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6043 }
6044 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6045 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6046 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6047 }
6048 wiphy_free(wiphy);
6049}
6050
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006051struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
6052 struct device *busdev)
6053{
6054 struct net_device *ndev = drvr->iflist[0]->ndev;
6055 struct brcmf_cfg80211_info *cfg;
6056 struct wiphy *wiphy;
6057 struct brcmf_cfg80211_vif *vif;
6058 struct brcmf_if *ifp;
6059 s32 err = 0;
6060 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006061 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006062
6063 if (!ndev) {
6064 brcmf_err("ndev is invalid\n");
6065 return NULL;
6066 }
6067
6068 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006069 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6070 if (!wiphy) {
6071 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006072 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006073 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006074 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006075 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006076
6077 cfg = wiphy_priv(wiphy);
6078 cfg->wiphy = wiphy;
6079 cfg->pub = drvr;
6080 init_vif_event(&cfg->vif_event);
6081 INIT_LIST_HEAD(&cfg->vif_list);
6082
6083 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006084 if (IS_ERR(vif))
6085 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006086
6087 vif->ifp = ifp;
6088 vif->wdev.netdev = ndev;
6089 ndev->ieee80211_ptr = &vif->wdev;
6090 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6091
6092 err = wl_init_priv(cfg);
6093 if (err) {
6094 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006095 brcmf_free_vif(vif);
6096 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006097 }
6098 ifp->vif = vif;
6099
Arend van Sprielb48d8912014-07-12 08:49:41 +02006100 /* determine d11 io type before wiphy setup */
6101 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006102 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006103 brcmf_err("Failed to get D11 version (%d)\n", err);
6104 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006105 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006106 cfg->d11inf.io_type = (u8)io_type;
6107 brcmu_d11_attach(&cfg->d11inf);
6108
6109 err = brcmf_setup_wiphy(wiphy, ifp);
6110 if (err < 0)
6111 goto priv_out;
6112
6113 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006114 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006115 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6116 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6117
6118 /* firmware defaults to 40MHz disabled in 2G band. We signal
6119 * cfg80211 here that we do and have it decide we can enable
6120 * it. But first check if device does support 2G operation.
6121 */
6122 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6123 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6124 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6125 }
6126 err = wiphy_register(wiphy);
6127 if (err < 0) {
6128 brcmf_err("Could not register wiphy device (%d)\n", err);
6129 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006130 }
6131
6132 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6133 * setup 40MHz in 2GHz band and enable OBSS scanning.
6134 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006135 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6136 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006137 if (!err)
6138 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6139 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006140 else
6141 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006142 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006143
6144 err = brcmf_p2p_attach(cfg);
6145 if (err) {
6146 brcmf_err("P2P initilisation failed (%d)\n", err);
6147 goto wiphy_unreg_out;
6148 }
6149 err = brcmf_btcoex_attach(cfg);
6150 if (err) {
6151 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6152 brcmf_p2p_detach(&cfg->p2p);
6153 goto wiphy_unreg_out;
6154 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006155
6156 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6157 if (err) {
6158 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6159 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman70b7d942014-07-30 13:20:07 +02006160 } else {
6161 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6162 brcmf_notify_tdls_peer_event);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006163 }
6164
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006165 return cfg;
6166
Arend van Sprielb48d8912014-07-12 08:49:41 +02006167wiphy_unreg_out:
6168 wiphy_unregister(cfg->wiphy);
6169priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006170 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006171 brcmf_free_vif(vif);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006172wiphy_out:
6173 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006174 return NULL;
6175}
6176
6177void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6178{
6179 if (!cfg)
6180 return;
6181
6182 WARN_ON(!list_empty(&cfg->vif_list));
6183 wiphy_unregister(cfg->wiphy);
6184 brcmf_btcoex_detach(cfg);
Arend van Sprielc3da74b2014-07-12 08:49:42 +02006185 brcmf_p2p_detach(&cfg->p2p);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006186 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006187 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006188}