blob: dc14dd483779fc5daa3a99dd16e7fdfadea036b4 [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;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100399 bool check_combos = false;
400 int ret = 0;
Arend van Spriel39504a22015-08-20 22:06:05 +0200401
402 memset(&iftype_num[0], 0, sizeof(iftype_num));
403 list_for_each_entry(pos, &cfg->vif_list, list)
Arend van Spriel353c46a2015-12-10 13:43:06 +0100404 if (pos == vif) {
Arend van Spriel39504a22015-08-20 22:06:05 +0200405 iftype_num[new_type]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100406 } else {
407 /* concurrent interfaces so need check combinations */
408 check_combos = true;
Arend van Spriel39504a22015-08-20 22:06:05 +0200409 iftype_num[pos->wdev.iftype]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100410 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200411
Arend van Spriel353c46a2015-12-10 13:43:06 +0100412 if (check_combos)
413 ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
414
415 return ret;
Arend van Spriel39504a22015-08-20 22:06:05 +0200416}
417
418static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
419 enum nl80211_iftype new_type)
420{
421 int iftype_num[NUM_NL80211_IFTYPES];
422 struct brcmf_cfg80211_vif *pos;
423
424 memset(&iftype_num[0], 0, sizeof(iftype_num));
425 list_for_each_entry(pos, &cfg->vif_list, list)
426 iftype_num[pos->wdev.iftype]++;
427
428 iftype_num[new_type]++;
429 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
430}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100431
Arend van Spriel5b435de2011-10-05 13:19:03 +0200432static void convert_key_from_CPU(struct brcmf_wsec_key *key,
433 struct brcmf_wsec_key_le *key_le)
434{
435 key_le->index = cpu_to_le32(key->index);
436 key_le->len = cpu_to_le32(key->len);
437 key_le->algo = cpu_to_le32(key->algo);
438 key_le->flags = cpu_to_le32(key->flags);
439 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
440 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
441 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
442 memcpy(key_le->data, key->data, sizeof(key->data));
443 memcpy(key_le->ea, key->ea, sizeof(key->ea));
444}
445
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200446static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100447send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200448{
449 int err;
450 struct brcmf_wsec_key_le key_le;
451
452 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200453
Hante Meuleman118eb302014-12-21 12:43:49 +0100454 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700455
Hante Meuleman118eb302014-12-21 12:43:49 +0100456 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700457 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200458
Arend van Spriel5b435de2011-10-05 13:19:03 +0200459 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100460 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200461 return err;
462}
463
Hante Meulemanb3657452013-05-27 21:09:53 +0200464static s32
465brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
466{
467 s32 err;
468 u32 mode;
469
470 if (enable)
471 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
472 else
473 mode = 0;
474
475 /* Try to set and enable ARP offload feature, this may fail, then it */
476 /* is simply not supported and err 0 will be returned */
477 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
478 if (err) {
479 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
480 mode, err);
481 err = 0;
482 } else {
483 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
484 if (err) {
485 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
486 enable, err);
487 err = 0;
488 } else
489 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
490 enable, mode);
491 }
492
493 return err;
494}
495
Hante Meuleman8851cce2014-07-30 13:20:02 +0200496static void
497brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
498{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200499 struct brcmf_cfg80211_vif *vif;
500 struct brcmf_if *ifp;
501
502 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
503 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200504
505 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
506 (wdev->iftype == NL80211_IFTYPE_AP) ||
507 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
508 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
509 ADDR_DIRECT);
510 else
511 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
512 ADDR_INDIRECT);
513}
514
Hante Meulemana44aa402014-12-03 21:05:33 +0100515static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
516{
517 struct brcmf_mbss_ssid_le mbss_ssid_le;
518 int bsscfgidx;
519 int err;
520
521 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
522 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
523 if (bsscfgidx < 0)
524 return bsscfgidx;
525
526 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
527 mbss_ssid_le.SSID_len = cpu_to_le32(5);
528 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
529
530 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
531 sizeof(mbss_ssid_le));
532 if (err < 0)
533 brcmf_err("setting ssid failed %d\n", err);
534
535 return err;
536}
537
538/**
539 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
540 *
541 * @wiphy: wiphy device of new interface.
542 * @name: name of the new interface.
543 * @flags: not used.
544 * @params: contains mac address for AP device.
545 */
546static
547struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
548 u32 *flags, struct vif_params *params)
549{
550 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
551 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
552 struct brcmf_cfg80211_vif *vif;
553 int err;
554
555 if (brcmf_cfg80211_vif_event_armed(cfg))
556 return ERR_PTR(-EBUSY);
557
558 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
559
560 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
561 if (IS_ERR(vif))
562 return (struct wireless_dev *)vif;
563
564 brcmf_cfg80211_arm_vif_event(cfg, vif);
565
566 err = brcmf_cfg80211_request_ap_if(ifp);
567 if (err) {
568 brcmf_cfg80211_arm_vif_event(cfg, NULL);
569 goto fail;
570 }
571
572 /* wait for firmware event */
573 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
574 msecs_to_jiffies(1500));
575 brcmf_cfg80211_arm_vif_event(cfg, NULL);
576 if (!err) {
577 brcmf_err("timeout occurred\n");
578 err = -EIO;
579 goto fail;
580 }
581
582 /* interface created in firmware */
583 ifp = vif->ifp;
584 if (!ifp) {
585 brcmf_err("no if pointer provided\n");
586 err = -ENOENT;
587 goto fail;
588 }
589
590 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
591 err = brcmf_net_attach(ifp, true);
592 if (err) {
593 brcmf_err("Registering netdevice failed\n");
594 goto fail;
595 }
596
597 return &ifp->vif->wdev;
598
599fail:
600 brcmf_free_vif(vif);
601 return ERR_PTR(err);
602}
603
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100604static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
605{
606 enum nl80211_iftype iftype;
607
608 iftype = vif->wdev.iftype;
609 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
610}
611
612static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
613{
614 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
615}
616
Arend van Spriel9f440b72013-02-08 15:53:36 +0100617static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
618 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100619 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100620 enum nl80211_iftype type,
621 u32 *flags,
622 struct vif_params *params)
623{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200624 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200625 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200626
Arend van Spriel9f440b72013-02-08 15:53:36 +0100627 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200628 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
629 if (err) {
630 brcmf_err("iface validation failed: err=%d\n", err);
631 return ERR_PTR(err);
632 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100633 switch (type) {
634 case NL80211_IFTYPE_ADHOC:
635 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100636 case NL80211_IFTYPE_AP_VLAN:
637 case NL80211_IFTYPE_WDS:
638 case NL80211_IFTYPE_MONITOR:
639 case NL80211_IFTYPE_MESH_POINT:
640 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100641 case NL80211_IFTYPE_AP:
642 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
643 if (!IS_ERR(wdev))
644 brcmf_cfg80211_update_proto_addr_mode(wdev);
645 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100646 case NL80211_IFTYPE_P2P_CLIENT:
647 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200648 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100649 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200650 if (!IS_ERR(wdev))
651 brcmf_cfg80211_update_proto_addr_mode(wdev);
652 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100653 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100654 default:
655 return ERR_PTR(-EINVAL);
656 }
657}
658
Daniel Kim5e787f72014-06-21 12:11:18 +0200659static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
660{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200661 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200662 brcmf_set_mpc(ifp, mpc);
663}
664
Arend van Sprielf96aa072013-04-05 10:57:48 +0200665void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100666{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100667 s32 err = 0;
668
669 if (check_vif_up(ifp->vif)) {
670 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
671 if (err) {
672 brcmf_err("fail to set mpc\n");
673 return;
674 }
675 brcmf_dbg(INFO, "MPC : %d\n", mpc);
676 }
677}
678
Arend van Spriela0f472a2013-04-05 10:57:49 +0200679s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
680 struct brcmf_if *ifp, bool aborted,
681 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100682{
683 struct brcmf_scan_params_le params_le;
684 struct cfg80211_scan_request *scan_request;
685 s32 err = 0;
686
687 brcmf_dbg(SCAN, "Enter\n");
688
689 /* clear scan request, because the FW abort can cause a second call */
690 /* to this functon and might cause a double cfg80211_scan_done */
691 scan_request = cfg->scan_request;
692 cfg->scan_request = NULL;
693
694 if (timer_pending(&cfg->escan_timeout))
695 del_timer_sync(&cfg->escan_timeout);
696
697 if (fw_abort) {
698 /* Do a scan abort to stop the driver's scan engine */
699 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
700 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800701 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100702 params_le.bss_type = DOT11_BSSTYPE_ANY;
703 params_le.scan_type = 0;
704 params_le.channel_num = cpu_to_le32(1);
705 params_le.nprobes = cpu_to_le32(1);
706 params_le.active_time = cpu_to_le32(-1);
707 params_le.passive_time = cpu_to_le32(-1);
708 params_le.home_time = cpu_to_le32(-1);
709 /* Scan is aborted by setting channel_list[0] to -1 */
710 params_le.channel_list[0] = cpu_to_le16(-1);
711 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200712 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100713 &params_le, sizeof(params_le));
714 if (err)
715 brcmf_err("Scan abort failed\n");
716 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200717
Daniel Kim5e787f72014-06-21 12:11:18 +0200718 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200719
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100720 /*
721 * e-scan can be initiated by scheduled scan
722 * which takes precedence.
723 */
724 if (cfg->sched_escan) {
725 brcmf_dbg(SCAN, "scheduled scan completed\n");
726 cfg->sched_escan = false;
727 if (!aborted)
728 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100729 } else if (scan_request) {
730 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
731 aborted ? "Aborted" : "Done");
732 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100733 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100734 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
735 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100736
737 return err;
738}
739
Arend van Spriel9f440b72013-02-08 15:53:36 +0100740static
741int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
742{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100743 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
744 struct net_device *ndev = wdev->netdev;
745
746 /* vif event pending in firmware */
747 if (brcmf_cfg80211_vif_event_armed(cfg))
748 return -EBUSY;
749
750 if (ndev) {
751 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200752 cfg->escan_info.ifp == netdev_priv(ndev))
753 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
754 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100755
756 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
757 }
758
Arend van Spriel9f440b72013-02-08 15:53:36 +0100759 switch (wdev->iftype) {
760 case NL80211_IFTYPE_ADHOC:
761 case NL80211_IFTYPE_STATION:
762 case NL80211_IFTYPE_AP:
763 case NL80211_IFTYPE_AP_VLAN:
764 case NL80211_IFTYPE_WDS:
765 case NL80211_IFTYPE_MONITOR:
766 case NL80211_IFTYPE_MESH_POINT:
767 return -EOPNOTSUPP;
768 case NL80211_IFTYPE_P2P_CLIENT:
769 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200770 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100771 return brcmf_p2p_del_vif(wiphy, wdev);
772 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100773 default:
774 return -EINVAL;
775 }
776 return -EOPNOTSUPP;
777}
778
Arend van Spriel5b435de2011-10-05 13:19:03 +0200779static s32
780brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
781 enum nl80211_iftype type, u32 *flags,
782 struct vif_params *params)
783{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100784 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700785 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100786 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200787 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200788 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200789 s32 err = 0;
790
Hante Meuleman37a869e2015-10-29 20:33:17 +0100791 brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
792 type);
Hante Meuleman178e9ef2015-09-18 22:08:11 +0200793
794 /* WAR: There are a number of p2p interface related problems which
795 * need to be handled initially (before doing the validate).
796 * wpa_supplicant tends to do iface changes on p2p device/client/go
797 * which are not always possible/allowed. However we need to return
798 * OK otherwise the wpa_supplicant wont start. The situation differs
799 * on configuration and setup (p2pon=1 module param). The first check
800 * is to see if the request is a change to station for p2p iface.
801 */
802 if ((type == NL80211_IFTYPE_STATION) &&
803 ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
804 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
805 (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
806 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
807 /* Now depending on whether module param p2pon=1 was used the
808 * response needs to be either 0 or EOPNOTSUPP. The reason is
809 * that if p2pon=1 is used, but a newer supplicant is used then
810 * we should return an error, as this combination wont work.
811 * In other situations 0 is returned and supplicant will start
812 * normally. It will give a trace in cfg80211, but it is the
813 * only way to get it working. Unfortunately this will result
814 * in situation where we wont support new supplicant in
815 * combination with module param p2pon=1, but that is the way
816 * it is. If the user tries this then unloading of driver might
817 * fail/lock.
818 */
819 if (cfg->p2p.p2pdev_dynamically)
820 return -EOPNOTSUPP;
821 else
822 return 0;
823 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200824 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
825 if (err) {
826 brcmf_err("iface validation failed: err=%d\n", err);
827 return err;
828 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200829 switch (type) {
830 case NL80211_IFTYPE_MONITOR:
831 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100832 brcmf_err("type (%d) : currently we do not support this type\n",
833 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200834 return -EOPNOTSUPP;
835 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200836 infra = 0;
837 break;
838 case NL80211_IFTYPE_STATION:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200839 infra = 1;
840 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200841 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100842 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200843 ap = 1;
844 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200845 default:
846 err = -EINVAL;
847 goto done;
848 }
849
Hante Meuleman1a873342012-09-27 14:17:54 +0200850 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100851 if (type == NL80211_IFTYPE_P2P_GO) {
852 brcmf_dbg(INFO, "IF Type = P2P GO\n");
853 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
854 }
855 if (!err) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100856 brcmf_dbg(INFO, "IF Type = AP\n");
857 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200858 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100859 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200860 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100861 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200862 err = -EAGAIN;
863 goto done;
864 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100865 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100866 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200867 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200868 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200869
Hante Meuleman8851cce2014-07-30 13:20:02 +0200870 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
871
Arend van Spriel5b435de2011-10-05 13:19:03 +0200872done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100873 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200874
875 return err;
876}
877
Franky Lin83cf17a2013-04-11 13:28:50 +0200878static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
879 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200880 struct cfg80211_scan_request *request)
881{
882 u32 n_ssids;
883 u32 n_channels;
884 s32 i;
885 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200886 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200887 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200888 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200889
Joe Perches93803b32015-03-02 19:54:49 -0800890 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200891 params_le->bss_type = DOT11_BSSTYPE_ANY;
892 params_le->scan_type = 0;
893 params_le->channel_num = 0;
894 params_le->nprobes = cpu_to_le32(-1);
895 params_le->active_time = cpu_to_le32(-1);
896 params_le->passive_time = cpu_to_le32(-1);
897 params_le->home_time = cpu_to_le32(-1);
898 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
899
900 /* if request is null exit so it will be all channel broadcast scan */
901 if (!request)
902 return;
903
904 n_ssids = request->n_ssids;
905 n_channels = request->n_channels;
906 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100907 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
908 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200909 if (n_channels > 0) {
910 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200911 chanspec = channel_to_chanspec(&cfg->d11inf,
912 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100913 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
914 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200915 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200916 }
917 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100918 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200919 }
920 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100921 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200922 if (n_ssids > 0) {
923 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
924 n_channels * sizeof(u16);
925 offset = roundup(offset, sizeof(u32));
926 ptr = (char *)params_le + offset;
927 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200928 memset(&ssid_le, 0, sizeof(ssid_le));
929 ssid_le.SSID_len =
930 cpu_to_le32(request->ssids[i].ssid_len);
931 memcpy(ssid_le.SSID, request->ssids[i].ssid,
932 request->ssids[i].ssid_len);
933 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100934 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200935 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100936 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
937 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200938 memcpy(ptr, &ssid_le, sizeof(ssid_le));
939 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200940 }
941 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100942 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200943 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100944 brcmf_dbg(SCAN, "SSID %s len=%d\n",
945 params_le->ssid_le.SSID,
946 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200947 params_le->ssid_le.SSID_len =
948 cpu_to_le32(request->ssids->ssid_len);
949 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
950 request->ssids->ssid_len);
951 }
952 }
953 /* Adding mask to channel numbers */
954 params_le->channel_num =
955 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
956 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
957}
958
959static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200960brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemanc4958102015-11-25 11:32:41 +0100961 struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200962{
963 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
964 offsetof(struct brcmf_escan_params_le, params_le);
965 struct brcmf_escan_params_le *params;
966 s32 err = 0;
967
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100968 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200969
970 if (request != NULL) {
971 /* Allocate space for populating ssids in struct */
972 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
973
974 /* Allocate space for populating ssids in struct */
Hante Meulemane9a6ca82015-11-25 11:32:37 +0100975 params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
Hante Meulemane756af52012-09-11 21:18:52 +0200976 }
977
978 params = kzalloc(params_size, GFP_KERNEL);
979 if (!params) {
980 err = -ENOMEM;
981 goto exit;
982 }
983 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200984 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200985 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
Hante Meulemanc4958102015-11-25 11:32:41 +0100986 params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200987 params->sync_id = cpu_to_le16(0x1234);
988
Arend van Spriela0f472a2013-04-05 10:57:49 +0200989 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200990 if (err) {
991 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100992 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200993 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100994 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200995 }
996
997 kfree(params);
998exit:
999 return err;
1000}
1001
1002static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001003brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +02001004 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001005{
1006 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001007 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001008 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001009 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001010
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001011 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001012 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001013 escan->wiphy = wiphy;
1014 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001015 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001016 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001017 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001018 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001019 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001020 return err;
1021 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001022 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001023 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001024 results->version = 0;
1025 results->count = 0;
1026 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1027
Hante Meulemanc4958102015-11-25 11:32:41 +01001028 err = escan->run(cfg, ifp, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001029 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001030 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001031 return err;
1032}
1033
1034static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001035brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001036 struct cfg80211_scan_request *request,
1037 struct cfg80211_ssid *this_ssid)
1038{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001039 struct brcmf_if *ifp = vif->ifp;
1040 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001041 struct cfg80211_ssid *ssids;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001042 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001043 bool escan_req;
1044 bool spec_scan;
1045 s32 err;
Hante Meuleman675f5d82015-12-10 13:43:01 +01001046 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +02001047 u32 SSID_len;
1048
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001049 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001050
Arend van Sprielc1179032012-10-22 13:55:33 -07001051 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001052 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001053 return -EAGAIN;
1054 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001055 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001056 brcmf_err("Scanning being aborted: status (%lu)\n",
1057 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001058 return -EAGAIN;
1059 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001060 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1061 brcmf_err("Scanning suppressed: status (%lu)\n",
1062 cfg->scan_status);
1063 return -EAGAIN;
1064 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001065 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001066 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001067 return -EAGAIN;
1068 }
1069
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001070 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001071 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1072 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001073
Hante Meulemane756af52012-09-11 21:18:52 +02001074 escan_req = false;
1075 if (request) {
1076 /* scan bss */
1077 ssids = request->ssids;
1078 escan_req = true;
1079 } else {
1080 /* scan in ibss */
1081 /* we don't do escan in ibss */
1082 ssids = this_ssid;
1083 }
1084
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001085 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001086 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001087 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001088 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001089 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001090 if (err)
1091 goto scan_out;
1092
Arend van Spriela0f472a2013-04-05 10:57:49 +02001093 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001094 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001095 goto scan_out;
1096 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001097 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1098 ssids->ssid, ssids->ssid_len);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001099 memset(&ssid_le, 0, sizeof(ssid_le));
1100 SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
1101 ssid_le.SSID_len = cpu_to_le32(0);
Hante Meulemane756af52012-09-11 21:18:52 +02001102 spec_scan = false;
1103 if (SSID_len) {
Hante Meuleman675f5d82015-12-10 13:43:01 +01001104 memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
1105 ssid_le.SSID_len = cpu_to_le32(SSID_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001106 spec_scan = true;
1107 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001108 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001109
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001110 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001111 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001112 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001113 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001114 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001115 goto scan_out;
1116 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001117 brcmf_scan_config_mpc(ifp, 0);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001118 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
1119 sizeof(ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001120 if (err) {
1121 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001122 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
Hante Meuleman675f5d82015-12-10 13:43:01 +01001123 ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001124 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001125 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001126
Daniel Kim5e787f72014-06-21 12:11:18 +02001127 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001128 goto scan_out;
1129 }
1130 }
1131
Hante Meuleman661fa952015-02-06 18:36:47 +01001132 /* Arm scan timeout timer */
1133 mod_timer(&cfg->escan_timeout, jiffies +
1134 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1135
Hante Meulemane756af52012-09-11 21:18:52 +02001136 return 0;
1137
1138scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001139 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001140 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001141 return err;
1142}
1143
Arend van Spriel5b435de2011-10-05 13:19:03 +02001144static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001145brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001146{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001147 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001148 s32 err = 0;
1149
Arend van Sprield96b8012012-12-05 15:26:02 +01001150 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001151 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1152 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001153 return -EIO;
1154
Arend van Spriela0f472a2013-04-05 10:57:49 +02001155 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001156
Arend van Spriel5b435de2011-10-05 13:19:03 +02001157 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001158 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001159
Arend van Sprield96b8012012-12-05 15:26:02 +01001160 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001161 return err;
1162}
1163
1164static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1165{
1166 s32 err = 0;
1167
Arend van Sprielac24be62012-10-22 10:36:23 -07001168 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1169 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001170 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001171 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001172
1173 return err;
1174}
1175
1176static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1177{
1178 s32 err = 0;
1179
Arend van Sprielac24be62012-10-22 10:36:23 -07001180 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1181 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001182 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001183 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184
1185 return err;
1186}
1187
1188static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1189{
1190 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001191 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001192
Arend van Sprielac24be62012-10-22 10:36:23 -07001193 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001194 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001195 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001196 return err;
1197 }
1198 return err;
1199}
1200
1201static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1202{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001203 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1204 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001205 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001206 s32 err = 0;
1207
Arend van Sprield96b8012012-12-05 15:26:02 +01001208 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001209 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001210 return -EIO;
1211
1212 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001213 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1214 cfg->conf->rts_threshold = wiphy->rts_threshold;
1215 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001216 if (!err)
1217 goto done;
1218 }
1219 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001220 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1221 cfg->conf->frag_threshold = wiphy->frag_threshold;
1222 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001223 if (!err)
1224 goto done;
1225 }
1226 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001227 && (cfg->conf->retry_long != wiphy->retry_long)) {
1228 cfg->conf->retry_long = wiphy->retry_long;
1229 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001230 if (!err)
1231 goto done;
1232 }
1233 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001234 && (cfg->conf->retry_short != wiphy->retry_short)) {
1235 cfg->conf->retry_short = wiphy->retry_short;
1236 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001237 if (!err)
1238 goto done;
1239 }
1240
1241done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001242 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001243 return err;
1244}
1245
Arend van Spriel5b435de2011-10-05 13:19:03 +02001246static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1247{
1248 memset(prof, 0, sizeof(*prof));
1249}
1250
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001251static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1252{
1253 u16 reason;
1254
1255 switch (e->event_code) {
1256 case BRCMF_E_DEAUTH:
1257 case BRCMF_E_DEAUTH_IND:
1258 case BRCMF_E_DISASSOC_IND:
1259 reason = e->reason;
1260 break;
1261 case BRCMF_E_LINK:
1262 default:
1263 reason = 0;
1264 break;
1265 }
1266 return reason;
1267}
1268
1269static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001270{
Piotr Haber61730d42013-04-23 12:53:12 +02001271 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001272 s32 err = 0;
1273
Arend van Sprield96b8012012-12-05 15:26:02 +01001274 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001275
Hante Meulemanb0a79082015-12-10 13:43:07 +01001276 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001277 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001278 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001279 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001280 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001281 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001282 }
Hante Meulemanb0a79082015-12-10 13:43:07 +01001283 if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1284 (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
1285 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
1286 true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001287 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001288 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001289 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1290 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001291 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001292}
1293
1294static s32
1295brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1296 struct cfg80211_ibss_params *params)
1297{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001298 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001299 struct brcmf_if *ifp = netdev_priv(ndev);
1300 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001301 struct brcmf_join_params join_params;
1302 size_t join_params_size = 0;
1303 s32 err = 0;
1304 s32 wsec = 0;
1305 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001306 u16 chanspec;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001307 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001308
Arend van Sprield96b8012012-12-05 15:26:02 +01001309 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001310 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001311 return -EIO;
1312
1313 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001314 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001315 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001316 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001317 return -EOPNOTSUPP;
1318 }
1319
Arend van Sprielc1179032012-10-22 13:55:33 -07001320 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321
1322 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001323 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001324 else
Arend van Spriel16886732012-12-05 15:26:04 +01001325 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001326
Johannes Berg683b6d32012-11-08 21:25:48 +01001327 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001328 brcmf_dbg(CONN, "channel: %d\n",
1329 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001330 else
Arend van Spriel16886732012-12-05 15:26:04 +01001331 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001332
1333 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001334 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001335 else
Arend van Spriel16886732012-12-05 15:26:04 +01001336 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337
1338 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001339 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001340 else
Arend van Spriel16886732012-12-05 15:26:04 +01001341 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001342
1343 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001344 brcmf_dbg(CONN, "beacon interval: %d\n",
1345 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001346 else
Arend van Spriel16886732012-12-05 15:26:04 +01001347 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001348
1349 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001350 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001351 else
Arend van Spriel16886732012-12-05 15:26:04 +01001352 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001353
1354 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001355 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001356 else
Arend van Spriel16886732012-12-05 15:26:04 +01001357 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001358
1359 /* Configure Privacy for starter */
1360 if (params->privacy)
1361 wsec |= WEP_ENABLED;
1362
Arend van Sprielc1179032012-10-22 13:55:33 -07001363 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001364 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001365 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001366 goto done;
1367 }
1368
1369 /* Configure Beacon Interval for starter */
1370 if (params->beacon_interval)
1371 bcnprd = params->beacon_interval;
1372 else
1373 bcnprd = 100;
1374
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001375 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001376 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001377 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001378 goto done;
1379 }
1380
1381 /* Configure required join parameter */
1382 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1383
1384 /* SSID */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001385 ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1386 memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1387 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001388 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001389
1390 /* BSSID */
1391 if (params->bssid) {
1392 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001393 join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001394 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001395 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001396 eth_broadcast_addr(join_params.params_le.bssid);
1397 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001398 }
1399
Arend van Spriel5b435de2011-10-05 13:19:03 +02001400 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001401 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001402 u32 target_channel;
1403
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001404 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001405 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001406 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001407 if (params->channel_fixed) {
1408 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001409 chanspec = chandef_to_chanspec(&cfg->d11inf,
1410 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001411 join_params.params_le.chanspec_list[0] =
1412 cpu_to_le16(chanspec);
1413 join_params.params_le.chanspec_num = cpu_to_le32(1);
1414 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415 }
1416
1417 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001418 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001419 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001420 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001421 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001422 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001423 goto done;
1424 }
1425 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001426 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001427
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001428 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001429
1430
Arend van Sprielc1179032012-10-22 13:55:33 -07001431 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001432 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001433 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001434 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001435 goto done;
1436 }
1437
1438done:
1439 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001440 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001441 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001442 return err;
1443}
1444
1445static s32
1446brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1447{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001448 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001449
Arend van Sprield96b8012012-12-05 15:26:02 +01001450 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman6a98d642016-01-02 09:41:40 +01001451 if (!check_vif_up(ifp->vif)) {
1452 /* When driver is being unloaded, it can end up here. If an
1453 * error is returned then later on a debug trace in the wireless
1454 * core module will be printed. To avoid this 0 is returned.
1455 */
1456 return 0;
1457 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001458
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001459 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001460
Arend van Sprield96b8012012-12-05 15:26:02 +01001461 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001462
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001463 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001464}
1465
1466static s32 brcmf_set_wpa_version(struct net_device *ndev,
1467 struct cfg80211_connect_params *sme)
1468{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001469 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001470 struct brcmf_cfg80211_security *sec;
1471 s32 val = 0;
1472 s32 err = 0;
1473
1474 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1475 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1476 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1477 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1478 else
1479 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001480 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001481 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001482 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001483 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001484 return err;
1485 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001486 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001487 sec->wpa_versions = sme->crypto.wpa_versions;
1488 return err;
1489}
1490
1491static s32 brcmf_set_auth_type(struct net_device *ndev,
1492 struct cfg80211_connect_params *sme)
1493{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001494 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001495 struct brcmf_cfg80211_security *sec;
1496 s32 val = 0;
1497 s32 err = 0;
1498
1499 switch (sme->auth_type) {
1500 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1501 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001502 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001503 break;
1504 case NL80211_AUTHTYPE_SHARED_KEY:
1505 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001506 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001507 break;
1508 case NL80211_AUTHTYPE_AUTOMATIC:
1509 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001510 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001511 break;
1512 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001513 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001514 default:
1515 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001516 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001517 break;
1518 }
1519
Hante Meuleman89286dc2013-02-08 15:53:46 +01001520 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001521 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001522 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001523 return err;
1524 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001525 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001526 sec->auth_type = sme->auth_type;
1527 return err;
1528}
1529
1530static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001531brcmf_set_wsec_mode(struct net_device *ndev,
1532 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001533{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001534 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001535 struct brcmf_cfg80211_security *sec;
1536 s32 pval = 0;
1537 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001538 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001539 s32 err = 0;
1540
1541 if (sme->crypto.n_ciphers_pairwise) {
1542 switch (sme->crypto.ciphers_pairwise[0]) {
1543 case WLAN_CIPHER_SUITE_WEP40:
1544 case WLAN_CIPHER_SUITE_WEP104:
1545 pval = WEP_ENABLED;
1546 break;
1547 case WLAN_CIPHER_SUITE_TKIP:
1548 pval = TKIP_ENABLED;
1549 break;
1550 case WLAN_CIPHER_SUITE_CCMP:
1551 pval = AES_ENABLED;
1552 break;
1553 case WLAN_CIPHER_SUITE_AES_CMAC:
1554 pval = AES_ENABLED;
1555 break;
1556 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001557 brcmf_err("invalid cipher pairwise (%d)\n",
1558 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001559 return -EINVAL;
1560 }
1561 }
1562 if (sme->crypto.cipher_group) {
1563 switch (sme->crypto.cipher_group) {
1564 case WLAN_CIPHER_SUITE_WEP40:
1565 case WLAN_CIPHER_SUITE_WEP104:
1566 gval = WEP_ENABLED;
1567 break;
1568 case WLAN_CIPHER_SUITE_TKIP:
1569 gval = TKIP_ENABLED;
1570 break;
1571 case WLAN_CIPHER_SUITE_CCMP:
1572 gval = AES_ENABLED;
1573 break;
1574 case WLAN_CIPHER_SUITE_AES_CMAC:
1575 gval = AES_ENABLED;
1576 break;
1577 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001578 brcmf_err("invalid cipher group (%d)\n",
1579 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001580 return -EINVAL;
1581 }
1582 }
1583
Arend van Spriel16886732012-12-05 15:26:04 +01001584 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001585 /* In case of privacy, but no security and WPS then simulate */
1586 /* setting AES. WPS-2.0 allows no security */
1587 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1588 sme->privacy)
1589 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001590
1591 if (mfp)
1592 wsec = pval | gval | MFP_CAPABLE;
1593 else
1594 wsec = pval | gval;
1595 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001596 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001597 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001598 return err;
1599 }
1600
Arend van Spriel06bb1232012-09-27 14:17:56 +02001601 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001602 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1603 sec->cipher_group = sme->crypto.cipher_group;
1604
1605 return err;
1606}
1607
1608static s32
1609brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1610{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001611 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001612 struct brcmf_cfg80211_security *sec;
1613 s32 val = 0;
1614 s32 err = 0;
1615
1616 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001617 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1618 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001619 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001620 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001621 return err;
1622 }
1623 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1624 switch (sme->crypto.akm_suites[0]) {
1625 case WLAN_AKM_SUITE_8021X:
1626 val = WPA_AUTH_UNSPECIFIED;
1627 break;
1628 case WLAN_AKM_SUITE_PSK:
1629 val = WPA_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 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1637 switch (sme->crypto.akm_suites[0]) {
1638 case WLAN_AKM_SUITE_8021X:
1639 val = WPA2_AUTH_UNSPECIFIED;
1640 break;
1641 case WLAN_AKM_SUITE_PSK:
1642 val = WPA2_AUTH_PSK;
1643 break;
1644 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001645 brcmf_err("invalid cipher group (%d)\n",
1646 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001647 return -EINVAL;
1648 }
1649 }
1650
Arend van Spriel16886732012-12-05 15:26:04 +01001651 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001652 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1653 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001654 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001655 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001656 return err;
1657 }
1658 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001659 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001660 sec->wpa_auth = sme->crypto.akm_suites[0];
1661
1662 return err;
1663}
1664
1665static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001666brcmf_set_sharedkey(struct net_device *ndev,
1667 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001668{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001669 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001670 struct brcmf_cfg80211_security *sec;
1671 struct brcmf_wsec_key key;
1672 s32 val;
1673 s32 err = 0;
1674
Arend van Spriel16886732012-12-05 15:26:04 +01001675 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001676
Roland Vossena718e2f2011-10-12 20:51:24 +02001677 if (sme->key_len == 0)
1678 return 0;
1679
Arend van Spriel06bb1232012-09-27 14:17:56 +02001680 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001681 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1682 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001683
1684 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1685 return 0;
1686
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001687 if (!(sec->cipher_pairwise &
1688 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1689 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001690
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001691 memset(&key, 0, sizeof(key));
1692 key.len = (u32) sme->key_len;
1693 key.index = (u32) sme->key_idx;
1694 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001695 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001696 return -EINVAL;
1697 }
1698 memcpy(key.data, sme->key, key.len);
1699 key.flags = BRCMF_PRIMARY_KEY;
1700 switch (sec->cipher_pairwise) {
1701 case WLAN_CIPHER_SUITE_WEP40:
1702 key.algo = CRYPTO_ALGO_WEP1;
1703 break;
1704 case WLAN_CIPHER_SUITE_WEP104:
1705 key.algo = CRYPTO_ALGO_WEP128;
1706 break;
1707 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001708 brcmf_err("Invalid algorithm (%d)\n",
1709 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001710 return -EINVAL;
1711 }
1712 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001713 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1714 key.len, key.index, key.algo);
1715 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001716 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001717 if (err)
1718 return err;
1719
1720 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001721 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001722 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001723 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001724 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001725 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001726 }
1727 return err;
1728}
1729
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001730static
1731enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1732 enum nl80211_auth_type type)
1733{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001734 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1735 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1736 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1737 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001738 }
1739 return type;
1740}
1741
Arend van Spriel5b435de2011-10-05 13:19:03 +02001742static s32
1743brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001744 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001745{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001746 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001747 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001748 struct ieee80211_channel *chan = sme->channel;
1749 struct brcmf_join_params join_params;
1750 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001751 const struct brcmf_tlv *rsn_ie;
1752 const struct brcmf_vs_tlv *wpa_ie;
1753 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001754 u32 ie_len;
1755 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001756 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001757 s32 err = 0;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001758 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001759
Arend van Sprield96b8012012-12-05 15:26:02 +01001760 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001761 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001762 return -EIO;
1763
1764 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001765 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001766 return -EOPNOTSUPP;
1767 }
1768
Hante Meuleman89286dc2013-02-08 15:53:46 +01001769 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1770 /* A normal (non P2P) connection request setup. */
1771 ie = NULL;
1772 ie_len = 0;
1773 /* find the WPA_IE */
1774 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1775 if (wpa_ie) {
1776 ie = wpa_ie;
1777 ie_len = wpa_ie->len + TLV_HDR_LEN;
1778 } else {
1779 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001780 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1781 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001782 WLAN_EID_RSN);
1783 if (rsn_ie) {
1784 ie = rsn_ie;
1785 ie_len = rsn_ie->len + TLV_HDR_LEN;
1786 }
1787 }
1788 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1789 }
1790
1791 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1792 sme->ie, sme->ie_len);
1793 if (err)
1794 brcmf_err("Set Assoc REQ IE Failed\n");
1795 else
1796 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1797
Arend van Sprielc1179032012-10-22 13:55:33 -07001798 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001799
1800 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001801 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001802 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001803 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001804 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1805 cfg->channel, chan->center_freq, chanspec);
1806 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001807 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001808 chanspec = 0;
1809 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001810
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001811 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001812
1813 err = brcmf_set_wpa_version(ndev, sme);
1814 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001815 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001816 goto done;
1817 }
1818
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001819 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001820 err = brcmf_set_auth_type(ndev, sme);
1821 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001822 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001823 goto done;
1824 }
1825
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001826 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001827 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001828 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001829 goto done;
1830 }
1831
1832 err = brcmf_set_key_mgmt(ndev, sme);
1833 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001834 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001835 goto done;
1836 }
1837
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001838 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001839 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001840 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001841 goto done;
1842 }
1843
Hante Meuleman89286dc2013-02-08 15:53:46 +01001844 /* Join with specific BSSID and cached SSID
1845 * If SSID is zero join based on BSSID only
1846 */
1847 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1848 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1849 if (cfg->channel)
1850 join_params_size += sizeof(u16);
1851 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1852 if (ext_join_params == NULL) {
1853 err = -ENOMEM;
1854 goto done;
1855 }
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001856 ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
1857 ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
1858 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
1859 if (ssid_len < IEEE80211_MAX_SSID_LEN)
1860 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
1861 ext_join_params->ssid_le.SSID, ssid_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001862
Hante Meuleman89286dc2013-02-08 15:53:46 +01001863 /* Set up join scan parameters */
1864 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001865 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1866
1867 if (sme->bssid)
1868 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1869 else
Joe Perches93803b32015-03-02 19:54:49 -08001870 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001871
1872 if (cfg->channel) {
1873 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1874
1875 ext_join_params->assoc_le.chanspec_list[0] =
1876 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001877 /* Increase dwell time to receive probe response or detect
1878 * beacon from target AP at a noisy air only during connect
1879 * command.
1880 */
1881 ext_join_params->scan_le.active_time =
1882 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1883 ext_join_params->scan_le.passive_time =
1884 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1885 /* To sync with presence period of VSDB GO send probe request
1886 * more frequently. Probe request will be stopped when it gets
1887 * probe response from target AP/GO.
1888 */
1889 ext_join_params->scan_le.nprobes =
1890 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1891 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1892 } else {
1893 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1894 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1895 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001896 }
1897
1898 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1899 join_params_size);
1900 kfree(ext_join_params);
1901 if (!err)
1902 /* This is it. join command worked, we are done */
1903 goto done;
1904
1905 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001906 memset(&join_params, 0, sizeof(join_params));
1907 join_params_size = sizeof(join_params.ssid_le);
1908
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001909 memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
1910 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001911
Hante Meuleman89286dc2013-02-08 15:53:46 +01001912 if (sme->bssid)
1913 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1914 else
Joe Perches93803b32015-03-02 19:54:49 -08001915 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001916
Hante Meuleman17012612013-02-06 18:40:44 +01001917 if (cfg->channel) {
1918 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1919 join_params.params_le.chanspec_num = cpu_to_le32(1);
1920 join_params_size += sizeof(join_params.params_le);
1921 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001922 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001923 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001924 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001925 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001926
1927done:
1928 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001929 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001930 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001931 return err;
1932}
1933
1934static s32
1935brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1936 u16 reason_code)
1937{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001938 struct brcmf_if *ifp = netdev_priv(ndev);
1939 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001940 struct brcmf_scb_val_le scbval;
1941 s32 err = 0;
1942
Arend van Sprield96b8012012-12-05 15:26:02 +01001943 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001944 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001945 return -EIO;
1946
Arend van Sprielc1179032012-10-22 13:55:33 -07001947 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001948 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001949 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001950
Arend van Spriel06bb1232012-09-27 14:17:56 +02001951 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001952 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001953 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001954 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001955 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001956 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001957
Arend van Sprield96b8012012-12-05 15:26:02 +01001958 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001959 return err;
1960}
1961
1962static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001963brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001964 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001965{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001966 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001967 struct net_device *ndev = cfg_to_ndev(cfg);
1968 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001969 s32 err;
1970 s32 disable;
1971 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001972
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001973 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07001974 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001975 return -EIO;
1976
1977 switch (type) {
1978 case NL80211_TX_POWER_AUTOMATIC:
1979 break;
1980 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001981 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001982 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001983 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001984 err = -EINVAL;
1985 goto done;
1986 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001987 qdbm = MBM_TO_DBM(4 * mbm);
1988 if (qdbm > 127)
1989 qdbm = 127;
1990 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001991 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001992 default:
1993 brcmf_err("Unsupported type %d\n", type);
1994 err = -EINVAL;
1995 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001996 }
1997 /* Make sure radio is off or on as far as software is concerned */
1998 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001999 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002000 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002001 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002002
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002003 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002004 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002005 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002006
2007done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002008 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002009 return err;
2010}
2011
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002012static s32
2013brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2014 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002015{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002016 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002017 struct net_device *ndev = cfg_to_ndev(cfg);
2018 struct brcmf_if *ifp = netdev_priv(ndev);
2019 s32 qdbm = 0;
2020 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002021
Arend van Sprield96b8012012-12-05 15:26:02 +01002022 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002023 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002024 return -EIO;
2025
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002026 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002027 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002028 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002029 goto done;
2030 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002031 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002032
2033done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002034 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002035 return err;
2036}
2037
2038static s32
2039brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002040 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002041{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002042 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002043 u32 index;
2044 u32 wsec;
2045 s32 err = 0;
2046
Arend van Sprield96b8012012-12-05 15:26:02 +01002047 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002048 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002049 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002050 return -EIO;
2051
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002052 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002053 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002054 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002055 goto done;
2056 }
2057
2058 if (wsec & WEP_ENABLED) {
2059 /* Just select a new current key */
2060 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002061 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002062 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002063 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002064 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002065 }
2066done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002067 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002068 return err;
2069}
2070
2071static s32
2072brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2073 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2074{
Hante Meuleman992f6062013-04-02 21:06:17 +02002075 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002076 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002077 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002078 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002079
2080 memset(&key, 0, sizeof(key));
2081 key.index = (u32) key_idx;
2082 /* Instead of bcast for ea address for default wep keys,
2083 driver needs it to be Null */
2084 if (!is_multicast_ether_addr(mac_addr))
2085 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2086 key.len = (u32) params->key_len;
2087 /* check for key index change */
2088 if (key.len == 0) {
2089 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002090 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002091 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002092 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002093 } else {
2094 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002095 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002096 return -EINVAL;
2097 }
2098
Arend van Spriel16886732012-12-05 15:26:04 +01002099 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002100 memcpy(key.data, params->key, key.len);
2101
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002102 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002103 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2104 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002105 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2106 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2107 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2108 }
2109
2110 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2111 if (params->seq && params->seq_len == 6) {
2112 /* rx iv */
2113 u8 *ivptr;
2114 ivptr = (u8 *) params->seq;
2115 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2116 (ivptr[3] << 8) | ivptr[2];
2117 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2118 key.iv_initialized = true;
2119 }
2120
2121 switch (params->cipher) {
2122 case WLAN_CIPHER_SUITE_WEP40:
2123 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002124 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002125 break;
2126 case WLAN_CIPHER_SUITE_WEP104:
2127 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002128 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002129 break;
2130 case WLAN_CIPHER_SUITE_TKIP:
2131 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002132 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002133 break;
2134 case WLAN_CIPHER_SUITE_AES_CMAC:
2135 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002136 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002137 break;
2138 case WLAN_CIPHER_SUITE_CCMP:
2139 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002140 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002141 break;
2142 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002143 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002144 return -EINVAL;
2145 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002146 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002147 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002148 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002149 }
2150 return err;
2151}
2152
2153static s32
2154brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2155 u8 key_idx, bool pairwise, const u8 *mac_addr,
2156 struct key_params *params)
2157{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002158 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002159 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002160 s32 val;
2161 s32 wsec;
2162 s32 err = 0;
2163 u8 keybuf[8];
2164
Arend van Sprield96b8012012-12-05 15:26:02 +01002165 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002166 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002167 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002168 return -EIO;
2169
Hante Meuleman118eb302014-12-21 12:43:49 +01002170 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2171 /* we ignore this key index in this case */
2172 brcmf_err("invalid key index (%d)\n", key_idx);
2173 return -EINVAL;
2174 }
2175
Daniel Kim787eb032014-01-29 15:32:23 +01002176 if (mac_addr &&
2177 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2178 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002179 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002180 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2181 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002182
Hante Meuleman118eb302014-12-21 12:43:49 +01002183 key = &ifp->vif->profile.key[key_idx];
2184 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002185
Hante Meuleman118eb302014-12-21 12:43:49 +01002186 if (params->key_len > sizeof(key->data)) {
2187 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002188 err = -EINVAL;
2189 goto done;
2190 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002191 key->len = params->key_len;
2192 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002193
Hante Meuleman118eb302014-12-21 12:43:49 +01002194 memcpy(key->data, params->key, key->len);
2195
2196 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002197 switch (params->cipher) {
2198 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002199 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002200 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002201 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002202 break;
2203 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002204 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002205 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002206 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002207 break;
2208 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002209 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002210 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002211 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2212 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2213 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002214 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002215 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002216 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002217 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002218 break;
2219 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002220 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002221 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002222 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002223 break;
2224 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002225 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002226 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002227 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002228 break;
2229 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002230 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002231 err = -EINVAL;
2232 goto done;
2233 }
2234
Hante Meuleman118eb302014-12-21 12:43:49 +01002235 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236 if (err)
2237 goto done;
2238
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002239 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002240 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002241 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002242 goto done;
2243 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002244 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002245 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002246 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002247 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002248 goto done;
2249 }
2250
Arend van Spriel5b435de2011-10-05 13:19:03 +02002251done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002252 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002253 return err;
2254}
2255
2256static s32
2257brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2258 u8 key_idx, bool pairwise, const u8 *mac_addr)
2259{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002260 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002261 struct brcmf_wsec_key key;
2262 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002263
Arend van Sprield96b8012012-12-05 15:26:02 +01002264 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002265 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002266 return -EIO;
2267
Hante Meuleman118eb302014-12-21 12:43:49 +01002268 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002269 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002270 return -EINVAL;
2271 }
2272
Arend van Spriel5b435de2011-10-05 13:19:03 +02002273 memset(&key, 0, sizeof(key));
2274
2275 key.index = (u32) key_idx;
2276 key.flags = BRCMF_PRIMARY_KEY;
2277 key.algo = CRYPTO_ALGO_OFF;
2278
Arend van Spriel16886732012-12-05 15:26:04 +01002279 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002280
2281 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002282 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002283
Arend van Sprield96b8012012-12-05 15:26:02 +01002284 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002285 return err;
2286}
2287
2288static s32
2289brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2290 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2291 void (*callback) (void *cookie, struct key_params * params))
2292{
2293 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002294 struct brcmf_if *ifp = netdev_priv(ndev);
2295 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002296 struct brcmf_cfg80211_security *sec;
2297 s32 wsec;
2298 s32 err = 0;
2299
Arend van Sprield96b8012012-12-05 15:26:02 +01002300 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002301 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002302 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002303 return -EIO;
2304
2305 memset(&params, 0, sizeof(params));
2306
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002307 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002308 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002309 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002310 /* Ignore this error, may happen during DISASSOC */
2311 err = -EAGAIN;
2312 goto done;
2313 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002314 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002315 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002316 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2317 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002318 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002319 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2320 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002321 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002322 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002323 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002324 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002325 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002326 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002327 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002328 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002329 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002330 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002331 err = -EINVAL;
2332 goto done;
2333 }
2334 callback(cookie, &params);
2335
2336done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002337 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002338 return err;
2339}
2340
2341static s32
2342brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2343 struct net_device *ndev, u8 key_idx)
2344{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002345 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002346
2347 return -EOPNOTSUPP;
2348}
2349
Hante Meuleman118eb302014-12-21 12:43:49 +01002350static void
2351brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2352{
2353 s32 err;
2354 u8 key_idx;
2355 struct brcmf_wsec_key *key;
2356 s32 wsec;
2357
2358 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2359 key = &ifp->vif->profile.key[key_idx];
2360 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2361 (key->algo == CRYPTO_ALGO_WEP128))
2362 break;
2363 }
2364 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2365 return;
2366
2367 err = send_key_to_dongle(ifp, key);
2368 if (err) {
2369 brcmf_err("Setting WEP key failed (%d)\n", err);
2370 return;
2371 }
2372 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2373 if (err) {
2374 brcmf_err("get wsec error (%d)\n", err);
2375 return;
2376 }
2377 wsec |= WEP_ENABLED;
2378 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2379 if (err)
2380 brcmf_err("set wsec error (%d)\n", err);
2381}
2382
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002383static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2384{
2385 struct nl80211_sta_flag_update *sfu;
2386
2387 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2388 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2389 sfu = &si->sta_flags;
2390 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2391 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2392 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2393 BIT(NL80211_STA_FLAG_AUTHORIZED);
2394 if (fw_sta_flags & BRCMF_STA_WME)
2395 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2396 if (fw_sta_flags & BRCMF_STA_AUTHE)
2397 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2398 if (fw_sta_flags & BRCMF_STA_ASSOC)
2399 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2400 if (fw_sta_flags & BRCMF_STA_AUTHO)
2401 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2402}
2403
2404static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2405{
2406 struct {
2407 __le32 len;
2408 struct brcmf_bss_info_le bss_le;
2409 } *buf;
2410 u16 capability;
2411 int err;
2412
2413 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2414 if (!buf)
2415 return;
2416
2417 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2418 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2419 WL_BSS_INFO_MAX);
2420 if (err) {
2421 brcmf_err("Failed to get bss info (%d)\n", err);
2422 return;
2423 }
2424 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2425 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2426 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2427 capability = le16_to_cpu(buf->bss_le.capability);
2428 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2429 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2430 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2431 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2432 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2433 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2434}
2435
Arend van Spriel5b435de2011-10-05 13:19:03 +02002436static s32
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002437brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
2438 struct station_info *sinfo)
2439{
2440 struct brcmf_scb_val_le scbval;
2441 struct brcmf_pktcnt_le pktcnt;
2442 s32 err;
2443 u32 rate;
2444 u32 rssi;
2445
2446 /* Get the current tx rate */
2447 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
2448 if (err < 0) {
2449 brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
2450 return err;
2451 }
2452 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
2453 sinfo->txrate.legacy = rate * 5;
2454
2455 memset(&scbval, 0, sizeof(scbval));
2456 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
2457 sizeof(scbval));
2458 if (err) {
2459 brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
2460 return err;
2461 }
2462 rssi = le32_to_cpu(scbval.val);
2463 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2464 sinfo->signal = rssi;
2465
2466 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
2467 sizeof(pktcnt));
2468 if (err) {
2469 brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
2470 return err;
2471 }
2472 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
2473 BIT(NL80211_STA_INFO_RX_DROP_MISC) |
2474 BIT(NL80211_STA_INFO_TX_PACKETS) |
2475 BIT(NL80211_STA_INFO_TX_FAILED);
2476 sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
2477 sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
2478 sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
2479 sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
2480
2481 return 0;
2482}
2483
2484static s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002485brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002486 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002487{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002488 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002489 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002490 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002491 u32 sta_flags;
2492 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002493 s32 total_rssi;
2494 s32 count_rssi;
2495 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002496
Arend van Sprield96b8012012-12-05 15:26:02 +01002497 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002498 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002499 return -EIO;
2500
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002501 if (brcmf_is_ibssmode(ifp->vif))
2502 return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
2503
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002504 memset(&sta_info_le, 0, sizeof(sta_info_le));
2505 memcpy(&sta_info_le, mac, ETH_ALEN);
2506 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2507 &sta_info_le,
2508 sizeof(sta_info_le));
2509 is_tdls_peer = !err;
2510 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002511 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002512 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002513 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002514 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002515 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002516 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002517 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002518 }
2519 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2520 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2521 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2522 sta_flags = le32_to_cpu(sta_info_le.flags);
2523 brcmf_convert_sta_flags(sta_flags, sinfo);
2524 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2525 if (is_tdls_peer)
2526 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2527 else
2528 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2529 if (sta_flags & BRCMF_STA_ASSOC) {
2530 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2531 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2532 brcmf_fill_bss_param(ifp, sinfo);
2533 }
2534 if (sta_flags & BRCMF_STA_SCBSTATS) {
2535 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2536 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2537 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2538 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2539 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2540 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2541 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2542 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2543 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002544 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002545 sinfo->txrate.legacy =
2546 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002547 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002548 if (sinfo->rx_packets) {
2549 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002550 sinfo->rxrate.legacy =
2551 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002552 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002553 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2554 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2555 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2556 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2557 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2558 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002559 total_rssi = 0;
2560 count_rssi = 0;
2561 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2562 if (sta_info_le.rssi[i]) {
2563 sinfo->chain_signal_avg[count_rssi] =
2564 sta_info_le.rssi[i];
2565 sinfo->chain_signal[count_rssi] =
2566 sta_info_le.rssi[i];
2567 total_rssi += sta_info_le.rssi[i];
2568 count_rssi++;
2569 }
2570 }
2571 if (count_rssi) {
2572 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2573 sinfo->chains = count_rssi;
2574
2575 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2576 total_rssi /= count_rssi;
2577 sinfo->signal = total_rssi;
2578 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002579 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002580done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002581 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002582 return err;
2583}
2584
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002585static int
2586brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2587 int idx, u8 *mac, struct station_info *sinfo)
2588{
2589 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2590 struct brcmf_if *ifp = netdev_priv(ndev);
2591 s32 err;
2592
2593 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2594
2595 if (idx == 0) {
2596 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2597 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2598 &cfg->assoclist,
2599 sizeof(cfg->assoclist));
2600 if (err) {
2601 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2602 err);
2603 cfg->assoclist.count = 0;
2604 return -EOPNOTSUPP;
2605 }
2606 }
2607 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2608 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2609 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2610 }
2611 return -ENOENT;
2612}
2613
Arend van Spriel5b435de2011-10-05 13:19:03 +02002614static s32
2615brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2616 bool enabled, s32 timeout)
2617{
2618 s32 pm;
2619 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002620 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002621 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002622
Arend van Sprield96b8012012-12-05 15:26:02 +01002623 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002624
2625 /*
2626 * Powersave enable/disable request is coming from the
2627 * cfg80211 even before the interface is up. In that
2628 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002629 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002630 * FW later while initializing the dongle
2631 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002632 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002633 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002634
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002635 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002636 goto done;
2637 }
2638
2639 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002640 /* Do not enable the power save after assoc if it is a p2p interface */
2641 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2642 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2643 pm = PM_OFF;
2644 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002645 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002646
Arend van Sprielc1179032012-10-22 13:55:33 -07002647 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002648 if (err) {
2649 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002650 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002651 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002652 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002653 }
2654done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002655 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002656 return err;
2657}
2658
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002659static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002660 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002661{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002662 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002663 struct ieee80211_channel *notify_channel;
2664 struct cfg80211_bss *bss;
2665 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002666 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002667 u16 channel;
2668 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002669 u16 notify_capability;
2670 u16 notify_interval;
2671 u8 *notify_ie;
2672 size_t notify_ielen;
2673 s32 notify_signal;
2674
2675 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002676 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002677 return 0;
2678 }
2679
Franky Lin83cf17a2013-04-11 13:28:50 +02002680 if (!bi->ctl_ch) {
2681 ch.chspec = le16_to_cpu(bi->chanspec);
2682 cfg->d11inf.decchspec(&ch);
2683 bi->ctl_ch = ch.chnum;
2684 }
2685 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002686
2687 if (channel <= CH_MAX_2G_CHANNEL)
2688 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2689 else
2690 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2691
2692 freq = ieee80211_channel_to_frequency(channel, band->band);
2693 notify_channel = ieee80211_get_channel(wiphy, freq);
2694
Arend van Spriel5b435de2011-10-05 13:19:03 +02002695 notify_capability = le16_to_cpu(bi->capability);
2696 notify_interval = le16_to_cpu(bi->beacon_period);
2697 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2698 notify_ielen = le32_to_cpu(bi->ie_length);
2699 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2700
Arend van Spriel16886732012-12-05 15:26:04 +01002701 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2702 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2703 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2704 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2705 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002706
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002707 bss = cfg80211_inform_bss(wiphy, notify_channel,
2708 CFG80211_BSS_FTYPE_UNKNOWN,
2709 (const u8 *)bi->BSSID,
2710 0, notify_capability,
2711 notify_interval, notify_ie,
2712 notify_ielen, notify_signal,
2713 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002714
Franky Line78946e2011-11-10 20:30:34 +01002715 if (!bss)
2716 return -ENOMEM;
2717
Johannes Berg5b112d32013-02-01 01:49:58 +01002718 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002719
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002720 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002721}
2722
Roland Vossen6f09be02011-10-18 14:03:02 +02002723static struct brcmf_bss_info_le *
2724next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2725{
2726 if (bss == NULL)
2727 return list->bss_info_le;
2728 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2729 le32_to_cpu(bss->length));
2730}
2731
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002732static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002733{
2734 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002735 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002736 s32 err = 0;
2737 int i;
2738
Hante Meulemanef8596e2014-09-30 10:23:13 +02002739 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002740 if (bss_list->count != 0 &&
2741 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002742 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2743 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002744 return -EOPNOTSUPP;
2745 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002746 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002747 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002748 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002749 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002750 if (err)
2751 break;
2752 }
2753 return err;
2754}
2755
Hante Meulemanb0a79082015-12-10 13:43:07 +01002756static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
2757 struct net_device *ndev, const u8 *bssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002758{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002759 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002760 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002761 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002762 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002763 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002764 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002765 u8 *buf = NULL;
2766 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002767 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002768 u16 notify_capability;
2769 u16 notify_interval;
2770 u8 *notify_ie;
2771 size_t notify_ielen;
2772 s32 notify_signal;
2773
Arend van Sprield96b8012012-12-05 15:26:02 +01002774 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002775
2776 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2777 if (buf == NULL) {
2778 err = -ENOMEM;
2779 goto CleanUp;
2780 }
2781
2782 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2783
Arend van Sprielac24be62012-10-22 10:36:23 -07002784 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2785 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002786 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002787 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002788 goto CleanUp;
2789 }
2790
Roland Vossend34bf642011-10-18 14:03:01 +02002791 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002792
Franky Lin83cf17a2013-04-11 13:28:50 +02002793 ch.chspec = le16_to_cpu(bi->chanspec);
2794 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002795
Franky Lin83cf17a2013-04-11 13:28:50 +02002796 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002797 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2798 else
2799 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2800
Franky Lin83cf17a2013-04-11 13:28:50 +02002801 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Hante Meulemanb0a79082015-12-10 13:43:07 +01002802 cfg->channel = freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002803 notify_channel = ieee80211_get_channel(wiphy, freq);
2804
Arend van Spriel5b435de2011-10-05 13:19:03 +02002805 notify_capability = le16_to_cpu(bi->capability);
2806 notify_interval = le16_to_cpu(bi->beacon_period);
2807 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2808 notify_ielen = le32_to_cpu(bi->ie_length);
2809 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2810
Franky Lin83cf17a2013-04-11 13:28:50 +02002811 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002812 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2813 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2814 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002815
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002816 bss = cfg80211_inform_bss(wiphy, notify_channel,
2817 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2818 notify_capability, notify_interval,
2819 notify_ie, notify_ielen, notify_signal,
2820 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002821
Franky Line78946e2011-11-10 20:30:34 +01002822 if (!bss) {
2823 err = -ENOMEM;
2824 goto CleanUp;
2825 }
2826
Johannes Berg5b112d32013-02-01 01:49:58 +01002827 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002828
Arend van Spriel5b435de2011-10-05 13:19:03 +02002829CleanUp:
2830
2831 kfree(buf);
2832
Arend van Sprield96b8012012-12-05 15:26:02 +01002833 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002834
2835 return err;
2836}
2837
Hante Meuleman89286dc2013-02-08 15:53:46 +01002838static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2839 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002840{
Roland Vossend34bf642011-10-18 14:03:01 +02002841 struct brcmf_bss_info_le *bi;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002842 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002843 u16 beacon_interval;
2844 u8 dtim_period;
2845 size_t ie_len;
2846 u8 *ie;
2847 s32 err = 0;
2848
Arend van Sprield96b8012012-12-05 15:26:02 +01002849 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002850 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002851 return err;
2852
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002853 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002854 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002855 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002856 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002857 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002858 goto update_bss_info_out;
2859 }
2860
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002861 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2862 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002863 if (err)
2864 goto update_bss_info_out;
2865
2866 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2867 ie_len = le32_to_cpu(bi->ie_length);
2868 beacon_interval = le16_to_cpu(bi->beacon_period);
2869
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002870 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002871 if (tim)
2872 dtim_period = tim->data[1];
2873 else {
2874 /*
2875 * active scan was done so we could not get dtim
2876 * information out of probe response.
2877 * so we speficially query dtim information to dongle.
2878 */
2879 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002880 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002881 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002882 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002883 goto update_bss_info_out;
2884 }
2885 dtim_period = (u8)var;
2886 }
2887
Arend van Spriel5b435de2011-10-05 13:19:03 +02002888update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002889 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002890 return err;
2891}
2892
Hante Meuleman18e2f612013-02-08 15:53:49 +01002893void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002894{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002895 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002896
Arend van Sprielc1179032012-10-22 13:55:33 -07002897 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002898 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002899 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002900 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002901 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002902 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2903 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002904}
2905
Hante Meulemane756af52012-09-11 21:18:52 +02002906static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2907{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002908 struct brcmf_cfg80211_info *cfg =
2909 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002910 escan_timeout_work);
2911
Hante Meulemanef8596e2014-09-30 10:23:13 +02002912 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002913 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002914}
2915
2916static void brcmf_escan_timeout(unsigned long data)
2917{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002918 struct brcmf_cfg80211_info *cfg =
2919 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002920
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002921 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002922 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002923 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002924 }
2925}
2926
2927static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002928brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2929 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002930 struct brcmf_bss_info_le *bss_info_le)
2931{
Franky Lin83cf17a2013-04-11 13:28:50 +02002932 struct brcmu_chan ch_bss, ch_bss_info_le;
2933
2934 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2935 cfg->d11inf.decchspec(&ch_bss);
2936 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2937 cfg->d11inf.decchspec(&ch_bss_info_le);
2938
Hante Meulemane756af52012-09-11 21:18:52 +02002939 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002940 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002941 bss_info_le->SSID_len == bss->SSID_len &&
2942 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002943 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2944 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002945 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2946 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2947
Hante Meulemane756af52012-09-11 21:18:52 +02002948 /* preserve max RSSI if the measurements are
2949 * both on-channel or both off-channel
2950 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002951 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002952 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002953 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2954 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002955 /* preserve the on-channel rssi measurement
2956 * if the new measurement is off channel
2957 */
2958 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002959 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002960 }
2961 return 1;
2962 }
2963 return 0;
2964}
2965
2966static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002967brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002968 const struct brcmf_event_msg *e, void *data)
2969{
Arend van Spriel19937322012-11-05 16:22:32 -08002970 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002971 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002972 struct brcmf_escan_result_le *escan_result_le;
2973 struct brcmf_bss_info_le *bss_info_le;
2974 struct brcmf_bss_info_le *bss = NULL;
2975 u32 bi_length;
2976 struct brcmf_scan_results *list;
2977 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002978 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002979
Arend van Spriel5c36b992012-11-14 18:46:05 -08002980 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002981
Arend van Spriela0f472a2013-04-05 10:57:49 +02002982 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meuleman37a869e2015-10-29 20:33:17 +01002983 brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002984 return -EPERM;
2985 }
2986
2987 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002988 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002989 escan_result_le = (struct brcmf_escan_result_le *) data;
2990 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002991 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002992 goto exit;
2993 }
Hante Meulemane756af52012-09-11 21:18:52 +02002994 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002995 brcmf_err("Invalid bss_count %d: ignoring\n",
2996 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002997 goto exit;
2998 }
2999 bss_info_le = &escan_result_le->bss_info_le;
3000
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003001 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
3002 goto exit;
3003
3004 if (!cfg->scan_request) {
3005 brcmf_dbg(SCAN, "result without cfg80211 request\n");
3006 goto exit;
3007 }
3008
Hante Meulemane756af52012-09-11 21:18:52 +02003009 bi_length = le32_to_cpu(bss_info_le->length);
3010 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
3011 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003012 brcmf_err("Invalid bss_info length %d: ignoring\n",
3013 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02003014 goto exit;
3015 }
3016
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003017 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02003018 BIT(NL80211_IFTYPE_ADHOC))) {
3019 if (le16_to_cpu(bss_info_le->capability) &
3020 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003021 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003022 goto exit;
3023 }
3024 }
3025
3026 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003027 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02003028 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003029 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003030 goto exit;
3031 }
3032
3033 for (i = 0; i < list->count; i++) {
3034 bss = bss ? (struct brcmf_bss_info_le *)
3035 ((unsigned char *)bss +
3036 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02003037 if (brcmf_compare_update_same_bss(cfg, bss,
3038 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02003039 goto exit;
3040 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003041 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02003042 bss_info_le, bi_length);
3043 list->version = le32_to_cpu(bss_info_le->version);
3044 list->buflen += bi_length;
3045 list->count++;
3046 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003047 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003048 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
3049 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003050 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003051 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003052 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02003053 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02003054 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003055 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
3056 status);
Hante Meulemane756af52012-09-11 21:18:52 +02003057 }
3058exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003059 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02003060}
3061
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003062static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003063{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003064 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3065 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003066 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3067 /* Init scan_timeout timer */
3068 init_timer(&cfg->escan_timeout);
3069 cfg->escan_timeout.data = (unsigned long) cfg;
3070 cfg->escan_timeout.function = brcmf_escan_timeout;
3071 INIT_WORK(&cfg->escan_timeout_work,
3072 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003073}
3074
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003075static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003076{
3077 if (ms < 1000 / HZ) {
3078 cond_resched();
3079 mdelay(ms);
3080 } else {
3081 msleep(ms);
3082 }
3083}
3084
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003085static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3086 u8 *pattern, u32 patternsize, u8 *mask,
3087 u32 packet_offset)
3088{
3089 struct brcmf_fil_wowl_pattern_le *filter;
3090 u32 masksize;
3091 u32 patternoffset;
3092 u8 *buf;
3093 u32 bufsize;
3094 s32 ret;
3095
3096 masksize = (patternsize + 7) / 8;
3097 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3098
3099 bufsize = sizeof(*filter) + patternsize + masksize;
3100 buf = kzalloc(bufsize, GFP_KERNEL);
3101 if (!buf)
3102 return -ENOMEM;
3103 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3104
3105 memcpy(filter->cmd, cmd, 4);
3106 filter->masksize = cpu_to_le32(masksize);
3107 filter->offset = cpu_to_le32(packet_offset);
3108 filter->patternoffset = cpu_to_le32(patternoffset);
3109 filter->patternsize = cpu_to_le32(patternsize);
3110 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3111
3112 if ((mask) && (masksize))
3113 memcpy(buf + sizeof(*filter), mask, masksize);
3114 if ((pattern) && (patternsize))
3115 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3116
3117 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3118
3119 kfree(buf);
3120 return ret;
3121}
3122
Hante Meulemanaeb64222015-10-29 20:33:19 +01003123#ifdef CONFIG_PM
3124
3125static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3126{
3127 struct brcmf_wowl_wakeind_le wake_ind_le;
3128 struct cfg80211_wowlan_wakeup wakeup_data;
3129 struct cfg80211_wowlan_wakeup *wakeup;
3130 u32 wakeind;
3131 s32 err;
3132
3133 err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
3134 sizeof(wake_ind_le));
3135 if (!err) {
3136 brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
3137 return;
3138 }
3139
3140 wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
3141 if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
3142 BRCMF_WOWL_RETR | BRCMF_WOWL_NET)) {
3143 wakeup = &wakeup_data;
3144 memset(&wakeup_data, 0, sizeof(wakeup_data));
3145 wakeup_data.pattern_idx = -1;
3146
3147 if (wakeind & BRCMF_WOWL_MAGIC) {
3148 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
3149 wakeup_data.magic_pkt = true;
3150 }
3151 if (wakeind & BRCMF_WOWL_DIS) {
3152 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
3153 wakeup_data.disconnect = true;
3154 }
3155 if (wakeind & BRCMF_WOWL_BCN) {
3156 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
3157 wakeup_data.disconnect = true;
3158 }
3159 if (wakeind & BRCMF_WOWL_RETR) {
3160 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
3161 wakeup_data.disconnect = true;
3162 }
3163 if (wakeind & BRCMF_WOWL_NET) {
3164 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
3165 /* For now always map to pattern 0, no API to get
3166 * correct information available at the moment.
3167 */
3168 wakeup_data.pattern_idx = 0;
3169 }
3170 } else {
3171 wakeup = NULL;
3172 }
3173 cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
3174}
3175
3176#else
3177
3178static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3179{
3180}
3181
3182#endif /* CONFIG_PM */
3183
Arend van Spriel5b435de2011-10-05 13:19:03 +02003184static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3185{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003186 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3187 struct net_device *ndev = cfg_to_ndev(cfg);
3188 struct brcmf_if *ifp = netdev_priv(ndev);
3189
Arend van Sprield96b8012012-12-05 15:26:02 +01003190 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003191
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003192 if (cfg->wowl_enabled) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003193 brcmf_report_wowl_wakeind(wiphy, ifp);
3194 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
3195 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003196 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003197 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3198 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003199 cfg->wowl_enabled = false;
3200 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003201 return 0;
3202}
3203
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003204static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3205 struct brcmf_if *ifp,
3206 struct cfg80211_wowlan *wowl)
3207{
3208 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003209 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003210
3211 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3212
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003213 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003214 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3215 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3216
3217 wowl_config = 0;
3218 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003219 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003220 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003221 wowl_config |= BRCMF_WOWL_MAGIC;
3222 if ((wowl->patterns) && (wowl->n_patterns)) {
3223 wowl_config |= BRCMF_WOWL_NET;
3224 for (i = 0; i < wowl->n_patterns; i++) {
3225 brcmf_config_wowl_pattern(ifp, "add",
3226 (u8 *)wowl->patterns[i].pattern,
3227 wowl->patterns[i].pattern_len,
3228 (u8 *)wowl->patterns[i].mask,
3229 wowl->patterns[i].pkt_offset);
3230 }
3231 }
Hante Meulemanaeb64222015-10-29 20:33:19 +01003232 brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear"));
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003233 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3234 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3235 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3236 cfg->wowl_enabled = true;
3237}
3238
Arend van Spriel5b435de2011-10-05 13:19:03 +02003239static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003240 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003241{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003242 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3243 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003244 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003245 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003246
Arend van Sprield96b8012012-12-05 15:26:02 +01003247 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003248
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003249 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003250 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003251 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003252 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003253 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003254
Arend van Spriel7d641072012-10-22 13:55:39 -07003255 /* end any scanning */
3256 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003257 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003258
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003259 if (wowl == NULL) {
3260 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3261 list_for_each_entry(vif, &cfg->vif_list, list) {
3262 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3263 continue;
3264 /* While going to suspend if associated with AP
3265 * disassociate from AP to save power while system is
3266 * in suspended state
3267 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003268 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003269 /* Make sure WPA_Supplicant receives all the event
3270 * generated due to DISASSOC call to the fw to keep
3271 * the state fw and WPA_Supplicant state consistent
3272 */
3273 brcmf_delay(500);
3274 }
3275 /* Configure MPC */
3276 brcmf_set_mpc(ifp, 1);
3277
3278 } else {
3279 /* Configure WOWL paramaters */
3280 brcmf_configure_wowl(cfg, ifp, wowl);
3281 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003282
Arend van Spriel7d641072012-10-22 13:55:39 -07003283exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003284 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003285 /* clear any scanning activity */
3286 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003287 return 0;
3288}
3289
3290static __used s32
Hante Meuleman6c404f32015-12-10 13:43:03 +01003291brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003292{
Hante Meuleman6c404f32015-12-10 13:43:03 +01003293 struct brcmf_pmk_list_le *pmk_list;
3294 int i;
3295 u32 npmk;
3296 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003297
Hante Meuleman6c404f32015-12-10 13:43:03 +01003298 pmk_list = &cfg->pmk_list;
3299 npmk = le32_to_cpu(pmk_list->npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003300
Hante Meuleman6c404f32015-12-10 13:43:03 +01003301 brcmf_dbg(CONN, "No of elements %d\n", npmk);
3302 for (i = 0; i < npmk; i++)
3303 brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003304
Hante Meuleman6c404f32015-12-10 13:43:03 +01003305 err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
3306 sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003307
3308 return err;
3309}
3310
3311static s32
3312brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3313 struct cfg80211_pmksa *pmksa)
3314{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003315 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003316 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003317 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3318 s32 err;
3319 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003320
Arend van Sprield96b8012012-12-05 15:26:02 +01003321 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003322 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003323 return -EIO;
3324
Hante Meuleman6c404f32015-12-10 13:43:03 +01003325 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3326 for (i = 0; i < npmk; i++)
3327 if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003328 break;
Hante Meuleman6c404f32015-12-10 13:43:03 +01003329 if (i < BRCMF_MAXPMKID) {
3330 memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
3331 memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
3332 if (i == npmk) {
3333 npmk++;
3334 cfg->pmk_list.npmk = cpu_to_le32(npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003335 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003336 } else {
3337 brcmf_err("Too many PMKSA entries cached %d\n", npmk);
3338 return -EINVAL;
3339 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003340
Hante Meuleman6c404f32015-12-10 13:43:03 +01003341 brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
3342 for (i = 0; i < WLAN_PMKID_LEN; i += 4)
3343 brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
3344 pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
3345 pmk[npmk].pmkid[i + 3]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003346
Hante Meuleman6c404f32015-12-10 13:43:03 +01003347 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003348
Arend van Sprield96b8012012-12-05 15:26:02 +01003349 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003350 return err;
3351}
3352
3353static s32
3354brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman6c404f32015-12-10 13:43:03 +01003355 struct cfg80211_pmksa *pmksa)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003356{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003357 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003358 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003359 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3360 s32 err;
3361 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003362
Arend van Sprield96b8012012-12-05 15:26:02 +01003363 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003364 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003365 return -EIO;
3366
Hante Meuleman6c404f32015-12-10 13:43:03 +01003367 brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003368
Hante Meuleman6c404f32015-12-10 13:43:03 +01003369 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3370 for (i = 0; i < npmk; i++)
3371 if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003372 break;
3373
Hante Meuleman6c404f32015-12-10 13:43:03 +01003374 if ((npmk > 0) && (i < npmk)) {
3375 for (; i < (npmk - 1); i++) {
3376 memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
3377 memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003378 WLAN_PMKID_LEN);
3379 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003380 memset(&pmk[i], 0, sizeof(*pmk));
3381 cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
3382 } else {
3383 brcmf_err("Cache entry not found\n");
3384 return -EINVAL;
3385 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003386
Hante Meuleman6c404f32015-12-10 13:43:03 +01003387 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003388
Arend van Sprield96b8012012-12-05 15:26:02 +01003389 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003390 return err;
3391
3392}
3393
3394static s32
3395brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3396{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003397 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003398 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003399 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003400
Arend van Sprield96b8012012-12-05 15:26:02 +01003401 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003402 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003403 return -EIO;
3404
Hante Meuleman6c404f32015-12-10 13:43:03 +01003405 memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
3406 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003407
Arend van Sprield96b8012012-12-05 15:26:02 +01003408 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003409 return err;
3410
3411}
3412
Arend van Spriele5806072012-09-19 22:21:08 +02003413/*
3414 * PFN result doesn't have all the info which are
3415 * required by the supplicant
3416 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3417 * via wl_inform_single_bss in the required format. Escan does require the
3418 * scan request in the form of cfg80211_scan_request. For timebeing, create
3419 * cfg80211_scan_request one out of the received PNO event.
3420 */
3421static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003422brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003423 const struct brcmf_event_msg *e, void *data)
3424{
Arend van Spriel19937322012-11-05 16:22:32 -08003425 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003426 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3427 struct cfg80211_scan_request *request = NULL;
3428 struct cfg80211_ssid *ssid = NULL;
3429 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003430 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003431 int err = 0;
3432 int channel_req = 0;
3433 int band = 0;
3434 struct brcmf_pno_scanresults_le *pfn_result;
3435 u32 result_count;
3436 u32 status;
3437
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003438 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003439
Arend van Spriel5c36b992012-11-14 18:46:05 -08003440 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003441 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003442 return 0;
3443 }
3444
3445 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3446 result_count = le32_to_cpu(pfn_result->count);
3447 status = le32_to_cpu(pfn_result->status);
3448
3449 /*
3450 * PFN event is limited to fit 512 bytes so we may get
3451 * multiple NET_FOUND events. For now place a warning here.
3452 */
3453 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003454 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003455 if (result_count > 0) {
3456 int i;
3457
3458 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003459 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3460 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003461 if (!request || !ssid || !channel) {
3462 err = -ENOMEM;
3463 goto out_err;
3464 }
3465
3466 request->wiphy = wiphy;
3467 data += sizeof(struct brcmf_pno_scanresults_le);
3468 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3469
3470 for (i = 0; i < result_count; i++) {
3471 netinfo = &netinfo_start[i];
3472 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003473 brcmf_err("Invalid netinfo ptr. index: %d\n",
3474 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003475 err = -EINVAL;
3476 goto out_err;
3477 }
3478
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003479 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3480 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003481 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3482 ssid[i].ssid_len = netinfo->SSID_len;
3483 request->n_ssids++;
3484
3485 channel_req = netinfo->channel;
3486 if (channel_req <= CH_MAX_2G_CHANNEL)
3487 band = NL80211_BAND_2GHZ;
3488 else
3489 band = NL80211_BAND_5GHZ;
3490 channel[i].center_freq =
3491 ieee80211_channel_to_frequency(channel_req,
3492 band);
3493 channel[i].band = band;
3494 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3495 request->channels[i] = &channel[i];
3496 request->n_channels++;
3497 }
3498
3499 /* assign parsed ssid array */
3500 if (request->n_ssids)
3501 request->ssids = &ssid[0];
3502
Arend van Sprielc1179032012-10-22 13:55:33 -07003503 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003504 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003505 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003506 }
3507
Arend van Sprielc1179032012-10-22 13:55:33 -07003508 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003509 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003510 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003511 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003512 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003513 goto out_err;
3514 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003515 cfg->sched_escan = true;
3516 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003517 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003518 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003519 goto out_err;
3520 }
3521
3522 kfree(ssid);
3523 kfree(channel);
3524 kfree(request);
3525 return 0;
3526
3527out_err:
3528 kfree(ssid);
3529 kfree(channel);
3530 kfree(request);
3531 cfg80211_sched_scan_stopped(wiphy);
3532 return err;
3533}
3534
Arend van Spriele5806072012-09-19 22:21:08 +02003535static int brcmf_dev_pno_clean(struct net_device *ndev)
3536{
Arend van Spriele5806072012-09-19 22:21:08 +02003537 int ret;
3538
3539 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003540 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003541 if (ret == 0) {
3542 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003543 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3544 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003545 }
3546 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003547 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003548
3549 return ret;
3550}
3551
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003552static int brcmf_dev_pno_config(struct brcmf_if *ifp,
3553 struct cfg80211_sched_scan_request *request)
Arend van Spriele5806072012-09-19 22:21:08 +02003554{
3555 struct brcmf_pno_param_le pfn_param;
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003556 struct brcmf_pno_macaddr_le pfn_mac;
3557 s32 err;
3558 u8 *mac_mask;
3559 int i;
Arend van Spriele5806072012-09-19 22:21:08 +02003560
3561 memset(&pfn_param, 0, sizeof(pfn_param));
3562 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3563
3564 /* set extra pno params */
3565 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3566 pfn_param.repeat = BRCMF_PNO_REPEAT;
3567 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3568
3569 /* set up pno scan fr */
3570 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3571
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003572 err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
3573 sizeof(pfn_param));
3574 if (err) {
3575 brcmf_err("pfn_set failed, err=%d\n", err);
3576 return err;
3577 }
3578
3579 /* Find out if mac randomization should be turned on */
3580 if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
3581 return 0;
3582
3583 pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
3584 pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
3585
3586 memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN);
3587 mac_mask = request->mac_addr_mask;
3588 for (i = 0; i < ETH_ALEN; i++) {
3589 pfn_mac.mac[i] &= mac_mask[i];
3590 pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
3591 }
3592 /* Clear multi bit */
3593 pfn_mac.mac[0] &= 0xFE;
3594 /* Set locally administered */
3595 pfn_mac.mac[0] |= 0x02;
3596
3597 err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
3598 sizeof(pfn_mac));
3599 if (err)
3600 brcmf_err("pfn_macaddr failed, err=%d\n", err);
3601
3602 return err;
Arend van Spriele5806072012-09-19 22:21:08 +02003603}
3604
3605static int
3606brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3607 struct net_device *ndev,
3608 struct cfg80211_sched_scan_request *request)
3609{
Arend van Sprielc1179032012-10-22 13:55:33 -07003610 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003611 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003612 struct brcmf_pno_net_param_le pfn;
3613 int i;
3614 int ret = 0;
3615
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003616 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003617 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003618 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003619 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003620 return -EAGAIN;
3621 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003622 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3623 brcmf_err("Scanning suppressed: status (%lu)\n",
3624 cfg->scan_status);
3625 return -EAGAIN;
3626 }
Arend van Spriele5806072012-09-19 22:21:08 +02003627
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003628 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003629 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003630 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003631 return -EINVAL;
3632 }
3633
3634 if (request->n_ssids > 0) {
3635 for (i = 0; i < request->n_ssids; i++) {
3636 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003637 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3638 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003639
3640 /*
3641 * match_set ssids is a supert set of n_ssid list,
3642 * so we need not add these set seperately.
3643 */
3644 }
3645 }
3646
3647 if (request->n_match_sets > 0) {
3648 /* clean up everything */
3649 ret = brcmf_dev_pno_clean(ndev);
3650 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003651 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003652 return ret;
3653 }
3654
3655 /* configure pno */
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003656 if (brcmf_dev_pno_config(ifp, request))
Arend van Spriele5806072012-09-19 22:21:08 +02003657 return -EINVAL;
Arend van Spriele5806072012-09-19 22:21:08 +02003658
3659 /* configure each match set */
3660 for (i = 0; i < request->n_match_sets; i++) {
3661 struct cfg80211_ssid *ssid;
3662 u32 ssid_len;
3663
3664 ssid = &request->match_sets[i].ssid;
3665 ssid_len = ssid->ssid_len;
3666
3667 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003668 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003669 continue;
3670 }
3671 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3672 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3673 pfn.wsec = cpu_to_le32(0);
3674 pfn.infra = cpu_to_le32(1);
3675 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3676 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3677 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003678 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003679 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003680 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3681 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003682 }
3683 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003684 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003685 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003686 return -EINVAL;
3687 }
3688 } else {
3689 return -EINVAL;
3690 }
3691
3692 return 0;
3693}
3694
3695static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3696 struct net_device *ndev)
3697{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003698 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003699
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003700 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003701 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003702 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003703 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003704 return 0;
3705}
Arend van Spriele5806072012-09-19 22:21:08 +02003706
Hante Meuleman1f170112013-02-06 18:40:38 +01003707static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003708{
3709 s32 err;
3710
3711 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003712 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003713 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003714 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003715 return err;
3716 }
3717 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003718 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003719 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003720 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003721 return err;
3722 }
3723 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003724 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003725 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003726 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003727 return err;
3728 }
3729
3730 return 0;
3731}
3732
3733static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3734{
3735 if (is_rsn_ie)
3736 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3737
3738 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3739}
3740
3741static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003742brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003743 const struct brcmf_vs_tlv *wpa_ie,
3744 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003745{
3746 u32 auth = 0; /* d11 open authentication */
3747 u16 count;
3748 s32 err = 0;
3749 s32 len = 0;
3750 u32 i;
3751 u32 wsec;
3752 u32 pval = 0;
3753 u32 gval = 0;
3754 u32 wpa_auth = 0;
3755 u32 offset;
3756 u8 *data;
3757 u16 rsn_cap;
3758 u32 wme_bss_disable;
3759
Arend van Sprield96b8012012-12-05 15:26:02 +01003760 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003761 if (wpa_ie == NULL)
3762 goto exit;
3763
3764 len = wpa_ie->len + TLV_HDR_LEN;
3765 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003766 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003767 if (!is_rsn_ie)
3768 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003769 else
3770 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003771
3772 /* check for multicast cipher suite */
3773 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3774 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003775 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003776 goto exit;
3777 }
3778
3779 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3780 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003781 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003782 goto exit;
3783 }
3784 offset += TLV_OUI_LEN;
3785
3786 /* pick up multicast cipher */
3787 switch (data[offset]) {
3788 case WPA_CIPHER_NONE:
3789 gval = 0;
3790 break;
3791 case WPA_CIPHER_WEP_40:
3792 case WPA_CIPHER_WEP_104:
3793 gval = WEP_ENABLED;
3794 break;
3795 case WPA_CIPHER_TKIP:
3796 gval = TKIP_ENABLED;
3797 break;
3798 case WPA_CIPHER_AES_CCM:
3799 gval = AES_ENABLED;
3800 break;
3801 default:
3802 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003803 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003804 goto exit;
3805 }
3806
3807 offset++;
3808 /* walk thru unicast cipher list and pick up what we recognize */
3809 count = data[offset] + (data[offset + 1] << 8);
3810 offset += WPA_IE_SUITE_COUNT_LEN;
3811 /* Check for unicast suite(s) */
3812 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3813 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003814 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003815 goto exit;
3816 }
3817 for (i = 0; i < count; i++) {
3818 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3819 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003820 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003821 goto exit;
3822 }
3823 offset += TLV_OUI_LEN;
3824 switch (data[offset]) {
3825 case WPA_CIPHER_NONE:
3826 break;
3827 case WPA_CIPHER_WEP_40:
3828 case WPA_CIPHER_WEP_104:
3829 pval |= WEP_ENABLED;
3830 break;
3831 case WPA_CIPHER_TKIP:
3832 pval |= TKIP_ENABLED;
3833 break;
3834 case WPA_CIPHER_AES_CCM:
3835 pval |= AES_ENABLED;
3836 break;
3837 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003838 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003839 }
3840 offset++;
3841 }
3842 /* walk thru auth management suite list and pick up what we recognize */
3843 count = data[offset] + (data[offset + 1] << 8);
3844 offset += WPA_IE_SUITE_COUNT_LEN;
3845 /* Check for auth key management suite(s) */
3846 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3847 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003848 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003849 goto exit;
3850 }
3851 for (i = 0; i < count; i++) {
3852 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3853 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003854 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003855 goto exit;
3856 }
3857 offset += TLV_OUI_LEN;
3858 switch (data[offset]) {
3859 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003860 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003861 wpa_auth |= WPA_AUTH_NONE;
3862 break;
3863 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003864 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003865 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3866 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3867 break;
3868 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003869 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003870 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3871 (wpa_auth |= WPA_AUTH_PSK);
3872 break;
3873 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003874 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003875 }
3876 offset++;
3877 }
3878
3879 if (is_rsn_ie) {
3880 wme_bss_disable = 1;
3881 if ((offset + RSN_CAP_LEN) <= len) {
3882 rsn_cap = data[offset] + (data[offset + 1] << 8);
3883 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3884 wme_bss_disable = 0;
3885 }
3886 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003887 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003888 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003889 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003890 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003891 goto exit;
3892 }
3893 }
3894 /* FOR WPS , set SES_OW_ENABLED */
3895 wsec = (pval | gval | SES_OW_ENABLED);
3896
3897 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003898 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003899 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003900 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003901 goto exit;
3902 }
3903 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003904 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003905 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003906 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003907 goto exit;
3908 }
3909 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003910 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003911 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003912 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003913 goto exit;
3914 }
3915
3916exit:
3917 return err;
3918}
3919
3920static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003921brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003922 struct parsed_vndr_ies *vndr_ies)
3923{
Hante Meuleman1a873342012-09-27 14:17:54 +02003924 struct brcmf_vs_tlv *vndrie;
3925 struct brcmf_tlv *ie;
3926 struct parsed_vndr_ie_info *parsed_info;
3927 s32 remaining_len;
3928
3929 remaining_len = (s32)vndr_ie_len;
3930 memset(vndr_ies, 0, sizeof(*vndr_ies));
3931
3932 ie = (struct brcmf_tlv *)vndr_ie_buf;
3933 while (ie) {
3934 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3935 goto next;
3936 vndrie = (struct brcmf_vs_tlv *)ie;
3937 /* len should be bigger than OUI length + one */
3938 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003939 brcmf_err("invalid vndr ie. length is too small %d\n",
3940 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003941 goto next;
3942 }
3943 /* if wpa or wme ie, do not add ie */
3944 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3945 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3946 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003947 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003948 goto next;
3949 }
3950
3951 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3952
3953 /* save vndr ie information */
3954 parsed_info->ie_ptr = (char *)vndrie;
3955 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3956 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3957
3958 vndr_ies->count++;
3959
Arend van Sprield96b8012012-12-05 15:26:02 +01003960 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3961 parsed_info->vndrie.oui[0],
3962 parsed_info->vndrie.oui[1],
3963 parsed_info->vndrie.oui[2],
3964 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003965
Arend van Spriel9f440b72013-02-08 15:53:36 +01003966 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003967 break;
3968next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003969 remaining_len -= (ie->len + TLV_HDR_LEN);
3970 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003971 ie = NULL;
3972 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003973 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3974 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003975 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003976 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003977}
3978
3979static u32
3980brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3981{
3982
Hante Meuleman1a873342012-09-27 14:17:54 +02003983 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3984 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3985
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303986 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003987
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303988 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003989
3990 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3991
3992 return ie_len + VNDR_IE_HDR_SIZE;
3993}
3994
Arend van Spriel1332e262012-11-05 16:22:18 -08003995s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3996 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003997{
Arend van Spriel1332e262012-11-05 16:22:18 -08003998 struct brcmf_if *ifp;
3999 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004000 s32 err = 0;
4001 u8 *iovar_ie_buf;
4002 u8 *curr_ie_buf;
4003 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004004 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07004005 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004006 u32 del_add_ie_buf_len = 0;
4007 u32 total_ie_buf_len = 0;
4008 u32 parsed_ie_buf_len = 0;
4009 struct parsed_vndr_ies old_vndr_ies;
4010 struct parsed_vndr_ies new_vndr_ies;
4011 struct parsed_vndr_ie_info *vndrie_info;
4012 s32 i;
4013 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004014 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004015
Arend van Spriel1332e262012-11-05 16:22:18 -08004016 if (!vif)
4017 return -ENODEV;
4018 ifp = vif->ifp;
4019 saved_ie = &vif->saved_ie;
4020
Hante Meuleman37a869e2015-10-29 20:33:17 +01004021 brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
4022 pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02004023 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4024 if (!iovar_ie_buf)
4025 return -ENOMEM;
4026 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01004027 switch (pktflag) {
4028 case BRCMF_VNDR_IE_PRBREQ_FLAG:
4029 mgmt_ie_buf = saved_ie->probe_req_ie;
4030 mgmt_ie_len = &saved_ie->probe_req_ie_len;
4031 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
4032 break;
4033 case BRCMF_VNDR_IE_PRBRSP_FLAG:
4034 mgmt_ie_buf = saved_ie->probe_res_ie;
4035 mgmt_ie_len = &saved_ie->probe_res_ie_len;
4036 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
4037 break;
4038 case BRCMF_VNDR_IE_BEACON_FLAG:
4039 mgmt_ie_buf = saved_ie->beacon_ie;
4040 mgmt_ie_len = &saved_ie->beacon_ie_len;
4041 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
4042 break;
4043 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
4044 mgmt_ie_buf = saved_ie->assoc_req_ie;
4045 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
4046 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
4047 break;
4048 default:
4049 err = -EPERM;
4050 brcmf_err("not suitable type\n");
4051 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004052 }
4053
4054 if (vndr_ie_len > mgmt_ie_buf_len) {
4055 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004056 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004057 goto exit;
4058 }
4059
4060 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
4061 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
4062 ptr = curr_ie_buf;
4063 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
4064 for (i = 0; i < new_vndr_ies.count; i++) {
4065 vndrie_info = &new_vndr_ies.ie_info[i];
4066 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
4067 vndrie_info->ie_len);
4068 parsed_ie_buf_len += vndrie_info->ie_len;
4069 }
4070 }
4071
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004072 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004073 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
4074 (memcmp(mgmt_ie_buf, curr_ie_buf,
4075 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004076 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004077 goto exit;
4078 }
4079
4080 /* parse old vndr_ie */
4081 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
4082
4083 /* make a command to delete old ie */
4084 for (i = 0; i < old_vndr_ies.count; i++) {
4085 vndrie_info = &old_vndr_ies.ie_info[i];
4086
Arend van Sprield96b8012012-12-05 15:26:02 +01004087 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4088 vndrie_info->vndrie.id,
4089 vndrie_info->vndrie.len,
4090 vndrie_info->vndrie.oui[0],
4091 vndrie_info->vndrie.oui[1],
4092 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004093
4094 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4095 vndrie_info->ie_ptr,
4096 vndrie_info->ie_len,
4097 "del");
4098 curr_ie_buf += del_add_ie_buf_len;
4099 total_ie_buf_len += del_add_ie_buf_len;
4100 }
4101 }
4102
4103 *mgmt_ie_len = 0;
4104 /* Add if there is any extra IE */
4105 if (mgmt_ie_buf && parsed_ie_buf_len) {
4106 ptr = mgmt_ie_buf;
4107
4108 remained_buf_len = mgmt_ie_buf_len;
4109
4110 /* make a command to add new ie */
4111 for (i = 0; i < new_vndr_ies.count; i++) {
4112 vndrie_info = &new_vndr_ies.ie_info[i];
4113
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004114 /* verify remained buf size before copy data */
4115 if (remained_buf_len < (vndrie_info->vndrie.len +
4116 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004117 brcmf_err("no space in mgmt_ie_buf: len left %d",
4118 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004119 break;
4120 }
4121 remained_buf_len -= (vndrie_info->ie_len +
4122 VNDR_IE_VSIE_OFFSET);
4123
Arend van Sprield96b8012012-12-05 15:26:02 +01004124 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4125 vndrie_info->vndrie.id,
4126 vndrie_info->vndrie.len,
4127 vndrie_info->vndrie.oui[0],
4128 vndrie_info->vndrie.oui[1],
4129 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004130
4131 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4132 vndrie_info->ie_ptr,
4133 vndrie_info->ie_len,
4134 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02004135
4136 /* save the parsed IE in wl struct */
4137 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4138 vndrie_info->ie_len);
4139 *mgmt_ie_len += vndrie_info->ie_len;
4140
4141 curr_ie_buf += del_add_ie_buf_len;
4142 total_ie_buf_len += del_add_ie_buf_len;
4143 }
4144 }
4145 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004146 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004147 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004148 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004149 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004150 }
4151
4152exit:
4153 kfree(iovar_ie_buf);
4154 return err;
4155}
4156
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004157s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4158{
4159 s32 pktflags[] = {
4160 BRCMF_VNDR_IE_PRBREQ_FLAG,
4161 BRCMF_VNDR_IE_PRBRSP_FLAG,
4162 BRCMF_VNDR_IE_BEACON_FLAG
4163 };
4164 int i;
4165
4166 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4167 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4168
4169 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4170 return 0;
4171}
4172
Hante Meuleman1a873342012-09-27 14:17:54 +02004173static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004174brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4175 struct cfg80211_beacon_data *beacon)
4176{
4177 s32 err;
4178
4179 /* Set Beacon IEs to FW */
4180 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4181 beacon->tail, beacon->tail_len);
4182 if (err) {
4183 brcmf_err("Set Beacon IE Failed\n");
4184 return err;
4185 }
4186 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4187
4188 /* Set Probe Response IEs to FW */
4189 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4190 beacon->proberesp_ies,
4191 beacon->proberesp_ies_len);
4192 if (err)
4193 brcmf_err("Set Probe Resp IE Failed\n");
4194 else
4195 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4196
4197 return err;
4198}
4199
4200static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004201brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4202 struct cfg80211_ap_settings *settings)
4203{
4204 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004205 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004206 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004207 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004208 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004209 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004210 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004211 const struct brcmf_tlv *rsn_ie;
4212 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004213 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004214 enum nl80211_iftype dev_role;
4215 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004216 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004217 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004218 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004219
Arend van Spriel06c01582014-05-12 10:47:37 +02004220 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4221 settings->chandef.chan->hw_value,
4222 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004223 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004224 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4225 settings->ssid, settings->ssid_len, settings->auth_type,
4226 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004227 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004228 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004229
Arend van Spriel98027762014-12-21 12:43:53 +01004230 /* store current 11d setting */
4231 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4232 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4233 settings->beacon.tail_len,
4234 WLAN_EID_COUNTRY);
4235 is_11d = country_ie ? 1 : 0;
4236
Hante Meuleman1a873342012-09-27 14:17:54 +02004237 memset(&ssid_le, 0, sizeof(ssid_le));
4238 if (settings->ssid == NULL || settings->ssid_len == 0) {
4239 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4240 ssid_ie = brcmf_parse_tlvs(
4241 (u8 *)&settings->beacon.head[ie_offset],
4242 settings->beacon.head_len - ie_offset,
4243 WLAN_EID_SSID);
4244 if (!ssid_ie)
4245 return -EINVAL;
4246
4247 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4248 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004249 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004250 } else {
4251 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4252 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4253 }
4254
Hante Meulemana44aa402014-12-03 21:05:33 +01004255 if (!mbss) {
4256 brcmf_set_mpc(ifp, 0);
4257 brcmf_configure_arp_offload(ifp, false);
4258 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004259
4260 /* find the RSN_IE */
4261 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4262 settings->beacon.tail_len, WLAN_EID_RSN);
4263
4264 /* find the WPA_IE */
4265 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4266 settings->beacon.tail_len);
4267
Hante Meuleman1a873342012-09-27 14:17:54 +02004268 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004269 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004270 if (wpa_ie != NULL) {
4271 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004272 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004273 if (err < 0)
4274 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004275 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004276 struct brcmf_vs_tlv *tmp_ie;
4277
4278 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4279
Hante Meuleman1a873342012-09-27 14:17:54 +02004280 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004281 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004282 if (err < 0)
4283 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004284 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004285 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004286 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004287 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004288 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004289
Hante Meulemana0f07952013-02-08 15:53:47 +01004290 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004291
Hante Meulemana44aa402014-12-03 21:05:33 +01004292 if (!mbss) {
4293 chanspec = chandef_to_chanspec(&cfg->d11inf,
4294 &settings->chandef);
4295 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004296 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004297 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4298 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004299 goto exit;
4300 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004301
Arend van Spriel98027762014-12-21 12:43:53 +01004302 if (is_11d != ifp->vif->is_11d) {
4303 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4304 is_11d);
4305 if (err < 0) {
4306 brcmf_err("Regulatory Set Error, %d\n", err);
4307 goto exit;
4308 }
4309 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004310 if (settings->beacon_interval) {
4311 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4312 settings->beacon_interval);
4313 if (err < 0) {
4314 brcmf_err("Beacon Interval Set Error, %d\n",
4315 err);
4316 goto exit;
4317 }
4318 }
4319 if (settings->dtim_period) {
4320 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4321 settings->dtim_period);
4322 if (err < 0) {
4323 brcmf_err("DTIM Interval Set Error, %d\n", err);
4324 goto exit;
4325 }
4326 }
4327
Hante Meuleman8abffd82015-10-29 20:33:16 +01004328 if ((dev_role == NL80211_IFTYPE_AP) &&
4329 ((ifp->ifidx == 0) ||
4330 !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004331 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4332 if (err < 0) {
4333 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4334 goto exit;
4335 }
4336 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4337 }
4338
4339 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004340 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004341 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004342 goto exit;
4343 }
Arend van Spriel98027762014-12-21 12:43:53 +01004344 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4345 /* Multiple-BSS should use same 11d configuration */
4346 err = -EINVAL;
4347 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004348 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004349 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004350 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4351 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4352
Hante Meulemana0f07952013-02-08 15:53:47 +01004353 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4354 if (err < 0) {
4355 brcmf_err("setting AP mode failed %d\n", err);
4356 goto exit;
4357 }
4358 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4359 if (err < 0) {
4360 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4361 goto exit;
4362 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004363 /* On DOWN the firmware removes the WEP keys, reconfigure
4364 * them if they were set.
4365 */
4366 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004367
4368 memset(&join_params, 0, sizeof(join_params));
4369 /* join parameters starts with ssid */
4370 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4371 /* create softap */
4372 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4373 &join_params, sizeof(join_params));
4374 if (err < 0) {
4375 brcmf_err("SET SSID error (%d)\n", err);
4376 goto exit;
4377 }
4378 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4379 } else {
4380 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4381 sizeof(ssid_le));
4382 if (err < 0) {
4383 brcmf_err("setting ssid failed %d\n", err);
4384 goto exit;
4385 }
Hante Meuleman37a869e2015-10-29 20:33:17 +01004386 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meulemana0f07952013-02-08 15:53:47 +01004387 bss_enable.enable = cpu_to_le32(1);
4388 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4389 sizeof(bss_enable));
4390 if (err < 0) {
4391 brcmf_err("bss_enable config failed %d\n", err);
4392 goto exit;
4393 }
4394
4395 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4396 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004397 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004398 brcmf_net_setcarrier(ifp, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004399
4400exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004401 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004402 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004403 brcmf_configure_arp_offload(ifp, true);
4404 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004405 return err;
4406}
4407
4408static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4409{
Arend van Sprielc1179032012-10-22 13:55:33 -07004410 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004411 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004412 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004413 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004414
Arend van Sprield96b8012012-12-05 15:26:02 +01004415 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004416
Hante Meuleman426d0a52013-02-08 15:53:53 +01004417 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004418 /* Due to most likely deauths outstanding we sleep */
4419 /* first to make sure they get processed by fw. */
4420 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004421
Hante Meulemana44aa402014-12-03 21:05:33 +01004422 if (ifp->vif->mbss) {
4423 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4424 return err;
4425 }
4426
Hante Meuleman5c33a942013-04-02 21:06:18 +02004427 memset(&join_params, 0, sizeof(join_params));
4428 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4429 &join_params, sizeof(join_params));
4430 if (err < 0)
4431 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004432 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004433 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004434 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004435 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4436 if (err < 0)
4437 brcmf_err("setting AP mode failed %d\n", err);
4438 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4439 if (err < 0)
4440 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004441 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4442 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004443 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4444 ifp->vif->is_11d);
4445 if (err < 0)
4446 brcmf_err("restoring REGULATORY setting failed %d\n",
4447 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004448 /* Bring device back up so it can be used again */
4449 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4450 if (err < 0)
4451 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004452 } else {
Hante Meuleman37a869e2015-10-29 20:33:17 +01004453 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004454 bss_enable.enable = cpu_to_le32(0);
4455 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4456 sizeof(bss_enable));
4457 if (err < 0)
4458 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004459 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004460 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004461 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004462 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004463 brcmf_net_setcarrier(ifp, false);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004464
Hante Meuleman1a873342012-09-27 14:17:54 +02004465 return err;
4466}
4467
Hante Meulemana0f07952013-02-08 15:53:47 +01004468static s32
4469brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4470 struct cfg80211_beacon_data *info)
4471{
Hante Meulemana0f07952013-02-08 15:53:47 +01004472 struct brcmf_if *ifp = netdev_priv(ndev);
4473 s32 err;
4474
4475 brcmf_dbg(TRACE, "Enter\n");
4476
Hante Meulemana0f07952013-02-08 15:53:47 +01004477 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4478
4479 return err;
4480}
4481
Hante Meuleman1a873342012-09-27 14:17:54 +02004482static int
4483brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004484 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004485{
Hante Meulemana0f07952013-02-08 15:53:47 +01004486 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004487 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004488 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004489 s32 err;
4490
Jouni Malinen89c771e2014-10-10 20:52:40 +03004491 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004492 return -EFAULT;
4493
Jouni Malinen89c771e2014-10-10 20:52:40 +03004494 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004495
Hante Meulemana0f07952013-02-08 15:53:47 +01004496 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4497 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004498 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004499 return -EIO;
4500
Jouni Malinen89c771e2014-10-10 20:52:40 +03004501 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004502 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004503 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004504 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004505 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004506 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004507
Arend van Sprield96b8012012-12-05 15:26:02 +01004508 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004509 return err;
4510}
4511
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004512static int
4513brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4514 const u8 *mac, struct station_parameters *params)
4515{
4516 struct brcmf_if *ifp = netdev_priv(ndev);
4517 s32 err;
4518
4519 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4520 params->sta_flags_mask, params->sta_flags_set);
4521
4522 /* Ignore all 00 MAC */
4523 if (is_zero_ether_addr(mac))
4524 return 0;
4525
4526 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4527 return 0;
4528
4529 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4530 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4531 (void *)mac, ETH_ALEN);
4532 else
4533 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4534 (void *)mac, ETH_ALEN);
4535 if (err < 0)
4536 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4537
4538 return err;
4539}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004540
4541static void
4542brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4543 struct wireless_dev *wdev,
4544 u16 frame_type, bool reg)
4545{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004546 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004547 u16 mgmt_type;
4548
4549 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4550
4551 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004552 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004553 if (reg)
4554 vif->mgmt_rx_reg |= BIT(mgmt_type);
4555 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004556 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004557}
4558
4559
4560static int
4561brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004562 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004563{
4564 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004565 struct ieee80211_channel *chan = params->chan;
4566 const u8 *buf = params->buf;
4567 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004568 const struct ieee80211_mgmt *mgmt;
4569 struct brcmf_cfg80211_vif *vif;
4570 s32 err = 0;
4571 s32 ie_offset;
4572 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004573 struct brcmf_fil_action_frame_le *action_frame;
4574 struct brcmf_fil_af_params_le *af_params;
4575 bool ack;
4576 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004577 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004578
4579 brcmf_dbg(TRACE, "Enter\n");
4580
4581 *cookie = 0;
4582
4583 mgmt = (const struct ieee80211_mgmt *)buf;
4584
Hante Meulemana0f07952013-02-08 15:53:47 +01004585 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4586 brcmf_err("Driver only allows MGMT packet type\n");
4587 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004588 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004589
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004590 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4591
Hante Meulemana0f07952013-02-08 15:53:47 +01004592 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4593 /* Right now the only reason to get a probe response */
4594 /* is for p2p listen response or for p2p GO from */
4595 /* wpa_supplicant. Unfortunately the probe is send */
4596 /* on primary ndev, while dongle wants it on the p2p */
4597 /* vif. Since this is only reason for a probe */
4598 /* response to be sent, the vif is taken from cfg. */
4599 /* If ever desired to send proberesp for non p2p */
4600 /* response then data should be checked for */
4601 /* "DIRECT-". Note in future supplicant will take */
4602 /* dedicated p2p wdev to do this and then this 'hack'*/
4603 /* is not needed anymore. */
4604 ie_offset = DOT11_MGMT_HDR_LEN +
4605 DOT11_BCN_PRB_FIXED_LEN;
4606 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004607 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4608 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4609 err = brcmf_vif_set_mgmt_ie(vif,
4610 BRCMF_VNDR_IE_PRBRSP_FLAG,
4611 &buf[ie_offset],
4612 ie_len);
4613 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4614 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004615 } else if (ieee80211_is_action(mgmt->frame_control)) {
4616 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4617 if (af_params == NULL) {
4618 brcmf_err("unable to allocate frame\n");
4619 err = -ENOMEM;
4620 goto exit;
4621 }
4622 action_frame = &af_params->action_frame;
4623 /* Add the packet Id */
4624 action_frame->packet_id = cpu_to_le32(*cookie);
4625 /* Add BSSID */
4626 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4627 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4628 /* Add the length exepted for 802.11 header */
4629 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004630 /* Add the channel. Use the one specified as parameter if any or
4631 * the current one (got from the firmware) otherwise
4632 */
4633 if (chan)
4634 freq = chan->center_freq;
4635 else
4636 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4637 &freq);
4638 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004639 af_params->channel = cpu_to_le32(chan_nr);
4640
4641 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4642 le16_to_cpu(action_frame->len));
4643
4644 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004645 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004646
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004647 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004648 af_params);
4649
4650 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4651 GFP_KERNEL);
4652 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004653 } else {
4654 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4655 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4656 }
4657
Hante Meuleman18e2f612013-02-08 15:53:49 +01004658exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004659 return err;
4660}
4661
4662
4663static int
4664brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4665 struct wireless_dev *wdev,
4666 u64 cookie)
4667{
4668 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4669 struct brcmf_cfg80211_vif *vif;
4670 int err = 0;
4671
4672 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4673
4674 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4675 if (vif == NULL) {
4676 brcmf_err("No p2p device available for probe response\n");
4677 err = -ENODEV;
4678 goto exit;
4679 }
4680 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4681exit:
4682 return err;
4683}
4684
Piotr Haber61730d42013-04-23 12:53:12 +02004685static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4686 struct wireless_dev *wdev,
4687 enum nl80211_crit_proto_id proto,
4688 u16 duration)
4689{
4690 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4691 struct brcmf_cfg80211_vif *vif;
4692
4693 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4694
4695 /* only DHCP support for now */
4696 if (proto != NL80211_CRIT_PROTO_DHCP)
4697 return -EINVAL;
4698
4699 /* suppress and abort scanning */
4700 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4701 brcmf_abort_scanning(cfg);
4702
4703 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4704}
4705
4706static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4707 struct wireless_dev *wdev)
4708{
4709 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4710 struct brcmf_cfg80211_vif *vif;
4711
4712 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4713
4714 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4715 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4716}
4717
Hante Meuleman70b7d942014-07-30 13:20:07 +02004718static s32
4719brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4720 const struct brcmf_event_msg *e, void *data)
4721{
4722 switch (e->reason) {
4723 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4724 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4725 break;
4726 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4727 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4728 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4729 break;
4730 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4731 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4732 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4733 break;
4734 }
4735
4736 return 0;
4737}
4738
Arend van Spriel89c2f382013-08-10 12:27:25 +02004739static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4740{
4741 int ret;
4742
4743 switch (oper) {
4744 case NL80211_TDLS_DISCOVERY_REQ:
4745 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4746 break;
4747 case NL80211_TDLS_SETUP:
4748 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4749 break;
4750 case NL80211_TDLS_TEARDOWN:
4751 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4752 break;
4753 default:
4754 brcmf_err("unsupported operation: %d\n", oper);
4755 ret = -EOPNOTSUPP;
4756 }
4757 return ret;
4758}
4759
4760static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004761 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004762 enum nl80211_tdls_operation oper)
4763{
4764 struct brcmf_if *ifp;
4765 struct brcmf_tdls_iovar_le info;
4766 int ret = 0;
4767
4768 ret = brcmf_convert_nl80211_tdls_oper(oper);
4769 if (ret < 0)
4770 return ret;
4771
4772 ifp = netdev_priv(ndev);
4773 memset(&info, 0, sizeof(info));
4774 info.mode = (u8)ret;
4775 if (peer)
4776 memcpy(info.ea, peer, ETH_ALEN);
4777
4778 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4779 &info, sizeof(info));
4780 if (ret < 0)
4781 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4782
4783 return ret;
4784}
4785
Arend van Spriel5b435de2011-10-05 13:19:03 +02004786static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004787 .add_virtual_intf = brcmf_cfg80211_add_iface,
4788 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004789 .change_virtual_intf = brcmf_cfg80211_change_iface,
4790 .scan = brcmf_cfg80211_scan,
4791 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4792 .join_ibss = brcmf_cfg80211_join_ibss,
4793 .leave_ibss = brcmf_cfg80211_leave_ibss,
4794 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02004795 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004796 .set_tx_power = brcmf_cfg80211_set_tx_power,
4797 .get_tx_power = brcmf_cfg80211_get_tx_power,
4798 .add_key = brcmf_cfg80211_add_key,
4799 .del_key = brcmf_cfg80211_del_key,
4800 .get_key = brcmf_cfg80211_get_key,
4801 .set_default_key = brcmf_cfg80211_config_default_key,
4802 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4803 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004804 .connect = brcmf_cfg80211_connect,
4805 .disconnect = brcmf_cfg80211_disconnect,
4806 .suspend = brcmf_cfg80211_suspend,
4807 .resume = brcmf_cfg80211_resume,
4808 .set_pmksa = brcmf_cfg80211_set_pmksa,
4809 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004810 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004811 .start_ap = brcmf_cfg80211_start_ap,
4812 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004813 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004814 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004815 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004816 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4817 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004818 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4819 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4820 .remain_on_channel = brcmf_p2p_remain_on_channel,
4821 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004822 .start_p2p_device = brcmf_p2p_start_device,
4823 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004824 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4825 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004826 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004827};
4828
Arend van Spriel3eacf862012-10-22 13:55:30 -07004829struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004830 enum nl80211_iftype type,
4831 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004832{
Hante Meulemana44aa402014-12-03 21:05:33 +01004833 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004834 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004835 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004836
Arend van Spriel33a6b152013-02-08 15:53:39 +01004837 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004838 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004839 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4840 if (!vif)
4841 return ERR_PTR(-ENOMEM);
4842
4843 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004844 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004845
Arend van Spriel3eacf862012-10-22 13:55:30 -07004846 vif->pm_block = pm_block;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004847
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004848 brcmf_init_prof(&vif->profile);
4849
Hante Meulemana44aa402014-12-03 21:05:33 +01004850 if (type == NL80211_IFTYPE_AP) {
4851 mbss = false;
4852 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4853 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4854 mbss = true;
4855 break;
4856 }
4857 }
4858 vif->mbss = mbss;
4859 }
4860
Arend van Spriel3eacf862012-10-22 13:55:30 -07004861 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004862 return vif;
4863}
4864
Arend van Spriel427dec52014-01-06 12:40:47 +01004865void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004866{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004867 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004868 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004869}
4870
Arend van Spriel9df4d542014-01-06 12:40:49 +01004871void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4872{
4873 struct brcmf_cfg80211_vif *vif;
4874 struct brcmf_if *ifp;
4875
4876 ifp = netdev_priv(ndev);
4877 vif = ifp->vif;
4878
Arend van Spriel95ef1232015-08-26 22:15:04 +02004879 if (vif)
4880 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01004881 free_netdev(ndev);
4882}
4883
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004884static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004885{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004886 u32 event = e->event_code;
4887 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004888
4889 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004890 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004891 return true;
4892 }
4893
4894 return false;
4895}
4896
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004897static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004898{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004899 u32 event = e->event_code;
4900 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004901
Hante Meuleman68ca3952014-02-25 20:30:26 +01004902 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4903 (event == BRCMF_E_DISASSOC_IND) ||
4904 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004905 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004906 return true;
4907 }
4908 return false;
4909}
4910
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004911static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004912 const struct brcmf_event_msg *e)
4913{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004914 u32 event = e->event_code;
4915 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004916
4917 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004918 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4919 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004920 return true;
4921 }
4922
4923 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004924 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004925 return true;
4926 }
4927
4928 return false;
4929}
4930
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004931static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004932{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004933 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004934
4935 kfree(conn_info->req_ie);
4936 conn_info->req_ie = NULL;
4937 conn_info->req_ie_len = 0;
4938 kfree(conn_info->resp_ie);
4939 conn_info->resp_ie = NULL;
4940 conn_info->resp_ie_len = 0;
4941}
4942
Hante Meuleman89286dc2013-02-08 15:53:46 +01004943static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4944 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004945{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004946 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004947 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004948 u32 req_len;
4949 u32 resp_len;
4950 s32 err = 0;
4951
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004952 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004953
Arend van Sprielac24be62012-10-22 10:36:23 -07004954 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4955 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004956 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004957 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004958 return err;
4959 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004960 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004961 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004962 req_len = le32_to_cpu(assoc_info->req_len);
4963 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004964 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004965 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004966 cfg->extra_buf,
4967 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004968 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004969 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004970 return err;
4971 }
4972 conn_info->req_ie_len = req_len;
4973 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004974 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004975 GFP_KERNEL);
4976 } else {
4977 conn_info->req_ie_len = 0;
4978 conn_info->req_ie = NULL;
4979 }
4980 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004981 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004982 cfg->extra_buf,
4983 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004984 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004985 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004986 return err;
4987 }
4988 conn_info->resp_ie_len = resp_len;
4989 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004990 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004991 GFP_KERNEL);
4992 } else {
4993 conn_info->resp_ie_len = 0;
4994 conn_info->resp_ie = NULL;
4995 }
Arend van Spriel16886732012-12-05 15:26:04 +01004996 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4997 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004998
4999 return err;
5000}
5001
5002static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005003brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005004 struct net_device *ndev,
5005 const struct brcmf_event_msg *e)
5006{
Arend van Sprielc1179032012-10-22 13:55:33 -07005007 struct brcmf_if *ifp = netdev_priv(ndev);
5008 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005009 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
5010 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07005011 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005012 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07005013 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02005014 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005015 u32 freq;
5016 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07005017 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005018
Arend van Sprield96b8012012-12-05 15:26:02 +01005019 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005020
Hante Meuleman89286dc2013-02-08 15:53:46 +01005021 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005022 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005023 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005024
Franky Lina180b832012-10-10 11:13:09 -07005025 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
5026 if (buf == NULL) {
5027 err = -ENOMEM;
5028 goto done;
5029 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005030
Franky Lina180b832012-10-10 11:13:09 -07005031 /* data sent to dongle has to be little endian */
5032 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07005033 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07005034 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07005035
5036 if (err)
5037 goto done;
5038
5039 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02005040 ch.chspec = le16_to_cpu(bi->chanspec);
5041 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005042
Franky Lin83cf17a2013-04-11 13:28:50 +02005043 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005044 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5045 else
5046 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5047
Franky Lin83cf17a2013-04-11 13:28:50 +02005048 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005049 notify_channel = ieee80211_get_channel(wiphy, freq);
5050
Franky Lina180b832012-10-10 11:13:09 -07005051done:
5052 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02005053 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005054 conn_info->req_ie, conn_info->req_ie_len,
5055 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005056 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005057
Arend van Sprielc1179032012-10-22 13:55:33 -07005058 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01005059 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005060 return err;
5061}
5062
5063static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005064brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005065 struct net_device *ndev, const struct brcmf_event_msg *e,
5066 bool completed)
5067{
Arend van Sprielc1179032012-10-22 13:55:33 -07005068 struct brcmf_if *ifp = netdev_priv(ndev);
5069 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005070 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005071
Arend van Sprield96b8012012-12-05 15:26:02 +01005072 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005073
Arend van Sprielc1179032012-10-22 13:55:33 -07005074 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5075 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005076 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01005077 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005078 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005079 brcmf_update_bss_info(cfg, ifp);
5080 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5081 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005082 }
5083 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02005084 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005085 conn_info->req_ie,
5086 conn_info->req_ie_len,
5087 conn_info->resp_ie,
5088 conn_info->resp_ie_len,
5089 completed ? WLAN_STATUS_SUCCESS :
5090 WLAN_STATUS_AUTH_TIMEOUT,
5091 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005092 brcmf_dbg(CONN, "Report connect result - connection %s\n",
5093 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005094 }
Arend van Sprield96b8012012-12-05 15:26:02 +01005095 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005096 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005097}
5098
5099static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005100brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02005101 struct net_device *ndev,
5102 const struct brcmf_event_msg *e, void *data)
5103{
Hante Meulemana44aa402014-12-03 21:05:33 +01005104 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01005105 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005106 u32 event = e->event_code;
5107 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02005108 struct station_info sinfo;
5109
Arend van Spriel16886732012-12-05 15:26:04 +01005110 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005111 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
5112 ndev != cfg_to_ndev(cfg)) {
5113 brcmf_dbg(CONN, "AP mode link down\n");
5114 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01005115 if (ifp->vif->mbss)
Arend van Sprielee6e3a32015-08-26 22:14:55 +02005116 brcmf_remove_interface(ifp);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005117 return 0;
5118 }
Hante Meuleman1a873342012-09-27 14:17:54 +02005119
Hante Meuleman1a873342012-09-27 14:17:54 +02005120 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01005121 (reason == BRCMF_E_STATUS_SUCCESS)) {
5122 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02005123 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005124 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02005125 return -EINVAL;
5126 }
5127 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005128 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02005129 generation++;
5130 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005131 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005132 } else if ((event == BRCMF_E_DISASSOC_IND) ||
5133 (event == BRCMF_E_DEAUTH_IND) ||
5134 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01005135 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005136 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01005137 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02005138}
5139
5140static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005141brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005142 const struct brcmf_event_msg *e, void *data)
5143{
Arend van Spriel19937322012-11-05 16:22:32 -08005144 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5145 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005146 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005147 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005148 s32 err = 0;
5149
Hante Meuleman8851cce2014-07-30 13:20:02 +02005150 if ((e->event_code == BRCMF_E_DEAUTH) ||
5151 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5152 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5153 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5154 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5155 }
5156
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005157 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005158 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005159 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005160 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005161 if (brcmf_is_ibssmode(ifp->vif)) {
Hante Meulemanb0a79082015-12-10 13:43:07 +01005162 brcmf_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005163 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005164 memcpy(profile->bssid, e->addr, ETH_ALEN);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005165 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005166 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5167 &ifp->vif->sme_state);
5168 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5169 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005170 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005171 brcmf_bss_connect_done(cfg, ndev, e, true);
Hante Meuleman92121e62015-10-08 20:33:21 +02005172 brcmf_net_setcarrier(ifp, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005173 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005174 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005175 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005176 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005177 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005178 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005179 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005180 if (ndev != cfg_to_ndev(cfg))
5181 complete(&cfg->vif_disabled);
Hante Meuleman92121e62015-10-08 20:33:21 +02005182 brcmf_net_setcarrier(ifp, false);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005183 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005184 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005185 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5186 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005187 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005188 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005189 }
5190
5191 return err;
5192}
5193
5194static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005195brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005196 const struct brcmf_event_msg *e, void *data)
5197{
Arend van Spriel19937322012-11-05 16:22:32 -08005198 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005199 u32 event = e->event_code;
5200 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201
5202 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005203 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005204 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005205 else
Arend van Spriel19937322012-11-05 16:22:32 -08005206 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005207 }
5208
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005209 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210}
5211
5212static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005213brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005214 const struct brcmf_event_msg *e, void *data)
5215{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005216 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005217 enum nl80211_key_type key_type;
5218
5219 if (flags & BRCMF_EVENT_MSG_GROUP)
5220 key_type = NL80211_KEYTYPE_GROUP;
5221 else
5222 key_type = NL80211_KEYTYPE_PAIRWISE;
5223
Arend van Spriel19937322012-11-05 16:22:32 -08005224 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005225 NULL, GFP_KERNEL);
5226
5227 return 0;
5228}
5229
Arend van Sprield3c0b632013-02-08 15:53:37 +01005230static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5231 const struct brcmf_event_msg *e, void *data)
5232{
5233 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5234 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5235 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5236 struct brcmf_cfg80211_vif *vif;
5237
Hante Meuleman37a869e2015-10-29 20:33:17 +01005238 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
Arend van Sprield3c0b632013-02-08 15:53:37 +01005239 ifevent->action, ifevent->flags, ifevent->ifidx,
Hante Meuleman37a869e2015-10-29 20:33:17 +01005240 ifevent->bsscfgidx);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005241
Arend van Sprield3c0b632013-02-08 15:53:37 +01005242 mutex_lock(&event->vif_event_lock);
5243 event->action = ifevent->action;
5244 vif = event->vif;
5245
5246 switch (ifevent->action) {
5247 case BRCMF_E_IF_ADD:
5248 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005249 if (!cfg->vif_event.vif) {
5250 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005251 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005252 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005253
5254 ifp->vif = vif;
5255 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005256 if (ifp->ndev) {
5257 vif->wdev.netdev = ifp->ndev;
5258 ifp->ndev->ieee80211_ptr = &vif->wdev;
5259 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5260 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005261 mutex_unlock(&event->vif_event_lock);
5262 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005263 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005264
5265 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005266 mutex_unlock(&event->vif_event_lock);
5267 /* event may not be upon user request */
5268 if (brcmf_cfg80211_vif_event_armed(cfg))
5269 wake_up(&event->vif_wq);
5270 return 0;
5271
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005272 case BRCMF_E_IF_CHANGE:
5273 mutex_unlock(&event->vif_event_lock);
5274 wake_up(&event->vif_wq);
5275 return 0;
5276
Arend van Sprield3c0b632013-02-08 15:53:37 +01005277 default:
5278 mutex_unlock(&event->vif_event_lock);
5279 break;
5280 }
5281 return -EINVAL;
5282}
5283
Arend van Spriel5b435de2011-10-05 13:19:03 +02005284static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5285{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005286 conf->frag_threshold = (u32)-1;
5287 conf->rts_threshold = (u32)-1;
5288 conf->retry_short = (u32)-1;
5289 conf->retry_long = (u32)-1;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005290}
5291
Arend van Spriel5c36b992012-11-14 18:46:05 -08005292static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005293{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005294 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5295 brcmf_notify_connect_status);
5296 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5297 brcmf_notify_connect_status);
5298 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5299 brcmf_notify_connect_status);
5300 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5301 brcmf_notify_connect_status);
5302 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5303 brcmf_notify_connect_status);
5304 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5305 brcmf_notify_connect_status);
5306 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5307 brcmf_notify_roaming_status);
5308 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5309 brcmf_notify_mic_status);
5310 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5311 brcmf_notify_connect_status);
5312 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5313 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005314 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5315 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005316 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005317 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005318 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5319 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005320 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5321 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005322 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5323 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005324 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5325 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005326}
5327
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005328static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005329{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005330 kfree(cfg->conf);
5331 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005332 kfree(cfg->escan_ioctl_buf);
5333 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005334 kfree(cfg->extra_buf);
5335 cfg->extra_buf = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005336}
5337
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005338static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005339{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005340 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5341 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005342 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005343 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5344 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005345 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005346 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5347 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005348 goto init_priv_mem_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005349
5350 return 0;
5351
5352init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005353 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005354
5355 return -ENOMEM;
5356}
5357
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005358static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005359{
5360 s32 err = 0;
5361
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005362 cfg->scan_request = NULL;
5363 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005364 cfg->active_scan = true; /* we do active scan per default */
5365 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005366 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005367 if (err)
5368 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005369 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005370 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005371 brcmf_init_escan(cfg);
5372 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005373 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005374 return err;
5375}
5376
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005377static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005378{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005379 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005380 brcmf_abort_scanning(cfg);
5381 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005382}
5383
Arend van Sprield3c0b632013-02-08 15:53:37 +01005384static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5385{
5386 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005387 mutex_init(&event->vif_event_lock);
5388}
5389
Hante Meuleman1119e232015-11-25 11:32:42 +01005390static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005391{
Hante Meuleman1119e232015-11-25 11:32:42 +01005392 s32 err;
5393 u32 bcn_timeout;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005394 __le32 roamtrigger[2];
5395 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005396
Hante Meuleman1119e232015-11-25 11:32:42 +01005397 /* Configure beacon timeout value based upon roaming setting */
5398 if (brcmf_roamoff)
5399 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
5400 else
5401 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
5402 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
5403 if (err) {
5404 brcmf_err("bcn_timeout error (%d)\n", err);
5405 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005406 }
5407
Hante Meuleman1119e232015-11-25 11:32:42 +01005408 /* Enable/Disable built-in roaming to allow supplicant to take care of
5409 * roaming.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005410 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005411 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5412 brcmf_roamoff ? "Off" : "On");
5413 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005414 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005415 brcmf_err("roam_off error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005416 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005417 }
5418
Arend van Sprielf588bc02011-10-12 20:51:22 +02005419 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5420 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005421 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005422 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005423 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005424 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005425 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005426 }
5427
Arend van Sprielf588bc02011-10-12 20:51:22 +02005428 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5429 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005430 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005431 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005432 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005433 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005434 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005435 }
5436
Hante Meuleman1119e232015-11-25 11:32:42 +01005437roam_setup_done:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005438 return err;
5439}
5440
5441static s32
Hante Meuleman1678ba82015-12-10 13:43:00 +01005442brcmf_dongle_scantime(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005443{
5444 s32 err = 0;
5445
Arend van Sprielac24be62012-10-22 10:36:23 -07005446 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005447 BRCMF_SCAN_CHANNEL_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005448 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005449 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005450 goto dongle_scantime_out;
5451 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005452 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005453 BRCMF_SCAN_UNASSOC_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005454 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005455 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005456 goto dongle_scantime_out;
5457 }
5458
Arend van Sprielac24be62012-10-22 10:36:23 -07005459 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005460 BRCMF_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005461 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005462 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005463 goto dongle_scantime_out;
5464 }
5465
5466dongle_scantime_out:
5467 return err;
5468}
5469
Arend van Sprielb48d8912014-07-12 08:49:41 +02005470static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5471 struct brcmu_chan *ch)
5472{
5473 u32 ht40_flag;
5474
5475 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5476 if (ch->sb == BRCMU_CHAN_SB_U) {
5477 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5478 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5479 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5480 } else {
5481 /* It should be one of
5482 * IEEE80211_CHAN_NO_HT40 or
5483 * IEEE80211_CHAN_NO_HT40PLUS
5484 */
5485 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5486 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5487 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5488 }
5489}
5490
5491static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5492 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005493{
5494 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005495 struct ieee80211_supported_band *band;
5496 struct ieee80211_channel *channel;
5497 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005498 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005499 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005500 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005501 u8 *pbuf;
5502 u32 i, j;
5503 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005504 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005505 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005506
5507 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5508
5509 if (pbuf == NULL)
5510 return -ENOMEM;
5511
5512 list = (struct brcmf_chanspec_list *)pbuf;
5513
5514 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5515 BRCMF_DCMD_MEDLEN);
5516 if (err) {
5517 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005518 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005519 }
5520
Arend van Sprielb48d8912014-07-12 08:49:41 +02005521 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005522 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5523 if (band)
5524 for (i = 0; i < band->n_channels; i++)
5525 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5526 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5527 if (band)
5528 for (i = 0; i < band->n_channels; i++)
5529 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005530
5531 total = le32_to_cpu(list->count);
5532 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005533 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5534 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005535
Franky Lin83cf17a2013-04-11 13:28:50 +02005536 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005537 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005538 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005539 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005540 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005541 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005542 continue;
5543 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005544 if (!band)
5545 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005546 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005547 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005548 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005549 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005550 ch.bw == BRCMU_CHAN_BW_80)
5551 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005552
5553 channel = band->channels;
5554 index = band->n_channels;
5555 for (j = 0; j < band->n_channels; j++) {
5556 if (channel[j].hw_value == ch.chnum) {
5557 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005558 break;
5559 }
5560 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005561 channel[index].center_freq =
5562 ieee80211_channel_to_frequency(ch.chnum, band->band);
5563 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005564
Arend van Sprielb48d8912014-07-12 08:49:41 +02005565 /* assuming the chanspecs order is HT20,
5566 * HT40 upper, HT40 lower, and VHT80.
5567 */
5568 if (ch.bw == BRCMU_CHAN_BW_80) {
5569 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5570 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5571 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5572 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005573 /* enable the channel and disable other bandwidths
5574 * for now as mentioned order assure they are enabled
5575 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005576 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005577 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5578 IEEE80211_CHAN_NO_80MHZ;
5579 ch.bw = BRCMU_CHAN_BW_20;
5580 cfg->d11inf.encchspec(&ch);
5581 chaninfo = ch.chspec;
5582 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5583 &chaninfo);
5584 if (!err) {
5585 if (chaninfo & WL_CHAN_RADAR)
5586 channel[index].flags |=
5587 (IEEE80211_CHAN_RADAR |
5588 IEEE80211_CHAN_NO_IR);
5589 if (chaninfo & WL_CHAN_PASSIVE)
5590 channel[index].flags |=
5591 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005592 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005593 }
5594 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005595
Arend van Sprielb48d8912014-07-12 08:49:41 +02005596fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005597 kfree(pbuf);
5598 return err;
5599}
5600
Arend van Sprielb48d8912014-07-12 08:49:41 +02005601static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005602{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005603 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5604 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005605 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005606 struct brcmf_chanspec_list *list;
5607 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005608 u32 val;
5609 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005610 struct brcmu_chan ch;
5611 u32 num_chan;
5612 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005613
5614 /* verify support for bw_cap command */
5615 val = WLC_BAND_5G;
5616 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5617
5618 if (!err) {
5619 /* only set 2G bandwidth using bw_cap command */
5620 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5621 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5622 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5623 sizeof(band_bwcap));
5624 } else {
5625 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5626 val = WLC_N_BW_40ALL;
5627 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5628 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005629
5630 if (!err) {
5631 /* update channel info in 2G band */
5632 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5633
5634 if (pbuf == NULL)
5635 return -ENOMEM;
5636
5637 ch.band = BRCMU_CHAN_BAND_2G;
5638 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005639 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005640 ch.chnum = 0;
5641 cfg->d11inf.encchspec(&ch);
5642
5643 /* pass encoded chanspec in query */
5644 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5645
5646 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5647 BRCMF_DCMD_MEDLEN);
5648 if (err) {
5649 brcmf_err("get chanspecs error (%d)\n", err);
5650 kfree(pbuf);
5651 return err;
5652 }
5653
5654 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5655 list = (struct brcmf_chanspec_list *)pbuf;
5656 num_chan = le32_to_cpu(list->count);
5657 for (i = 0; i < num_chan; i++) {
5658 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5659 cfg->d11inf.decchspec(&ch);
5660 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5661 continue;
5662 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5663 continue;
5664 for (j = 0; j < band->n_channels; j++) {
5665 if (band->channels[j].hw_value == ch.chnum)
5666 break;
5667 }
5668 if (WARN_ON(j == band->n_channels))
5669 continue;
5670
5671 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5672 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005673 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005674 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005675 return err;
5676}
5677
Arend van Spriel2375d972014-01-06 12:40:41 +01005678static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5679{
5680 u32 band, mimo_bwcap;
5681 int err;
5682
5683 band = WLC_BAND_2G;
5684 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5685 if (!err) {
5686 bw_cap[IEEE80211_BAND_2GHZ] = band;
5687 band = WLC_BAND_5G;
5688 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5689 if (!err) {
5690 bw_cap[IEEE80211_BAND_5GHZ] = band;
5691 return;
5692 }
5693 WARN_ON(1);
5694 return;
5695 }
5696 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5697 mimo_bwcap = 0;
5698 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5699 if (err)
5700 /* assume 20MHz if firmware does not give a clue */
5701 mimo_bwcap = WLC_N_BW_20ALL;
5702
5703 switch (mimo_bwcap) {
5704 case WLC_N_BW_40ALL:
5705 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5706 /* fall-thru */
5707 case WLC_N_BW_20IN2G_40IN5G:
5708 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5709 /* fall-thru */
5710 case WLC_N_BW_20ALL:
5711 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5712 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5713 break;
5714 default:
5715 brcmf_err("invalid mimo_bw_cap value\n");
5716 }
5717}
Hante Meulemand48200b2013-04-03 12:40:29 +02005718
Arend van Spriel18d6c532014-05-12 10:47:35 +02005719static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5720 u32 bw_cap[2], u32 nchain)
5721{
5722 band->ht_cap.ht_supported = true;
5723 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5724 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5725 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5726 }
5727 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5728 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5729 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5730 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5731 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5732 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5733}
5734
5735static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5736{
5737 u16 mcs_map;
5738 int i;
5739
5740 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5741 mcs_map = (mcs_map << 2) | supp;
5742
5743 return cpu_to_le16(mcs_map);
5744}
5745
5746static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005747 u32 bw_cap[2], u32 nchain, u32 txstreams,
5748 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
Arend van Spriel18d6c532014-05-12 10:47:35 +02005749{
5750 __le16 mcs_map;
5751
5752 /* not allowed in 2.4G band */
5753 if (band->band == IEEE80211_BAND_2GHZ)
5754 return;
5755
5756 band->vht_cap.vht_supported = true;
5757 /* 80MHz is mandatory */
5758 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5759 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5760 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5761 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5762 }
5763 /* all support 256-QAM */
5764 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5765 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5766 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005767
5768 /* Beamforming support information */
5769 if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
5770 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
5771 if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
5772 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
5773 if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
5774 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
5775 if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
5776 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
5777
5778 if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
5779 band->vht_cap.cap |=
5780 (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
5781 band->vht_cap.cap |= ((txstreams - 1) <<
5782 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
5783 band->vht_cap.cap |=
5784 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
5785 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005786}
5787
Arend van Sprielb48d8912014-07-12 08:49:41 +02005788static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005789{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005790 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005791 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005792 u32 nmode = 0;
5793 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005794 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005795 u32 rxchain;
5796 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005797 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005798 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005799 struct ieee80211_supported_band *band;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005800 u32 txstreams = 0;
5801 u32 txbf_bfe_cap = 0;
5802 u32 txbf_bfr_cap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005803
Arend van Spriel18d6c532014-05-12 10:47:35 +02005804 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005805 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5806 if (err) {
5807 brcmf_err("nmode error (%d)\n", err);
5808 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005809 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005810 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005811 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5812 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5813 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005814
Daniel Kim4aca7a12014-02-25 20:30:36 +01005815 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5816 if (err) {
5817 brcmf_err("rxchain error (%d)\n", err);
5818 nchain = 1;
5819 } else {
5820 for (nchain = 0; rxchain; nchain++)
5821 rxchain = rxchain & (rxchain - 1);
5822 }
5823 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5824
Arend van Sprielb48d8912014-07-12 08:49:41 +02005825 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005826 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005827 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005828 return err;
5829 }
5830
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005831 if (vhtmode) {
5832 (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
5833 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
5834 &txbf_bfe_cap);
5835 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
5836 &txbf_bfr_cap);
5837 }
5838
Arend van Sprielb48d8912014-07-12 08:49:41 +02005839 wiphy = cfg_to_wiphy(cfg);
5840 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5841 band = wiphy->bands[i];
5842 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005843 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005844
Arend van Spriel18d6c532014-05-12 10:47:35 +02005845 if (nmode)
5846 brcmf_update_ht_cap(band, bw_cap, nchain);
5847 if (vhtmode)
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005848 brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
5849 txbf_bfe_cap, txbf_bfr_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005850 }
5851
Arend van Sprielb48d8912014-07-12 08:49:41 +02005852 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005853}
5854
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005855static const struct ieee80211_txrx_stypes
5856brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5857 [NL80211_IFTYPE_STATION] = {
5858 .tx = 0xffff,
5859 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5860 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5861 },
5862 [NL80211_IFTYPE_P2P_CLIENT] = {
5863 .tx = 0xffff,
5864 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5865 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5866 },
5867 [NL80211_IFTYPE_P2P_GO] = {
5868 .tx = 0xffff,
5869 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5870 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5871 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5872 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5873 BIT(IEEE80211_STYPE_AUTH >> 4) |
5874 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5875 BIT(IEEE80211_STYPE_ACTION >> 4)
5876 },
5877 [NL80211_IFTYPE_P2P_DEVICE] = {
5878 .tx = 0xffff,
5879 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5880 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5881 }
5882};
5883
Arend van Spriel0882dda2015-08-20 22:06:03 +02005884/**
5885 * brcmf_setup_ifmodes() - determine interface modes and combinations.
5886 *
5887 * @wiphy: wiphy object.
5888 * @ifp: interface object needed for feat module api.
5889 *
5890 * The interface modes and combinations are determined dynamically here
5891 * based on firmware functionality.
5892 *
5893 * no p2p and no mbss:
5894 *
5895 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5896 *
5897 * no p2p and mbss:
5898 *
5899 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5900 * #AP <= 4, matching BI, channels = 1, 4 total
5901 *
5902 * p2p, no mchan, and mbss:
5903 *
5904 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
5905 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5906 * #AP <= 4, matching BI, channels = 1, 4 total
5907 *
5908 * p2p, mchan, and mbss:
5909 *
5910 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
5911 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5912 * #AP <= 4, matching BI, channels = 1, 4 total
5913 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005914static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5915{
5916 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02005917 struct ieee80211_iface_limit *c0_limits = NULL;
5918 struct ieee80211_iface_limit *p2p_limits = NULL;
5919 struct ieee80211_iface_limit *mbss_limits = NULL;
5920 bool mbss, p2p;
5921 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005922
Arend van Spriel0882dda2015-08-20 22:06:03 +02005923 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
5924 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
5925
5926 n_combos = 1 + !!p2p + !!mbss;
5927 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005928 if (!combo)
5929 goto err;
5930
Arend van Spriel0882dda2015-08-20 22:06:03 +02005931 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
5932 if (!c0_limits)
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005933 goto err;
5934
Arend van Spriel0882dda2015-08-20 22:06:03 +02005935 if (p2p) {
5936 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
5937 if (!p2p_limits)
5938 goto err;
5939 }
5940
5941 if (mbss) {
5942 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
5943 if (!mbss_limits)
5944 goto err;
5945 }
5946
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005947 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5948 BIT(NL80211_IFTYPE_ADHOC) |
5949 BIT(NL80211_IFTYPE_AP);
5950
Arend van Spriel0882dda2015-08-20 22:06:03 +02005951 c = 0;
5952 i = 0;
5953 combo[c].num_different_channels = 1;
5954 c0_limits[i].max = 1;
5955 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5956 if (p2p) {
5957 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5958 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005959 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5960 BIT(NL80211_IFTYPE_P2P_GO) |
5961 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02005962 c0_limits[i].max = 1;
5963 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5964 c0_limits[i].max = 1;
5965 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5966 BIT(NL80211_IFTYPE_P2P_GO);
5967 } else {
5968 c0_limits[i].max = 1;
5969 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005970 }
Arend van Spriel0882dda2015-08-20 22:06:03 +02005971 combo[c].max_interfaces = i;
5972 combo[c].n_limits = i;
5973 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005974
Arend van Spriel0882dda2015-08-20 22:06:03 +02005975 if (p2p) {
5976 c++;
5977 i = 0;
5978 combo[c].num_different_channels = 1;
5979 p2p_limits[i].max = 1;
5980 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5981 p2p_limits[i].max = 1;
5982 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
5983 p2p_limits[i].max = 1;
5984 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
5985 p2p_limits[i].max = 1;
5986 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5987 combo[c].max_interfaces = i;
5988 combo[c].n_limits = i;
5989 combo[c].limits = p2p_limits;
5990 }
5991
5992 if (mbss) {
5993 c++;
5994 combo[c].beacon_int_infra_match = true;
5995 combo[c].num_different_channels = 1;
5996 mbss_limits[0].max = 4;
5997 mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
5998 combo[c].max_interfaces = 4;
5999 combo[c].n_limits = 1;
6000 combo[c].limits = mbss_limits;
6001 }
6002 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006003 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006004 return 0;
6005
6006err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02006007 kfree(c0_limits);
6008 kfree(p2p_limits);
6009 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006010 kfree(combo);
6011 return -ENOMEM;
6012}
6013
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006014static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
6015{
6016 /* scheduled scan settings */
6017 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
6018 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
6019 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
6020 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
6021}
6022
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006023#ifdef CONFIG_PM
6024static const struct wiphy_wowlan_support brcmf_wowlan_support = {
6025 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01006026 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
6027 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
6028 .pattern_min_len = 1,
6029 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006030};
6031#endif
6032
6033static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
6034{
6035#ifdef CONFIG_PM
6036 /* wowl settings */
6037 wiphy->wowlan = &brcmf_wowlan_support;
6038#endif
6039}
6040
Arend van Sprielb48d8912014-07-12 08:49:41 +02006041static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006042{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006043 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006044 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006045 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006046 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006047 __le32 bandlist[3];
6048 u32 n_bands;
6049 int err, i;
6050
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006051 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
6052 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
Hante Meuleman6c404f32015-12-10 13:43:03 +01006053 wiphy->max_num_pmkids = BRCMF_MAXPMKID;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006054
6055 err = brcmf_setup_ifmodes(wiphy, ifp);
6056 if (err)
6057 return err;
6058
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006059 for (i = 0, combo = wiphy->iface_combinations;
6060 i < wiphy->n_iface_combinations; i++, combo++) {
6061 max_interfaces = max(max_interfaces, combo->max_interfaces);
6062 }
6063
6064 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
6065 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006066 u8 *addr = drvr->addresses[i].addr;
6067
6068 memcpy(addr, drvr->mac, ETH_ALEN);
6069 if (i) {
6070 addr[0] |= BIT(1);
6071 addr[ETH_ALEN - 1] ^= i;
6072 }
6073 }
6074 wiphy->addresses = drvr->addresses;
6075 wiphy->n_addresses = i;
6076
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006077 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
6078 wiphy->cipher_suites = __wl_cipher_suites;
6079 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
6080 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
6081 WIPHY_FLAG_OFFCHAN_TX |
Hante Meulemana7b82d42015-12-10 13:43:04 +01006082 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
6083 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
6084 wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006085 if (!brcmf_roamoff)
6086 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
6087 wiphy->mgmt_stypes = brcmf_txrx_stypes;
6088 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02006089 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
6090 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006091
6092 /* vendor commands/events support */
6093 wiphy->vendor_commands = brcmf_vendor_cmds;
6094 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
6095
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006096 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
6097 brcmf_wiphy_wowl_params(wiphy);
6098
Arend van Spriel58de92d2015-04-14 20:10:24 +02006099 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
6100 sizeof(bandlist));
6101 if (err) {
6102 brcmf_err("could not obtain band info: err=%d\n", err);
6103 return err;
6104 }
6105 /* first entry in bandlist is number of bands */
6106 n_bands = le32_to_cpu(bandlist[0]);
6107 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
6108 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
6109 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
6110 GFP_KERNEL);
6111 if (!band)
6112 return -ENOMEM;
6113
6114 band->channels = kmemdup(&__wl_2ghz_channels,
6115 sizeof(__wl_2ghz_channels),
6116 GFP_KERNEL);
6117 if (!band->channels) {
6118 kfree(band);
6119 return -ENOMEM;
6120 }
6121
6122 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
6123 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
6124 }
6125 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
6126 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
6127 GFP_KERNEL);
6128 if (!band)
6129 return -ENOMEM;
6130
6131 band->channels = kmemdup(&__wl_5ghz_channels,
6132 sizeof(__wl_5ghz_channels),
6133 GFP_KERNEL);
6134 if (!band->channels) {
6135 kfree(band);
6136 return -ENOMEM;
6137 }
6138
6139 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
6140 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
6141 }
6142 }
6143 err = brcmf_setup_wiphybands(wiphy);
6144 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006145}
6146
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006147static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006148{
6149 struct net_device *ndev;
6150 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01006151 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006152 s32 power_mode;
6153 s32 err = 0;
6154
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006155 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006156 return err;
6157
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006158 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006159 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006160 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006161
Hante Meuleman40a23292013-01-02 15:22:51 +01006162 /* make sure RF is ready for work */
6163 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6164
Hante Meuleman1678ba82015-12-10 13:43:00 +01006165 brcmf_dongle_scantime(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006166
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006167 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006168 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006169 if (err)
6170 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006171 brcmf_dbg(INFO, "power save set to %s\n",
6172 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006173
Hante Meuleman1119e232015-11-25 11:32:42 +01006174 err = brcmf_dongle_roam(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006175 if (err)
6176 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006177 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
6178 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006179 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006180 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006181
Hante Meulemanb3657452013-05-27 21:09:53 +02006182 brcmf_configure_arp_offload(ifp, true);
6183
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006184 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006185default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006186
6187 return err;
6188
6189}
6190
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006191static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006192{
Arend van Sprielc1179032012-10-22 13:55:33 -07006193 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006194
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006195 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006196}
6197
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006198static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006199{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006200 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006201
Arend van Spriel5b435de2011-10-05 13:19:03 +02006202 /*
6203 * While going down, if associated with AP disassociate
6204 * from AP to save power
6205 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006206 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006207 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006208
6209 /* Make sure WPA_Supplicant receives all the event
6210 generated due to DISASSOC call to the fw to keep
6211 the state fw and WPA_Supplicant state consistent
6212 */
6213 brcmf_delay(500);
6214 }
6215
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006216 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006217 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006218
Arend van Spriel5b435de2011-10-05 13:19:03 +02006219 return 0;
6220}
6221
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006222s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006223{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006224 struct brcmf_if *ifp = netdev_priv(ndev);
6225 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006226 s32 err = 0;
6227
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006228 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006229 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006230 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006231
6232 return err;
6233}
6234
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006235s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006236{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006237 struct brcmf_if *ifp = netdev_priv(ndev);
6238 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006239 s32 err = 0;
6240
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006241 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006242 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006243 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006244
6245 return err;
6246}
6247
Arend van Spriela7965fb2013-04-11 17:08:37 +02006248enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6249{
6250 struct wireless_dev *wdev = &ifp->vif->wdev;
6251
6252 return wdev->iftype;
6253}
6254
Hante Meulemanbfe81972014-10-28 14:56:16 +01006255bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6256 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006257{
6258 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006259
6260 list_for_each_entry(vif, &cfg->vif_list, list) {
6261 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006262 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006263 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006264 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006265}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006266
6267static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6268 u8 action)
6269{
6270 u8 evt_action;
6271
6272 mutex_lock(&event->vif_event_lock);
6273 evt_action = event->action;
6274 mutex_unlock(&event->vif_event_lock);
6275 return evt_action == action;
6276}
6277
6278void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6279 struct brcmf_cfg80211_vif *vif)
6280{
6281 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6282
6283 mutex_lock(&event->vif_event_lock);
6284 event->vif = vif;
6285 event->action = 0;
6286 mutex_unlock(&event->vif_event_lock);
6287}
6288
6289bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6290{
6291 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6292 bool armed;
6293
6294 mutex_lock(&event->vif_event_lock);
6295 armed = event->vif != NULL;
6296 mutex_unlock(&event->vif_event_lock);
6297
6298 return armed;
6299}
6300int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6301 u8 action, ulong timeout)
6302{
6303 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6304
6305 return wait_event_timeout(event->vif_wq,
6306 vif_event_equals(event, action), timeout);
6307}
6308
Arend van Spriel63db1a42014-12-21 12:43:51 +01006309static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6310 struct regulatory_request *req)
6311{
6312 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6313 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6314 struct brcmf_fil_country_le ccreq;
6315 int i;
6316
6317 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6318 req->alpha2[0], req->alpha2[1]);
6319
6320 /* ignore non-ISO3166 country codes */
6321 for (i = 0; i < sizeof(req->alpha2); i++)
6322 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6323 brcmf_err("not a ISO3166 code\n");
6324 return;
6325 }
6326 memset(&ccreq, 0, sizeof(ccreq));
6327 ccreq.rev = cpu_to_le32(-1);
6328 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006329 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6330 brcmf_err("firmware rejected country setting\n");
6331 return;
6332 }
6333 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006334}
6335
Arend van Sprielb48d8912014-07-12 08:49:41 +02006336static void brcmf_free_wiphy(struct wiphy *wiphy)
6337{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006338 int i;
6339
Arend van Spriel58de92d2015-04-14 20:10:24 +02006340 if (!wiphy)
6341 return;
6342
Arend van Spriel0882dda2015-08-20 22:06:03 +02006343 if (wiphy->iface_combinations) {
6344 for (i = 0; i < wiphy->n_iface_combinations; i++)
6345 kfree(wiphy->iface_combinations[i].limits);
6346 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006347 kfree(wiphy->iface_combinations);
6348 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6349 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6350 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6351 }
6352 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6353 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6354 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6355 }
6356 wiphy_free(wiphy);
6357}
6358
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006359struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006360 struct device *busdev,
6361 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006362{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006363 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006364 struct brcmf_cfg80211_info *cfg;
6365 struct wiphy *wiphy;
6366 struct brcmf_cfg80211_vif *vif;
6367 struct brcmf_if *ifp;
6368 s32 err = 0;
6369 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006370 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006371
6372 if (!ndev) {
6373 brcmf_err("ndev is invalid\n");
6374 return NULL;
6375 }
6376
6377 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006378 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6379 if (!wiphy) {
6380 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006381 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006382 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006383 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006384 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006385
6386 cfg = wiphy_priv(wiphy);
6387 cfg->wiphy = wiphy;
6388 cfg->pub = drvr;
6389 init_vif_event(&cfg->vif_event);
6390 INIT_LIST_HEAD(&cfg->vif_list);
6391
6392 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006393 if (IS_ERR(vif))
6394 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006395
6396 vif->ifp = ifp;
6397 vif->wdev.netdev = ndev;
6398 ndev->ieee80211_ptr = &vif->wdev;
6399 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6400
6401 err = wl_init_priv(cfg);
6402 if (err) {
6403 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006404 brcmf_free_vif(vif);
6405 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006406 }
6407 ifp->vif = vif;
6408
Arend van Sprielb48d8912014-07-12 08:49:41 +02006409 /* determine d11 io type before wiphy setup */
6410 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006411 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006412 brcmf_err("Failed to get D11 version (%d)\n", err);
6413 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006414 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006415 cfg->d11inf.io_type = (u8)io_type;
6416 brcmu_d11_attach(&cfg->d11inf);
6417
6418 err = brcmf_setup_wiphy(wiphy, ifp);
6419 if (err < 0)
6420 goto priv_out;
6421
6422 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006423 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006424 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6425 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6426
6427 /* firmware defaults to 40MHz disabled in 2G band. We signal
6428 * cfg80211 here that we do and have it decide we can enable
6429 * it. But first check if device does support 2G operation.
6430 */
6431 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6432 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6433 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6434 }
6435 err = wiphy_register(wiphy);
6436 if (err < 0) {
6437 brcmf_err("Could not register wiphy device (%d)\n", err);
6438 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006439 }
6440
6441 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6442 * setup 40MHz in 2GHz band and enable OBSS scanning.
6443 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006444 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6445 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006446 if (!err)
6447 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6448 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006449 else
6450 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006451 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006452 /* p2p might require that "if-events" get processed by fweh. So
6453 * activate the already registered event handlers now and activate
6454 * the rest when initialization has completed. drvr->config needs to
6455 * be assigned before activating events.
6456 */
6457 drvr->config = cfg;
6458 err = brcmf_fweh_activate_events(ifp);
6459 if (err) {
6460 brcmf_err("FWEH activation failed (%d)\n", err);
6461 goto wiphy_unreg_out;
6462 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006463
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006464 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006465 if (err) {
6466 brcmf_err("P2P initilisation failed (%d)\n", err);
6467 goto wiphy_unreg_out;
6468 }
6469 err = brcmf_btcoex_attach(cfg);
6470 if (err) {
6471 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6472 brcmf_p2p_detach(&cfg->p2p);
6473 goto wiphy_unreg_out;
6474 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006475
Hante Meulemana7b82d42015-12-10 13:43:04 +01006476 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
6477 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6478 if (err) {
6479 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6480 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
6481 } else {
6482 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6483 brcmf_notify_tdls_peer_event);
6484 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006485 }
6486
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006487 /* (re-) activate FWEH event handling */
6488 err = brcmf_fweh_activate_events(ifp);
6489 if (err) {
6490 brcmf_err("FWEH activation failed (%d)\n", err);
6491 goto wiphy_unreg_out;
6492 }
6493
Hante Meuleman48ed16e2016-01-02 09:41:38 +01006494 /* Fill in some of the advertised nl80211 supported features */
6495 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
6496 wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
6497#ifdef CONFIG_PM
6498 if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
6499 wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
6500#endif
6501 }
6502
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006503 return cfg;
6504
Arend van Sprielb48d8912014-07-12 08:49:41 +02006505wiphy_unreg_out:
6506 wiphy_unregister(cfg->wiphy);
6507priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006508 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006509 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006510 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006511wiphy_out:
6512 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006513 return NULL;
6514}
6515
6516void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6517{
6518 if (!cfg)
6519 return;
6520
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006521 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006522 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006523 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006524 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006525}