blob: ffe526070d6f19a99de1a777129a51692e508147 [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 Spriel1f0dc592015-06-11 00:12:19 +02002399static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2400{
2401 struct nl80211_sta_flag_update *sfu;
2402
2403 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2404 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2405 sfu = &si->sta_flags;
2406 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2407 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2408 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2409 BIT(NL80211_STA_FLAG_AUTHORIZED);
2410 if (fw_sta_flags & BRCMF_STA_WME)
2411 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2412 if (fw_sta_flags & BRCMF_STA_AUTHE)
2413 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2414 if (fw_sta_flags & BRCMF_STA_ASSOC)
2415 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2416 if (fw_sta_flags & BRCMF_STA_AUTHO)
2417 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2418}
2419
2420static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2421{
2422 struct {
2423 __le32 len;
2424 struct brcmf_bss_info_le bss_le;
2425 } *buf;
2426 u16 capability;
2427 int err;
2428
2429 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2430 if (!buf)
2431 return;
2432
2433 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2434 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2435 WL_BSS_INFO_MAX);
2436 if (err) {
2437 brcmf_err("Failed to get bss info (%d)\n", err);
2438 return;
2439 }
2440 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2441 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2442 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2443 capability = le16_to_cpu(buf->bss_le.capability);
2444 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2445 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2446 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2447 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2448 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2449 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2450}
2451
Arend van Spriel5b435de2011-10-05 13:19:03 +02002452static s32
2453brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002454 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002455{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002456 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002457 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002458 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002459 u32 sta_flags;
2460 u32 is_tdls_peer;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002461
Arend van Sprield96b8012012-12-05 15:26:02 +01002462 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002463 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002464 return -EIO;
2465
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002466 memset(&sta_info_le, 0, sizeof(sta_info_le));
2467 memcpy(&sta_info_le, mac, ETH_ALEN);
2468 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2469 &sta_info_le,
2470 sizeof(sta_info_le));
2471 is_tdls_peer = !err;
2472 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002473 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002474 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002475 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002476 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002477 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002478 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002479 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002480 }
2481 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2482 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2483 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2484 sta_flags = le32_to_cpu(sta_info_le.flags);
2485 brcmf_convert_sta_flags(sta_flags, sinfo);
2486 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2487 if (is_tdls_peer)
2488 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2489 else
2490 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2491 if (sta_flags & BRCMF_STA_ASSOC) {
2492 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2493 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2494 brcmf_fill_bss_param(ifp, sinfo);
2495 }
2496 if (sta_flags & BRCMF_STA_SCBSTATS) {
2497 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2498 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2499 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2500 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2501 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2502 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2503 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2504 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2505 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002506 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002507 sinfo->txrate.legacy = le32_to_cpu(sta_info_le.tx_rate);
2508 sinfo->txrate.legacy /= 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002509 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002510 if (sinfo->rx_packets) {
2511 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
2512 sinfo->rxrate.legacy = le32_to_cpu(sta_info_le.rx_rate);
2513 sinfo->rxrate.legacy /= 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002514 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002515 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2516 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2517 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2518 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2519 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2520 }
2521 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002522done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002523 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002524 return err;
2525}
2526
2527static s32
2528brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2529 bool enabled, s32 timeout)
2530{
2531 s32 pm;
2532 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002533 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002534 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002535
Arend van Sprield96b8012012-12-05 15:26:02 +01002536 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002537
2538 /*
2539 * Powersave enable/disable request is coming from the
2540 * cfg80211 even before the interface is up. In that
2541 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002542 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002543 * FW later while initializing the dongle
2544 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002545 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002546 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002547
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002548 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002549 goto done;
2550 }
2551
2552 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002553 /* Do not enable the power save after assoc if it is a p2p interface */
2554 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2555 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2556 pm = PM_OFF;
2557 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002558 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002559
Arend van Sprielc1179032012-10-22 13:55:33 -07002560 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002561 if (err) {
2562 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002563 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002564 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002565 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002566 }
2567done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002568 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002569 return err;
2570}
2571
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002572static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002573 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002574{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002575 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002576 struct ieee80211_channel *notify_channel;
2577 struct cfg80211_bss *bss;
2578 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002579 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002580 u16 channel;
2581 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002582 u16 notify_capability;
2583 u16 notify_interval;
2584 u8 *notify_ie;
2585 size_t notify_ielen;
2586 s32 notify_signal;
2587
2588 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002589 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002590 return 0;
2591 }
2592
Franky Lin83cf17a2013-04-11 13:28:50 +02002593 if (!bi->ctl_ch) {
2594 ch.chspec = le16_to_cpu(bi->chanspec);
2595 cfg->d11inf.decchspec(&ch);
2596 bi->ctl_ch = ch.chnum;
2597 }
2598 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002599
2600 if (channel <= CH_MAX_2G_CHANNEL)
2601 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2602 else
2603 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2604
2605 freq = ieee80211_channel_to_frequency(channel, band->band);
2606 notify_channel = ieee80211_get_channel(wiphy, freq);
2607
Arend van Spriel5b435de2011-10-05 13:19:03 +02002608 notify_capability = le16_to_cpu(bi->capability);
2609 notify_interval = le16_to_cpu(bi->beacon_period);
2610 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2611 notify_ielen = le32_to_cpu(bi->ie_length);
2612 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2613
Arend van Spriel16886732012-12-05 15:26:04 +01002614 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2615 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2616 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2617 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2618 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002619
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002620 bss = cfg80211_inform_bss(wiphy, notify_channel,
2621 CFG80211_BSS_FTYPE_UNKNOWN,
2622 (const u8 *)bi->BSSID,
2623 0, notify_capability,
2624 notify_interval, notify_ie,
2625 notify_ielen, notify_signal,
2626 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002627
Franky Line78946e2011-11-10 20:30:34 +01002628 if (!bss)
2629 return -ENOMEM;
2630
Johannes Berg5b112d32013-02-01 01:49:58 +01002631 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002632
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002633 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002634}
2635
Roland Vossen6f09be02011-10-18 14:03:02 +02002636static struct brcmf_bss_info_le *
2637next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2638{
2639 if (bss == NULL)
2640 return list->bss_info_le;
2641 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2642 le32_to_cpu(bss->length));
2643}
2644
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002645static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002646{
2647 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002648 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002649 s32 err = 0;
2650 int i;
2651
Hante Meulemanef8596e2014-09-30 10:23:13 +02002652 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002653 if (bss_list->count != 0 &&
2654 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002655 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2656 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002657 return -EOPNOTSUPP;
2658 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002659 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002660 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002661 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002662 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002663 if (err)
2664 break;
2665 }
2666 return err;
2667}
2668
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002669static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002670 struct net_device *ndev, const u8 *bssid)
2671{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002672 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002673 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002674 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002675 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002676 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002677 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002678 u8 *buf = NULL;
2679 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002680 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002681 u16 notify_capability;
2682 u16 notify_interval;
2683 u8 *notify_ie;
2684 size_t notify_ielen;
2685 s32 notify_signal;
2686
Arend van Sprield96b8012012-12-05 15:26:02 +01002687 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002688
2689 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2690 if (buf == NULL) {
2691 err = -ENOMEM;
2692 goto CleanUp;
2693 }
2694
2695 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2696
Arend van Sprielac24be62012-10-22 10:36:23 -07002697 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2698 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002699 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002700 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002701 goto CleanUp;
2702 }
2703
Roland Vossend34bf642011-10-18 14:03:01 +02002704 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002705
Franky Lin83cf17a2013-04-11 13:28:50 +02002706 ch.chspec = le16_to_cpu(bi->chanspec);
2707 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002708
Franky Lin83cf17a2013-04-11 13:28:50 +02002709 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002710 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2711 else
2712 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2713
Franky Lin83cf17a2013-04-11 13:28:50 +02002714 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002715 notify_channel = ieee80211_get_channel(wiphy, freq);
2716
Arend van Spriel5b435de2011-10-05 13:19:03 +02002717 notify_capability = le16_to_cpu(bi->capability);
2718 notify_interval = le16_to_cpu(bi->beacon_period);
2719 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2720 notify_ielen = le32_to_cpu(bi->ie_length);
2721 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2722
Franky Lin83cf17a2013-04-11 13:28:50 +02002723 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002724 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2725 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2726 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002727
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002728 bss = cfg80211_inform_bss(wiphy, notify_channel,
2729 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2730 notify_capability, notify_interval,
2731 notify_ie, notify_ielen, notify_signal,
2732 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002733
Franky Line78946e2011-11-10 20:30:34 +01002734 if (!bss) {
2735 err = -ENOMEM;
2736 goto CleanUp;
2737 }
2738
Johannes Berg5b112d32013-02-01 01:49:58 +01002739 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002740
Arend van Spriel5b435de2011-10-05 13:19:03 +02002741CleanUp:
2742
2743 kfree(buf);
2744
Arend van Sprield96b8012012-12-05 15:26:02 +01002745 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002746
2747 return err;
2748}
2749
Hante Meuleman89286dc2013-02-08 15:53:46 +01002750static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2751 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002752{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002753 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002754 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002755 struct brcmf_ssid *ssid;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002756 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002757 u16 beacon_interval;
2758 u8 dtim_period;
2759 size_t ie_len;
2760 u8 *ie;
2761 s32 err = 0;
2762
Arend van Sprield96b8012012-12-05 15:26:02 +01002763 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002764 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002765 return err;
2766
Arend van Spriel06bb1232012-09-27 14:17:56 +02002767 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002768
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002769 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002770 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002771 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002772 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002773 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002774 goto update_bss_info_out;
2775 }
2776
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002777 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2778 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002779 if (err)
2780 goto update_bss_info_out;
2781
2782 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2783 ie_len = le32_to_cpu(bi->ie_length);
2784 beacon_interval = le16_to_cpu(bi->beacon_period);
2785
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002786 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002787 if (tim)
2788 dtim_period = tim->data[1];
2789 else {
2790 /*
2791 * active scan was done so we could not get dtim
2792 * information out of probe response.
2793 * so we speficially query dtim information to dongle.
2794 */
2795 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002796 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002797 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002798 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002799 goto update_bss_info_out;
2800 }
2801 dtim_period = (u8)var;
2802 }
2803
Arend van Spriel5b435de2011-10-05 13:19:03 +02002804update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002805 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002806 return err;
2807}
2808
Hante Meuleman18e2f612013-02-08 15:53:49 +01002809void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002810{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002811 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002812
Arend van Sprielc1179032012-10-22 13:55:33 -07002813 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002814 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002815 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002816 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002817 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002818 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2819 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002820}
2821
Hante Meulemane756af52012-09-11 21:18:52 +02002822static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2823{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002824 struct brcmf_cfg80211_info *cfg =
2825 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002826 escan_timeout_work);
2827
Hante Meulemanef8596e2014-09-30 10:23:13 +02002828 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002829 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002830}
2831
2832static void brcmf_escan_timeout(unsigned long data)
2833{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002834 struct brcmf_cfg80211_info *cfg =
2835 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002836
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002837 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002838 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002839 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002840 }
2841}
2842
2843static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002844brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2845 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002846 struct brcmf_bss_info_le *bss_info_le)
2847{
Franky Lin83cf17a2013-04-11 13:28:50 +02002848 struct brcmu_chan ch_bss, ch_bss_info_le;
2849
2850 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2851 cfg->d11inf.decchspec(&ch_bss);
2852 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2853 cfg->d11inf.decchspec(&ch_bss_info_le);
2854
Hante Meulemane756af52012-09-11 21:18:52 +02002855 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002856 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002857 bss_info_le->SSID_len == bss->SSID_len &&
2858 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002859 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2860 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002861 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2862 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2863
Hante Meulemane756af52012-09-11 21:18:52 +02002864 /* preserve max RSSI if the measurements are
2865 * both on-channel or both off-channel
2866 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002867 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002868 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002869 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2870 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002871 /* preserve the on-channel rssi measurement
2872 * if the new measurement is off channel
2873 */
2874 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002875 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002876 }
2877 return 1;
2878 }
2879 return 0;
2880}
2881
2882static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002883brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002884 const struct brcmf_event_msg *e, void *data)
2885{
Arend van Spriel19937322012-11-05 16:22:32 -08002886 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002887 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002888 struct brcmf_escan_result_le *escan_result_le;
2889 struct brcmf_bss_info_le *bss_info_le;
2890 struct brcmf_bss_info_le *bss = NULL;
2891 u32 bi_length;
2892 struct brcmf_scan_results *list;
2893 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002894 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002895
Arend van Spriel5c36b992012-11-14 18:46:05 -08002896 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002897
Arend van Spriela0f472a2013-04-05 10:57:49 +02002898 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2899 brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002900 return -EPERM;
2901 }
2902
2903 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002904 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002905 escan_result_le = (struct brcmf_escan_result_le *) data;
2906 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002907 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002908 goto exit;
2909 }
Hante Meulemane756af52012-09-11 21:18:52 +02002910 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002911 brcmf_err("Invalid bss_count %d: ignoring\n",
2912 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002913 goto exit;
2914 }
2915 bss_info_le = &escan_result_le->bss_info_le;
2916
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002917 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2918 goto exit;
2919
2920 if (!cfg->scan_request) {
2921 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2922 goto exit;
2923 }
2924
Hante Meulemane756af52012-09-11 21:18:52 +02002925 bi_length = le32_to_cpu(bss_info_le->length);
2926 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2927 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002928 brcmf_err("Invalid bss_info length %d: ignoring\n",
2929 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002930 goto exit;
2931 }
2932
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002933 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002934 BIT(NL80211_IFTYPE_ADHOC))) {
2935 if (le16_to_cpu(bss_info_le->capability) &
2936 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002937 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002938 goto exit;
2939 }
2940 }
2941
2942 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002943 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002944 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002945 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002946 goto exit;
2947 }
2948
2949 for (i = 0; i < list->count; i++) {
2950 bss = bss ? (struct brcmf_bss_info_le *)
2951 ((unsigned char *)bss +
2952 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002953 if (brcmf_compare_update_same_bss(cfg, bss,
2954 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002955 goto exit;
2956 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002957 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002958 bss_info_le, bi_length);
2959 list->version = le32_to_cpu(bss_info_le->version);
2960 list->buflen += bi_length;
2961 list->count++;
2962 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002963 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002964 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2965 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002966 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002967 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002968 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02002969 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02002970 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002971 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2972 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002973 }
2974exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002975 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02002976}
2977
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002978static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002979{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002980 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
2981 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08002982 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2983 /* Init scan_timeout timer */
2984 init_timer(&cfg->escan_timeout);
2985 cfg->escan_timeout.data = (unsigned long) cfg;
2986 cfg->escan_timeout.function = brcmf_escan_timeout;
2987 INIT_WORK(&cfg->escan_timeout_work,
2988 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002989}
2990
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002991static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002992{
2993 if (ms < 1000 / HZ) {
2994 cond_resched();
2995 mdelay(ms);
2996 } else {
2997 msleep(ms);
2998 }
2999}
3000
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003001static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3002 u8 *pattern, u32 patternsize, u8 *mask,
3003 u32 packet_offset)
3004{
3005 struct brcmf_fil_wowl_pattern_le *filter;
3006 u32 masksize;
3007 u32 patternoffset;
3008 u8 *buf;
3009 u32 bufsize;
3010 s32 ret;
3011
3012 masksize = (patternsize + 7) / 8;
3013 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3014
3015 bufsize = sizeof(*filter) + patternsize + masksize;
3016 buf = kzalloc(bufsize, GFP_KERNEL);
3017 if (!buf)
3018 return -ENOMEM;
3019 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3020
3021 memcpy(filter->cmd, cmd, 4);
3022 filter->masksize = cpu_to_le32(masksize);
3023 filter->offset = cpu_to_le32(packet_offset);
3024 filter->patternoffset = cpu_to_le32(patternoffset);
3025 filter->patternsize = cpu_to_le32(patternsize);
3026 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3027
3028 if ((mask) && (masksize))
3029 memcpy(buf + sizeof(*filter), mask, masksize);
3030 if ((pattern) && (patternsize))
3031 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3032
3033 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3034
3035 kfree(buf);
3036 return ret;
3037}
3038
Arend van Spriel5b435de2011-10-05 13:19:03 +02003039static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3040{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003041 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3042 struct net_device *ndev = cfg_to_ndev(cfg);
3043 struct brcmf_if *ifp = netdev_priv(ndev);
3044
Arend van Sprield96b8012012-12-05 15:26:02 +01003045 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003046
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003047 if (cfg->wowl_enabled) {
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003048 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003049 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3050 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003051 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003052 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003053 cfg->wowl_enabled = false;
3054 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003055 return 0;
3056}
3057
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003058static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3059 struct brcmf_if *ifp,
3060 struct cfg80211_wowlan *wowl)
3061{
3062 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003063 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003064
3065 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3066
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003067 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003068 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3069 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3070
3071 wowl_config = 0;
3072 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003073 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003074 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003075 wowl_config |= BRCMF_WOWL_MAGIC;
3076 if ((wowl->patterns) && (wowl->n_patterns)) {
3077 wowl_config |= BRCMF_WOWL_NET;
3078 for (i = 0; i < wowl->n_patterns; i++) {
3079 brcmf_config_wowl_pattern(ifp, "add",
3080 (u8 *)wowl->patterns[i].pattern,
3081 wowl->patterns[i].pattern_len,
3082 (u8 *)wowl->patterns[i].mask,
3083 wowl->patterns[i].pkt_offset);
3084 }
3085 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003086 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3087 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3088 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3089 cfg->wowl_enabled = true;
3090}
3091
Arend van Spriel5b435de2011-10-05 13:19:03 +02003092static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003093 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003094{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003095 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3096 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003097 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003098 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003099
Arend van Sprield96b8012012-12-05 15:26:02 +01003100 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003101
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003102 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003103 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003104 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003105 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003106 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003107
Arend van Spriel7d641072012-10-22 13:55:39 -07003108 /* end any scanning */
3109 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003110 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003111
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003112 if (wowl == NULL) {
3113 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3114 list_for_each_entry(vif, &cfg->vif_list, list) {
3115 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3116 continue;
3117 /* While going to suspend if associated with AP
3118 * disassociate from AP to save power while system is
3119 * in suspended state
3120 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003121 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003122 /* Make sure WPA_Supplicant receives all the event
3123 * generated due to DISASSOC call to the fw to keep
3124 * the state fw and WPA_Supplicant state consistent
3125 */
3126 brcmf_delay(500);
3127 }
3128 /* Configure MPC */
3129 brcmf_set_mpc(ifp, 1);
3130
3131 } else {
3132 /* Configure WOWL paramaters */
3133 brcmf_configure_wowl(cfg, ifp, wowl);
3134 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003135
Arend van Spriel7d641072012-10-22 13:55:39 -07003136exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003137 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003138 /* clear any scanning activity */
3139 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003140 return 0;
3141}
3142
3143static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003144brcmf_update_pmklist(struct net_device *ndev,
3145 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3146{
3147 int i, j;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003148 u32 pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003149
Arend van Spriel40c8e952011-10-12 20:51:20 +02003150 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3151
Arend van Spriel16886732012-12-05 15:26:04 +01003152 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003153 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01003154 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
3155 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003156 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01003157 brcmf_dbg(CONN, "%02x\n",
3158 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003159 }
3160
3161 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07003162 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
3163 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003164
3165 return err;
3166}
3167
3168static s32
3169brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3170 struct cfg80211_pmksa *pmksa)
3171{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003172 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003173 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003174 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003175 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003176 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003177
Arend van Sprield96b8012012-12-05 15:26:02 +01003178 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003179 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003180 return -EIO;
3181
Arend van Spriel40c8e952011-10-12 20:51:20 +02003182 pmkid_len = le32_to_cpu(pmkids->npmkid);
3183 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003184 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3185 break;
3186 if (i < WL_NUM_PMKIDS_MAX) {
3187 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3188 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003189 if (i == pmkid_len) {
3190 pmkid_len++;
3191 pmkids->npmkid = cpu_to_le32(pmkid_len);
3192 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003193 } else
3194 err = -EINVAL;
3195
Arend van Spriel16886732012-12-05 15:26:04 +01003196 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
3197 pmkids->pmkid[pmkid_len].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", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003200
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003201 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003202
Arend van Sprield96b8012012-12-05 15:26:02 +01003203 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003204 return err;
3205}
3206
3207static s32
3208brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3209 struct cfg80211_pmksa *pmksa)
3210{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003211 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003212 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003213 struct pmkid_list pmkid;
3214 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003215 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003216
Arend van Sprield96b8012012-12-05 15:26:02 +01003217 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003218 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003219 return -EIO;
3220
3221 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3222 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3223
Arend van Spriel16886732012-12-05 15:26:04 +01003224 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3225 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003226 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003227 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003228
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003229 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003230 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003231 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003232 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003233 ETH_ALEN))
3234 break;
3235
Arend van Spriel40c8e952011-10-12 20:51:20 +02003236 if ((pmkid_len > 0)
3237 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003238 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003239 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003240 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003241 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3242 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003243 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003244 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3245 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003246 WLAN_PMKID_LEN);
3247 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003248 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003249 } else
3250 err = -EINVAL;
3251
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003252 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003253
Arend van Sprield96b8012012-12-05 15:26:02 +01003254 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003255 return err;
3256
3257}
3258
3259static s32
3260brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3261{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003262 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003263 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003264 s32 err = 0;
3265
Arend van Sprield96b8012012-12-05 15:26:02 +01003266 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003267 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003268 return -EIO;
3269
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003270 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3271 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003272
Arend van Sprield96b8012012-12-05 15:26:02 +01003273 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003274 return err;
3275
3276}
3277
Arend van Spriele5806072012-09-19 22:21:08 +02003278/*
3279 * PFN result doesn't have all the info which are
3280 * required by the supplicant
3281 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3282 * via wl_inform_single_bss in the required format. Escan does require the
3283 * scan request in the form of cfg80211_scan_request. For timebeing, create
3284 * cfg80211_scan_request one out of the received PNO event.
3285 */
3286static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003287brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003288 const struct brcmf_event_msg *e, void *data)
3289{
Arend van Spriel19937322012-11-05 16:22:32 -08003290 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003291 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3292 struct cfg80211_scan_request *request = NULL;
3293 struct cfg80211_ssid *ssid = NULL;
3294 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003295 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003296 int err = 0;
3297 int channel_req = 0;
3298 int band = 0;
3299 struct brcmf_pno_scanresults_le *pfn_result;
3300 u32 result_count;
3301 u32 status;
3302
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003303 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003304
Arend van Spriel5c36b992012-11-14 18:46:05 -08003305 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003306 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003307 return 0;
3308 }
3309
3310 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3311 result_count = le32_to_cpu(pfn_result->count);
3312 status = le32_to_cpu(pfn_result->status);
3313
3314 /*
3315 * PFN event is limited to fit 512 bytes so we may get
3316 * multiple NET_FOUND events. For now place a warning here.
3317 */
3318 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003319 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003320 if (result_count > 0) {
3321 int i;
3322
3323 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003324 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3325 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003326 if (!request || !ssid || !channel) {
3327 err = -ENOMEM;
3328 goto out_err;
3329 }
3330
3331 request->wiphy = wiphy;
3332 data += sizeof(struct brcmf_pno_scanresults_le);
3333 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3334
3335 for (i = 0; i < result_count; i++) {
3336 netinfo = &netinfo_start[i];
3337 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003338 brcmf_err("Invalid netinfo ptr. index: %d\n",
3339 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003340 err = -EINVAL;
3341 goto out_err;
3342 }
3343
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003344 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3345 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003346 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3347 ssid[i].ssid_len = netinfo->SSID_len;
3348 request->n_ssids++;
3349
3350 channel_req = netinfo->channel;
3351 if (channel_req <= CH_MAX_2G_CHANNEL)
3352 band = NL80211_BAND_2GHZ;
3353 else
3354 band = NL80211_BAND_5GHZ;
3355 channel[i].center_freq =
3356 ieee80211_channel_to_frequency(channel_req,
3357 band);
3358 channel[i].band = band;
3359 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3360 request->channels[i] = &channel[i];
3361 request->n_channels++;
3362 }
3363
3364 /* assign parsed ssid array */
3365 if (request->n_ssids)
3366 request->ssids = &ssid[0];
3367
Arend van Sprielc1179032012-10-22 13:55:33 -07003368 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003369 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003370 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003371 }
3372
Arend van Sprielc1179032012-10-22 13:55:33 -07003373 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003374 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003375 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003376 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003377 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003378 goto out_err;
3379 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003380 cfg->sched_escan = true;
3381 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003382 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003383 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003384 goto out_err;
3385 }
3386
3387 kfree(ssid);
3388 kfree(channel);
3389 kfree(request);
3390 return 0;
3391
3392out_err:
3393 kfree(ssid);
3394 kfree(channel);
3395 kfree(request);
3396 cfg80211_sched_scan_stopped(wiphy);
3397 return err;
3398}
3399
Arend van Spriele5806072012-09-19 22:21:08 +02003400static int brcmf_dev_pno_clean(struct net_device *ndev)
3401{
Arend van Spriele5806072012-09-19 22:21:08 +02003402 int ret;
3403
3404 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003405 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003406 if (ret == 0) {
3407 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003408 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3409 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003410 }
3411 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003412 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003413
3414 return ret;
3415}
3416
3417static int brcmf_dev_pno_config(struct net_device *ndev)
3418{
3419 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003420
3421 memset(&pfn_param, 0, sizeof(pfn_param));
3422 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3423
3424 /* set extra pno params */
3425 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3426 pfn_param.repeat = BRCMF_PNO_REPEAT;
3427 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3428
3429 /* set up pno scan fr */
3430 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3431
Arend van Sprielac24be62012-10-22 10:36:23 -07003432 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3433 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003434}
3435
3436static int
3437brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3438 struct net_device *ndev,
3439 struct cfg80211_sched_scan_request *request)
3440{
Arend van Sprielc1179032012-10-22 13:55:33 -07003441 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003442 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003443 struct brcmf_pno_net_param_le pfn;
3444 int i;
3445 int ret = 0;
3446
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003447 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003448 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003449 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003450 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003451 return -EAGAIN;
3452 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003453 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3454 brcmf_err("Scanning suppressed: status (%lu)\n",
3455 cfg->scan_status);
3456 return -EAGAIN;
3457 }
Arend van Spriele5806072012-09-19 22:21:08 +02003458
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003459 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003460 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003461 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003462 return -EINVAL;
3463 }
3464
3465 if (request->n_ssids > 0) {
3466 for (i = 0; i < request->n_ssids; i++) {
3467 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003468 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3469 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003470
3471 /*
3472 * match_set ssids is a supert set of n_ssid list,
3473 * so we need not add these set seperately.
3474 */
3475 }
3476 }
3477
3478 if (request->n_match_sets > 0) {
3479 /* clean up everything */
3480 ret = brcmf_dev_pno_clean(ndev);
3481 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003482 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003483 return ret;
3484 }
3485
3486 /* configure pno */
3487 ret = brcmf_dev_pno_config(ndev);
3488 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003489 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003490 return -EINVAL;
3491 }
3492
3493 /* configure each match set */
3494 for (i = 0; i < request->n_match_sets; i++) {
3495 struct cfg80211_ssid *ssid;
3496 u32 ssid_len;
3497
3498 ssid = &request->match_sets[i].ssid;
3499 ssid_len = ssid->ssid_len;
3500
3501 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003502 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003503 continue;
3504 }
3505 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3506 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3507 pfn.wsec = cpu_to_le32(0);
3508 pfn.infra = cpu_to_le32(1);
3509 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3510 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3511 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003512 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003513 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003514 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3515 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003516 }
3517 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003518 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003519 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003520 return -EINVAL;
3521 }
3522 } else {
3523 return -EINVAL;
3524 }
3525
3526 return 0;
3527}
3528
3529static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3530 struct net_device *ndev)
3531{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003532 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003533
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003534 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003535 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003536 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003537 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003538 return 0;
3539}
Arend van Spriele5806072012-09-19 22:21:08 +02003540
Hante Meuleman1f170112013-02-06 18:40:38 +01003541static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003542{
3543 s32 err;
3544
3545 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003546 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003547 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003548 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003549 return err;
3550 }
3551 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003552 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003553 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003554 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003555 return err;
3556 }
3557 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003558 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003559 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003560 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003561 return err;
3562 }
3563
3564 return 0;
3565}
3566
3567static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3568{
3569 if (is_rsn_ie)
3570 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3571
3572 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3573}
3574
3575static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003576brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003577 const struct brcmf_vs_tlv *wpa_ie,
3578 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003579{
3580 u32 auth = 0; /* d11 open authentication */
3581 u16 count;
3582 s32 err = 0;
3583 s32 len = 0;
3584 u32 i;
3585 u32 wsec;
3586 u32 pval = 0;
3587 u32 gval = 0;
3588 u32 wpa_auth = 0;
3589 u32 offset;
3590 u8 *data;
3591 u16 rsn_cap;
3592 u32 wme_bss_disable;
3593
Arend van Sprield96b8012012-12-05 15:26:02 +01003594 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003595 if (wpa_ie == NULL)
3596 goto exit;
3597
3598 len = wpa_ie->len + TLV_HDR_LEN;
3599 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003600 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003601 if (!is_rsn_ie)
3602 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003603 else
3604 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003605
3606 /* check for multicast cipher suite */
3607 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3608 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003609 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003610 goto exit;
3611 }
3612
3613 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3614 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003615 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003616 goto exit;
3617 }
3618 offset += TLV_OUI_LEN;
3619
3620 /* pick up multicast cipher */
3621 switch (data[offset]) {
3622 case WPA_CIPHER_NONE:
3623 gval = 0;
3624 break;
3625 case WPA_CIPHER_WEP_40:
3626 case WPA_CIPHER_WEP_104:
3627 gval = WEP_ENABLED;
3628 break;
3629 case WPA_CIPHER_TKIP:
3630 gval = TKIP_ENABLED;
3631 break;
3632 case WPA_CIPHER_AES_CCM:
3633 gval = AES_ENABLED;
3634 break;
3635 default:
3636 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003637 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003638 goto exit;
3639 }
3640
3641 offset++;
3642 /* walk thru unicast cipher list and pick up what we recognize */
3643 count = data[offset] + (data[offset + 1] << 8);
3644 offset += WPA_IE_SUITE_COUNT_LEN;
3645 /* Check for unicast suite(s) */
3646 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3647 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003648 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003649 goto exit;
3650 }
3651 for (i = 0; i < count; i++) {
3652 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3653 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003654 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003655 goto exit;
3656 }
3657 offset += TLV_OUI_LEN;
3658 switch (data[offset]) {
3659 case WPA_CIPHER_NONE:
3660 break;
3661 case WPA_CIPHER_WEP_40:
3662 case WPA_CIPHER_WEP_104:
3663 pval |= WEP_ENABLED;
3664 break;
3665 case WPA_CIPHER_TKIP:
3666 pval |= TKIP_ENABLED;
3667 break;
3668 case WPA_CIPHER_AES_CCM:
3669 pval |= AES_ENABLED;
3670 break;
3671 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003672 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003673 }
3674 offset++;
3675 }
3676 /* walk thru auth management suite list and pick up what we recognize */
3677 count = data[offset] + (data[offset + 1] << 8);
3678 offset += WPA_IE_SUITE_COUNT_LEN;
3679 /* Check for auth key management suite(s) */
3680 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3681 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003682 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003683 goto exit;
3684 }
3685 for (i = 0; i < count; i++) {
3686 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3687 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003688 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003689 goto exit;
3690 }
3691 offset += TLV_OUI_LEN;
3692 switch (data[offset]) {
3693 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003694 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003695 wpa_auth |= WPA_AUTH_NONE;
3696 break;
3697 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003698 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003699 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3700 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3701 break;
3702 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003703 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003704 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3705 (wpa_auth |= WPA_AUTH_PSK);
3706 break;
3707 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003708 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003709 }
3710 offset++;
3711 }
3712
3713 if (is_rsn_ie) {
3714 wme_bss_disable = 1;
3715 if ((offset + RSN_CAP_LEN) <= len) {
3716 rsn_cap = data[offset] + (data[offset + 1] << 8);
3717 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3718 wme_bss_disable = 0;
3719 }
3720 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003721 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003722 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003723 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003724 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003725 goto exit;
3726 }
3727 }
3728 /* FOR WPS , set SES_OW_ENABLED */
3729 wsec = (pval | gval | SES_OW_ENABLED);
3730
3731 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003732 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003733 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003734 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003735 goto exit;
3736 }
3737 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003738 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003739 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003740 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003741 goto exit;
3742 }
3743 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003744 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003745 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003746 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003747 goto exit;
3748 }
3749
3750exit:
3751 return err;
3752}
3753
3754static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003755brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003756 struct parsed_vndr_ies *vndr_ies)
3757{
Hante Meuleman1a873342012-09-27 14:17:54 +02003758 struct brcmf_vs_tlv *vndrie;
3759 struct brcmf_tlv *ie;
3760 struct parsed_vndr_ie_info *parsed_info;
3761 s32 remaining_len;
3762
3763 remaining_len = (s32)vndr_ie_len;
3764 memset(vndr_ies, 0, sizeof(*vndr_ies));
3765
3766 ie = (struct brcmf_tlv *)vndr_ie_buf;
3767 while (ie) {
3768 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3769 goto next;
3770 vndrie = (struct brcmf_vs_tlv *)ie;
3771 /* len should be bigger than OUI length + one */
3772 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003773 brcmf_err("invalid vndr ie. length is too small %d\n",
3774 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003775 goto next;
3776 }
3777 /* if wpa or wme ie, do not add ie */
3778 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3779 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3780 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003781 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003782 goto next;
3783 }
3784
3785 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3786
3787 /* save vndr ie information */
3788 parsed_info->ie_ptr = (char *)vndrie;
3789 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3790 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3791
3792 vndr_ies->count++;
3793
Arend van Sprield96b8012012-12-05 15:26:02 +01003794 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3795 parsed_info->vndrie.oui[0],
3796 parsed_info->vndrie.oui[1],
3797 parsed_info->vndrie.oui[2],
3798 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003799
Arend van Spriel9f440b72013-02-08 15:53:36 +01003800 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003801 break;
3802next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003803 remaining_len -= (ie->len + TLV_HDR_LEN);
3804 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003805 ie = NULL;
3806 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003807 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3808 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003809 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003810 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003811}
3812
3813static u32
3814brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3815{
3816
Hante Meuleman1a873342012-09-27 14:17:54 +02003817 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3818 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3819
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303820 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003821
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303822 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003823
3824 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3825
3826 return ie_len + VNDR_IE_HDR_SIZE;
3827}
3828
Arend van Spriel1332e262012-11-05 16:22:18 -08003829s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3830 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003831{
Arend van Spriel1332e262012-11-05 16:22:18 -08003832 struct brcmf_if *ifp;
3833 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003834 s32 err = 0;
3835 u8 *iovar_ie_buf;
3836 u8 *curr_ie_buf;
3837 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003838 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003839 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003840 u32 del_add_ie_buf_len = 0;
3841 u32 total_ie_buf_len = 0;
3842 u32 parsed_ie_buf_len = 0;
3843 struct parsed_vndr_ies old_vndr_ies;
3844 struct parsed_vndr_ies new_vndr_ies;
3845 struct parsed_vndr_ie_info *vndrie_info;
3846 s32 i;
3847 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003848 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003849
Arend van Spriel1332e262012-11-05 16:22:18 -08003850 if (!vif)
3851 return -ENODEV;
3852 ifp = vif->ifp;
3853 saved_ie = &vif->saved_ie;
3854
Arend van Sprield96b8012012-12-05 15:26:02 +01003855 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003856 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3857 if (!iovar_ie_buf)
3858 return -ENOMEM;
3859 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003860 switch (pktflag) {
3861 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3862 mgmt_ie_buf = saved_ie->probe_req_ie;
3863 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3864 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3865 break;
3866 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3867 mgmt_ie_buf = saved_ie->probe_res_ie;
3868 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3869 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3870 break;
3871 case BRCMF_VNDR_IE_BEACON_FLAG:
3872 mgmt_ie_buf = saved_ie->beacon_ie;
3873 mgmt_ie_len = &saved_ie->beacon_ie_len;
3874 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3875 break;
3876 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3877 mgmt_ie_buf = saved_ie->assoc_req_ie;
3878 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3879 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3880 break;
3881 default:
3882 err = -EPERM;
3883 brcmf_err("not suitable type\n");
3884 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003885 }
3886
3887 if (vndr_ie_len > mgmt_ie_buf_len) {
3888 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003889 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003890 goto exit;
3891 }
3892
3893 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3894 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3895 ptr = curr_ie_buf;
3896 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3897 for (i = 0; i < new_vndr_ies.count; i++) {
3898 vndrie_info = &new_vndr_ies.ie_info[i];
3899 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3900 vndrie_info->ie_len);
3901 parsed_ie_buf_len += vndrie_info->ie_len;
3902 }
3903 }
3904
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003905 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003906 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3907 (memcmp(mgmt_ie_buf, curr_ie_buf,
3908 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003909 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003910 goto exit;
3911 }
3912
3913 /* parse old vndr_ie */
3914 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3915
3916 /* make a command to delete old ie */
3917 for (i = 0; i < old_vndr_ies.count; i++) {
3918 vndrie_info = &old_vndr_ies.ie_info[i];
3919
Arend van Sprield96b8012012-12-05 15:26:02 +01003920 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3921 vndrie_info->vndrie.id,
3922 vndrie_info->vndrie.len,
3923 vndrie_info->vndrie.oui[0],
3924 vndrie_info->vndrie.oui[1],
3925 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003926
3927 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3928 vndrie_info->ie_ptr,
3929 vndrie_info->ie_len,
3930 "del");
3931 curr_ie_buf += del_add_ie_buf_len;
3932 total_ie_buf_len += del_add_ie_buf_len;
3933 }
3934 }
3935
3936 *mgmt_ie_len = 0;
3937 /* Add if there is any extra IE */
3938 if (mgmt_ie_buf && parsed_ie_buf_len) {
3939 ptr = mgmt_ie_buf;
3940
3941 remained_buf_len = mgmt_ie_buf_len;
3942
3943 /* make a command to add new ie */
3944 for (i = 0; i < new_vndr_ies.count; i++) {
3945 vndrie_info = &new_vndr_ies.ie_info[i];
3946
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003947 /* verify remained buf size before copy data */
3948 if (remained_buf_len < (vndrie_info->vndrie.len +
3949 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003950 brcmf_err("no space in mgmt_ie_buf: len left %d",
3951 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003952 break;
3953 }
3954 remained_buf_len -= (vndrie_info->ie_len +
3955 VNDR_IE_VSIE_OFFSET);
3956
Arend van Sprield96b8012012-12-05 15:26:02 +01003957 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3958 vndrie_info->vndrie.id,
3959 vndrie_info->vndrie.len,
3960 vndrie_info->vndrie.oui[0],
3961 vndrie_info->vndrie.oui[1],
3962 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003963
3964 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3965 vndrie_info->ie_ptr,
3966 vndrie_info->ie_len,
3967 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003968
3969 /* save the parsed IE in wl struct */
3970 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3971 vndrie_info->ie_len);
3972 *mgmt_ie_len += vndrie_info->ie_len;
3973
3974 curr_ie_buf += del_add_ie_buf_len;
3975 total_ie_buf_len += del_add_ie_buf_len;
3976 }
3977 }
3978 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003979 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003980 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003981 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003982 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003983 }
3984
3985exit:
3986 kfree(iovar_ie_buf);
3987 return err;
3988}
3989
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01003990s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
3991{
3992 s32 pktflags[] = {
3993 BRCMF_VNDR_IE_PRBREQ_FLAG,
3994 BRCMF_VNDR_IE_PRBRSP_FLAG,
3995 BRCMF_VNDR_IE_BEACON_FLAG
3996 };
3997 int i;
3998
3999 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4000 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4001
4002 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4003 return 0;
4004}
4005
Hante Meuleman1a873342012-09-27 14:17:54 +02004006static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004007brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4008 struct cfg80211_beacon_data *beacon)
4009{
4010 s32 err;
4011
4012 /* Set Beacon IEs to FW */
4013 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4014 beacon->tail, beacon->tail_len);
4015 if (err) {
4016 brcmf_err("Set Beacon IE Failed\n");
4017 return err;
4018 }
4019 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4020
4021 /* Set Probe Response IEs to FW */
4022 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4023 beacon->proberesp_ies,
4024 beacon->proberesp_ies_len);
4025 if (err)
4026 brcmf_err("Set Probe Resp IE Failed\n");
4027 else
4028 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4029
4030 return err;
4031}
4032
4033static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004034brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4035 struct cfg80211_ap_settings *settings)
4036{
4037 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004038 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004039 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004040 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004041 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004042 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004043 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004044 const struct brcmf_tlv *rsn_ie;
4045 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004046 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004047 enum nl80211_iftype dev_role;
4048 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004049 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004050 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004051 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004052
Arend van Spriel06c01582014-05-12 10:47:37 +02004053 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4054 settings->chandef.chan->hw_value,
4055 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004056 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004057 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4058 settings->ssid, settings->ssid_len, settings->auth_type,
4059 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004060 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004061 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004062
Arend van Spriel98027762014-12-21 12:43:53 +01004063 /* store current 11d setting */
4064 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4065 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4066 settings->beacon.tail_len,
4067 WLAN_EID_COUNTRY);
4068 is_11d = country_ie ? 1 : 0;
4069
Hante Meuleman1a873342012-09-27 14:17:54 +02004070 memset(&ssid_le, 0, sizeof(ssid_le));
4071 if (settings->ssid == NULL || settings->ssid_len == 0) {
4072 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4073 ssid_ie = brcmf_parse_tlvs(
4074 (u8 *)&settings->beacon.head[ie_offset],
4075 settings->beacon.head_len - ie_offset,
4076 WLAN_EID_SSID);
4077 if (!ssid_ie)
4078 return -EINVAL;
4079
4080 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4081 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004082 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004083 } else {
4084 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4085 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4086 }
4087
Hante Meulemana44aa402014-12-03 21:05:33 +01004088 if (!mbss) {
4089 brcmf_set_mpc(ifp, 0);
4090 brcmf_configure_arp_offload(ifp, false);
4091 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004092
4093 /* find the RSN_IE */
4094 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4095 settings->beacon.tail_len, WLAN_EID_RSN);
4096
4097 /* find the WPA_IE */
4098 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4099 settings->beacon.tail_len);
4100
Hante Meuleman1a873342012-09-27 14:17:54 +02004101 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004102 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004103 if (wpa_ie != NULL) {
4104 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004105 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004106 if (err < 0)
4107 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004108 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004109 struct brcmf_vs_tlv *tmp_ie;
4110
4111 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4112
Hante Meuleman1a873342012-09-27 14:17:54 +02004113 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004114 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004115 if (err < 0)
4116 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004117 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004118 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004119 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004120 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004121 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004122
Hante Meulemana0f07952013-02-08 15:53:47 +01004123 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004124
Hante Meulemana44aa402014-12-03 21:05:33 +01004125 if (!mbss) {
4126 chanspec = chandef_to_chanspec(&cfg->d11inf,
4127 &settings->chandef);
4128 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004129 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004130 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4131 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004132 goto exit;
4133 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004134
Arend van Spriel98027762014-12-21 12:43:53 +01004135 if (is_11d != ifp->vif->is_11d) {
4136 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4137 is_11d);
4138 if (err < 0) {
4139 brcmf_err("Regulatory Set Error, %d\n", err);
4140 goto exit;
4141 }
4142 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004143 if (settings->beacon_interval) {
4144 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4145 settings->beacon_interval);
4146 if (err < 0) {
4147 brcmf_err("Beacon Interval Set Error, %d\n",
4148 err);
4149 goto exit;
4150 }
4151 }
4152 if (settings->dtim_period) {
4153 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4154 settings->dtim_period);
4155 if (err < 0) {
4156 brcmf_err("DTIM Interval Set Error, %d\n", err);
4157 goto exit;
4158 }
4159 }
4160
4161 if (dev_role == NL80211_IFTYPE_AP) {
4162 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4163 if (err < 0) {
4164 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4165 goto exit;
4166 }
4167 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4168 }
4169
4170 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004171 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004172 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004173 goto exit;
4174 }
Arend van Spriel98027762014-12-21 12:43:53 +01004175 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4176 /* Multiple-BSS should use same 11d configuration */
4177 err = -EINVAL;
4178 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004179 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004180 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004181 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4182 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4183
Hante Meulemana0f07952013-02-08 15:53:47 +01004184 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4185 if (err < 0) {
4186 brcmf_err("setting AP mode failed %d\n", err);
4187 goto exit;
4188 }
4189 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4190 if (err < 0) {
4191 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4192 goto exit;
4193 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004194 /* On DOWN the firmware removes the WEP keys, reconfigure
4195 * them if they were set.
4196 */
4197 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004198
4199 memset(&join_params, 0, sizeof(join_params));
4200 /* join parameters starts with ssid */
4201 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4202 /* create softap */
4203 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4204 &join_params, sizeof(join_params));
4205 if (err < 0) {
4206 brcmf_err("SET SSID error (%d)\n", err);
4207 goto exit;
4208 }
4209 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4210 } else {
4211 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4212 sizeof(ssid_le));
4213 if (err < 0) {
4214 brcmf_err("setting ssid failed %d\n", err);
4215 goto exit;
4216 }
4217 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4218 bss_enable.enable = cpu_to_le32(1);
4219 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4220 sizeof(bss_enable));
4221 if (err < 0) {
4222 brcmf_err("bss_enable config failed %d\n", err);
4223 goto exit;
4224 }
4225
4226 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4227 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004228 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4229 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02004230
4231exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004232 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004233 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004234 brcmf_configure_arp_offload(ifp, true);
4235 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004236 return err;
4237}
4238
4239static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4240{
Arend van Sprielc1179032012-10-22 13:55:33 -07004241 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004242 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004243 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004244 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004245
Arend van Sprield96b8012012-12-05 15:26:02 +01004246 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004247
Hante Meuleman426d0a52013-02-08 15:53:53 +01004248 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004249 /* Due to most likely deauths outstanding we sleep */
4250 /* first to make sure they get processed by fw. */
4251 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004252
Hante Meulemana44aa402014-12-03 21:05:33 +01004253 if (ifp->vif->mbss) {
4254 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4255 return err;
4256 }
4257
Hante Meuleman5c33a942013-04-02 21:06:18 +02004258 memset(&join_params, 0, sizeof(join_params));
4259 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4260 &join_params, sizeof(join_params));
4261 if (err < 0)
4262 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004263 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004264 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004265 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004266 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4267 if (err < 0)
4268 brcmf_err("setting AP mode failed %d\n", err);
4269 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4270 if (err < 0)
4271 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004272 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4273 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004274 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4275 ifp->vif->is_11d);
4276 if (err < 0)
4277 brcmf_err("restoring REGULATORY setting failed %d\n",
4278 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004279 /* Bring device back up so it can be used again */
4280 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4281 if (err < 0)
4282 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004283 } else {
4284 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4285 bss_enable.enable = cpu_to_le32(0);
4286 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4287 sizeof(bss_enable));
4288 if (err < 0)
4289 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004290 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004291 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004292 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004293 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4294 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
4295
Hante Meuleman1a873342012-09-27 14:17:54 +02004296 return err;
4297}
4298
Hante Meulemana0f07952013-02-08 15:53:47 +01004299static s32
4300brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4301 struct cfg80211_beacon_data *info)
4302{
Hante Meulemana0f07952013-02-08 15:53:47 +01004303 struct brcmf_if *ifp = netdev_priv(ndev);
4304 s32 err;
4305
4306 brcmf_dbg(TRACE, "Enter\n");
4307
Hante Meulemana0f07952013-02-08 15:53:47 +01004308 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4309
4310 return err;
4311}
4312
Hante Meuleman1a873342012-09-27 14:17:54 +02004313static int
4314brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004315 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004316{
Hante Meulemana0f07952013-02-08 15:53:47 +01004317 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004318 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004319 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004320 s32 err;
4321
Jouni Malinen89c771e2014-10-10 20:52:40 +03004322 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004323 return -EFAULT;
4324
Jouni Malinen89c771e2014-10-10 20:52:40 +03004325 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004326
Hante Meulemana0f07952013-02-08 15:53:47 +01004327 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4328 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004329 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004330 return -EIO;
4331
Jouni Malinen89c771e2014-10-10 20:52:40 +03004332 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004333 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004334 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004335 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004336 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004337 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004338
Arend van Sprield96b8012012-12-05 15:26:02 +01004339 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004340 return err;
4341}
4342
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004343static int
4344brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4345 const u8 *mac, struct station_parameters *params)
4346{
4347 struct brcmf_if *ifp = netdev_priv(ndev);
4348 s32 err;
4349
4350 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4351 params->sta_flags_mask, params->sta_flags_set);
4352
4353 /* Ignore all 00 MAC */
4354 if (is_zero_ether_addr(mac))
4355 return 0;
4356
4357 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4358 return 0;
4359
4360 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4361 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4362 (void *)mac, ETH_ALEN);
4363 else
4364 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4365 (void *)mac, ETH_ALEN);
4366 if (err < 0)
4367 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4368
4369 return err;
4370}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004371
4372static void
4373brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4374 struct wireless_dev *wdev,
4375 u16 frame_type, bool reg)
4376{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004377 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004378 u16 mgmt_type;
4379
4380 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4381
4382 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004383 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004384 if (reg)
4385 vif->mgmt_rx_reg |= BIT(mgmt_type);
4386 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004387 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004388}
4389
4390
4391static int
4392brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004393 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004394{
4395 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004396 struct ieee80211_channel *chan = params->chan;
4397 const u8 *buf = params->buf;
4398 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004399 const struct ieee80211_mgmt *mgmt;
4400 struct brcmf_cfg80211_vif *vif;
4401 s32 err = 0;
4402 s32 ie_offset;
4403 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004404 struct brcmf_fil_action_frame_le *action_frame;
4405 struct brcmf_fil_af_params_le *af_params;
4406 bool ack;
4407 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004408 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004409
4410 brcmf_dbg(TRACE, "Enter\n");
4411
4412 *cookie = 0;
4413
4414 mgmt = (const struct ieee80211_mgmt *)buf;
4415
Hante Meulemana0f07952013-02-08 15:53:47 +01004416 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4417 brcmf_err("Driver only allows MGMT packet type\n");
4418 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004419 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004420
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004421 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4422
Hante Meulemana0f07952013-02-08 15:53:47 +01004423 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4424 /* Right now the only reason to get a probe response */
4425 /* is for p2p listen response or for p2p GO from */
4426 /* wpa_supplicant. Unfortunately the probe is send */
4427 /* on primary ndev, while dongle wants it on the p2p */
4428 /* vif. Since this is only reason for a probe */
4429 /* response to be sent, the vif is taken from cfg. */
4430 /* If ever desired to send proberesp for non p2p */
4431 /* response then data should be checked for */
4432 /* "DIRECT-". Note in future supplicant will take */
4433 /* dedicated p2p wdev to do this and then this 'hack'*/
4434 /* is not needed anymore. */
4435 ie_offset = DOT11_MGMT_HDR_LEN +
4436 DOT11_BCN_PRB_FIXED_LEN;
4437 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004438 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4439 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4440 err = brcmf_vif_set_mgmt_ie(vif,
4441 BRCMF_VNDR_IE_PRBRSP_FLAG,
4442 &buf[ie_offset],
4443 ie_len);
4444 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4445 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004446 } else if (ieee80211_is_action(mgmt->frame_control)) {
4447 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4448 if (af_params == NULL) {
4449 brcmf_err("unable to allocate frame\n");
4450 err = -ENOMEM;
4451 goto exit;
4452 }
4453 action_frame = &af_params->action_frame;
4454 /* Add the packet Id */
4455 action_frame->packet_id = cpu_to_le32(*cookie);
4456 /* Add BSSID */
4457 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4458 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4459 /* Add the length exepted for 802.11 header */
4460 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004461 /* Add the channel. Use the one specified as parameter if any or
4462 * the current one (got from the firmware) otherwise
4463 */
4464 if (chan)
4465 freq = chan->center_freq;
4466 else
4467 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4468 &freq);
4469 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004470 af_params->channel = cpu_to_le32(chan_nr);
4471
4472 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4473 le16_to_cpu(action_frame->len));
4474
4475 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004476 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004477
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004478 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004479 af_params);
4480
4481 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4482 GFP_KERNEL);
4483 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004484 } else {
4485 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4486 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4487 }
4488
Hante Meuleman18e2f612013-02-08 15:53:49 +01004489exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004490 return err;
4491}
4492
4493
4494static int
4495brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4496 struct wireless_dev *wdev,
4497 u64 cookie)
4498{
4499 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4500 struct brcmf_cfg80211_vif *vif;
4501 int err = 0;
4502
4503 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4504
4505 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4506 if (vif == NULL) {
4507 brcmf_err("No p2p device available for probe response\n");
4508 err = -ENODEV;
4509 goto exit;
4510 }
4511 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4512exit:
4513 return err;
4514}
4515
Piotr Haber61730d42013-04-23 12:53:12 +02004516static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4517 struct wireless_dev *wdev,
4518 enum nl80211_crit_proto_id proto,
4519 u16 duration)
4520{
4521 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4522 struct brcmf_cfg80211_vif *vif;
4523
4524 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4525
4526 /* only DHCP support for now */
4527 if (proto != NL80211_CRIT_PROTO_DHCP)
4528 return -EINVAL;
4529
4530 /* suppress and abort scanning */
4531 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4532 brcmf_abort_scanning(cfg);
4533
4534 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4535}
4536
4537static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4538 struct wireless_dev *wdev)
4539{
4540 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4541 struct brcmf_cfg80211_vif *vif;
4542
4543 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4544
4545 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4546 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4547}
4548
Hante Meuleman70b7d942014-07-30 13:20:07 +02004549static s32
4550brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4551 const struct brcmf_event_msg *e, void *data)
4552{
4553 switch (e->reason) {
4554 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4555 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4556 break;
4557 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4558 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4559 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4560 break;
4561 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4562 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4563 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4564 break;
4565 }
4566
4567 return 0;
4568}
4569
Arend van Spriel89c2f382013-08-10 12:27:25 +02004570static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4571{
4572 int ret;
4573
4574 switch (oper) {
4575 case NL80211_TDLS_DISCOVERY_REQ:
4576 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4577 break;
4578 case NL80211_TDLS_SETUP:
4579 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4580 break;
4581 case NL80211_TDLS_TEARDOWN:
4582 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4583 break;
4584 default:
4585 brcmf_err("unsupported operation: %d\n", oper);
4586 ret = -EOPNOTSUPP;
4587 }
4588 return ret;
4589}
4590
4591static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004592 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004593 enum nl80211_tdls_operation oper)
4594{
4595 struct brcmf_if *ifp;
4596 struct brcmf_tdls_iovar_le info;
4597 int ret = 0;
4598
4599 ret = brcmf_convert_nl80211_tdls_oper(oper);
4600 if (ret < 0)
4601 return ret;
4602
4603 ifp = netdev_priv(ndev);
4604 memset(&info, 0, sizeof(info));
4605 info.mode = (u8)ret;
4606 if (peer)
4607 memcpy(info.ea, peer, ETH_ALEN);
4608
4609 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4610 &info, sizeof(info));
4611 if (ret < 0)
4612 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4613
4614 return ret;
4615}
4616
Arend van Spriel5b435de2011-10-05 13:19:03 +02004617static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004618 .add_virtual_intf = brcmf_cfg80211_add_iface,
4619 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004620 .change_virtual_intf = brcmf_cfg80211_change_iface,
4621 .scan = brcmf_cfg80211_scan,
4622 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4623 .join_ibss = brcmf_cfg80211_join_ibss,
4624 .leave_ibss = brcmf_cfg80211_leave_ibss,
4625 .get_station = brcmf_cfg80211_get_station,
4626 .set_tx_power = brcmf_cfg80211_set_tx_power,
4627 .get_tx_power = brcmf_cfg80211_get_tx_power,
4628 .add_key = brcmf_cfg80211_add_key,
4629 .del_key = brcmf_cfg80211_del_key,
4630 .get_key = brcmf_cfg80211_get_key,
4631 .set_default_key = brcmf_cfg80211_config_default_key,
4632 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4633 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004634 .connect = brcmf_cfg80211_connect,
4635 .disconnect = brcmf_cfg80211_disconnect,
4636 .suspend = brcmf_cfg80211_suspend,
4637 .resume = brcmf_cfg80211_resume,
4638 .set_pmksa = brcmf_cfg80211_set_pmksa,
4639 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004640 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004641 .start_ap = brcmf_cfg80211_start_ap,
4642 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004643 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004644 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004645 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004646 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4647 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004648 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4649 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4650 .remain_on_channel = brcmf_p2p_remain_on_channel,
4651 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004652 .start_p2p_device = brcmf_p2p_start_device,
4653 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004654 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4655 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004656 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004657};
4658
Arend van Spriel3eacf862012-10-22 13:55:30 -07004659struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004660 enum nl80211_iftype type,
4661 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004662{
Hante Meulemana44aa402014-12-03 21:05:33 +01004663 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004664 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004665 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004666
Arend van Spriel33a6b152013-02-08 15:53:39 +01004667 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004668 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004669 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4670 if (!vif)
4671 return ERR_PTR(-ENOMEM);
4672
4673 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004674 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004675
Arend van Spriel3eacf862012-10-22 13:55:30 -07004676 vif->pm_block = pm_block;
4677 vif->roam_off = -1;
4678
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004679 brcmf_init_prof(&vif->profile);
4680
Hante Meulemana44aa402014-12-03 21:05:33 +01004681 if (type == NL80211_IFTYPE_AP) {
4682 mbss = false;
4683 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4684 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4685 mbss = true;
4686 break;
4687 }
4688 }
4689 vif->mbss = mbss;
4690 }
4691
Arend van Spriel3eacf862012-10-22 13:55:30 -07004692 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004693 return vif;
4694}
4695
Arend van Spriel427dec52014-01-06 12:40:47 +01004696void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004697{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004698 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004699 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004700}
4701
Arend van Spriel9df4d542014-01-06 12:40:49 +01004702void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4703{
4704 struct brcmf_cfg80211_vif *vif;
4705 struct brcmf_if *ifp;
4706
4707 ifp = netdev_priv(ndev);
4708 vif = ifp->vif;
4709
4710 brcmf_free_vif(vif);
4711 free_netdev(ndev);
4712}
4713
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004714static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004715{
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_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004720 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004721 return true;
4722 }
4723
4724 return false;
4725}
4726
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004727static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004728{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004729 u32 event = e->event_code;
4730 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004731
Hante Meuleman68ca3952014-02-25 20:30:26 +01004732 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4733 (event == BRCMF_E_DISASSOC_IND) ||
4734 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004735 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004736 return true;
4737 }
4738 return false;
4739}
4740
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004741static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004742 const struct brcmf_event_msg *e)
4743{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004744 u32 event = e->event_code;
4745 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004746
4747 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004748 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4749 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004750 return true;
4751 }
4752
4753 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004754 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004755 return true;
4756 }
4757
4758 return false;
4759}
4760
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004761static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004762{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004763 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004764
4765 kfree(conn_info->req_ie);
4766 conn_info->req_ie = NULL;
4767 conn_info->req_ie_len = 0;
4768 kfree(conn_info->resp_ie);
4769 conn_info->resp_ie = NULL;
4770 conn_info->resp_ie_len = 0;
4771}
4772
Hante Meuleman89286dc2013-02-08 15:53:46 +01004773static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4774 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004775{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004776 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004777 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004778 u32 req_len;
4779 u32 resp_len;
4780 s32 err = 0;
4781
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004782 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004783
Arend van Sprielac24be62012-10-22 10:36:23 -07004784 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4785 cfg->extra_buf, 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 info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004788 return err;
4789 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004790 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004791 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004792 req_len = le32_to_cpu(assoc_info->req_len);
4793 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004794 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004795 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004796 cfg->extra_buf,
4797 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004798 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004799 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004800 return err;
4801 }
4802 conn_info->req_ie_len = req_len;
4803 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004804 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004805 GFP_KERNEL);
4806 } else {
4807 conn_info->req_ie_len = 0;
4808 conn_info->req_ie = NULL;
4809 }
4810 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004811 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004812 cfg->extra_buf,
4813 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004814 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004815 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004816 return err;
4817 }
4818 conn_info->resp_ie_len = resp_len;
4819 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004820 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004821 GFP_KERNEL);
4822 } else {
4823 conn_info->resp_ie_len = 0;
4824 conn_info->resp_ie = NULL;
4825 }
Arend van Spriel16886732012-12-05 15:26:04 +01004826 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4827 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004828
4829 return err;
4830}
4831
4832static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004833brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004834 struct net_device *ndev,
4835 const struct brcmf_event_msg *e)
4836{
Arend van Sprielc1179032012-10-22 13:55:33 -07004837 struct brcmf_if *ifp = netdev_priv(ndev);
4838 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004839 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4840 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004841 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004842 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004843 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004844 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004845 u32 freq;
4846 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004847 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004848
Arend van Sprield96b8012012-12-05 15:26:02 +01004849 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004850
Hante Meuleman89286dc2013-02-08 15:53:46 +01004851 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004852 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004853 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004854
Franky Lina180b832012-10-10 11:13:09 -07004855 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4856 if (buf == NULL) {
4857 err = -ENOMEM;
4858 goto done;
4859 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004860
Franky Lina180b832012-10-10 11:13:09 -07004861 /* data sent to dongle has to be little endian */
4862 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004863 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004864 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004865
4866 if (err)
4867 goto done;
4868
4869 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004870 ch.chspec = le16_to_cpu(bi->chanspec);
4871 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004872
Franky Lin83cf17a2013-04-11 13:28:50 +02004873 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004874 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4875 else
4876 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4877
Franky Lin83cf17a2013-04-11 13:28:50 +02004878 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004879 notify_channel = ieee80211_get_channel(wiphy, freq);
4880
Franky Lina180b832012-10-10 11:13:09 -07004881done:
4882 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004883 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004884 conn_info->req_ie, conn_info->req_ie_len,
4885 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004886 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004887
Arend van Sprielc1179032012-10-22 13:55:33 -07004888 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004889 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004890 return err;
4891}
4892
4893static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004894brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004895 struct net_device *ndev, const struct brcmf_event_msg *e,
4896 bool completed)
4897{
Arend van Sprielc1179032012-10-22 13:55:33 -07004898 struct brcmf_if *ifp = netdev_priv(ndev);
4899 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004900 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004901
Arend van Sprield96b8012012-12-05 15:26:02 +01004902 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004903
Arend van Sprielc1179032012-10-22 13:55:33 -07004904 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4905 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004906 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004907 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004908 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004909 brcmf_update_bss_info(cfg, ifp);
4910 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4911 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004912 }
4913 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004914 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004915 conn_info->req_ie,
4916 conn_info->req_ie_len,
4917 conn_info->resp_ie,
4918 conn_info->resp_ie_len,
4919 completed ? WLAN_STATUS_SUCCESS :
4920 WLAN_STATUS_AUTH_TIMEOUT,
4921 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004922 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4923 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004924 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004925 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004926 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004927}
4928
4929static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004930brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004931 struct net_device *ndev,
4932 const struct brcmf_event_msg *e, void *data)
4933{
Hante Meulemana44aa402014-12-03 21:05:33 +01004934 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01004935 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004936 u32 event = e->event_code;
4937 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004938 struct station_info sinfo;
4939
Arend van Spriel16886732012-12-05 15:26:04 +01004940 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004941 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4942 ndev != cfg_to_ndev(cfg)) {
4943 brcmf_dbg(CONN, "AP mode link down\n");
4944 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01004945 if (ifp->vif->mbss)
4946 brcmf_remove_interface(ifp->drvr, ifp->bssidx);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004947 return 0;
4948 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004949
Hante Meuleman1a873342012-09-27 14:17:54 +02004950 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004951 (reason == BRCMF_E_STATUS_SUCCESS)) {
4952 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004953 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004954 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004955 return -EINVAL;
4956 }
4957 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004958 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004959 generation++;
4960 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004961 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004962 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4963 (event == BRCMF_E_DEAUTH_IND) ||
4964 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004965 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004966 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004967 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004968}
4969
4970static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004971brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004972 const struct brcmf_event_msg *e, void *data)
4973{
Arend van Spriel19937322012-11-05 16:22:32 -08004974 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4975 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004976 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004977 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004978 s32 err = 0;
4979
Hante Meuleman8851cce2014-07-30 13:20:02 +02004980 if ((e->event_code == BRCMF_E_DEAUTH) ||
4981 (e->event_code == BRCMF_E_DEAUTH_IND) ||
4982 (e->event_code == BRCMF_E_DISASSOC_IND) ||
4983 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
4984 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4985 }
4986
Arend van Spriel967fe2c2014-03-15 17:18:21 +01004987 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004988 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004989 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004990 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004991 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004992 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004993 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004994 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004995 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004996 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4997 &ifp->vif->sme_state);
4998 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4999 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005000 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005001 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005002 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005003 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005004 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005005 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005006 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005007 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005008 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005009 if (ndev != cfg_to_ndev(cfg))
5010 complete(&cfg->vif_disabled);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005011 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005012 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005013 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5014 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005015 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005016 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005017 }
5018
5019 return err;
5020}
5021
5022static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005023brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005024 const struct brcmf_event_msg *e, void *data)
5025{
Arend van Spriel19937322012-11-05 16:22:32 -08005026 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005027 u32 event = e->event_code;
5028 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005029
5030 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005031 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005032 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005033 else
Arend van Spriel19937322012-11-05 16:22:32 -08005034 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005035 }
5036
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005037 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005038}
5039
5040static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005041brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005042 const struct brcmf_event_msg *e, void *data)
5043{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005044 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005045 enum nl80211_key_type key_type;
5046
5047 if (flags & BRCMF_EVENT_MSG_GROUP)
5048 key_type = NL80211_KEYTYPE_GROUP;
5049 else
5050 key_type = NL80211_KEYTYPE_PAIRWISE;
5051
Arend van Spriel19937322012-11-05 16:22:32 -08005052 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005053 NULL, GFP_KERNEL);
5054
5055 return 0;
5056}
5057
Arend van Sprield3c0b632013-02-08 15:53:37 +01005058static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5059 const struct brcmf_event_msg *e, void *data)
5060{
5061 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5062 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5063 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5064 struct brcmf_cfg80211_vif *vif;
5065
5066 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
5067 ifevent->action, ifevent->flags, ifevent->ifidx,
5068 ifevent->bssidx);
5069
Arend van Sprield3c0b632013-02-08 15:53:37 +01005070 mutex_lock(&event->vif_event_lock);
5071 event->action = ifevent->action;
5072 vif = event->vif;
5073
5074 switch (ifevent->action) {
5075 case BRCMF_E_IF_ADD:
5076 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005077 if (!cfg->vif_event.vif) {
5078 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005079 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005080 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005081
5082 ifp->vif = vif;
5083 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005084 if (ifp->ndev) {
5085 vif->wdev.netdev = ifp->ndev;
5086 ifp->ndev->ieee80211_ptr = &vif->wdev;
5087 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5088 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005089 mutex_unlock(&event->vif_event_lock);
5090 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005091 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005092
5093 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005094 mutex_unlock(&event->vif_event_lock);
5095 /* event may not be upon user request */
5096 if (brcmf_cfg80211_vif_event_armed(cfg))
5097 wake_up(&event->vif_wq);
5098 return 0;
5099
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005100 case BRCMF_E_IF_CHANGE:
5101 mutex_unlock(&event->vif_event_lock);
5102 wake_up(&event->vif_wq);
5103 return 0;
5104
Arend van Sprield3c0b632013-02-08 15:53:37 +01005105 default:
5106 mutex_unlock(&event->vif_event_lock);
5107 break;
5108 }
5109 return -EINVAL;
5110}
5111
Arend van Spriel5b435de2011-10-05 13:19:03 +02005112static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5113{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005114 conf->frag_threshold = (u32)-1;
5115 conf->rts_threshold = (u32)-1;
5116 conf->retry_short = (u32)-1;
5117 conf->retry_long = (u32)-1;
5118 conf->tx_power = -1;
5119}
5120
Arend van Spriel5c36b992012-11-14 18:46:05 -08005121static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005122{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005123 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5124 brcmf_notify_connect_status);
5125 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5126 brcmf_notify_connect_status);
5127 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5128 brcmf_notify_connect_status);
5129 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5130 brcmf_notify_connect_status);
5131 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5132 brcmf_notify_connect_status);
5133 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5134 brcmf_notify_connect_status);
5135 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5136 brcmf_notify_roaming_status);
5137 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5138 brcmf_notify_mic_status);
5139 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5140 brcmf_notify_connect_status);
5141 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5142 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005143 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5144 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005145 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005146 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005147 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5148 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005149 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5150 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005151 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5152 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005153 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5154 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005155}
5156
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005157static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005158{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005159 kfree(cfg->conf);
5160 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005161 kfree(cfg->escan_ioctl_buf);
5162 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005163 kfree(cfg->extra_buf);
5164 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005165 kfree(cfg->pmk_list);
5166 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005167}
5168
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005169static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005170{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005171 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5172 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005173 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005174 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5175 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005176 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005177 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5178 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005179 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005180 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5181 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005182 goto init_priv_mem_out;
5183
5184 return 0;
5185
5186init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005187 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005188
5189 return -ENOMEM;
5190}
5191
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005192static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005193{
5194 s32 err = 0;
5195
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005196 cfg->scan_request = NULL;
5197 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005198 cfg->active_scan = true; /* we do active scan per default */
5199 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005200 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201 if (err)
5202 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005203 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005204 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005205 brcmf_init_escan(cfg);
5206 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005207 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005208 return err;
5209}
5210
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005211static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005212{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005213 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005214 brcmf_abort_scanning(cfg);
5215 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005216}
5217
Arend van Sprield3c0b632013-02-08 15:53:37 +01005218static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5219{
5220 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005221 mutex_init(&event->vif_event_lock);
5222}
5223
Arend van Spriel5b435de2011-10-05 13:19:03 +02005224static s32
Hante Meuleman68ca3952014-02-25 20:30:26 +01005225brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005226{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005227 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005228 __le32 roamtrigger[2];
5229 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005230
5231 /*
5232 * Setup timeout if Beacons are lost and roam is
5233 * off to report link down
5234 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005235 if (brcmf_roamoff) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005236 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005237 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005238 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005239 goto dongle_rom_out;
5240 }
5241 }
5242
5243 /*
5244 * Enable/Disable built-in roaming to allow supplicant
5245 * to take care of roaming
5246 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005247 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5248 brcmf_roamoff ? "Off" : "On");
5249 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005250 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005251 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005252 goto dongle_rom_out;
5253 }
5254
Arend van Sprielf588bc02011-10-12 20:51:22 +02005255 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5256 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005257 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005258 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005259 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005260 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005261 goto dongle_rom_out;
5262 }
5263
Arend van Sprielf588bc02011-10-12 20:51:22 +02005264 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5265 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005266 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005267 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005268 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005269 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005270 goto dongle_rom_out;
5271 }
5272
5273dongle_rom_out:
5274 return err;
5275}
5276
5277static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01005278brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005279 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005280{
5281 s32 err = 0;
5282
Arend van Sprielac24be62012-10-22 10:36:23 -07005283 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005284 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005285 if (err) {
5286 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005287 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005288 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005289 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005290 goto dongle_scantime_out;
5291 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005292 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005293 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005294 if (err) {
5295 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005296 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005297 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005298 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005299 goto dongle_scantime_out;
5300 }
5301
Arend van Sprielac24be62012-10-22 10:36:23 -07005302 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005303 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005304 if (err) {
5305 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005306 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005307 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005308 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005309 goto dongle_scantime_out;
5310 }
5311
5312dongle_scantime_out:
5313 return err;
5314}
5315
Arend van Sprielb48d8912014-07-12 08:49:41 +02005316static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5317 struct brcmu_chan *ch)
5318{
5319 u32 ht40_flag;
5320
5321 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5322 if (ch->sb == BRCMU_CHAN_SB_U) {
5323 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5324 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5325 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5326 } else {
5327 /* It should be one of
5328 * IEEE80211_CHAN_NO_HT40 or
5329 * IEEE80211_CHAN_NO_HT40PLUS
5330 */
5331 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5332 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5333 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5334 }
5335}
5336
5337static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5338 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005339{
5340 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005341 struct ieee80211_supported_band *band;
5342 struct ieee80211_channel *channel;
5343 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005344 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005345 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005346 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005347 u8 *pbuf;
5348 u32 i, j;
5349 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005350 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005351 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005352
5353 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5354
5355 if (pbuf == NULL)
5356 return -ENOMEM;
5357
5358 list = (struct brcmf_chanspec_list *)pbuf;
5359
5360 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5361 BRCMF_DCMD_MEDLEN);
5362 if (err) {
5363 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005364 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005365 }
5366
Arend van Sprielb48d8912014-07-12 08:49:41 +02005367 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005368 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5369 if (band)
5370 for (i = 0; i < band->n_channels; i++)
5371 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5372 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5373 if (band)
5374 for (i = 0; i < band->n_channels; i++)
5375 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005376
5377 total = le32_to_cpu(list->count);
5378 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005379 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5380 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005381
Franky Lin83cf17a2013-04-11 13:28:50 +02005382 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005383 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005384 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005385 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005386 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005387 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005388 continue;
5389 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005390 if (!band)
5391 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005392 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005393 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005394 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005395 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005396 ch.bw == BRCMU_CHAN_BW_80)
5397 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005398
5399 channel = band->channels;
5400 index = band->n_channels;
5401 for (j = 0; j < band->n_channels; j++) {
5402 if (channel[j].hw_value == ch.chnum) {
5403 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005404 break;
5405 }
5406 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005407 channel[index].center_freq =
5408 ieee80211_channel_to_frequency(ch.chnum, band->band);
5409 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005410
Arend van Sprielb48d8912014-07-12 08:49:41 +02005411 /* assuming the chanspecs order is HT20,
5412 * HT40 upper, HT40 lower, and VHT80.
5413 */
5414 if (ch.bw == BRCMU_CHAN_BW_80) {
5415 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5416 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5417 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5418 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005419 /* enable the channel and disable other bandwidths
5420 * for now as mentioned order assure they are enabled
5421 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005422 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005423 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5424 IEEE80211_CHAN_NO_80MHZ;
5425 ch.bw = BRCMU_CHAN_BW_20;
5426 cfg->d11inf.encchspec(&ch);
5427 chaninfo = ch.chspec;
5428 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5429 &chaninfo);
5430 if (!err) {
5431 if (chaninfo & WL_CHAN_RADAR)
5432 channel[index].flags |=
5433 (IEEE80211_CHAN_RADAR |
5434 IEEE80211_CHAN_NO_IR);
5435 if (chaninfo & WL_CHAN_PASSIVE)
5436 channel[index].flags |=
5437 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005438 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005439 }
5440 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005441
Arend van Sprielb48d8912014-07-12 08:49:41 +02005442fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005443 kfree(pbuf);
5444 return err;
5445}
5446
Arend van Sprielb48d8912014-07-12 08:49:41 +02005447static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005448{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005449 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5450 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005451 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005452 struct brcmf_chanspec_list *list;
5453 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005454 u32 val;
5455 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005456 struct brcmu_chan ch;
5457 u32 num_chan;
5458 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005459
5460 /* verify support for bw_cap command */
5461 val = WLC_BAND_5G;
5462 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5463
5464 if (!err) {
5465 /* only set 2G bandwidth using bw_cap command */
5466 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5467 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5468 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5469 sizeof(band_bwcap));
5470 } else {
5471 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5472 val = WLC_N_BW_40ALL;
5473 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5474 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005475
5476 if (!err) {
5477 /* update channel info in 2G band */
5478 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5479
5480 if (pbuf == NULL)
5481 return -ENOMEM;
5482
5483 ch.band = BRCMU_CHAN_BAND_2G;
5484 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005485 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005486 ch.chnum = 0;
5487 cfg->d11inf.encchspec(&ch);
5488
5489 /* pass encoded chanspec in query */
5490 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5491
5492 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5493 BRCMF_DCMD_MEDLEN);
5494 if (err) {
5495 brcmf_err("get chanspecs error (%d)\n", err);
5496 kfree(pbuf);
5497 return err;
5498 }
5499
5500 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5501 list = (struct brcmf_chanspec_list *)pbuf;
5502 num_chan = le32_to_cpu(list->count);
5503 for (i = 0; i < num_chan; i++) {
5504 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5505 cfg->d11inf.decchspec(&ch);
5506 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5507 continue;
5508 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5509 continue;
5510 for (j = 0; j < band->n_channels; j++) {
5511 if (band->channels[j].hw_value == ch.chnum)
5512 break;
5513 }
5514 if (WARN_ON(j == band->n_channels))
5515 continue;
5516
5517 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5518 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005519 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005520 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005521 return err;
5522}
5523
Arend van Spriel2375d972014-01-06 12:40:41 +01005524static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5525{
5526 u32 band, mimo_bwcap;
5527 int err;
5528
5529 band = WLC_BAND_2G;
5530 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5531 if (!err) {
5532 bw_cap[IEEE80211_BAND_2GHZ] = band;
5533 band = WLC_BAND_5G;
5534 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5535 if (!err) {
5536 bw_cap[IEEE80211_BAND_5GHZ] = band;
5537 return;
5538 }
5539 WARN_ON(1);
5540 return;
5541 }
5542 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5543 mimo_bwcap = 0;
5544 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5545 if (err)
5546 /* assume 20MHz if firmware does not give a clue */
5547 mimo_bwcap = WLC_N_BW_20ALL;
5548
5549 switch (mimo_bwcap) {
5550 case WLC_N_BW_40ALL:
5551 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5552 /* fall-thru */
5553 case WLC_N_BW_20IN2G_40IN5G:
5554 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5555 /* fall-thru */
5556 case WLC_N_BW_20ALL:
5557 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5558 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5559 break;
5560 default:
5561 brcmf_err("invalid mimo_bw_cap value\n");
5562 }
5563}
Hante Meulemand48200b2013-04-03 12:40:29 +02005564
Arend van Spriel18d6c532014-05-12 10:47:35 +02005565static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5566 u32 bw_cap[2], u32 nchain)
5567{
5568 band->ht_cap.ht_supported = true;
5569 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5570 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5571 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5572 }
5573 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5574 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5575 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5576 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5577 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5578 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5579}
5580
5581static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5582{
5583 u16 mcs_map;
5584 int i;
5585
5586 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5587 mcs_map = (mcs_map << 2) | supp;
5588
5589 return cpu_to_le16(mcs_map);
5590}
5591
5592static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
5593 u32 bw_cap[2], u32 nchain)
5594{
5595 __le16 mcs_map;
5596
5597 /* not allowed in 2.4G band */
5598 if (band->band == IEEE80211_BAND_2GHZ)
5599 return;
5600
5601 band->vht_cap.vht_supported = true;
5602 /* 80MHz is mandatory */
5603 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5604 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5605 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5606 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5607 }
5608 /* all support 256-QAM */
5609 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5610 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5611 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
5612}
5613
Arend van Sprielb48d8912014-07-12 08:49:41 +02005614static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005615{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005616 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005617 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005618 u32 nmode = 0;
5619 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005620 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005621 u32 rxchain;
5622 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005623 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005624 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005625 struct ieee80211_supported_band *band;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005626
Arend van Spriel18d6c532014-05-12 10:47:35 +02005627 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005628 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5629 if (err) {
5630 brcmf_err("nmode error (%d)\n", err);
5631 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005632 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005633 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005634 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5635 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5636 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005637
Daniel Kim4aca7a12014-02-25 20:30:36 +01005638 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5639 if (err) {
5640 brcmf_err("rxchain error (%d)\n", err);
5641 nchain = 1;
5642 } else {
5643 for (nchain = 0; rxchain; nchain++)
5644 rxchain = rxchain & (rxchain - 1);
5645 }
5646 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5647
Arend van Sprielb48d8912014-07-12 08:49:41 +02005648 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005649 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005650 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005651 return err;
5652 }
5653
Arend van Sprielb48d8912014-07-12 08:49:41 +02005654 wiphy = cfg_to_wiphy(cfg);
5655 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5656 band = wiphy->bands[i];
5657 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005658 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005659
Arend van Spriel18d6c532014-05-12 10:47:35 +02005660 if (nmode)
5661 brcmf_update_ht_cap(band, bw_cap, nchain);
5662 if (vhtmode)
5663 brcmf_update_vht_cap(band, bw_cap, nchain);
Hante Meulemand48200b2013-04-03 12:40:29 +02005664 }
5665
Arend van Sprielb48d8912014-07-12 08:49:41 +02005666 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005667}
5668
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005669static const struct ieee80211_txrx_stypes
5670brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5671 [NL80211_IFTYPE_STATION] = {
5672 .tx = 0xffff,
5673 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5674 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5675 },
5676 [NL80211_IFTYPE_P2P_CLIENT] = {
5677 .tx = 0xffff,
5678 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5679 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5680 },
5681 [NL80211_IFTYPE_P2P_GO] = {
5682 .tx = 0xffff,
5683 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5684 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5685 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5686 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5687 BIT(IEEE80211_STYPE_AUTH >> 4) |
5688 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5689 BIT(IEEE80211_STYPE_ACTION >> 4)
5690 },
5691 [NL80211_IFTYPE_P2P_DEVICE] = {
5692 .tx = 0xffff,
5693 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5694 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5695 }
5696};
5697
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005698static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5699{
5700 struct ieee80211_iface_combination *combo = NULL;
5701 struct ieee80211_iface_limit *limits = NULL;
5702 int i = 0, max_iface_cnt;
5703
5704 combo = kzalloc(sizeof(*combo), GFP_KERNEL);
5705 if (!combo)
5706 goto err;
5707
5708 limits = kzalloc(sizeof(*limits) * 4, GFP_KERNEL);
5709 if (!limits)
5710 goto err;
5711
5712 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5713 BIT(NL80211_IFTYPE_ADHOC) |
5714 BIT(NL80211_IFTYPE_AP);
5715
5716 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5717 combo->num_different_channels = 2;
5718 else
5719 combo->num_different_channels = 1;
5720
5721 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
5722 limits[i].max = 1;
5723 limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5724 limits[i].max = 4;
5725 limits[i++].types = BIT(NL80211_IFTYPE_AP);
5726 max_iface_cnt = 5;
5727 } else {
5728 limits[i].max = 2;
5729 limits[i++].types = BIT(NL80211_IFTYPE_STATION) |
5730 BIT(NL80211_IFTYPE_AP);
5731 max_iface_cnt = 2;
5732 }
5733
5734 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P)) {
5735 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5736 BIT(NL80211_IFTYPE_P2P_GO) |
5737 BIT(NL80211_IFTYPE_P2P_DEVICE);
5738 limits[i].max = 1;
5739 limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5740 BIT(NL80211_IFTYPE_P2P_GO);
5741 limits[i].max = 1;
5742 limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5743 max_iface_cnt += 2;
5744 }
5745 combo->max_interfaces = max_iface_cnt;
5746 combo->limits = limits;
5747 combo->n_limits = i;
5748
5749 wiphy->iface_combinations = combo;
5750 wiphy->n_iface_combinations = 1;
5751 return 0;
5752
5753err:
5754 kfree(limits);
5755 kfree(combo);
5756 return -ENOMEM;
5757}
5758
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005759static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
5760{
5761 /* scheduled scan settings */
5762 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
5763 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
5764 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5765 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
5766}
5767
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005768#ifdef CONFIG_PM
5769static const struct wiphy_wowlan_support brcmf_wowlan_support = {
5770 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01005771 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
5772 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
5773 .pattern_min_len = 1,
5774 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005775};
5776#endif
5777
5778static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
5779{
5780#ifdef CONFIG_PM
5781 /* wowl settings */
5782 wiphy->wowlan = &brcmf_wowlan_support;
5783#endif
5784}
5785
Arend van Sprielb48d8912014-07-12 08:49:41 +02005786static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005787{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005788 struct brcmf_pub *drvr = ifp->drvr;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005789 struct ieee80211_supported_band *band;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005790 __le32 bandlist[3];
5791 u32 n_bands;
5792 int err, i;
5793
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005794 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
5795 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5796 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005797
5798 err = brcmf_setup_ifmodes(wiphy, ifp);
5799 if (err)
5800 return err;
5801
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005802 for (i = 0; i < wiphy->iface_combinations->max_interfaces &&
5803 i < ARRAY_SIZE(drvr->addresses); i++) {
5804 u8 *addr = drvr->addresses[i].addr;
5805
5806 memcpy(addr, drvr->mac, ETH_ALEN);
5807 if (i) {
5808 addr[0] |= BIT(1);
5809 addr[ETH_ALEN - 1] ^= i;
5810 }
5811 }
5812 wiphy->addresses = drvr->addresses;
5813 wiphy->n_addresses = i;
5814
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005815 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
5816 wiphy->cipher_suites = __wl_cipher_suites;
5817 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
5818 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
5819 WIPHY_FLAG_OFFCHAN_TX |
5820 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
5821 WIPHY_FLAG_SUPPORTS_TDLS;
5822 if (!brcmf_roamoff)
5823 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
5824 wiphy->mgmt_stypes = brcmf_txrx_stypes;
5825 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02005826 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
5827 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005828
5829 /* vendor commands/events support */
5830 wiphy->vendor_commands = brcmf_vendor_cmds;
5831 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
5832
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005833 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
5834 brcmf_wiphy_wowl_params(wiphy);
5835
Arend van Spriel58de92d2015-04-14 20:10:24 +02005836 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
5837 sizeof(bandlist));
5838 if (err) {
5839 brcmf_err("could not obtain band info: err=%d\n", err);
5840 return err;
5841 }
5842 /* first entry in bandlist is number of bands */
5843 n_bands = le32_to_cpu(bandlist[0]);
5844 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
5845 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
5846 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
5847 GFP_KERNEL);
5848 if (!band)
5849 return -ENOMEM;
5850
5851 band->channels = kmemdup(&__wl_2ghz_channels,
5852 sizeof(__wl_2ghz_channels),
5853 GFP_KERNEL);
5854 if (!band->channels) {
5855 kfree(band);
5856 return -ENOMEM;
5857 }
5858
5859 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
5860 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
5861 }
5862 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
5863 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
5864 GFP_KERNEL);
5865 if (!band)
5866 return -ENOMEM;
5867
5868 band->channels = kmemdup(&__wl_5ghz_channels,
5869 sizeof(__wl_5ghz_channels),
5870 GFP_KERNEL);
5871 if (!band->channels) {
5872 kfree(band);
5873 return -ENOMEM;
5874 }
5875
5876 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
5877 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
5878 }
5879 }
5880 err = brcmf_setup_wiphybands(wiphy);
5881 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005882}
5883
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005884static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005885{
5886 struct net_device *ndev;
5887 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005888 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005889 s32 power_mode;
5890 s32 err = 0;
5891
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005892 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005893 return err;
5894
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005895 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005896 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01005897 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005898
Hante Meuleman40a23292013-01-02 15:22:51 +01005899 /* make sure RF is ready for work */
5900 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
5901
5902 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
5903 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005904
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005905 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01005906 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005907 if (err)
5908 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005909 brcmf_dbg(INFO, "power save set to %s\n",
5910 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005911
Hante Meuleman68ca3952014-02-25 20:30:26 +01005912 err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005913 if (err)
5914 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005915 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5916 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01005917 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005918 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005919
Hante Meulemanb3657452013-05-27 21:09:53 +02005920 brcmf_configure_arp_offload(ifp, true);
5921
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005922 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01005923default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005924
5925 return err;
5926
5927}
5928
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005929static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005930{
Arend van Sprielc1179032012-10-22 13:55:33 -07005931 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005932
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005933 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005934}
5935
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005936static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005937{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005938 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07005939
Arend van Spriel5b435de2011-10-05 13:19:03 +02005940 /*
5941 * While going down, if associated with AP disassociate
5942 * from AP to save power
5943 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005944 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005945 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005946
5947 /* Make sure WPA_Supplicant receives all the event
5948 generated due to DISASSOC call to the fw to keep
5949 the state fw and WPA_Supplicant state consistent
5950 */
5951 brcmf_delay(500);
5952 }
5953
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005954 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07005955 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005956
Arend van Spriel5b435de2011-10-05 13:19:03 +02005957 return 0;
5958}
5959
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005960s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005961{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005962 struct brcmf_if *ifp = netdev_priv(ndev);
5963 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005964 s32 err = 0;
5965
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005966 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005967 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005968 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005969
5970 return err;
5971}
5972
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005973s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005974{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005975 struct brcmf_if *ifp = netdev_priv(ndev);
5976 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005977 s32 err = 0;
5978
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005979 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005980 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005981 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005982
5983 return err;
5984}
5985
Arend van Spriela7965fb2013-04-11 17:08:37 +02005986enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
5987{
5988 struct wireless_dev *wdev = &ifp->vif->wdev;
5989
5990 return wdev->iftype;
5991}
5992
Hante Meulemanbfe81972014-10-28 14:56:16 +01005993bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
5994 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01005995{
5996 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005997
5998 list_for_each_entry(vif, &cfg->vif_list, list) {
5999 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006000 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006001 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006002 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006003}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006004
6005static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6006 u8 action)
6007{
6008 u8 evt_action;
6009
6010 mutex_lock(&event->vif_event_lock);
6011 evt_action = event->action;
6012 mutex_unlock(&event->vif_event_lock);
6013 return evt_action == action;
6014}
6015
6016void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6017 struct brcmf_cfg80211_vif *vif)
6018{
6019 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6020
6021 mutex_lock(&event->vif_event_lock);
6022 event->vif = vif;
6023 event->action = 0;
6024 mutex_unlock(&event->vif_event_lock);
6025}
6026
6027bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6028{
6029 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6030 bool armed;
6031
6032 mutex_lock(&event->vif_event_lock);
6033 armed = event->vif != NULL;
6034 mutex_unlock(&event->vif_event_lock);
6035
6036 return armed;
6037}
6038int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6039 u8 action, ulong timeout)
6040{
6041 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6042
6043 return wait_event_timeout(event->vif_wq,
6044 vif_event_equals(event, action), timeout);
6045}
6046
Arend van Spriel63db1a42014-12-21 12:43:51 +01006047static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6048 struct regulatory_request *req)
6049{
6050 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6051 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6052 struct brcmf_fil_country_le ccreq;
6053 int i;
6054
6055 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6056 req->alpha2[0], req->alpha2[1]);
6057
6058 /* ignore non-ISO3166 country codes */
6059 for (i = 0; i < sizeof(req->alpha2); i++)
6060 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6061 brcmf_err("not a ISO3166 code\n");
6062 return;
6063 }
6064 memset(&ccreq, 0, sizeof(ccreq));
6065 ccreq.rev = cpu_to_le32(-1);
6066 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006067 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6068 brcmf_err("firmware rejected country setting\n");
6069 return;
6070 }
6071 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006072}
6073
Arend van Sprielb48d8912014-07-12 08:49:41 +02006074static void brcmf_free_wiphy(struct wiphy *wiphy)
6075{
Arend van Spriel58de92d2015-04-14 20:10:24 +02006076 if (!wiphy)
6077 return;
6078
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006079 if (wiphy->iface_combinations)
6080 kfree(wiphy->iface_combinations->limits);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006081 kfree(wiphy->iface_combinations);
6082 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6083 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6084 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6085 }
6086 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6087 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6088 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6089 }
6090 wiphy_free(wiphy);
6091}
6092
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006093struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
6094 struct device *busdev)
6095{
6096 struct net_device *ndev = drvr->iflist[0]->ndev;
6097 struct brcmf_cfg80211_info *cfg;
6098 struct wiphy *wiphy;
6099 struct brcmf_cfg80211_vif *vif;
6100 struct brcmf_if *ifp;
6101 s32 err = 0;
6102 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006103 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006104
6105 if (!ndev) {
6106 brcmf_err("ndev is invalid\n");
6107 return NULL;
6108 }
6109
6110 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006111 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6112 if (!wiphy) {
6113 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006114 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006115 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006116 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006117 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006118
6119 cfg = wiphy_priv(wiphy);
6120 cfg->wiphy = wiphy;
6121 cfg->pub = drvr;
6122 init_vif_event(&cfg->vif_event);
6123 INIT_LIST_HEAD(&cfg->vif_list);
6124
6125 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006126 if (IS_ERR(vif))
6127 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006128
6129 vif->ifp = ifp;
6130 vif->wdev.netdev = ndev;
6131 ndev->ieee80211_ptr = &vif->wdev;
6132 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6133
6134 err = wl_init_priv(cfg);
6135 if (err) {
6136 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006137 brcmf_free_vif(vif);
6138 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006139 }
6140 ifp->vif = vif;
6141
Arend van Sprielb48d8912014-07-12 08:49:41 +02006142 /* determine d11 io type before wiphy setup */
6143 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006144 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006145 brcmf_err("Failed to get D11 version (%d)\n", err);
6146 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006147 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006148 cfg->d11inf.io_type = (u8)io_type;
6149 brcmu_d11_attach(&cfg->d11inf);
6150
6151 err = brcmf_setup_wiphy(wiphy, ifp);
6152 if (err < 0)
6153 goto priv_out;
6154
6155 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006156 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006157 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6158 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6159
6160 /* firmware defaults to 40MHz disabled in 2G band. We signal
6161 * cfg80211 here that we do and have it decide we can enable
6162 * it. But first check if device does support 2G operation.
6163 */
6164 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6165 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6166 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6167 }
6168 err = wiphy_register(wiphy);
6169 if (err < 0) {
6170 brcmf_err("Could not register wiphy device (%d)\n", err);
6171 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006172 }
6173
6174 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6175 * setup 40MHz in 2GHz band and enable OBSS scanning.
6176 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006177 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6178 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006179 if (!err)
6180 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6181 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006182 else
6183 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006184 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006185
6186 err = brcmf_p2p_attach(cfg);
6187 if (err) {
6188 brcmf_err("P2P initilisation failed (%d)\n", err);
6189 goto wiphy_unreg_out;
6190 }
6191 err = brcmf_btcoex_attach(cfg);
6192 if (err) {
6193 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6194 brcmf_p2p_detach(&cfg->p2p);
6195 goto wiphy_unreg_out;
6196 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006197
6198 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6199 if (err) {
6200 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6201 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman70b7d942014-07-30 13:20:07 +02006202 } else {
6203 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6204 brcmf_notify_tdls_peer_event);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006205 }
6206
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006207 return cfg;
6208
Arend van Sprielb48d8912014-07-12 08:49:41 +02006209wiphy_unreg_out:
6210 wiphy_unregister(cfg->wiphy);
6211priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006212 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006213 brcmf_free_vif(vif);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006214wiphy_out:
6215 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006216 return NULL;
6217}
6218
6219void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6220{
6221 if (!cfg)
6222 return;
6223
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006224 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006225 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006226 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006227 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006228}