blob: 4aebb2a9d3aafd9986bd0f0053cfdf9262254582 [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
18
19#include <linux/kernel.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <linux/etherdevice.h>
Hante Meuleman68ca3952014-02-25 20:30:26 +010021#include <linux/module.h>
Franky Lin1bacb042014-06-21 12:11:16 +020022#include <linux/vmalloc.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020024#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020025
26#include <brcmu_utils.h>
27#include <defs.h>
28#include <brcmu_wifi.h>
Hante Meuleman122d3d02014-10-28 14:56:18 +010029#include "core.h"
Hante Meulemana8e8ed32014-10-28 14:56:13 +010030#include "debug.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020031#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010032#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010033#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020034#include "btcoex.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010035#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020036#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070037#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020038#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020039#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010040#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010041#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020042
Arend van Spriele5806072012-09-19 22:21:08 +020043#define BRCMF_SCAN_IE_LEN_MAX 2048
44#define BRCMF_PNO_VERSION 2
45#define BRCMF_PNO_TIME 30
46#define BRCMF_PNO_REPEAT 4
47#define BRCMF_PNO_FREQ_EXPO_MAX 3
48#define BRCMF_PNO_MAX_PFN_COUNT 16
49#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
50#define BRCMF_PNO_HIDDEN_BIT 2
51#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
52#define BRCMF_PNO_SCAN_COMPLETE 1
53#define BRCMF_PNO_SCAN_INCOMPLETE 0
54
Hante Meuleman1a873342012-09-27 14:17:54 +020055#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010059#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020060
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
75#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
76#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
77
78#define VNDR_IE_CMD_LEN 4 /* length of the set command
79 * string :"add", "del" (+ NUL)
80 */
81#define VNDR_IE_COUNT_OFFSET 4
82#define VNDR_IE_PKTFLAG_OFFSET 8
83#define VNDR_IE_VSIE_OFFSET 12
84#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010085#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020086
87#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
88#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020089
Hante Meuleman89286dc2013-02-08 15:53:46 +010090#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
91#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
92#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
93
Hante Meuleman1678ba82015-12-10 13:43:00 +010094#define BRCMF_SCAN_CHANNEL_TIME 40
95#define BRCMF_SCAN_UNASSOC_TIME 40
96#define BRCMF_SCAN_PASSIVE_TIME 120
97
Arend van Spriel5b435de2011-10-05 13:19:03 +020098#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
99 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
100
Arend van Sprielce81e312012-10-22 13:55:37 -0700101static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200102{
Arend van Sprielc1179032012-10-22 13:55:33 -0700103 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100104 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
105 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200106 return false;
107 }
108 return true;
109}
110
Arend van Spriel5b435de2011-10-05 13:19:03 +0200111#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
112#define RATETAB_ENT(_rateid, _flags) \
113 { \
114 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
115 .hw_value = (_rateid), \
116 .flags = (_flags), \
117 }
118
119static struct ieee80211_rate __wl_rates[] = {
120 RATETAB_ENT(BRCM_RATE_1M, 0),
121 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
122 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
123 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
124 RATETAB_ENT(BRCM_RATE_6M, 0),
125 RATETAB_ENT(BRCM_RATE_9M, 0),
126 RATETAB_ENT(BRCM_RATE_12M, 0),
127 RATETAB_ENT(BRCM_RATE_18M, 0),
128 RATETAB_ENT(BRCM_RATE_24M, 0),
129 RATETAB_ENT(BRCM_RATE_36M, 0),
130 RATETAB_ENT(BRCM_RATE_48M, 0),
131 RATETAB_ENT(BRCM_RATE_54M, 0),
132};
133
Arend van Spriel5b435de2011-10-05 13:19:03 +0200134#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200135#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
136#define wl_a_rates (__wl_rates + 4)
137#define wl_a_rates_size (wl_g_rates_size - 4)
138
139#define CHAN2G(_channel, _freq) { \
140 .band = IEEE80211_BAND_2GHZ, \
141 .center_freq = (_freq), \
142 .hw_value = (_channel), \
143 .flags = IEEE80211_CHAN_DISABLED, \
144 .max_antenna_gain = 0, \
145 .max_power = 30, \
146}
147
148#define CHAN5G(_channel) { \
149 .band = IEEE80211_BAND_5GHZ, \
150 .center_freq = 5000 + (5 * (_channel)), \
151 .hw_value = (_channel), \
152 .flags = IEEE80211_CHAN_DISABLED, \
153 .max_antenna_gain = 0, \
154 .max_power = 30, \
155}
156
157static struct ieee80211_channel __wl_2ghz_channels[] = {
158 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
159 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
160 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
161 CHAN2G(13, 2472), CHAN2G(14, 2484)
162};
163
164static struct ieee80211_channel __wl_5ghz_channels[] = {
165 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
166 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
167 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
168 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
169 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
170 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
171};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200172
Arend van Sprielb48d8912014-07-12 08:49:41 +0200173/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200174 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200175 */
176static const struct ieee80211_supported_band __wl_band_2ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200177 .band = IEEE80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200178 .bitrates = wl_g_rates,
179 .n_bitrates = wl_g_rates_size,
180};
181
Arend van Spriel58de92d2015-04-14 20:10:24 +0200182static const struct ieee80211_supported_band __wl_band_5ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200183 .band = IEEE80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200184 .bitrates = wl_a_rates,
185 .n_bitrates = wl_a_rates_size,
186};
187
Hante Meulemand48200b2013-04-03 12:40:29 +0200188/* This is to override regulatory domains defined in cfg80211 module (reg.c)
189 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200190 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
191 * With respect to these flags, wpa_supplicant doesn't * start p2p
192 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200193 * domain are to be done here.
194 */
195static const struct ieee80211_regdomain brcmf_regdom = {
196 .n_reg_rules = 4,
197 .alpha2 = "99",
198 .reg_rules = {
199 /* IEEE 802.11b/g, channels 1..11 */
200 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
201 /* If any */
202 /* IEEE 802.11 channel 14 - Only JP enables
203 * this and for 802.11b only
204 */
205 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
206 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200207 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200208 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200209 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200210};
211
212static const u32 __wl_cipher_suites[] = {
213 WLAN_CIPHER_SUITE_WEP40,
214 WLAN_CIPHER_SUITE_WEP104,
215 WLAN_CIPHER_SUITE_TKIP,
216 WLAN_CIPHER_SUITE_CCMP,
217 WLAN_CIPHER_SUITE_AES_CMAC,
218};
219
Hante Meuleman1a873342012-09-27 14:17:54 +0200220/* Vendor specific ie. id = 221, oui and type defines exact ie */
221struct brcmf_vs_tlv {
222 u8 id;
223 u8 len;
224 u8 oui[3];
225 u8 oui_type;
226};
227
228struct parsed_vndr_ie_info {
229 u8 *ie_ptr;
230 u32 ie_len; /* total length including id & length field */
231 struct brcmf_vs_tlv vndrie;
232};
233
234struct parsed_vndr_ies {
235 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100236 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200237};
238
Hante Meuleman68ca3952014-02-25 20:30:26 +0100239static int brcmf_roamoff;
240module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
241MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
242
Alwin Beukersef6ac172011-10-12 20:51:26 +0200243
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200244static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
245 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200246{
247 struct brcmu_chan ch_inf;
248 s32 primary_offset;
249
250 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
251 ch->chan->center_freq, ch->center_freq1, ch->width);
252 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
253 primary_offset = ch->center_freq1 - ch->chan->center_freq;
254 switch (ch->width) {
255 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100256 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200257 ch_inf.bw = BRCMU_CHAN_BW_20;
258 WARN_ON(primary_offset != 0);
259 break;
260 case NL80211_CHAN_WIDTH_40:
261 ch_inf.bw = BRCMU_CHAN_BW_40;
262 if (primary_offset < 0)
263 ch_inf.sb = BRCMU_CHAN_SB_U;
264 else
265 ch_inf.sb = BRCMU_CHAN_SB_L;
266 break;
267 case NL80211_CHAN_WIDTH_80:
268 ch_inf.bw = BRCMU_CHAN_BW_80;
269 if (primary_offset < 0) {
270 if (primary_offset < -CH_10MHZ_APART)
271 ch_inf.sb = BRCMU_CHAN_SB_UU;
272 else
273 ch_inf.sb = BRCMU_CHAN_SB_UL;
274 } else {
275 if (primary_offset > CH_10MHZ_APART)
276 ch_inf.sb = BRCMU_CHAN_SB_LL;
277 else
278 ch_inf.sb = BRCMU_CHAN_SB_LU;
279 }
280 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100281 case NL80211_CHAN_WIDTH_80P80:
282 case NL80211_CHAN_WIDTH_160:
283 case NL80211_CHAN_WIDTH_5:
284 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200285 default:
286 WARN_ON_ONCE(1);
287 }
288 switch (ch->chan->band) {
289 case IEEE80211_BAND_2GHZ:
290 ch_inf.band = BRCMU_CHAN_BAND_2G;
291 break;
292 case IEEE80211_BAND_5GHZ:
293 ch_inf.band = BRCMU_CHAN_BAND_5G;
294 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100295 case IEEE80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200296 default:
297 WARN_ON_ONCE(1);
298 }
299 d11inf->encchspec(&ch_inf);
300
301 return ch_inf.chspec;
302}
303
Franky Lin83cf17a2013-04-11 13:28:50 +0200304u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
305 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700306{
Franky Lin83cf17a2013-04-11 13:28:50 +0200307 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700308
Franky Lin83cf17a2013-04-11 13:28:50 +0200309 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
310 ch_inf.bw = BRCMU_CHAN_BW_20;
311 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700312
Franky Lin83cf17a2013-04-11 13:28:50 +0200313 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700314}
315
Hante Meuleman89286dc2013-02-08 15:53:46 +0100316/* Traverse a string of 1-byte tag/1-byte length/variable-length value
317 * triples, returning a pointer to the substring whose first element
318 * matches tag
319 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100320const struct brcmf_tlv *
321brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100322{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100323 const struct brcmf_tlv *elt = buf;
324 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100325
326 /* find tagged parameter */
327 while (totlen >= TLV_HDR_LEN) {
328 int len = elt->len;
329
330 /* validate remaining totlen */
331 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
332 return elt;
333
334 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
335 totlen -= (len + TLV_HDR_LEN);
336 }
337
338 return NULL;
339}
340
341/* Is any of the tlvs the expected entry? If
342 * not update the tlvs buffer pointer/length.
343 */
344static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100345brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
346 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100347{
348 /* If the contents match the OUI and the type */
349 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
350 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
351 type == ie[TLV_BODY_OFF + oui_len]) {
352 return true;
353 }
354
355 if (tlvs == NULL)
356 return false;
357 /* point to the next ie */
358 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
359 /* calculate the length of the rest of the buffer */
360 *tlvs_len -= (int)(ie - *tlvs);
361 /* update the pointer to the start of the buffer */
362 *tlvs = ie;
363
364 return false;
365}
366
367static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100368brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100369{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100370 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100371
372 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100373 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100374 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
375 return (struct brcmf_vs_tlv *)ie;
376 }
377 return NULL;
378}
379
380static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100381brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100382{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100383 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100384
385 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
386 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
387 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
388 return (struct brcmf_vs_tlv *)ie;
389 }
390 return NULL;
391}
392
Arend van Spriel39504a22015-08-20 22:06:05 +0200393static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
394 struct brcmf_cfg80211_vif *vif,
395 enum nl80211_iftype new_type)
396{
397 int iftype_num[NUM_NL80211_IFTYPES];
398 struct brcmf_cfg80211_vif *pos;
399
400 memset(&iftype_num[0], 0, sizeof(iftype_num));
401 list_for_each_entry(pos, &cfg->vif_list, list)
402 if (pos == vif)
403 iftype_num[new_type]++;
404 else
405 iftype_num[pos->wdev.iftype]++;
406
407 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
408}
409
410static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
411 enum nl80211_iftype new_type)
412{
413 int iftype_num[NUM_NL80211_IFTYPES];
414 struct brcmf_cfg80211_vif *pos;
415
416 memset(&iftype_num[0], 0, sizeof(iftype_num));
417 list_for_each_entry(pos, &cfg->vif_list, list)
418 iftype_num[pos->wdev.iftype]++;
419
420 iftype_num[new_type]++;
421 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
422}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100423
Arend van Spriel5b435de2011-10-05 13:19:03 +0200424static void convert_key_from_CPU(struct brcmf_wsec_key *key,
425 struct brcmf_wsec_key_le *key_le)
426{
427 key_le->index = cpu_to_le32(key->index);
428 key_le->len = cpu_to_le32(key->len);
429 key_le->algo = cpu_to_le32(key->algo);
430 key_le->flags = cpu_to_le32(key->flags);
431 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
432 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
433 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
434 memcpy(key_le->data, key->data, sizeof(key->data));
435 memcpy(key_le->ea, key->ea, sizeof(key->ea));
436}
437
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200438static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100439send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200440{
441 int err;
442 struct brcmf_wsec_key_le key_le;
443
444 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200445
Hante Meuleman118eb302014-12-21 12:43:49 +0100446 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700447
Hante Meuleman118eb302014-12-21 12:43:49 +0100448 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700449 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200450
Arend van Spriel5b435de2011-10-05 13:19:03 +0200451 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100452 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200453 return err;
454}
455
Hante Meulemanb3657452013-05-27 21:09:53 +0200456static s32
457brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
458{
459 s32 err;
460 u32 mode;
461
462 if (enable)
463 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
464 else
465 mode = 0;
466
467 /* Try to set and enable ARP offload feature, this may fail, then it */
468 /* is simply not supported and err 0 will be returned */
469 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
470 if (err) {
471 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
472 mode, err);
473 err = 0;
474 } else {
475 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
476 if (err) {
477 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
478 enable, err);
479 err = 0;
480 } else
481 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
482 enable, mode);
483 }
484
485 return err;
486}
487
Hante Meuleman8851cce2014-07-30 13:20:02 +0200488static void
489brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
490{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200491 struct brcmf_cfg80211_vif *vif;
492 struct brcmf_if *ifp;
493
494 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
495 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200496
497 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
498 (wdev->iftype == NL80211_IFTYPE_AP) ||
499 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
500 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
501 ADDR_DIRECT);
502 else
503 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
504 ADDR_INDIRECT);
505}
506
Hante Meulemana44aa402014-12-03 21:05:33 +0100507static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
508{
509 struct brcmf_mbss_ssid_le mbss_ssid_le;
510 int bsscfgidx;
511 int err;
512
513 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
514 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
515 if (bsscfgidx < 0)
516 return bsscfgidx;
517
518 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
519 mbss_ssid_le.SSID_len = cpu_to_le32(5);
520 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
521
522 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
523 sizeof(mbss_ssid_le));
524 if (err < 0)
525 brcmf_err("setting ssid failed %d\n", err);
526
527 return err;
528}
529
530/**
531 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
532 *
533 * @wiphy: wiphy device of new interface.
534 * @name: name of the new interface.
535 * @flags: not used.
536 * @params: contains mac address for AP device.
537 */
538static
539struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
540 u32 *flags, struct vif_params *params)
541{
542 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
543 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
544 struct brcmf_cfg80211_vif *vif;
545 int err;
546
547 if (brcmf_cfg80211_vif_event_armed(cfg))
548 return ERR_PTR(-EBUSY);
549
550 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
551
552 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
553 if (IS_ERR(vif))
554 return (struct wireless_dev *)vif;
555
556 brcmf_cfg80211_arm_vif_event(cfg, vif);
557
558 err = brcmf_cfg80211_request_ap_if(ifp);
559 if (err) {
560 brcmf_cfg80211_arm_vif_event(cfg, NULL);
561 goto fail;
562 }
563
564 /* wait for firmware event */
565 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
566 msecs_to_jiffies(1500));
567 brcmf_cfg80211_arm_vif_event(cfg, NULL);
568 if (!err) {
569 brcmf_err("timeout occurred\n");
570 err = -EIO;
571 goto fail;
572 }
573
574 /* interface created in firmware */
575 ifp = vif->ifp;
576 if (!ifp) {
577 brcmf_err("no if pointer provided\n");
578 err = -ENOENT;
579 goto fail;
580 }
581
582 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
583 err = brcmf_net_attach(ifp, true);
584 if (err) {
585 brcmf_err("Registering netdevice failed\n");
586 goto fail;
587 }
588
589 return &ifp->vif->wdev;
590
591fail:
592 brcmf_free_vif(vif);
593 return ERR_PTR(err);
594}
595
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100596static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
597{
598 enum nl80211_iftype iftype;
599
600 iftype = vif->wdev.iftype;
601 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
602}
603
604static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
605{
606 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
607}
608
Arend van Spriel9f440b72013-02-08 15:53:36 +0100609static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
610 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100611 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100612 enum nl80211_iftype type,
613 u32 *flags,
614 struct vif_params *params)
615{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200616 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200617 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200618
Arend van Spriel9f440b72013-02-08 15:53:36 +0100619 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200620 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
621 if (err) {
622 brcmf_err("iface validation failed: err=%d\n", err);
623 return ERR_PTR(err);
624 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100625 switch (type) {
626 case NL80211_IFTYPE_ADHOC:
627 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100628 case NL80211_IFTYPE_AP_VLAN:
629 case NL80211_IFTYPE_WDS:
630 case NL80211_IFTYPE_MONITOR:
631 case NL80211_IFTYPE_MESH_POINT:
632 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100633 case NL80211_IFTYPE_AP:
634 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
635 if (!IS_ERR(wdev))
636 brcmf_cfg80211_update_proto_addr_mode(wdev);
637 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100638 case NL80211_IFTYPE_P2P_CLIENT:
639 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200640 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100641 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200642 if (!IS_ERR(wdev))
643 brcmf_cfg80211_update_proto_addr_mode(wdev);
644 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100645 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100646 default:
647 return ERR_PTR(-EINVAL);
648 }
649}
650
Daniel Kim5e787f72014-06-21 12:11:18 +0200651static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
652{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200653 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200654 brcmf_set_mpc(ifp, mpc);
655}
656
Arend van Sprielf96aa072013-04-05 10:57:48 +0200657void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100658{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100659 s32 err = 0;
660
661 if (check_vif_up(ifp->vif)) {
662 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
663 if (err) {
664 brcmf_err("fail to set mpc\n");
665 return;
666 }
667 brcmf_dbg(INFO, "MPC : %d\n", mpc);
668 }
669}
670
Arend van Spriela0f472a2013-04-05 10:57:49 +0200671s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
672 struct brcmf_if *ifp, bool aborted,
673 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100674{
675 struct brcmf_scan_params_le params_le;
676 struct cfg80211_scan_request *scan_request;
677 s32 err = 0;
678
679 brcmf_dbg(SCAN, "Enter\n");
680
681 /* clear scan request, because the FW abort can cause a second call */
682 /* to this functon and might cause a double cfg80211_scan_done */
683 scan_request = cfg->scan_request;
684 cfg->scan_request = NULL;
685
686 if (timer_pending(&cfg->escan_timeout))
687 del_timer_sync(&cfg->escan_timeout);
688
689 if (fw_abort) {
690 /* Do a scan abort to stop the driver's scan engine */
691 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
692 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800693 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100694 params_le.bss_type = DOT11_BSSTYPE_ANY;
695 params_le.scan_type = 0;
696 params_le.channel_num = cpu_to_le32(1);
697 params_le.nprobes = cpu_to_le32(1);
698 params_le.active_time = cpu_to_le32(-1);
699 params_le.passive_time = cpu_to_le32(-1);
700 params_le.home_time = cpu_to_le32(-1);
701 /* Scan is aborted by setting channel_list[0] to -1 */
702 params_le.channel_list[0] = cpu_to_le16(-1);
703 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200704 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100705 &params_le, sizeof(params_le));
706 if (err)
707 brcmf_err("Scan abort failed\n");
708 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200709
Daniel Kim5e787f72014-06-21 12:11:18 +0200710 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200711
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100712 /*
713 * e-scan can be initiated by scheduled scan
714 * which takes precedence.
715 */
716 if (cfg->sched_escan) {
717 brcmf_dbg(SCAN, "scheduled scan completed\n");
718 cfg->sched_escan = false;
719 if (!aborted)
720 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100721 } else if (scan_request) {
722 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
723 aborted ? "Aborted" : "Done");
724 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100725 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100726 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
727 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100728
729 return err;
730}
731
Arend van Spriel9f440b72013-02-08 15:53:36 +0100732static
733int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
734{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100735 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
736 struct net_device *ndev = wdev->netdev;
737
738 /* vif event pending in firmware */
739 if (brcmf_cfg80211_vif_event_armed(cfg))
740 return -EBUSY;
741
742 if (ndev) {
743 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200744 cfg->escan_info.ifp == netdev_priv(ndev))
745 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
746 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100747
748 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
749 }
750
Arend van Spriel9f440b72013-02-08 15:53:36 +0100751 switch (wdev->iftype) {
752 case NL80211_IFTYPE_ADHOC:
753 case NL80211_IFTYPE_STATION:
754 case NL80211_IFTYPE_AP:
755 case NL80211_IFTYPE_AP_VLAN:
756 case NL80211_IFTYPE_WDS:
757 case NL80211_IFTYPE_MONITOR:
758 case NL80211_IFTYPE_MESH_POINT:
759 return -EOPNOTSUPP;
760 case NL80211_IFTYPE_P2P_CLIENT:
761 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200762 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100763 return brcmf_p2p_del_vif(wiphy, wdev);
764 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100765 default:
766 return -EINVAL;
767 }
768 return -EOPNOTSUPP;
769}
770
Arend van Spriel5b435de2011-10-05 13:19:03 +0200771static s32
772brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
773 enum nl80211_iftype type, u32 *flags,
774 struct vif_params *params)
775{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100776 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700777 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100778 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200779 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200780 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200781 s32 err = 0;
782
Hante Meuleman37a869e2015-10-29 20:33:17 +0100783 brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
784 type);
Hante Meuleman178e9ef2015-09-18 22:08:11 +0200785
786 /* WAR: There are a number of p2p interface related problems which
787 * need to be handled initially (before doing the validate).
788 * wpa_supplicant tends to do iface changes on p2p device/client/go
789 * which are not always possible/allowed. However we need to return
790 * OK otherwise the wpa_supplicant wont start. The situation differs
791 * on configuration and setup (p2pon=1 module param). The first check
792 * is to see if the request is a change to station for p2p iface.
793 */
794 if ((type == NL80211_IFTYPE_STATION) &&
795 ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
796 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
797 (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
798 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
799 /* Now depending on whether module param p2pon=1 was used the
800 * response needs to be either 0 or EOPNOTSUPP. The reason is
801 * that if p2pon=1 is used, but a newer supplicant is used then
802 * we should return an error, as this combination wont work.
803 * In other situations 0 is returned and supplicant will start
804 * normally. It will give a trace in cfg80211, but it is the
805 * only way to get it working. Unfortunately this will result
806 * in situation where we wont support new supplicant in
807 * combination with module param p2pon=1, but that is the way
808 * it is. If the user tries this then unloading of driver might
809 * fail/lock.
810 */
811 if (cfg->p2p.p2pdev_dynamically)
812 return -EOPNOTSUPP;
813 else
814 return 0;
815 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200816 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
817 if (err) {
818 brcmf_err("iface validation failed: err=%d\n", err);
819 return err;
820 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200821 switch (type) {
822 case NL80211_IFTYPE_MONITOR:
823 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100824 brcmf_err("type (%d) : currently we do not support this type\n",
825 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200826 return -EOPNOTSUPP;
827 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200828 infra = 0;
829 break;
830 case NL80211_IFTYPE_STATION:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200831 infra = 1;
832 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200833 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100834 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200835 ap = 1;
836 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200837 default:
838 err = -EINVAL;
839 goto done;
840 }
841
Hante Meuleman1a873342012-09-27 14:17:54 +0200842 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100843 if (type == NL80211_IFTYPE_P2P_GO) {
844 brcmf_dbg(INFO, "IF Type = P2P GO\n");
845 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
846 }
847 if (!err) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100848 brcmf_dbg(INFO, "IF Type = AP\n");
849 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200850 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100851 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200852 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100853 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200854 err = -EAGAIN;
855 goto done;
856 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100857 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100858 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200859 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200860 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200861
Hante Meuleman8851cce2014-07-30 13:20:02 +0200862 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
863
Arend van Spriel5b435de2011-10-05 13:19:03 +0200864done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100865 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200866
867 return err;
868}
869
Franky Lin83cf17a2013-04-11 13:28:50 +0200870static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
871 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200872 struct cfg80211_scan_request *request)
873{
874 u32 n_ssids;
875 u32 n_channels;
876 s32 i;
877 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200878 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200879 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200880 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200881
Joe Perches93803b32015-03-02 19:54:49 -0800882 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200883 params_le->bss_type = DOT11_BSSTYPE_ANY;
884 params_le->scan_type = 0;
885 params_le->channel_num = 0;
886 params_le->nprobes = cpu_to_le32(-1);
887 params_le->active_time = cpu_to_le32(-1);
888 params_le->passive_time = cpu_to_le32(-1);
889 params_le->home_time = cpu_to_le32(-1);
890 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
891
892 /* if request is null exit so it will be all channel broadcast scan */
893 if (!request)
894 return;
895
896 n_ssids = request->n_ssids;
897 n_channels = request->n_channels;
898 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100899 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
900 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200901 if (n_channels > 0) {
902 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200903 chanspec = channel_to_chanspec(&cfg->d11inf,
904 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100905 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
906 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200907 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200908 }
909 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100910 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200911 }
912 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100913 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200914 if (n_ssids > 0) {
915 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
916 n_channels * sizeof(u16);
917 offset = roundup(offset, sizeof(u32));
918 ptr = (char *)params_le + offset;
919 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200920 memset(&ssid_le, 0, sizeof(ssid_le));
921 ssid_le.SSID_len =
922 cpu_to_le32(request->ssids[i].ssid_len);
923 memcpy(ssid_le.SSID, request->ssids[i].ssid,
924 request->ssids[i].ssid_len);
925 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100926 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200927 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100928 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
929 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200930 memcpy(ptr, &ssid_le, sizeof(ssid_le));
931 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200932 }
933 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100934 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200935 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100936 brcmf_dbg(SCAN, "SSID %s len=%d\n",
937 params_le->ssid_le.SSID,
938 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200939 params_le->ssid_le.SSID_len =
940 cpu_to_le32(request->ssids->ssid_len);
941 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
942 request->ssids->ssid_len);
943 }
944 }
945 /* Adding mask to channel numbers */
946 params_le->channel_num =
947 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
948 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
949}
950
951static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200952brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemanc4958102015-11-25 11:32:41 +0100953 struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200954{
955 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
956 offsetof(struct brcmf_escan_params_le, params_le);
957 struct brcmf_escan_params_le *params;
958 s32 err = 0;
959
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100960 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200961
962 if (request != NULL) {
963 /* Allocate space for populating ssids in struct */
964 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
965
966 /* Allocate space for populating ssids in struct */
Hante Meulemane9a6ca82015-11-25 11:32:37 +0100967 params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
Hante Meulemane756af52012-09-11 21:18:52 +0200968 }
969
970 params = kzalloc(params_size, GFP_KERNEL);
971 if (!params) {
972 err = -ENOMEM;
973 goto exit;
974 }
975 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200976 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200977 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
Hante Meulemanc4958102015-11-25 11:32:41 +0100978 params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200979 params->sync_id = cpu_to_le16(0x1234);
980
Arend van Spriela0f472a2013-04-05 10:57:49 +0200981 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200982 if (err) {
983 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100984 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200985 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100986 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200987 }
988
989 kfree(params);
990exit:
991 return err;
992}
993
994static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200995brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +0200996 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200997{
998 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700999 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001000 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001001 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001002
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001003 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001004 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001005 escan->wiphy = wiphy;
1006 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001007 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001008 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001009 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001010 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001011 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001012 return err;
1013 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001014 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001015 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001016 results->version = 0;
1017 results->count = 0;
1018 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1019
Hante Meulemanc4958102015-11-25 11:32:41 +01001020 err = escan->run(cfg, ifp, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001021 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001022 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001023 return err;
1024}
1025
1026static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001027brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001028 struct cfg80211_scan_request *request,
1029 struct cfg80211_ssid *this_ssid)
1030{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001031 struct brcmf_if *ifp = vif->ifp;
1032 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001033 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -08001034 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001035 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001036 bool escan_req;
1037 bool spec_scan;
1038 s32 err;
1039 u32 SSID_len;
1040
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001041 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001042
Arend van Sprielc1179032012-10-22 13:55:33 -07001043 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001044 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001045 return -EAGAIN;
1046 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001047 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001048 brcmf_err("Scanning being aborted: status (%lu)\n",
1049 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001050 return -EAGAIN;
1051 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001052 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1053 brcmf_err("Scanning suppressed: status (%lu)\n",
1054 cfg->scan_status);
1055 return -EAGAIN;
1056 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001057 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001058 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001059 return -EAGAIN;
1060 }
1061
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001062 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001063 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1064 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001065
Hante Meulemane756af52012-09-11 21:18:52 +02001066 escan_req = false;
1067 if (request) {
1068 /* scan bss */
1069 ssids = request->ssids;
1070 escan_req = true;
1071 } else {
1072 /* scan in ibss */
1073 /* we don't do escan in ibss */
1074 ssids = this_ssid;
1075 }
1076
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001077 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001078 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001079 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001080 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001081 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001082 if (err)
1083 goto scan_out;
1084
Arend van Spriela0f472a2013-04-05 10:57:49 +02001085 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001086 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001087 goto scan_out;
1088 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001089 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1090 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001091 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1092 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1093 sr->ssid_le.SSID_len = cpu_to_le32(0);
1094 spec_scan = false;
1095 if (SSID_len) {
1096 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1097 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1098 spec_scan = true;
1099 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001100 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001101
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001102 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001103 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001104 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001105 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001106 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001107 goto scan_out;
1108 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001109 brcmf_scan_config_mpc(ifp, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -07001110 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -07001111 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001112 if (err) {
1113 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001114 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
1115 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001116 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001117 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001118
Daniel Kim5e787f72014-06-21 12:11:18 +02001119 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001120 goto scan_out;
1121 }
1122 }
1123
Hante Meuleman661fa952015-02-06 18:36:47 +01001124 /* Arm scan timeout timer */
1125 mod_timer(&cfg->escan_timeout, jiffies +
1126 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1127
Hante Meulemane756af52012-09-11 21:18:52 +02001128 return 0;
1129
1130scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001131 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001132 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001133 return err;
1134}
1135
Arend van Spriel5b435de2011-10-05 13:19:03 +02001136static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001137brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001138{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001139 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001140 s32 err = 0;
1141
Arend van Sprield96b8012012-12-05 15:26:02 +01001142 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001143 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1144 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001145 return -EIO;
1146
Arend van Spriela0f472a2013-04-05 10:57:49 +02001147 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001148
Arend van Spriel5b435de2011-10-05 13:19:03 +02001149 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001150 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001151
Arend van Sprield96b8012012-12-05 15:26:02 +01001152 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001153 return err;
1154}
1155
1156static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1157{
1158 s32 err = 0;
1159
Arend van Sprielac24be62012-10-22 10:36:23 -07001160 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1161 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001162 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001163 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001164
1165 return err;
1166}
1167
1168static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1169{
1170 s32 err = 0;
1171
Arend van Sprielac24be62012-10-22 10:36:23 -07001172 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1173 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001174 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001175 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001176
1177 return err;
1178}
1179
1180static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1181{
1182 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001183 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184
Arend van Sprielac24be62012-10-22 10:36:23 -07001185 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001186 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001187 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001188 return err;
1189 }
1190 return err;
1191}
1192
1193static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1194{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001195 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1196 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001197 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001198 s32 err = 0;
1199
Arend van Sprield96b8012012-12-05 15:26:02 +01001200 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001201 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001202 return -EIO;
1203
1204 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001205 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1206 cfg->conf->rts_threshold = wiphy->rts_threshold;
1207 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001208 if (!err)
1209 goto done;
1210 }
1211 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001212 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1213 cfg->conf->frag_threshold = wiphy->frag_threshold;
1214 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001215 if (!err)
1216 goto done;
1217 }
1218 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001219 && (cfg->conf->retry_long != wiphy->retry_long)) {
1220 cfg->conf->retry_long = wiphy->retry_long;
1221 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001222 if (!err)
1223 goto done;
1224 }
1225 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001226 && (cfg->conf->retry_short != wiphy->retry_short)) {
1227 cfg->conf->retry_short = wiphy->retry_short;
1228 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001229 if (!err)
1230 goto done;
1231 }
1232
1233done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001234 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001235 return err;
1236}
1237
Arend van Spriel5b435de2011-10-05 13:19:03 +02001238static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1239{
1240 memset(prof, 0, sizeof(*prof));
1241}
1242
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001243static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1244{
1245 u16 reason;
1246
1247 switch (e->event_code) {
1248 case BRCMF_E_DEAUTH:
1249 case BRCMF_E_DEAUTH_IND:
1250 case BRCMF_E_DISASSOC_IND:
1251 reason = e->reason;
1252 break;
1253 case BRCMF_E_LINK:
1254 default:
1255 reason = 0;
1256 break;
1257 }
1258 return reason;
1259}
1260
1261static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001262{
Piotr Haber61730d42013-04-23 12:53:12 +02001263 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001264 s32 err = 0;
1265
Arend van Sprield96b8012012-12-05 15:26:02 +01001266 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001267
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001268 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001269 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001270 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001271 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001272 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001273 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001274 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001275 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001276 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
Johannes Berg80279fb2015-05-22 16:22:20 +02001277 true, GFP_KERNEL);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001278
Arend van Spriel5b435de2011-10-05 13:19:03 +02001279 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001280 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001281 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1282 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001283 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001284}
1285
1286static s32
1287brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1288 struct cfg80211_ibss_params *params)
1289{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001290 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001291 struct brcmf_if *ifp = netdev_priv(ndev);
1292 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001293 struct brcmf_join_params join_params;
1294 size_t join_params_size = 0;
1295 s32 err = 0;
1296 s32 wsec = 0;
1297 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001298 u16 chanspec;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001299 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001300
Arend van Sprield96b8012012-12-05 15:26:02 +01001301 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001302 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001303 return -EIO;
1304
1305 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001306 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001307 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001308 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001309 return -EOPNOTSUPP;
1310 }
1311
Arend van Sprielc1179032012-10-22 13:55:33 -07001312 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001313
1314 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001315 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001316 else
Arend van Spriel16886732012-12-05 15:26:04 +01001317 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001318
Johannes Berg683b6d32012-11-08 21:25:48 +01001319 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001320 brcmf_dbg(CONN, "channel: %d\n",
1321 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001322 else
Arend van Spriel16886732012-12-05 15:26:04 +01001323 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001324
1325 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001326 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001327 else
Arend van Spriel16886732012-12-05 15:26:04 +01001328 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001329
1330 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001331 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001332 else
Arend van Spriel16886732012-12-05 15:26:04 +01001333 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334
1335 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001336 brcmf_dbg(CONN, "beacon interval: %d\n",
1337 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001338 else
Arend van Spriel16886732012-12-05 15:26:04 +01001339 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001340
1341 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001342 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001343 else
Arend van Spriel16886732012-12-05 15:26:04 +01001344 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001345
1346 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001347 brcmf_dbg(CONN, "privacy 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 privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001350
1351 /* Configure Privacy for starter */
1352 if (params->privacy)
1353 wsec |= WEP_ENABLED;
1354
Arend van Sprielc1179032012-10-22 13:55:33 -07001355 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001356 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001357 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001358 goto done;
1359 }
1360
1361 /* Configure Beacon Interval for starter */
1362 if (params->beacon_interval)
1363 bcnprd = params->beacon_interval;
1364 else
1365 bcnprd = 100;
1366
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001367 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001368 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001369 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001370 goto done;
1371 }
1372
1373 /* Configure required join parameter */
1374 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1375
1376 /* SSID */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001377 ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1378 memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1379 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001380 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001381
1382 /* BSSID */
1383 if (params->bssid) {
1384 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001385 join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001386 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001387 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001388 eth_broadcast_addr(join_params.params_le.bssid);
1389 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001390 }
1391
Arend van Spriel5b435de2011-10-05 13:19:03 +02001392 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001393 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001394 u32 target_channel;
1395
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001396 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001397 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001398 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001399 if (params->channel_fixed) {
1400 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001401 chanspec = chandef_to_chanspec(&cfg->d11inf,
1402 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001403 join_params.params_le.chanspec_list[0] =
1404 cpu_to_le16(chanspec);
1405 join_params.params_le.chanspec_num = cpu_to_le32(1);
1406 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001407 }
1408
1409 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001410 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001411 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001412 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001413 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001414 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415 goto done;
1416 }
1417 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001418 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001419
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001420 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001421
1422
Arend van Sprielc1179032012-10-22 13:55:33 -07001423 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001424 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001425 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001426 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001427 goto done;
1428 }
1429
1430done:
1431 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001432 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001433 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001434 return err;
1435}
1436
1437static s32
1438brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1439{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001440 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001441
Arend van Sprield96b8012012-12-05 15:26:02 +01001442 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001443 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001444 return -EIO;
1445
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001446 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001447
Arend van Sprield96b8012012-12-05 15:26:02 +01001448 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001449
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001450 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001451}
1452
1453static s32 brcmf_set_wpa_version(struct net_device *ndev,
1454 struct cfg80211_connect_params *sme)
1455{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001456 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001457 struct brcmf_cfg80211_security *sec;
1458 s32 val = 0;
1459 s32 err = 0;
1460
1461 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1462 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1463 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1464 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1465 else
1466 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001467 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001468 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001469 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001470 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001471 return err;
1472 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001473 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001474 sec->wpa_versions = sme->crypto.wpa_versions;
1475 return err;
1476}
1477
1478static s32 brcmf_set_auth_type(struct net_device *ndev,
1479 struct cfg80211_connect_params *sme)
1480{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001481 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001482 struct brcmf_cfg80211_security *sec;
1483 s32 val = 0;
1484 s32 err = 0;
1485
1486 switch (sme->auth_type) {
1487 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1488 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001489 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001490 break;
1491 case NL80211_AUTHTYPE_SHARED_KEY:
1492 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001493 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001494 break;
1495 case NL80211_AUTHTYPE_AUTOMATIC:
1496 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001497 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001498 break;
1499 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001500 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001501 default:
1502 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001503 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001504 break;
1505 }
1506
Hante Meuleman89286dc2013-02-08 15:53:46 +01001507 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001508 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001509 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001510 return err;
1511 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001512 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001513 sec->auth_type = sme->auth_type;
1514 return err;
1515}
1516
1517static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001518brcmf_set_wsec_mode(struct net_device *ndev,
1519 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001520{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001521 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001522 struct brcmf_cfg80211_security *sec;
1523 s32 pval = 0;
1524 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001525 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001526 s32 err = 0;
1527
1528 if (sme->crypto.n_ciphers_pairwise) {
1529 switch (sme->crypto.ciphers_pairwise[0]) {
1530 case WLAN_CIPHER_SUITE_WEP40:
1531 case WLAN_CIPHER_SUITE_WEP104:
1532 pval = WEP_ENABLED;
1533 break;
1534 case WLAN_CIPHER_SUITE_TKIP:
1535 pval = TKIP_ENABLED;
1536 break;
1537 case WLAN_CIPHER_SUITE_CCMP:
1538 pval = AES_ENABLED;
1539 break;
1540 case WLAN_CIPHER_SUITE_AES_CMAC:
1541 pval = AES_ENABLED;
1542 break;
1543 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001544 brcmf_err("invalid cipher pairwise (%d)\n",
1545 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001546 return -EINVAL;
1547 }
1548 }
1549 if (sme->crypto.cipher_group) {
1550 switch (sme->crypto.cipher_group) {
1551 case WLAN_CIPHER_SUITE_WEP40:
1552 case WLAN_CIPHER_SUITE_WEP104:
1553 gval = WEP_ENABLED;
1554 break;
1555 case WLAN_CIPHER_SUITE_TKIP:
1556 gval = TKIP_ENABLED;
1557 break;
1558 case WLAN_CIPHER_SUITE_CCMP:
1559 gval = AES_ENABLED;
1560 break;
1561 case WLAN_CIPHER_SUITE_AES_CMAC:
1562 gval = AES_ENABLED;
1563 break;
1564 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001565 brcmf_err("invalid cipher group (%d)\n",
1566 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001567 return -EINVAL;
1568 }
1569 }
1570
Arend van Spriel16886732012-12-05 15:26:04 +01001571 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001572 /* In case of privacy, but no security and WPS then simulate */
1573 /* setting AES. WPS-2.0 allows no security */
1574 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1575 sme->privacy)
1576 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001577
1578 if (mfp)
1579 wsec = pval | gval | MFP_CAPABLE;
1580 else
1581 wsec = pval | gval;
1582 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001583 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001584 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001585 return err;
1586 }
1587
Arend van Spriel06bb1232012-09-27 14:17:56 +02001588 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001589 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1590 sec->cipher_group = sme->crypto.cipher_group;
1591
1592 return err;
1593}
1594
1595static s32
1596brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1597{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001598 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001599 struct brcmf_cfg80211_security *sec;
1600 s32 val = 0;
1601 s32 err = 0;
1602
1603 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001604 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1605 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001606 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001607 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001608 return err;
1609 }
1610 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1611 switch (sme->crypto.akm_suites[0]) {
1612 case WLAN_AKM_SUITE_8021X:
1613 val = WPA_AUTH_UNSPECIFIED;
1614 break;
1615 case WLAN_AKM_SUITE_PSK:
1616 val = WPA_AUTH_PSK;
1617 break;
1618 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001619 brcmf_err("invalid cipher group (%d)\n",
1620 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001621 return -EINVAL;
1622 }
1623 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1624 switch (sme->crypto.akm_suites[0]) {
1625 case WLAN_AKM_SUITE_8021X:
1626 val = WPA2_AUTH_UNSPECIFIED;
1627 break;
1628 case WLAN_AKM_SUITE_PSK:
1629 val = WPA2_AUTH_PSK;
1630 break;
1631 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001632 brcmf_err("invalid cipher group (%d)\n",
1633 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001634 return -EINVAL;
1635 }
1636 }
1637
Arend van Spriel16886732012-12-05 15:26:04 +01001638 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001639 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1640 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001641 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001642 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001643 return err;
1644 }
1645 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001646 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001647 sec->wpa_auth = sme->crypto.akm_suites[0];
1648
1649 return err;
1650}
1651
1652static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001653brcmf_set_sharedkey(struct net_device *ndev,
1654 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001655{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001656 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001657 struct brcmf_cfg80211_security *sec;
1658 struct brcmf_wsec_key key;
1659 s32 val;
1660 s32 err = 0;
1661
Arend van Spriel16886732012-12-05 15:26:04 +01001662 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001663
Roland Vossena718e2f2011-10-12 20:51:24 +02001664 if (sme->key_len == 0)
1665 return 0;
1666
Arend van Spriel06bb1232012-09-27 14:17:56 +02001667 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001668 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1669 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001670
1671 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1672 return 0;
1673
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001674 if (!(sec->cipher_pairwise &
1675 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1676 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001677
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001678 memset(&key, 0, sizeof(key));
1679 key.len = (u32) sme->key_len;
1680 key.index = (u32) sme->key_idx;
1681 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001682 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001683 return -EINVAL;
1684 }
1685 memcpy(key.data, sme->key, key.len);
1686 key.flags = BRCMF_PRIMARY_KEY;
1687 switch (sec->cipher_pairwise) {
1688 case WLAN_CIPHER_SUITE_WEP40:
1689 key.algo = CRYPTO_ALGO_WEP1;
1690 break;
1691 case WLAN_CIPHER_SUITE_WEP104:
1692 key.algo = CRYPTO_ALGO_WEP128;
1693 break;
1694 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001695 brcmf_err("Invalid algorithm (%d)\n",
1696 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001697 return -EINVAL;
1698 }
1699 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001700 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1701 key.len, key.index, key.algo);
1702 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001703 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001704 if (err)
1705 return err;
1706
1707 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001708 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001709 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001710 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001711 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001712 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001713 }
1714 return err;
1715}
1716
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001717static
1718enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1719 enum nl80211_auth_type type)
1720{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001721 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1722 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1723 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1724 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001725 }
1726 return type;
1727}
1728
Arend van Spriel5b435de2011-10-05 13:19:03 +02001729static s32
1730brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001731 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001732{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001733 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001734 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001735 struct ieee80211_channel *chan = sme->channel;
1736 struct brcmf_join_params join_params;
1737 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001738 const struct brcmf_tlv *rsn_ie;
1739 const struct brcmf_vs_tlv *wpa_ie;
1740 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001741 u32 ie_len;
1742 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001743 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001744 s32 err = 0;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001745 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001746
Arend van Sprield96b8012012-12-05 15:26:02 +01001747 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001748 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001749 return -EIO;
1750
1751 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001752 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001753 return -EOPNOTSUPP;
1754 }
1755
Hante Meuleman89286dc2013-02-08 15:53:46 +01001756 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1757 /* A normal (non P2P) connection request setup. */
1758 ie = NULL;
1759 ie_len = 0;
1760 /* find the WPA_IE */
1761 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1762 if (wpa_ie) {
1763 ie = wpa_ie;
1764 ie_len = wpa_ie->len + TLV_HDR_LEN;
1765 } else {
1766 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001767 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1768 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001769 WLAN_EID_RSN);
1770 if (rsn_ie) {
1771 ie = rsn_ie;
1772 ie_len = rsn_ie->len + TLV_HDR_LEN;
1773 }
1774 }
1775 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1776 }
1777
1778 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1779 sme->ie, sme->ie_len);
1780 if (err)
1781 brcmf_err("Set Assoc REQ IE Failed\n");
1782 else
1783 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1784
Arend van Sprielc1179032012-10-22 13:55:33 -07001785 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001786
1787 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001788 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001789 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001790 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001791 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1792 cfg->channel, chan->center_freq, chanspec);
1793 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001794 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001795 chanspec = 0;
1796 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001797
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001798 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001799
1800 err = brcmf_set_wpa_version(ndev, sme);
1801 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001802 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001803 goto done;
1804 }
1805
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001806 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001807 err = brcmf_set_auth_type(ndev, sme);
1808 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001809 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001810 goto done;
1811 }
1812
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001813 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001814 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001815 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001816 goto done;
1817 }
1818
1819 err = brcmf_set_key_mgmt(ndev, sme);
1820 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001821 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001822 goto done;
1823 }
1824
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001825 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001826 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001827 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001828 goto done;
1829 }
1830
Hante Meuleman89286dc2013-02-08 15:53:46 +01001831 /* Join with specific BSSID and cached SSID
1832 * If SSID is zero join based on BSSID only
1833 */
1834 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1835 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1836 if (cfg->channel)
1837 join_params_size += sizeof(u16);
1838 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1839 if (ext_join_params == NULL) {
1840 err = -ENOMEM;
1841 goto done;
1842 }
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001843 ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
1844 ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
1845 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
1846 if (ssid_len < IEEE80211_MAX_SSID_LEN)
1847 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
1848 ext_join_params->ssid_le.SSID, ssid_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001849
Hante Meuleman89286dc2013-02-08 15:53:46 +01001850 /* Set up join scan parameters */
1851 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001852 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1853
1854 if (sme->bssid)
1855 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1856 else
Joe Perches93803b32015-03-02 19:54:49 -08001857 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001858
1859 if (cfg->channel) {
1860 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1861
1862 ext_join_params->assoc_le.chanspec_list[0] =
1863 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001864 /* Increase dwell time to receive probe response or detect
1865 * beacon from target AP at a noisy air only during connect
1866 * command.
1867 */
1868 ext_join_params->scan_le.active_time =
1869 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1870 ext_join_params->scan_le.passive_time =
1871 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1872 /* To sync with presence period of VSDB GO send probe request
1873 * more frequently. Probe request will be stopped when it gets
1874 * probe response from target AP/GO.
1875 */
1876 ext_join_params->scan_le.nprobes =
1877 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1878 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1879 } else {
1880 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1881 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1882 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001883 }
1884
1885 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1886 join_params_size);
1887 kfree(ext_join_params);
1888 if (!err)
1889 /* This is it. join command worked, we are done */
1890 goto done;
1891
1892 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001893 memset(&join_params, 0, sizeof(join_params));
1894 join_params_size = sizeof(join_params.ssid_le);
1895
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001896 memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
1897 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001898
Hante Meuleman89286dc2013-02-08 15:53:46 +01001899 if (sme->bssid)
1900 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1901 else
Joe Perches93803b32015-03-02 19:54:49 -08001902 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001903
Hante Meuleman17012612013-02-06 18:40:44 +01001904 if (cfg->channel) {
1905 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1906 join_params.params_le.chanspec_num = cpu_to_le32(1);
1907 join_params_size += sizeof(join_params.params_le);
1908 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001909 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001910 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001911 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001912 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001913
1914done:
1915 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001916 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001917 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001918 return err;
1919}
1920
1921static s32
1922brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1923 u16 reason_code)
1924{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001925 struct brcmf_if *ifp = netdev_priv(ndev);
1926 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001927 struct brcmf_scb_val_le scbval;
1928 s32 err = 0;
1929
Arend van Sprield96b8012012-12-05 15:26:02 +01001930 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001931 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001932 return -EIO;
1933
Arend van Sprielc1179032012-10-22 13:55:33 -07001934 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001935 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001936 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001937
Arend van Spriel06bb1232012-09-27 14:17:56 +02001938 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001939 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001940 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001941 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001942 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001943 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001944
Arend van Sprield96b8012012-12-05 15:26:02 +01001945 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001946 return err;
1947}
1948
1949static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001950brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001951 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001952{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001953 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001954 struct net_device *ndev = cfg_to_ndev(cfg);
1955 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001956 s32 err;
1957 s32 disable;
1958 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001959
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001960 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07001961 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001962 return -EIO;
1963
1964 switch (type) {
1965 case NL80211_TX_POWER_AUTOMATIC:
1966 break;
1967 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001968 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001969 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001970 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 err = -EINVAL;
1972 goto done;
1973 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001974 qdbm = MBM_TO_DBM(4 * mbm);
1975 if (qdbm > 127)
1976 qdbm = 127;
1977 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001978 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001979 default:
1980 brcmf_err("Unsupported type %d\n", type);
1981 err = -EINVAL;
1982 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001983 }
1984 /* Make sure radio is off or on as far as software is concerned */
1985 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001986 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001987 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001988 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001989
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001990 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001991 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001992 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001993
1994done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001995 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001996 return err;
1997}
1998
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001999static s32
2000brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2001 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002002{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002003 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002004 struct net_device *ndev = cfg_to_ndev(cfg);
2005 struct brcmf_if *ifp = netdev_priv(ndev);
2006 s32 qdbm = 0;
2007 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002008
Arend van Sprield96b8012012-12-05 15:26:02 +01002009 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002010 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002011 return -EIO;
2012
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002013 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002014 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002015 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002016 goto done;
2017 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002018 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002019
2020done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002021 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002022 return err;
2023}
2024
2025static s32
2026brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002027 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002028{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002029 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002030 u32 index;
2031 u32 wsec;
2032 s32 err = 0;
2033
Arend van Sprield96b8012012-12-05 15:26:02 +01002034 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002035 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002036 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002037 return -EIO;
2038
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002039 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002040 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002041 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002042 goto done;
2043 }
2044
2045 if (wsec & WEP_ENABLED) {
2046 /* Just select a new current key */
2047 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002048 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002049 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002050 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002051 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002052 }
2053done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002054 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002055 return err;
2056}
2057
2058static s32
2059brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2060 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2061{
Hante Meuleman992f6062013-04-02 21:06:17 +02002062 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002063 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002064 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002065 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002066
2067 memset(&key, 0, sizeof(key));
2068 key.index = (u32) key_idx;
2069 /* Instead of bcast for ea address for default wep keys,
2070 driver needs it to be Null */
2071 if (!is_multicast_ether_addr(mac_addr))
2072 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2073 key.len = (u32) params->key_len;
2074 /* check for key index change */
2075 if (key.len == 0) {
2076 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002077 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002078 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002079 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002080 } else {
2081 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002082 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002083 return -EINVAL;
2084 }
2085
Arend van Spriel16886732012-12-05 15:26:04 +01002086 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002087 memcpy(key.data, params->key, key.len);
2088
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002089 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002090 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2091 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002092 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2093 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2094 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2095 }
2096
2097 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2098 if (params->seq && params->seq_len == 6) {
2099 /* rx iv */
2100 u8 *ivptr;
2101 ivptr = (u8 *) params->seq;
2102 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2103 (ivptr[3] << 8) | ivptr[2];
2104 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2105 key.iv_initialized = true;
2106 }
2107
2108 switch (params->cipher) {
2109 case WLAN_CIPHER_SUITE_WEP40:
2110 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002111 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002112 break;
2113 case WLAN_CIPHER_SUITE_WEP104:
2114 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002115 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002116 break;
2117 case WLAN_CIPHER_SUITE_TKIP:
2118 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002119 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002120 break;
2121 case WLAN_CIPHER_SUITE_AES_CMAC:
2122 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002123 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002124 break;
2125 case WLAN_CIPHER_SUITE_CCMP:
2126 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002127 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002128 break;
2129 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002130 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002131 return -EINVAL;
2132 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002133 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002134 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002135 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002136 }
2137 return err;
2138}
2139
2140static s32
2141brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2142 u8 key_idx, bool pairwise, const u8 *mac_addr,
2143 struct key_params *params)
2144{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002145 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002146 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002147 s32 val;
2148 s32 wsec;
2149 s32 err = 0;
2150 u8 keybuf[8];
2151
Arend van Sprield96b8012012-12-05 15:26:02 +01002152 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002153 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002154 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002155 return -EIO;
2156
Hante Meuleman118eb302014-12-21 12:43:49 +01002157 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2158 /* we ignore this key index in this case */
2159 brcmf_err("invalid key index (%d)\n", key_idx);
2160 return -EINVAL;
2161 }
2162
Daniel Kim787eb032014-01-29 15:32:23 +01002163 if (mac_addr &&
2164 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2165 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002166 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002167 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2168 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002169
Hante Meuleman118eb302014-12-21 12:43:49 +01002170 key = &ifp->vif->profile.key[key_idx];
2171 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002172
Hante Meuleman118eb302014-12-21 12:43:49 +01002173 if (params->key_len > sizeof(key->data)) {
2174 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002175 err = -EINVAL;
2176 goto done;
2177 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002178 key->len = params->key_len;
2179 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002180
Hante Meuleman118eb302014-12-21 12:43:49 +01002181 memcpy(key->data, params->key, key->len);
2182
2183 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002184 switch (params->cipher) {
2185 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002186 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002187 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002188 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002189 break;
2190 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002191 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002192 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002193 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002194 break;
2195 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002196 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002197 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002198 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2199 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2200 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002201 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002202 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002203 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002204 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002205 break;
2206 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002207 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002208 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002209 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002210 break;
2211 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002212 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002213 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002214 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002215 break;
2216 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002217 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002218 err = -EINVAL;
2219 goto done;
2220 }
2221
Hante Meuleman118eb302014-12-21 12:43:49 +01002222 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002223 if (err)
2224 goto done;
2225
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002226 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002227 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002228 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002229 goto done;
2230 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002231 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002232 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002233 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002234 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002235 goto done;
2236 }
2237
Arend van Spriel5b435de2011-10-05 13:19:03 +02002238done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002239 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002240 return err;
2241}
2242
2243static s32
2244brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2245 u8 key_idx, bool pairwise, const u8 *mac_addr)
2246{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002247 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002248 struct brcmf_wsec_key key;
2249 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002250
Arend van Sprield96b8012012-12-05 15:26:02 +01002251 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002252 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002253 return -EIO;
2254
Hante Meuleman118eb302014-12-21 12:43:49 +01002255 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002256 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002257 return -EINVAL;
2258 }
2259
Arend van Spriel5b435de2011-10-05 13:19:03 +02002260 memset(&key, 0, sizeof(key));
2261
2262 key.index = (u32) key_idx;
2263 key.flags = BRCMF_PRIMARY_KEY;
2264 key.algo = CRYPTO_ALGO_OFF;
2265
Arend van Spriel16886732012-12-05 15:26:04 +01002266 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002267
2268 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002269 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002270
Arend van Sprield96b8012012-12-05 15:26:02 +01002271 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002272 return err;
2273}
2274
2275static s32
2276brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2277 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2278 void (*callback) (void *cookie, struct key_params * params))
2279{
2280 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002281 struct brcmf_if *ifp = netdev_priv(ndev);
2282 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002283 struct brcmf_cfg80211_security *sec;
2284 s32 wsec;
2285 s32 err = 0;
2286
Arend van Sprield96b8012012-12-05 15:26:02 +01002287 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002288 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002289 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002290 return -EIO;
2291
2292 memset(&params, 0, sizeof(params));
2293
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002294 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002295 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002296 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002297 /* Ignore this error, may happen during DISASSOC */
2298 err = -EAGAIN;
2299 goto done;
2300 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002301 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002302 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002303 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2304 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002305 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002306 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2307 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002308 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002309 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002310 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002311 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002312 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002313 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002314 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002315 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002316 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002317 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002318 err = -EINVAL;
2319 goto done;
2320 }
2321 callback(cookie, &params);
2322
2323done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002324 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002325 return err;
2326}
2327
2328static s32
2329brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2330 struct net_device *ndev, u8 key_idx)
2331{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002332 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002333
2334 return -EOPNOTSUPP;
2335}
2336
Hante Meuleman118eb302014-12-21 12:43:49 +01002337static void
2338brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2339{
2340 s32 err;
2341 u8 key_idx;
2342 struct brcmf_wsec_key *key;
2343 s32 wsec;
2344
2345 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2346 key = &ifp->vif->profile.key[key_idx];
2347 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2348 (key->algo == CRYPTO_ALGO_WEP128))
2349 break;
2350 }
2351 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2352 return;
2353
2354 err = send_key_to_dongle(ifp, key);
2355 if (err) {
2356 brcmf_err("Setting WEP key failed (%d)\n", err);
2357 return;
2358 }
2359 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2360 if (err) {
2361 brcmf_err("get wsec error (%d)\n", err);
2362 return;
2363 }
2364 wsec |= WEP_ENABLED;
2365 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2366 if (err)
2367 brcmf_err("set wsec error (%d)\n", err);
2368}
2369
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002370static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2371{
2372 struct nl80211_sta_flag_update *sfu;
2373
2374 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2375 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2376 sfu = &si->sta_flags;
2377 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2378 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2379 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2380 BIT(NL80211_STA_FLAG_AUTHORIZED);
2381 if (fw_sta_flags & BRCMF_STA_WME)
2382 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2383 if (fw_sta_flags & BRCMF_STA_AUTHE)
2384 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2385 if (fw_sta_flags & BRCMF_STA_ASSOC)
2386 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2387 if (fw_sta_flags & BRCMF_STA_AUTHO)
2388 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2389}
2390
2391static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2392{
2393 struct {
2394 __le32 len;
2395 struct brcmf_bss_info_le bss_le;
2396 } *buf;
2397 u16 capability;
2398 int err;
2399
2400 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2401 if (!buf)
2402 return;
2403
2404 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2405 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2406 WL_BSS_INFO_MAX);
2407 if (err) {
2408 brcmf_err("Failed to get bss info (%d)\n", err);
2409 return;
2410 }
2411 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2412 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2413 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2414 capability = le16_to_cpu(buf->bss_le.capability);
2415 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2416 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2417 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2418 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2419 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2420 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2421}
2422
Arend van Spriel5b435de2011-10-05 13:19:03 +02002423static s32
2424brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002425 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002426{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002427 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002428 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002429 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002430 u32 sta_flags;
2431 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002432 s32 total_rssi;
2433 s32 count_rssi;
2434 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002435
Arend van Sprield96b8012012-12-05 15:26:02 +01002436 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002437 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002438 return -EIO;
2439
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002440 memset(&sta_info_le, 0, sizeof(sta_info_le));
2441 memcpy(&sta_info_le, mac, ETH_ALEN);
2442 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2443 &sta_info_le,
2444 sizeof(sta_info_le));
2445 is_tdls_peer = !err;
2446 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002447 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002448 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002449 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002450 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002451 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002452 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002453 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002454 }
2455 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2456 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2457 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2458 sta_flags = le32_to_cpu(sta_info_le.flags);
2459 brcmf_convert_sta_flags(sta_flags, sinfo);
2460 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2461 if (is_tdls_peer)
2462 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2463 else
2464 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2465 if (sta_flags & BRCMF_STA_ASSOC) {
2466 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2467 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2468 brcmf_fill_bss_param(ifp, sinfo);
2469 }
2470 if (sta_flags & BRCMF_STA_SCBSTATS) {
2471 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2472 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2473 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2474 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2475 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2476 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2477 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2478 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2479 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002480 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002481 sinfo->txrate.legacy =
2482 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002483 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002484 if (sinfo->rx_packets) {
2485 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002486 sinfo->rxrate.legacy =
2487 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002488 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002489 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2490 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2491 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2492 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2493 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2494 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002495 total_rssi = 0;
2496 count_rssi = 0;
2497 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2498 if (sta_info_le.rssi[i]) {
2499 sinfo->chain_signal_avg[count_rssi] =
2500 sta_info_le.rssi[i];
2501 sinfo->chain_signal[count_rssi] =
2502 sta_info_le.rssi[i];
2503 total_rssi += sta_info_le.rssi[i];
2504 count_rssi++;
2505 }
2506 }
2507 if (count_rssi) {
2508 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2509 sinfo->chains = count_rssi;
2510
2511 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2512 total_rssi /= count_rssi;
2513 sinfo->signal = total_rssi;
2514 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002515 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002516done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002517 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002518 return err;
2519}
2520
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002521static int
2522brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2523 int idx, u8 *mac, struct station_info *sinfo)
2524{
2525 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2526 struct brcmf_if *ifp = netdev_priv(ndev);
2527 s32 err;
2528
2529 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2530
2531 if (idx == 0) {
2532 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2533 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2534 &cfg->assoclist,
2535 sizeof(cfg->assoclist));
2536 if (err) {
2537 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2538 err);
2539 cfg->assoclist.count = 0;
2540 return -EOPNOTSUPP;
2541 }
2542 }
2543 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2544 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2545 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2546 }
2547 return -ENOENT;
2548}
2549
Arend van Spriel5b435de2011-10-05 13:19:03 +02002550static s32
2551brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2552 bool enabled, s32 timeout)
2553{
2554 s32 pm;
2555 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002556 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002557 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002558
Arend van Sprield96b8012012-12-05 15:26:02 +01002559 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002560
2561 /*
2562 * Powersave enable/disable request is coming from the
2563 * cfg80211 even before the interface is up. In that
2564 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002565 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002566 * FW later while initializing the dongle
2567 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002568 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002569 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002570
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002571 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002572 goto done;
2573 }
2574
2575 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002576 /* Do not enable the power save after assoc if it is a p2p interface */
2577 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2578 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2579 pm = PM_OFF;
2580 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002581 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002582
Arend van Sprielc1179032012-10-22 13:55:33 -07002583 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002584 if (err) {
2585 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002586 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002587 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002588 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002589 }
2590done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002591 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002592 return err;
2593}
2594
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002595static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002596 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002597{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002598 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002599 struct ieee80211_channel *notify_channel;
2600 struct cfg80211_bss *bss;
2601 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002602 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002603 u16 channel;
2604 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002605 u16 notify_capability;
2606 u16 notify_interval;
2607 u8 *notify_ie;
2608 size_t notify_ielen;
2609 s32 notify_signal;
2610
2611 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002612 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002613 return 0;
2614 }
2615
Franky Lin83cf17a2013-04-11 13:28:50 +02002616 if (!bi->ctl_ch) {
2617 ch.chspec = le16_to_cpu(bi->chanspec);
2618 cfg->d11inf.decchspec(&ch);
2619 bi->ctl_ch = ch.chnum;
2620 }
2621 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002622
2623 if (channel <= CH_MAX_2G_CHANNEL)
2624 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2625 else
2626 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2627
2628 freq = ieee80211_channel_to_frequency(channel, band->band);
2629 notify_channel = ieee80211_get_channel(wiphy, freq);
2630
Arend van Spriel5b435de2011-10-05 13:19:03 +02002631 notify_capability = le16_to_cpu(bi->capability);
2632 notify_interval = le16_to_cpu(bi->beacon_period);
2633 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2634 notify_ielen = le32_to_cpu(bi->ie_length);
2635 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2636
Arend van Spriel16886732012-12-05 15:26:04 +01002637 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2638 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2639 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2640 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2641 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002642
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002643 bss = cfg80211_inform_bss(wiphy, notify_channel,
2644 CFG80211_BSS_FTYPE_UNKNOWN,
2645 (const u8 *)bi->BSSID,
2646 0, notify_capability,
2647 notify_interval, notify_ie,
2648 notify_ielen, notify_signal,
2649 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002650
Franky Line78946e2011-11-10 20:30:34 +01002651 if (!bss)
2652 return -ENOMEM;
2653
Johannes Berg5b112d32013-02-01 01:49:58 +01002654 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002655
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002656 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002657}
2658
Roland Vossen6f09be02011-10-18 14:03:02 +02002659static struct brcmf_bss_info_le *
2660next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2661{
2662 if (bss == NULL)
2663 return list->bss_info_le;
2664 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2665 le32_to_cpu(bss->length));
2666}
2667
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002668static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002669{
2670 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002671 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002672 s32 err = 0;
2673 int i;
2674
Hante Meulemanef8596e2014-09-30 10:23:13 +02002675 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002676 if (bss_list->count != 0 &&
2677 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002678 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2679 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002680 return -EOPNOTSUPP;
2681 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002682 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002683 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002684 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002685 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002686 if (err)
2687 break;
2688 }
2689 return err;
2690}
2691
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002692static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002693 struct net_device *ndev, const u8 *bssid)
2694{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002695 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002696 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002697 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002698 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002699 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002700 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002701 u8 *buf = NULL;
2702 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002703 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002704 u16 notify_capability;
2705 u16 notify_interval;
2706 u8 *notify_ie;
2707 size_t notify_ielen;
2708 s32 notify_signal;
2709
Arend van Sprield96b8012012-12-05 15:26:02 +01002710 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002711
2712 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2713 if (buf == NULL) {
2714 err = -ENOMEM;
2715 goto CleanUp;
2716 }
2717
2718 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2719
Arend van Sprielac24be62012-10-22 10:36:23 -07002720 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2721 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002722 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002723 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002724 goto CleanUp;
2725 }
2726
Roland Vossend34bf642011-10-18 14:03:01 +02002727 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002728
Franky Lin83cf17a2013-04-11 13:28:50 +02002729 ch.chspec = le16_to_cpu(bi->chanspec);
2730 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002731
Franky Lin83cf17a2013-04-11 13:28:50 +02002732 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002733 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2734 else
2735 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2736
Franky Lin83cf17a2013-04-11 13:28:50 +02002737 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002738 notify_channel = ieee80211_get_channel(wiphy, freq);
2739
Arend van Spriel5b435de2011-10-05 13:19:03 +02002740 notify_capability = le16_to_cpu(bi->capability);
2741 notify_interval = le16_to_cpu(bi->beacon_period);
2742 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2743 notify_ielen = le32_to_cpu(bi->ie_length);
2744 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2745
Franky Lin83cf17a2013-04-11 13:28:50 +02002746 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002747 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2748 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2749 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002750
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002751 bss = cfg80211_inform_bss(wiphy, notify_channel,
2752 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2753 notify_capability, notify_interval,
2754 notify_ie, notify_ielen, notify_signal,
2755 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002756
Franky Line78946e2011-11-10 20:30:34 +01002757 if (!bss) {
2758 err = -ENOMEM;
2759 goto CleanUp;
2760 }
2761
Johannes Berg5b112d32013-02-01 01:49:58 +01002762 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002763
Arend van Spriel5b435de2011-10-05 13:19:03 +02002764CleanUp:
2765
2766 kfree(buf);
2767
Arend van Sprield96b8012012-12-05 15:26:02 +01002768 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002769
2770 return err;
2771}
2772
Hante Meuleman89286dc2013-02-08 15:53:46 +01002773static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2774 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002775{
Roland Vossend34bf642011-10-18 14:03:01 +02002776 struct brcmf_bss_info_le *bi;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002777 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002778 u16 beacon_interval;
2779 u8 dtim_period;
2780 size_t ie_len;
2781 u8 *ie;
2782 s32 err = 0;
2783
Arend van Sprield96b8012012-12-05 15:26:02 +01002784 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002785 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002786 return err;
2787
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002788 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002789 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002790 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002791 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002792 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002793 goto update_bss_info_out;
2794 }
2795
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002796 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2797 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002798 if (err)
2799 goto update_bss_info_out;
2800
2801 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2802 ie_len = le32_to_cpu(bi->ie_length);
2803 beacon_interval = le16_to_cpu(bi->beacon_period);
2804
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002805 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002806 if (tim)
2807 dtim_period = tim->data[1];
2808 else {
2809 /*
2810 * active scan was done so we could not get dtim
2811 * information out of probe response.
2812 * so we speficially query dtim information to dongle.
2813 */
2814 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002815 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002816 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002817 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002818 goto update_bss_info_out;
2819 }
2820 dtim_period = (u8)var;
2821 }
2822
Arend van Spriel5b435de2011-10-05 13:19:03 +02002823update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002824 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002825 return err;
2826}
2827
Hante Meuleman18e2f612013-02-08 15:53:49 +01002828void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002829{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002830 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002831
Arend van Sprielc1179032012-10-22 13:55:33 -07002832 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002833 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002834 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002835 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002836 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002837 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2838 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002839}
2840
Hante Meulemane756af52012-09-11 21:18:52 +02002841static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2842{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002843 struct brcmf_cfg80211_info *cfg =
2844 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002845 escan_timeout_work);
2846
Hante Meulemanef8596e2014-09-30 10:23:13 +02002847 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002848 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002849}
2850
2851static void brcmf_escan_timeout(unsigned long data)
2852{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002853 struct brcmf_cfg80211_info *cfg =
2854 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002855
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002856 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002857 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002858 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002859 }
2860}
2861
2862static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002863brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2864 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002865 struct brcmf_bss_info_le *bss_info_le)
2866{
Franky Lin83cf17a2013-04-11 13:28:50 +02002867 struct brcmu_chan ch_bss, ch_bss_info_le;
2868
2869 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2870 cfg->d11inf.decchspec(&ch_bss);
2871 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2872 cfg->d11inf.decchspec(&ch_bss_info_le);
2873
Hante Meulemane756af52012-09-11 21:18:52 +02002874 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002875 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002876 bss_info_le->SSID_len == bss->SSID_len &&
2877 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002878 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2879 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002880 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2881 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2882
Hante Meulemane756af52012-09-11 21:18:52 +02002883 /* preserve max RSSI if the measurements are
2884 * both on-channel or both off-channel
2885 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002886 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002887 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002888 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2889 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002890 /* preserve the on-channel rssi measurement
2891 * if the new measurement is off channel
2892 */
2893 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002894 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002895 }
2896 return 1;
2897 }
2898 return 0;
2899}
2900
2901static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002902brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002903 const struct brcmf_event_msg *e, void *data)
2904{
Arend van Spriel19937322012-11-05 16:22:32 -08002905 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002906 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002907 struct brcmf_escan_result_le *escan_result_le;
2908 struct brcmf_bss_info_le *bss_info_le;
2909 struct brcmf_bss_info_le *bss = NULL;
2910 u32 bi_length;
2911 struct brcmf_scan_results *list;
2912 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002913 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002914
Arend van Spriel5c36b992012-11-14 18:46:05 -08002915 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002916
Arend van Spriela0f472a2013-04-05 10:57:49 +02002917 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meuleman37a869e2015-10-29 20:33:17 +01002918 brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002919 return -EPERM;
2920 }
2921
2922 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002923 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002924 escan_result_le = (struct brcmf_escan_result_le *) data;
2925 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002926 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002927 goto exit;
2928 }
Hante Meulemane756af52012-09-11 21:18:52 +02002929 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002930 brcmf_err("Invalid bss_count %d: ignoring\n",
2931 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002932 goto exit;
2933 }
2934 bss_info_le = &escan_result_le->bss_info_le;
2935
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002936 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2937 goto exit;
2938
2939 if (!cfg->scan_request) {
2940 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2941 goto exit;
2942 }
2943
Hante Meulemane756af52012-09-11 21:18:52 +02002944 bi_length = le32_to_cpu(bss_info_le->length);
2945 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2946 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002947 brcmf_err("Invalid bss_info length %d: ignoring\n",
2948 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002949 goto exit;
2950 }
2951
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002952 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002953 BIT(NL80211_IFTYPE_ADHOC))) {
2954 if (le16_to_cpu(bss_info_le->capability) &
2955 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002956 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002957 goto exit;
2958 }
2959 }
2960
2961 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002962 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002963 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002964 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002965 goto exit;
2966 }
2967
2968 for (i = 0; i < list->count; i++) {
2969 bss = bss ? (struct brcmf_bss_info_le *)
2970 ((unsigned char *)bss +
2971 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002972 if (brcmf_compare_update_same_bss(cfg, bss,
2973 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002974 goto exit;
2975 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002976 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002977 bss_info_le, bi_length);
2978 list->version = le32_to_cpu(bss_info_le->version);
2979 list->buflen += bi_length;
2980 list->count++;
2981 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002982 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002983 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2984 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002985 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002986 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002987 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02002988 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02002989 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002990 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2991 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002992 }
2993exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002994 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02002995}
2996
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002997static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002998{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002999 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3000 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003001 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3002 /* Init scan_timeout timer */
3003 init_timer(&cfg->escan_timeout);
3004 cfg->escan_timeout.data = (unsigned long) cfg;
3005 cfg->escan_timeout.function = brcmf_escan_timeout;
3006 INIT_WORK(&cfg->escan_timeout_work,
3007 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003008}
3009
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003010static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003011{
3012 if (ms < 1000 / HZ) {
3013 cond_resched();
3014 mdelay(ms);
3015 } else {
3016 msleep(ms);
3017 }
3018}
3019
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003020static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3021 u8 *pattern, u32 patternsize, u8 *mask,
3022 u32 packet_offset)
3023{
3024 struct brcmf_fil_wowl_pattern_le *filter;
3025 u32 masksize;
3026 u32 patternoffset;
3027 u8 *buf;
3028 u32 bufsize;
3029 s32 ret;
3030
3031 masksize = (patternsize + 7) / 8;
3032 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3033
3034 bufsize = sizeof(*filter) + patternsize + masksize;
3035 buf = kzalloc(bufsize, GFP_KERNEL);
3036 if (!buf)
3037 return -ENOMEM;
3038 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3039
3040 memcpy(filter->cmd, cmd, 4);
3041 filter->masksize = cpu_to_le32(masksize);
3042 filter->offset = cpu_to_le32(packet_offset);
3043 filter->patternoffset = cpu_to_le32(patternoffset);
3044 filter->patternsize = cpu_to_le32(patternsize);
3045 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3046
3047 if ((mask) && (masksize))
3048 memcpy(buf + sizeof(*filter), mask, masksize);
3049 if ((pattern) && (patternsize))
3050 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3051
3052 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3053
3054 kfree(buf);
3055 return ret;
3056}
3057
Hante Meulemanaeb64222015-10-29 20:33:19 +01003058#ifdef CONFIG_PM
3059
3060static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3061{
3062 struct brcmf_wowl_wakeind_le wake_ind_le;
3063 struct cfg80211_wowlan_wakeup wakeup_data;
3064 struct cfg80211_wowlan_wakeup *wakeup;
3065 u32 wakeind;
3066 s32 err;
3067
3068 err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
3069 sizeof(wake_ind_le));
3070 if (!err) {
3071 brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
3072 return;
3073 }
3074
3075 wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
3076 if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
3077 BRCMF_WOWL_RETR | BRCMF_WOWL_NET)) {
3078 wakeup = &wakeup_data;
3079 memset(&wakeup_data, 0, sizeof(wakeup_data));
3080 wakeup_data.pattern_idx = -1;
3081
3082 if (wakeind & BRCMF_WOWL_MAGIC) {
3083 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
3084 wakeup_data.magic_pkt = true;
3085 }
3086 if (wakeind & BRCMF_WOWL_DIS) {
3087 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
3088 wakeup_data.disconnect = true;
3089 }
3090 if (wakeind & BRCMF_WOWL_BCN) {
3091 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
3092 wakeup_data.disconnect = true;
3093 }
3094 if (wakeind & BRCMF_WOWL_RETR) {
3095 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
3096 wakeup_data.disconnect = true;
3097 }
3098 if (wakeind & BRCMF_WOWL_NET) {
3099 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
3100 /* For now always map to pattern 0, no API to get
3101 * correct information available at the moment.
3102 */
3103 wakeup_data.pattern_idx = 0;
3104 }
3105 } else {
3106 wakeup = NULL;
3107 }
3108 cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
3109}
3110
3111#else
3112
3113static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3114{
3115}
3116
3117#endif /* CONFIG_PM */
3118
Arend van Spriel5b435de2011-10-05 13:19:03 +02003119static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3120{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003121 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3122 struct net_device *ndev = cfg_to_ndev(cfg);
3123 struct brcmf_if *ifp = netdev_priv(ndev);
3124
Arend van Sprield96b8012012-12-05 15:26:02 +01003125 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003126
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003127 if (cfg->wowl_enabled) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003128 brcmf_report_wowl_wakeind(wiphy, ifp);
3129 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
3130 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003131 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003132 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3133 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003134 cfg->wowl_enabled = false;
3135 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003136 return 0;
3137}
3138
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003139static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3140 struct brcmf_if *ifp,
3141 struct cfg80211_wowlan *wowl)
3142{
3143 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003144 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003145
3146 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3147
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003148 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003149 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3150 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3151
3152 wowl_config = 0;
3153 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003154 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003155 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003156 wowl_config |= BRCMF_WOWL_MAGIC;
3157 if ((wowl->patterns) && (wowl->n_patterns)) {
3158 wowl_config |= BRCMF_WOWL_NET;
3159 for (i = 0; i < wowl->n_patterns; i++) {
3160 brcmf_config_wowl_pattern(ifp, "add",
3161 (u8 *)wowl->patterns[i].pattern,
3162 wowl->patterns[i].pattern_len,
3163 (u8 *)wowl->patterns[i].mask,
3164 wowl->patterns[i].pkt_offset);
3165 }
3166 }
Hante Meulemanaeb64222015-10-29 20:33:19 +01003167 brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear"));
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003168 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3169 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3170 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3171 cfg->wowl_enabled = true;
3172}
3173
Arend van Spriel5b435de2011-10-05 13:19:03 +02003174static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003175 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003176{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003177 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3178 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003179 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003180 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003181
Arend van Sprield96b8012012-12-05 15:26:02 +01003182 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003183
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003184 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003185 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003186 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003187 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003188 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003189
Arend van Spriel7d641072012-10-22 13:55:39 -07003190 /* end any scanning */
3191 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003192 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003193
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003194 if (wowl == NULL) {
3195 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3196 list_for_each_entry(vif, &cfg->vif_list, list) {
3197 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3198 continue;
3199 /* While going to suspend if associated with AP
3200 * disassociate from AP to save power while system is
3201 * in suspended state
3202 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003203 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003204 /* Make sure WPA_Supplicant receives all the event
3205 * generated due to DISASSOC call to the fw to keep
3206 * the state fw and WPA_Supplicant state consistent
3207 */
3208 brcmf_delay(500);
3209 }
3210 /* Configure MPC */
3211 brcmf_set_mpc(ifp, 1);
3212
3213 } else {
3214 /* Configure WOWL paramaters */
3215 brcmf_configure_wowl(cfg, ifp, wowl);
3216 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003217
Arend van Spriel7d641072012-10-22 13:55:39 -07003218exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003219 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003220 /* clear any scanning activity */
3221 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003222 return 0;
3223}
3224
3225static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003226brcmf_update_pmklist(struct net_device *ndev,
3227 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3228{
3229 int i, j;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003230 u32 pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003231
Arend van Spriel40c8e952011-10-12 20:51:20 +02003232 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3233
Arend van Spriel16886732012-12-05 15:26:04 +01003234 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003235 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01003236 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
3237 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003238 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01003239 brcmf_dbg(CONN, "%02x\n",
3240 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003241 }
3242
3243 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07003244 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
3245 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003246
3247 return err;
3248}
3249
3250static s32
3251brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3252 struct cfg80211_pmksa *pmksa)
3253{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003254 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003255 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003256 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003257 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003258 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003259
Arend van Sprield96b8012012-12-05 15:26:02 +01003260 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003261 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003262 return -EIO;
3263
Arend van Spriel40c8e952011-10-12 20:51:20 +02003264 pmkid_len = le32_to_cpu(pmkids->npmkid);
3265 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003266 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3267 break;
3268 if (i < WL_NUM_PMKIDS_MAX) {
3269 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3270 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003271 if (i == pmkid_len) {
3272 pmkid_len++;
3273 pmkids->npmkid = cpu_to_le32(pmkid_len);
3274 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003275 } else
3276 err = -EINVAL;
3277
Arend van Spriel16886732012-12-05 15:26:04 +01003278 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
3279 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003280 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003281 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003282
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003283 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003284
Arend van Sprield96b8012012-12-05 15:26:02 +01003285 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003286 return err;
3287}
3288
3289static s32
3290brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3291 struct cfg80211_pmksa *pmksa)
3292{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003293 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003294 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003295 struct pmkid_list pmkid;
3296 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003297 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003298
Arend van Sprield96b8012012-12-05 15:26:02 +01003299 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003300 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003301 return -EIO;
3302
3303 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3304 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3305
Arend van Spriel16886732012-12-05 15:26:04 +01003306 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3307 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003308 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003309 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003310
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003311 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003312 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003313 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003314 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003315 ETH_ALEN))
3316 break;
3317
Arend van Spriel40c8e952011-10-12 20:51:20 +02003318 if ((pmkid_len > 0)
3319 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003320 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003321 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003322 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003323 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3324 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003325 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003326 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3327 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003328 WLAN_PMKID_LEN);
3329 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003330 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003331 } else
3332 err = -EINVAL;
3333
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003334 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003335
Arend van Sprield96b8012012-12-05 15:26:02 +01003336 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003337 return err;
3338
3339}
3340
3341static s32
3342brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3343{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003344 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003345 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003346 s32 err = 0;
3347
Arend van Sprield96b8012012-12-05 15:26:02 +01003348 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003349 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003350 return -EIO;
3351
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003352 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3353 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003354
Arend van Sprield96b8012012-12-05 15:26:02 +01003355 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003356 return err;
3357
3358}
3359
Arend van Spriele5806072012-09-19 22:21:08 +02003360/*
3361 * PFN result doesn't have all the info which are
3362 * required by the supplicant
3363 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3364 * via wl_inform_single_bss in the required format. Escan does require the
3365 * scan request in the form of cfg80211_scan_request. For timebeing, create
3366 * cfg80211_scan_request one out of the received PNO event.
3367 */
3368static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003369brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003370 const struct brcmf_event_msg *e, void *data)
3371{
Arend van Spriel19937322012-11-05 16:22:32 -08003372 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003373 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3374 struct cfg80211_scan_request *request = NULL;
3375 struct cfg80211_ssid *ssid = NULL;
3376 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003377 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003378 int err = 0;
3379 int channel_req = 0;
3380 int band = 0;
3381 struct brcmf_pno_scanresults_le *pfn_result;
3382 u32 result_count;
3383 u32 status;
3384
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003385 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003386
Arend van Spriel5c36b992012-11-14 18:46:05 -08003387 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003388 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003389 return 0;
3390 }
3391
3392 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3393 result_count = le32_to_cpu(pfn_result->count);
3394 status = le32_to_cpu(pfn_result->status);
3395
3396 /*
3397 * PFN event is limited to fit 512 bytes so we may get
3398 * multiple NET_FOUND events. For now place a warning here.
3399 */
3400 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003401 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003402 if (result_count > 0) {
3403 int i;
3404
3405 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003406 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3407 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003408 if (!request || !ssid || !channel) {
3409 err = -ENOMEM;
3410 goto out_err;
3411 }
3412
3413 request->wiphy = wiphy;
3414 data += sizeof(struct brcmf_pno_scanresults_le);
3415 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3416
3417 for (i = 0; i < result_count; i++) {
3418 netinfo = &netinfo_start[i];
3419 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003420 brcmf_err("Invalid netinfo ptr. index: %d\n",
3421 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003422 err = -EINVAL;
3423 goto out_err;
3424 }
3425
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003426 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3427 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003428 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3429 ssid[i].ssid_len = netinfo->SSID_len;
3430 request->n_ssids++;
3431
3432 channel_req = netinfo->channel;
3433 if (channel_req <= CH_MAX_2G_CHANNEL)
3434 band = NL80211_BAND_2GHZ;
3435 else
3436 band = NL80211_BAND_5GHZ;
3437 channel[i].center_freq =
3438 ieee80211_channel_to_frequency(channel_req,
3439 band);
3440 channel[i].band = band;
3441 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3442 request->channels[i] = &channel[i];
3443 request->n_channels++;
3444 }
3445
3446 /* assign parsed ssid array */
3447 if (request->n_ssids)
3448 request->ssids = &ssid[0];
3449
Arend van Sprielc1179032012-10-22 13:55:33 -07003450 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003451 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003452 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003453 }
3454
Arend van Sprielc1179032012-10-22 13:55:33 -07003455 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003456 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003457 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003458 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003459 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003460 goto out_err;
3461 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003462 cfg->sched_escan = true;
3463 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003464 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003465 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003466 goto out_err;
3467 }
3468
3469 kfree(ssid);
3470 kfree(channel);
3471 kfree(request);
3472 return 0;
3473
3474out_err:
3475 kfree(ssid);
3476 kfree(channel);
3477 kfree(request);
3478 cfg80211_sched_scan_stopped(wiphy);
3479 return err;
3480}
3481
Arend van Spriele5806072012-09-19 22:21:08 +02003482static int brcmf_dev_pno_clean(struct net_device *ndev)
3483{
Arend van Spriele5806072012-09-19 22:21:08 +02003484 int ret;
3485
3486 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003487 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003488 if (ret == 0) {
3489 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003490 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3491 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003492 }
3493 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003494 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003495
3496 return ret;
3497}
3498
3499static int brcmf_dev_pno_config(struct net_device *ndev)
3500{
3501 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003502
3503 memset(&pfn_param, 0, sizeof(pfn_param));
3504 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3505
3506 /* set extra pno params */
3507 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3508 pfn_param.repeat = BRCMF_PNO_REPEAT;
3509 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3510
3511 /* set up pno scan fr */
3512 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3513
Arend van Sprielac24be62012-10-22 10:36:23 -07003514 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3515 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003516}
3517
3518static int
3519brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3520 struct net_device *ndev,
3521 struct cfg80211_sched_scan_request *request)
3522{
Arend van Sprielc1179032012-10-22 13:55:33 -07003523 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003524 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003525 struct brcmf_pno_net_param_le pfn;
3526 int i;
3527 int ret = 0;
3528
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003529 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003530 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003531 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003532 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003533 return -EAGAIN;
3534 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003535 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3536 brcmf_err("Scanning suppressed: status (%lu)\n",
3537 cfg->scan_status);
3538 return -EAGAIN;
3539 }
Arend van Spriele5806072012-09-19 22:21:08 +02003540
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003541 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003542 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003543 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003544 return -EINVAL;
3545 }
3546
3547 if (request->n_ssids > 0) {
3548 for (i = 0; i < request->n_ssids; i++) {
3549 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003550 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3551 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003552
3553 /*
3554 * match_set ssids is a supert set of n_ssid list,
3555 * so we need not add these set seperately.
3556 */
3557 }
3558 }
3559
3560 if (request->n_match_sets > 0) {
3561 /* clean up everything */
3562 ret = brcmf_dev_pno_clean(ndev);
3563 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003564 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003565 return ret;
3566 }
3567
3568 /* configure pno */
3569 ret = brcmf_dev_pno_config(ndev);
3570 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003571 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003572 return -EINVAL;
3573 }
3574
3575 /* configure each match set */
3576 for (i = 0; i < request->n_match_sets; i++) {
3577 struct cfg80211_ssid *ssid;
3578 u32 ssid_len;
3579
3580 ssid = &request->match_sets[i].ssid;
3581 ssid_len = ssid->ssid_len;
3582
3583 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003584 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003585 continue;
3586 }
3587 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3588 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3589 pfn.wsec = cpu_to_le32(0);
3590 pfn.infra = cpu_to_le32(1);
3591 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3592 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3593 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003594 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003595 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003596 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3597 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003598 }
3599 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003600 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003601 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003602 return -EINVAL;
3603 }
3604 } else {
3605 return -EINVAL;
3606 }
3607
3608 return 0;
3609}
3610
3611static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3612 struct net_device *ndev)
3613{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003614 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003615
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003616 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003617 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003618 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003619 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003620 return 0;
3621}
Arend van Spriele5806072012-09-19 22:21:08 +02003622
Hante Meuleman1f170112013-02-06 18:40:38 +01003623static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003624{
3625 s32 err;
3626
3627 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003628 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003629 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003630 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003631 return err;
3632 }
3633 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003634 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003635 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003636 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003637 return err;
3638 }
3639 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003640 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003641 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003642 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003643 return err;
3644 }
3645
3646 return 0;
3647}
3648
3649static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3650{
3651 if (is_rsn_ie)
3652 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3653
3654 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3655}
3656
3657static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003658brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003659 const struct brcmf_vs_tlv *wpa_ie,
3660 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003661{
3662 u32 auth = 0; /* d11 open authentication */
3663 u16 count;
3664 s32 err = 0;
3665 s32 len = 0;
3666 u32 i;
3667 u32 wsec;
3668 u32 pval = 0;
3669 u32 gval = 0;
3670 u32 wpa_auth = 0;
3671 u32 offset;
3672 u8 *data;
3673 u16 rsn_cap;
3674 u32 wme_bss_disable;
3675
Arend van Sprield96b8012012-12-05 15:26:02 +01003676 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003677 if (wpa_ie == NULL)
3678 goto exit;
3679
3680 len = wpa_ie->len + TLV_HDR_LEN;
3681 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003682 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003683 if (!is_rsn_ie)
3684 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003685 else
3686 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003687
3688 /* check for multicast cipher suite */
3689 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3690 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003691 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003692 goto exit;
3693 }
3694
3695 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3696 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003697 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003698 goto exit;
3699 }
3700 offset += TLV_OUI_LEN;
3701
3702 /* pick up multicast cipher */
3703 switch (data[offset]) {
3704 case WPA_CIPHER_NONE:
3705 gval = 0;
3706 break;
3707 case WPA_CIPHER_WEP_40:
3708 case WPA_CIPHER_WEP_104:
3709 gval = WEP_ENABLED;
3710 break;
3711 case WPA_CIPHER_TKIP:
3712 gval = TKIP_ENABLED;
3713 break;
3714 case WPA_CIPHER_AES_CCM:
3715 gval = AES_ENABLED;
3716 break;
3717 default:
3718 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003719 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003720 goto exit;
3721 }
3722
3723 offset++;
3724 /* walk thru unicast cipher list and pick up what we recognize */
3725 count = data[offset] + (data[offset + 1] << 8);
3726 offset += WPA_IE_SUITE_COUNT_LEN;
3727 /* Check for unicast suite(s) */
3728 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3729 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003730 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003731 goto exit;
3732 }
3733 for (i = 0; i < count; i++) {
3734 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3735 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003736 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003737 goto exit;
3738 }
3739 offset += TLV_OUI_LEN;
3740 switch (data[offset]) {
3741 case WPA_CIPHER_NONE:
3742 break;
3743 case WPA_CIPHER_WEP_40:
3744 case WPA_CIPHER_WEP_104:
3745 pval |= WEP_ENABLED;
3746 break;
3747 case WPA_CIPHER_TKIP:
3748 pval |= TKIP_ENABLED;
3749 break;
3750 case WPA_CIPHER_AES_CCM:
3751 pval |= AES_ENABLED;
3752 break;
3753 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003754 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003755 }
3756 offset++;
3757 }
3758 /* walk thru auth management suite list and pick up what we recognize */
3759 count = data[offset] + (data[offset + 1] << 8);
3760 offset += WPA_IE_SUITE_COUNT_LEN;
3761 /* Check for auth key management suite(s) */
3762 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3763 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003764 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003765 goto exit;
3766 }
3767 for (i = 0; i < count; i++) {
3768 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3769 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003770 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003771 goto exit;
3772 }
3773 offset += TLV_OUI_LEN;
3774 switch (data[offset]) {
3775 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003776 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003777 wpa_auth |= WPA_AUTH_NONE;
3778 break;
3779 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003780 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003781 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3782 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3783 break;
3784 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003785 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003786 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3787 (wpa_auth |= WPA_AUTH_PSK);
3788 break;
3789 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003790 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003791 }
3792 offset++;
3793 }
3794
3795 if (is_rsn_ie) {
3796 wme_bss_disable = 1;
3797 if ((offset + RSN_CAP_LEN) <= len) {
3798 rsn_cap = data[offset] + (data[offset + 1] << 8);
3799 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3800 wme_bss_disable = 0;
3801 }
3802 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003803 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003804 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003805 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003806 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003807 goto exit;
3808 }
3809 }
3810 /* FOR WPS , set SES_OW_ENABLED */
3811 wsec = (pval | gval | SES_OW_ENABLED);
3812
3813 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003814 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003815 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003816 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003817 goto exit;
3818 }
3819 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003820 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003821 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003822 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003823 goto exit;
3824 }
3825 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003826 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003827 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003828 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003829 goto exit;
3830 }
3831
3832exit:
3833 return err;
3834}
3835
3836static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003837brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003838 struct parsed_vndr_ies *vndr_ies)
3839{
Hante Meuleman1a873342012-09-27 14:17:54 +02003840 struct brcmf_vs_tlv *vndrie;
3841 struct brcmf_tlv *ie;
3842 struct parsed_vndr_ie_info *parsed_info;
3843 s32 remaining_len;
3844
3845 remaining_len = (s32)vndr_ie_len;
3846 memset(vndr_ies, 0, sizeof(*vndr_ies));
3847
3848 ie = (struct brcmf_tlv *)vndr_ie_buf;
3849 while (ie) {
3850 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3851 goto next;
3852 vndrie = (struct brcmf_vs_tlv *)ie;
3853 /* len should be bigger than OUI length + one */
3854 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003855 brcmf_err("invalid vndr ie. length is too small %d\n",
3856 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003857 goto next;
3858 }
3859 /* if wpa or wme ie, do not add ie */
3860 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3861 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3862 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003863 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003864 goto next;
3865 }
3866
3867 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3868
3869 /* save vndr ie information */
3870 parsed_info->ie_ptr = (char *)vndrie;
3871 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3872 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3873
3874 vndr_ies->count++;
3875
Arend van Sprield96b8012012-12-05 15:26:02 +01003876 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3877 parsed_info->vndrie.oui[0],
3878 parsed_info->vndrie.oui[1],
3879 parsed_info->vndrie.oui[2],
3880 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003881
Arend van Spriel9f440b72013-02-08 15:53:36 +01003882 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003883 break;
3884next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003885 remaining_len -= (ie->len + TLV_HDR_LEN);
3886 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003887 ie = NULL;
3888 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003889 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3890 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003891 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003892 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003893}
3894
3895static u32
3896brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3897{
3898
Hante Meuleman1a873342012-09-27 14:17:54 +02003899 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3900 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3901
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303902 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003903
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303904 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003905
3906 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3907
3908 return ie_len + VNDR_IE_HDR_SIZE;
3909}
3910
Arend van Spriel1332e262012-11-05 16:22:18 -08003911s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3912 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003913{
Arend van Spriel1332e262012-11-05 16:22:18 -08003914 struct brcmf_if *ifp;
3915 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003916 s32 err = 0;
3917 u8 *iovar_ie_buf;
3918 u8 *curr_ie_buf;
3919 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003920 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003921 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003922 u32 del_add_ie_buf_len = 0;
3923 u32 total_ie_buf_len = 0;
3924 u32 parsed_ie_buf_len = 0;
3925 struct parsed_vndr_ies old_vndr_ies;
3926 struct parsed_vndr_ies new_vndr_ies;
3927 struct parsed_vndr_ie_info *vndrie_info;
3928 s32 i;
3929 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003930 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003931
Arend van Spriel1332e262012-11-05 16:22:18 -08003932 if (!vif)
3933 return -ENODEV;
3934 ifp = vif->ifp;
3935 saved_ie = &vif->saved_ie;
3936
Hante Meuleman37a869e2015-10-29 20:33:17 +01003937 brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
3938 pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003939 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3940 if (!iovar_ie_buf)
3941 return -ENOMEM;
3942 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003943 switch (pktflag) {
3944 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3945 mgmt_ie_buf = saved_ie->probe_req_ie;
3946 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3947 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3948 break;
3949 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3950 mgmt_ie_buf = saved_ie->probe_res_ie;
3951 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3952 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3953 break;
3954 case BRCMF_VNDR_IE_BEACON_FLAG:
3955 mgmt_ie_buf = saved_ie->beacon_ie;
3956 mgmt_ie_len = &saved_ie->beacon_ie_len;
3957 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3958 break;
3959 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3960 mgmt_ie_buf = saved_ie->assoc_req_ie;
3961 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3962 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3963 break;
3964 default:
3965 err = -EPERM;
3966 brcmf_err("not suitable type\n");
3967 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003968 }
3969
3970 if (vndr_ie_len > mgmt_ie_buf_len) {
3971 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003972 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003973 goto exit;
3974 }
3975
3976 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3977 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3978 ptr = curr_ie_buf;
3979 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3980 for (i = 0; i < new_vndr_ies.count; i++) {
3981 vndrie_info = &new_vndr_ies.ie_info[i];
3982 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3983 vndrie_info->ie_len);
3984 parsed_ie_buf_len += vndrie_info->ie_len;
3985 }
3986 }
3987
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003988 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003989 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3990 (memcmp(mgmt_ie_buf, curr_ie_buf,
3991 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003992 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003993 goto exit;
3994 }
3995
3996 /* parse old vndr_ie */
3997 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3998
3999 /* make a command to delete old ie */
4000 for (i = 0; i < old_vndr_ies.count; i++) {
4001 vndrie_info = &old_vndr_ies.ie_info[i];
4002
Arend van Sprield96b8012012-12-05 15:26:02 +01004003 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4004 vndrie_info->vndrie.id,
4005 vndrie_info->vndrie.len,
4006 vndrie_info->vndrie.oui[0],
4007 vndrie_info->vndrie.oui[1],
4008 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004009
4010 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4011 vndrie_info->ie_ptr,
4012 vndrie_info->ie_len,
4013 "del");
4014 curr_ie_buf += del_add_ie_buf_len;
4015 total_ie_buf_len += del_add_ie_buf_len;
4016 }
4017 }
4018
4019 *mgmt_ie_len = 0;
4020 /* Add if there is any extra IE */
4021 if (mgmt_ie_buf && parsed_ie_buf_len) {
4022 ptr = mgmt_ie_buf;
4023
4024 remained_buf_len = mgmt_ie_buf_len;
4025
4026 /* make a command to add new ie */
4027 for (i = 0; i < new_vndr_ies.count; i++) {
4028 vndrie_info = &new_vndr_ies.ie_info[i];
4029
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004030 /* verify remained buf size before copy data */
4031 if (remained_buf_len < (vndrie_info->vndrie.len +
4032 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004033 brcmf_err("no space in mgmt_ie_buf: len left %d",
4034 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004035 break;
4036 }
4037 remained_buf_len -= (vndrie_info->ie_len +
4038 VNDR_IE_VSIE_OFFSET);
4039
Arend van Sprield96b8012012-12-05 15:26:02 +01004040 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4041 vndrie_info->vndrie.id,
4042 vndrie_info->vndrie.len,
4043 vndrie_info->vndrie.oui[0],
4044 vndrie_info->vndrie.oui[1],
4045 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004046
4047 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4048 vndrie_info->ie_ptr,
4049 vndrie_info->ie_len,
4050 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02004051
4052 /* save the parsed IE in wl struct */
4053 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4054 vndrie_info->ie_len);
4055 *mgmt_ie_len += vndrie_info->ie_len;
4056
4057 curr_ie_buf += del_add_ie_buf_len;
4058 total_ie_buf_len += del_add_ie_buf_len;
4059 }
4060 }
4061 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004062 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004063 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004064 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004065 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004066 }
4067
4068exit:
4069 kfree(iovar_ie_buf);
4070 return err;
4071}
4072
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004073s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4074{
4075 s32 pktflags[] = {
4076 BRCMF_VNDR_IE_PRBREQ_FLAG,
4077 BRCMF_VNDR_IE_PRBRSP_FLAG,
4078 BRCMF_VNDR_IE_BEACON_FLAG
4079 };
4080 int i;
4081
4082 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4083 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4084
4085 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4086 return 0;
4087}
4088
Hante Meuleman1a873342012-09-27 14:17:54 +02004089static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004090brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4091 struct cfg80211_beacon_data *beacon)
4092{
4093 s32 err;
4094
4095 /* Set Beacon IEs to FW */
4096 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4097 beacon->tail, beacon->tail_len);
4098 if (err) {
4099 brcmf_err("Set Beacon IE Failed\n");
4100 return err;
4101 }
4102 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4103
4104 /* Set Probe Response IEs to FW */
4105 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4106 beacon->proberesp_ies,
4107 beacon->proberesp_ies_len);
4108 if (err)
4109 brcmf_err("Set Probe Resp IE Failed\n");
4110 else
4111 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4112
4113 return err;
4114}
4115
4116static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004117brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4118 struct cfg80211_ap_settings *settings)
4119{
4120 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004121 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004122 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004123 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004124 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004125 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004126 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004127 const struct brcmf_tlv *rsn_ie;
4128 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004129 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004130 enum nl80211_iftype dev_role;
4131 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004132 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004133 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004134 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004135
Arend van Spriel06c01582014-05-12 10:47:37 +02004136 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4137 settings->chandef.chan->hw_value,
4138 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004139 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004140 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4141 settings->ssid, settings->ssid_len, settings->auth_type,
4142 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004143 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004144 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004145
Arend van Spriel98027762014-12-21 12:43:53 +01004146 /* store current 11d setting */
4147 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4148 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4149 settings->beacon.tail_len,
4150 WLAN_EID_COUNTRY);
4151 is_11d = country_ie ? 1 : 0;
4152
Hante Meuleman1a873342012-09-27 14:17:54 +02004153 memset(&ssid_le, 0, sizeof(ssid_le));
4154 if (settings->ssid == NULL || settings->ssid_len == 0) {
4155 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4156 ssid_ie = brcmf_parse_tlvs(
4157 (u8 *)&settings->beacon.head[ie_offset],
4158 settings->beacon.head_len - ie_offset,
4159 WLAN_EID_SSID);
4160 if (!ssid_ie)
4161 return -EINVAL;
4162
4163 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4164 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004165 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004166 } else {
4167 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4168 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4169 }
4170
Hante Meulemana44aa402014-12-03 21:05:33 +01004171 if (!mbss) {
4172 brcmf_set_mpc(ifp, 0);
4173 brcmf_configure_arp_offload(ifp, false);
4174 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004175
4176 /* find the RSN_IE */
4177 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4178 settings->beacon.tail_len, WLAN_EID_RSN);
4179
4180 /* find the WPA_IE */
4181 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4182 settings->beacon.tail_len);
4183
Hante Meuleman1a873342012-09-27 14:17:54 +02004184 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004185 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004186 if (wpa_ie != NULL) {
4187 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004188 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004189 if (err < 0)
4190 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004191 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004192 struct brcmf_vs_tlv *tmp_ie;
4193
4194 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4195
Hante Meuleman1a873342012-09-27 14:17:54 +02004196 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004197 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004198 if (err < 0)
4199 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004200 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004201 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004202 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004203 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004204 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004205
Hante Meulemana0f07952013-02-08 15:53:47 +01004206 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004207
Hante Meulemana44aa402014-12-03 21:05:33 +01004208 if (!mbss) {
4209 chanspec = chandef_to_chanspec(&cfg->d11inf,
4210 &settings->chandef);
4211 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004212 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004213 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4214 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004215 goto exit;
4216 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004217
Arend van Spriel98027762014-12-21 12:43:53 +01004218 if (is_11d != ifp->vif->is_11d) {
4219 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4220 is_11d);
4221 if (err < 0) {
4222 brcmf_err("Regulatory Set Error, %d\n", err);
4223 goto exit;
4224 }
4225 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004226 if (settings->beacon_interval) {
4227 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4228 settings->beacon_interval);
4229 if (err < 0) {
4230 brcmf_err("Beacon Interval Set Error, %d\n",
4231 err);
4232 goto exit;
4233 }
4234 }
4235 if (settings->dtim_period) {
4236 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4237 settings->dtim_period);
4238 if (err < 0) {
4239 brcmf_err("DTIM Interval Set Error, %d\n", err);
4240 goto exit;
4241 }
4242 }
4243
Hante Meuleman8abffd82015-10-29 20:33:16 +01004244 if ((dev_role == NL80211_IFTYPE_AP) &&
4245 ((ifp->ifidx == 0) ||
4246 !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004247 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4248 if (err < 0) {
4249 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4250 goto exit;
4251 }
4252 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4253 }
4254
4255 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004256 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004257 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004258 goto exit;
4259 }
Arend van Spriel98027762014-12-21 12:43:53 +01004260 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4261 /* Multiple-BSS should use same 11d configuration */
4262 err = -EINVAL;
4263 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004264 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004265 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004266 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4267 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4268
Hante Meulemana0f07952013-02-08 15:53:47 +01004269 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4270 if (err < 0) {
4271 brcmf_err("setting AP mode failed %d\n", err);
4272 goto exit;
4273 }
4274 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4275 if (err < 0) {
4276 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4277 goto exit;
4278 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004279 /* On DOWN the firmware removes the WEP keys, reconfigure
4280 * them if they were set.
4281 */
4282 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004283
4284 memset(&join_params, 0, sizeof(join_params));
4285 /* join parameters starts with ssid */
4286 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4287 /* create softap */
4288 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4289 &join_params, sizeof(join_params));
4290 if (err < 0) {
4291 brcmf_err("SET SSID error (%d)\n", err);
4292 goto exit;
4293 }
4294 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4295 } else {
4296 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4297 sizeof(ssid_le));
4298 if (err < 0) {
4299 brcmf_err("setting ssid failed %d\n", err);
4300 goto exit;
4301 }
Hante Meuleman37a869e2015-10-29 20:33:17 +01004302 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meulemana0f07952013-02-08 15:53:47 +01004303 bss_enable.enable = cpu_to_le32(1);
4304 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4305 sizeof(bss_enable));
4306 if (err < 0) {
4307 brcmf_err("bss_enable config failed %d\n", err);
4308 goto exit;
4309 }
4310
4311 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4312 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004313 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004314 brcmf_net_setcarrier(ifp, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004315
4316exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004317 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004318 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004319 brcmf_configure_arp_offload(ifp, true);
4320 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004321 return err;
4322}
4323
4324static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4325{
Arend van Sprielc1179032012-10-22 13:55:33 -07004326 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004327 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004328 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004329 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004330
Arend van Sprield96b8012012-12-05 15:26:02 +01004331 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004332
Hante Meuleman426d0a52013-02-08 15:53:53 +01004333 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004334 /* Due to most likely deauths outstanding we sleep */
4335 /* first to make sure they get processed by fw. */
4336 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004337
Hante Meulemana44aa402014-12-03 21:05:33 +01004338 if (ifp->vif->mbss) {
4339 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4340 return err;
4341 }
4342
Hante Meuleman5c33a942013-04-02 21:06:18 +02004343 memset(&join_params, 0, sizeof(join_params));
4344 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4345 &join_params, sizeof(join_params));
4346 if (err < 0)
4347 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004348 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004349 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004350 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004351 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4352 if (err < 0)
4353 brcmf_err("setting AP mode failed %d\n", err);
4354 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4355 if (err < 0)
4356 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004357 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4358 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004359 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4360 ifp->vif->is_11d);
4361 if (err < 0)
4362 brcmf_err("restoring REGULATORY setting failed %d\n",
4363 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004364 /* Bring device back up so it can be used again */
4365 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4366 if (err < 0)
4367 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004368 } else {
Hante Meuleman37a869e2015-10-29 20:33:17 +01004369 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004370 bss_enable.enable = cpu_to_le32(0);
4371 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4372 sizeof(bss_enable));
4373 if (err < 0)
4374 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004375 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004376 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004377 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004378 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004379 brcmf_net_setcarrier(ifp, false);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004380
Hante Meuleman1a873342012-09-27 14:17:54 +02004381 return err;
4382}
4383
Hante Meulemana0f07952013-02-08 15:53:47 +01004384static s32
4385brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4386 struct cfg80211_beacon_data *info)
4387{
Hante Meulemana0f07952013-02-08 15:53:47 +01004388 struct brcmf_if *ifp = netdev_priv(ndev);
4389 s32 err;
4390
4391 brcmf_dbg(TRACE, "Enter\n");
4392
Hante Meulemana0f07952013-02-08 15:53:47 +01004393 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4394
4395 return err;
4396}
4397
Hante Meuleman1a873342012-09-27 14:17:54 +02004398static int
4399brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004400 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004401{
Hante Meulemana0f07952013-02-08 15:53:47 +01004402 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004403 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004404 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004405 s32 err;
4406
Jouni Malinen89c771e2014-10-10 20:52:40 +03004407 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004408 return -EFAULT;
4409
Jouni Malinen89c771e2014-10-10 20:52:40 +03004410 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004411
Hante Meulemana0f07952013-02-08 15:53:47 +01004412 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4413 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004414 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004415 return -EIO;
4416
Jouni Malinen89c771e2014-10-10 20:52:40 +03004417 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004418 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004419 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004420 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004421 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004422 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004423
Arend van Sprield96b8012012-12-05 15:26:02 +01004424 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004425 return err;
4426}
4427
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004428static int
4429brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4430 const u8 *mac, struct station_parameters *params)
4431{
4432 struct brcmf_if *ifp = netdev_priv(ndev);
4433 s32 err;
4434
4435 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4436 params->sta_flags_mask, params->sta_flags_set);
4437
4438 /* Ignore all 00 MAC */
4439 if (is_zero_ether_addr(mac))
4440 return 0;
4441
4442 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4443 return 0;
4444
4445 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4446 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4447 (void *)mac, ETH_ALEN);
4448 else
4449 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4450 (void *)mac, ETH_ALEN);
4451 if (err < 0)
4452 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4453
4454 return err;
4455}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004456
4457static void
4458brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4459 struct wireless_dev *wdev,
4460 u16 frame_type, bool reg)
4461{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004462 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004463 u16 mgmt_type;
4464
4465 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4466
4467 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004468 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004469 if (reg)
4470 vif->mgmt_rx_reg |= BIT(mgmt_type);
4471 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004472 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004473}
4474
4475
4476static int
4477brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004478 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004479{
4480 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004481 struct ieee80211_channel *chan = params->chan;
4482 const u8 *buf = params->buf;
4483 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004484 const struct ieee80211_mgmt *mgmt;
4485 struct brcmf_cfg80211_vif *vif;
4486 s32 err = 0;
4487 s32 ie_offset;
4488 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004489 struct brcmf_fil_action_frame_le *action_frame;
4490 struct brcmf_fil_af_params_le *af_params;
4491 bool ack;
4492 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004493 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004494
4495 brcmf_dbg(TRACE, "Enter\n");
4496
4497 *cookie = 0;
4498
4499 mgmt = (const struct ieee80211_mgmt *)buf;
4500
Hante Meulemana0f07952013-02-08 15:53:47 +01004501 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4502 brcmf_err("Driver only allows MGMT packet type\n");
4503 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004504 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004505
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004506 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4507
Hante Meulemana0f07952013-02-08 15:53:47 +01004508 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4509 /* Right now the only reason to get a probe response */
4510 /* is for p2p listen response or for p2p GO from */
4511 /* wpa_supplicant. Unfortunately the probe is send */
4512 /* on primary ndev, while dongle wants it on the p2p */
4513 /* vif. Since this is only reason for a probe */
4514 /* response to be sent, the vif is taken from cfg. */
4515 /* If ever desired to send proberesp for non p2p */
4516 /* response then data should be checked for */
4517 /* "DIRECT-". Note in future supplicant will take */
4518 /* dedicated p2p wdev to do this and then this 'hack'*/
4519 /* is not needed anymore. */
4520 ie_offset = DOT11_MGMT_HDR_LEN +
4521 DOT11_BCN_PRB_FIXED_LEN;
4522 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004523 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4524 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4525 err = brcmf_vif_set_mgmt_ie(vif,
4526 BRCMF_VNDR_IE_PRBRSP_FLAG,
4527 &buf[ie_offset],
4528 ie_len);
4529 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4530 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004531 } else if (ieee80211_is_action(mgmt->frame_control)) {
4532 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4533 if (af_params == NULL) {
4534 brcmf_err("unable to allocate frame\n");
4535 err = -ENOMEM;
4536 goto exit;
4537 }
4538 action_frame = &af_params->action_frame;
4539 /* Add the packet Id */
4540 action_frame->packet_id = cpu_to_le32(*cookie);
4541 /* Add BSSID */
4542 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4543 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4544 /* Add the length exepted for 802.11 header */
4545 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004546 /* Add the channel. Use the one specified as parameter if any or
4547 * the current one (got from the firmware) otherwise
4548 */
4549 if (chan)
4550 freq = chan->center_freq;
4551 else
4552 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4553 &freq);
4554 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004555 af_params->channel = cpu_to_le32(chan_nr);
4556
4557 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4558 le16_to_cpu(action_frame->len));
4559
4560 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004561 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004562
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004563 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004564 af_params);
4565
4566 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4567 GFP_KERNEL);
4568 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004569 } else {
4570 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4571 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4572 }
4573
Hante Meuleman18e2f612013-02-08 15:53:49 +01004574exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004575 return err;
4576}
4577
4578
4579static int
4580brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4581 struct wireless_dev *wdev,
4582 u64 cookie)
4583{
4584 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4585 struct brcmf_cfg80211_vif *vif;
4586 int err = 0;
4587
4588 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4589
4590 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4591 if (vif == NULL) {
4592 brcmf_err("No p2p device available for probe response\n");
4593 err = -ENODEV;
4594 goto exit;
4595 }
4596 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4597exit:
4598 return err;
4599}
4600
Piotr Haber61730d42013-04-23 12:53:12 +02004601static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4602 struct wireless_dev *wdev,
4603 enum nl80211_crit_proto_id proto,
4604 u16 duration)
4605{
4606 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4607 struct brcmf_cfg80211_vif *vif;
4608
4609 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4610
4611 /* only DHCP support for now */
4612 if (proto != NL80211_CRIT_PROTO_DHCP)
4613 return -EINVAL;
4614
4615 /* suppress and abort scanning */
4616 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4617 brcmf_abort_scanning(cfg);
4618
4619 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4620}
4621
4622static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4623 struct wireless_dev *wdev)
4624{
4625 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4626 struct brcmf_cfg80211_vif *vif;
4627
4628 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4629
4630 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4631 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4632}
4633
Hante Meuleman70b7d942014-07-30 13:20:07 +02004634static s32
4635brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4636 const struct brcmf_event_msg *e, void *data)
4637{
4638 switch (e->reason) {
4639 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4640 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4641 break;
4642 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4643 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4644 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4645 break;
4646 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4647 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4648 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4649 break;
4650 }
4651
4652 return 0;
4653}
4654
Arend van Spriel89c2f382013-08-10 12:27:25 +02004655static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4656{
4657 int ret;
4658
4659 switch (oper) {
4660 case NL80211_TDLS_DISCOVERY_REQ:
4661 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4662 break;
4663 case NL80211_TDLS_SETUP:
4664 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4665 break;
4666 case NL80211_TDLS_TEARDOWN:
4667 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4668 break;
4669 default:
4670 brcmf_err("unsupported operation: %d\n", oper);
4671 ret = -EOPNOTSUPP;
4672 }
4673 return ret;
4674}
4675
4676static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004677 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004678 enum nl80211_tdls_operation oper)
4679{
4680 struct brcmf_if *ifp;
4681 struct brcmf_tdls_iovar_le info;
4682 int ret = 0;
4683
4684 ret = brcmf_convert_nl80211_tdls_oper(oper);
4685 if (ret < 0)
4686 return ret;
4687
4688 ifp = netdev_priv(ndev);
4689 memset(&info, 0, sizeof(info));
4690 info.mode = (u8)ret;
4691 if (peer)
4692 memcpy(info.ea, peer, ETH_ALEN);
4693
4694 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4695 &info, sizeof(info));
4696 if (ret < 0)
4697 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4698
4699 return ret;
4700}
4701
Arend van Spriel5b435de2011-10-05 13:19:03 +02004702static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004703 .add_virtual_intf = brcmf_cfg80211_add_iface,
4704 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004705 .change_virtual_intf = brcmf_cfg80211_change_iface,
4706 .scan = brcmf_cfg80211_scan,
4707 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4708 .join_ibss = brcmf_cfg80211_join_ibss,
4709 .leave_ibss = brcmf_cfg80211_leave_ibss,
4710 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02004711 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004712 .set_tx_power = brcmf_cfg80211_set_tx_power,
4713 .get_tx_power = brcmf_cfg80211_get_tx_power,
4714 .add_key = brcmf_cfg80211_add_key,
4715 .del_key = brcmf_cfg80211_del_key,
4716 .get_key = brcmf_cfg80211_get_key,
4717 .set_default_key = brcmf_cfg80211_config_default_key,
4718 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4719 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004720 .connect = brcmf_cfg80211_connect,
4721 .disconnect = brcmf_cfg80211_disconnect,
4722 .suspend = brcmf_cfg80211_suspend,
4723 .resume = brcmf_cfg80211_resume,
4724 .set_pmksa = brcmf_cfg80211_set_pmksa,
4725 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004726 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004727 .start_ap = brcmf_cfg80211_start_ap,
4728 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004729 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004730 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004731 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004732 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4733 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004734 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4735 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4736 .remain_on_channel = brcmf_p2p_remain_on_channel,
4737 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004738 .start_p2p_device = brcmf_p2p_start_device,
4739 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004740 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4741 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004742 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004743};
4744
Arend van Spriel3eacf862012-10-22 13:55:30 -07004745struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004746 enum nl80211_iftype type,
4747 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004748{
Hante Meulemana44aa402014-12-03 21:05:33 +01004749 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004750 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004751 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004752
Arend van Spriel33a6b152013-02-08 15:53:39 +01004753 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004754 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004755 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4756 if (!vif)
4757 return ERR_PTR(-ENOMEM);
4758
4759 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004760 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004761
Arend van Spriel3eacf862012-10-22 13:55:30 -07004762 vif->pm_block = pm_block;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004763
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004764 brcmf_init_prof(&vif->profile);
4765
Hante Meulemana44aa402014-12-03 21:05:33 +01004766 if (type == NL80211_IFTYPE_AP) {
4767 mbss = false;
4768 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4769 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4770 mbss = true;
4771 break;
4772 }
4773 }
4774 vif->mbss = mbss;
4775 }
4776
Arend van Spriel3eacf862012-10-22 13:55:30 -07004777 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004778 return vif;
4779}
4780
Arend van Spriel427dec52014-01-06 12:40:47 +01004781void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004782{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004783 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004784 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004785}
4786
Arend van Spriel9df4d542014-01-06 12:40:49 +01004787void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4788{
4789 struct brcmf_cfg80211_vif *vif;
4790 struct brcmf_if *ifp;
4791
4792 ifp = netdev_priv(ndev);
4793 vif = ifp->vif;
4794
Arend van Spriel95ef1232015-08-26 22:15:04 +02004795 if (vif)
4796 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01004797 free_netdev(ndev);
4798}
4799
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004800static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004801{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004802 u32 event = e->event_code;
4803 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004804
4805 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004806 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004807 return true;
4808 }
4809
4810 return false;
4811}
4812
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004813static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004814{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004815 u32 event = e->event_code;
4816 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004817
Hante Meuleman68ca3952014-02-25 20:30:26 +01004818 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4819 (event == BRCMF_E_DISASSOC_IND) ||
4820 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004821 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004822 return true;
4823 }
4824 return false;
4825}
4826
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004827static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004828 const struct brcmf_event_msg *e)
4829{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004830 u32 event = e->event_code;
4831 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004832
4833 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004834 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4835 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004836 return true;
4837 }
4838
4839 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004840 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004841 return true;
4842 }
4843
4844 return false;
4845}
4846
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004847static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004848{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004849 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004850
4851 kfree(conn_info->req_ie);
4852 conn_info->req_ie = NULL;
4853 conn_info->req_ie_len = 0;
4854 kfree(conn_info->resp_ie);
4855 conn_info->resp_ie = NULL;
4856 conn_info->resp_ie_len = 0;
4857}
4858
Hante Meuleman89286dc2013-02-08 15:53:46 +01004859static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4860 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004861{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004862 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004863 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004864 u32 req_len;
4865 u32 resp_len;
4866 s32 err = 0;
4867
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004868 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004869
Arend van Sprielac24be62012-10-22 10:36:23 -07004870 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4871 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004872 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004873 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004874 return err;
4875 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004876 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004877 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004878 req_len = le32_to_cpu(assoc_info->req_len);
4879 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004880 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004881 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004882 cfg->extra_buf,
4883 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004884 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004885 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004886 return err;
4887 }
4888 conn_info->req_ie_len = req_len;
4889 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004890 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004891 GFP_KERNEL);
4892 } else {
4893 conn_info->req_ie_len = 0;
4894 conn_info->req_ie = NULL;
4895 }
4896 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004897 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004898 cfg->extra_buf,
4899 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004900 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004901 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004902 return err;
4903 }
4904 conn_info->resp_ie_len = resp_len;
4905 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004906 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004907 GFP_KERNEL);
4908 } else {
4909 conn_info->resp_ie_len = 0;
4910 conn_info->resp_ie = NULL;
4911 }
Arend van Spriel16886732012-12-05 15:26:04 +01004912 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4913 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004914
4915 return err;
4916}
4917
4918static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004919brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004920 struct net_device *ndev,
4921 const struct brcmf_event_msg *e)
4922{
Arend van Sprielc1179032012-10-22 13:55:33 -07004923 struct brcmf_if *ifp = netdev_priv(ndev);
4924 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004925 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4926 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004927 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004928 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004929 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004930 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004931 u32 freq;
4932 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004933 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004934
Arend van Sprield96b8012012-12-05 15:26:02 +01004935 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004936
Hante Meuleman89286dc2013-02-08 15:53:46 +01004937 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004938 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004939 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004940
Franky Lina180b832012-10-10 11:13:09 -07004941 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4942 if (buf == NULL) {
4943 err = -ENOMEM;
4944 goto done;
4945 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004946
Franky Lina180b832012-10-10 11:13:09 -07004947 /* data sent to dongle has to be little endian */
4948 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004949 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004950 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004951
4952 if (err)
4953 goto done;
4954
4955 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004956 ch.chspec = le16_to_cpu(bi->chanspec);
4957 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004958
Franky Lin83cf17a2013-04-11 13:28:50 +02004959 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004960 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4961 else
4962 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4963
Franky Lin83cf17a2013-04-11 13:28:50 +02004964 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004965 notify_channel = ieee80211_get_channel(wiphy, freq);
4966
Franky Lina180b832012-10-10 11:13:09 -07004967done:
4968 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004969 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004970 conn_info->req_ie, conn_info->req_ie_len,
4971 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004972 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004973
Arend van Sprielc1179032012-10-22 13:55:33 -07004974 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004975 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004976 return err;
4977}
4978
4979static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004980brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004981 struct net_device *ndev, const struct brcmf_event_msg *e,
4982 bool completed)
4983{
Arend van Sprielc1179032012-10-22 13:55:33 -07004984 struct brcmf_if *ifp = netdev_priv(ndev);
4985 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004986 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004987
Arend van Sprield96b8012012-12-05 15:26:02 +01004988 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004989
Arend van Sprielc1179032012-10-22 13:55:33 -07004990 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4991 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004992 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004993 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004994 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004995 brcmf_update_bss_info(cfg, ifp);
4996 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4997 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004998 }
4999 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02005000 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005001 conn_info->req_ie,
5002 conn_info->req_ie_len,
5003 conn_info->resp_ie,
5004 conn_info->resp_ie_len,
5005 completed ? WLAN_STATUS_SUCCESS :
5006 WLAN_STATUS_AUTH_TIMEOUT,
5007 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005008 brcmf_dbg(CONN, "Report connect result - connection %s\n",
5009 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005010 }
Arend van Sprield96b8012012-12-05 15:26:02 +01005011 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005012 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005013}
5014
5015static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005016brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02005017 struct net_device *ndev,
5018 const struct brcmf_event_msg *e, void *data)
5019{
Hante Meulemana44aa402014-12-03 21:05:33 +01005020 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01005021 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005022 u32 event = e->event_code;
5023 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02005024 struct station_info sinfo;
5025
Arend van Spriel16886732012-12-05 15:26:04 +01005026 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005027 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
5028 ndev != cfg_to_ndev(cfg)) {
5029 brcmf_dbg(CONN, "AP mode link down\n");
5030 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01005031 if (ifp->vif->mbss)
Arend van Sprielee6e3a32015-08-26 22:14:55 +02005032 brcmf_remove_interface(ifp);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005033 return 0;
5034 }
Hante Meuleman1a873342012-09-27 14:17:54 +02005035
Hante Meuleman1a873342012-09-27 14:17:54 +02005036 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01005037 (reason == BRCMF_E_STATUS_SUCCESS)) {
5038 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02005039 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005040 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02005041 return -EINVAL;
5042 }
5043 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005044 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02005045 generation++;
5046 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005047 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005048 } else if ((event == BRCMF_E_DISASSOC_IND) ||
5049 (event == BRCMF_E_DEAUTH_IND) ||
5050 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01005051 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005052 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01005053 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02005054}
5055
5056static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005057brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005058 const struct brcmf_event_msg *e, void *data)
5059{
Arend van Spriel19937322012-11-05 16:22:32 -08005060 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5061 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005062 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005063 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005064 s32 err = 0;
5065
Hante Meuleman8851cce2014-07-30 13:20:02 +02005066 if ((e->event_code == BRCMF_E_DEAUTH) ||
5067 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5068 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5069 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5070 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5071 }
5072
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005073 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005074 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005075 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005076 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005077 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005078 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005079 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005080 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005081 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005082 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5083 &ifp->vif->sme_state);
5084 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5085 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005086 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005087 brcmf_bss_connect_done(cfg, ndev, e, true);
Hante Meuleman92121e62015-10-08 20:33:21 +02005088 brcmf_net_setcarrier(ifp, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005089 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005090 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005091 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005092 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005093 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005094 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005095 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005096 if (ndev != cfg_to_ndev(cfg))
5097 complete(&cfg->vif_disabled);
Hante Meuleman92121e62015-10-08 20:33:21 +02005098 brcmf_net_setcarrier(ifp, false);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005099 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005100 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005101 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5102 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005103 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005104 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005105 }
5106
5107 return err;
5108}
5109
5110static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005111brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005112 const struct brcmf_event_msg *e, void *data)
5113{
Arend van Spriel19937322012-11-05 16:22:32 -08005114 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005115 u32 event = e->event_code;
5116 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005117
5118 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005119 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005120 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005121 else
Arend van Spriel19937322012-11-05 16:22:32 -08005122 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005123 }
5124
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005125 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005126}
5127
5128static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005129brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005130 const struct brcmf_event_msg *e, void *data)
5131{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005132 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005133 enum nl80211_key_type key_type;
5134
5135 if (flags & BRCMF_EVENT_MSG_GROUP)
5136 key_type = NL80211_KEYTYPE_GROUP;
5137 else
5138 key_type = NL80211_KEYTYPE_PAIRWISE;
5139
Arend van Spriel19937322012-11-05 16:22:32 -08005140 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005141 NULL, GFP_KERNEL);
5142
5143 return 0;
5144}
5145
Arend van Sprield3c0b632013-02-08 15:53:37 +01005146static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5147 const struct brcmf_event_msg *e, void *data)
5148{
5149 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5150 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5151 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5152 struct brcmf_cfg80211_vif *vif;
5153
Hante Meuleman37a869e2015-10-29 20:33:17 +01005154 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
Arend van Sprield3c0b632013-02-08 15:53:37 +01005155 ifevent->action, ifevent->flags, ifevent->ifidx,
Hante Meuleman37a869e2015-10-29 20:33:17 +01005156 ifevent->bsscfgidx);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005157
Arend van Sprield3c0b632013-02-08 15:53:37 +01005158 mutex_lock(&event->vif_event_lock);
5159 event->action = ifevent->action;
5160 vif = event->vif;
5161
5162 switch (ifevent->action) {
5163 case BRCMF_E_IF_ADD:
5164 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005165 if (!cfg->vif_event.vif) {
5166 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005167 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005168 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005169
5170 ifp->vif = vif;
5171 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005172 if (ifp->ndev) {
5173 vif->wdev.netdev = ifp->ndev;
5174 ifp->ndev->ieee80211_ptr = &vif->wdev;
5175 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5176 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005177 mutex_unlock(&event->vif_event_lock);
5178 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005179 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005180
5181 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005182 mutex_unlock(&event->vif_event_lock);
5183 /* event may not be upon user request */
5184 if (brcmf_cfg80211_vif_event_armed(cfg))
5185 wake_up(&event->vif_wq);
5186 return 0;
5187
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005188 case BRCMF_E_IF_CHANGE:
5189 mutex_unlock(&event->vif_event_lock);
5190 wake_up(&event->vif_wq);
5191 return 0;
5192
Arend van Sprield3c0b632013-02-08 15:53:37 +01005193 default:
5194 mutex_unlock(&event->vif_event_lock);
5195 break;
5196 }
5197 return -EINVAL;
5198}
5199
Arend van Spriel5b435de2011-10-05 13:19:03 +02005200static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5201{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005202 conf->frag_threshold = (u32)-1;
5203 conf->rts_threshold = (u32)-1;
5204 conf->retry_short = (u32)-1;
5205 conf->retry_long = (u32)-1;
5206 conf->tx_power = -1;
5207}
5208
Arend van Spriel5c36b992012-11-14 18:46:05 -08005209static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005211 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5212 brcmf_notify_connect_status);
5213 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5214 brcmf_notify_connect_status);
5215 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5216 brcmf_notify_connect_status);
5217 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5218 brcmf_notify_connect_status);
5219 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5220 brcmf_notify_connect_status);
5221 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5222 brcmf_notify_connect_status);
5223 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5224 brcmf_notify_roaming_status);
5225 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5226 brcmf_notify_mic_status);
5227 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5228 brcmf_notify_connect_status);
5229 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5230 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005231 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5232 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005233 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005234 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005235 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5236 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005237 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5238 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005239 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5240 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005241 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5242 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005243}
5244
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005245static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005246{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005247 kfree(cfg->conf);
5248 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005249 kfree(cfg->escan_ioctl_buf);
5250 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005251 kfree(cfg->extra_buf);
5252 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005253 kfree(cfg->pmk_list);
5254 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005255}
5256
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005257static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005258{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005259 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5260 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005261 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005262 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5263 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005264 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005265 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5266 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005267 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005268 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5269 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005270 goto init_priv_mem_out;
5271
5272 return 0;
5273
5274init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005275 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005276
5277 return -ENOMEM;
5278}
5279
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005280static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005281{
5282 s32 err = 0;
5283
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005284 cfg->scan_request = NULL;
5285 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005286 cfg->active_scan = true; /* we do active scan per default */
5287 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005288 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005289 if (err)
5290 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005291 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005292 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005293 brcmf_init_escan(cfg);
5294 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005295 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005296 return err;
5297}
5298
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005299static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005300{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005301 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005302 brcmf_abort_scanning(cfg);
5303 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005304}
5305
Arend van Sprield3c0b632013-02-08 15:53:37 +01005306static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5307{
5308 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005309 mutex_init(&event->vif_event_lock);
5310}
5311
Hante Meuleman1119e232015-11-25 11:32:42 +01005312static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005313{
Hante Meuleman1119e232015-11-25 11:32:42 +01005314 s32 err;
5315 u32 bcn_timeout;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005316 __le32 roamtrigger[2];
5317 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005318
Hante Meuleman1119e232015-11-25 11:32:42 +01005319 /* Configure beacon timeout value based upon roaming setting */
5320 if (brcmf_roamoff)
5321 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
5322 else
5323 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
5324 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
5325 if (err) {
5326 brcmf_err("bcn_timeout error (%d)\n", err);
5327 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005328 }
5329
Hante Meuleman1119e232015-11-25 11:32:42 +01005330 /* Enable/Disable built-in roaming to allow supplicant to take care of
5331 * roaming.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005332 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005333 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5334 brcmf_roamoff ? "Off" : "On");
5335 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005336 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005337 brcmf_err("roam_off error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005338 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005339 }
5340
Arend van Sprielf588bc02011-10-12 20:51:22 +02005341 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5342 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005343 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005344 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005345 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005346 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005347 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005348 }
5349
Arend van Sprielf588bc02011-10-12 20:51:22 +02005350 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5351 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005352 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005353 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005354 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005355 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005356 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005357 }
5358
Hante Meuleman1119e232015-11-25 11:32:42 +01005359roam_setup_done:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005360 return err;
5361}
5362
5363static s32
Hante Meuleman1678ba82015-12-10 13:43:00 +01005364brcmf_dongle_scantime(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005365{
5366 s32 err = 0;
5367
Arend van Sprielac24be62012-10-22 10:36:23 -07005368 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005369 BRCMF_SCAN_CHANNEL_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005370 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005371 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005372 goto dongle_scantime_out;
5373 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005374 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005375 BRCMF_SCAN_UNASSOC_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005376 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005377 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005378 goto dongle_scantime_out;
5379 }
5380
Arend van Sprielac24be62012-10-22 10:36:23 -07005381 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005382 BRCMF_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005383 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005384 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005385 goto dongle_scantime_out;
5386 }
5387
5388dongle_scantime_out:
5389 return err;
5390}
5391
Arend van Sprielb48d8912014-07-12 08:49:41 +02005392static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5393 struct brcmu_chan *ch)
5394{
5395 u32 ht40_flag;
5396
5397 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5398 if (ch->sb == BRCMU_CHAN_SB_U) {
5399 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5400 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5401 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5402 } else {
5403 /* It should be one of
5404 * IEEE80211_CHAN_NO_HT40 or
5405 * IEEE80211_CHAN_NO_HT40PLUS
5406 */
5407 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5408 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5409 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5410 }
5411}
5412
5413static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5414 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005415{
5416 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005417 struct ieee80211_supported_band *band;
5418 struct ieee80211_channel *channel;
5419 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005420 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005421 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005422 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005423 u8 *pbuf;
5424 u32 i, j;
5425 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005426 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005427 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005428
5429 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5430
5431 if (pbuf == NULL)
5432 return -ENOMEM;
5433
5434 list = (struct brcmf_chanspec_list *)pbuf;
5435
5436 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5437 BRCMF_DCMD_MEDLEN);
5438 if (err) {
5439 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005440 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005441 }
5442
Arend van Sprielb48d8912014-07-12 08:49:41 +02005443 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005444 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5445 if (band)
5446 for (i = 0; i < band->n_channels; i++)
5447 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5448 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5449 if (band)
5450 for (i = 0; i < band->n_channels; i++)
5451 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005452
5453 total = le32_to_cpu(list->count);
5454 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005455 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5456 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005457
Franky Lin83cf17a2013-04-11 13:28:50 +02005458 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005459 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005460 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005461 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005462 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005463 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005464 continue;
5465 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005466 if (!band)
5467 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005468 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005469 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005470 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005471 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005472 ch.bw == BRCMU_CHAN_BW_80)
5473 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005474
5475 channel = band->channels;
5476 index = band->n_channels;
5477 for (j = 0; j < band->n_channels; j++) {
5478 if (channel[j].hw_value == ch.chnum) {
5479 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005480 break;
5481 }
5482 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005483 channel[index].center_freq =
5484 ieee80211_channel_to_frequency(ch.chnum, band->band);
5485 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005486
Arend van Sprielb48d8912014-07-12 08:49:41 +02005487 /* assuming the chanspecs order is HT20,
5488 * HT40 upper, HT40 lower, and VHT80.
5489 */
5490 if (ch.bw == BRCMU_CHAN_BW_80) {
5491 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5492 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5493 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5494 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005495 /* enable the channel and disable other bandwidths
5496 * for now as mentioned order assure they are enabled
5497 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005498 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005499 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5500 IEEE80211_CHAN_NO_80MHZ;
5501 ch.bw = BRCMU_CHAN_BW_20;
5502 cfg->d11inf.encchspec(&ch);
5503 chaninfo = ch.chspec;
5504 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5505 &chaninfo);
5506 if (!err) {
5507 if (chaninfo & WL_CHAN_RADAR)
5508 channel[index].flags |=
5509 (IEEE80211_CHAN_RADAR |
5510 IEEE80211_CHAN_NO_IR);
5511 if (chaninfo & WL_CHAN_PASSIVE)
5512 channel[index].flags |=
5513 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005514 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005515 }
5516 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005517
Arend van Sprielb48d8912014-07-12 08:49:41 +02005518fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005519 kfree(pbuf);
5520 return err;
5521}
5522
Arend van Sprielb48d8912014-07-12 08:49:41 +02005523static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005524{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005525 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5526 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005527 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005528 struct brcmf_chanspec_list *list;
5529 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005530 u32 val;
5531 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005532 struct brcmu_chan ch;
5533 u32 num_chan;
5534 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005535
5536 /* verify support for bw_cap command */
5537 val = WLC_BAND_5G;
5538 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5539
5540 if (!err) {
5541 /* only set 2G bandwidth using bw_cap command */
5542 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5543 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5544 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5545 sizeof(band_bwcap));
5546 } else {
5547 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5548 val = WLC_N_BW_40ALL;
5549 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5550 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005551
5552 if (!err) {
5553 /* update channel info in 2G band */
5554 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5555
5556 if (pbuf == NULL)
5557 return -ENOMEM;
5558
5559 ch.band = BRCMU_CHAN_BAND_2G;
5560 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005561 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005562 ch.chnum = 0;
5563 cfg->d11inf.encchspec(&ch);
5564
5565 /* pass encoded chanspec in query */
5566 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5567
5568 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5569 BRCMF_DCMD_MEDLEN);
5570 if (err) {
5571 brcmf_err("get chanspecs error (%d)\n", err);
5572 kfree(pbuf);
5573 return err;
5574 }
5575
5576 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5577 list = (struct brcmf_chanspec_list *)pbuf;
5578 num_chan = le32_to_cpu(list->count);
5579 for (i = 0; i < num_chan; i++) {
5580 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5581 cfg->d11inf.decchspec(&ch);
5582 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5583 continue;
5584 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5585 continue;
5586 for (j = 0; j < band->n_channels; j++) {
5587 if (band->channels[j].hw_value == ch.chnum)
5588 break;
5589 }
5590 if (WARN_ON(j == band->n_channels))
5591 continue;
5592
5593 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5594 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005595 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005596 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005597 return err;
5598}
5599
Arend van Spriel2375d972014-01-06 12:40:41 +01005600static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5601{
5602 u32 band, mimo_bwcap;
5603 int err;
5604
5605 band = WLC_BAND_2G;
5606 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5607 if (!err) {
5608 bw_cap[IEEE80211_BAND_2GHZ] = band;
5609 band = WLC_BAND_5G;
5610 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5611 if (!err) {
5612 bw_cap[IEEE80211_BAND_5GHZ] = band;
5613 return;
5614 }
5615 WARN_ON(1);
5616 return;
5617 }
5618 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5619 mimo_bwcap = 0;
5620 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5621 if (err)
5622 /* assume 20MHz if firmware does not give a clue */
5623 mimo_bwcap = WLC_N_BW_20ALL;
5624
5625 switch (mimo_bwcap) {
5626 case WLC_N_BW_40ALL:
5627 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5628 /* fall-thru */
5629 case WLC_N_BW_20IN2G_40IN5G:
5630 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5631 /* fall-thru */
5632 case WLC_N_BW_20ALL:
5633 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5634 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5635 break;
5636 default:
5637 brcmf_err("invalid mimo_bw_cap value\n");
5638 }
5639}
Hante Meulemand48200b2013-04-03 12:40:29 +02005640
Arend van Spriel18d6c532014-05-12 10:47:35 +02005641static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5642 u32 bw_cap[2], u32 nchain)
5643{
5644 band->ht_cap.ht_supported = true;
5645 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5646 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5647 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5648 }
5649 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5650 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5651 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5652 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5653 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5654 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5655}
5656
5657static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5658{
5659 u16 mcs_map;
5660 int i;
5661
5662 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5663 mcs_map = (mcs_map << 2) | supp;
5664
5665 return cpu_to_le16(mcs_map);
5666}
5667
5668static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005669 u32 bw_cap[2], u32 nchain, u32 txstreams,
5670 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
Arend van Spriel18d6c532014-05-12 10:47:35 +02005671{
5672 __le16 mcs_map;
5673
5674 /* not allowed in 2.4G band */
5675 if (band->band == IEEE80211_BAND_2GHZ)
5676 return;
5677
5678 band->vht_cap.vht_supported = true;
5679 /* 80MHz is mandatory */
5680 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5681 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5682 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5683 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5684 }
5685 /* all support 256-QAM */
5686 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5687 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5688 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005689
5690 /* Beamforming support information */
5691 if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
5692 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
5693 if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
5694 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
5695 if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
5696 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
5697 if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
5698 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
5699
5700 if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
5701 band->vht_cap.cap |=
5702 (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
5703 band->vht_cap.cap |= ((txstreams - 1) <<
5704 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
5705 band->vht_cap.cap |=
5706 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
5707 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005708}
5709
Arend van Sprielb48d8912014-07-12 08:49:41 +02005710static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005711{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005712 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005713 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005714 u32 nmode = 0;
5715 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005716 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005717 u32 rxchain;
5718 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005719 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005720 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005721 struct ieee80211_supported_band *band;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005722 u32 txstreams = 0;
5723 u32 txbf_bfe_cap = 0;
5724 u32 txbf_bfr_cap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005725
Arend van Spriel18d6c532014-05-12 10:47:35 +02005726 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005727 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5728 if (err) {
5729 brcmf_err("nmode error (%d)\n", err);
5730 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005731 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005732 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005733 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5734 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5735 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005736
Daniel Kim4aca7a12014-02-25 20:30:36 +01005737 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5738 if (err) {
5739 brcmf_err("rxchain error (%d)\n", err);
5740 nchain = 1;
5741 } else {
5742 for (nchain = 0; rxchain; nchain++)
5743 rxchain = rxchain & (rxchain - 1);
5744 }
5745 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5746
Arend van Sprielb48d8912014-07-12 08:49:41 +02005747 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005748 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005749 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005750 return err;
5751 }
5752
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005753 if (vhtmode) {
5754 (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
5755 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
5756 &txbf_bfe_cap);
5757 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
5758 &txbf_bfr_cap);
5759 }
5760
Arend van Sprielb48d8912014-07-12 08:49:41 +02005761 wiphy = cfg_to_wiphy(cfg);
5762 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5763 band = wiphy->bands[i];
5764 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005765 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005766
Arend van Spriel18d6c532014-05-12 10:47:35 +02005767 if (nmode)
5768 brcmf_update_ht_cap(band, bw_cap, nchain);
5769 if (vhtmode)
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005770 brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
5771 txbf_bfe_cap, txbf_bfr_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005772 }
5773
Arend van Sprielb48d8912014-07-12 08:49:41 +02005774 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005775}
5776
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005777static const struct ieee80211_txrx_stypes
5778brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5779 [NL80211_IFTYPE_STATION] = {
5780 .tx = 0xffff,
5781 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5782 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5783 },
5784 [NL80211_IFTYPE_P2P_CLIENT] = {
5785 .tx = 0xffff,
5786 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5787 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5788 },
5789 [NL80211_IFTYPE_P2P_GO] = {
5790 .tx = 0xffff,
5791 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5792 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5793 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5794 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5795 BIT(IEEE80211_STYPE_AUTH >> 4) |
5796 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5797 BIT(IEEE80211_STYPE_ACTION >> 4)
5798 },
5799 [NL80211_IFTYPE_P2P_DEVICE] = {
5800 .tx = 0xffff,
5801 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5802 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5803 }
5804};
5805
Arend van Spriel0882dda2015-08-20 22:06:03 +02005806/**
5807 * brcmf_setup_ifmodes() - determine interface modes and combinations.
5808 *
5809 * @wiphy: wiphy object.
5810 * @ifp: interface object needed for feat module api.
5811 *
5812 * The interface modes and combinations are determined dynamically here
5813 * based on firmware functionality.
5814 *
5815 * no p2p and no mbss:
5816 *
5817 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5818 *
5819 * no p2p and mbss:
5820 *
5821 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5822 * #AP <= 4, matching BI, channels = 1, 4 total
5823 *
5824 * p2p, no mchan, and mbss:
5825 *
5826 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
5827 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5828 * #AP <= 4, matching BI, channels = 1, 4 total
5829 *
5830 * p2p, mchan, and mbss:
5831 *
5832 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
5833 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5834 * #AP <= 4, matching BI, channels = 1, 4 total
5835 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005836static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5837{
5838 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02005839 struct ieee80211_iface_limit *c0_limits = NULL;
5840 struct ieee80211_iface_limit *p2p_limits = NULL;
5841 struct ieee80211_iface_limit *mbss_limits = NULL;
5842 bool mbss, p2p;
5843 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005844
Arend van Spriel0882dda2015-08-20 22:06:03 +02005845 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
5846 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
5847
5848 n_combos = 1 + !!p2p + !!mbss;
5849 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005850 if (!combo)
5851 goto err;
5852
Arend van Spriel0882dda2015-08-20 22:06:03 +02005853 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
5854 if (!c0_limits)
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005855 goto err;
5856
Arend van Spriel0882dda2015-08-20 22:06:03 +02005857 if (p2p) {
5858 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
5859 if (!p2p_limits)
5860 goto err;
5861 }
5862
5863 if (mbss) {
5864 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
5865 if (!mbss_limits)
5866 goto err;
5867 }
5868
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005869 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5870 BIT(NL80211_IFTYPE_ADHOC) |
5871 BIT(NL80211_IFTYPE_AP);
5872
Arend van Spriel0882dda2015-08-20 22:06:03 +02005873 c = 0;
5874 i = 0;
5875 combo[c].num_different_channels = 1;
5876 c0_limits[i].max = 1;
5877 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5878 if (p2p) {
5879 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5880 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005881 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5882 BIT(NL80211_IFTYPE_P2P_GO) |
5883 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02005884 c0_limits[i].max = 1;
5885 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5886 c0_limits[i].max = 1;
5887 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5888 BIT(NL80211_IFTYPE_P2P_GO);
5889 } else {
5890 c0_limits[i].max = 1;
5891 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005892 }
Arend van Spriel0882dda2015-08-20 22:06:03 +02005893 combo[c].max_interfaces = i;
5894 combo[c].n_limits = i;
5895 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005896
Arend van Spriel0882dda2015-08-20 22:06:03 +02005897 if (p2p) {
5898 c++;
5899 i = 0;
5900 combo[c].num_different_channels = 1;
5901 p2p_limits[i].max = 1;
5902 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5903 p2p_limits[i].max = 1;
5904 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
5905 p2p_limits[i].max = 1;
5906 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
5907 p2p_limits[i].max = 1;
5908 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5909 combo[c].max_interfaces = i;
5910 combo[c].n_limits = i;
5911 combo[c].limits = p2p_limits;
5912 }
5913
5914 if (mbss) {
5915 c++;
5916 combo[c].beacon_int_infra_match = true;
5917 combo[c].num_different_channels = 1;
5918 mbss_limits[0].max = 4;
5919 mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
5920 combo[c].max_interfaces = 4;
5921 combo[c].n_limits = 1;
5922 combo[c].limits = mbss_limits;
5923 }
5924 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005925 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005926 return 0;
5927
5928err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02005929 kfree(c0_limits);
5930 kfree(p2p_limits);
5931 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005932 kfree(combo);
5933 return -ENOMEM;
5934}
5935
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005936static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
5937{
5938 /* scheduled scan settings */
5939 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
5940 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
5941 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5942 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
5943}
5944
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005945#ifdef CONFIG_PM
5946static const struct wiphy_wowlan_support brcmf_wowlan_support = {
5947 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01005948 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
5949 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
5950 .pattern_min_len = 1,
5951 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005952};
5953#endif
5954
5955static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
5956{
5957#ifdef CONFIG_PM
5958 /* wowl settings */
5959 wiphy->wowlan = &brcmf_wowlan_support;
5960#endif
5961}
5962
Arend van Sprielb48d8912014-07-12 08:49:41 +02005963static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005964{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005965 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005966 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005967 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005968 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005969 __le32 bandlist[3];
5970 u32 n_bands;
5971 int err, i;
5972
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005973 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
5974 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5975 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005976
5977 err = brcmf_setup_ifmodes(wiphy, ifp);
5978 if (err)
5979 return err;
5980
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005981 for (i = 0, combo = wiphy->iface_combinations;
5982 i < wiphy->n_iface_combinations; i++, combo++) {
5983 max_interfaces = max(max_interfaces, combo->max_interfaces);
5984 }
5985
5986 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
5987 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005988 u8 *addr = drvr->addresses[i].addr;
5989
5990 memcpy(addr, drvr->mac, ETH_ALEN);
5991 if (i) {
5992 addr[0] |= BIT(1);
5993 addr[ETH_ALEN - 1] ^= i;
5994 }
5995 }
5996 wiphy->addresses = drvr->addresses;
5997 wiphy->n_addresses = i;
5998
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005999 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
6000 wiphy->cipher_suites = __wl_cipher_suites;
6001 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
6002 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
6003 WIPHY_FLAG_OFFCHAN_TX |
6004 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
6005 WIPHY_FLAG_SUPPORTS_TDLS;
6006 if (!brcmf_roamoff)
6007 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
6008 wiphy->mgmt_stypes = brcmf_txrx_stypes;
6009 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02006010 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
6011 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006012
6013 /* vendor commands/events support */
6014 wiphy->vendor_commands = brcmf_vendor_cmds;
6015 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
6016
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006017 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
6018 brcmf_wiphy_wowl_params(wiphy);
6019
Arend van Spriel58de92d2015-04-14 20:10:24 +02006020 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
6021 sizeof(bandlist));
6022 if (err) {
6023 brcmf_err("could not obtain band info: err=%d\n", err);
6024 return err;
6025 }
6026 /* first entry in bandlist is number of bands */
6027 n_bands = le32_to_cpu(bandlist[0]);
6028 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
6029 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
6030 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
6031 GFP_KERNEL);
6032 if (!band)
6033 return -ENOMEM;
6034
6035 band->channels = kmemdup(&__wl_2ghz_channels,
6036 sizeof(__wl_2ghz_channels),
6037 GFP_KERNEL);
6038 if (!band->channels) {
6039 kfree(band);
6040 return -ENOMEM;
6041 }
6042
6043 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
6044 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
6045 }
6046 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
6047 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
6048 GFP_KERNEL);
6049 if (!band)
6050 return -ENOMEM;
6051
6052 band->channels = kmemdup(&__wl_5ghz_channels,
6053 sizeof(__wl_5ghz_channels),
6054 GFP_KERNEL);
6055 if (!band->channels) {
6056 kfree(band);
6057 return -ENOMEM;
6058 }
6059
6060 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
6061 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
6062 }
6063 }
6064 err = brcmf_setup_wiphybands(wiphy);
6065 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006066}
6067
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006068static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006069{
6070 struct net_device *ndev;
6071 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01006072 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006073 s32 power_mode;
6074 s32 err = 0;
6075
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006076 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006077 return err;
6078
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006079 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006080 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006081 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006082
Hante Meuleman40a23292013-01-02 15:22:51 +01006083 /* make sure RF is ready for work */
6084 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6085
Hante Meuleman1678ba82015-12-10 13:43:00 +01006086 brcmf_dongle_scantime(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006087
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006088 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006089 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006090 if (err)
6091 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006092 brcmf_dbg(INFO, "power save set to %s\n",
6093 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006094
Hante Meuleman1119e232015-11-25 11:32:42 +01006095 err = brcmf_dongle_roam(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006096 if (err)
6097 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006098 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
6099 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006100 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006101 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006102
Hante Meulemanb3657452013-05-27 21:09:53 +02006103 brcmf_configure_arp_offload(ifp, true);
6104
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006105 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006106default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006107
6108 return err;
6109
6110}
6111
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006112static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006113{
Arend van Sprielc1179032012-10-22 13:55:33 -07006114 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006115
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006116 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006117}
6118
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006119static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006120{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006121 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006122
Arend van Spriel5b435de2011-10-05 13:19:03 +02006123 /*
6124 * While going down, if associated with AP disassociate
6125 * from AP to save power
6126 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006127 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006128 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006129
6130 /* Make sure WPA_Supplicant receives all the event
6131 generated due to DISASSOC call to the fw to keep
6132 the state fw and WPA_Supplicant state consistent
6133 */
6134 brcmf_delay(500);
6135 }
6136
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006137 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006138 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006139
Arend van Spriel5b435de2011-10-05 13:19:03 +02006140 return 0;
6141}
6142
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006143s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006144{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006145 struct brcmf_if *ifp = netdev_priv(ndev);
6146 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006147 s32 err = 0;
6148
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006149 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006150 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006151 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006152
6153 return err;
6154}
6155
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006156s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006157{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006158 struct brcmf_if *ifp = netdev_priv(ndev);
6159 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006160 s32 err = 0;
6161
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006162 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006163 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006164 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006165
6166 return err;
6167}
6168
Arend van Spriela7965fb2013-04-11 17:08:37 +02006169enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6170{
6171 struct wireless_dev *wdev = &ifp->vif->wdev;
6172
6173 return wdev->iftype;
6174}
6175
Hante Meulemanbfe81972014-10-28 14:56:16 +01006176bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6177 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006178{
6179 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006180
6181 list_for_each_entry(vif, &cfg->vif_list, list) {
6182 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006183 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006184 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006185 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006186}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006187
6188static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6189 u8 action)
6190{
6191 u8 evt_action;
6192
6193 mutex_lock(&event->vif_event_lock);
6194 evt_action = event->action;
6195 mutex_unlock(&event->vif_event_lock);
6196 return evt_action == action;
6197}
6198
6199void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6200 struct brcmf_cfg80211_vif *vif)
6201{
6202 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6203
6204 mutex_lock(&event->vif_event_lock);
6205 event->vif = vif;
6206 event->action = 0;
6207 mutex_unlock(&event->vif_event_lock);
6208}
6209
6210bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6211{
6212 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6213 bool armed;
6214
6215 mutex_lock(&event->vif_event_lock);
6216 armed = event->vif != NULL;
6217 mutex_unlock(&event->vif_event_lock);
6218
6219 return armed;
6220}
6221int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6222 u8 action, ulong timeout)
6223{
6224 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6225
6226 return wait_event_timeout(event->vif_wq,
6227 vif_event_equals(event, action), timeout);
6228}
6229
Arend van Spriel63db1a42014-12-21 12:43:51 +01006230static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6231 struct regulatory_request *req)
6232{
6233 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6234 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6235 struct brcmf_fil_country_le ccreq;
6236 int i;
6237
6238 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6239 req->alpha2[0], req->alpha2[1]);
6240
6241 /* ignore non-ISO3166 country codes */
6242 for (i = 0; i < sizeof(req->alpha2); i++)
6243 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6244 brcmf_err("not a ISO3166 code\n");
6245 return;
6246 }
6247 memset(&ccreq, 0, sizeof(ccreq));
6248 ccreq.rev = cpu_to_le32(-1);
6249 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006250 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6251 brcmf_err("firmware rejected country setting\n");
6252 return;
6253 }
6254 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006255}
6256
Arend van Sprielb48d8912014-07-12 08:49:41 +02006257static void brcmf_free_wiphy(struct wiphy *wiphy)
6258{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006259 int i;
6260
Arend van Spriel58de92d2015-04-14 20:10:24 +02006261 if (!wiphy)
6262 return;
6263
Arend van Spriel0882dda2015-08-20 22:06:03 +02006264 if (wiphy->iface_combinations) {
6265 for (i = 0; i < wiphy->n_iface_combinations; i++)
6266 kfree(wiphy->iface_combinations[i].limits);
6267 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006268 kfree(wiphy->iface_combinations);
6269 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6270 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6271 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6272 }
6273 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6274 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6275 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6276 }
6277 wiphy_free(wiphy);
6278}
6279
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006280struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006281 struct device *busdev,
6282 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006283{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006284 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006285 struct brcmf_cfg80211_info *cfg;
6286 struct wiphy *wiphy;
6287 struct brcmf_cfg80211_vif *vif;
6288 struct brcmf_if *ifp;
6289 s32 err = 0;
6290 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006291 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006292
6293 if (!ndev) {
6294 brcmf_err("ndev is invalid\n");
6295 return NULL;
6296 }
6297
6298 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006299 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6300 if (!wiphy) {
6301 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006302 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006303 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006304 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006305 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006306
6307 cfg = wiphy_priv(wiphy);
6308 cfg->wiphy = wiphy;
6309 cfg->pub = drvr;
6310 init_vif_event(&cfg->vif_event);
6311 INIT_LIST_HEAD(&cfg->vif_list);
6312
6313 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006314 if (IS_ERR(vif))
6315 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006316
6317 vif->ifp = ifp;
6318 vif->wdev.netdev = ndev;
6319 ndev->ieee80211_ptr = &vif->wdev;
6320 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6321
6322 err = wl_init_priv(cfg);
6323 if (err) {
6324 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006325 brcmf_free_vif(vif);
6326 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006327 }
6328 ifp->vif = vif;
6329
Arend van Sprielb48d8912014-07-12 08:49:41 +02006330 /* determine d11 io type before wiphy setup */
6331 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006332 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006333 brcmf_err("Failed to get D11 version (%d)\n", err);
6334 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006335 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006336 cfg->d11inf.io_type = (u8)io_type;
6337 brcmu_d11_attach(&cfg->d11inf);
6338
6339 err = brcmf_setup_wiphy(wiphy, ifp);
6340 if (err < 0)
6341 goto priv_out;
6342
6343 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006344 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006345 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6346 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6347
6348 /* firmware defaults to 40MHz disabled in 2G band. We signal
6349 * cfg80211 here that we do and have it decide we can enable
6350 * it. But first check if device does support 2G operation.
6351 */
6352 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6353 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6354 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6355 }
6356 err = wiphy_register(wiphy);
6357 if (err < 0) {
6358 brcmf_err("Could not register wiphy device (%d)\n", err);
6359 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006360 }
6361
6362 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6363 * setup 40MHz in 2GHz band and enable OBSS scanning.
6364 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006365 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6366 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006367 if (!err)
6368 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6369 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006370 else
6371 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006372 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006373 /* p2p might require that "if-events" get processed by fweh. So
6374 * activate the already registered event handlers now and activate
6375 * the rest when initialization has completed. drvr->config needs to
6376 * be assigned before activating events.
6377 */
6378 drvr->config = cfg;
6379 err = brcmf_fweh_activate_events(ifp);
6380 if (err) {
6381 brcmf_err("FWEH activation failed (%d)\n", err);
6382 goto wiphy_unreg_out;
6383 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006384
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006385 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006386 if (err) {
6387 brcmf_err("P2P initilisation failed (%d)\n", err);
6388 goto wiphy_unreg_out;
6389 }
6390 err = brcmf_btcoex_attach(cfg);
6391 if (err) {
6392 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6393 brcmf_p2p_detach(&cfg->p2p);
6394 goto wiphy_unreg_out;
6395 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006396
6397 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6398 if (err) {
6399 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6400 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman70b7d942014-07-30 13:20:07 +02006401 } else {
6402 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6403 brcmf_notify_tdls_peer_event);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006404 }
6405
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006406 /* (re-) activate FWEH event handling */
6407 err = brcmf_fweh_activate_events(ifp);
6408 if (err) {
6409 brcmf_err("FWEH activation failed (%d)\n", err);
6410 goto wiphy_unreg_out;
6411 }
6412
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006413 return cfg;
6414
Arend van Sprielb48d8912014-07-12 08:49:41 +02006415wiphy_unreg_out:
6416 wiphy_unregister(cfg->wiphy);
6417priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006418 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006419 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006420 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006421wiphy_out:
6422 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006423 return NULL;
6424}
6425
6426void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6427{
6428 if (!cfg)
6429 return;
6430
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006431 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006432 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006433 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006434 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006435}