blob: 5cecd67265fa23de890ddcaf128db09265decb1a [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
Arend van Spriel9f440b72013-02-08 15:53:36 +010055#define BRCMF_IFACE_MAX_CNT 3
Arend van Spriel3eacf862012-10-22 13:55:30 -070056
Hante Meuleman1a873342012-09-27 14:17:54 +020057#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
58#define WPA_OUI_TYPE 1
59#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
60#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010061#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020062
63#define VS_IE_FIXED_HDR_LEN 6
64#define WPA_IE_VERSION_LEN 2
65#define WPA_IE_MIN_OUI_LEN 4
66#define WPA_IE_SUITE_COUNT_LEN 2
67
68#define WPA_CIPHER_NONE 0 /* None */
69#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
70#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
71#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
72#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
73
74#define RSN_AKM_NONE 0 /* None (IBSS) */
75#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
76#define RSN_AKM_PSK 2 /* Pre-shared Key */
77#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
78#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
79
80#define VNDR_IE_CMD_LEN 4 /* length of the set command
81 * string :"add", "del" (+ NUL)
82 */
83#define VNDR_IE_COUNT_OFFSET 4
84#define VNDR_IE_PKTFLAG_OFFSET 8
85#define VNDR_IE_VSIE_OFFSET 12
86#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010087#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020088
89#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
90#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020091
Hante Meuleman89286dc2013-02-08 15:53:46 +010092#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
93#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
94#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
95
Arend van Spriel5b435de2011-10-05 13:19:03 +020096#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
97 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
98
Arend van Sprielce81e312012-10-22 13:55:37 -070099static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200100{
Arend van Sprielc1179032012-10-22 13:55:33 -0700101 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100102 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
103 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200104 return false;
105 }
106 return true;
107}
108
Arend van Spriel5b435de2011-10-05 13:19:03 +0200109#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
110#define RATETAB_ENT(_rateid, _flags) \
111 { \
112 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
113 .hw_value = (_rateid), \
114 .flags = (_flags), \
115 }
116
117static struct ieee80211_rate __wl_rates[] = {
118 RATETAB_ENT(BRCM_RATE_1M, 0),
119 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
120 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
121 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
122 RATETAB_ENT(BRCM_RATE_6M, 0),
123 RATETAB_ENT(BRCM_RATE_9M, 0),
124 RATETAB_ENT(BRCM_RATE_12M, 0),
125 RATETAB_ENT(BRCM_RATE_18M, 0),
126 RATETAB_ENT(BRCM_RATE_24M, 0),
127 RATETAB_ENT(BRCM_RATE_36M, 0),
128 RATETAB_ENT(BRCM_RATE_48M, 0),
129 RATETAB_ENT(BRCM_RATE_54M, 0),
130};
131
Arend van Spriel5b435de2011-10-05 13:19:03 +0200132#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200133#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
134#define wl_a_rates (__wl_rates + 4)
135#define wl_a_rates_size (wl_g_rates_size - 4)
136
137#define CHAN2G(_channel, _freq) { \
138 .band = IEEE80211_BAND_2GHZ, \
139 .center_freq = (_freq), \
140 .hw_value = (_channel), \
141 .flags = IEEE80211_CHAN_DISABLED, \
142 .max_antenna_gain = 0, \
143 .max_power = 30, \
144}
145
146#define CHAN5G(_channel) { \
147 .band = IEEE80211_BAND_5GHZ, \
148 .center_freq = 5000 + (5 * (_channel)), \
149 .hw_value = (_channel), \
150 .flags = IEEE80211_CHAN_DISABLED, \
151 .max_antenna_gain = 0, \
152 .max_power = 30, \
153}
154
155static struct ieee80211_channel __wl_2ghz_channels[] = {
156 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
157 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
158 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
159 CHAN2G(13, 2472), CHAN2G(14, 2484)
160};
161
162static struct ieee80211_channel __wl_5ghz_channels[] = {
163 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
164 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
165 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
166 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
167 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
168 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
169};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200170
Arend van Sprielb48d8912014-07-12 08:49:41 +0200171/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200172 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200173 */
174static const struct ieee80211_supported_band __wl_band_2ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200175 .band = IEEE80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200176 .bitrates = wl_g_rates,
177 .n_bitrates = wl_g_rates_size,
178};
179
Arend van Spriel58de92d2015-04-14 20:10:24 +0200180static const struct ieee80211_supported_band __wl_band_5ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200181 .band = IEEE80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200182 .bitrates = wl_a_rates,
183 .n_bitrates = wl_a_rates_size,
184};
185
Hante Meulemand48200b2013-04-03 12:40:29 +0200186/* This is to override regulatory domains defined in cfg80211 module (reg.c)
187 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200188 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
189 * With respect to these flags, wpa_supplicant doesn't * start p2p
190 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200191 * domain are to be done here.
192 */
193static const struct ieee80211_regdomain brcmf_regdom = {
194 .n_reg_rules = 4,
195 .alpha2 = "99",
196 .reg_rules = {
197 /* IEEE 802.11b/g, channels 1..11 */
198 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
199 /* If any */
200 /* IEEE 802.11 channel 14 - Only JP enables
201 * this and for 802.11b only
202 */
203 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
204 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200205 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200206 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200207 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200208};
209
210static const u32 __wl_cipher_suites[] = {
211 WLAN_CIPHER_SUITE_WEP40,
212 WLAN_CIPHER_SUITE_WEP104,
213 WLAN_CIPHER_SUITE_TKIP,
214 WLAN_CIPHER_SUITE_CCMP,
215 WLAN_CIPHER_SUITE_AES_CMAC,
216};
217
Hante Meuleman1a873342012-09-27 14:17:54 +0200218/* Vendor specific ie. id = 221, oui and type defines exact ie */
219struct brcmf_vs_tlv {
220 u8 id;
221 u8 len;
222 u8 oui[3];
223 u8 oui_type;
224};
225
226struct parsed_vndr_ie_info {
227 u8 *ie_ptr;
228 u32 ie_len; /* total length including id & length field */
229 struct brcmf_vs_tlv vndrie;
230};
231
232struct parsed_vndr_ies {
233 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100234 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200235};
236
Hante Meuleman68ca3952014-02-25 20:30:26 +0100237static int brcmf_roamoff;
238module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
239MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
240
Alwin Beukersef6ac172011-10-12 20:51:26 +0200241/* Quarter dBm units to mW
242 * Table starts at QDBM_OFFSET, so the first entry is mW for qdBm=153
243 * Table is offset so the last entry is largest mW value that fits in
244 * a u16.
245 */
246
247#define QDBM_OFFSET 153 /* Offset for first entry */
248#define QDBM_TABLE_LEN 40 /* Table size */
249
250/* Smallest mW value that will round up to the first table entry, QDBM_OFFSET.
251 * Value is ( mW(QDBM_OFFSET - 1) + mW(QDBM_OFFSET) ) / 2
252 */
253#define QDBM_TABLE_LOW_BOUND 6493 /* Low bound */
254
255/* Largest mW value that will round down to the last table entry,
256 * QDBM_OFFSET + QDBM_TABLE_LEN-1.
257 * Value is ( mW(QDBM_OFFSET + QDBM_TABLE_LEN - 1) +
258 * mW(QDBM_OFFSET + QDBM_TABLE_LEN) ) / 2.
259 */
260#define QDBM_TABLE_HIGH_BOUND 64938 /* High bound */
261
262static const u16 nqdBm_to_mW_map[QDBM_TABLE_LEN] = {
263/* qdBm: +0 +1 +2 +3 +4 +5 +6 +7 */
264/* 153: */ 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000,
265/* 161: */ 10593, 11220, 11885, 12589, 13335, 14125, 14962, 15849,
266/* 169: */ 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119,
267/* 177: */ 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811,
268/* 185: */ 42170, 44668, 47315, 50119, 53088, 56234, 59566, 63096
269};
270
271static u16 brcmf_qdbm_to_mw(u8 qdbm)
272{
273 uint factor = 1;
274 int idx = qdbm - QDBM_OFFSET;
275
276 if (idx >= QDBM_TABLE_LEN)
277 /* clamp to max u16 mW value */
278 return 0xFFFF;
279
280 /* scale the qdBm index up to the range of the table 0-40
281 * where an offset of 40 qdBm equals a factor of 10 mW.
282 */
283 while (idx < 0) {
284 idx += 40;
285 factor *= 10;
286 }
287
288 /* return the mW value scaled down to the correct factor of 10,
289 * adding in factor/2 to get proper rounding.
290 */
291 return (nqdBm_to_mW_map[idx] + factor / 2) / factor;
292}
293
294static u8 brcmf_mw_to_qdbm(u16 mw)
295{
296 u8 qdbm;
297 int offset;
298 uint mw_uint = mw;
299 uint boundary;
300
301 /* handle boundary case */
302 if (mw_uint <= 1)
303 return 0;
304
305 offset = QDBM_OFFSET;
306
307 /* move mw into the range of the table */
308 while (mw_uint < QDBM_TABLE_LOW_BOUND) {
309 mw_uint *= 10;
310 offset -= 40;
311 }
312
313 for (qdbm = 0; qdbm < QDBM_TABLE_LEN - 1; qdbm++) {
314 boundary = nqdBm_to_mW_map[qdbm] + (nqdBm_to_mW_map[qdbm + 1] -
315 nqdBm_to_mW_map[qdbm]) / 2;
316 if (mw_uint < boundary)
317 break;
318 }
319
320 qdbm += (u8) offset;
321
322 return qdbm;
323}
324
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200325static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
326 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200327{
328 struct brcmu_chan ch_inf;
329 s32 primary_offset;
330
331 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
332 ch->chan->center_freq, ch->center_freq1, ch->width);
333 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
334 primary_offset = ch->center_freq1 - ch->chan->center_freq;
335 switch (ch->width) {
336 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100337 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200338 ch_inf.bw = BRCMU_CHAN_BW_20;
339 WARN_ON(primary_offset != 0);
340 break;
341 case NL80211_CHAN_WIDTH_40:
342 ch_inf.bw = BRCMU_CHAN_BW_40;
343 if (primary_offset < 0)
344 ch_inf.sb = BRCMU_CHAN_SB_U;
345 else
346 ch_inf.sb = BRCMU_CHAN_SB_L;
347 break;
348 case NL80211_CHAN_WIDTH_80:
349 ch_inf.bw = BRCMU_CHAN_BW_80;
350 if (primary_offset < 0) {
351 if (primary_offset < -CH_10MHZ_APART)
352 ch_inf.sb = BRCMU_CHAN_SB_UU;
353 else
354 ch_inf.sb = BRCMU_CHAN_SB_UL;
355 } else {
356 if (primary_offset > CH_10MHZ_APART)
357 ch_inf.sb = BRCMU_CHAN_SB_LL;
358 else
359 ch_inf.sb = BRCMU_CHAN_SB_LU;
360 }
361 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100362 case NL80211_CHAN_WIDTH_80P80:
363 case NL80211_CHAN_WIDTH_160:
364 case NL80211_CHAN_WIDTH_5:
365 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200366 default:
367 WARN_ON_ONCE(1);
368 }
369 switch (ch->chan->band) {
370 case IEEE80211_BAND_2GHZ:
371 ch_inf.band = BRCMU_CHAN_BAND_2G;
372 break;
373 case IEEE80211_BAND_5GHZ:
374 ch_inf.band = BRCMU_CHAN_BAND_5G;
375 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100376 case IEEE80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200377 default:
378 WARN_ON_ONCE(1);
379 }
380 d11inf->encchspec(&ch_inf);
381
382 return ch_inf.chspec;
383}
384
Franky Lin83cf17a2013-04-11 13:28:50 +0200385u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
386 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700387{
Franky Lin83cf17a2013-04-11 13:28:50 +0200388 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700389
Franky Lin83cf17a2013-04-11 13:28:50 +0200390 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
391 ch_inf.bw = BRCMU_CHAN_BW_20;
392 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700393
Franky Lin83cf17a2013-04-11 13:28:50 +0200394 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700395}
396
Hante Meuleman89286dc2013-02-08 15:53:46 +0100397/* Traverse a string of 1-byte tag/1-byte length/variable-length value
398 * triples, returning a pointer to the substring whose first element
399 * matches tag
400 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100401const struct brcmf_tlv *
402brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100403{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100404 const struct brcmf_tlv *elt = buf;
405 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100406
407 /* find tagged parameter */
408 while (totlen >= TLV_HDR_LEN) {
409 int len = elt->len;
410
411 /* validate remaining totlen */
412 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
413 return elt;
414
415 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
416 totlen -= (len + TLV_HDR_LEN);
417 }
418
419 return NULL;
420}
421
422/* Is any of the tlvs the expected entry? If
423 * not update the tlvs buffer pointer/length.
424 */
425static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100426brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
427 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100428{
429 /* If the contents match the OUI and the type */
430 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
431 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
432 type == ie[TLV_BODY_OFF + oui_len]) {
433 return true;
434 }
435
436 if (tlvs == NULL)
437 return false;
438 /* point to the next ie */
439 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
440 /* calculate the length of the rest of the buffer */
441 *tlvs_len -= (int)(ie - *tlvs);
442 /* update the pointer to the start of the buffer */
443 *tlvs = ie;
444
445 return false;
446}
447
448static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100449brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100450{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100451 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100452
453 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100454 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100455 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
456 return (struct brcmf_vs_tlv *)ie;
457 }
458 return NULL;
459}
460
461static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100462brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100463{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100464 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100465
466 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
467 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
468 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
469 return (struct brcmf_vs_tlv *)ie;
470 }
471 return NULL;
472}
473
474
Arend van Spriel5b435de2011-10-05 13:19:03 +0200475static void convert_key_from_CPU(struct brcmf_wsec_key *key,
476 struct brcmf_wsec_key_le *key_le)
477{
478 key_le->index = cpu_to_le32(key->index);
479 key_le->len = cpu_to_le32(key->len);
480 key_le->algo = cpu_to_le32(key->algo);
481 key_le->flags = cpu_to_le32(key->flags);
482 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
483 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
484 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
485 memcpy(key_le->data, key->data, sizeof(key->data));
486 memcpy(key_le->ea, key->ea, sizeof(key->ea));
487}
488
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200489static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100490send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200491{
492 int err;
493 struct brcmf_wsec_key_le key_le;
494
495 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200496
Hante Meuleman118eb302014-12-21 12:43:49 +0100497 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700498
Hante Meuleman118eb302014-12-21 12:43:49 +0100499 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700500 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200501
Arend van Spriel5b435de2011-10-05 13:19:03 +0200502 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100503 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200504 return err;
505}
506
Hante Meulemanb3657452013-05-27 21:09:53 +0200507static s32
508brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
509{
510 s32 err;
511 u32 mode;
512
513 if (enable)
514 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
515 else
516 mode = 0;
517
518 /* Try to set and enable ARP offload feature, this may fail, then it */
519 /* is simply not supported and err 0 will be returned */
520 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
521 if (err) {
522 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
523 mode, err);
524 err = 0;
525 } else {
526 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
527 if (err) {
528 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
529 enable, err);
530 err = 0;
531 } else
532 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
533 enable, mode);
534 }
535
536 return err;
537}
538
Hante Meuleman8851cce2014-07-30 13:20:02 +0200539static void
540brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
541{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200542 struct brcmf_cfg80211_vif *vif;
543 struct brcmf_if *ifp;
544
545 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
546 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200547
548 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
549 (wdev->iftype == NL80211_IFTYPE_AP) ||
550 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
551 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
552 ADDR_DIRECT);
553 else
554 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
555 ADDR_INDIRECT);
556}
557
Hante Meulemana44aa402014-12-03 21:05:33 +0100558static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
559{
560 struct brcmf_mbss_ssid_le mbss_ssid_le;
561 int bsscfgidx;
562 int err;
563
564 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
565 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
566 if (bsscfgidx < 0)
567 return bsscfgidx;
568
569 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
570 mbss_ssid_le.SSID_len = cpu_to_le32(5);
571 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
572
573 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
574 sizeof(mbss_ssid_le));
575 if (err < 0)
576 brcmf_err("setting ssid failed %d\n", err);
577
578 return err;
579}
580
581/**
582 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
583 *
584 * @wiphy: wiphy device of new interface.
585 * @name: name of the new interface.
586 * @flags: not used.
587 * @params: contains mac address for AP device.
588 */
589static
590struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
591 u32 *flags, struct vif_params *params)
592{
593 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
594 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
595 struct brcmf_cfg80211_vif *vif;
596 int err;
597
598 if (brcmf_cfg80211_vif_event_armed(cfg))
599 return ERR_PTR(-EBUSY);
600
601 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
602
603 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
604 if (IS_ERR(vif))
605 return (struct wireless_dev *)vif;
606
607 brcmf_cfg80211_arm_vif_event(cfg, vif);
608
609 err = brcmf_cfg80211_request_ap_if(ifp);
610 if (err) {
611 brcmf_cfg80211_arm_vif_event(cfg, NULL);
612 goto fail;
613 }
614
615 /* wait for firmware event */
616 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
617 msecs_to_jiffies(1500));
618 brcmf_cfg80211_arm_vif_event(cfg, NULL);
619 if (!err) {
620 brcmf_err("timeout occurred\n");
621 err = -EIO;
622 goto fail;
623 }
624
625 /* interface created in firmware */
626 ifp = vif->ifp;
627 if (!ifp) {
628 brcmf_err("no if pointer provided\n");
629 err = -ENOENT;
630 goto fail;
631 }
632
633 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
634 err = brcmf_net_attach(ifp, true);
635 if (err) {
636 brcmf_err("Registering netdevice failed\n");
637 goto fail;
638 }
639
640 return &ifp->vif->wdev;
641
642fail:
643 brcmf_free_vif(vif);
644 return ERR_PTR(err);
645}
646
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100647static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
648{
649 enum nl80211_iftype iftype;
650
651 iftype = vif->wdev.iftype;
652 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
653}
654
655static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
656{
657 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
658}
659
Arend van Spriel9f440b72013-02-08 15:53:36 +0100660static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
661 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100662 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100663 enum nl80211_iftype type,
664 u32 *flags,
665 struct vif_params *params)
666{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200667 struct wireless_dev *wdev;
668
Arend van Spriel9f440b72013-02-08 15:53:36 +0100669 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
670 switch (type) {
671 case NL80211_IFTYPE_ADHOC:
672 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100673 case NL80211_IFTYPE_AP_VLAN:
674 case NL80211_IFTYPE_WDS:
675 case NL80211_IFTYPE_MONITOR:
676 case NL80211_IFTYPE_MESH_POINT:
677 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100678 case NL80211_IFTYPE_AP:
679 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
680 if (!IS_ERR(wdev))
681 brcmf_cfg80211_update_proto_addr_mode(wdev);
682 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100683 case NL80211_IFTYPE_P2P_CLIENT:
684 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200685 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100686 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200687 if (!IS_ERR(wdev))
688 brcmf_cfg80211_update_proto_addr_mode(wdev);
689 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100690 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100691 default:
692 return ERR_PTR(-EINVAL);
693 }
694}
695
Daniel Kim5e787f72014-06-21 12:11:18 +0200696static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
697{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200698 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200699 brcmf_set_mpc(ifp, mpc);
700}
701
Arend van Sprielf96aa072013-04-05 10:57:48 +0200702void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100703{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100704 s32 err = 0;
705
706 if (check_vif_up(ifp->vif)) {
707 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
708 if (err) {
709 brcmf_err("fail to set mpc\n");
710 return;
711 }
712 brcmf_dbg(INFO, "MPC : %d\n", mpc);
713 }
714}
715
Arend van Spriela0f472a2013-04-05 10:57:49 +0200716s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
717 struct brcmf_if *ifp, bool aborted,
718 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100719{
720 struct brcmf_scan_params_le params_le;
721 struct cfg80211_scan_request *scan_request;
722 s32 err = 0;
723
724 brcmf_dbg(SCAN, "Enter\n");
725
726 /* clear scan request, because the FW abort can cause a second call */
727 /* to this functon and might cause a double cfg80211_scan_done */
728 scan_request = cfg->scan_request;
729 cfg->scan_request = NULL;
730
731 if (timer_pending(&cfg->escan_timeout))
732 del_timer_sync(&cfg->escan_timeout);
733
734 if (fw_abort) {
735 /* Do a scan abort to stop the driver's scan engine */
736 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
737 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800738 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100739 params_le.bss_type = DOT11_BSSTYPE_ANY;
740 params_le.scan_type = 0;
741 params_le.channel_num = cpu_to_le32(1);
742 params_le.nprobes = cpu_to_le32(1);
743 params_le.active_time = cpu_to_le32(-1);
744 params_le.passive_time = cpu_to_le32(-1);
745 params_le.home_time = cpu_to_le32(-1);
746 /* Scan is aborted by setting channel_list[0] to -1 */
747 params_le.channel_list[0] = cpu_to_le16(-1);
748 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200749 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100750 &params_le, sizeof(params_le));
751 if (err)
752 brcmf_err("Scan abort failed\n");
753 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200754
Daniel Kim5e787f72014-06-21 12:11:18 +0200755 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200756
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100757 /*
758 * e-scan can be initiated by scheduled scan
759 * which takes precedence.
760 */
761 if (cfg->sched_escan) {
762 brcmf_dbg(SCAN, "scheduled scan completed\n");
763 cfg->sched_escan = false;
764 if (!aborted)
765 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100766 } else if (scan_request) {
767 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
768 aborted ? "Aborted" : "Done");
769 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100770 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100771 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
772 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100773
774 return err;
775}
776
Arend van Spriel9f440b72013-02-08 15:53:36 +0100777static
778int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
779{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100780 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
781 struct net_device *ndev = wdev->netdev;
782
783 /* vif event pending in firmware */
784 if (brcmf_cfg80211_vif_event_armed(cfg))
785 return -EBUSY;
786
787 if (ndev) {
788 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200789 cfg->escan_info.ifp == netdev_priv(ndev))
790 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
791 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100792
793 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
794 }
795
Arend van Spriel9f440b72013-02-08 15:53:36 +0100796 switch (wdev->iftype) {
797 case NL80211_IFTYPE_ADHOC:
798 case NL80211_IFTYPE_STATION:
799 case NL80211_IFTYPE_AP:
800 case NL80211_IFTYPE_AP_VLAN:
801 case NL80211_IFTYPE_WDS:
802 case NL80211_IFTYPE_MONITOR:
803 case NL80211_IFTYPE_MESH_POINT:
804 return -EOPNOTSUPP;
805 case NL80211_IFTYPE_P2P_CLIENT:
806 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200807 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100808 return brcmf_p2p_del_vif(wiphy, wdev);
809 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100810 default:
811 return -EINVAL;
812 }
813 return -EOPNOTSUPP;
814}
815
Arend van Spriel5b435de2011-10-05 13:19:03 +0200816static s32
817brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
818 enum nl80211_iftype type, u32 *flags,
819 struct vif_params *params)
820{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100821 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700822 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100823 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200824 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200825 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200826 s32 err = 0;
827
Arend van Sprield96b8012012-12-05 15:26:02 +0100828 brcmf_dbg(TRACE, "Enter, ndev=%p, type=%d\n", ndev, type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200829
830 switch (type) {
831 case NL80211_IFTYPE_MONITOR:
832 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100833 brcmf_err("type (%d) : currently we do not support this type\n",
834 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200835 return -EOPNOTSUPP;
836 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200837 infra = 0;
838 break;
839 case NL80211_IFTYPE_STATION:
Hante Meuleman1bc7c652013-02-08 15:53:56 +0100840 /* Ignore change for p2p IF. Unclear why supplicant does this */
841 if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
842 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
843 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
844 /* WAR: It is unexpected to get a change of VIF for P2P
845 * IF, but it happens. The request can not be handled
846 * but returning EPERM causes a crash. Returning 0
847 * without setting ieee80211_ptr->iftype causes trace
848 * (WARN_ON) but it works with wpa_supplicant
849 */
850 return 0;
851 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200852 infra = 1;
853 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200854 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100855 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200856 ap = 1;
857 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200858 default:
859 err = -EINVAL;
860 goto done;
861 }
862
Hante Meuleman1a873342012-09-27 14:17:54 +0200863 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100864 if (type == NL80211_IFTYPE_P2P_GO) {
865 brcmf_dbg(INFO, "IF Type = P2P GO\n");
866 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
867 }
868 if (!err) {
869 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
870 brcmf_dbg(INFO, "IF Type = AP\n");
871 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200872 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100873 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200874 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100875 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200876 err = -EAGAIN;
877 goto done;
878 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100879 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100880 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200881 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200882 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200883
Hante Meuleman8851cce2014-07-30 13:20:02 +0200884 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
885
Arend van Spriel5b435de2011-10-05 13:19:03 +0200886done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100887 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200888
889 return err;
890}
891
Franky Lin83cf17a2013-04-11 13:28:50 +0200892static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
893 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200894 struct cfg80211_scan_request *request)
895{
896 u32 n_ssids;
897 u32 n_channels;
898 s32 i;
899 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200900 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200901 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200902 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200903
Joe Perches93803b32015-03-02 19:54:49 -0800904 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200905 params_le->bss_type = DOT11_BSSTYPE_ANY;
906 params_le->scan_type = 0;
907 params_le->channel_num = 0;
908 params_le->nprobes = cpu_to_le32(-1);
909 params_le->active_time = cpu_to_le32(-1);
910 params_le->passive_time = cpu_to_le32(-1);
911 params_le->home_time = cpu_to_le32(-1);
912 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
913
914 /* if request is null exit so it will be all channel broadcast scan */
915 if (!request)
916 return;
917
918 n_ssids = request->n_ssids;
919 n_channels = request->n_channels;
920 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100921 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
922 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200923 if (n_channels > 0) {
924 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200925 chanspec = channel_to_chanspec(&cfg->d11inf,
926 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100927 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
928 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200929 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200930 }
931 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100932 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200933 }
934 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100935 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200936 if (n_ssids > 0) {
937 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
938 n_channels * sizeof(u16);
939 offset = roundup(offset, sizeof(u32));
940 ptr = (char *)params_le + offset;
941 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200942 memset(&ssid_le, 0, sizeof(ssid_le));
943 ssid_le.SSID_len =
944 cpu_to_le32(request->ssids[i].ssid_len);
945 memcpy(ssid_le.SSID, request->ssids[i].ssid,
946 request->ssids[i].ssid_len);
947 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100948 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200949 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100950 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
951 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200952 memcpy(ptr, &ssid_le, sizeof(ssid_le));
953 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200954 }
955 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100956 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200957 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100958 brcmf_dbg(SCAN, "SSID %s len=%d\n",
959 params_le->ssid_le.SSID,
960 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200961 params_le->ssid_le.SSID_len =
962 cpu_to_le32(request->ssids->ssid_len);
963 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
964 request->ssids->ssid_len);
965 }
966 }
967 /* Adding mask to channel numbers */
968 params_le->channel_num =
969 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
970 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
971}
972
973static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200974brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +0200975 struct cfg80211_scan_request *request, u16 action)
976{
977 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
978 offsetof(struct brcmf_escan_params_le, params_le);
979 struct brcmf_escan_params_le *params;
980 s32 err = 0;
981
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100982 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200983
984 if (request != NULL) {
985 /* Allocate space for populating ssids in struct */
986 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
987
988 /* Allocate space for populating ssids in struct */
989 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
990 }
991
992 params = kzalloc(params_size, GFP_KERNEL);
993 if (!params) {
994 err = -ENOMEM;
995 goto exit;
996 }
997 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200998 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200999 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
1000 params->action = cpu_to_le16(action);
1001 params->sync_id = cpu_to_le16(0x1234);
1002
Arend van Spriela0f472a2013-04-05 10:57:49 +02001003 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +02001004 if (err) {
1005 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001006 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001007 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001008 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001009 }
1010
1011 kfree(params);
1012exit:
1013 return err;
1014}
1015
1016static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001017brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +02001018 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001019{
1020 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001021 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001022 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001023 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001024
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001025 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001026 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001027 escan->wiphy = wiphy;
1028 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001029 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001030 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001031 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001032 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001033 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001034 return err;
1035 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001036 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001037 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001038 results->version = 0;
1039 results->count = 0;
1040 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1041
Arend van Spriela0f472a2013-04-05 10:57:49 +02001042 err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001043 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001044 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001045 return err;
1046}
1047
1048static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001049brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001050 struct cfg80211_scan_request *request,
1051 struct cfg80211_ssid *this_ssid)
1052{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001053 struct brcmf_if *ifp = vif->ifp;
1054 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001055 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -08001056 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001057 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001058 bool escan_req;
1059 bool spec_scan;
1060 s32 err;
1061 u32 SSID_len;
1062
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001063 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001064
Arend van Sprielc1179032012-10-22 13:55:33 -07001065 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001066 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001067 return -EAGAIN;
1068 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001069 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001070 brcmf_err("Scanning being aborted: status (%lu)\n",
1071 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001072 return -EAGAIN;
1073 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001074 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1075 brcmf_err("Scanning suppressed: status (%lu)\n",
1076 cfg->scan_status);
1077 return -EAGAIN;
1078 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001079 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001080 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001081 return -EAGAIN;
1082 }
1083
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001084 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001085 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1086 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001087
Hante Meulemane756af52012-09-11 21:18:52 +02001088 escan_req = false;
1089 if (request) {
1090 /* scan bss */
1091 ssids = request->ssids;
1092 escan_req = true;
1093 } else {
1094 /* scan in ibss */
1095 /* we don't do escan in ibss */
1096 ssids = this_ssid;
1097 }
1098
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001099 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001100 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001101 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001102 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001103 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001104 if (err)
1105 goto scan_out;
1106
Arend van Spriela0f472a2013-04-05 10:57:49 +02001107 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001108 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001109 goto scan_out;
1110 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001111 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1112 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001113 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1114 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1115 sr->ssid_le.SSID_len = cpu_to_le32(0);
1116 spec_scan = false;
1117 if (SSID_len) {
1118 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1119 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1120 spec_scan = true;
1121 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001122 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001123
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001124 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001125 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001126 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001127 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001128 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001129 goto scan_out;
1130 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001131 brcmf_scan_config_mpc(ifp, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -07001132 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -07001133 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001134 if (err) {
1135 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001136 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
1137 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001138 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001139 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001140
Daniel Kim5e787f72014-06-21 12:11:18 +02001141 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001142 goto scan_out;
1143 }
1144 }
1145
Hante Meuleman661fa952015-02-06 18:36:47 +01001146 /* Arm scan timeout timer */
1147 mod_timer(&cfg->escan_timeout, jiffies +
1148 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1149
Hante Meulemane756af52012-09-11 21:18:52 +02001150 return 0;
1151
1152scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001153 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001154 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001155 return err;
1156}
1157
Arend van Spriel5b435de2011-10-05 13:19:03 +02001158static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001159brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001160{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001161 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001162 s32 err = 0;
1163
Arend van Sprield96b8012012-12-05 15:26:02 +01001164 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001165 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1166 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001167 return -EIO;
1168
Arend van Spriela0f472a2013-04-05 10:57:49 +02001169 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001170
Arend van Spriel5b435de2011-10-05 13:19:03 +02001171 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001172 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001173
Arend van Sprield96b8012012-12-05 15:26:02 +01001174 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001175 return err;
1176}
1177
1178static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1179{
1180 s32 err = 0;
1181
Arend van Sprielac24be62012-10-22 10:36:23 -07001182 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1183 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001185 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001186
1187 return err;
1188}
1189
1190static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1191{
1192 s32 err = 0;
1193
Arend van Sprielac24be62012-10-22 10:36:23 -07001194 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1195 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001196 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001197 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001198
1199 return err;
1200}
1201
1202static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1203{
1204 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001205 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001206
Arend van Sprielac24be62012-10-22 10:36:23 -07001207 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001208 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001209 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001210 return err;
1211 }
1212 return err;
1213}
1214
1215static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1216{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001217 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1218 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001219 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001220 s32 err = 0;
1221
Arend van Sprield96b8012012-12-05 15:26:02 +01001222 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001223 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001224 return -EIO;
1225
1226 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001227 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1228 cfg->conf->rts_threshold = wiphy->rts_threshold;
1229 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001230 if (!err)
1231 goto done;
1232 }
1233 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001234 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1235 cfg->conf->frag_threshold = wiphy->frag_threshold;
1236 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001237 if (!err)
1238 goto done;
1239 }
1240 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001241 && (cfg->conf->retry_long != wiphy->retry_long)) {
1242 cfg->conf->retry_long = wiphy->retry_long;
1243 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001244 if (!err)
1245 goto done;
1246 }
1247 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001248 && (cfg->conf->retry_short != wiphy->retry_short)) {
1249 cfg->conf->retry_short = wiphy->retry_short;
1250 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001251 if (!err)
1252 goto done;
1253 }
1254
1255done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001256 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001257 return err;
1258}
1259
Arend van Spriel5b435de2011-10-05 13:19:03 +02001260static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1261{
1262 memset(prof, 0, sizeof(*prof));
1263}
1264
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001265static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1266{
1267 u16 reason;
1268
1269 switch (e->event_code) {
1270 case BRCMF_E_DEAUTH:
1271 case BRCMF_E_DEAUTH_IND:
1272 case BRCMF_E_DISASSOC_IND:
1273 reason = e->reason;
1274 break;
1275 case BRCMF_E_LINK:
1276 default:
1277 reason = 0;
1278 break;
1279 }
1280 return reason;
1281}
1282
1283static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001284{
Piotr Haber61730d42013-04-23 12:53:12 +02001285 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001286 s32 err = 0;
1287
Arend van Sprield96b8012012-12-05 15:26:02 +01001288 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001289
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001290 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001291 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001292 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001293 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001294 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001295 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001296 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001297 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001298 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
Johannes Berg80279fb2015-05-22 16:22:20 +02001299 true, GFP_KERNEL);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001300
Arend van Spriel5b435de2011-10-05 13:19:03 +02001301 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001302 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001303 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1304 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001305 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001306}
1307
1308static s32
1309brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1310 struct cfg80211_ibss_params *params)
1311{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001312 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001313 struct brcmf_if *ifp = netdev_priv(ndev);
1314 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001315 struct brcmf_join_params join_params;
1316 size_t join_params_size = 0;
1317 s32 err = 0;
1318 s32 wsec = 0;
1319 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001320 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321
Arend van Sprield96b8012012-12-05 15:26:02 +01001322 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001323 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001324 return -EIO;
1325
1326 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001327 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001328 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001329 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001330 return -EOPNOTSUPP;
1331 }
1332
Arend van Sprielc1179032012-10-22 13:55:33 -07001333 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334
1335 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001336 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337 else
Arend van Spriel16886732012-12-05 15:26:04 +01001338 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001339
Johannes Berg683b6d32012-11-08 21:25:48 +01001340 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001341 brcmf_dbg(CONN, "channel: %d\n",
1342 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001343 else
Arend van Spriel16886732012-12-05 15:26:04 +01001344 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001345
1346 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001347 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001348 else
Arend van Spriel16886732012-12-05 15:26:04 +01001349 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001350
1351 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001352 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001353 else
Arend van Spriel16886732012-12-05 15:26:04 +01001354 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001355
1356 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001357 brcmf_dbg(CONN, "beacon interval: %d\n",
1358 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001359 else
Arend van Spriel16886732012-12-05 15:26:04 +01001360 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001361
1362 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001363 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001364 else
Arend van Spriel16886732012-12-05 15:26:04 +01001365 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001366
1367 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001368 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001369 else
Arend van Spriel16886732012-12-05 15:26:04 +01001370 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001371
1372 /* Configure Privacy for starter */
1373 if (params->privacy)
1374 wsec |= WEP_ENABLED;
1375
Arend van Sprielc1179032012-10-22 13:55:33 -07001376 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001377 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001378 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001379 goto done;
1380 }
1381
1382 /* Configure Beacon Interval for starter */
1383 if (params->beacon_interval)
1384 bcnprd = params->beacon_interval;
1385 else
1386 bcnprd = 100;
1387
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001388 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001389 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001390 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001391 goto done;
1392 }
1393
1394 /* Configure required join parameter */
1395 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1396
1397 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001398 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1399 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1400 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1401 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001402 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001403
1404 /* BSSID */
1405 if (params->bssid) {
1406 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1407 join_params_size = sizeof(join_params.ssid_le) +
1408 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001409 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001410 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001411 eth_broadcast_addr(join_params.params_le.bssid);
1412 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001413 }
1414
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001416 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001417 u32 target_channel;
1418
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001419 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001420 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001421 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001422 if (params->channel_fixed) {
1423 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001424 chanspec = chandef_to_chanspec(&cfg->d11inf,
1425 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001426 join_params.params_le.chanspec_list[0] =
1427 cpu_to_le16(chanspec);
1428 join_params.params_le.chanspec_num = cpu_to_le32(1);
1429 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001430 }
1431
1432 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001433 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001434 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001435 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001436 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001437 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001438 goto done;
1439 }
1440 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001441 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001442
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001443 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001444
1445
Arend van Sprielc1179032012-10-22 13:55:33 -07001446 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001447 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001448 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001449 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001450 goto done;
1451 }
1452
1453done:
1454 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001455 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001456 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001457 return err;
1458}
1459
1460static s32
1461brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1462{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001463 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001464
Arend van Sprield96b8012012-12-05 15:26:02 +01001465 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001466 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001467 return -EIO;
1468
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001469 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001470
Arend van Sprield96b8012012-12-05 15:26:02 +01001471 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001472
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001473 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001474}
1475
1476static s32 brcmf_set_wpa_version(struct net_device *ndev,
1477 struct cfg80211_connect_params *sme)
1478{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001479 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001480 struct brcmf_cfg80211_security *sec;
1481 s32 val = 0;
1482 s32 err = 0;
1483
1484 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1485 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1486 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1487 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1488 else
1489 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001490 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001491 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001492 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001493 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001494 return err;
1495 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001496 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001497 sec->wpa_versions = sme->crypto.wpa_versions;
1498 return err;
1499}
1500
1501static s32 brcmf_set_auth_type(struct net_device *ndev,
1502 struct cfg80211_connect_params *sme)
1503{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001504 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001505 struct brcmf_cfg80211_security *sec;
1506 s32 val = 0;
1507 s32 err = 0;
1508
1509 switch (sme->auth_type) {
1510 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1511 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001512 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001513 break;
1514 case NL80211_AUTHTYPE_SHARED_KEY:
1515 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001516 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001517 break;
1518 case NL80211_AUTHTYPE_AUTOMATIC:
1519 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001520 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001521 break;
1522 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001523 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001524 default:
1525 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001526 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001527 break;
1528 }
1529
Hante Meuleman89286dc2013-02-08 15:53:46 +01001530 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001531 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001532 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001533 return err;
1534 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001535 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001536 sec->auth_type = sme->auth_type;
1537 return err;
1538}
1539
1540static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001541brcmf_set_wsec_mode(struct net_device *ndev,
1542 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001543{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001544 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001545 struct brcmf_cfg80211_security *sec;
1546 s32 pval = 0;
1547 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001548 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001549 s32 err = 0;
1550
1551 if (sme->crypto.n_ciphers_pairwise) {
1552 switch (sme->crypto.ciphers_pairwise[0]) {
1553 case WLAN_CIPHER_SUITE_WEP40:
1554 case WLAN_CIPHER_SUITE_WEP104:
1555 pval = WEP_ENABLED;
1556 break;
1557 case WLAN_CIPHER_SUITE_TKIP:
1558 pval = TKIP_ENABLED;
1559 break;
1560 case WLAN_CIPHER_SUITE_CCMP:
1561 pval = AES_ENABLED;
1562 break;
1563 case WLAN_CIPHER_SUITE_AES_CMAC:
1564 pval = AES_ENABLED;
1565 break;
1566 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001567 brcmf_err("invalid cipher pairwise (%d)\n",
1568 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001569 return -EINVAL;
1570 }
1571 }
1572 if (sme->crypto.cipher_group) {
1573 switch (sme->crypto.cipher_group) {
1574 case WLAN_CIPHER_SUITE_WEP40:
1575 case WLAN_CIPHER_SUITE_WEP104:
1576 gval = WEP_ENABLED;
1577 break;
1578 case WLAN_CIPHER_SUITE_TKIP:
1579 gval = TKIP_ENABLED;
1580 break;
1581 case WLAN_CIPHER_SUITE_CCMP:
1582 gval = AES_ENABLED;
1583 break;
1584 case WLAN_CIPHER_SUITE_AES_CMAC:
1585 gval = AES_ENABLED;
1586 break;
1587 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001588 brcmf_err("invalid cipher group (%d)\n",
1589 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001590 return -EINVAL;
1591 }
1592 }
1593
Arend van Spriel16886732012-12-05 15:26:04 +01001594 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001595 /* In case of privacy, but no security and WPS then simulate */
1596 /* setting AES. WPS-2.0 allows no security */
1597 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1598 sme->privacy)
1599 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001600
1601 if (mfp)
1602 wsec = pval | gval | MFP_CAPABLE;
1603 else
1604 wsec = pval | gval;
1605 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001606 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001607 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001608 return err;
1609 }
1610
Arend van Spriel06bb1232012-09-27 14:17:56 +02001611 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001612 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1613 sec->cipher_group = sme->crypto.cipher_group;
1614
1615 return err;
1616}
1617
1618static s32
1619brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1620{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001621 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001622 struct brcmf_cfg80211_security *sec;
1623 s32 val = 0;
1624 s32 err = 0;
1625
1626 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001627 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1628 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001629 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001630 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001631 return err;
1632 }
1633 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1634 switch (sme->crypto.akm_suites[0]) {
1635 case WLAN_AKM_SUITE_8021X:
1636 val = WPA_AUTH_UNSPECIFIED;
1637 break;
1638 case WLAN_AKM_SUITE_PSK:
1639 val = WPA_AUTH_PSK;
1640 break;
1641 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001642 brcmf_err("invalid cipher group (%d)\n",
1643 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001644 return -EINVAL;
1645 }
1646 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1647 switch (sme->crypto.akm_suites[0]) {
1648 case WLAN_AKM_SUITE_8021X:
1649 val = WPA2_AUTH_UNSPECIFIED;
1650 break;
1651 case WLAN_AKM_SUITE_PSK:
1652 val = WPA2_AUTH_PSK;
1653 break;
1654 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001655 brcmf_err("invalid cipher group (%d)\n",
1656 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001657 return -EINVAL;
1658 }
1659 }
1660
Arend van Spriel16886732012-12-05 15:26:04 +01001661 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001662 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1663 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001664 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001665 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001666 return err;
1667 }
1668 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001669 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001670 sec->wpa_auth = sme->crypto.akm_suites[0];
1671
1672 return err;
1673}
1674
1675static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001676brcmf_set_sharedkey(struct net_device *ndev,
1677 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001678{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001679 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001680 struct brcmf_cfg80211_security *sec;
1681 struct brcmf_wsec_key key;
1682 s32 val;
1683 s32 err = 0;
1684
Arend van Spriel16886732012-12-05 15:26:04 +01001685 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001686
Roland Vossena718e2f2011-10-12 20:51:24 +02001687 if (sme->key_len == 0)
1688 return 0;
1689
Arend van Spriel06bb1232012-09-27 14:17:56 +02001690 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001691 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1692 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001693
1694 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1695 return 0;
1696
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001697 if (!(sec->cipher_pairwise &
1698 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1699 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001700
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001701 memset(&key, 0, sizeof(key));
1702 key.len = (u32) sme->key_len;
1703 key.index = (u32) sme->key_idx;
1704 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001705 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001706 return -EINVAL;
1707 }
1708 memcpy(key.data, sme->key, key.len);
1709 key.flags = BRCMF_PRIMARY_KEY;
1710 switch (sec->cipher_pairwise) {
1711 case WLAN_CIPHER_SUITE_WEP40:
1712 key.algo = CRYPTO_ALGO_WEP1;
1713 break;
1714 case WLAN_CIPHER_SUITE_WEP104:
1715 key.algo = CRYPTO_ALGO_WEP128;
1716 break;
1717 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001718 brcmf_err("Invalid algorithm (%d)\n",
1719 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001720 return -EINVAL;
1721 }
1722 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001723 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1724 key.len, key.index, key.algo);
1725 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001726 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001727 if (err)
1728 return err;
1729
1730 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001731 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001732 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001733 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001734 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001735 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001736 }
1737 return err;
1738}
1739
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001740static
1741enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1742 enum nl80211_auth_type type)
1743{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001744 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1745 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1746 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1747 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001748 }
1749 return type;
1750}
1751
Arend van Spriel5b435de2011-10-05 13:19:03 +02001752static s32
1753brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001754 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001755{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001756 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001757 struct brcmf_if *ifp = netdev_priv(ndev);
1758 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001759 struct ieee80211_channel *chan = sme->channel;
1760 struct brcmf_join_params join_params;
1761 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001762 const struct brcmf_tlv *rsn_ie;
1763 const struct brcmf_vs_tlv *wpa_ie;
1764 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001765 u32 ie_len;
1766 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001767 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001768 s32 err = 0;
1769
Arend van Sprield96b8012012-12-05 15:26:02 +01001770 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001771 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001772 return -EIO;
1773
1774 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001775 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001776 return -EOPNOTSUPP;
1777 }
1778
Hante Meuleman89286dc2013-02-08 15:53:46 +01001779 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1780 /* A normal (non P2P) connection request setup. */
1781 ie = NULL;
1782 ie_len = 0;
1783 /* find the WPA_IE */
1784 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1785 if (wpa_ie) {
1786 ie = wpa_ie;
1787 ie_len = wpa_ie->len + TLV_HDR_LEN;
1788 } else {
1789 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001790 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1791 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001792 WLAN_EID_RSN);
1793 if (rsn_ie) {
1794 ie = rsn_ie;
1795 ie_len = rsn_ie->len + TLV_HDR_LEN;
1796 }
1797 }
1798 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1799 }
1800
1801 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1802 sme->ie, sme->ie_len);
1803 if (err)
1804 brcmf_err("Set Assoc REQ IE Failed\n");
1805 else
1806 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1807
Arend van Sprielc1179032012-10-22 13:55:33 -07001808 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001809
1810 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001811 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001812 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001813 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001814 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1815 cfg->channel, chan->center_freq, chanspec);
1816 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001817 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001818 chanspec = 0;
1819 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001820
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001821 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001822
1823 err = brcmf_set_wpa_version(ndev, sme);
1824 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001825 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001826 goto done;
1827 }
1828
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001829 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001830 err = brcmf_set_auth_type(ndev, sme);
1831 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001832 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001833 goto done;
1834 }
1835
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001836 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001837 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001838 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001839 goto done;
1840 }
1841
1842 err = brcmf_set_key_mgmt(ndev, sme);
1843 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001844 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001845 goto done;
1846 }
1847
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001848 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001849 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001850 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001851 goto done;
1852 }
1853
Hante Meuleman89286dc2013-02-08 15:53:46 +01001854 profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
1855 (u32)sme->ssid_len);
1856 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1857 if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1858 profile->ssid.SSID[profile->ssid.SSID_len] = 0;
1859 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
1860 profile->ssid.SSID_len);
1861 }
1862
1863 /* Join with specific BSSID and cached SSID
1864 * If SSID is zero join based on BSSID only
1865 */
1866 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1867 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1868 if (cfg->channel)
1869 join_params_size += sizeof(u16);
1870 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1871 if (ext_join_params == NULL) {
1872 err = -ENOMEM;
1873 goto done;
1874 }
1875 ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
1876 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
1877 profile->ssid.SSID_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001878
Hante Meuleman89286dc2013-02-08 15:53:46 +01001879 /* Set up join scan parameters */
1880 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001881 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1882
1883 if (sme->bssid)
1884 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1885 else
Joe Perches93803b32015-03-02 19:54:49 -08001886 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001887
1888 if (cfg->channel) {
1889 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1890
1891 ext_join_params->assoc_le.chanspec_list[0] =
1892 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001893 /* Increase dwell time to receive probe response or detect
1894 * beacon from target AP at a noisy air only during connect
1895 * command.
1896 */
1897 ext_join_params->scan_le.active_time =
1898 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1899 ext_join_params->scan_le.passive_time =
1900 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1901 /* To sync with presence period of VSDB GO send probe request
1902 * more frequently. Probe request will be stopped when it gets
1903 * probe response from target AP/GO.
1904 */
1905 ext_join_params->scan_le.nprobes =
1906 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1907 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1908 } else {
1909 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1910 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1911 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001912 }
1913
1914 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1915 join_params_size);
1916 kfree(ext_join_params);
1917 if (!err)
1918 /* This is it. join command worked, we are done */
1919 goto done;
1920
1921 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001922 memset(&join_params, 0, sizeof(join_params));
1923 join_params_size = sizeof(join_params.ssid_le);
1924
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001925 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001926 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001927
Hante Meuleman89286dc2013-02-08 15:53:46 +01001928 if (sme->bssid)
1929 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1930 else
Joe Perches93803b32015-03-02 19:54:49 -08001931 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001932
Hante Meuleman17012612013-02-06 18:40:44 +01001933 if (cfg->channel) {
1934 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1935 join_params.params_le.chanspec_num = cpu_to_le32(1);
1936 join_params_size += sizeof(join_params.params_le);
1937 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001938 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001939 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001940 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001941 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001942
1943done:
1944 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001945 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001946 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001947 return err;
1948}
1949
1950static s32
1951brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1952 u16 reason_code)
1953{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001954 struct brcmf_if *ifp = netdev_priv(ndev);
1955 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001956 struct brcmf_scb_val_le scbval;
1957 s32 err = 0;
1958
Arend van Sprield96b8012012-12-05 15:26:02 +01001959 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001960 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001961 return -EIO;
1962
Arend van Sprielc1179032012-10-22 13:55:33 -07001963 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001964 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001965 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001966
Arend van Spriel06bb1232012-09-27 14:17:56 +02001967 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001968 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001969 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001970 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001972 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001973
Arend van Sprield96b8012012-12-05 15:26:02 +01001974 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001975 return err;
1976}
1977
1978static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001979brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001980 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001981{
1982
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001983 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001984 struct net_device *ndev = cfg_to_ndev(cfg);
1985 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001986 u16 txpwrmw;
1987 s32 err = 0;
1988 s32 disable = 0;
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001989 s32 dbm = MBM_TO_DBM(mbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001990
Arend van Sprield96b8012012-12-05 15:26:02 +01001991 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001992 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001993 return -EIO;
1994
1995 switch (type) {
1996 case NL80211_TX_POWER_AUTOMATIC:
1997 break;
1998 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001999 case NL80211_TX_POWER_FIXED:
2000 if (dbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002001 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002002 err = -EINVAL;
2003 goto done;
2004 }
2005 break;
2006 }
2007 /* Make sure radio is off or on as far as software is concerned */
2008 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07002009 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002010 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002011 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002012
2013 if (dbm > 0xffff)
2014 txpwrmw = 0xffff;
2015 else
2016 txpwrmw = (u16) dbm;
Arend van Sprielac24be62012-10-22 10:36:23 -07002017 err = brcmf_fil_iovar_int_set(ifp, "qtxpower",
2018 (s32)brcmf_mw_to_qdbm(txpwrmw));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002019 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002020 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002021 cfg->conf->tx_power = dbm;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002022
2023done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002024 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002025 return err;
2026}
2027
Johannes Bergc8442112012-10-24 10:17:18 +02002028static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy,
2029 struct wireless_dev *wdev,
2030 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002031{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002032 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002033 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002034 s32 txpwrdbm;
2035 u8 result;
2036 s32 err = 0;
2037
Arend van Sprield96b8012012-12-05 15:26:02 +01002038 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002039 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002040 return -EIO;
2041
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002042 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &txpwrdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002043 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002044 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002045 goto done;
2046 }
2047
2048 result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
Alwin Beukersef6ac172011-10-12 20:51:26 +02002049 *dbm = (s32) brcmf_qdbm_to_mw(result);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002050
2051done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002052 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002053 return err;
2054}
2055
2056static s32
2057brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
2058 u8 key_idx, bool unicast, bool multicast)
2059{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002060 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002061 u32 index;
2062 u32 wsec;
2063 s32 err = 0;
2064
Arend van Sprield96b8012012-12-05 15:26:02 +01002065 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002066 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002067 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002068 return -EIO;
2069
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002070 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002071 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002072 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002073 goto done;
2074 }
2075
2076 if (wsec & WEP_ENABLED) {
2077 /* Just select a new current key */
2078 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002079 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002080 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002081 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002082 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002083 }
2084done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002085 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002086 return err;
2087}
2088
2089static s32
2090brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2091 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2092{
Hante Meuleman992f6062013-04-02 21:06:17 +02002093 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002094 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002095 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002096 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002097
2098 memset(&key, 0, sizeof(key));
2099 key.index = (u32) key_idx;
2100 /* Instead of bcast for ea address for default wep keys,
2101 driver needs it to be Null */
2102 if (!is_multicast_ether_addr(mac_addr))
2103 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2104 key.len = (u32) params->key_len;
2105 /* check for key index change */
2106 if (key.len == 0) {
2107 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002108 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002109 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002110 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002111 } else {
2112 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002113 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002114 return -EINVAL;
2115 }
2116
Arend van Spriel16886732012-12-05 15:26:04 +01002117 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002118 memcpy(key.data, params->key, key.len);
2119
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002120 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002121 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2122 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002123 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2124 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2125 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2126 }
2127
2128 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2129 if (params->seq && params->seq_len == 6) {
2130 /* rx iv */
2131 u8 *ivptr;
2132 ivptr = (u8 *) params->seq;
2133 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2134 (ivptr[3] << 8) | ivptr[2];
2135 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2136 key.iv_initialized = true;
2137 }
2138
2139 switch (params->cipher) {
2140 case WLAN_CIPHER_SUITE_WEP40:
2141 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002142 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002143 break;
2144 case WLAN_CIPHER_SUITE_WEP104:
2145 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002146 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002147 break;
2148 case WLAN_CIPHER_SUITE_TKIP:
2149 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002150 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002151 break;
2152 case WLAN_CIPHER_SUITE_AES_CMAC:
2153 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002154 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002155 break;
2156 case WLAN_CIPHER_SUITE_CCMP:
2157 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002158 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002159 break;
2160 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002161 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002162 return -EINVAL;
2163 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002164 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002165 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002166 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002167 }
2168 return err;
2169}
2170
2171static s32
2172brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2173 u8 key_idx, bool pairwise, const u8 *mac_addr,
2174 struct key_params *params)
2175{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002176 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002177 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002178 s32 val;
2179 s32 wsec;
2180 s32 err = 0;
2181 u8 keybuf[8];
2182
Arend van Sprield96b8012012-12-05 15:26:02 +01002183 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002184 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002185 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002186 return -EIO;
2187
Hante Meuleman118eb302014-12-21 12:43:49 +01002188 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2189 /* we ignore this key index in this case */
2190 brcmf_err("invalid key index (%d)\n", key_idx);
2191 return -EINVAL;
2192 }
2193
Daniel Kim787eb032014-01-29 15:32:23 +01002194 if (mac_addr &&
2195 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2196 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002197 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002198 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2199 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002200
Hante Meuleman118eb302014-12-21 12:43:49 +01002201 key = &ifp->vif->profile.key[key_idx];
2202 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002203
Hante Meuleman118eb302014-12-21 12:43:49 +01002204 if (params->key_len > sizeof(key->data)) {
2205 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002206 err = -EINVAL;
2207 goto done;
2208 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002209 key->len = params->key_len;
2210 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002211
Hante Meuleman118eb302014-12-21 12:43:49 +01002212 memcpy(key->data, params->key, key->len);
2213
2214 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002215 switch (params->cipher) {
2216 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002217 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002218 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002219 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002220 break;
2221 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002222 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002223 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002224 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002225 break;
2226 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002227 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002228 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002229 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2230 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2231 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002232 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002233 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002234 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002235 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236 break;
2237 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002238 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002239 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002240 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002241 break;
2242 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002243 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002244 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002245 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002246 break;
2247 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002248 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002249 err = -EINVAL;
2250 goto done;
2251 }
2252
Hante Meuleman118eb302014-12-21 12:43:49 +01002253 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002254 if (err)
2255 goto done;
2256
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002257 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002258 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002259 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002260 goto done;
2261 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002262 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002263 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002264 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002265 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002266 goto done;
2267 }
2268
Arend van Spriel5b435de2011-10-05 13:19:03 +02002269done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002270 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002271 return err;
2272}
2273
2274static s32
2275brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2276 u8 key_idx, bool pairwise, const u8 *mac_addr)
2277{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002278 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002279 struct brcmf_wsec_key key;
2280 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002281
Arend van Sprield96b8012012-12-05 15:26:02 +01002282 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002283 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002284 return -EIO;
2285
Hante Meuleman118eb302014-12-21 12:43:49 +01002286 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002287 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002288 return -EINVAL;
2289 }
2290
Arend van Spriel5b435de2011-10-05 13:19:03 +02002291 memset(&key, 0, sizeof(key));
2292
2293 key.index = (u32) key_idx;
2294 key.flags = BRCMF_PRIMARY_KEY;
2295 key.algo = CRYPTO_ALGO_OFF;
2296
Arend van Spriel16886732012-12-05 15:26:04 +01002297 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002298
2299 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002300 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002301
Arend van Sprield96b8012012-12-05 15:26:02 +01002302 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002303 return err;
2304}
2305
2306static s32
2307brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2308 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2309 void (*callback) (void *cookie, struct key_params * params))
2310{
2311 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002312 struct brcmf_if *ifp = netdev_priv(ndev);
2313 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002314 struct brcmf_cfg80211_security *sec;
2315 s32 wsec;
2316 s32 err = 0;
2317
Arend van Sprield96b8012012-12-05 15:26:02 +01002318 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002319 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002320 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002321 return -EIO;
2322
2323 memset(&params, 0, sizeof(params));
2324
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002325 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002326 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002327 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002328 /* Ignore this error, may happen during DISASSOC */
2329 err = -EAGAIN;
2330 goto done;
2331 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002332 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002333 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002334 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2335 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002336 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002337 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2338 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002339 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002340 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002341 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002342 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002343 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002344 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002345 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002346 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002347 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002348 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002349 err = -EINVAL;
2350 goto done;
2351 }
2352 callback(cookie, &params);
2353
2354done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002355 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002356 return err;
2357}
2358
2359static s32
2360brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2361 struct net_device *ndev, u8 key_idx)
2362{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002363 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002364
2365 return -EOPNOTSUPP;
2366}
2367
Hante Meuleman118eb302014-12-21 12:43:49 +01002368static void
2369brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2370{
2371 s32 err;
2372 u8 key_idx;
2373 struct brcmf_wsec_key *key;
2374 s32 wsec;
2375
2376 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2377 key = &ifp->vif->profile.key[key_idx];
2378 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2379 (key->algo == CRYPTO_ALGO_WEP128))
2380 break;
2381 }
2382 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2383 return;
2384
2385 err = send_key_to_dongle(ifp, key);
2386 if (err) {
2387 brcmf_err("Setting WEP key failed (%d)\n", err);
2388 return;
2389 }
2390 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2391 if (err) {
2392 brcmf_err("get wsec error (%d)\n", err);
2393 return;
2394 }
2395 wsec |= WEP_ENABLED;
2396 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2397 if (err)
2398 brcmf_err("set wsec error (%d)\n", err);
2399}
2400
Arend van Spriel5b435de2011-10-05 13:19:03 +02002401static s32
2402brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002403 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002404{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002405 struct brcmf_if *ifp = netdev_priv(ndev);
2406 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002407 struct brcmf_scb_val_le scb_val;
2408 int rssi;
2409 s32 rate;
2410 s32 err = 0;
Arend van Spriel06bb1232012-09-27 14:17:56 +02002411 u8 *bssid = profile->bssid;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002412 struct brcmf_sta_info_le sta_info_le;
Hante Meuleman9ee66d12014-01-29 15:32:11 +01002413 u32 beacon_period;
2414 u32 dtim_period;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002415
Arend van Sprield96b8012012-12-05 15:26:02 +01002416 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002417 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002418 return -EIO;
2419
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002420 if (brcmf_is_apmode(ifp->vif)) {
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002421 memcpy(&sta_info_le, mac, ETH_ALEN);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002422 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002423 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002424 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002425 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002426 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002427 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002428 }
Johannes Berg319090b2014-11-17 14:08:11 +01002429 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002430 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2431 if (le32_to_cpu(sta_info_le.flags) & BRCMF_STA_ASSOC) {
Johannes Berg319090b2014-11-17 14:08:11 +01002432 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002433 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
Hante Meuleman1a873342012-09-27 14:17:54 +02002434 }
Arend van Sprield96b8012012-12-05 15:26:02 +01002435 brcmf_dbg(TRACE, "STA idle time : %d ms, connected time :%d sec\n",
2436 sinfo->inactive_time, sinfo->connected_time);
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002437 } else if (ifp->vif->wdev.iftype == NL80211_IFTYPE_STATION) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002438 if (memcmp(mac, bssid, ETH_ALEN)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002439 brcmf_err("Wrong Mac address cfg_mac-%pM wl_bssid-%pM\n",
2440 mac, bssid);
Hante Meuleman1a873342012-09-27 14:17:54 +02002441 err = -ENOENT;
2442 goto done;
2443 }
2444 /* Report the current tx rate */
Hante Meuleman89286dc2013-02-08 15:53:46 +01002445 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
Hante Meuleman1a873342012-09-27 14:17:54 +02002446 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002447 brcmf_err("Could not get rate (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002448 goto done;
2449 } else {
Johannes Berg319090b2014-11-17 14:08:11 +01002450 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman1a873342012-09-27 14:17:54 +02002451 sinfo->txrate.legacy = rate * 5;
Arend van Spriel16886732012-12-05 15:26:04 +01002452 brcmf_dbg(CONN, "Rate %d Mbps\n", rate / 2);
Hante Meuleman1a873342012-09-27 14:17:54 +02002453 }
2454
Arend van Sprielc1179032012-10-22 13:55:33 -07002455 if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
2456 &ifp->vif->sme_state)) {
Hante Meuleman1a873342012-09-27 14:17:54 +02002457 memset(&scb_val, 0, sizeof(scb_val));
Arend van Sprielc1179032012-10-22 13:55:33 -07002458 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
2459 &scb_val, sizeof(scb_val));
Hante Meuleman1a873342012-09-27 14:17:54 +02002460 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002461 brcmf_err("Could not get rssi (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002462 goto done;
2463 } else {
2464 rssi = le32_to_cpu(scb_val.val);
Johannes Berg319090b2014-11-17 14:08:11 +01002465 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
Hante Meuleman1a873342012-09-27 14:17:54 +02002466 sinfo->signal = rssi;
Arend van Spriel16886732012-12-05 15:26:04 +01002467 brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
Hante Meuleman1a873342012-09-27 14:17:54 +02002468 }
Hante Meuleman9ee66d12014-01-29 15:32:11 +01002469 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_BCNPRD,
2470 &beacon_period);
2471 if (err) {
2472 brcmf_err("Could not get beacon period (%d)\n",
2473 err);
2474 goto done;
2475 } else {
2476 sinfo->bss_param.beacon_interval =
2477 beacon_period;
2478 brcmf_dbg(CONN, "Beacon peroid %d\n",
2479 beacon_period);
2480 }
2481 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_DTIMPRD,
2482 &dtim_period);
2483 if (err) {
2484 brcmf_err("Could not get DTIM period (%d)\n",
2485 err);
2486 goto done;
2487 } else {
2488 sinfo->bss_param.dtim_period = dtim_period;
2489 brcmf_dbg(CONN, "DTIM peroid %d\n",
2490 dtim_period);
2491 }
Johannes Berg319090b2014-11-17 14:08:11 +01002492 sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
Hante Meuleman1a873342012-09-27 14:17:54 +02002493 }
2494 } else
2495 err = -EPERM;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002496done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002497 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002498 return err;
2499}
2500
2501static s32
2502brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2503 bool enabled, s32 timeout)
2504{
2505 s32 pm;
2506 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002507 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002508 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002509
Arend van Sprield96b8012012-12-05 15:26:02 +01002510 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002511
2512 /*
2513 * Powersave enable/disable request is coming from the
2514 * cfg80211 even before the interface is up. In that
2515 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002516 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002517 * FW later while initializing the dongle
2518 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002519 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002520 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002521
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002522 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002523 goto done;
2524 }
2525
2526 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002527 /* Do not enable the power save after assoc if it is a p2p interface */
2528 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2529 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2530 pm = PM_OFF;
2531 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002532 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002533
Arend van Sprielc1179032012-10-22 13:55:33 -07002534 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002535 if (err) {
2536 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002537 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002538 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002539 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002540 }
2541done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002542 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002543 return err;
2544}
2545
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002546static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002547 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002548{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002549 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002550 struct ieee80211_channel *notify_channel;
2551 struct cfg80211_bss *bss;
2552 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002553 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002554 u16 channel;
2555 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002556 u16 notify_capability;
2557 u16 notify_interval;
2558 u8 *notify_ie;
2559 size_t notify_ielen;
2560 s32 notify_signal;
2561
2562 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002563 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002564 return 0;
2565 }
2566
Franky Lin83cf17a2013-04-11 13:28:50 +02002567 if (!bi->ctl_ch) {
2568 ch.chspec = le16_to_cpu(bi->chanspec);
2569 cfg->d11inf.decchspec(&ch);
2570 bi->ctl_ch = ch.chnum;
2571 }
2572 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002573
2574 if (channel <= CH_MAX_2G_CHANNEL)
2575 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2576 else
2577 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2578
2579 freq = ieee80211_channel_to_frequency(channel, band->band);
2580 notify_channel = ieee80211_get_channel(wiphy, freq);
2581
Arend van Spriel5b435de2011-10-05 13:19:03 +02002582 notify_capability = le16_to_cpu(bi->capability);
2583 notify_interval = le16_to_cpu(bi->beacon_period);
2584 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2585 notify_ielen = le32_to_cpu(bi->ie_length);
2586 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2587
Arend van Spriel16886732012-12-05 15:26:04 +01002588 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2589 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2590 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2591 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2592 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002593
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002594 bss = cfg80211_inform_bss(wiphy, notify_channel,
2595 CFG80211_BSS_FTYPE_UNKNOWN,
2596 (const u8 *)bi->BSSID,
2597 0, notify_capability,
2598 notify_interval, notify_ie,
2599 notify_ielen, notify_signal,
2600 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002601
Franky Line78946e2011-11-10 20:30:34 +01002602 if (!bss)
2603 return -ENOMEM;
2604
Johannes Berg5b112d32013-02-01 01:49:58 +01002605 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002606
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002607 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002608}
2609
Roland Vossen6f09be02011-10-18 14:03:02 +02002610static struct brcmf_bss_info_le *
2611next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2612{
2613 if (bss == NULL)
2614 return list->bss_info_le;
2615 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2616 le32_to_cpu(bss->length));
2617}
2618
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002619static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002620{
2621 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002622 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002623 s32 err = 0;
2624 int i;
2625
Hante Meulemanef8596e2014-09-30 10:23:13 +02002626 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002627 if (bss_list->count != 0 &&
2628 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002629 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2630 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002631 return -EOPNOTSUPP;
2632 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002633 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002634 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002635 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002636 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002637 if (err)
2638 break;
2639 }
2640 return err;
2641}
2642
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002643static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002644 struct net_device *ndev, const u8 *bssid)
2645{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002646 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002647 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002648 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002649 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002650 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002651 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002652 u8 *buf = NULL;
2653 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002654 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002655 u16 notify_capability;
2656 u16 notify_interval;
2657 u8 *notify_ie;
2658 size_t notify_ielen;
2659 s32 notify_signal;
2660
Arend van Sprield96b8012012-12-05 15:26:02 +01002661 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002662
2663 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2664 if (buf == NULL) {
2665 err = -ENOMEM;
2666 goto CleanUp;
2667 }
2668
2669 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2670
Arend van Sprielac24be62012-10-22 10:36:23 -07002671 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2672 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002673 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002674 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002675 goto CleanUp;
2676 }
2677
Roland Vossend34bf642011-10-18 14:03:01 +02002678 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002679
Franky Lin83cf17a2013-04-11 13:28:50 +02002680 ch.chspec = le16_to_cpu(bi->chanspec);
2681 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002682
Franky Lin83cf17a2013-04-11 13:28:50 +02002683 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002684 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2685 else
2686 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2687
Franky Lin83cf17a2013-04-11 13:28:50 +02002688 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002689 notify_channel = ieee80211_get_channel(wiphy, freq);
2690
Arend van Spriel5b435de2011-10-05 13:19:03 +02002691 notify_capability = le16_to_cpu(bi->capability);
2692 notify_interval = le16_to_cpu(bi->beacon_period);
2693 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2694 notify_ielen = le32_to_cpu(bi->ie_length);
2695 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2696
Franky Lin83cf17a2013-04-11 13:28:50 +02002697 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002698 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2699 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2700 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002701
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002702 bss = cfg80211_inform_bss(wiphy, notify_channel,
2703 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2704 notify_capability, notify_interval,
2705 notify_ie, notify_ielen, notify_signal,
2706 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002707
Franky Line78946e2011-11-10 20:30:34 +01002708 if (!bss) {
2709 err = -ENOMEM;
2710 goto CleanUp;
2711 }
2712
Johannes Berg5b112d32013-02-01 01:49:58 +01002713 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002714
Arend van Spriel5b435de2011-10-05 13:19:03 +02002715CleanUp:
2716
2717 kfree(buf);
2718
Arend van Sprield96b8012012-12-05 15:26:02 +01002719 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002720
2721 return err;
2722}
2723
Hante Meuleman89286dc2013-02-08 15:53:46 +01002724static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2725 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002726{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002727 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002728 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002729 struct brcmf_ssid *ssid;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002730 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002731 u16 beacon_interval;
2732 u8 dtim_period;
2733 size_t ie_len;
2734 u8 *ie;
2735 s32 err = 0;
2736
Arend van Sprield96b8012012-12-05 15:26:02 +01002737 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002738 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002739 return err;
2740
Arend van Spriel06bb1232012-09-27 14:17:56 +02002741 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002742
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002743 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002744 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002745 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002746 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002747 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002748 goto update_bss_info_out;
2749 }
2750
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002751 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2752 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002753 if (err)
2754 goto update_bss_info_out;
2755
2756 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2757 ie_len = le32_to_cpu(bi->ie_length);
2758 beacon_interval = le16_to_cpu(bi->beacon_period);
2759
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002760 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002761 if (tim)
2762 dtim_period = tim->data[1];
2763 else {
2764 /*
2765 * active scan was done so we could not get dtim
2766 * information out of probe response.
2767 * so we speficially query dtim information to dongle.
2768 */
2769 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002770 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002771 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002772 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002773 goto update_bss_info_out;
2774 }
2775 dtim_period = (u8)var;
2776 }
2777
Arend van Spriel5b435de2011-10-05 13:19:03 +02002778update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002779 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002780 return err;
2781}
2782
Hante Meuleman18e2f612013-02-08 15:53:49 +01002783void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002784{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002785 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002786
Arend van Sprielc1179032012-10-22 13:55:33 -07002787 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002788 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002789 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002790 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002791 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002792 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2793 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002794}
2795
Hante Meulemane756af52012-09-11 21:18:52 +02002796static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2797{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002798 struct brcmf_cfg80211_info *cfg =
2799 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002800 escan_timeout_work);
2801
Hante Meulemanef8596e2014-09-30 10:23:13 +02002802 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002803 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002804}
2805
2806static void brcmf_escan_timeout(unsigned long data)
2807{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002808 struct brcmf_cfg80211_info *cfg =
2809 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002810
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002811 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002812 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002813 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002814 }
2815}
2816
2817static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002818brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2819 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002820 struct brcmf_bss_info_le *bss_info_le)
2821{
Franky Lin83cf17a2013-04-11 13:28:50 +02002822 struct brcmu_chan ch_bss, ch_bss_info_le;
2823
2824 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2825 cfg->d11inf.decchspec(&ch_bss);
2826 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2827 cfg->d11inf.decchspec(&ch_bss_info_le);
2828
Hante Meulemane756af52012-09-11 21:18:52 +02002829 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002830 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002831 bss_info_le->SSID_len == bss->SSID_len &&
2832 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002833 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2834 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002835 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2836 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2837
Hante Meulemane756af52012-09-11 21:18:52 +02002838 /* preserve max RSSI if the measurements are
2839 * both on-channel or both off-channel
2840 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002841 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002842 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002843 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2844 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002845 /* preserve the on-channel rssi measurement
2846 * if the new measurement is off channel
2847 */
2848 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002849 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002850 }
2851 return 1;
2852 }
2853 return 0;
2854}
2855
2856static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002857brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002858 const struct brcmf_event_msg *e, void *data)
2859{
Arend van Spriel19937322012-11-05 16:22:32 -08002860 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002861 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002862 struct brcmf_escan_result_le *escan_result_le;
2863 struct brcmf_bss_info_le *bss_info_le;
2864 struct brcmf_bss_info_le *bss = NULL;
2865 u32 bi_length;
2866 struct brcmf_scan_results *list;
2867 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002868 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002869
Arend van Spriel5c36b992012-11-14 18:46:05 -08002870 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002871
Arend van Spriela0f472a2013-04-05 10:57:49 +02002872 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2873 brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002874 return -EPERM;
2875 }
2876
2877 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002878 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002879 escan_result_le = (struct brcmf_escan_result_le *) data;
2880 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002881 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002882 goto exit;
2883 }
Hante Meulemane756af52012-09-11 21:18:52 +02002884 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002885 brcmf_err("Invalid bss_count %d: ignoring\n",
2886 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002887 goto exit;
2888 }
2889 bss_info_le = &escan_result_le->bss_info_le;
2890
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002891 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2892 goto exit;
2893
2894 if (!cfg->scan_request) {
2895 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2896 goto exit;
2897 }
2898
Hante Meulemane756af52012-09-11 21:18:52 +02002899 bi_length = le32_to_cpu(bss_info_le->length);
2900 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2901 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002902 brcmf_err("Invalid bss_info length %d: ignoring\n",
2903 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002904 goto exit;
2905 }
2906
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002907 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002908 BIT(NL80211_IFTYPE_ADHOC))) {
2909 if (le16_to_cpu(bss_info_le->capability) &
2910 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002911 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002912 goto exit;
2913 }
2914 }
2915
2916 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002917 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002918 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002919 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002920 goto exit;
2921 }
2922
2923 for (i = 0; i < list->count; i++) {
2924 bss = bss ? (struct brcmf_bss_info_le *)
2925 ((unsigned char *)bss +
2926 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002927 if (brcmf_compare_update_same_bss(cfg, bss,
2928 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002929 goto exit;
2930 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002931 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002932 bss_info_le, bi_length);
2933 list->version = le32_to_cpu(bss_info_le->version);
2934 list->buflen += bi_length;
2935 list->count++;
2936 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002937 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002938 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2939 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002940 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002941 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002942 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02002943 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02002944 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002945 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2946 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002947 }
2948exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002949 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02002950}
2951
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002952static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002953{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002954 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
2955 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08002956 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2957 /* Init scan_timeout timer */
2958 init_timer(&cfg->escan_timeout);
2959 cfg->escan_timeout.data = (unsigned long) cfg;
2960 cfg->escan_timeout.function = brcmf_escan_timeout;
2961 INIT_WORK(&cfg->escan_timeout_work,
2962 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002963}
2964
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002965static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002966{
2967 if (ms < 1000 / HZ) {
2968 cond_resched();
2969 mdelay(ms);
2970 } else {
2971 msleep(ms);
2972 }
2973}
2974
Hante Meulemanb9a82f82014-10-28 14:56:06 +01002975static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
2976 u8 *pattern, u32 patternsize, u8 *mask,
2977 u32 packet_offset)
2978{
2979 struct brcmf_fil_wowl_pattern_le *filter;
2980 u32 masksize;
2981 u32 patternoffset;
2982 u8 *buf;
2983 u32 bufsize;
2984 s32 ret;
2985
2986 masksize = (patternsize + 7) / 8;
2987 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
2988
2989 bufsize = sizeof(*filter) + patternsize + masksize;
2990 buf = kzalloc(bufsize, GFP_KERNEL);
2991 if (!buf)
2992 return -ENOMEM;
2993 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
2994
2995 memcpy(filter->cmd, cmd, 4);
2996 filter->masksize = cpu_to_le32(masksize);
2997 filter->offset = cpu_to_le32(packet_offset);
2998 filter->patternoffset = cpu_to_le32(patternoffset);
2999 filter->patternsize = cpu_to_le32(patternsize);
3000 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3001
3002 if ((mask) && (masksize))
3003 memcpy(buf + sizeof(*filter), mask, masksize);
3004 if ((pattern) && (patternsize))
3005 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3006
3007 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3008
3009 kfree(buf);
3010 return ret;
3011}
3012
Arend van Spriel5b435de2011-10-05 13:19:03 +02003013static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3014{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003015 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3016 struct net_device *ndev = cfg_to_ndev(cfg);
3017 struct brcmf_if *ifp = netdev_priv(ndev);
3018
Arend van Sprield96b8012012-12-05 15:26:02 +01003019 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003020
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003021 if (cfg->wowl_enabled) {
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003022 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003023 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3024 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003025 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003026 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003027 cfg->wowl_enabled = false;
3028 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003029 return 0;
3030}
3031
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003032static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3033 struct brcmf_if *ifp,
3034 struct cfg80211_wowlan *wowl)
3035{
3036 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003037 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003038
3039 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3040
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003041 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003042 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3043 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3044
3045 wowl_config = 0;
3046 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003047 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003048 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003049 wowl_config |= BRCMF_WOWL_MAGIC;
3050 if ((wowl->patterns) && (wowl->n_patterns)) {
3051 wowl_config |= BRCMF_WOWL_NET;
3052 for (i = 0; i < wowl->n_patterns; i++) {
3053 brcmf_config_wowl_pattern(ifp, "add",
3054 (u8 *)wowl->patterns[i].pattern,
3055 wowl->patterns[i].pattern_len,
3056 (u8 *)wowl->patterns[i].mask,
3057 wowl->patterns[i].pkt_offset);
3058 }
3059 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003060 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3061 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3062 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3063 cfg->wowl_enabled = true;
3064}
3065
Arend van Spriel5b435de2011-10-05 13:19:03 +02003066static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003067 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003068{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003069 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3070 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003071 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003072 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003073
Arend van Sprield96b8012012-12-05 15:26:02 +01003074 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003075
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003076 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003077 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003078 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003079 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003080 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003081
Arend van Spriel7d641072012-10-22 13:55:39 -07003082 /* end any scanning */
3083 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003084 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003085
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003086 if (wowl == NULL) {
3087 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3088 list_for_each_entry(vif, &cfg->vif_list, list) {
3089 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3090 continue;
3091 /* While going to suspend if associated with AP
3092 * disassociate from AP to save power while system is
3093 * in suspended state
3094 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003095 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003096 /* Make sure WPA_Supplicant receives all the event
3097 * generated due to DISASSOC call to the fw to keep
3098 * the state fw and WPA_Supplicant state consistent
3099 */
3100 brcmf_delay(500);
3101 }
3102 /* Configure MPC */
3103 brcmf_set_mpc(ifp, 1);
3104
3105 } else {
3106 /* Configure WOWL paramaters */
3107 brcmf_configure_wowl(cfg, ifp, wowl);
3108 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003109
Arend van Spriel7d641072012-10-22 13:55:39 -07003110exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003111 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003112 /* clear any scanning activity */
3113 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003114 return 0;
3115}
3116
3117static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003118brcmf_update_pmklist(struct net_device *ndev,
3119 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3120{
3121 int i, j;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003122 u32 pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003123
Arend van Spriel40c8e952011-10-12 20:51:20 +02003124 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3125
Arend van Spriel16886732012-12-05 15:26:04 +01003126 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003127 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01003128 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
3129 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003130 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01003131 brcmf_dbg(CONN, "%02x\n",
3132 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003133 }
3134
3135 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07003136 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
3137 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003138
3139 return err;
3140}
3141
3142static s32
3143brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3144 struct cfg80211_pmksa *pmksa)
3145{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003146 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003147 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003148 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003149 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003150 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003151
Arend van Sprield96b8012012-12-05 15:26:02 +01003152 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003153 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003154 return -EIO;
3155
Arend van Spriel40c8e952011-10-12 20:51:20 +02003156 pmkid_len = le32_to_cpu(pmkids->npmkid);
3157 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003158 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3159 break;
3160 if (i < WL_NUM_PMKIDS_MAX) {
3161 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3162 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003163 if (i == pmkid_len) {
3164 pmkid_len++;
3165 pmkids->npmkid = cpu_to_le32(pmkid_len);
3166 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003167 } else
3168 err = -EINVAL;
3169
Arend van Spriel16886732012-12-05 15:26:04 +01003170 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
3171 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003172 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003173 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003174
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003175 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003176
Arend van Sprield96b8012012-12-05 15:26:02 +01003177 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003178 return err;
3179}
3180
3181static s32
3182brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3183 struct cfg80211_pmksa *pmksa)
3184{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003185 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003186 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003187 struct pmkid_list pmkid;
3188 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003189 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003190
Arend van Sprield96b8012012-12-05 15:26:02 +01003191 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003192 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003193 return -EIO;
3194
3195 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3196 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3197
Arend van Spriel16886732012-12-05 15:26:04 +01003198 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3199 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003200 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003201 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003202
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003203 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003204 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003205 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003206 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003207 ETH_ALEN))
3208 break;
3209
Arend van Spriel40c8e952011-10-12 20:51:20 +02003210 if ((pmkid_len > 0)
3211 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003212 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003213 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003214 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003215 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3216 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003217 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003218 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3219 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003220 WLAN_PMKID_LEN);
3221 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003222 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003223 } else
3224 err = -EINVAL;
3225
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003226 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003227
Arend van Sprield96b8012012-12-05 15:26:02 +01003228 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003229 return err;
3230
3231}
3232
3233static s32
3234brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3235{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003236 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003237 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003238 s32 err = 0;
3239
Arend van Sprield96b8012012-12-05 15:26:02 +01003240 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003241 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003242 return -EIO;
3243
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003244 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3245 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003246
Arend van Sprield96b8012012-12-05 15:26:02 +01003247 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003248 return err;
3249
3250}
3251
Arend van Spriele5806072012-09-19 22:21:08 +02003252/*
3253 * PFN result doesn't have all the info which are
3254 * required by the supplicant
3255 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3256 * via wl_inform_single_bss in the required format. Escan does require the
3257 * scan request in the form of cfg80211_scan_request. For timebeing, create
3258 * cfg80211_scan_request one out of the received PNO event.
3259 */
3260static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003261brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003262 const struct brcmf_event_msg *e, void *data)
3263{
Arend van Spriel19937322012-11-05 16:22:32 -08003264 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003265 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3266 struct cfg80211_scan_request *request = NULL;
3267 struct cfg80211_ssid *ssid = NULL;
3268 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003269 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003270 int err = 0;
3271 int channel_req = 0;
3272 int band = 0;
3273 struct brcmf_pno_scanresults_le *pfn_result;
3274 u32 result_count;
3275 u32 status;
3276
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003277 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003278
Arend van Spriel5c36b992012-11-14 18:46:05 -08003279 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003280 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003281 return 0;
3282 }
3283
3284 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3285 result_count = le32_to_cpu(pfn_result->count);
3286 status = le32_to_cpu(pfn_result->status);
3287
3288 /*
3289 * PFN event is limited to fit 512 bytes so we may get
3290 * multiple NET_FOUND events. For now place a warning here.
3291 */
3292 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003293 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003294 if (result_count > 0) {
3295 int i;
3296
3297 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003298 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3299 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003300 if (!request || !ssid || !channel) {
3301 err = -ENOMEM;
3302 goto out_err;
3303 }
3304
3305 request->wiphy = wiphy;
3306 data += sizeof(struct brcmf_pno_scanresults_le);
3307 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3308
3309 for (i = 0; i < result_count; i++) {
3310 netinfo = &netinfo_start[i];
3311 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003312 brcmf_err("Invalid netinfo ptr. index: %d\n",
3313 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003314 err = -EINVAL;
3315 goto out_err;
3316 }
3317
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003318 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3319 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003320 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3321 ssid[i].ssid_len = netinfo->SSID_len;
3322 request->n_ssids++;
3323
3324 channel_req = netinfo->channel;
3325 if (channel_req <= CH_MAX_2G_CHANNEL)
3326 band = NL80211_BAND_2GHZ;
3327 else
3328 band = NL80211_BAND_5GHZ;
3329 channel[i].center_freq =
3330 ieee80211_channel_to_frequency(channel_req,
3331 band);
3332 channel[i].band = band;
3333 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3334 request->channels[i] = &channel[i];
3335 request->n_channels++;
3336 }
3337
3338 /* assign parsed ssid array */
3339 if (request->n_ssids)
3340 request->ssids = &ssid[0];
3341
Arend van Sprielc1179032012-10-22 13:55:33 -07003342 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003343 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003344 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003345 }
3346
Arend van Sprielc1179032012-10-22 13:55:33 -07003347 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003348 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003349 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003350 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003351 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003352 goto out_err;
3353 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003354 cfg->sched_escan = true;
3355 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003356 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003357 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003358 goto out_err;
3359 }
3360
3361 kfree(ssid);
3362 kfree(channel);
3363 kfree(request);
3364 return 0;
3365
3366out_err:
3367 kfree(ssid);
3368 kfree(channel);
3369 kfree(request);
3370 cfg80211_sched_scan_stopped(wiphy);
3371 return err;
3372}
3373
Arend van Spriele5806072012-09-19 22:21:08 +02003374static int brcmf_dev_pno_clean(struct net_device *ndev)
3375{
Arend van Spriele5806072012-09-19 22:21:08 +02003376 int ret;
3377
3378 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003379 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003380 if (ret == 0) {
3381 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003382 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3383 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003384 }
3385 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003386 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003387
3388 return ret;
3389}
3390
3391static int brcmf_dev_pno_config(struct net_device *ndev)
3392{
3393 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003394
3395 memset(&pfn_param, 0, sizeof(pfn_param));
3396 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3397
3398 /* set extra pno params */
3399 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3400 pfn_param.repeat = BRCMF_PNO_REPEAT;
3401 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3402
3403 /* set up pno scan fr */
3404 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3405
Arend van Sprielac24be62012-10-22 10:36:23 -07003406 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3407 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003408}
3409
3410static int
3411brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3412 struct net_device *ndev,
3413 struct cfg80211_sched_scan_request *request)
3414{
Arend van Sprielc1179032012-10-22 13:55:33 -07003415 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003416 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003417 struct brcmf_pno_net_param_le pfn;
3418 int i;
3419 int ret = 0;
3420
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003421 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003422 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003423 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003424 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003425 return -EAGAIN;
3426 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003427 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3428 brcmf_err("Scanning suppressed: status (%lu)\n",
3429 cfg->scan_status);
3430 return -EAGAIN;
3431 }
Arend van Spriele5806072012-09-19 22:21:08 +02003432
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003433 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003434 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003435 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003436 return -EINVAL;
3437 }
3438
3439 if (request->n_ssids > 0) {
3440 for (i = 0; i < request->n_ssids; i++) {
3441 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003442 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3443 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003444
3445 /*
3446 * match_set ssids is a supert set of n_ssid list,
3447 * so we need not add these set seperately.
3448 */
3449 }
3450 }
3451
3452 if (request->n_match_sets > 0) {
3453 /* clean up everything */
3454 ret = brcmf_dev_pno_clean(ndev);
3455 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003456 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003457 return ret;
3458 }
3459
3460 /* configure pno */
3461 ret = brcmf_dev_pno_config(ndev);
3462 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003463 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003464 return -EINVAL;
3465 }
3466
3467 /* configure each match set */
3468 for (i = 0; i < request->n_match_sets; i++) {
3469 struct cfg80211_ssid *ssid;
3470 u32 ssid_len;
3471
3472 ssid = &request->match_sets[i].ssid;
3473 ssid_len = ssid->ssid_len;
3474
3475 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003476 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003477 continue;
3478 }
3479 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3480 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3481 pfn.wsec = cpu_to_le32(0);
3482 pfn.infra = cpu_to_le32(1);
3483 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3484 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3485 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003486 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003487 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003488 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3489 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003490 }
3491 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003492 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003493 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003494 return -EINVAL;
3495 }
3496 } else {
3497 return -EINVAL;
3498 }
3499
3500 return 0;
3501}
3502
3503static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3504 struct net_device *ndev)
3505{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003506 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003507
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003508 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003509 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003510 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003511 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003512 return 0;
3513}
Arend van Spriele5806072012-09-19 22:21:08 +02003514
Hante Meuleman1f170112013-02-06 18:40:38 +01003515static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003516{
3517 s32 err;
3518
3519 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003520 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003521 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003522 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003523 return err;
3524 }
3525 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003526 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003527 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003528 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003529 return err;
3530 }
3531 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003532 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003533 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003534 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003535 return err;
3536 }
3537
3538 return 0;
3539}
3540
3541static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3542{
3543 if (is_rsn_ie)
3544 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3545
3546 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3547}
3548
3549static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003550brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003551 const struct brcmf_vs_tlv *wpa_ie,
3552 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003553{
3554 u32 auth = 0; /* d11 open authentication */
3555 u16 count;
3556 s32 err = 0;
3557 s32 len = 0;
3558 u32 i;
3559 u32 wsec;
3560 u32 pval = 0;
3561 u32 gval = 0;
3562 u32 wpa_auth = 0;
3563 u32 offset;
3564 u8 *data;
3565 u16 rsn_cap;
3566 u32 wme_bss_disable;
3567
Arend van Sprield96b8012012-12-05 15:26:02 +01003568 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003569 if (wpa_ie == NULL)
3570 goto exit;
3571
3572 len = wpa_ie->len + TLV_HDR_LEN;
3573 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003574 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003575 if (!is_rsn_ie)
3576 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003577 else
3578 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003579
3580 /* check for multicast cipher suite */
3581 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3582 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003583 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003584 goto exit;
3585 }
3586
3587 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3588 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003589 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003590 goto exit;
3591 }
3592 offset += TLV_OUI_LEN;
3593
3594 /* pick up multicast cipher */
3595 switch (data[offset]) {
3596 case WPA_CIPHER_NONE:
3597 gval = 0;
3598 break;
3599 case WPA_CIPHER_WEP_40:
3600 case WPA_CIPHER_WEP_104:
3601 gval = WEP_ENABLED;
3602 break;
3603 case WPA_CIPHER_TKIP:
3604 gval = TKIP_ENABLED;
3605 break;
3606 case WPA_CIPHER_AES_CCM:
3607 gval = AES_ENABLED;
3608 break;
3609 default:
3610 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003611 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003612 goto exit;
3613 }
3614
3615 offset++;
3616 /* walk thru unicast cipher list and pick up what we recognize */
3617 count = data[offset] + (data[offset + 1] << 8);
3618 offset += WPA_IE_SUITE_COUNT_LEN;
3619 /* Check for unicast suite(s) */
3620 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3621 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003622 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003623 goto exit;
3624 }
3625 for (i = 0; i < count; i++) {
3626 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3627 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003628 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003629 goto exit;
3630 }
3631 offset += TLV_OUI_LEN;
3632 switch (data[offset]) {
3633 case WPA_CIPHER_NONE:
3634 break;
3635 case WPA_CIPHER_WEP_40:
3636 case WPA_CIPHER_WEP_104:
3637 pval |= WEP_ENABLED;
3638 break;
3639 case WPA_CIPHER_TKIP:
3640 pval |= TKIP_ENABLED;
3641 break;
3642 case WPA_CIPHER_AES_CCM:
3643 pval |= AES_ENABLED;
3644 break;
3645 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003646 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003647 }
3648 offset++;
3649 }
3650 /* walk thru auth management suite list and pick up what we recognize */
3651 count = data[offset] + (data[offset + 1] << 8);
3652 offset += WPA_IE_SUITE_COUNT_LEN;
3653 /* Check for auth key management suite(s) */
3654 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3655 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003656 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003657 goto exit;
3658 }
3659 for (i = 0; i < count; i++) {
3660 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3661 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003662 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003663 goto exit;
3664 }
3665 offset += TLV_OUI_LEN;
3666 switch (data[offset]) {
3667 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003668 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003669 wpa_auth |= WPA_AUTH_NONE;
3670 break;
3671 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003672 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003673 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3674 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3675 break;
3676 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003677 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003678 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3679 (wpa_auth |= WPA_AUTH_PSK);
3680 break;
3681 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003682 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003683 }
3684 offset++;
3685 }
3686
3687 if (is_rsn_ie) {
3688 wme_bss_disable = 1;
3689 if ((offset + RSN_CAP_LEN) <= len) {
3690 rsn_cap = data[offset] + (data[offset + 1] << 8);
3691 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3692 wme_bss_disable = 0;
3693 }
3694 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003695 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003696 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003697 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003698 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003699 goto exit;
3700 }
3701 }
3702 /* FOR WPS , set SES_OW_ENABLED */
3703 wsec = (pval | gval | SES_OW_ENABLED);
3704
3705 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003706 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003707 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003708 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003709 goto exit;
3710 }
3711 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003712 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003713 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003714 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003715 goto exit;
3716 }
3717 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003718 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003719 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003720 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003721 goto exit;
3722 }
3723
3724exit:
3725 return err;
3726}
3727
3728static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003729brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003730 struct parsed_vndr_ies *vndr_ies)
3731{
Hante Meuleman1a873342012-09-27 14:17:54 +02003732 struct brcmf_vs_tlv *vndrie;
3733 struct brcmf_tlv *ie;
3734 struct parsed_vndr_ie_info *parsed_info;
3735 s32 remaining_len;
3736
3737 remaining_len = (s32)vndr_ie_len;
3738 memset(vndr_ies, 0, sizeof(*vndr_ies));
3739
3740 ie = (struct brcmf_tlv *)vndr_ie_buf;
3741 while (ie) {
3742 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3743 goto next;
3744 vndrie = (struct brcmf_vs_tlv *)ie;
3745 /* len should be bigger than OUI length + one */
3746 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003747 brcmf_err("invalid vndr ie. length is too small %d\n",
3748 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003749 goto next;
3750 }
3751 /* if wpa or wme ie, do not add ie */
3752 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3753 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3754 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003755 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003756 goto next;
3757 }
3758
3759 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3760
3761 /* save vndr ie information */
3762 parsed_info->ie_ptr = (char *)vndrie;
3763 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3764 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3765
3766 vndr_ies->count++;
3767
Arend van Sprield96b8012012-12-05 15:26:02 +01003768 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3769 parsed_info->vndrie.oui[0],
3770 parsed_info->vndrie.oui[1],
3771 parsed_info->vndrie.oui[2],
3772 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003773
Arend van Spriel9f440b72013-02-08 15:53:36 +01003774 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003775 break;
3776next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003777 remaining_len -= (ie->len + TLV_HDR_LEN);
3778 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003779 ie = NULL;
3780 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003781 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3782 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003783 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003784 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003785}
3786
3787static u32
3788brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3789{
3790
Hante Meuleman1a873342012-09-27 14:17:54 +02003791 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3792 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3793
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303794 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003795
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303796 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003797
3798 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3799
3800 return ie_len + VNDR_IE_HDR_SIZE;
3801}
3802
Arend van Spriel1332e262012-11-05 16:22:18 -08003803s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3804 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003805{
Arend van Spriel1332e262012-11-05 16:22:18 -08003806 struct brcmf_if *ifp;
3807 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003808 s32 err = 0;
3809 u8 *iovar_ie_buf;
3810 u8 *curr_ie_buf;
3811 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003812 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003813 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003814 u32 del_add_ie_buf_len = 0;
3815 u32 total_ie_buf_len = 0;
3816 u32 parsed_ie_buf_len = 0;
3817 struct parsed_vndr_ies old_vndr_ies;
3818 struct parsed_vndr_ies new_vndr_ies;
3819 struct parsed_vndr_ie_info *vndrie_info;
3820 s32 i;
3821 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003822 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003823
Arend van Spriel1332e262012-11-05 16:22:18 -08003824 if (!vif)
3825 return -ENODEV;
3826 ifp = vif->ifp;
3827 saved_ie = &vif->saved_ie;
3828
Arend van Sprield96b8012012-12-05 15:26:02 +01003829 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003830 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3831 if (!iovar_ie_buf)
3832 return -ENOMEM;
3833 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003834 switch (pktflag) {
3835 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3836 mgmt_ie_buf = saved_ie->probe_req_ie;
3837 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3838 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3839 break;
3840 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3841 mgmt_ie_buf = saved_ie->probe_res_ie;
3842 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3843 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3844 break;
3845 case BRCMF_VNDR_IE_BEACON_FLAG:
3846 mgmt_ie_buf = saved_ie->beacon_ie;
3847 mgmt_ie_len = &saved_ie->beacon_ie_len;
3848 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3849 break;
3850 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3851 mgmt_ie_buf = saved_ie->assoc_req_ie;
3852 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3853 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3854 break;
3855 default:
3856 err = -EPERM;
3857 brcmf_err("not suitable type\n");
3858 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003859 }
3860
3861 if (vndr_ie_len > mgmt_ie_buf_len) {
3862 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003863 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003864 goto exit;
3865 }
3866
3867 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3868 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3869 ptr = curr_ie_buf;
3870 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3871 for (i = 0; i < new_vndr_ies.count; i++) {
3872 vndrie_info = &new_vndr_ies.ie_info[i];
3873 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3874 vndrie_info->ie_len);
3875 parsed_ie_buf_len += vndrie_info->ie_len;
3876 }
3877 }
3878
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003879 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003880 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3881 (memcmp(mgmt_ie_buf, curr_ie_buf,
3882 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003883 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003884 goto exit;
3885 }
3886
3887 /* parse old vndr_ie */
3888 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3889
3890 /* make a command to delete old ie */
3891 for (i = 0; i < old_vndr_ies.count; i++) {
3892 vndrie_info = &old_vndr_ies.ie_info[i];
3893
Arend van Sprield96b8012012-12-05 15:26:02 +01003894 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3895 vndrie_info->vndrie.id,
3896 vndrie_info->vndrie.len,
3897 vndrie_info->vndrie.oui[0],
3898 vndrie_info->vndrie.oui[1],
3899 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003900
3901 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3902 vndrie_info->ie_ptr,
3903 vndrie_info->ie_len,
3904 "del");
3905 curr_ie_buf += del_add_ie_buf_len;
3906 total_ie_buf_len += del_add_ie_buf_len;
3907 }
3908 }
3909
3910 *mgmt_ie_len = 0;
3911 /* Add if there is any extra IE */
3912 if (mgmt_ie_buf && parsed_ie_buf_len) {
3913 ptr = mgmt_ie_buf;
3914
3915 remained_buf_len = mgmt_ie_buf_len;
3916
3917 /* make a command to add new ie */
3918 for (i = 0; i < new_vndr_ies.count; i++) {
3919 vndrie_info = &new_vndr_ies.ie_info[i];
3920
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003921 /* verify remained buf size before copy data */
3922 if (remained_buf_len < (vndrie_info->vndrie.len +
3923 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003924 brcmf_err("no space in mgmt_ie_buf: len left %d",
3925 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003926 break;
3927 }
3928 remained_buf_len -= (vndrie_info->ie_len +
3929 VNDR_IE_VSIE_OFFSET);
3930
Arend van Sprield96b8012012-12-05 15:26:02 +01003931 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3932 vndrie_info->vndrie.id,
3933 vndrie_info->vndrie.len,
3934 vndrie_info->vndrie.oui[0],
3935 vndrie_info->vndrie.oui[1],
3936 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003937
3938 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3939 vndrie_info->ie_ptr,
3940 vndrie_info->ie_len,
3941 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003942
3943 /* save the parsed IE in wl struct */
3944 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3945 vndrie_info->ie_len);
3946 *mgmt_ie_len += vndrie_info->ie_len;
3947
3948 curr_ie_buf += del_add_ie_buf_len;
3949 total_ie_buf_len += del_add_ie_buf_len;
3950 }
3951 }
3952 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003953 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003954 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003955 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003956 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003957 }
3958
3959exit:
3960 kfree(iovar_ie_buf);
3961 return err;
3962}
3963
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01003964s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
3965{
3966 s32 pktflags[] = {
3967 BRCMF_VNDR_IE_PRBREQ_FLAG,
3968 BRCMF_VNDR_IE_PRBRSP_FLAG,
3969 BRCMF_VNDR_IE_BEACON_FLAG
3970 };
3971 int i;
3972
3973 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
3974 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
3975
3976 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
3977 return 0;
3978}
3979
Hante Meuleman1a873342012-09-27 14:17:54 +02003980static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01003981brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
3982 struct cfg80211_beacon_data *beacon)
3983{
3984 s32 err;
3985
3986 /* Set Beacon IEs to FW */
3987 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
3988 beacon->tail, beacon->tail_len);
3989 if (err) {
3990 brcmf_err("Set Beacon IE Failed\n");
3991 return err;
3992 }
3993 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
3994
3995 /* Set Probe Response IEs to FW */
3996 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
3997 beacon->proberesp_ies,
3998 beacon->proberesp_ies_len);
3999 if (err)
4000 brcmf_err("Set Probe Resp IE Failed\n");
4001 else
4002 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4003
4004 return err;
4005}
4006
4007static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004008brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4009 struct cfg80211_ap_settings *settings)
4010{
4011 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004012 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004013 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004014 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004015 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004016 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004017 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004018 const struct brcmf_tlv *rsn_ie;
4019 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004020 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004021 enum nl80211_iftype dev_role;
4022 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004023 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004024 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004025 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004026
Arend van Spriel06c01582014-05-12 10:47:37 +02004027 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4028 settings->chandef.chan->hw_value,
4029 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004030 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004031 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4032 settings->ssid, settings->ssid_len, settings->auth_type,
4033 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004034 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004035 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004036
Arend van Spriel98027762014-12-21 12:43:53 +01004037 /* store current 11d setting */
4038 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4039 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4040 settings->beacon.tail_len,
4041 WLAN_EID_COUNTRY);
4042 is_11d = country_ie ? 1 : 0;
4043
Hante Meuleman1a873342012-09-27 14:17:54 +02004044 memset(&ssid_le, 0, sizeof(ssid_le));
4045 if (settings->ssid == NULL || settings->ssid_len == 0) {
4046 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4047 ssid_ie = brcmf_parse_tlvs(
4048 (u8 *)&settings->beacon.head[ie_offset],
4049 settings->beacon.head_len - ie_offset,
4050 WLAN_EID_SSID);
4051 if (!ssid_ie)
4052 return -EINVAL;
4053
4054 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4055 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004056 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004057 } else {
4058 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4059 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4060 }
4061
Hante Meulemana44aa402014-12-03 21:05:33 +01004062 if (!mbss) {
4063 brcmf_set_mpc(ifp, 0);
4064 brcmf_configure_arp_offload(ifp, false);
4065 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004066
4067 /* find the RSN_IE */
4068 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4069 settings->beacon.tail_len, WLAN_EID_RSN);
4070
4071 /* find the WPA_IE */
4072 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4073 settings->beacon.tail_len);
4074
Hante Meuleman1a873342012-09-27 14:17:54 +02004075 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004076 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004077 if (wpa_ie != NULL) {
4078 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004079 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004080 if (err < 0)
4081 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004082 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004083 struct brcmf_vs_tlv *tmp_ie;
4084
4085 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4086
Hante Meuleman1a873342012-09-27 14:17:54 +02004087 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004088 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004089 if (err < 0)
4090 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004091 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004092 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004093 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004094 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004095 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004096
Hante Meulemana0f07952013-02-08 15:53:47 +01004097 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004098
Hante Meulemana44aa402014-12-03 21:05:33 +01004099 if (!mbss) {
4100 chanspec = chandef_to_chanspec(&cfg->d11inf,
4101 &settings->chandef);
4102 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004103 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004104 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4105 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004106 goto exit;
4107 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004108
Arend van Spriel98027762014-12-21 12:43:53 +01004109 if (is_11d != ifp->vif->is_11d) {
4110 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4111 is_11d);
4112 if (err < 0) {
4113 brcmf_err("Regulatory Set Error, %d\n", err);
4114 goto exit;
4115 }
4116 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004117 if (settings->beacon_interval) {
4118 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4119 settings->beacon_interval);
4120 if (err < 0) {
4121 brcmf_err("Beacon Interval Set Error, %d\n",
4122 err);
4123 goto exit;
4124 }
4125 }
4126 if (settings->dtim_period) {
4127 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4128 settings->dtim_period);
4129 if (err < 0) {
4130 brcmf_err("DTIM Interval Set Error, %d\n", err);
4131 goto exit;
4132 }
4133 }
4134
4135 if (dev_role == NL80211_IFTYPE_AP) {
4136 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4137 if (err < 0) {
4138 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4139 goto exit;
4140 }
4141 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4142 }
4143
4144 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004145 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004146 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004147 goto exit;
4148 }
Arend van Spriel98027762014-12-21 12:43:53 +01004149 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4150 /* Multiple-BSS should use same 11d configuration */
4151 err = -EINVAL;
4152 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004153 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004154 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004155 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4156 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4157
Hante Meulemana0f07952013-02-08 15:53:47 +01004158 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4159 if (err < 0) {
4160 brcmf_err("setting AP mode failed %d\n", err);
4161 goto exit;
4162 }
4163 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4164 if (err < 0) {
4165 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4166 goto exit;
4167 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004168 /* On DOWN the firmware removes the WEP keys, reconfigure
4169 * them if they were set.
4170 */
4171 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004172
4173 memset(&join_params, 0, sizeof(join_params));
4174 /* join parameters starts with ssid */
4175 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4176 /* create softap */
4177 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4178 &join_params, sizeof(join_params));
4179 if (err < 0) {
4180 brcmf_err("SET SSID error (%d)\n", err);
4181 goto exit;
4182 }
4183 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4184 } else {
4185 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4186 sizeof(ssid_le));
4187 if (err < 0) {
4188 brcmf_err("setting ssid failed %d\n", err);
4189 goto exit;
4190 }
4191 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4192 bss_enable.enable = cpu_to_le32(1);
4193 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4194 sizeof(bss_enable));
4195 if (err < 0) {
4196 brcmf_err("bss_enable config failed %d\n", err);
4197 goto exit;
4198 }
4199
4200 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4201 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004202 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4203 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02004204
4205exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004206 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004207 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004208 brcmf_configure_arp_offload(ifp, true);
4209 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004210 return err;
4211}
4212
4213static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4214{
Arend van Sprielc1179032012-10-22 13:55:33 -07004215 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004216 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004217 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004218 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004219
Arend van Sprield96b8012012-12-05 15:26:02 +01004220 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004221
Hante Meuleman426d0a52013-02-08 15:53:53 +01004222 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004223 /* Due to most likely deauths outstanding we sleep */
4224 /* first to make sure they get processed by fw. */
4225 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004226
Hante Meulemana44aa402014-12-03 21:05:33 +01004227 if (ifp->vif->mbss) {
4228 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4229 return err;
4230 }
4231
Hante Meuleman5c33a942013-04-02 21:06:18 +02004232 memset(&join_params, 0, sizeof(join_params));
4233 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4234 &join_params, sizeof(join_params));
4235 if (err < 0)
4236 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004237 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004238 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004239 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004240 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4241 if (err < 0)
4242 brcmf_err("setting AP mode failed %d\n", err);
4243 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4244 if (err < 0)
4245 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004246 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4247 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004248 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4249 ifp->vif->is_11d);
4250 if (err < 0)
4251 brcmf_err("restoring REGULATORY setting failed %d\n",
4252 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004253 /* Bring device back up so it can be used again */
4254 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4255 if (err < 0)
4256 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004257 } else {
4258 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4259 bss_enable.enable = cpu_to_le32(0);
4260 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4261 sizeof(bss_enable));
4262 if (err < 0)
4263 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004264 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004265 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004266 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004267 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4268 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
4269
Hante Meuleman1a873342012-09-27 14:17:54 +02004270 return err;
4271}
4272
Hante Meulemana0f07952013-02-08 15:53:47 +01004273static s32
4274brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4275 struct cfg80211_beacon_data *info)
4276{
Hante Meulemana0f07952013-02-08 15:53:47 +01004277 struct brcmf_if *ifp = netdev_priv(ndev);
4278 s32 err;
4279
4280 brcmf_dbg(TRACE, "Enter\n");
4281
Hante Meulemana0f07952013-02-08 15:53:47 +01004282 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4283
4284 return err;
4285}
4286
Hante Meuleman1a873342012-09-27 14:17:54 +02004287static int
4288brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004289 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004290{
Hante Meulemana0f07952013-02-08 15:53:47 +01004291 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004292 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004293 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004294 s32 err;
4295
Jouni Malinen89c771e2014-10-10 20:52:40 +03004296 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004297 return -EFAULT;
4298
Jouni Malinen89c771e2014-10-10 20:52:40 +03004299 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004300
Hante Meulemana0f07952013-02-08 15:53:47 +01004301 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4302 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004303 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004304 return -EIO;
4305
Jouni Malinen89c771e2014-10-10 20:52:40 +03004306 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004307 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004308 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004309 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004310 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004311 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004312
Arend van Sprield96b8012012-12-05 15:26:02 +01004313 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004314 return err;
4315}
4316
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004317static int
4318brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4319 const u8 *mac, struct station_parameters *params)
4320{
4321 struct brcmf_if *ifp = netdev_priv(ndev);
4322 s32 err;
4323
4324 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4325 params->sta_flags_mask, params->sta_flags_set);
4326
4327 /* Ignore all 00 MAC */
4328 if (is_zero_ether_addr(mac))
4329 return 0;
4330
4331 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4332 return 0;
4333
4334 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4335 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4336 (void *)mac, ETH_ALEN);
4337 else
4338 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4339 (void *)mac, ETH_ALEN);
4340 if (err < 0)
4341 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4342
4343 return err;
4344}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004345
4346static void
4347brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4348 struct wireless_dev *wdev,
4349 u16 frame_type, bool reg)
4350{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004351 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004352 u16 mgmt_type;
4353
4354 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4355
4356 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004357 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004358 if (reg)
4359 vif->mgmt_rx_reg |= BIT(mgmt_type);
4360 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004361 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004362}
4363
4364
4365static int
4366brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004367 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004368{
4369 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004370 struct ieee80211_channel *chan = params->chan;
4371 const u8 *buf = params->buf;
4372 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004373 const struct ieee80211_mgmt *mgmt;
4374 struct brcmf_cfg80211_vif *vif;
4375 s32 err = 0;
4376 s32 ie_offset;
4377 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004378 struct brcmf_fil_action_frame_le *action_frame;
4379 struct brcmf_fil_af_params_le *af_params;
4380 bool ack;
4381 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004382 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004383
4384 brcmf_dbg(TRACE, "Enter\n");
4385
4386 *cookie = 0;
4387
4388 mgmt = (const struct ieee80211_mgmt *)buf;
4389
Hante Meulemana0f07952013-02-08 15:53:47 +01004390 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4391 brcmf_err("Driver only allows MGMT packet type\n");
4392 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004393 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004394
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004395 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4396
Hante Meulemana0f07952013-02-08 15:53:47 +01004397 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4398 /* Right now the only reason to get a probe response */
4399 /* is for p2p listen response or for p2p GO from */
4400 /* wpa_supplicant. Unfortunately the probe is send */
4401 /* on primary ndev, while dongle wants it on the p2p */
4402 /* vif. Since this is only reason for a probe */
4403 /* response to be sent, the vif is taken from cfg. */
4404 /* If ever desired to send proberesp for non p2p */
4405 /* response then data should be checked for */
4406 /* "DIRECT-". Note in future supplicant will take */
4407 /* dedicated p2p wdev to do this and then this 'hack'*/
4408 /* is not needed anymore. */
4409 ie_offset = DOT11_MGMT_HDR_LEN +
4410 DOT11_BCN_PRB_FIXED_LEN;
4411 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004412 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4413 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4414 err = brcmf_vif_set_mgmt_ie(vif,
4415 BRCMF_VNDR_IE_PRBRSP_FLAG,
4416 &buf[ie_offset],
4417 ie_len);
4418 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4419 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004420 } else if (ieee80211_is_action(mgmt->frame_control)) {
4421 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4422 if (af_params == NULL) {
4423 brcmf_err("unable to allocate frame\n");
4424 err = -ENOMEM;
4425 goto exit;
4426 }
4427 action_frame = &af_params->action_frame;
4428 /* Add the packet Id */
4429 action_frame->packet_id = cpu_to_le32(*cookie);
4430 /* Add BSSID */
4431 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4432 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4433 /* Add the length exepted for 802.11 header */
4434 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004435 /* Add the channel. Use the one specified as parameter if any or
4436 * the current one (got from the firmware) otherwise
4437 */
4438 if (chan)
4439 freq = chan->center_freq;
4440 else
4441 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4442 &freq);
4443 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004444 af_params->channel = cpu_to_le32(chan_nr);
4445
4446 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4447 le16_to_cpu(action_frame->len));
4448
4449 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004450 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004451
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004452 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004453 af_params);
4454
4455 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4456 GFP_KERNEL);
4457 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004458 } else {
4459 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4460 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4461 }
4462
Hante Meuleman18e2f612013-02-08 15:53:49 +01004463exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004464 return err;
4465}
4466
4467
4468static int
4469brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4470 struct wireless_dev *wdev,
4471 u64 cookie)
4472{
4473 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4474 struct brcmf_cfg80211_vif *vif;
4475 int err = 0;
4476
4477 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4478
4479 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4480 if (vif == NULL) {
4481 brcmf_err("No p2p device available for probe response\n");
4482 err = -ENODEV;
4483 goto exit;
4484 }
4485 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4486exit:
4487 return err;
4488}
4489
Piotr Haber61730d42013-04-23 12:53:12 +02004490static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4491 struct wireless_dev *wdev,
4492 enum nl80211_crit_proto_id proto,
4493 u16 duration)
4494{
4495 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4496 struct brcmf_cfg80211_vif *vif;
4497
4498 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4499
4500 /* only DHCP support for now */
4501 if (proto != NL80211_CRIT_PROTO_DHCP)
4502 return -EINVAL;
4503
4504 /* suppress and abort scanning */
4505 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4506 brcmf_abort_scanning(cfg);
4507
4508 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4509}
4510
4511static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4512 struct wireless_dev *wdev)
4513{
4514 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4515 struct brcmf_cfg80211_vif *vif;
4516
4517 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4518
4519 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4520 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4521}
4522
Hante Meuleman70b7d942014-07-30 13:20:07 +02004523static s32
4524brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4525 const struct brcmf_event_msg *e, void *data)
4526{
4527 switch (e->reason) {
4528 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4529 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4530 break;
4531 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4532 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4533 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4534 break;
4535 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4536 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4537 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4538 break;
4539 }
4540
4541 return 0;
4542}
4543
Arend van Spriel89c2f382013-08-10 12:27:25 +02004544static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4545{
4546 int ret;
4547
4548 switch (oper) {
4549 case NL80211_TDLS_DISCOVERY_REQ:
4550 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4551 break;
4552 case NL80211_TDLS_SETUP:
4553 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4554 break;
4555 case NL80211_TDLS_TEARDOWN:
4556 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4557 break;
4558 default:
4559 brcmf_err("unsupported operation: %d\n", oper);
4560 ret = -EOPNOTSUPP;
4561 }
4562 return ret;
4563}
4564
4565static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004566 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004567 enum nl80211_tdls_operation oper)
4568{
4569 struct brcmf_if *ifp;
4570 struct brcmf_tdls_iovar_le info;
4571 int ret = 0;
4572
4573 ret = brcmf_convert_nl80211_tdls_oper(oper);
4574 if (ret < 0)
4575 return ret;
4576
4577 ifp = netdev_priv(ndev);
4578 memset(&info, 0, sizeof(info));
4579 info.mode = (u8)ret;
4580 if (peer)
4581 memcpy(info.ea, peer, ETH_ALEN);
4582
4583 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4584 &info, sizeof(info));
4585 if (ret < 0)
4586 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4587
4588 return ret;
4589}
4590
Arend van Spriel5b435de2011-10-05 13:19:03 +02004591static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004592 .add_virtual_intf = brcmf_cfg80211_add_iface,
4593 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004594 .change_virtual_intf = brcmf_cfg80211_change_iface,
4595 .scan = brcmf_cfg80211_scan,
4596 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4597 .join_ibss = brcmf_cfg80211_join_ibss,
4598 .leave_ibss = brcmf_cfg80211_leave_ibss,
4599 .get_station = brcmf_cfg80211_get_station,
4600 .set_tx_power = brcmf_cfg80211_set_tx_power,
4601 .get_tx_power = brcmf_cfg80211_get_tx_power,
4602 .add_key = brcmf_cfg80211_add_key,
4603 .del_key = brcmf_cfg80211_del_key,
4604 .get_key = brcmf_cfg80211_get_key,
4605 .set_default_key = brcmf_cfg80211_config_default_key,
4606 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4607 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004608 .connect = brcmf_cfg80211_connect,
4609 .disconnect = brcmf_cfg80211_disconnect,
4610 .suspend = brcmf_cfg80211_suspend,
4611 .resume = brcmf_cfg80211_resume,
4612 .set_pmksa = brcmf_cfg80211_set_pmksa,
4613 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004614 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004615 .start_ap = brcmf_cfg80211_start_ap,
4616 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004617 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004618 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004619 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004620 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4621 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004622 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4623 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4624 .remain_on_channel = brcmf_p2p_remain_on_channel,
4625 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004626 .start_p2p_device = brcmf_p2p_start_device,
4627 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004628 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4629 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004630 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004631};
4632
Arend van Spriel3eacf862012-10-22 13:55:30 -07004633struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004634 enum nl80211_iftype type,
4635 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004636{
Hante Meulemana44aa402014-12-03 21:05:33 +01004637 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004638 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004639 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004640
Arend van Spriel33a6b152013-02-08 15:53:39 +01004641 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004642 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004643 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4644 if (!vif)
4645 return ERR_PTR(-ENOMEM);
4646
4647 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004648 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004649
Arend van Spriel3eacf862012-10-22 13:55:30 -07004650 vif->pm_block = pm_block;
4651 vif->roam_off = -1;
4652
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004653 brcmf_init_prof(&vif->profile);
4654
Hante Meulemana44aa402014-12-03 21:05:33 +01004655 if (type == NL80211_IFTYPE_AP) {
4656 mbss = false;
4657 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4658 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4659 mbss = true;
4660 break;
4661 }
4662 }
4663 vif->mbss = mbss;
4664 }
4665
Arend van Spriel3eacf862012-10-22 13:55:30 -07004666 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004667 return vif;
4668}
4669
Arend van Spriel427dec52014-01-06 12:40:47 +01004670void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004671{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004672 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004673 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004674}
4675
Arend van Spriel9df4d542014-01-06 12:40:49 +01004676void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4677{
4678 struct brcmf_cfg80211_vif *vif;
4679 struct brcmf_if *ifp;
4680
4681 ifp = netdev_priv(ndev);
4682 vif = ifp->vif;
4683
4684 brcmf_free_vif(vif);
4685 free_netdev(ndev);
4686}
4687
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004688static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004689{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004690 u32 event = e->event_code;
4691 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004692
4693 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004694 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004695 return true;
4696 }
4697
4698 return false;
4699}
4700
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004701static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004702{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004703 u32 event = e->event_code;
4704 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004705
Hante Meuleman68ca3952014-02-25 20:30:26 +01004706 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4707 (event == BRCMF_E_DISASSOC_IND) ||
4708 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004709 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004710 return true;
4711 }
4712 return false;
4713}
4714
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004715static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004716 const struct brcmf_event_msg *e)
4717{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004718 u32 event = e->event_code;
4719 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004720
4721 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004722 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4723 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004724 return true;
4725 }
4726
4727 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004728 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004729 return true;
4730 }
4731
4732 return false;
4733}
4734
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004735static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004736{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004737 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004738
4739 kfree(conn_info->req_ie);
4740 conn_info->req_ie = NULL;
4741 conn_info->req_ie_len = 0;
4742 kfree(conn_info->resp_ie);
4743 conn_info->resp_ie = NULL;
4744 conn_info->resp_ie_len = 0;
4745}
4746
Hante Meuleman89286dc2013-02-08 15:53:46 +01004747static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4748 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004749{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004750 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004751 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004752 u32 req_len;
4753 u32 resp_len;
4754 s32 err = 0;
4755
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004756 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004757
Arend van Sprielac24be62012-10-22 10:36:23 -07004758 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4759 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004760 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004761 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004762 return err;
4763 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004764 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004765 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004766 req_len = le32_to_cpu(assoc_info->req_len);
4767 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004768 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004769 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004770 cfg->extra_buf,
4771 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004772 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004773 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004774 return err;
4775 }
4776 conn_info->req_ie_len = req_len;
4777 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004778 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004779 GFP_KERNEL);
4780 } else {
4781 conn_info->req_ie_len = 0;
4782 conn_info->req_ie = NULL;
4783 }
4784 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004785 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004786 cfg->extra_buf,
4787 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004788 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004789 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004790 return err;
4791 }
4792 conn_info->resp_ie_len = resp_len;
4793 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004794 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004795 GFP_KERNEL);
4796 } else {
4797 conn_info->resp_ie_len = 0;
4798 conn_info->resp_ie = NULL;
4799 }
Arend van Spriel16886732012-12-05 15:26:04 +01004800 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4801 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004802
4803 return err;
4804}
4805
4806static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004807brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004808 struct net_device *ndev,
4809 const struct brcmf_event_msg *e)
4810{
Arend van Sprielc1179032012-10-22 13:55:33 -07004811 struct brcmf_if *ifp = netdev_priv(ndev);
4812 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004813 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4814 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004815 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004816 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004817 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004818 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004819 u32 freq;
4820 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004821 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004822
Arend van Sprield96b8012012-12-05 15:26:02 +01004823 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004824
Hante Meuleman89286dc2013-02-08 15:53:46 +01004825 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004826 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004827 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004828
Franky Lina180b832012-10-10 11:13:09 -07004829 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4830 if (buf == NULL) {
4831 err = -ENOMEM;
4832 goto done;
4833 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004834
Franky Lina180b832012-10-10 11:13:09 -07004835 /* data sent to dongle has to be little endian */
4836 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004837 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004838 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004839
4840 if (err)
4841 goto done;
4842
4843 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004844 ch.chspec = le16_to_cpu(bi->chanspec);
4845 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004846
Franky Lin83cf17a2013-04-11 13:28:50 +02004847 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004848 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4849 else
4850 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4851
Franky Lin83cf17a2013-04-11 13:28:50 +02004852 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004853 notify_channel = ieee80211_get_channel(wiphy, freq);
4854
Franky Lina180b832012-10-10 11:13:09 -07004855done:
4856 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004857 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004858 conn_info->req_ie, conn_info->req_ie_len,
4859 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004860 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004861
Arend van Sprielc1179032012-10-22 13:55:33 -07004862 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004863 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004864 return err;
4865}
4866
4867static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004868brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004869 struct net_device *ndev, const struct brcmf_event_msg *e,
4870 bool completed)
4871{
Arend van Sprielc1179032012-10-22 13:55:33 -07004872 struct brcmf_if *ifp = netdev_priv(ndev);
4873 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004874 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004875
Arend van Sprield96b8012012-12-05 15:26:02 +01004876 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004877
Arend van Sprielc1179032012-10-22 13:55:33 -07004878 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4879 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004880 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004881 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004882 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004883 brcmf_update_bss_info(cfg, ifp);
4884 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4885 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004886 }
4887 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004888 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004889 conn_info->req_ie,
4890 conn_info->req_ie_len,
4891 conn_info->resp_ie,
4892 conn_info->resp_ie_len,
4893 completed ? WLAN_STATUS_SUCCESS :
4894 WLAN_STATUS_AUTH_TIMEOUT,
4895 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004896 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4897 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004898 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004899 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004900 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004901}
4902
4903static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004904brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004905 struct net_device *ndev,
4906 const struct brcmf_event_msg *e, void *data)
4907{
Hante Meulemana44aa402014-12-03 21:05:33 +01004908 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01004909 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004910 u32 event = e->event_code;
4911 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004912 struct station_info sinfo;
4913
Arend van Spriel16886732012-12-05 15:26:04 +01004914 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004915 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4916 ndev != cfg_to_ndev(cfg)) {
4917 brcmf_dbg(CONN, "AP mode link down\n");
4918 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01004919 if (ifp->vif->mbss)
4920 brcmf_remove_interface(ifp->drvr, ifp->bssidx);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004921 return 0;
4922 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004923
Hante Meuleman1a873342012-09-27 14:17:54 +02004924 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004925 (reason == BRCMF_E_STATUS_SUCCESS)) {
4926 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004927 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004928 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004929 return -EINVAL;
4930 }
4931 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004932 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004933 generation++;
4934 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004935 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004936 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4937 (event == BRCMF_E_DEAUTH_IND) ||
4938 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004939 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004940 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004941 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004942}
4943
4944static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004945brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004946 const struct brcmf_event_msg *e, void *data)
4947{
Arend van Spriel19937322012-11-05 16:22:32 -08004948 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4949 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004950 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004951 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004952 s32 err = 0;
4953
Hante Meuleman8851cce2014-07-30 13:20:02 +02004954 if ((e->event_code == BRCMF_E_DEAUTH) ||
4955 (e->event_code == BRCMF_E_DEAUTH_IND) ||
4956 (e->event_code == BRCMF_E_DISASSOC_IND) ||
4957 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
4958 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4959 }
4960
Arend van Spriel967fe2c2014-03-15 17:18:21 +01004961 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004962 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004963 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004964 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004965 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004966 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004967 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004968 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004969 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004970 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4971 &ifp->vif->sme_state);
4972 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4973 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004974 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004975 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004976 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004977 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004978 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004979 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004980 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01004981 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004982 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004983 if (ndev != cfg_to_ndev(cfg))
4984 complete(&cfg->vif_disabled);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004985 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004986 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07004987 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4988 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004989 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004990 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004991 }
4992
4993 return err;
4994}
4995
4996static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004997brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004998 const struct brcmf_event_msg *e, void *data)
4999{
Arend van Spriel19937322012-11-05 16:22:32 -08005000 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005001 u32 event = e->event_code;
5002 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005003
5004 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005005 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005006 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005007 else
Arend van Spriel19937322012-11-05 16:22:32 -08005008 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005009 }
5010
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005011 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005012}
5013
5014static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005015brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005016 const struct brcmf_event_msg *e, void *data)
5017{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005018 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005019 enum nl80211_key_type key_type;
5020
5021 if (flags & BRCMF_EVENT_MSG_GROUP)
5022 key_type = NL80211_KEYTYPE_GROUP;
5023 else
5024 key_type = NL80211_KEYTYPE_PAIRWISE;
5025
Arend van Spriel19937322012-11-05 16:22:32 -08005026 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005027 NULL, GFP_KERNEL);
5028
5029 return 0;
5030}
5031
Arend van Sprield3c0b632013-02-08 15:53:37 +01005032static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5033 const struct brcmf_event_msg *e, void *data)
5034{
5035 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5036 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5037 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5038 struct brcmf_cfg80211_vif *vif;
5039
5040 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
5041 ifevent->action, ifevent->flags, ifevent->ifidx,
5042 ifevent->bssidx);
5043
Arend van Sprield3c0b632013-02-08 15:53:37 +01005044 mutex_lock(&event->vif_event_lock);
5045 event->action = ifevent->action;
5046 vif = event->vif;
5047
5048 switch (ifevent->action) {
5049 case BRCMF_E_IF_ADD:
5050 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005051 if (!cfg->vif_event.vif) {
5052 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005053 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005054 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005055
5056 ifp->vif = vif;
5057 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005058 if (ifp->ndev) {
5059 vif->wdev.netdev = ifp->ndev;
5060 ifp->ndev->ieee80211_ptr = &vif->wdev;
5061 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5062 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005063 mutex_unlock(&event->vif_event_lock);
5064 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005065 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005066
5067 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005068 mutex_unlock(&event->vif_event_lock);
5069 /* event may not be upon user request */
5070 if (brcmf_cfg80211_vif_event_armed(cfg))
5071 wake_up(&event->vif_wq);
5072 return 0;
5073
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005074 case BRCMF_E_IF_CHANGE:
5075 mutex_unlock(&event->vif_event_lock);
5076 wake_up(&event->vif_wq);
5077 return 0;
5078
Arend van Sprield3c0b632013-02-08 15:53:37 +01005079 default:
5080 mutex_unlock(&event->vif_event_lock);
5081 break;
5082 }
5083 return -EINVAL;
5084}
5085
Arend van Spriel5b435de2011-10-05 13:19:03 +02005086static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5087{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005088 conf->frag_threshold = (u32)-1;
5089 conf->rts_threshold = (u32)-1;
5090 conf->retry_short = (u32)-1;
5091 conf->retry_long = (u32)-1;
5092 conf->tx_power = -1;
5093}
5094
Arend van Spriel5c36b992012-11-14 18:46:05 -08005095static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005096{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005097 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5098 brcmf_notify_connect_status);
5099 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5100 brcmf_notify_connect_status);
5101 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5102 brcmf_notify_connect_status);
5103 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5104 brcmf_notify_connect_status);
5105 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5106 brcmf_notify_connect_status);
5107 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5108 brcmf_notify_connect_status);
5109 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5110 brcmf_notify_roaming_status);
5111 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5112 brcmf_notify_mic_status);
5113 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5114 brcmf_notify_connect_status);
5115 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5116 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005117 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5118 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005119 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005120 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005121 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5122 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005123 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5124 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005125 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5126 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005127 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5128 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005129}
5130
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005131static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005132{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005133 kfree(cfg->conf);
5134 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005135 kfree(cfg->escan_ioctl_buf);
5136 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005137 kfree(cfg->extra_buf);
5138 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005139 kfree(cfg->pmk_list);
5140 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005141}
5142
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005143static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005144{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005145 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5146 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005147 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005148 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5149 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005150 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005151 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5152 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005153 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005154 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5155 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005156 goto init_priv_mem_out;
5157
5158 return 0;
5159
5160init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005161 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005162
5163 return -ENOMEM;
5164}
5165
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005166static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005167{
5168 s32 err = 0;
5169
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005170 cfg->scan_request = NULL;
5171 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005172 cfg->active_scan = true; /* we do active scan per default */
5173 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005174 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005175 if (err)
5176 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005177 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005178 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005179 brcmf_init_escan(cfg);
5180 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005181 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005182 return err;
5183}
5184
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005185static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005186{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005187 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005188 brcmf_abort_scanning(cfg);
5189 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005190}
5191
Arend van Sprield3c0b632013-02-08 15:53:37 +01005192static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5193{
5194 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005195 mutex_init(&event->vif_event_lock);
5196}
5197
Arend van Spriel5b435de2011-10-05 13:19:03 +02005198static s32
Hante Meuleman68ca3952014-02-25 20:30:26 +01005199brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005200{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005202 __le32 roamtrigger[2];
5203 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005204
5205 /*
5206 * Setup timeout if Beacons are lost and roam is
5207 * off to report link down
5208 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005209 if (brcmf_roamoff) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005210 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005211 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005212 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005213 goto dongle_rom_out;
5214 }
5215 }
5216
5217 /*
5218 * Enable/Disable built-in roaming to allow supplicant
5219 * to take care of roaming
5220 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005221 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5222 brcmf_roamoff ? "Off" : "On");
5223 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005224 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005225 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005226 goto dongle_rom_out;
5227 }
5228
Arend van Sprielf588bc02011-10-12 20:51:22 +02005229 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5230 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005231 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005232 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005233 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005234 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005235 goto dongle_rom_out;
5236 }
5237
Arend van Sprielf588bc02011-10-12 20:51:22 +02005238 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5239 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005240 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005241 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005242 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005243 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005244 goto dongle_rom_out;
5245 }
5246
5247dongle_rom_out:
5248 return err;
5249}
5250
5251static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01005252brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005253 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005254{
5255 s32 err = 0;
5256
Arend van Sprielac24be62012-10-22 10:36:23 -07005257 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005258 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005259 if (err) {
5260 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005261 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005262 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005263 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005264 goto dongle_scantime_out;
5265 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005266 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005267 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005268 if (err) {
5269 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005270 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005271 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005272 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005273 goto dongle_scantime_out;
5274 }
5275
Arend van Sprielac24be62012-10-22 10:36:23 -07005276 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005277 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005278 if (err) {
5279 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005280 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005281 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005282 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005283 goto dongle_scantime_out;
5284 }
5285
5286dongle_scantime_out:
5287 return err;
5288}
5289
Arend van Sprielb48d8912014-07-12 08:49:41 +02005290static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5291 struct brcmu_chan *ch)
5292{
5293 u32 ht40_flag;
5294
5295 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5296 if (ch->sb == BRCMU_CHAN_SB_U) {
5297 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5298 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5299 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5300 } else {
5301 /* It should be one of
5302 * IEEE80211_CHAN_NO_HT40 or
5303 * IEEE80211_CHAN_NO_HT40PLUS
5304 */
5305 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5306 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5307 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5308 }
5309}
5310
5311static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5312 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005313{
5314 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005315 struct ieee80211_supported_band *band;
5316 struct ieee80211_channel *channel;
5317 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005318 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005319 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005320 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005321 u8 *pbuf;
5322 u32 i, j;
5323 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005324 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005325 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005326
5327 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5328
5329 if (pbuf == NULL)
5330 return -ENOMEM;
5331
5332 list = (struct brcmf_chanspec_list *)pbuf;
5333
5334 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5335 BRCMF_DCMD_MEDLEN);
5336 if (err) {
5337 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005338 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005339 }
5340
Arend van Sprielb48d8912014-07-12 08:49:41 +02005341 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005342 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5343 if (band)
5344 for (i = 0; i < band->n_channels; i++)
5345 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5346 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5347 if (band)
5348 for (i = 0; i < band->n_channels; i++)
5349 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005350
5351 total = le32_to_cpu(list->count);
5352 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005353 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5354 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005355
Franky Lin83cf17a2013-04-11 13:28:50 +02005356 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005357 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005358 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005359 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005360 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005361 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005362 continue;
5363 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005364 if (!band)
5365 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005366 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005367 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005368 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005369 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005370 ch.bw == BRCMU_CHAN_BW_80)
5371 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005372
5373 channel = band->channels;
5374 index = band->n_channels;
5375 for (j = 0; j < band->n_channels; j++) {
5376 if (channel[j].hw_value == ch.chnum) {
5377 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005378 break;
5379 }
5380 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005381 channel[index].center_freq =
5382 ieee80211_channel_to_frequency(ch.chnum, band->band);
5383 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005384
Arend van Sprielb48d8912014-07-12 08:49:41 +02005385 /* assuming the chanspecs order is HT20,
5386 * HT40 upper, HT40 lower, and VHT80.
5387 */
5388 if (ch.bw == BRCMU_CHAN_BW_80) {
5389 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5390 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5391 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5392 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005393 /* enable the channel and disable other bandwidths
5394 * for now as mentioned order assure they are enabled
5395 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005396 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005397 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5398 IEEE80211_CHAN_NO_80MHZ;
5399 ch.bw = BRCMU_CHAN_BW_20;
5400 cfg->d11inf.encchspec(&ch);
5401 chaninfo = ch.chspec;
5402 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5403 &chaninfo);
5404 if (!err) {
5405 if (chaninfo & WL_CHAN_RADAR)
5406 channel[index].flags |=
5407 (IEEE80211_CHAN_RADAR |
5408 IEEE80211_CHAN_NO_IR);
5409 if (chaninfo & WL_CHAN_PASSIVE)
5410 channel[index].flags |=
5411 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005412 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005413 }
5414 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005415
Arend van Sprielb48d8912014-07-12 08:49:41 +02005416fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005417 kfree(pbuf);
5418 return err;
5419}
5420
Arend van Sprielb48d8912014-07-12 08:49:41 +02005421static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005422{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005423 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5424 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005425 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005426 struct brcmf_chanspec_list *list;
5427 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005428 u32 val;
5429 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005430 struct brcmu_chan ch;
5431 u32 num_chan;
5432 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005433
5434 /* verify support for bw_cap command */
5435 val = WLC_BAND_5G;
5436 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5437
5438 if (!err) {
5439 /* only set 2G bandwidth using bw_cap command */
5440 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5441 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5442 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5443 sizeof(band_bwcap));
5444 } else {
5445 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5446 val = WLC_N_BW_40ALL;
5447 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5448 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005449
5450 if (!err) {
5451 /* update channel info in 2G band */
5452 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5453
5454 if (pbuf == NULL)
5455 return -ENOMEM;
5456
5457 ch.band = BRCMU_CHAN_BAND_2G;
5458 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005459 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005460 ch.chnum = 0;
5461 cfg->d11inf.encchspec(&ch);
5462
5463 /* pass encoded chanspec in query */
5464 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5465
5466 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5467 BRCMF_DCMD_MEDLEN);
5468 if (err) {
5469 brcmf_err("get chanspecs error (%d)\n", err);
5470 kfree(pbuf);
5471 return err;
5472 }
5473
5474 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5475 list = (struct brcmf_chanspec_list *)pbuf;
5476 num_chan = le32_to_cpu(list->count);
5477 for (i = 0; i < num_chan; i++) {
5478 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5479 cfg->d11inf.decchspec(&ch);
5480 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5481 continue;
5482 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5483 continue;
5484 for (j = 0; j < band->n_channels; j++) {
5485 if (band->channels[j].hw_value == ch.chnum)
5486 break;
5487 }
5488 if (WARN_ON(j == band->n_channels))
5489 continue;
5490
5491 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5492 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005493 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005494 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005495 return err;
5496}
5497
Arend van Spriel2375d972014-01-06 12:40:41 +01005498static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5499{
5500 u32 band, mimo_bwcap;
5501 int err;
5502
5503 band = WLC_BAND_2G;
5504 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5505 if (!err) {
5506 bw_cap[IEEE80211_BAND_2GHZ] = band;
5507 band = WLC_BAND_5G;
5508 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5509 if (!err) {
5510 bw_cap[IEEE80211_BAND_5GHZ] = band;
5511 return;
5512 }
5513 WARN_ON(1);
5514 return;
5515 }
5516 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5517 mimo_bwcap = 0;
5518 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5519 if (err)
5520 /* assume 20MHz if firmware does not give a clue */
5521 mimo_bwcap = WLC_N_BW_20ALL;
5522
5523 switch (mimo_bwcap) {
5524 case WLC_N_BW_40ALL:
5525 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5526 /* fall-thru */
5527 case WLC_N_BW_20IN2G_40IN5G:
5528 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5529 /* fall-thru */
5530 case WLC_N_BW_20ALL:
5531 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5532 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5533 break;
5534 default:
5535 brcmf_err("invalid mimo_bw_cap value\n");
5536 }
5537}
Hante Meulemand48200b2013-04-03 12:40:29 +02005538
Arend van Spriel18d6c532014-05-12 10:47:35 +02005539static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5540 u32 bw_cap[2], u32 nchain)
5541{
5542 band->ht_cap.ht_supported = true;
5543 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5544 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5545 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5546 }
5547 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5548 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5549 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5550 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5551 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5552 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5553}
5554
5555static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5556{
5557 u16 mcs_map;
5558 int i;
5559
5560 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5561 mcs_map = (mcs_map << 2) | supp;
5562
5563 return cpu_to_le16(mcs_map);
5564}
5565
5566static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
5567 u32 bw_cap[2], u32 nchain)
5568{
5569 __le16 mcs_map;
5570
5571 /* not allowed in 2.4G band */
5572 if (band->band == IEEE80211_BAND_2GHZ)
5573 return;
5574
5575 band->vht_cap.vht_supported = true;
5576 /* 80MHz is mandatory */
5577 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5578 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5579 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5580 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5581 }
5582 /* all support 256-QAM */
5583 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5584 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5585 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
5586}
5587
Arend van Sprielb48d8912014-07-12 08:49:41 +02005588static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005589{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005590 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005591 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005592 u32 nmode = 0;
5593 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005594 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005595 u32 rxchain;
5596 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005597 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005598 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005599 struct ieee80211_supported_band *band;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005600
Arend van Spriel18d6c532014-05-12 10:47:35 +02005601 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005602 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5603 if (err) {
5604 brcmf_err("nmode error (%d)\n", err);
5605 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005606 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005607 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005608 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5609 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5610 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005611
Daniel Kim4aca7a12014-02-25 20:30:36 +01005612 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5613 if (err) {
5614 brcmf_err("rxchain error (%d)\n", err);
5615 nchain = 1;
5616 } else {
5617 for (nchain = 0; rxchain; nchain++)
5618 rxchain = rxchain & (rxchain - 1);
5619 }
5620 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5621
Arend van Sprielb48d8912014-07-12 08:49:41 +02005622 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005623 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005624 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005625 return err;
5626 }
5627
Arend van Sprielb48d8912014-07-12 08:49:41 +02005628 wiphy = cfg_to_wiphy(cfg);
5629 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5630 band = wiphy->bands[i];
5631 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005632 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005633
Arend van Spriel18d6c532014-05-12 10:47:35 +02005634 if (nmode)
5635 brcmf_update_ht_cap(band, bw_cap, nchain);
5636 if (vhtmode)
5637 brcmf_update_vht_cap(band, bw_cap, nchain);
Hante Meulemand48200b2013-04-03 12:40:29 +02005638 }
5639
Arend van Sprielb48d8912014-07-12 08:49:41 +02005640 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005641}
5642
Hante Meulemana44aa402014-12-03 21:05:33 +01005643static const struct ieee80211_iface_limit brcmf_iface_limits_mbss[] = {
5644 {
5645 .max = 1,
5646 .types = BIT(NL80211_IFTYPE_STATION) |
5647 BIT(NL80211_IFTYPE_ADHOC)
5648 },
5649 {
5650 .max = 4,
5651 .types = BIT(NL80211_IFTYPE_AP)
5652 },
5653 {
5654 .max = 1,
5655 .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5656 BIT(NL80211_IFTYPE_P2P_GO)
5657 },
5658 {
5659 .max = 1,
5660 .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
5661 }
5662};
5663
5664static const struct ieee80211_iface_limit brcmf_iface_limits_sbss[] = {
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005665 {
5666 .max = 2,
5667 .types = BIT(NL80211_IFTYPE_STATION) |
5668 BIT(NL80211_IFTYPE_ADHOC) |
5669 BIT(NL80211_IFTYPE_AP)
5670 },
5671 {
5672 .max = 1,
5673 .types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5674 BIT(NL80211_IFTYPE_P2P_GO)
5675 },
5676 {
5677 .max = 1,
5678 .types = BIT(NL80211_IFTYPE_P2P_DEVICE)
5679 }
5680};
5681static struct ieee80211_iface_combination brcmf_iface_combos[] = {
5682 {
5683 .max_interfaces = BRCMF_IFACE_MAX_CNT,
5684 .num_different_channels = 1,
Hante Meulemana44aa402014-12-03 21:05:33 +01005685 .n_limits = ARRAY_SIZE(brcmf_iface_limits_sbss),
5686 .limits = brcmf_iface_limits_sbss,
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005687 }
5688};
5689
5690static const struct ieee80211_txrx_stypes
5691brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5692 [NL80211_IFTYPE_STATION] = {
5693 .tx = 0xffff,
5694 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5695 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5696 },
5697 [NL80211_IFTYPE_P2P_CLIENT] = {
5698 .tx = 0xffff,
5699 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5700 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5701 },
5702 [NL80211_IFTYPE_P2P_GO] = {
5703 .tx = 0xffff,
5704 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5705 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5706 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5707 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5708 BIT(IEEE80211_STYPE_AUTH >> 4) |
5709 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5710 BIT(IEEE80211_STYPE_ACTION >> 4)
5711 },
5712 [NL80211_IFTYPE_P2P_DEVICE] = {
5713 .tx = 0xffff,
5714 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5715 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5716 }
5717};
5718
5719static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
5720{
5721 /* scheduled scan settings */
5722 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
5723 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
5724 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5725 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
5726}
5727
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005728#ifdef CONFIG_PM
5729static const struct wiphy_wowlan_support brcmf_wowlan_support = {
5730 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01005731 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
5732 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
5733 .pattern_min_len = 1,
5734 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005735};
5736#endif
5737
5738static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
5739{
5740#ifdef CONFIG_PM
5741 /* wowl settings */
5742 wiphy->wowlan = &brcmf_wowlan_support;
5743#endif
5744}
5745
Arend van Sprielb48d8912014-07-12 08:49:41 +02005746static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005747{
Arend van Spriel58de92d2015-04-14 20:10:24 +02005748 struct ieee80211_supported_band *band;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005749 struct ieee80211_iface_combination ifc_combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005750 __le32 bandlist[3];
5751 u32 n_bands;
5752 int err, i;
5753
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005754 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
5755 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5756 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
5757 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5758 BIT(NL80211_IFTYPE_ADHOC) |
5759 BIT(NL80211_IFTYPE_AP) |
5760 BIT(NL80211_IFTYPE_P2P_CLIENT) |
5761 BIT(NL80211_IFTYPE_P2P_GO) |
5762 BIT(NL80211_IFTYPE_P2P_DEVICE);
5763 /* need VSDB firmware feature for concurrent channels */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005764 ifc_combo = brcmf_iface_combos[0];
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005765 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
Arend van Sprielb48d8912014-07-12 08:49:41 +02005766 ifc_combo.num_different_channels = 2;
Hante Meulemana44aa402014-12-03 21:05:33 +01005767 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
5768 ifc_combo.n_limits = ARRAY_SIZE(brcmf_iface_limits_mbss),
5769 ifc_combo.limits = brcmf_iface_limits_mbss;
5770 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005771 wiphy->iface_combinations = kmemdup(&ifc_combo,
5772 sizeof(ifc_combo),
5773 GFP_KERNEL);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005774 wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005775 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
5776 wiphy->cipher_suites = __wl_cipher_suites;
5777 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
5778 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
5779 WIPHY_FLAG_OFFCHAN_TX |
5780 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
5781 WIPHY_FLAG_SUPPORTS_TDLS;
5782 if (!brcmf_roamoff)
5783 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
5784 wiphy->mgmt_stypes = brcmf_txrx_stypes;
5785 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02005786 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
5787 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005788
5789 /* vendor commands/events support */
5790 wiphy->vendor_commands = brcmf_vendor_cmds;
5791 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
5792
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005793 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
5794 brcmf_wiphy_wowl_params(wiphy);
5795
Arend van Spriel58de92d2015-04-14 20:10:24 +02005796 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
5797 sizeof(bandlist));
5798 if (err) {
5799 brcmf_err("could not obtain band info: err=%d\n", err);
5800 return err;
5801 }
5802 /* first entry in bandlist is number of bands */
5803 n_bands = le32_to_cpu(bandlist[0]);
5804 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
5805 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
5806 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
5807 GFP_KERNEL);
5808 if (!band)
5809 return -ENOMEM;
5810
5811 band->channels = kmemdup(&__wl_2ghz_channels,
5812 sizeof(__wl_2ghz_channels),
5813 GFP_KERNEL);
5814 if (!band->channels) {
5815 kfree(band);
5816 return -ENOMEM;
5817 }
5818
5819 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
5820 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
5821 }
5822 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
5823 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
5824 GFP_KERNEL);
5825 if (!band)
5826 return -ENOMEM;
5827
5828 band->channels = kmemdup(&__wl_5ghz_channels,
5829 sizeof(__wl_5ghz_channels),
5830 GFP_KERNEL);
5831 if (!band->channels) {
5832 kfree(band);
5833 return -ENOMEM;
5834 }
5835
5836 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
5837 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
5838 }
5839 }
5840 err = brcmf_setup_wiphybands(wiphy);
5841 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005842}
5843
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005844static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005845{
5846 struct net_device *ndev;
5847 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005848 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005849 s32 power_mode;
5850 s32 err = 0;
5851
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005852 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005853 return err;
5854
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005855 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005856 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01005857 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005858
Hante Meuleman40a23292013-01-02 15:22:51 +01005859 /* make sure RF is ready for work */
5860 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
5861
5862 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
5863 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005864
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005865 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01005866 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005867 if (err)
5868 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005869 brcmf_dbg(INFO, "power save set to %s\n",
5870 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005871
Hante Meuleman68ca3952014-02-25 20:30:26 +01005872 err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005873 if (err)
5874 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005875 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5876 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01005877 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005878 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005879
Hante Meulemanb3657452013-05-27 21:09:53 +02005880 brcmf_configure_arp_offload(ifp, true);
5881
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005882 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01005883default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005884
5885 return err;
5886
5887}
5888
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005889static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005890{
Arend van Sprielc1179032012-10-22 13:55:33 -07005891 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005892
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005893 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005894}
5895
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005896static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005897{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005898 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07005899
Arend van Spriel5b435de2011-10-05 13:19:03 +02005900 /*
5901 * While going down, if associated with AP disassociate
5902 * from AP to save power
5903 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005904 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005905 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005906
5907 /* Make sure WPA_Supplicant receives all the event
5908 generated due to DISASSOC call to the fw to keep
5909 the state fw and WPA_Supplicant state consistent
5910 */
5911 brcmf_delay(500);
5912 }
5913
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005914 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07005915 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005916
Arend van Spriel5b435de2011-10-05 13:19:03 +02005917 return 0;
5918}
5919
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005920s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005921{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005922 struct brcmf_if *ifp = netdev_priv(ndev);
5923 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005924 s32 err = 0;
5925
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005926 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005927 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005928 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005929
5930 return err;
5931}
5932
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005933s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005934{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005935 struct brcmf_if *ifp = netdev_priv(ndev);
5936 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005937 s32 err = 0;
5938
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005939 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005940 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005941 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005942
5943 return err;
5944}
5945
Arend van Spriela7965fb2013-04-11 17:08:37 +02005946enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
5947{
5948 struct wireless_dev *wdev = &ifp->vif->wdev;
5949
5950 return wdev->iftype;
5951}
5952
Hante Meulemanbfe81972014-10-28 14:56:16 +01005953bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
5954 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01005955{
5956 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005957
5958 list_for_each_entry(vif, &cfg->vif_list, list) {
5959 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02005960 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005961 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02005962 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005963}
Arend van Sprield3c0b632013-02-08 15:53:37 +01005964
5965static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
5966 u8 action)
5967{
5968 u8 evt_action;
5969
5970 mutex_lock(&event->vif_event_lock);
5971 evt_action = event->action;
5972 mutex_unlock(&event->vif_event_lock);
5973 return evt_action == action;
5974}
5975
5976void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
5977 struct brcmf_cfg80211_vif *vif)
5978{
5979 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5980
5981 mutex_lock(&event->vif_event_lock);
5982 event->vif = vif;
5983 event->action = 0;
5984 mutex_unlock(&event->vif_event_lock);
5985}
5986
5987bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
5988{
5989 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5990 bool armed;
5991
5992 mutex_lock(&event->vif_event_lock);
5993 armed = event->vif != NULL;
5994 mutex_unlock(&event->vif_event_lock);
5995
5996 return armed;
5997}
5998int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
5999 u8 action, ulong timeout)
6000{
6001 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6002
6003 return wait_event_timeout(event->vif_wq,
6004 vif_event_equals(event, action), timeout);
6005}
6006
Arend van Spriel63db1a42014-12-21 12:43:51 +01006007static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6008 struct regulatory_request *req)
6009{
6010 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6011 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6012 struct brcmf_fil_country_le ccreq;
6013 int i;
6014
6015 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6016 req->alpha2[0], req->alpha2[1]);
6017
6018 /* ignore non-ISO3166 country codes */
6019 for (i = 0; i < sizeof(req->alpha2); i++)
6020 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6021 brcmf_err("not a ISO3166 code\n");
6022 return;
6023 }
6024 memset(&ccreq, 0, sizeof(ccreq));
6025 ccreq.rev = cpu_to_le32(-1);
6026 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006027 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6028 brcmf_err("firmware rejected country setting\n");
6029 return;
6030 }
6031 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006032}
6033
Arend van Sprielb48d8912014-07-12 08:49:41 +02006034static void brcmf_free_wiphy(struct wiphy *wiphy)
6035{
Arend van Spriel58de92d2015-04-14 20:10:24 +02006036 if (!wiphy)
6037 return;
6038
Arend van Sprielb48d8912014-07-12 08:49:41 +02006039 kfree(wiphy->iface_combinations);
6040 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6041 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6042 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6043 }
6044 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6045 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6046 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6047 }
6048 wiphy_free(wiphy);
6049}
6050
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006051struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
6052 struct device *busdev)
6053{
6054 struct net_device *ndev = drvr->iflist[0]->ndev;
6055 struct brcmf_cfg80211_info *cfg;
6056 struct wiphy *wiphy;
6057 struct brcmf_cfg80211_vif *vif;
6058 struct brcmf_if *ifp;
6059 s32 err = 0;
6060 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006061 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006062
6063 if (!ndev) {
6064 brcmf_err("ndev is invalid\n");
6065 return NULL;
6066 }
6067
6068 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006069 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6070 if (!wiphy) {
6071 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006072 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006073 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006074 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006075 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006076
6077 cfg = wiphy_priv(wiphy);
6078 cfg->wiphy = wiphy;
6079 cfg->pub = drvr;
6080 init_vif_event(&cfg->vif_event);
6081 INIT_LIST_HEAD(&cfg->vif_list);
6082
6083 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006084 if (IS_ERR(vif))
6085 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006086
6087 vif->ifp = ifp;
6088 vif->wdev.netdev = ndev;
6089 ndev->ieee80211_ptr = &vif->wdev;
6090 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6091
6092 err = wl_init_priv(cfg);
6093 if (err) {
6094 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006095 brcmf_free_vif(vif);
6096 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006097 }
6098 ifp->vif = vif;
6099
Arend van Sprielb48d8912014-07-12 08:49:41 +02006100 /* determine d11 io type before wiphy setup */
6101 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006102 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006103 brcmf_err("Failed to get D11 version (%d)\n", err);
6104 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006105 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006106 cfg->d11inf.io_type = (u8)io_type;
6107 brcmu_d11_attach(&cfg->d11inf);
6108
6109 err = brcmf_setup_wiphy(wiphy, ifp);
6110 if (err < 0)
6111 goto priv_out;
6112
6113 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006114 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006115 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6116 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6117
6118 /* firmware defaults to 40MHz disabled in 2G band. We signal
6119 * cfg80211 here that we do and have it decide we can enable
6120 * it. But first check if device does support 2G operation.
6121 */
6122 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6123 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6124 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6125 }
6126 err = wiphy_register(wiphy);
6127 if (err < 0) {
6128 brcmf_err("Could not register wiphy device (%d)\n", err);
6129 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006130 }
6131
6132 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6133 * setup 40MHz in 2GHz band and enable OBSS scanning.
6134 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006135 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6136 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006137 if (!err)
6138 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6139 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006140 else
6141 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006142 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006143
6144 err = brcmf_p2p_attach(cfg);
6145 if (err) {
6146 brcmf_err("P2P initilisation failed (%d)\n", err);
6147 goto wiphy_unreg_out;
6148 }
6149 err = brcmf_btcoex_attach(cfg);
6150 if (err) {
6151 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6152 brcmf_p2p_detach(&cfg->p2p);
6153 goto wiphy_unreg_out;
6154 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006155
6156 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6157 if (err) {
6158 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6159 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman70b7d942014-07-30 13:20:07 +02006160 } else {
6161 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6162 brcmf_notify_tdls_peer_event);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006163 }
6164
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006165 return cfg;
6166
Arend van Sprielb48d8912014-07-12 08:49:41 +02006167wiphy_unreg_out:
6168 wiphy_unregister(cfg->wiphy);
6169priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006170 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006171 brcmf_free_vif(vif);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006172wiphy_out:
6173 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006174 return NULL;
6175}
6176
6177void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6178{
6179 if (!cfg)
6180 return;
6181
6182 WARN_ON(!list_empty(&cfg->vif_list));
6183 wiphy_unregister(cfg->wiphy);
6184 brcmf_btcoex_detach(cfg);
Arend van Sprielc3da74b2014-07-12 08:49:42 +02006185 brcmf_p2p_detach(&cfg->p2p);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006186 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006187 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006188}