blob: 18293377f338479a611cd907e6750969b72f52ea [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
18
19#include <linux/kernel.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <linux/etherdevice.h>
Hante Meuleman68ca3952014-02-25 20:30:26 +010021#include <linux/module.h>
Franky Lin1bacb042014-06-21 12:11:16 +020022#include <linux/vmalloc.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020024#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020025
26#include <brcmu_utils.h>
27#include <defs.h>
28#include <brcmu_wifi.h>
Hante Meuleman122d3d02014-10-28 14:56:18 +010029#include "core.h"
Hante Meulemana8e8ed32014-10-28 14:56:13 +010030#include "debug.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020031#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010032#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010033#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020034#include "btcoex.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010035#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020036#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070037#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020038#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020039#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010040#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010041#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020042
Arend van Spriele5806072012-09-19 22:21:08 +020043#define BRCMF_SCAN_IE_LEN_MAX 2048
44#define BRCMF_PNO_VERSION 2
45#define BRCMF_PNO_TIME 30
46#define BRCMF_PNO_REPEAT 4
47#define BRCMF_PNO_FREQ_EXPO_MAX 3
48#define BRCMF_PNO_MAX_PFN_COUNT 16
49#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
50#define BRCMF_PNO_HIDDEN_BIT 2
51#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
52#define BRCMF_PNO_SCAN_COMPLETE 1
53#define BRCMF_PNO_SCAN_INCOMPLETE 0
54
Hante Meuleman1a873342012-09-27 14:17:54 +020055#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010059#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020060
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
75#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
76#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
77
78#define VNDR_IE_CMD_LEN 4 /* length of the set command
79 * string :"add", "del" (+ NUL)
80 */
81#define VNDR_IE_COUNT_OFFSET 4
82#define VNDR_IE_PKTFLAG_OFFSET 8
83#define VNDR_IE_VSIE_OFFSET 12
84#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010085#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020086
87#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
88#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020089
Hante Meuleman89286dc2013-02-08 15:53:46 +010090#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
91#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
92#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
93
Arend van Spriel5b435de2011-10-05 13:19:03 +020094#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
95 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
96
Arend van Sprielce81e312012-10-22 13:55:37 -070097static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +020098{
Arend van Sprielc1179032012-10-22 13:55:33 -070099 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100100 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
101 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200102 return false;
103 }
104 return true;
105}
106
Arend van Spriel5b435de2011-10-05 13:19:03 +0200107#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
108#define RATETAB_ENT(_rateid, _flags) \
109 { \
110 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
111 .hw_value = (_rateid), \
112 .flags = (_flags), \
113 }
114
115static struct ieee80211_rate __wl_rates[] = {
116 RATETAB_ENT(BRCM_RATE_1M, 0),
117 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
118 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
119 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
120 RATETAB_ENT(BRCM_RATE_6M, 0),
121 RATETAB_ENT(BRCM_RATE_9M, 0),
122 RATETAB_ENT(BRCM_RATE_12M, 0),
123 RATETAB_ENT(BRCM_RATE_18M, 0),
124 RATETAB_ENT(BRCM_RATE_24M, 0),
125 RATETAB_ENT(BRCM_RATE_36M, 0),
126 RATETAB_ENT(BRCM_RATE_48M, 0),
127 RATETAB_ENT(BRCM_RATE_54M, 0),
128};
129
Arend van Spriel5b435de2011-10-05 13:19:03 +0200130#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200131#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
132#define wl_a_rates (__wl_rates + 4)
133#define wl_a_rates_size (wl_g_rates_size - 4)
134
135#define CHAN2G(_channel, _freq) { \
136 .band = IEEE80211_BAND_2GHZ, \
137 .center_freq = (_freq), \
138 .hw_value = (_channel), \
139 .flags = IEEE80211_CHAN_DISABLED, \
140 .max_antenna_gain = 0, \
141 .max_power = 30, \
142}
143
144#define CHAN5G(_channel) { \
145 .band = IEEE80211_BAND_5GHZ, \
146 .center_freq = 5000 + (5 * (_channel)), \
147 .hw_value = (_channel), \
148 .flags = IEEE80211_CHAN_DISABLED, \
149 .max_antenna_gain = 0, \
150 .max_power = 30, \
151}
152
153static struct ieee80211_channel __wl_2ghz_channels[] = {
154 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
155 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
156 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
157 CHAN2G(13, 2472), CHAN2G(14, 2484)
158};
159
160static struct ieee80211_channel __wl_5ghz_channels[] = {
161 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
162 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
163 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
164 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
165 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
166 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
167};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200168
Arend van Sprielb48d8912014-07-12 08:49:41 +0200169/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200170 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200171 */
172static const struct ieee80211_supported_band __wl_band_2ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200173 .band = IEEE80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200174 .bitrates = wl_g_rates,
175 .n_bitrates = wl_g_rates_size,
176};
177
Arend van Spriel58de92d2015-04-14 20:10:24 +0200178static const struct ieee80211_supported_band __wl_band_5ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200179 .band = IEEE80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200180 .bitrates = wl_a_rates,
181 .n_bitrates = wl_a_rates_size,
182};
183
Hante Meulemand48200b2013-04-03 12:40:29 +0200184/* This is to override regulatory domains defined in cfg80211 module (reg.c)
185 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200186 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
187 * With respect to these flags, wpa_supplicant doesn't * start p2p
188 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200189 * domain are to be done here.
190 */
191static const struct ieee80211_regdomain brcmf_regdom = {
192 .n_reg_rules = 4,
193 .alpha2 = "99",
194 .reg_rules = {
195 /* IEEE 802.11b/g, channels 1..11 */
196 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
197 /* If any */
198 /* IEEE 802.11 channel 14 - Only JP enables
199 * this and for 802.11b only
200 */
201 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
202 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200203 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200204 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200205 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200206};
207
208static const u32 __wl_cipher_suites[] = {
209 WLAN_CIPHER_SUITE_WEP40,
210 WLAN_CIPHER_SUITE_WEP104,
211 WLAN_CIPHER_SUITE_TKIP,
212 WLAN_CIPHER_SUITE_CCMP,
213 WLAN_CIPHER_SUITE_AES_CMAC,
214};
215
Hante Meuleman1a873342012-09-27 14:17:54 +0200216/* Vendor specific ie. id = 221, oui and type defines exact ie */
217struct brcmf_vs_tlv {
218 u8 id;
219 u8 len;
220 u8 oui[3];
221 u8 oui_type;
222};
223
224struct parsed_vndr_ie_info {
225 u8 *ie_ptr;
226 u32 ie_len; /* total length including id & length field */
227 struct brcmf_vs_tlv vndrie;
228};
229
230struct parsed_vndr_ies {
231 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100232 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200233};
234
Hante Meuleman68ca3952014-02-25 20:30:26 +0100235static int brcmf_roamoff;
236module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
237MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
238
Alwin Beukersef6ac172011-10-12 20:51:26 +0200239
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200240static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
241 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200242{
243 struct brcmu_chan ch_inf;
244 s32 primary_offset;
245
246 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
247 ch->chan->center_freq, ch->center_freq1, ch->width);
248 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
249 primary_offset = ch->center_freq1 - ch->chan->center_freq;
250 switch (ch->width) {
251 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100252 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200253 ch_inf.bw = BRCMU_CHAN_BW_20;
254 WARN_ON(primary_offset != 0);
255 break;
256 case NL80211_CHAN_WIDTH_40:
257 ch_inf.bw = BRCMU_CHAN_BW_40;
258 if (primary_offset < 0)
259 ch_inf.sb = BRCMU_CHAN_SB_U;
260 else
261 ch_inf.sb = BRCMU_CHAN_SB_L;
262 break;
263 case NL80211_CHAN_WIDTH_80:
264 ch_inf.bw = BRCMU_CHAN_BW_80;
265 if (primary_offset < 0) {
266 if (primary_offset < -CH_10MHZ_APART)
267 ch_inf.sb = BRCMU_CHAN_SB_UU;
268 else
269 ch_inf.sb = BRCMU_CHAN_SB_UL;
270 } else {
271 if (primary_offset > CH_10MHZ_APART)
272 ch_inf.sb = BRCMU_CHAN_SB_LL;
273 else
274 ch_inf.sb = BRCMU_CHAN_SB_LU;
275 }
276 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100277 case NL80211_CHAN_WIDTH_80P80:
278 case NL80211_CHAN_WIDTH_160:
279 case NL80211_CHAN_WIDTH_5:
280 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200281 default:
282 WARN_ON_ONCE(1);
283 }
284 switch (ch->chan->band) {
285 case IEEE80211_BAND_2GHZ:
286 ch_inf.band = BRCMU_CHAN_BAND_2G;
287 break;
288 case IEEE80211_BAND_5GHZ:
289 ch_inf.band = BRCMU_CHAN_BAND_5G;
290 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100291 case IEEE80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200292 default:
293 WARN_ON_ONCE(1);
294 }
295 d11inf->encchspec(&ch_inf);
296
297 return ch_inf.chspec;
298}
299
Franky Lin83cf17a2013-04-11 13:28:50 +0200300u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
301 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700302{
Franky Lin83cf17a2013-04-11 13:28:50 +0200303 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700304
Franky Lin83cf17a2013-04-11 13:28:50 +0200305 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
306 ch_inf.bw = BRCMU_CHAN_BW_20;
307 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700308
Franky Lin83cf17a2013-04-11 13:28:50 +0200309 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700310}
311
Hante Meuleman89286dc2013-02-08 15:53:46 +0100312/* Traverse a string of 1-byte tag/1-byte length/variable-length value
313 * triples, returning a pointer to the substring whose first element
314 * matches tag
315 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100316const struct brcmf_tlv *
317brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100318{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100319 const struct brcmf_tlv *elt = buf;
320 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100321
322 /* find tagged parameter */
323 while (totlen >= TLV_HDR_LEN) {
324 int len = elt->len;
325
326 /* validate remaining totlen */
327 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
328 return elt;
329
330 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
331 totlen -= (len + TLV_HDR_LEN);
332 }
333
334 return NULL;
335}
336
337/* Is any of the tlvs the expected entry? If
338 * not update the tlvs buffer pointer/length.
339 */
340static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100341brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
342 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100343{
344 /* If the contents match the OUI and the type */
345 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
346 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
347 type == ie[TLV_BODY_OFF + oui_len]) {
348 return true;
349 }
350
351 if (tlvs == NULL)
352 return false;
353 /* point to the next ie */
354 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
355 /* calculate the length of the rest of the buffer */
356 *tlvs_len -= (int)(ie - *tlvs);
357 /* update the pointer to the start of the buffer */
358 *tlvs = ie;
359
360 return false;
361}
362
363static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100364brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100365{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100366 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100367
368 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100369 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100370 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
371 return (struct brcmf_vs_tlv *)ie;
372 }
373 return NULL;
374}
375
376static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100377brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100378{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100379 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100380
381 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
382 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
383 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
384 return (struct brcmf_vs_tlv *)ie;
385 }
386 return NULL;
387}
388
Arend van Spriel39504a22015-08-20 22:06:05 +0200389static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
390 struct brcmf_cfg80211_vif *vif,
391 enum nl80211_iftype new_type)
392{
393 int iftype_num[NUM_NL80211_IFTYPES];
394 struct brcmf_cfg80211_vif *pos;
395
396 memset(&iftype_num[0], 0, sizeof(iftype_num));
397 list_for_each_entry(pos, &cfg->vif_list, list)
398 if (pos == vif)
399 iftype_num[new_type]++;
400 else
401 iftype_num[pos->wdev.iftype]++;
402
403 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
404}
405
406static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
407 enum nl80211_iftype new_type)
408{
409 int iftype_num[NUM_NL80211_IFTYPES];
410 struct brcmf_cfg80211_vif *pos;
411
412 memset(&iftype_num[0], 0, sizeof(iftype_num));
413 list_for_each_entry(pos, &cfg->vif_list, list)
414 iftype_num[pos->wdev.iftype]++;
415
416 iftype_num[new_type]++;
417 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
418}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100419
Arend van Spriel5b435de2011-10-05 13:19:03 +0200420static void convert_key_from_CPU(struct brcmf_wsec_key *key,
421 struct brcmf_wsec_key_le *key_le)
422{
423 key_le->index = cpu_to_le32(key->index);
424 key_le->len = cpu_to_le32(key->len);
425 key_le->algo = cpu_to_le32(key->algo);
426 key_le->flags = cpu_to_le32(key->flags);
427 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
428 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
429 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
430 memcpy(key_le->data, key->data, sizeof(key->data));
431 memcpy(key_le->ea, key->ea, sizeof(key->ea));
432}
433
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200434static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100435send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200436{
437 int err;
438 struct brcmf_wsec_key_le key_le;
439
440 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200441
Hante Meuleman118eb302014-12-21 12:43:49 +0100442 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700443
Hante Meuleman118eb302014-12-21 12:43:49 +0100444 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700445 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200446
Arend van Spriel5b435de2011-10-05 13:19:03 +0200447 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100448 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200449 return err;
450}
451
Hante Meulemanb3657452013-05-27 21:09:53 +0200452static s32
453brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
454{
455 s32 err;
456 u32 mode;
457
458 if (enable)
459 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
460 else
461 mode = 0;
462
463 /* Try to set and enable ARP offload feature, this may fail, then it */
464 /* is simply not supported and err 0 will be returned */
465 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
466 if (err) {
467 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
468 mode, err);
469 err = 0;
470 } else {
471 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
472 if (err) {
473 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
474 enable, err);
475 err = 0;
476 } else
477 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
478 enable, mode);
479 }
480
481 return err;
482}
483
Hante Meuleman8851cce2014-07-30 13:20:02 +0200484static void
485brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
486{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200487 struct brcmf_cfg80211_vif *vif;
488 struct brcmf_if *ifp;
489
490 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
491 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200492
493 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
494 (wdev->iftype == NL80211_IFTYPE_AP) ||
495 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
496 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
497 ADDR_DIRECT);
498 else
499 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
500 ADDR_INDIRECT);
501}
502
Hante Meulemana44aa402014-12-03 21:05:33 +0100503static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
504{
505 struct brcmf_mbss_ssid_le mbss_ssid_le;
506 int bsscfgidx;
507 int err;
508
509 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
510 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
511 if (bsscfgidx < 0)
512 return bsscfgidx;
513
514 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
515 mbss_ssid_le.SSID_len = cpu_to_le32(5);
516 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
517
518 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
519 sizeof(mbss_ssid_le));
520 if (err < 0)
521 brcmf_err("setting ssid failed %d\n", err);
522
523 return err;
524}
525
526/**
527 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
528 *
529 * @wiphy: wiphy device of new interface.
530 * @name: name of the new interface.
531 * @flags: not used.
532 * @params: contains mac address for AP device.
533 */
534static
535struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
536 u32 *flags, struct vif_params *params)
537{
538 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
539 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
540 struct brcmf_cfg80211_vif *vif;
541 int err;
542
543 if (brcmf_cfg80211_vif_event_armed(cfg))
544 return ERR_PTR(-EBUSY);
545
546 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
547
548 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
549 if (IS_ERR(vif))
550 return (struct wireless_dev *)vif;
551
552 brcmf_cfg80211_arm_vif_event(cfg, vif);
553
554 err = brcmf_cfg80211_request_ap_if(ifp);
555 if (err) {
556 brcmf_cfg80211_arm_vif_event(cfg, NULL);
557 goto fail;
558 }
559
560 /* wait for firmware event */
561 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
562 msecs_to_jiffies(1500));
563 brcmf_cfg80211_arm_vif_event(cfg, NULL);
564 if (!err) {
565 brcmf_err("timeout occurred\n");
566 err = -EIO;
567 goto fail;
568 }
569
570 /* interface created in firmware */
571 ifp = vif->ifp;
572 if (!ifp) {
573 brcmf_err("no if pointer provided\n");
574 err = -ENOENT;
575 goto fail;
576 }
577
578 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
579 err = brcmf_net_attach(ifp, true);
580 if (err) {
581 brcmf_err("Registering netdevice failed\n");
582 goto fail;
583 }
584
585 return &ifp->vif->wdev;
586
587fail:
588 brcmf_free_vif(vif);
589 return ERR_PTR(err);
590}
591
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100592static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
593{
594 enum nl80211_iftype iftype;
595
596 iftype = vif->wdev.iftype;
597 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
598}
599
600static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
601{
602 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
603}
604
Arend van Spriel9f440b72013-02-08 15:53:36 +0100605static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
606 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100607 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100608 enum nl80211_iftype type,
609 u32 *flags,
610 struct vif_params *params)
611{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200612 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200613 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200614
Arend van Spriel9f440b72013-02-08 15:53:36 +0100615 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200616 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
617 if (err) {
618 brcmf_err("iface validation failed: err=%d\n", err);
619 return ERR_PTR(err);
620 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100621 switch (type) {
622 case NL80211_IFTYPE_ADHOC:
623 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100624 case NL80211_IFTYPE_AP_VLAN:
625 case NL80211_IFTYPE_WDS:
626 case NL80211_IFTYPE_MONITOR:
627 case NL80211_IFTYPE_MESH_POINT:
628 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100629 case NL80211_IFTYPE_AP:
630 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
631 if (!IS_ERR(wdev))
632 brcmf_cfg80211_update_proto_addr_mode(wdev);
633 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100634 case NL80211_IFTYPE_P2P_CLIENT:
635 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200636 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100637 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200638 if (!IS_ERR(wdev))
639 brcmf_cfg80211_update_proto_addr_mode(wdev);
640 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100641 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100642 default:
643 return ERR_PTR(-EINVAL);
644 }
645}
646
Daniel Kim5e787f72014-06-21 12:11:18 +0200647static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
648{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200649 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200650 brcmf_set_mpc(ifp, mpc);
651}
652
Arend van Sprielf96aa072013-04-05 10:57:48 +0200653void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100654{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100655 s32 err = 0;
656
657 if (check_vif_up(ifp->vif)) {
658 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
659 if (err) {
660 brcmf_err("fail to set mpc\n");
661 return;
662 }
663 brcmf_dbg(INFO, "MPC : %d\n", mpc);
664 }
665}
666
Arend van Spriela0f472a2013-04-05 10:57:49 +0200667s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
668 struct brcmf_if *ifp, bool aborted,
669 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100670{
671 struct brcmf_scan_params_le params_le;
672 struct cfg80211_scan_request *scan_request;
673 s32 err = 0;
674
675 brcmf_dbg(SCAN, "Enter\n");
676
677 /* clear scan request, because the FW abort can cause a second call */
678 /* to this functon and might cause a double cfg80211_scan_done */
679 scan_request = cfg->scan_request;
680 cfg->scan_request = NULL;
681
682 if (timer_pending(&cfg->escan_timeout))
683 del_timer_sync(&cfg->escan_timeout);
684
685 if (fw_abort) {
686 /* Do a scan abort to stop the driver's scan engine */
687 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
688 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800689 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100690 params_le.bss_type = DOT11_BSSTYPE_ANY;
691 params_le.scan_type = 0;
692 params_le.channel_num = cpu_to_le32(1);
693 params_le.nprobes = cpu_to_le32(1);
694 params_le.active_time = cpu_to_le32(-1);
695 params_le.passive_time = cpu_to_le32(-1);
696 params_le.home_time = cpu_to_le32(-1);
697 /* Scan is aborted by setting channel_list[0] to -1 */
698 params_le.channel_list[0] = cpu_to_le16(-1);
699 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200700 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100701 &params_le, sizeof(params_le));
702 if (err)
703 brcmf_err("Scan abort failed\n");
704 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200705
Daniel Kim5e787f72014-06-21 12:11:18 +0200706 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200707
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100708 /*
709 * e-scan can be initiated by scheduled scan
710 * which takes precedence.
711 */
712 if (cfg->sched_escan) {
713 brcmf_dbg(SCAN, "scheduled scan completed\n");
714 cfg->sched_escan = false;
715 if (!aborted)
716 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100717 } else if (scan_request) {
718 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
719 aborted ? "Aborted" : "Done");
720 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100721 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100722 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
723 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100724
725 return err;
726}
727
Arend van Spriel9f440b72013-02-08 15:53:36 +0100728static
729int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
730{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100731 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
732 struct net_device *ndev = wdev->netdev;
733
734 /* vif event pending in firmware */
735 if (brcmf_cfg80211_vif_event_armed(cfg))
736 return -EBUSY;
737
738 if (ndev) {
739 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200740 cfg->escan_info.ifp == netdev_priv(ndev))
741 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
742 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100743
744 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
745 }
746
Arend van Spriel9f440b72013-02-08 15:53:36 +0100747 switch (wdev->iftype) {
748 case NL80211_IFTYPE_ADHOC:
749 case NL80211_IFTYPE_STATION:
750 case NL80211_IFTYPE_AP:
751 case NL80211_IFTYPE_AP_VLAN:
752 case NL80211_IFTYPE_WDS:
753 case NL80211_IFTYPE_MONITOR:
754 case NL80211_IFTYPE_MESH_POINT:
755 return -EOPNOTSUPP;
756 case NL80211_IFTYPE_P2P_CLIENT:
757 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200758 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100759 return brcmf_p2p_del_vif(wiphy, wdev);
760 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100761 default:
762 return -EINVAL;
763 }
764 return -EOPNOTSUPP;
765}
766
Arend van Spriel5b435de2011-10-05 13:19:03 +0200767static s32
768brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
769 enum nl80211_iftype type, u32 *flags,
770 struct vif_params *params)
771{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100772 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700773 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100774 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200775 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200776 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200777 s32 err = 0;
778
Arend van Spriel39504a22015-08-20 22:06:05 +0200779 brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type);
Hante Meuleman178e9ef2015-09-18 22:08:11 +0200780
781 /* WAR: There are a number of p2p interface related problems which
782 * need to be handled initially (before doing the validate).
783 * wpa_supplicant tends to do iface changes on p2p device/client/go
784 * which are not always possible/allowed. However we need to return
785 * OK otherwise the wpa_supplicant wont start. The situation differs
786 * on configuration and setup (p2pon=1 module param). The first check
787 * is to see if the request is a change to station for p2p iface.
788 */
789 if ((type == NL80211_IFTYPE_STATION) &&
790 ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
791 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
792 (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
793 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
794 /* Now depending on whether module param p2pon=1 was used the
795 * response needs to be either 0 or EOPNOTSUPP. The reason is
796 * that if p2pon=1 is used, but a newer supplicant is used then
797 * we should return an error, as this combination wont work.
798 * In other situations 0 is returned and supplicant will start
799 * normally. It will give a trace in cfg80211, but it is the
800 * only way to get it working. Unfortunately this will result
801 * in situation where we wont support new supplicant in
802 * combination with module param p2pon=1, but that is the way
803 * it is. If the user tries this then unloading of driver might
804 * fail/lock.
805 */
806 if (cfg->p2p.p2pdev_dynamically)
807 return -EOPNOTSUPP;
808 else
809 return 0;
810 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200811 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
812 if (err) {
813 brcmf_err("iface validation failed: err=%d\n", err);
814 return err;
815 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200816 switch (type) {
817 case NL80211_IFTYPE_MONITOR:
818 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100819 brcmf_err("type (%d) : currently we do not support this type\n",
820 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200821 return -EOPNOTSUPP;
822 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200823 infra = 0;
824 break;
825 case NL80211_IFTYPE_STATION:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200826 infra = 1;
827 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200828 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100829 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200830 ap = 1;
831 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200832 default:
833 err = -EINVAL;
834 goto done;
835 }
836
Hante Meuleman1a873342012-09-27 14:17:54 +0200837 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100838 if (type == NL80211_IFTYPE_P2P_GO) {
839 brcmf_dbg(INFO, "IF Type = P2P GO\n");
840 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
841 }
842 if (!err) {
843 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
844 brcmf_dbg(INFO, "IF Type = AP\n");
845 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200846 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100847 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200848 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100849 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200850 err = -EAGAIN;
851 goto done;
852 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100853 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100854 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200855 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200856 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200857
Hante Meuleman8851cce2014-07-30 13:20:02 +0200858 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
859
Arend van Spriel5b435de2011-10-05 13:19:03 +0200860done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100861 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200862
863 return err;
864}
865
Franky Lin83cf17a2013-04-11 13:28:50 +0200866static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
867 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200868 struct cfg80211_scan_request *request)
869{
870 u32 n_ssids;
871 u32 n_channels;
872 s32 i;
873 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200874 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200875 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200876 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200877
Joe Perches93803b32015-03-02 19:54:49 -0800878 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200879 params_le->bss_type = DOT11_BSSTYPE_ANY;
880 params_le->scan_type = 0;
881 params_le->channel_num = 0;
882 params_le->nprobes = cpu_to_le32(-1);
883 params_le->active_time = cpu_to_le32(-1);
884 params_le->passive_time = cpu_to_le32(-1);
885 params_le->home_time = cpu_to_le32(-1);
886 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
887
888 /* if request is null exit so it will be all channel broadcast scan */
889 if (!request)
890 return;
891
892 n_ssids = request->n_ssids;
893 n_channels = request->n_channels;
894 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100895 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
896 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200897 if (n_channels > 0) {
898 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200899 chanspec = channel_to_chanspec(&cfg->d11inf,
900 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100901 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
902 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200903 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200904 }
905 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100906 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200907 }
908 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100909 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200910 if (n_ssids > 0) {
911 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
912 n_channels * sizeof(u16);
913 offset = roundup(offset, sizeof(u32));
914 ptr = (char *)params_le + offset;
915 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200916 memset(&ssid_le, 0, sizeof(ssid_le));
917 ssid_le.SSID_len =
918 cpu_to_le32(request->ssids[i].ssid_len);
919 memcpy(ssid_le.SSID, request->ssids[i].ssid,
920 request->ssids[i].ssid_len);
921 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100922 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200923 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100924 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
925 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200926 memcpy(ptr, &ssid_le, sizeof(ssid_le));
927 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200928 }
929 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100930 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200931 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100932 brcmf_dbg(SCAN, "SSID %s len=%d\n",
933 params_le->ssid_le.SSID,
934 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200935 params_le->ssid_le.SSID_len =
936 cpu_to_le32(request->ssids->ssid_len);
937 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
938 request->ssids->ssid_len);
939 }
940 }
941 /* Adding mask to channel numbers */
942 params_le->channel_num =
943 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
944 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
945}
946
947static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200948brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +0200949 struct cfg80211_scan_request *request, u16 action)
950{
951 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
952 offsetof(struct brcmf_escan_params_le, params_le);
953 struct brcmf_escan_params_le *params;
954 s32 err = 0;
955
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100956 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200957
958 if (request != NULL) {
959 /* Allocate space for populating ssids in struct */
960 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
961
962 /* Allocate space for populating ssids in struct */
963 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
964 }
965
966 params = kzalloc(params_size, GFP_KERNEL);
967 if (!params) {
968 err = -ENOMEM;
969 goto exit;
970 }
971 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200972 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200973 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
974 params->action = cpu_to_le16(action);
975 params->sync_id = cpu_to_le16(0x1234);
976
Arend van Spriela0f472a2013-04-05 10:57:49 +0200977 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200978 if (err) {
979 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100980 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200981 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100982 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200983 }
984
985 kfree(params);
986exit:
987 return err;
988}
989
990static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200991brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +0200992 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200993{
994 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700995 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200996 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100997 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +0200998
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100999 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001000 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001001 escan->wiphy = wiphy;
1002 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001003 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001004 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001005 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001006 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001007 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001008 return err;
1009 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001010 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001011 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001012 results->version = 0;
1013 results->count = 0;
1014 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1015
Arend van Spriela0f472a2013-04-05 10:57:49 +02001016 err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001017 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001018 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001019 return err;
1020}
1021
1022static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001023brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001024 struct cfg80211_scan_request *request,
1025 struct cfg80211_ssid *this_ssid)
1026{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001027 struct brcmf_if *ifp = vif->ifp;
1028 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001029 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -08001030 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001031 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001032 bool escan_req;
1033 bool spec_scan;
1034 s32 err;
1035 u32 SSID_len;
1036
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001037 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001038
Arend van Sprielc1179032012-10-22 13:55:33 -07001039 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001040 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001041 return -EAGAIN;
1042 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001043 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001044 brcmf_err("Scanning being aborted: status (%lu)\n",
1045 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001046 return -EAGAIN;
1047 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001048 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1049 brcmf_err("Scanning suppressed: status (%lu)\n",
1050 cfg->scan_status);
1051 return -EAGAIN;
1052 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001053 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001054 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001055 return -EAGAIN;
1056 }
1057
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001058 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001059 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1060 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001061
Hante Meulemane756af52012-09-11 21:18:52 +02001062 escan_req = false;
1063 if (request) {
1064 /* scan bss */
1065 ssids = request->ssids;
1066 escan_req = true;
1067 } else {
1068 /* scan in ibss */
1069 /* we don't do escan in ibss */
1070 ssids = this_ssid;
1071 }
1072
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001073 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001074 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001075 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001076 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001077 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001078 if (err)
1079 goto scan_out;
1080
Arend van Spriela0f472a2013-04-05 10:57:49 +02001081 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001082 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001083 goto scan_out;
1084 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001085 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1086 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001087 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1088 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1089 sr->ssid_le.SSID_len = cpu_to_le32(0);
1090 spec_scan = false;
1091 if (SSID_len) {
1092 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1093 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1094 spec_scan = true;
1095 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001096 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001097
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001098 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001099 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001100 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001101 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001102 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001103 goto scan_out;
1104 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001105 brcmf_scan_config_mpc(ifp, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -07001106 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -07001107 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001108 if (err) {
1109 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001110 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
1111 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001112 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001113 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001114
Daniel Kim5e787f72014-06-21 12:11:18 +02001115 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001116 goto scan_out;
1117 }
1118 }
1119
Hante Meuleman661fa952015-02-06 18:36:47 +01001120 /* Arm scan timeout timer */
1121 mod_timer(&cfg->escan_timeout, jiffies +
1122 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1123
Hante Meulemane756af52012-09-11 21:18:52 +02001124 return 0;
1125
1126scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001127 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001128 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001129 return err;
1130}
1131
Arend van Spriel5b435de2011-10-05 13:19:03 +02001132static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001133brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001134{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001135 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001136 s32 err = 0;
1137
Arend van Sprield96b8012012-12-05 15:26:02 +01001138 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001139 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1140 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001141 return -EIO;
1142
Arend van Spriela0f472a2013-04-05 10:57:49 +02001143 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001144
Arend van Spriel5b435de2011-10-05 13:19:03 +02001145 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001146 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001147
Arend van Sprield96b8012012-12-05 15:26:02 +01001148 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001149 return err;
1150}
1151
1152static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1153{
1154 s32 err = 0;
1155
Arend van Sprielac24be62012-10-22 10:36:23 -07001156 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1157 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001158 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001159 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001160
1161 return err;
1162}
1163
1164static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_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), "fragthresh",
1169 frag_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_retry(struct net_device *ndev, u32 retry, bool l)
1177{
1178 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001179 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001180
Arend van Sprielac24be62012-10-22 10:36:23 -07001181 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001182 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001183 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001184 return err;
1185 }
1186 return err;
1187}
1188
1189static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1190{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001191 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1192 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001193 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001194 s32 err = 0;
1195
Arend van Sprield96b8012012-12-05 15:26:02 +01001196 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001197 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001198 return -EIO;
1199
1200 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001201 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1202 cfg->conf->rts_threshold = wiphy->rts_threshold;
1203 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001204 if (!err)
1205 goto done;
1206 }
1207 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001208 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1209 cfg->conf->frag_threshold = wiphy->frag_threshold;
1210 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001211 if (!err)
1212 goto done;
1213 }
1214 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001215 && (cfg->conf->retry_long != wiphy->retry_long)) {
1216 cfg->conf->retry_long = wiphy->retry_long;
1217 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001218 if (!err)
1219 goto done;
1220 }
1221 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001222 && (cfg->conf->retry_short != wiphy->retry_short)) {
1223 cfg->conf->retry_short = wiphy->retry_short;
1224 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001225 if (!err)
1226 goto done;
1227 }
1228
1229done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001230 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001231 return err;
1232}
1233
Arend van Spriel5b435de2011-10-05 13:19:03 +02001234static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1235{
1236 memset(prof, 0, sizeof(*prof));
1237}
1238
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001239static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1240{
1241 u16 reason;
1242
1243 switch (e->event_code) {
1244 case BRCMF_E_DEAUTH:
1245 case BRCMF_E_DEAUTH_IND:
1246 case BRCMF_E_DISASSOC_IND:
1247 reason = e->reason;
1248 break;
1249 case BRCMF_E_LINK:
1250 default:
1251 reason = 0;
1252 break;
1253 }
1254 return reason;
1255}
1256
1257static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001258{
Piotr Haber61730d42013-04-23 12:53:12 +02001259 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001260 s32 err = 0;
1261
Arend van Sprield96b8012012-12-05 15:26:02 +01001262 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001263
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001264 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001265 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001266 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001267 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001268 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001269 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001270 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001271 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001272 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
Johannes Berg80279fb2015-05-22 16:22:20 +02001273 true, GFP_KERNEL);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001274
Arend van Spriel5b435de2011-10-05 13:19:03 +02001275 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001276 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001277 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1278 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001279 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001280}
1281
1282static s32
1283brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1284 struct cfg80211_ibss_params *params)
1285{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001286 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001287 struct brcmf_if *ifp = netdev_priv(ndev);
1288 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001289 struct brcmf_join_params join_params;
1290 size_t join_params_size = 0;
1291 s32 err = 0;
1292 s32 wsec = 0;
1293 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001294 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001295
Arend van Sprield96b8012012-12-05 15:26:02 +01001296 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001297 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001298 return -EIO;
1299
1300 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001301 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001302 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001303 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001304 return -EOPNOTSUPP;
1305 }
1306
Arend van Sprielc1179032012-10-22 13:55:33 -07001307 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001308
1309 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001310 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001311 else
Arend van Spriel16886732012-12-05 15:26:04 +01001312 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001313
Johannes Berg683b6d32012-11-08 21:25:48 +01001314 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001315 brcmf_dbg(CONN, "channel: %d\n",
1316 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001317 else
Arend van Spriel16886732012-12-05 15:26:04 +01001318 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001319
1320 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001321 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001322 else
Arend van Spriel16886732012-12-05 15:26:04 +01001323 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001324
1325 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001326 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001327 else
Arend van Spriel16886732012-12-05 15:26:04 +01001328 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001329
1330 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001331 brcmf_dbg(CONN, "beacon interval: %d\n",
1332 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001333 else
Arend van Spriel16886732012-12-05 15:26:04 +01001334 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001335
1336 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001337 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001338 else
Arend van Spriel16886732012-12-05 15:26:04 +01001339 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001340
1341 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001342 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001343 else
Arend van Spriel16886732012-12-05 15:26:04 +01001344 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001345
1346 /* Configure Privacy for starter */
1347 if (params->privacy)
1348 wsec |= WEP_ENABLED;
1349
Arend van Sprielc1179032012-10-22 13:55:33 -07001350 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001351 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001352 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001353 goto done;
1354 }
1355
1356 /* Configure Beacon Interval for starter */
1357 if (params->beacon_interval)
1358 bcnprd = params->beacon_interval;
1359 else
1360 bcnprd = 100;
1361
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001362 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001363 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001364 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001365 goto done;
1366 }
1367
1368 /* Configure required join parameter */
1369 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1370
1371 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001372 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1373 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1374 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1375 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001376 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001377
1378 /* BSSID */
1379 if (params->bssid) {
1380 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1381 join_params_size = sizeof(join_params.ssid_le) +
1382 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001383 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001384 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001385 eth_broadcast_addr(join_params.params_le.bssid);
1386 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001387 }
1388
Arend van Spriel5b435de2011-10-05 13:19:03 +02001389 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001390 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001391 u32 target_channel;
1392
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001393 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001394 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001395 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001396 if (params->channel_fixed) {
1397 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001398 chanspec = chandef_to_chanspec(&cfg->d11inf,
1399 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001400 join_params.params_le.chanspec_list[0] =
1401 cpu_to_le16(chanspec);
1402 join_params.params_le.chanspec_num = cpu_to_le32(1);
1403 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001404 }
1405
1406 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001407 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001408 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001409 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001410 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001411 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001412 goto done;
1413 }
1414 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001415 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001416
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001417 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001418
1419
Arend van Sprielc1179032012-10-22 13:55:33 -07001420 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001421 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001422 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001423 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001424 goto done;
1425 }
1426
1427done:
1428 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001429 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001430 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001431 return err;
1432}
1433
1434static s32
1435brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1436{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001437 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001438
Arend van Sprield96b8012012-12-05 15:26:02 +01001439 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001440 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001441 return -EIO;
1442
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001443 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001444
Arend van Sprield96b8012012-12-05 15:26:02 +01001445 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001446
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001447 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001448}
1449
1450static s32 brcmf_set_wpa_version(struct net_device *ndev,
1451 struct cfg80211_connect_params *sme)
1452{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001453 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001454 struct brcmf_cfg80211_security *sec;
1455 s32 val = 0;
1456 s32 err = 0;
1457
1458 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1459 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1460 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1461 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1462 else
1463 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001464 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001465 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001466 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001467 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001468 return err;
1469 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001470 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001471 sec->wpa_versions = sme->crypto.wpa_versions;
1472 return err;
1473}
1474
1475static s32 brcmf_set_auth_type(struct net_device *ndev,
1476 struct cfg80211_connect_params *sme)
1477{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001478 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001479 struct brcmf_cfg80211_security *sec;
1480 s32 val = 0;
1481 s32 err = 0;
1482
1483 switch (sme->auth_type) {
1484 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1485 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001486 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001487 break;
1488 case NL80211_AUTHTYPE_SHARED_KEY:
1489 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001490 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001491 break;
1492 case NL80211_AUTHTYPE_AUTOMATIC:
1493 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001494 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001495 break;
1496 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001497 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001498 default:
1499 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001500 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001501 break;
1502 }
1503
Hante Meuleman89286dc2013-02-08 15:53:46 +01001504 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001505 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001506 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001507 return err;
1508 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001509 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001510 sec->auth_type = sme->auth_type;
1511 return err;
1512}
1513
1514static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001515brcmf_set_wsec_mode(struct net_device *ndev,
1516 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001517{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001518 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001519 struct brcmf_cfg80211_security *sec;
1520 s32 pval = 0;
1521 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001522 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001523 s32 err = 0;
1524
1525 if (sme->crypto.n_ciphers_pairwise) {
1526 switch (sme->crypto.ciphers_pairwise[0]) {
1527 case WLAN_CIPHER_SUITE_WEP40:
1528 case WLAN_CIPHER_SUITE_WEP104:
1529 pval = WEP_ENABLED;
1530 break;
1531 case WLAN_CIPHER_SUITE_TKIP:
1532 pval = TKIP_ENABLED;
1533 break;
1534 case WLAN_CIPHER_SUITE_CCMP:
1535 pval = AES_ENABLED;
1536 break;
1537 case WLAN_CIPHER_SUITE_AES_CMAC:
1538 pval = AES_ENABLED;
1539 break;
1540 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001541 brcmf_err("invalid cipher pairwise (%d)\n",
1542 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001543 return -EINVAL;
1544 }
1545 }
1546 if (sme->crypto.cipher_group) {
1547 switch (sme->crypto.cipher_group) {
1548 case WLAN_CIPHER_SUITE_WEP40:
1549 case WLAN_CIPHER_SUITE_WEP104:
1550 gval = WEP_ENABLED;
1551 break;
1552 case WLAN_CIPHER_SUITE_TKIP:
1553 gval = TKIP_ENABLED;
1554 break;
1555 case WLAN_CIPHER_SUITE_CCMP:
1556 gval = AES_ENABLED;
1557 break;
1558 case WLAN_CIPHER_SUITE_AES_CMAC:
1559 gval = AES_ENABLED;
1560 break;
1561 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001562 brcmf_err("invalid cipher group (%d)\n",
1563 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001564 return -EINVAL;
1565 }
1566 }
1567
Arend van Spriel16886732012-12-05 15:26:04 +01001568 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001569 /* In case of privacy, but no security and WPS then simulate */
1570 /* setting AES. WPS-2.0 allows no security */
1571 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1572 sme->privacy)
1573 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001574
1575 if (mfp)
1576 wsec = pval | gval | MFP_CAPABLE;
1577 else
1578 wsec = pval | gval;
1579 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001580 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001581 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001582 return err;
1583 }
1584
Arend van Spriel06bb1232012-09-27 14:17:56 +02001585 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001586 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1587 sec->cipher_group = sme->crypto.cipher_group;
1588
1589 return err;
1590}
1591
1592static s32
1593brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1594{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001595 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001596 struct brcmf_cfg80211_security *sec;
1597 s32 val = 0;
1598 s32 err = 0;
1599
1600 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001601 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1602 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001603 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001604 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001605 return err;
1606 }
1607 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1608 switch (sme->crypto.akm_suites[0]) {
1609 case WLAN_AKM_SUITE_8021X:
1610 val = WPA_AUTH_UNSPECIFIED;
1611 break;
1612 case WLAN_AKM_SUITE_PSK:
1613 val = WPA_AUTH_PSK;
1614 break;
1615 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001616 brcmf_err("invalid cipher group (%d)\n",
1617 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001618 return -EINVAL;
1619 }
1620 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1621 switch (sme->crypto.akm_suites[0]) {
1622 case WLAN_AKM_SUITE_8021X:
1623 val = WPA2_AUTH_UNSPECIFIED;
1624 break;
1625 case WLAN_AKM_SUITE_PSK:
1626 val = WPA2_AUTH_PSK;
1627 break;
1628 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001629 brcmf_err("invalid cipher group (%d)\n",
1630 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001631 return -EINVAL;
1632 }
1633 }
1634
Arend van Spriel16886732012-12-05 15:26:04 +01001635 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001636 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1637 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001638 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001639 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001640 return err;
1641 }
1642 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001643 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001644 sec->wpa_auth = sme->crypto.akm_suites[0];
1645
1646 return err;
1647}
1648
1649static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001650brcmf_set_sharedkey(struct net_device *ndev,
1651 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001652{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001653 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001654 struct brcmf_cfg80211_security *sec;
1655 struct brcmf_wsec_key key;
1656 s32 val;
1657 s32 err = 0;
1658
Arend van Spriel16886732012-12-05 15:26:04 +01001659 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001660
Roland Vossena718e2f2011-10-12 20:51:24 +02001661 if (sme->key_len == 0)
1662 return 0;
1663
Arend van Spriel06bb1232012-09-27 14:17:56 +02001664 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001665 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1666 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001667
1668 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1669 return 0;
1670
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001671 if (!(sec->cipher_pairwise &
1672 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1673 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001674
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001675 memset(&key, 0, sizeof(key));
1676 key.len = (u32) sme->key_len;
1677 key.index = (u32) sme->key_idx;
1678 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001679 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001680 return -EINVAL;
1681 }
1682 memcpy(key.data, sme->key, key.len);
1683 key.flags = BRCMF_PRIMARY_KEY;
1684 switch (sec->cipher_pairwise) {
1685 case WLAN_CIPHER_SUITE_WEP40:
1686 key.algo = CRYPTO_ALGO_WEP1;
1687 break;
1688 case WLAN_CIPHER_SUITE_WEP104:
1689 key.algo = CRYPTO_ALGO_WEP128;
1690 break;
1691 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001692 brcmf_err("Invalid algorithm (%d)\n",
1693 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001694 return -EINVAL;
1695 }
1696 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001697 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1698 key.len, key.index, key.algo);
1699 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001700 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001701 if (err)
1702 return err;
1703
1704 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001705 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001706 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001707 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001708 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001709 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001710 }
1711 return err;
1712}
1713
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001714static
1715enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1716 enum nl80211_auth_type type)
1717{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001718 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1719 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1720 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1721 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001722 }
1723 return type;
1724}
1725
Arend van Spriel5b435de2011-10-05 13:19:03 +02001726static s32
1727brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001728 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001729{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001730 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001731 struct brcmf_if *ifp = netdev_priv(ndev);
1732 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001733 struct ieee80211_channel *chan = sme->channel;
1734 struct brcmf_join_params join_params;
1735 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001736 const struct brcmf_tlv *rsn_ie;
1737 const struct brcmf_vs_tlv *wpa_ie;
1738 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001739 u32 ie_len;
1740 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001741 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001742 s32 err = 0;
1743
Arend van Sprield96b8012012-12-05 15:26:02 +01001744 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001745 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001746 return -EIO;
1747
1748 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001749 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001750 return -EOPNOTSUPP;
1751 }
1752
Hante Meuleman89286dc2013-02-08 15:53:46 +01001753 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1754 /* A normal (non P2P) connection request setup. */
1755 ie = NULL;
1756 ie_len = 0;
1757 /* find the WPA_IE */
1758 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1759 if (wpa_ie) {
1760 ie = wpa_ie;
1761 ie_len = wpa_ie->len + TLV_HDR_LEN;
1762 } else {
1763 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001764 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1765 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001766 WLAN_EID_RSN);
1767 if (rsn_ie) {
1768 ie = rsn_ie;
1769 ie_len = rsn_ie->len + TLV_HDR_LEN;
1770 }
1771 }
1772 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1773 }
1774
1775 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1776 sme->ie, sme->ie_len);
1777 if (err)
1778 brcmf_err("Set Assoc REQ IE Failed\n");
1779 else
1780 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1781
Arend van Sprielc1179032012-10-22 13:55:33 -07001782 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001783
1784 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001785 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001786 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001787 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001788 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1789 cfg->channel, chan->center_freq, chanspec);
1790 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001791 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001792 chanspec = 0;
1793 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001794
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001795 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001796
1797 err = brcmf_set_wpa_version(ndev, sme);
1798 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001799 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001800 goto done;
1801 }
1802
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001803 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001804 err = brcmf_set_auth_type(ndev, sme);
1805 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001806 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001807 goto done;
1808 }
1809
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001810 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001811 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001812 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001813 goto done;
1814 }
1815
1816 err = brcmf_set_key_mgmt(ndev, sme);
1817 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001818 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001819 goto done;
1820 }
1821
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001822 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001823 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001824 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001825 goto done;
1826 }
1827
Hante Meuleman89286dc2013-02-08 15:53:46 +01001828 profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
1829 (u32)sme->ssid_len);
1830 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1831 if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1832 profile->ssid.SSID[profile->ssid.SSID_len] = 0;
1833 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
1834 profile->ssid.SSID_len);
1835 }
1836
1837 /* Join with specific BSSID and cached SSID
1838 * If SSID is zero join based on BSSID only
1839 */
1840 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1841 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1842 if (cfg->channel)
1843 join_params_size += sizeof(u16);
1844 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1845 if (ext_join_params == NULL) {
1846 err = -ENOMEM;
1847 goto done;
1848 }
1849 ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
1850 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
1851 profile->ssid.SSID_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001852
Hante Meuleman89286dc2013-02-08 15:53:46 +01001853 /* Set up join scan parameters */
1854 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001855 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1856
1857 if (sme->bssid)
1858 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1859 else
Joe Perches93803b32015-03-02 19:54:49 -08001860 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001861
1862 if (cfg->channel) {
1863 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1864
1865 ext_join_params->assoc_le.chanspec_list[0] =
1866 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001867 /* Increase dwell time to receive probe response or detect
1868 * beacon from target AP at a noisy air only during connect
1869 * command.
1870 */
1871 ext_join_params->scan_le.active_time =
1872 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1873 ext_join_params->scan_le.passive_time =
1874 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1875 /* To sync with presence period of VSDB GO send probe request
1876 * more frequently. Probe request will be stopped when it gets
1877 * probe response from target AP/GO.
1878 */
1879 ext_join_params->scan_le.nprobes =
1880 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1881 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1882 } else {
1883 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1884 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1885 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001886 }
1887
1888 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1889 join_params_size);
1890 kfree(ext_join_params);
1891 if (!err)
1892 /* This is it. join command worked, we are done */
1893 goto done;
1894
1895 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001896 memset(&join_params, 0, sizeof(join_params));
1897 join_params_size = sizeof(join_params.ssid_le);
1898
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001899 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001900 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001901
Hante Meuleman89286dc2013-02-08 15:53:46 +01001902 if (sme->bssid)
1903 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1904 else
Joe Perches93803b32015-03-02 19:54:49 -08001905 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001906
Hante Meuleman17012612013-02-06 18:40:44 +01001907 if (cfg->channel) {
1908 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1909 join_params.params_le.chanspec_num = cpu_to_le32(1);
1910 join_params_size += sizeof(join_params.params_le);
1911 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001912 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001913 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001914 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001915 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001916
1917done:
1918 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001919 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001920 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001921 return err;
1922}
1923
1924static s32
1925brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1926 u16 reason_code)
1927{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001928 struct brcmf_if *ifp = netdev_priv(ndev);
1929 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001930 struct brcmf_scb_val_le scbval;
1931 s32 err = 0;
1932
Arend van Sprield96b8012012-12-05 15:26:02 +01001933 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001934 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001935 return -EIO;
1936
Arend van Sprielc1179032012-10-22 13:55:33 -07001937 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001938 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001939 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001940
Arend van Spriel06bb1232012-09-27 14:17:56 +02001941 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001942 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001943 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001944 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001945 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001946 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001947
Arend van Sprield96b8012012-12-05 15:26:02 +01001948 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001949 return err;
1950}
1951
1952static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001953brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001954 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001955{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001956 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001957 struct net_device *ndev = cfg_to_ndev(cfg);
1958 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001959 s32 err;
1960 s32 disable;
1961 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001962
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001963 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07001964 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001965 return -EIO;
1966
1967 switch (type) {
1968 case NL80211_TX_POWER_AUTOMATIC:
1969 break;
1970 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001972 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001973 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001974 err = -EINVAL;
1975 goto done;
1976 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001977 qdbm = MBM_TO_DBM(4 * mbm);
1978 if (qdbm > 127)
1979 qdbm = 127;
1980 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001981 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001982 default:
1983 brcmf_err("Unsupported type %d\n", type);
1984 err = -EINVAL;
1985 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001986 }
1987 /* Make sure radio is off or on as far as software is concerned */
1988 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001989 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001990 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001991 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001992
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001993 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001994 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001995 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001996
1997done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001998 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001999 return err;
2000}
2001
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002002static s32
2003brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2004 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002005{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002006 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002007 struct net_device *ndev = cfg_to_ndev(cfg);
2008 struct brcmf_if *ifp = netdev_priv(ndev);
2009 s32 qdbm = 0;
2010 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002011
Arend van Sprield96b8012012-12-05 15:26:02 +01002012 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002013 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002014 return -EIO;
2015
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002016 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002017 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002018 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002019 goto done;
2020 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002021 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002022
2023done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002024 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002025 return err;
2026}
2027
2028static s32
2029brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002030 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002031{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002032 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002033 u32 index;
2034 u32 wsec;
2035 s32 err = 0;
2036
Arend van Sprield96b8012012-12-05 15:26:02 +01002037 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002038 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002039 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002040 return -EIO;
2041
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002042 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002043 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002044 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002045 goto done;
2046 }
2047
2048 if (wsec & WEP_ENABLED) {
2049 /* Just select a new current key */
2050 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002051 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002052 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002053 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002054 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002055 }
2056done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002057 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002058 return err;
2059}
2060
2061static s32
2062brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2063 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2064{
Hante Meuleman992f6062013-04-02 21:06:17 +02002065 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002066 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002067 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002068 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002069
2070 memset(&key, 0, sizeof(key));
2071 key.index = (u32) key_idx;
2072 /* Instead of bcast for ea address for default wep keys,
2073 driver needs it to be Null */
2074 if (!is_multicast_ether_addr(mac_addr))
2075 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2076 key.len = (u32) params->key_len;
2077 /* check for key index change */
2078 if (key.len == 0) {
2079 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002080 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002081 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002082 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002083 } else {
2084 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002085 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002086 return -EINVAL;
2087 }
2088
Arend van Spriel16886732012-12-05 15:26:04 +01002089 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002090 memcpy(key.data, params->key, key.len);
2091
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002092 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002093 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2094 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002095 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2096 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2097 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2098 }
2099
2100 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2101 if (params->seq && params->seq_len == 6) {
2102 /* rx iv */
2103 u8 *ivptr;
2104 ivptr = (u8 *) params->seq;
2105 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2106 (ivptr[3] << 8) | ivptr[2];
2107 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2108 key.iv_initialized = true;
2109 }
2110
2111 switch (params->cipher) {
2112 case WLAN_CIPHER_SUITE_WEP40:
2113 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002114 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002115 break;
2116 case WLAN_CIPHER_SUITE_WEP104:
2117 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002118 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002119 break;
2120 case WLAN_CIPHER_SUITE_TKIP:
2121 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002122 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002123 break;
2124 case WLAN_CIPHER_SUITE_AES_CMAC:
2125 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002126 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002127 break;
2128 case WLAN_CIPHER_SUITE_CCMP:
2129 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002130 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002131 break;
2132 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002133 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002134 return -EINVAL;
2135 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002136 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002137 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002138 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002139 }
2140 return err;
2141}
2142
2143static s32
2144brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2145 u8 key_idx, bool pairwise, const u8 *mac_addr,
2146 struct key_params *params)
2147{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002148 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002149 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002150 s32 val;
2151 s32 wsec;
2152 s32 err = 0;
2153 u8 keybuf[8];
2154
Arend van Sprield96b8012012-12-05 15:26:02 +01002155 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002156 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002157 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002158 return -EIO;
2159
Hante Meuleman118eb302014-12-21 12:43:49 +01002160 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2161 /* we ignore this key index in this case */
2162 brcmf_err("invalid key index (%d)\n", key_idx);
2163 return -EINVAL;
2164 }
2165
Daniel Kim787eb032014-01-29 15:32:23 +01002166 if (mac_addr &&
2167 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2168 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002169 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002170 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2171 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002172
Hante Meuleman118eb302014-12-21 12:43:49 +01002173 key = &ifp->vif->profile.key[key_idx];
2174 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002175
Hante Meuleman118eb302014-12-21 12:43:49 +01002176 if (params->key_len > sizeof(key->data)) {
2177 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002178 err = -EINVAL;
2179 goto done;
2180 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002181 key->len = params->key_len;
2182 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002183
Hante Meuleman118eb302014-12-21 12:43:49 +01002184 memcpy(key->data, params->key, key->len);
2185
2186 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002187 switch (params->cipher) {
2188 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002189 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002190 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002191 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002192 break;
2193 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002194 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002195 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002196 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002197 break;
2198 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002199 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002200 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002201 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2202 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2203 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002204 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002205 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002206 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002207 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002208 break;
2209 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002210 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002211 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002212 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002213 break;
2214 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002215 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002216 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002217 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002218 break;
2219 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002220 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002221 err = -EINVAL;
2222 goto done;
2223 }
2224
Hante Meuleman118eb302014-12-21 12:43:49 +01002225 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002226 if (err)
2227 goto done;
2228
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002229 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002230 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002231 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002232 goto done;
2233 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002234 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002235 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002237 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002238 goto done;
2239 }
2240
Arend van Spriel5b435de2011-10-05 13:19:03 +02002241done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002242 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002243 return err;
2244}
2245
2246static s32
2247brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2248 u8 key_idx, bool pairwise, const u8 *mac_addr)
2249{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002250 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002251 struct brcmf_wsec_key key;
2252 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002253
Arend van Sprield96b8012012-12-05 15:26:02 +01002254 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002255 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002256 return -EIO;
2257
Hante Meuleman118eb302014-12-21 12:43:49 +01002258 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002259 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002260 return -EINVAL;
2261 }
2262
Arend van Spriel5b435de2011-10-05 13:19:03 +02002263 memset(&key, 0, sizeof(key));
2264
2265 key.index = (u32) key_idx;
2266 key.flags = BRCMF_PRIMARY_KEY;
2267 key.algo = CRYPTO_ALGO_OFF;
2268
Arend van Spriel16886732012-12-05 15:26:04 +01002269 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002270
2271 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002272 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002273
Arend van Sprield96b8012012-12-05 15:26:02 +01002274 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002275 return err;
2276}
2277
2278static s32
2279brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2280 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2281 void (*callback) (void *cookie, struct key_params * params))
2282{
2283 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002284 struct brcmf_if *ifp = netdev_priv(ndev);
2285 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002286 struct brcmf_cfg80211_security *sec;
2287 s32 wsec;
2288 s32 err = 0;
2289
Arend van Sprield96b8012012-12-05 15:26:02 +01002290 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002291 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002292 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002293 return -EIO;
2294
2295 memset(&params, 0, sizeof(params));
2296
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002297 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002298 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002299 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002300 /* Ignore this error, may happen during DISASSOC */
2301 err = -EAGAIN;
2302 goto done;
2303 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002304 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002305 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002306 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2307 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002308 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002309 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2310 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002311 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002312 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002313 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002314 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002315 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002316 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002317 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002318 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002319 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002320 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002321 err = -EINVAL;
2322 goto done;
2323 }
2324 callback(cookie, &params);
2325
2326done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002327 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002328 return err;
2329}
2330
2331static s32
2332brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2333 struct net_device *ndev, u8 key_idx)
2334{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002335 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002336
2337 return -EOPNOTSUPP;
2338}
2339
Hante Meuleman118eb302014-12-21 12:43:49 +01002340static void
2341brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2342{
2343 s32 err;
2344 u8 key_idx;
2345 struct brcmf_wsec_key *key;
2346 s32 wsec;
2347
2348 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2349 key = &ifp->vif->profile.key[key_idx];
2350 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2351 (key->algo == CRYPTO_ALGO_WEP128))
2352 break;
2353 }
2354 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2355 return;
2356
2357 err = send_key_to_dongle(ifp, key);
2358 if (err) {
2359 brcmf_err("Setting WEP key failed (%d)\n", err);
2360 return;
2361 }
2362 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2363 if (err) {
2364 brcmf_err("get wsec error (%d)\n", err);
2365 return;
2366 }
2367 wsec |= WEP_ENABLED;
2368 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2369 if (err)
2370 brcmf_err("set wsec error (%d)\n", err);
2371}
2372
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002373static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2374{
2375 struct nl80211_sta_flag_update *sfu;
2376
2377 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2378 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2379 sfu = &si->sta_flags;
2380 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2381 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2382 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2383 BIT(NL80211_STA_FLAG_AUTHORIZED);
2384 if (fw_sta_flags & BRCMF_STA_WME)
2385 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2386 if (fw_sta_flags & BRCMF_STA_AUTHE)
2387 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2388 if (fw_sta_flags & BRCMF_STA_ASSOC)
2389 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2390 if (fw_sta_flags & BRCMF_STA_AUTHO)
2391 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2392}
2393
2394static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2395{
2396 struct {
2397 __le32 len;
2398 struct brcmf_bss_info_le bss_le;
2399 } *buf;
2400 u16 capability;
2401 int err;
2402
2403 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2404 if (!buf)
2405 return;
2406
2407 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2408 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2409 WL_BSS_INFO_MAX);
2410 if (err) {
2411 brcmf_err("Failed to get bss info (%d)\n", err);
2412 return;
2413 }
2414 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2415 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2416 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2417 capability = le16_to_cpu(buf->bss_le.capability);
2418 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2419 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2420 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2421 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2422 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2423 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2424}
2425
Arend van Spriel5b435de2011-10-05 13:19:03 +02002426static s32
2427brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002428 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002429{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002430 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002431 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002432 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002433 u32 sta_flags;
2434 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002435 s32 total_rssi;
2436 s32 count_rssi;
2437 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002438
Arend van Sprield96b8012012-12-05 15:26:02 +01002439 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002440 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002441 return -EIO;
2442
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002443 memset(&sta_info_le, 0, sizeof(sta_info_le));
2444 memcpy(&sta_info_le, mac, ETH_ALEN);
2445 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2446 &sta_info_le,
2447 sizeof(sta_info_le));
2448 is_tdls_peer = !err;
2449 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002450 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002451 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002452 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002453 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002454 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002455 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002456 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002457 }
2458 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2459 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2460 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2461 sta_flags = le32_to_cpu(sta_info_le.flags);
2462 brcmf_convert_sta_flags(sta_flags, sinfo);
2463 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2464 if (is_tdls_peer)
2465 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2466 else
2467 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2468 if (sta_flags & BRCMF_STA_ASSOC) {
2469 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2470 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2471 brcmf_fill_bss_param(ifp, sinfo);
2472 }
2473 if (sta_flags & BRCMF_STA_SCBSTATS) {
2474 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2475 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2476 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2477 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2478 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2479 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2480 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2481 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2482 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002483 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002484 sinfo->txrate.legacy =
2485 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002486 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002487 if (sinfo->rx_packets) {
2488 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002489 sinfo->rxrate.legacy =
2490 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002491 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002492 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2493 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2494 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2495 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2496 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2497 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002498 total_rssi = 0;
2499 count_rssi = 0;
2500 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2501 if (sta_info_le.rssi[i]) {
2502 sinfo->chain_signal_avg[count_rssi] =
2503 sta_info_le.rssi[i];
2504 sinfo->chain_signal[count_rssi] =
2505 sta_info_le.rssi[i];
2506 total_rssi += sta_info_le.rssi[i];
2507 count_rssi++;
2508 }
2509 }
2510 if (count_rssi) {
2511 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2512 sinfo->chains = count_rssi;
2513
2514 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2515 total_rssi /= count_rssi;
2516 sinfo->signal = total_rssi;
2517 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002518 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002519done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002520 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002521 return err;
2522}
2523
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002524static int
2525brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2526 int idx, u8 *mac, struct station_info *sinfo)
2527{
2528 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2529 struct brcmf_if *ifp = netdev_priv(ndev);
2530 s32 err;
2531
2532 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2533
2534 if (idx == 0) {
2535 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2536 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2537 &cfg->assoclist,
2538 sizeof(cfg->assoclist));
2539 if (err) {
2540 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2541 err);
2542 cfg->assoclist.count = 0;
2543 return -EOPNOTSUPP;
2544 }
2545 }
2546 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2547 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2548 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2549 }
2550 return -ENOENT;
2551}
2552
Arend van Spriel5b435de2011-10-05 13:19:03 +02002553static s32
2554brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2555 bool enabled, s32 timeout)
2556{
2557 s32 pm;
2558 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002559 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002560 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002561
Arend van Sprield96b8012012-12-05 15:26:02 +01002562 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002563
2564 /*
2565 * Powersave enable/disable request is coming from the
2566 * cfg80211 even before the interface is up. In that
2567 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002568 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002569 * FW later while initializing the dongle
2570 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002571 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002572 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002573
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002574 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002575 goto done;
2576 }
2577
2578 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002579 /* Do not enable the power save after assoc if it is a p2p interface */
2580 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2581 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2582 pm = PM_OFF;
2583 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002584 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002585
Arend van Sprielc1179032012-10-22 13:55:33 -07002586 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002587 if (err) {
2588 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002589 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002590 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002591 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002592 }
2593done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002594 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002595 return err;
2596}
2597
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002598static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002599 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002600{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002601 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002602 struct ieee80211_channel *notify_channel;
2603 struct cfg80211_bss *bss;
2604 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002605 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002606 u16 channel;
2607 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002608 u16 notify_capability;
2609 u16 notify_interval;
2610 u8 *notify_ie;
2611 size_t notify_ielen;
2612 s32 notify_signal;
2613
2614 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002615 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002616 return 0;
2617 }
2618
Franky Lin83cf17a2013-04-11 13:28:50 +02002619 if (!bi->ctl_ch) {
2620 ch.chspec = le16_to_cpu(bi->chanspec);
2621 cfg->d11inf.decchspec(&ch);
2622 bi->ctl_ch = ch.chnum;
2623 }
2624 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002625
2626 if (channel <= CH_MAX_2G_CHANNEL)
2627 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2628 else
2629 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2630
2631 freq = ieee80211_channel_to_frequency(channel, band->band);
2632 notify_channel = ieee80211_get_channel(wiphy, freq);
2633
Arend van Spriel5b435de2011-10-05 13:19:03 +02002634 notify_capability = le16_to_cpu(bi->capability);
2635 notify_interval = le16_to_cpu(bi->beacon_period);
2636 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2637 notify_ielen = le32_to_cpu(bi->ie_length);
2638 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2639
Arend van Spriel16886732012-12-05 15:26:04 +01002640 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2641 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2642 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2643 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2644 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002645
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002646 bss = cfg80211_inform_bss(wiphy, notify_channel,
2647 CFG80211_BSS_FTYPE_UNKNOWN,
2648 (const u8 *)bi->BSSID,
2649 0, notify_capability,
2650 notify_interval, notify_ie,
2651 notify_ielen, notify_signal,
2652 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002653
Franky Line78946e2011-11-10 20:30:34 +01002654 if (!bss)
2655 return -ENOMEM;
2656
Johannes Berg5b112d32013-02-01 01:49:58 +01002657 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002658
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002659 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002660}
2661
Roland Vossen6f09be02011-10-18 14:03:02 +02002662static struct brcmf_bss_info_le *
2663next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2664{
2665 if (bss == NULL)
2666 return list->bss_info_le;
2667 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2668 le32_to_cpu(bss->length));
2669}
2670
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002671static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002672{
2673 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002674 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002675 s32 err = 0;
2676 int i;
2677
Hante Meulemanef8596e2014-09-30 10:23:13 +02002678 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002679 if (bss_list->count != 0 &&
2680 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002681 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2682 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002683 return -EOPNOTSUPP;
2684 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002685 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002686 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002687 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002688 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002689 if (err)
2690 break;
2691 }
2692 return err;
2693}
2694
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002695static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002696 struct net_device *ndev, const u8 *bssid)
2697{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002698 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002699 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002700 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002701 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002702 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002703 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002704 u8 *buf = NULL;
2705 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002706 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002707 u16 notify_capability;
2708 u16 notify_interval;
2709 u8 *notify_ie;
2710 size_t notify_ielen;
2711 s32 notify_signal;
2712
Arend van Sprield96b8012012-12-05 15:26:02 +01002713 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002714
2715 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2716 if (buf == NULL) {
2717 err = -ENOMEM;
2718 goto CleanUp;
2719 }
2720
2721 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2722
Arend van Sprielac24be62012-10-22 10:36:23 -07002723 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2724 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002725 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002726 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002727 goto CleanUp;
2728 }
2729
Roland Vossend34bf642011-10-18 14:03:01 +02002730 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002731
Franky Lin83cf17a2013-04-11 13:28:50 +02002732 ch.chspec = le16_to_cpu(bi->chanspec);
2733 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002734
Franky Lin83cf17a2013-04-11 13:28:50 +02002735 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002736 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2737 else
2738 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2739
Franky Lin83cf17a2013-04-11 13:28:50 +02002740 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002741 notify_channel = ieee80211_get_channel(wiphy, freq);
2742
Arend van Spriel5b435de2011-10-05 13:19:03 +02002743 notify_capability = le16_to_cpu(bi->capability);
2744 notify_interval = le16_to_cpu(bi->beacon_period);
2745 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2746 notify_ielen = le32_to_cpu(bi->ie_length);
2747 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2748
Franky Lin83cf17a2013-04-11 13:28:50 +02002749 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002750 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2751 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2752 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002753
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002754 bss = cfg80211_inform_bss(wiphy, notify_channel,
2755 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2756 notify_capability, notify_interval,
2757 notify_ie, notify_ielen, notify_signal,
2758 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002759
Franky Line78946e2011-11-10 20:30:34 +01002760 if (!bss) {
2761 err = -ENOMEM;
2762 goto CleanUp;
2763 }
2764
Johannes Berg5b112d32013-02-01 01:49:58 +01002765 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002766
Arend van Spriel5b435de2011-10-05 13:19:03 +02002767CleanUp:
2768
2769 kfree(buf);
2770
Arend van Sprield96b8012012-12-05 15:26:02 +01002771 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002772
2773 return err;
2774}
2775
Hante Meuleman89286dc2013-02-08 15:53:46 +01002776static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2777 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002778{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002779 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002780 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002781 struct brcmf_ssid *ssid;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002782 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002783 u16 beacon_interval;
2784 u8 dtim_period;
2785 size_t ie_len;
2786 u8 *ie;
2787 s32 err = 0;
2788
Arend van Sprield96b8012012-12-05 15:26:02 +01002789 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002790 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002791 return err;
2792
Arend van Spriel06bb1232012-09-27 14:17:56 +02002793 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002794
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002795 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002796 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002797 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002798 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002799 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002800 goto update_bss_info_out;
2801 }
2802
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002803 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2804 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002805 if (err)
2806 goto update_bss_info_out;
2807
2808 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2809 ie_len = le32_to_cpu(bi->ie_length);
2810 beacon_interval = le16_to_cpu(bi->beacon_period);
2811
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002812 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002813 if (tim)
2814 dtim_period = tim->data[1];
2815 else {
2816 /*
2817 * active scan was done so we could not get dtim
2818 * information out of probe response.
2819 * so we speficially query dtim information to dongle.
2820 */
2821 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002822 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002823 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002824 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002825 goto update_bss_info_out;
2826 }
2827 dtim_period = (u8)var;
2828 }
2829
Arend van Spriel5b435de2011-10-05 13:19:03 +02002830update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002831 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002832 return err;
2833}
2834
Hante Meuleman18e2f612013-02-08 15:53:49 +01002835void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002836{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002837 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002838
Arend van Sprielc1179032012-10-22 13:55:33 -07002839 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002840 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002841 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002842 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002843 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002844 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2845 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002846}
2847
Hante Meulemane756af52012-09-11 21:18:52 +02002848static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2849{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002850 struct brcmf_cfg80211_info *cfg =
2851 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002852 escan_timeout_work);
2853
Hante Meulemanef8596e2014-09-30 10:23:13 +02002854 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002855 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002856}
2857
2858static void brcmf_escan_timeout(unsigned long data)
2859{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002860 struct brcmf_cfg80211_info *cfg =
2861 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002862
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002863 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002864 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002865 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002866 }
2867}
2868
2869static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002870brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2871 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002872 struct brcmf_bss_info_le *bss_info_le)
2873{
Franky Lin83cf17a2013-04-11 13:28:50 +02002874 struct brcmu_chan ch_bss, ch_bss_info_le;
2875
2876 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2877 cfg->d11inf.decchspec(&ch_bss);
2878 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2879 cfg->d11inf.decchspec(&ch_bss_info_le);
2880
Hante Meulemane756af52012-09-11 21:18:52 +02002881 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002882 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002883 bss_info_le->SSID_len == bss->SSID_len &&
2884 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002885 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2886 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002887 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2888 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2889
Hante Meulemane756af52012-09-11 21:18:52 +02002890 /* preserve max RSSI if the measurements are
2891 * both on-channel or both off-channel
2892 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002893 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002894 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002895 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2896 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002897 /* preserve the on-channel rssi measurement
2898 * if the new measurement is off channel
2899 */
2900 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002901 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002902 }
2903 return 1;
2904 }
2905 return 0;
2906}
2907
2908static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002909brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002910 const struct brcmf_event_msg *e, void *data)
2911{
Arend van Spriel19937322012-11-05 16:22:32 -08002912 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002913 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002914 struct brcmf_escan_result_le *escan_result_le;
2915 struct brcmf_bss_info_le *bss_info_le;
2916 struct brcmf_bss_info_le *bss = NULL;
2917 u32 bi_length;
2918 struct brcmf_scan_results *list;
2919 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002920 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002921
Arend van Spriel5c36b992012-11-14 18:46:05 -08002922 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002923
Arend van Spriela0f472a2013-04-05 10:57:49 +02002924 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2925 brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002926 return -EPERM;
2927 }
2928
2929 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002930 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002931 escan_result_le = (struct brcmf_escan_result_le *) data;
2932 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002933 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002934 goto exit;
2935 }
Hante Meulemane756af52012-09-11 21:18:52 +02002936 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002937 brcmf_err("Invalid bss_count %d: ignoring\n",
2938 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002939 goto exit;
2940 }
2941 bss_info_le = &escan_result_le->bss_info_le;
2942
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002943 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2944 goto exit;
2945
2946 if (!cfg->scan_request) {
2947 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2948 goto exit;
2949 }
2950
Hante Meulemane756af52012-09-11 21:18:52 +02002951 bi_length = le32_to_cpu(bss_info_le->length);
2952 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2953 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002954 brcmf_err("Invalid bss_info length %d: ignoring\n",
2955 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002956 goto exit;
2957 }
2958
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002959 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002960 BIT(NL80211_IFTYPE_ADHOC))) {
2961 if (le16_to_cpu(bss_info_le->capability) &
2962 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002963 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002964 goto exit;
2965 }
2966 }
2967
2968 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002969 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002970 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002971 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002972 goto exit;
2973 }
2974
2975 for (i = 0; i < list->count; i++) {
2976 bss = bss ? (struct brcmf_bss_info_le *)
2977 ((unsigned char *)bss +
2978 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002979 if (brcmf_compare_update_same_bss(cfg, bss,
2980 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002981 goto exit;
2982 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002983 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002984 bss_info_le, bi_length);
2985 list->version = le32_to_cpu(bss_info_le->version);
2986 list->buflen += bi_length;
2987 list->count++;
2988 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002989 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002990 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2991 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002992 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002993 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002994 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02002995 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02002996 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002997 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2998 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002999 }
3000exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003001 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02003002}
3003
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003004static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003005{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003006 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3007 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003008 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3009 /* Init scan_timeout timer */
3010 init_timer(&cfg->escan_timeout);
3011 cfg->escan_timeout.data = (unsigned long) cfg;
3012 cfg->escan_timeout.function = brcmf_escan_timeout;
3013 INIT_WORK(&cfg->escan_timeout_work,
3014 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003015}
3016
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003017static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003018{
3019 if (ms < 1000 / HZ) {
3020 cond_resched();
3021 mdelay(ms);
3022 } else {
3023 msleep(ms);
3024 }
3025}
3026
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003027static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3028 u8 *pattern, u32 patternsize, u8 *mask,
3029 u32 packet_offset)
3030{
3031 struct brcmf_fil_wowl_pattern_le *filter;
3032 u32 masksize;
3033 u32 patternoffset;
3034 u8 *buf;
3035 u32 bufsize;
3036 s32 ret;
3037
3038 masksize = (patternsize + 7) / 8;
3039 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3040
3041 bufsize = sizeof(*filter) + patternsize + masksize;
3042 buf = kzalloc(bufsize, GFP_KERNEL);
3043 if (!buf)
3044 return -ENOMEM;
3045 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3046
3047 memcpy(filter->cmd, cmd, 4);
3048 filter->masksize = cpu_to_le32(masksize);
3049 filter->offset = cpu_to_le32(packet_offset);
3050 filter->patternoffset = cpu_to_le32(patternoffset);
3051 filter->patternsize = cpu_to_le32(patternsize);
3052 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3053
3054 if ((mask) && (masksize))
3055 memcpy(buf + sizeof(*filter), mask, masksize);
3056 if ((pattern) && (patternsize))
3057 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3058
3059 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3060
3061 kfree(buf);
3062 return ret;
3063}
3064
Arend van Spriel5b435de2011-10-05 13:19:03 +02003065static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3066{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003067 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3068 struct net_device *ndev = cfg_to_ndev(cfg);
3069 struct brcmf_if *ifp = netdev_priv(ndev);
3070
Arend van Sprield96b8012012-12-05 15:26:02 +01003071 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003072
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003073 if (cfg->wowl_enabled) {
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003074 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003075 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3076 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003077 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003078 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003079 cfg->wowl_enabled = false;
3080 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003081 return 0;
3082}
3083
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003084static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3085 struct brcmf_if *ifp,
3086 struct cfg80211_wowlan *wowl)
3087{
3088 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003089 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003090
3091 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3092
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003093 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003094 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3095 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3096
3097 wowl_config = 0;
3098 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003099 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003100 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003101 wowl_config |= BRCMF_WOWL_MAGIC;
3102 if ((wowl->patterns) && (wowl->n_patterns)) {
3103 wowl_config |= BRCMF_WOWL_NET;
3104 for (i = 0; i < wowl->n_patterns; i++) {
3105 brcmf_config_wowl_pattern(ifp, "add",
3106 (u8 *)wowl->patterns[i].pattern,
3107 wowl->patterns[i].pattern_len,
3108 (u8 *)wowl->patterns[i].mask,
3109 wowl->patterns[i].pkt_offset);
3110 }
3111 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003112 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3113 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3114 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3115 cfg->wowl_enabled = true;
3116}
3117
Arend van Spriel5b435de2011-10-05 13:19:03 +02003118static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003119 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003120{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003121 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3122 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003123 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003124 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003125
Arend van Sprield96b8012012-12-05 15:26:02 +01003126 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003127
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003128 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003129 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003130 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003131 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003132 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003133
Arend van Spriel7d641072012-10-22 13:55:39 -07003134 /* end any scanning */
3135 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003136 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003137
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003138 if (wowl == NULL) {
3139 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3140 list_for_each_entry(vif, &cfg->vif_list, list) {
3141 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3142 continue;
3143 /* While going to suspend if associated with AP
3144 * disassociate from AP to save power while system is
3145 * in suspended state
3146 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003147 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003148 /* Make sure WPA_Supplicant receives all the event
3149 * generated due to DISASSOC call to the fw to keep
3150 * the state fw and WPA_Supplicant state consistent
3151 */
3152 brcmf_delay(500);
3153 }
3154 /* Configure MPC */
3155 brcmf_set_mpc(ifp, 1);
3156
3157 } else {
3158 /* Configure WOWL paramaters */
3159 brcmf_configure_wowl(cfg, ifp, wowl);
3160 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003161
Arend van Spriel7d641072012-10-22 13:55:39 -07003162exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003163 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003164 /* clear any scanning activity */
3165 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003166 return 0;
3167}
3168
3169static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003170brcmf_update_pmklist(struct net_device *ndev,
3171 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3172{
3173 int i, j;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003174 u32 pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003175
Arend van Spriel40c8e952011-10-12 20:51:20 +02003176 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3177
Arend van Spriel16886732012-12-05 15:26:04 +01003178 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003179 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01003180 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
3181 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003182 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01003183 brcmf_dbg(CONN, "%02x\n",
3184 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003185 }
3186
3187 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07003188 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
3189 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003190
3191 return err;
3192}
3193
3194static s32
3195brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3196 struct cfg80211_pmksa *pmksa)
3197{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003198 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003199 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003200 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003201 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003202 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003203
Arend van Sprield96b8012012-12-05 15:26:02 +01003204 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003205 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003206 return -EIO;
3207
Arend van Spriel40c8e952011-10-12 20:51:20 +02003208 pmkid_len = le32_to_cpu(pmkids->npmkid);
3209 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003210 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3211 break;
3212 if (i < WL_NUM_PMKIDS_MAX) {
3213 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3214 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003215 if (i == pmkid_len) {
3216 pmkid_len++;
3217 pmkids->npmkid = cpu_to_le32(pmkid_len);
3218 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003219 } else
3220 err = -EINVAL;
3221
Arend van Spriel16886732012-12-05 15:26:04 +01003222 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
3223 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003224 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003225 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003226
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003227 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003228
Arend van Sprield96b8012012-12-05 15:26:02 +01003229 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003230 return err;
3231}
3232
3233static s32
3234brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3235 struct cfg80211_pmksa *pmksa)
3236{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003237 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003238 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003239 struct pmkid_list pmkid;
3240 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003241 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003242
Arend van Sprield96b8012012-12-05 15:26:02 +01003243 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003244 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003245 return -EIO;
3246
3247 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3248 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3249
Arend van Spriel16886732012-12-05 15:26:04 +01003250 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3251 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003252 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003253 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003254
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003255 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003256 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003257 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003258 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003259 ETH_ALEN))
3260 break;
3261
Arend van Spriel40c8e952011-10-12 20:51:20 +02003262 if ((pmkid_len > 0)
3263 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003264 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003265 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003266 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003267 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3268 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003269 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003270 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3271 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003272 WLAN_PMKID_LEN);
3273 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003274 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003275 } else
3276 err = -EINVAL;
3277
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003278 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003279
Arend van Sprield96b8012012-12-05 15:26:02 +01003280 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003281 return err;
3282
3283}
3284
3285static s32
3286brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3287{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003288 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003289 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003290 s32 err = 0;
3291
Arend van Sprield96b8012012-12-05 15:26:02 +01003292 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003293 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003294 return -EIO;
3295
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003296 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3297 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003298
Arend van Sprield96b8012012-12-05 15:26:02 +01003299 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003300 return err;
3301
3302}
3303
Arend van Spriele5806072012-09-19 22:21:08 +02003304/*
3305 * PFN result doesn't have all the info which are
3306 * required by the supplicant
3307 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3308 * via wl_inform_single_bss in the required format. Escan does require the
3309 * scan request in the form of cfg80211_scan_request. For timebeing, create
3310 * cfg80211_scan_request one out of the received PNO event.
3311 */
3312static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003313brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003314 const struct brcmf_event_msg *e, void *data)
3315{
Arend van Spriel19937322012-11-05 16:22:32 -08003316 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003317 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3318 struct cfg80211_scan_request *request = NULL;
3319 struct cfg80211_ssid *ssid = NULL;
3320 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003321 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003322 int err = 0;
3323 int channel_req = 0;
3324 int band = 0;
3325 struct brcmf_pno_scanresults_le *pfn_result;
3326 u32 result_count;
3327 u32 status;
3328
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003329 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003330
Arend van Spriel5c36b992012-11-14 18:46:05 -08003331 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003332 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003333 return 0;
3334 }
3335
3336 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3337 result_count = le32_to_cpu(pfn_result->count);
3338 status = le32_to_cpu(pfn_result->status);
3339
3340 /*
3341 * PFN event is limited to fit 512 bytes so we may get
3342 * multiple NET_FOUND events. For now place a warning here.
3343 */
3344 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003345 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003346 if (result_count > 0) {
3347 int i;
3348
3349 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003350 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3351 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003352 if (!request || !ssid || !channel) {
3353 err = -ENOMEM;
3354 goto out_err;
3355 }
3356
3357 request->wiphy = wiphy;
3358 data += sizeof(struct brcmf_pno_scanresults_le);
3359 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3360
3361 for (i = 0; i < result_count; i++) {
3362 netinfo = &netinfo_start[i];
3363 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003364 brcmf_err("Invalid netinfo ptr. index: %d\n",
3365 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003366 err = -EINVAL;
3367 goto out_err;
3368 }
3369
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003370 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3371 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003372 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3373 ssid[i].ssid_len = netinfo->SSID_len;
3374 request->n_ssids++;
3375
3376 channel_req = netinfo->channel;
3377 if (channel_req <= CH_MAX_2G_CHANNEL)
3378 band = NL80211_BAND_2GHZ;
3379 else
3380 band = NL80211_BAND_5GHZ;
3381 channel[i].center_freq =
3382 ieee80211_channel_to_frequency(channel_req,
3383 band);
3384 channel[i].band = band;
3385 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3386 request->channels[i] = &channel[i];
3387 request->n_channels++;
3388 }
3389
3390 /* assign parsed ssid array */
3391 if (request->n_ssids)
3392 request->ssids = &ssid[0];
3393
Arend van Sprielc1179032012-10-22 13:55:33 -07003394 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003395 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003396 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003397 }
3398
Arend van Sprielc1179032012-10-22 13:55:33 -07003399 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003400 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003401 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003402 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003403 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003404 goto out_err;
3405 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003406 cfg->sched_escan = true;
3407 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003408 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003409 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003410 goto out_err;
3411 }
3412
3413 kfree(ssid);
3414 kfree(channel);
3415 kfree(request);
3416 return 0;
3417
3418out_err:
3419 kfree(ssid);
3420 kfree(channel);
3421 kfree(request);
3422 cfg80211_sched_scan_stopped(wiphy);
3423 return err;
3424}
3425
Arend van Spriele5806072012-09-19 22:21:08 +02003426static int brcmf_dev_pno_clean(struct net_device *ndev)
3427{
Arend van Spriele5806072012-09-19 22:21:08 +02003428 int ret;
3429
3430 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003431 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003432 if (ret == 0) {
3433 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003434 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3435 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003436 }
3437 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003438 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003439
3440 return ret;
3441}
3442
3443static int brcmf_dev_pno_config(struct net_device *ndev)
3444{
3445 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003446
3447 memset(&pfn_param, 0, sizeof(pfn_param));
3448 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3449
3450 /* set extra pno params */
3451 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3452 pfn_param.repeat = BRCMF_PNO_REPEAT;
3453 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3454
3455 /* set up pno scan fr */
3456 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3457
Arend van Sprielac24be62012-10-22 10:36:23 -07003458 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3459 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003460}
3461
3462static int
3463brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3464 struct net_device *ndev,
3465 struct cfg80211_sched_scan_request *request)
3466{
Arend van Sprielc1179032012-10-22 13:55:33 -07003467 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003468 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003469 struct brcmf_pno_net_param_le pfn;
3470 int i;
3471 int ret = 0;
3472
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003473 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003474 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003475 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003476 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003477 return -EAGAIN;
3478 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003479 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3480 brcmf_err("Scanning suppressed: status (%lu)\n",
3481 cfg->scan_status);
3482 return -EAGAIN;
3483 }
Arend van Spriele5806072012-09-19 22:21:08 +02003484
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003485 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003486 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003487 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003488 return -EINVAL;
3489 }
3490
3491 if (request->n_ssids > 0) {
3492 for (i = 0; i < request->n_ssids; i++) {
3493 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003494 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3495 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003496
3497 /*
3498 * match_set ssids is a supert set of n_ssid list,
3499 * so we need not add these set seperately.
3500 */
3501 }
3502 }
3503
3504 if (request->n_match_sets > 0) {
3505 /* clean up everything */
3506 ret = brcmf_dev_pno_clean(ndev);
3507 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003508 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003509 return ret;
3510 }
3511
3512 /* configure pno */
3513 ret = brcmf_dev_pno_config(ndev);
3514 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003515 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003516 return -EINVAL;
3517 }
3518
3519 /* configure each match set */
3520 for (i = 0; i < request->n_match_sets; i++) {
3521 struct cfg80211_ssid *ssid;
3522 u32 ssid_len;
3523
3524 ssid = &request->match_sets[i].ssid;
3525 ssid_len = ssid->ssid_len;
3526
3527 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003528 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003529 continue;
3530 }
3531 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3532 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3533 pfn.wsec = cpu_to_le32(0);
3534 pfn.infra = cpu_to_le32(1);
3535 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3536 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3537 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003538 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003539 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003540 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3541 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003542 }
3543 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003544 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003545 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003546 return -EINVAL;
3547 }
3548 } else {
3549 return -EINVAL;
3550 }
3551
3552 return 0;
3553}
3554
3555static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3556 struct net_device *ndev)
3557{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003558 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003559
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003560 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003561 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003562 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003563 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003564 return 0;
3565}
Arend van Spriele5806072012-09-19 22:21:08 +02003566
Hante Meuleman1f170112013-02-06 18:40:38 +01003567static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003568{
3569 s32 err;
3570
3571 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003572 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003573 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003574 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003575 return err;
3576 }
3577 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003578 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003579 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003580 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003581 return err;
3582 }
3583 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003584 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003585 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003586 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003587 return err;
3588 }
3589
3590 return 0;
3591}
3592
3593static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3594{
3595 if (is_rsn_ie)
3596 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3597
3598 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3599}
3600
3601static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003602brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003603 const struct brcmf_vs_tlv *wpa_ie,
3604 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003605{
3606 u32 auth = 0; /* d11 open authentication */
3607 u16 count;
3608 s32 err = 0;
3609 s32 len = 0;
3610 u32 i;
3611 u32 wsec;
3612 u32 pval = 0;
3613 u32 gval = 0;
3614 u32 wpa_auth = 0;
3615 u32 offset;
3616 u8 *data;
3617 u16 rsn_cap;
3618 u32 wme_bss_disable;
3619
Arend van Sprield96b8012012-12-05 15:26:02 +01003620 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003621 if (wpa_ie == NULL)
3622 goto exit;
3623
3624 len = wpa_ie->len + TLV_HDR_LEN;
3625 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003626 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003627 if (!is_rsn_ie)
3628 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003629 else
3630 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003631
3632 /* check for multicast cipher suite */
3633 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3634 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003635 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003636 goto exit;
3637 }
3638
3639 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3640 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003641 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003642 goto exit;
3643 }
3644 offset += TLV_OUI_LEN;
3645
3646 /* pick up multicast cipher */
3647 switch (data[offset]) {
3648 case WPA_CIPHER_NONE:
3649 gval = 0;
3650 break;
3651 case WPA_CIPHER_WEP_40:
3652 case WPA_CIPHER_WEP_104:
3653 gval = WEP_ENABLED;
3654 break;
3655 case WPA_CIPHER_TKIP:
3656 gval = TKIP_ENABLED;
3657 break;
3658 case WPA_CIPHER_AES_CCM:
3659 gval = AES_ENABLED;
3660 break;
3661 default:
3662 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003663 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003664 goto exit;
3665 }
3666
3667 offset++;
3668 /* walk thru unicast cipher list and pick up what we recognize */
3669 count = data[offset] + (data[offset + 1] << 8);
3670 offset += WPA_IE_SUITE_COUNT_LEN;
3671 /* Check for unicast suite(s) */
3672 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3673 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003674 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003675 goto exit;
3676 }
3677 for (i = 0; i < count; i++) {
3678 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3679 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003680 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003681 goto exit;
3682 }
3683 offset += TLV_OUI_LEN;
3684 switch (data[offset]) {
3685 case WPA_CIPHER_NONE:
3686 break;
3687 case WPA_CIPHER_WEP_40:
3688 case WPA_CIPHER_WEP_104:
3689 pval |= WEP_ENABLED;
3690 break;
3691 case WPA_CIPHER_TKIP:
3692 pval |= TKIP_ENABLED;
3693 break;
3694 case WPA_CIPHER_AES_CCM:
3695 pval |= AES_ENABLED;
3696 break;
3697 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003698 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003699 }
3700 offset++;
3701 }
3702 /* walk thru auth management suite list and pick up what we recognize */
3703 count = data[offset] + (data[offset + 1] << 8);
3704 offset += WPA_IE_SUITE_COUNT_LEN;
3705 /* Check for auth key management suite(s) */
3706 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3707 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003708 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003709 goto exit;
3710 }
3711 for (i = 0; i < count; i++) {
3712 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3713 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003714 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003715 goto exit;
3716 }
3717 offset += TLV_OUI_LEN;
3718 switch (data[offset]) {
3719 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003720 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003721 wpa_auth |= WPA_AUTH_NONE;
3722 break;
3723 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003724 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003725 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3726 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3727 break;
3728 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003729 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003730 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3731 (wpa_auth |= WPA_AUTH_PSK);
3732 break;
3733 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003734 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003735 }
3736 offset++;
3737 }
3738
3739 if (is_rsn_ie) {
3740 wme_bss_disable = 1;
3741 if ((offset + RSN_CAP_LEN) <= len) {
3742 rsn_cap = data[offset] + (data[offset + 1] << 8);
3743 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3744 wme_bss_disable = 0;
3745 }
3746 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003747 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003748 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003749 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003750 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003751 goto exit;
3752 }
3753 }
3754 /* FOR WPS , set SES_OW_ENABLED */
3755 wsec = (pval | gval | SES_OW_ENABLED);
3756
3757 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003758 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003759 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003760 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003761 goto exit;
3762 }
3763 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003764 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003765 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003766 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003767 goto exit;
3768 }
3769 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003770 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003771 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003772 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003773 goto exit;
3774 }
3775
3776exit:
3777 return err;
3778}
3779
3780static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003781brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003782 struct parsed_vndr_ies *vndr_ies)
3783{
Hante Meuleman1a873342012-09-27 14:17:54 +02003784 struct brcmf_vs_tlv *vndrie;
3785 struct brcmf_tlv *ie;
3786 struct parsed_vndr_ie_info *parsed_info;
3787 s32 remaining_len;
3788
3789 remaining_len = (s32)vndr_ie_len;
3790 memset(vndr_ies, 0, sizeof(*vndr_ies));
3791
3792 ie = (struct brcmf_tlv *)vndr_ie_buf;
3793 while (ie) {
3794 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3795 goto next;
3796 vndrie = (struct brcmf_vs_tlv *)ie;
3797 /* len should be bigger than OUI length + one */
3798 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003799 brcmf_err("invalid vndr ie. length is too small %d\n",
3800 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003801 goto next;
3802 }
3803 /* if wpa or wme ie, do not add ie */
3804 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3805 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3806 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003807 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003808 goto next;
3809 }
3810
3811 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3812
3813 /* save vndr ie information */
3814 parsed_info->ie_ptr = (char *)vndrie;
3815 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3816 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3817
3818 vndr_ies->count++;
3819
Arend van Sprield96b8012012-12-05 15:26:02 +01003820 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3821 parsed_info->vndrie.oui[0],
3822 parsed_info->vndrie.oui[1],
3823 parsed_info->vndrie.oui[2],
3824 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003825
Arend van Spriel9f440b72013-02-08 15:53:36 +01003826 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003827 break;
3828next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003829 remaining_len -= (ie->len + TLV_HDR_LEN);
3830 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003831 ie = NULL;
3832 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003833 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3834 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003835 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003836 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003837}
3838
3839static u32
3840brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3841{
3842
Hante Meuleman1a873342012-09-27 14:17:54 +02003843 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3844 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3845
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303846 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003847
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303848 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003849
3850 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3851
3852 return ie_len + VNDR_IE_HDR_SIZE;
3853}
3854
Arend van Spriel1332e262012-11-05 16:22:18 -08003855s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3856 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003857{
Arend van Spriel1332e262012-11-05 16:22:18 -08003858 struct brcmf_if *ifp;
3859 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003860 s32 err = 0;
3861 u8 *iovar_ie_buf;
3862 u8 *curr_ie_buf;
3863 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003864 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003865 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003866 u32 del_add_ie_buf_len = 0;
3867 u32 total_ie_buf_len = 0;
3868 u32 parsed_ie_buf_len = 0;
3869 struct parsed_vndr_ies old_vndr_ies;
3870 struct parsed_vndr_ies new_vndr_ies;
3871 struct parsed_vndr_ie_info *vndrie_info;
3872 s32 i;
3873 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003874 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003875
Arend van Spriel1332e262012-11-05 16:22:18 -08003876 if (!vif)
3877 return -ENODEV;
3878 ifp = vif->ifp;
3879 saved_ie = &vif->saved_ie;
3880
Arend van Sprield96b8012012-12-05 15:26:02 +01003881 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003882 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3883 if (!iovar_ie_buf)
3884 return -ENOMEM;
3885 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003886 switch (pktflag) {
3887 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3888 mgmt_ie_buf = saved_ie->probe_req_ie;
3889 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3890 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3891 break;
3892 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3893 mgmt_ie_buf = saved_ie->probe_res_ie;
3894 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3895 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3896 break;
3897 case BRCMF_VNDR_IE_BEACON_FLAG:
3898 mgmt_ie_buf = saved_ie->beacon_ie;
3899 mgmt_ie_len = &saved_ie->beacon_ie_len;
3900 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3901 break;
3902 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3903 mgmt_ie_buf = saved_ie->assoc_req_ie;
3904 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3905 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3906 break;
3907 default:
3908 err = -EPERM;
3909 brcmf_err("not suitable type\n");
3910 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003911 }
3912
3913 if (vndr_ie_len > mgmt_ie_buf_len) {
3914 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003915 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003916 goto exit;
3917 }
3918
3919 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3920 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3921 ptr = curr_ie_buf;
3922 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3923 for (i = 0; i < new_vndr_ies.count; i++) {
3924 vndrie_info = &new_vndr_ies.ie_info[i];
3925 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3926 vndrie_info->ie_len);
3927 parsed_ie_buf_len += vndrie_info->ie_len;
3928 }
3929 }
3930
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003931 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003932 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3933 (memcmp(mgmt_ie_buf, curr_ie_buf,
3934 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003935 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003936 goto exit;
3937 }
3938
3939 /* parse old vndr_ie */
3940 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3941
3942 /* make a command to delete old ie */
3943 for (i = 0; i < old_vndr_ies.count; i++) {
3944 vndrie_info = &old_vndr_ies.ie_info[i];
3945
Arend van Sprield96b8012012-12-05 15:26:02 +01003946 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3947 vndrie_info->vndrie.id,
3948 vndrie_info->vndrie.len,
3949 vndrie_info->vndrie.oui[0],
3950 vndrie_info->vndrie.oui[1],
3951 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003952
3953 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3954 vndrie_info->ie_ptr,
3955 vndrie_info->ie_len,
3956 "del");
3957 curr_ie_buf += del_add_ie_buf_len;
3958 total_ie_buf_len += del_add_ie_buf_len;
3959 }
3960 }
3961
3962 *mgmt_ie_len = 0;
3963 /* Add if there is any extra IE */
3964 if (mgmt_ie_buf && parsed_ie_buf_len) {
3965 ptr = mgmt_ie_buf;
3966
3967 remained_buf_len = mgmt_ie_buf_len;
3968
3969 /* make a command to add new ie */
3970 for (i = 0; i < new_vndr_ies.count; i++) {
3971 vndrie_info = &new_vndr_ies.ie_info[i];
3972
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003973 /* verify remained buf size before copy data */
3974 if (remained_buf_len < (vndrie_info->vndrie.len +
3975 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003976 brcmf_err("no space in mgmt_ie_buf: len left %d",
3977 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003978 break;
3979 }
3980 remained_buf_len -= (vndrie_info->ie_len +
3981 VNDR_IE_VSIE_OFFSET);
3982
Arend van Sprield96b8012012-12-05 15:26:02 +01003983 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3984 vndrie_info->vndrie.id,
3985 vndrie_info->vndrie.len,
3986 vndrie_info->vndrie.oui[0],
3987 vndrie_info->vndrie.oui[1],
3988 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003989
3990 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3991 vndrie_info->ie_ptr,
3992 vndrie_info->ie_len,
3993 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003994
3995 /* save the parsed IE in wl struct */
3996 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3997 vndrie_info->ie_len);
3998 *mgmt_ie_len += vndrie_info->ie_len;
3999
4000 curr_ie_buf += del_add_ie_buf_len;
4001 total_ie_buf_len += del_add_ie_buf_len;
4002 }
4003 }
4004 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004005 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004006 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004007 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004008 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004009 }
4010
4011exit:
4012 kfree(iovar_ie_buf);
4013 return err;
4014}
4015
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004016s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4017{
4018 s32 pktflags[] = {
4019 BRCMF_VNDR_IE_PRBREQ_FLAG,
4020 BRCMF_VNDR_IE_PRBRSP_FLAG,
4021 BRCMF_VNDR_IE_BEACON_FLAG
4022 };
4023 int i;
4024
4025 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4026 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4027
4028 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4029 return 0;
4030}
4031
Hante Meuleman1a873342012-09-27 14:17:54 +02004032static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004033brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4034 struct cfg80211_beacon_data *beacon)
4035{
4036 s32 err;
4037
4038 /* Set Beacon IEs to FW */
4039 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4040 beacon->tail, beacon->tail_len);
4041 if (err) {
4042 brcmf_err("Set Beacon IE Failed\n");
4043 return err;
4044 }
4045 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4046
4047 /* Set Probe Response IEs to FW */
4048 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4049 beacon->proberesp_ies,
4050 beacon->proberesp_ies_len);
4051 if (err)
4052 brcmf_err("Set Probe Resp IE Failed\n");
4053 else
4054 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4055
4056 return err;
4057}
4058
4059static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004060brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4061 struct cfg80211_ap_settings *settings)
4062{
4063 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004064 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004065 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004066 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004067 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004068 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004069 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004070 const struct brcmf_tlv *rsn_ie;
4071 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004072 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004073 enum nl80211_iftype dev_role;
4074 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004075 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004076 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004077 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004078
Arend van Spriel06c01582014-05-12 10:47:37 +02004079 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4080 settings->chandef.chan->hw_value,
4081 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004082 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004083 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4084 settings->ssid, settings->ssid_len, settings->auth_type,
4085 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004086 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004087 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004088
Arend van Spriel98027762014-12-21 12:43:53 +01004089 /* store current 11d setting */
4090 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4091 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4092 settings->beacon.tail_len,
4093 WLAN_EID_COUNTRY);
4094 is_11d = country_ie ? 1 : 0;
4095
Hante Meuleman1a873342012-09-27 14:17:54 +02004096 memset(&ssid_le, 0, sizeof(ssid_le));
4097 if (settings->ssid == NULL || settings->ssid_len == 0) {
4098 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4099 ssid_ie = brcmf_parse_tlvs(
4100 (u8 *)&settings->beacon.head[ie_offset],
4101 settings->beacon.head_len - ie_offset,
4102 WLAN_EID_SSID);
4103 if (!ssid_ie)
4104 return -EINVAL;
4105
4106 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4107 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004108 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004109 } else {
4110 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4111 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4112 }
4113
Hante Meulemana44aa402014-12-03 21:05:33 +01004114 if (!mbss) {
4115 brcmf_set_mpc(ifp, 0);
4116 brcmf_configure_arp_offload(ifp, false);
4117 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004118
4119 /* find the RSN_IE */
4120 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4121 settings->beacon.tail_len, WLAN_EID_RSN);
4122
4123 /* find the WPA_IE */
4124 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4125 settings->beacon.tail_len);
4126
Hante Meuleman1a873342012-09-27 14:17:54 +02004127 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004128 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004129 if (wpa_ie != NULL) {
4130 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004131 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004132 if (err < 0)
4133 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004134 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004135 struct brcmf_vs_tlv *tmp_ie;
4136
4137 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4138
Hante Meuleman1a873342012-09-27 14:17:54 +02004139 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004140 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004141 if (err < 0)
4142 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004143 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004144 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004145 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004146 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004147 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004148
Hante Meulemana0f07952013-02-08 15:53:47 +01004149 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004150
Hante Meulemana44aa402014-12-03 21:05:33 +01004151 if (!mbss) {
4152 chanspec = chandef_to_chanspec(&cfg->d11inf,
4153 &settings->chandef);
4154 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004155 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004156 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4157 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004158 goto exit;
4159 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004160
Arend van Spriel98027762014-12-21 12:43:53 +01004161 if (is_11d != ifp->vif->is_11d) {
4162 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4163 is_11d);
4164 if (err < 0) {
4165 brcmf_err("Regulatory Set Error, %d\n", err);
4166 goto exit;
4167 }
4168 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004169 if (settings->beacon_interval) {
4170 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4171 settings->beacon_interval);
4172 if (err < 0) {
4173 brcmf_err("Beacon Interval Set Error, %d\n",
4174 err);
4175 goto exit;
4176 }
4177 }
4178 if (settings->dtim_period) {
4179 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4180 settings->dtim_period);
4181 if (err < 0) {
4182 brcmf_err("DTIM Interval Set Error, %d\n", err);
4183 goto exit;
4184 }
4185 }
4186
4187 if (dev_role == NL80211_IFTYPE_AP) {
4188 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4189 if (err < 0) {
4190 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4191 goto exit;
4192 }
4193 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4194 }
4195
4196 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004197 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004198 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004199 goto exit;
4200 }
Arend van Spriel98027762014-12-21 12:43:53 +01004201 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4202 /* Multiple-BSS should use same 11d configuration */
4203 err = -EINVAL;
4204 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004205 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004206 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004207 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4208 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4209
Hante Meulemana0f07952013-02-08 15:53:47 +01004210 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4211 if (err < 0) {
4212 brcmf_err("setting AP mode failed %d\n", err);
4213 goto exit;
4214 }
4215 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4216 if (err < 0) {
4217 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4218 goto exit;
4219 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004220 /* On DOWN the firmware removes the WEP keys, reconfigure
4221 * them if they were set.
4222 */
4223 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004224
4225 memset(&join_params, 0, sizeof(join_params));
4226 /* join parameters starts with ssid */
4227 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4228 /* create softap */
4229 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4230 &join_params, sizeof(join_params));
4231 if (err < 0) {
4232 brcmf_err("SET SSID error (%d)\n", err);
4233 goto exit;
4234 }
4235 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4236 } else {
4237 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4238 sizeof(ssid_le));
4239 if (err < 0) {
4240 brcmf_err("setting ssid failed %d\n", err);
4241 goto exit;
4242 }
4243 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4244 bss_enable.enable = cpu_to_le32(1);
4245 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4246 sizeof(bss_enable));
4247 if (err < 0) {
4248 brcmf_err("bss_enable config failed %d\n", err);
4249 goto exit;
4250 }
4251
4252 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4253 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004254 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4255 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02004256
4257exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004258 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004259 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004260 brcmf_configure_arp_offload(ifp, true);
4261 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004262 return err;
4263}
4264
4265static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4266{
Arend van Sprielc1179032012-10-22 13:55:33 -07004267 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004268 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004269 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004270 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004271
Arend van Sprield96b8012012-12-05 15:26:02 +01004272 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004273
Hante Meuleman426d0a52013-02-08 15:53:53 +01004274 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004275 /* Due to most likely deauths outstanding we sleep */
4276 /* first to make sure they get processed by fw. */
4277 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004278
Hante Meulemana44aa402014-12-03 21:05:33 +01004279 if (ifp->vif->mbss) {
4280 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4281 return err;
4282 }
4283
Hante Meuleman5c33a942013-04-02 21:06:18 +02004284 memset(&join_params, 0, sizeof(join_params));
4285 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4286 &join_params, sizeof(join_params));
4287 if (err < 0)
4288 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004289 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004290 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004291 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004292 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4293 if (err < 0)
4294 brcmf_err("setting AP mode failed %d\n", err);
4295 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4296 if (err < 0)
4297 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004298 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4299 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004300 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4301 ifp->vif->is_11d);
4302 if (err < 0)
4303 brcmf_err("restoring REGULATORY setting failed %d\n",
4304 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004305 /* Bring device back up so it can be used again */
4306 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4307 if (err < 0)
4308 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004309 } else {
4310 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4311 bss_enable.enable = cpu_to_le32(0);
4312 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4313 sizeof(bss_enable));
4314 if (err < 0)
4315 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004316 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004317 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004318 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004319 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4320 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
4321
Hante Meuleman1a873342012-09-27 14:17:54 +02004322 return err;
4323}
4324
Hante Meulemana0f07952013-02-08 15:53:47 +01004325static s32
4326brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4327 struct cfg80211_beacon_data *info)
4328{
Hante Meulemana0f07952013-02-08 15:53:47 +01004329 struct brcmf_if *ifp = netdev_priv(ndev);
4330 s32 err;
4331
4332 brcmf_dbg(TRACE, "Enter\n");
4333
Hante Meulemana0f07952013-02-08 15:53:47 +01004334 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4335
4336 return err;
4337}
4338
Hante Meuleman1a873342012-09-27 14:17:54 +02004339static int
4340brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004341 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004342{
Hante Meulemana0f07952013-02-08 15:53:47 +01004343 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004344 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004345 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004346 s32 err;
4347
Jouni Malinen89c771e2014-10-10 20:52:40 +03004348 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004349 return -EFAULT;
4350
Jouni Malinen89c771e2014-10-10 20:52:40 +03004351 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004352
Hante Meulemana0f07952013-02-08 15:53:47 +01004353 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4354 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004355 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004356 return -EIO;
4357
Jouni Malinen89c771e2014-10-10 20:52:40 +03004358 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004359 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004360 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004361 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004362 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004363 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004364
Arend van Sprield96b8012012-12-05 15:26:02 +01004365 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004366 return err;
4367}
4368
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004369static int
4370brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4371 const u8 *mac, struct station_parameters *params)
4372{
4373 struct brcmf_if *ifp = netdev_priv(ndev);
4374 s32 err;
4375
4376 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4377 params->sta_flags_mask, params->sta_flags_set);
4378
4379 /* Ignore all 00 MAC */
4380 if (is_zero_ether_addr(mac))
4381 return 0;
4382
4383 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4384 return 0;
4385
4386 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4387 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4388 (void *)mac, ETH_ALEN);
4389 else
4390 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4391 (void *)mac, ETH_ALEN);
4392 if (err < 0)
4393 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4394
4395 return err;
4396}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004397
4398static void
4399brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4400 struct wireless_dev *wdev,
4401 u16 frame_type, bool reg)
4402{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004403 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004404 u16 mgmt_type;
4405
4406 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4407
4408 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004409 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004410 if (reg)
4411 vif->mgmt_rx_reg |= BIT(mgmt_type);
4412 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004413 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004414}
4415
4416
4417static int
4418brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004419 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004420{
4421 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004422 struct ieee80211_channel *chan = params->chan;
4423 const u8 *buf = params->buf;
4424 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004425 const struct ieee80211_mgmt *mgmt;
4426 struct brcmf_cfg80211_vif *vif;
4427 s32 err = 0;
4428 s32 ie_offset;
4429 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004430 struct brcmf_fil_action_frame_le *action_frame;
4431 struct brcmf_fil_af_params_le *af_params;
4432 bool ack;
4433 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004434 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004435
4436 brcmf_dbg(TRACE, "Enter\n");
4437
4438 *cookie = 0;
4439
4440 mgmt = (const struct ieee80211_mgmt *)buf;
4441
Hante Meulemana0f07952013-02-08 15:53:47 +01004442 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4443 brcmf_err("Driver only allows MGMT packet type\n");
4444 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004445 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004446
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004447 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4448
Hante Meulemana0f07952013-02-08 15:53:47 +01004449 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4450 /* Right now the only reason to get a probe response */
4451 /* is for p2p listen response or for p2p GO from */
4452 /* wpa_supplicant. Unfortunately the probe is send */
4453 /* on primary ndev, while dongle wants it on the p2p */
4454 /* vif. Since this is only reason for a probe */
4455 /* response to be sent, the vif is taken from cfg. */
4456 /* If ever desired to send proberesp for non p2p */
4457 /* response then data should be checked for */
4458 /* "DIRECT-". Note in future supplicant will take */
4459 /* dedicated p2p wdev to do this and then this 'hack'*/
4460 /* is not needed anymore. */
4461 ie_offset = DOT11_MGMT_HDR_LEN +
4462 DOT11_BCN_PRB_FIXED_LEN;
4463 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004464 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4465 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4466 err = brcmf_vif_set_mgmt_ie(vif,
4467 BRCMF_VNDR_IE_PRBRSP_FLAG,
4468 &buf[ie_offset],
4469 ie_len);
4470 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4471 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004472 } else if (ieee80211_is_action(mgmt->frame_control)) {
4473 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4474 if (af_params == NULL) {
4475 brcmf_err("unable to allocate frame\n");
4476 err = -ENOMEM;
4477 goto exit;
4478 }
4479 action_frame = &af_params->action_frame;
4480 /* Add the packet Id */
4481 action_frame->packet_id = cpu_to_le32(*cookie);
4482 /* Add BSSID */
4483 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4484 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4485 /* Add the length exepted for 802.11 header */
4486 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004487 /* Add the channel. Use the one specified as parameter if any or
4488 * the current one (got from the firmware) otherwise
4489 */
4490 if (chan)
4491 freq = chan->center_freq;
4492 else
4493 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4494 &freq);
4495 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004496 af_params->channel = cpu_to_le32(chan_nr);
4497
4498 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4499 le16_to_cpu(action_frame->len));
4500
4501 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004502 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004503
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004504 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004505 af_params);
4506
4507 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4508 GFP_KERNEL);
4509 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004510 } else {
4511 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4512 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4513 }
4514
Hante Meuleman18e2f612013-02-08 15:53:49 +01004515exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004516 return err;
4517}
4518
4519
4520static int
4521brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4522 struct wireless_dev *wdev,
4523 u64 cookie)
4524{
4525 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4526 struct brcmf_cfg80211_vif *vif;
4527 int err = 0;
4528
4529 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4530
4531 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4532 if (vif == NULL) {
4533 brcmf_err("No p2p device available for probe response\n");
4534 err = -ENODEV;
4535 goto exit;
4536 }
4537 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4538exit:
4539 return err;
4540}
4541
Piotr Haber61730d42013-04-23 12:53:12 +02004542static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4543 struct wireless_dev *wdev,
4544 enum nl80211_crit_proto_id proto,
4545 u16 duration)
4546{
4547 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4548 struct brcmf_cfg80211_vif *vif;
4549
4550 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4551
4552 /* only DHCP support for now */
4553 if (proto != NL80211_CRIT_PROTO_DHCP)
4554 return -EINVAL;
4555
4556 /* suppress and abort scanning */
4557 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4558 brcmf_abort_scanning(cfg);
4559
4560 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4561}
4562
4563static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4564 struct wireless_dev *wdev)
4565{
4566 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4567 struct brcmf_cfg80211_vif *vif;
4568
4569 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4570
4571 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4572 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4573}
4574
Hante Meuleman70b7d942014-07-30 13:20:07 +02004575static s32
4576brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4577 const struct brcmf_event_msg *e, void *data)
4578{
4579 switch (e->reason) {
4580 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4581 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4582 break;
4583 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4584 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4585 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4586 break;
4587 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4588 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4589 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4590 break;
4591 }
4592
4593 return 0;
4594}
4595
Arend van Spriel89c2f382013-08-10 12:27:25 +02004596static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4597{
4598 int ret;
4599
4600 switch (oper) {
4601 case NL80211_TDLS_DISCOVERY_REQ:
4602 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4603 break;
4604 case NL80211_TDLS_SETUP:
4605 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4606 break;
4607 case NL80211_TDLS_TEARDOWN:
4608 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4609 break;
4610 default:
4611 brcmf_err("unsupported operation: %d\n", oper);
4612 ret = -EOPNOTSUPP;
4613 }
4614 return ret;
4615}
4616
4617static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004618 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004619 enum nl80211_tdls_operation oper)
4620{
4621 struct brcmf_if *ifp;
4622 struct brcmf_tdls_iovar_le info;
4623 int ret = 0;
4624
4625 ret = brcmf_convert_nl80211_tdls_oper(oper);
4626 if (ret < 0)
4627 return ret;
4628
4629 ifp = netdev_priv(ndev);
4630 memset(&info, 0, sizeof(info));
4631 info.mode = (u8)ret;
4632 if (peer)
4633 memcpy(info.ea, peer, ETH_ALEN);
4634
4635 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4636 &info, sizeof(info));
4637 if (ret < 0)
4638 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4639
4640 return ret;
4641}
4642
Arend van Spriel5b435de2011-10-05 13:19:03 +02004643static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004644 .add_virtual_intf = brcmf_cfg80211_add_iface,
4645 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004646 .change_virtual_intf = brcmf_cfg80211_change_iface,
4647 .scan = brcmf_cfg80211_scan,
4648 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4649 .join_ibss = brcmf_cfg80211_join_ibss,
4650 .leave_ibss = brcmf_cfg80211_leave_ibss,
4651 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02004652 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004653 .set_tx_power = brcmf_cfg80211_set_tx_power,
4654 .get_tx_power = brcmf_cfg80211_get_tx_power,
4655 .add_key = brcmf_cfg80211_add_key,
4656 .del_key = brcmf_cfg80211_del_key,
4657 .get_key = brcmf_cfg80211_get_key,
4658 .set_default_key = brcmf_cfg80211_config_default_key,
4659 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4660 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004661 .connect = brcmf_cfg80211_connect,
4662 .disconnect = brcmf_cfg80211_disconnect,
4663 .suspend = brcmf_cfg80211_suspend,
4664 .resume = brcmf_cfg80211_resume,
4665 .set_pmksa = brcmf_cfg80211_set_pmksa,
4666 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004667 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004668 .start_ap = brcmf_cfg80211_start_ap,
4669 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004670 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004671 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004672 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004673 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4674 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004675 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4676 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4677 .remain_on_channel = brcmf_p2p_remain_on_channel,
4678 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004679 .start_p2p_device = brcmf_p2p_start_device,
4680 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004681 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4682 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004683 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004684};
4685
Arend van Spriel3eacf862012-10-22 13:55:30 -07004686struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004687 enum nl80211_iftype type,
4688 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004689{
Hante Meulemana44aa402014-12-03 21:05:33 +01004690 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004691 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004692 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004693
Arend van Spriel33a6b152013-02-08 15:53:39 +01004694 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004695 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004696 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4697 if (!vif)
4698 return ERR_PTR(-ENOMEM);
4699
4700 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004701 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004702
Arend van Spriel3eacf862012-10-22 13:55:30 -07004703 vif->pm_block = pm_block;
4704 vif->roam_off = -1;
4705
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004706 brcmf_init_prof(&vif->profile);
4707
Hante Meulemana44aa402014-12-03 21:05:33 +01004708 if (type == NL80211_IFTYPE_AP) {
4709 mbss = false;
4710 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4711 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4712 mbss = true;
4713 break;
4714 }
4715 }
4716 vif->mbss = mbss;
4717 }
4718
Arend van Spriel3eacf862012-10-22 13:55:30 -07004719 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004720 return vif;
4721}
4722
Arend van Spriel427dec52014-01-06 12:40:47 +01004723void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004724{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004725 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004726 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004727}
4728
Arend van Spriel9df4d542014-01-06 12:40:49 +01004729void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4730{
4731 struct brcmf_cfg80211_vif *vif;
4732 struct brcmf_if *ifp;
4733
4734 ifp = netdev_priv(ndev);
4735 vif = ifp->vif;
4736
Arend van Spriel95ef1232015-08-26 22:15:04 +02004737 if (vif)
4738 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01004739 free_netdev(ndev);
4740}
4741
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004742static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004743{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004744 u32 event = e->event_code;
4745 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004746
4747 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004748 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004749 return true;
4750 }
4751
4752 return false;
4753}
4754
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004755static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004756{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004757 u32 event = e->event_code;
4758 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004759
Hante Meuleman68ca3952014-02-25 20:30:26 +01004760 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4761 (event == BRCMF_E_DISASSOC_IND) ||
4762 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004763 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004764 return true;
4765 }
4766 return false;
4767}
4768
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004769static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004770 const struct brcmf_event_msg *e)
4771{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004772 u32 event = e->event_code;
4773 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004774
4775 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004776 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4777 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004778 return true;
4779 }
4780
4781 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004782 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004783 return true;
4784 }
4785
4786 return false;
4787}
4788
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004789static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004790{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004791 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004792
4793 kfree(conn_info->req_ie);
4794 conn_info->req_ie = NULL;
4795 conn_info->req_ie_len = 0;
4796 kfree(conn_info->resp_ie);
4797 conn_info->resp_ie = NULL;
4798 conn_info->resp_ie_len = 0;
4799}
4800
Hante Meuleman89286dc2013-02-08 15:53:46 +01004801static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4802 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004803{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004804 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004805 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004806 u32 req_len;
4807 u32 resp_len;
4808 s32 err = 0;
4809
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004810 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004811
Arend van Sprielac24be62012-10-22 10:36:23 -07004812 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4813 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004814 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004815 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004816 return err;
4817 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004818 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004819 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004820 req_len = le32_to_cpu(assoc_info->req_len);
4821 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004822 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004823 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004824 cfg->extra_buf,
4825 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004826 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004827 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004828 return err;
4829 }
4830 conn_info->req_ie_len = req_len;
4831 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004832 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004833 GFP_KERNEL);
4834 } else {
4835 conn_info->req_ie_len = 0;
4836 conn_info->req_ie = NULL;
4837 }
4838 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004839 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004840 cfg->extra_buf,
4841 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004842 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004843 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004844 return err;
4845 }
4846 conn_info->resp_ie_len = resp_len;
4847 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004848 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004849 GFP_KERNEL);
4850 } else {
4851 conn_info->resp_ie_len = 0;
4852 conn_info->resp_ie = NULL;
4853 }
Arend van Spriel16886732012-12-05 15:26:04 +01004854 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4855 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004856
4857 return err;
4858}
4859
4860static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004861brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004862 struct net_device *ndev,
4863 const struct brcmf_event_msg *e)
4864{
Arend van Sprielc1179032012-10-22 13:55:33 -07004865 struct brcmf_if *ifp = netdev_priv(ndev);
4866 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004867 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4868 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004869 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004870 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004871 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004872 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004873 u32 freq;
4874 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004875 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004876
Arend van Sprield96b8012012-12-05 15:26:02 +01004877 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004878
Hante Meuleman89286dc2013-02-08 15:53:46 +01004879 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004880 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004881 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004882
Franky Lina180b832012-10-10 11:13:09 -07004883 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4884 if (buf == NULL) {
4885 err = -ENOMEM;
4886 goto done;
4887 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004888
Franky Lina180b832012-10-10 11:13:09 -07004889 /* data sent to dongle has to be little endian */
4890 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004891 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004892 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004893
4894 if (err)
4895 goto done;
4896
4897 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004898 ch.chspec = le16_to_cpu(bi->chanspec);
4899 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004900
Franky Lin83cf17a2013-04-11 13:28:50 +02004901 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004902 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4903 else
4904 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4905
Franky Lin83cf17a2013-04-11 13:28:50 +02004906 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004907 notify_channel = ieee80211_get_channel(wiphy, freq);
4908
Franky Lina180b832012-10-10 11:13:09 -07004909done:
4910 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004911 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004912 conn_info->req_ie, conn_info->req_ie_len,
4913 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004914 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004915
Arend van Sprielc1179032012-10-22 13:55:33 -07004916 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004917 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004918 return err;
4919}
4920
4921static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004922brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004923 struct net_device *ndev, const struct brcmf_event_msg *e,
4924 bool completed)
4925{
Arend van Sprielc1179032012-10-22 13:55:33 -07004926 struct brcmf_if *ifp = netdev_priv(ndev);
4927 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004928 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004929
Arend van Sprield96b8012012-12-05 15:26:02 +01004930 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004931
Arend van Sprielc1179032012-10-22 13:55:33 -07004932 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4933 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004934 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004935 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004936 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004937 brcmf_update_bss_info(cfg, ifp);
4938 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4939 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004940 }
4941 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004942 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004943 conn_info->req_ie,
4944 conn_info->req_ie_len,
4945 conn_info->resp_ie,
4946 conn_info->resp_ie_len,
4947 completed ? WLAN_STATUS_SUCCESS :
4948 WLAN_STATUS_AUTH_TIMEOUT,
4949 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004950 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4951 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004952 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004953 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004954 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004955}
4956
4957static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004958brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004959 struct net_device *ndev,
4960 const struct brcmf_event_msg *e, void *data)
4961{
Hante Meulemana44aa402014-12-03 21:05:33 +01004962 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01004963 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004964 u32 event = e->event_code;
4965 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004966 struct station_info sinfo;
4967
Arend van Spriel16886732012-12-05 15:26:04 +01004968 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004969 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4970 ndev != cfg_to_ndev(cfg)) {
4971 brcmf_dbg(CONN, "AP mode link down\n");
4972 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01004973 if (ifp->vif->mbss)
Arend van Sprielee6e3a32015-08-26 22:14:55 +02004974 brcmf_remove_interface(ifp);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004975 return 0;
4976 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004977
Hante Meuleman1a873342012-09-27 14:17:54 +02004978 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004979 (reason == BRCMF_E_STATUS_SUCCESS)) {
4980 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004981 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004982 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004983 return -EINVAL;
4984 }
4985 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004986 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004987 generation++;
4988 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004989 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004990 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4991 (event == BRCMF_E_DEAUTH_IND) ||
4992 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004993 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004994 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004995 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004996}
4997
4998static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004999brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005000 const struct brcmf_event_msg *e, void *data)
5001{
Arend van Spriel19937322012-11-05 16:22:32 -08005002 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5003 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005004 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005005 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005006 s32 err = 0;
5007
Hante Meuleman8851cce2014-07-30 13:20:02 +02005008 if ((e->event_code == BRCMF_E_DEAUTH) ||
5009 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5010 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5011 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5012 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5013 }
5014
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005015 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005016 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005017 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005018 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005019 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005020 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005021 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005022 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005023 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005024 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5025 &ifp->vif->sme_state);
5026 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5027 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005028 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005029 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005030 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005031 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005032 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005033 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005034 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005035 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005036 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005037 if (ndev != cfg_to_ndev(cfg))
5038 complete(&cfg->vif_disabled);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005039 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005040 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005041 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5042 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005043 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005044 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005045 }
5046
5047 return err;
5048}
5049
5050static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005051brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005052 const struct brcmf_event_msg *e, void *data)
5053{
Arend van Spriel19937322012-11-05 16:22:32 -08005054 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005055 u32 event = e->event_code;
5056 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005057
5058 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005059 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005060 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005061 else
Arend van Spriel19937322012-11-05 16:22:32 -08005062 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005063 }
5064
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005065 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005066}
5067
5068static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005069brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005070 const struct brcmf_event_msg *e, void *data)
5071{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005072 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005073 enum nl80211_key_type key_type;
5074
5075 if (flags & BRCMF_EVENT_MSG_GROUP)
5076 key_type = NL80211_KEYTYPE_GROUP;
5077 else
5078 key_type = NL80211_KEYTYPE_PAIRWISE;
5079
Arend van Spriel19937322012-11-05 16:22:32 -08005080 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005081 NULL, GFP_KERNEL);
5082
5083 return 0;
5084}
5085
Arend van Sprield3c0b632013-02-08 15:53:37 +01005086static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5087 const struct brcmf_event_msg *e, void *data)
5088{
5089 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5090 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5091 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5092 struct brcmf_cfg80211_vif *vif;
5093
5094 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
5095 ifevent->action, ifevent->flags, ifevent->ifidx,
5096 ifevent->bssidx);
5097
Arend van Sprield3c0b632013-02-08 15:53:37 +01005098 mutex_lock(&event->vif_event_lock);
5099 event->action = ifevent->action;
5100 vif = event->vif;
5101
5102 switch (ifevent->action) {
5103 case BRCMF_E_IF_ADD:
5104 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005105 if (!cfg->vif_event.vif) {
5106 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005107 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005108 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005109
5110 ifp->vif = vif;
5111 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005112 if (ifp->ndev) {
5113 vif->wdev.netdev = ifp->ndev;
5114 ifp->ndev->ieee80211_ptr = &vif->wdev;
5115 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5116 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005117 mutex_unlock(&event->vif_event_lock);
5118 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005119 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005120
5121 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005122 mutex_unlock(&event->vif_event_lock);
5123 /* event may not be upon user request */
5124 if (brcmf_cfg80211_vif_event_armed(cfg))
5125 wake_up(&event->vif_wq);
5126 return 0;
5127
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005128 case BRCMF_E_IF_CHANGE:
5129 mutex_unlock(&event->vif_event_lock);
5130 wake_up(&event->vif_wq);
5131 return 0;
5132
Arend van Sprield3c0b632013-02-08 15:53:37 +01005133 default:
5134 mutex_unlock(&event->vif_event_lock);
5135 break;
5136 }
5137 return -EINVAL;
5138}
5139
Arend van Spriel5b435de2011-10-05 13:19:03 +02005140static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5141{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005142 conf->frag_threshold = (u32)-1;
5143 conf->rts_threshold = (u32)-1;
5144 conf->retry_short = (u32)-1;
5145 conf->retry_long = (u32)-1;
5146 conf->tx_power = -1;
5147}
5148
Arend van Spriel5c36b992012-11-14 18:46:05 -08005149static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005150{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005151 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5152 brcmf_notify_connect_status);
5153 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5154 brcmf_notify_connect_status);
5155 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5156 brcmf_notify_connect_status);
5157 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5158 brcmf_notify_connect_status);
5159 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5160 brcmf_notify_connect_status);
5161 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5162 brcmf_notify_connect_status);
5163 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5164 brcmf_notify_roaming_status);
5165 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5166 brcmf_notify_mic_status);
5167 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5168 brcmf_notify_connect_status);
5169 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5170 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005171 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5172 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005173 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005174 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005175 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5176 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005177 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5178 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005179 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5180 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005181 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5182 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005183}
5184
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005185static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005186{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005187 kfree(cfg->conf);
5188 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005189 kfree(cfg->escan_ioctl_buf);
5190 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005191 kfree(cfg->extra_buf);
5192 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005193 kfree(cfg->pmk_list);
5194 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005195}
5196
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005197static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005198{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005199 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5200 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005202 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5203 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005204 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005205 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5206 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005207 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005208 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5209 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210 goto init_priv_mem_out;
5211
5212 return 0;
5213
5214init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005215 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005216
5217 return -ENOMEM;
5218}
5219
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005220static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005221{
5222 s32 err = 0;
5223
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005224 cfg->scan_request = NULL;
5225 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005226 cfg->active_scan = true; /* we do active scan per default */
5227 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005228 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005229 if (err)
5230 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005231 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005232 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005233 brcmf_init_escan(cfg);
5234 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005235 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005236 return err;
5237}
5238
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005239static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005240{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005241 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005242 brcmf_abort_scanning(cfg);
5243 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005244}
5245
Arend van Sprield3c0b632013-02-08 15:53:37 +01005246static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5247{
5248 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005249 mutex_init(&event->vif_event_lock);
5250}
5251
Arend van Spriel5b435de2011-10-05 13:19:03 +02005252static s32
Hante Meuleman68ca3952014-02-25 20:30:26 +01005253brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005254{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005255 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005256 __le32 roamtrigger[2];
5257 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005258
5259 /*
5260 * Setup timeout if Beacons are lost and roam is
5261 * off to report link down
5262 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005263 if (brcmf_roamoff) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005264 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005265 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005266 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005267 goto dongle_rom_out;
5268 }
5269 }
5270
5271 /*
5272 * Enable/Disable built-in roaming to allow supplicant
5273 * to take care of roaming
5274 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005275 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5276 brcmf_roamoff ? "Off" : "On");
5277 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005278 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005279 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005280 goto dongle_rom_out;
5281 }
5282
Arend van Sprielf588bc02011-10-12 20:51:22 +02005283 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5284 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005285 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005286 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005287 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005288 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005289 goto dongle_rom_out;
5290 }
5291
Arend van Sprielf588bc02011-10-12 20:51:22 +02005292 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5293 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005294 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005295 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005296 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005297 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005298 goto dongle_rom_out;
5299 }
5300
5301dongle_rom_out:
5302 return err;
5303}
5304
5305static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01005306brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005307 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005308{
5309 s32 err = 0;
5310
Arend van Sprielac24be62012-10-22 10:36:23 -07005311 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005312 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005313 if (err) {
5314 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005315 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005316 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005317 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005318 goto dongle_scantime_out;
5319 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005320 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005321 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005322 if (err) {
5323 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005324 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005325 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005326 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005327 goto dongle_scantime_out;
5328 }
5329
Arend van Sprielac24be62012-10-22 10:36:23 -07005330 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005331 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005332 if (err) {
5333 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005334 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005335 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005336 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005337 goto dongle_scantime_out;
5338 }
5339
5340dongle_scantime_out:
5341 return err;
5342}
5343
Arend van Sprielb48d8912014-07-12 08:49:41 +02005344static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5345 struct brcmu_chan *ch)
5346{
5347 u32 ht40_flag;
5348
5349 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5350 if (ch->sb == BRCMU_CHAN_SB_U) {
5351 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5352 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5353 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5354 } else {
5355 /* It should be one of
5356 * IEEE80211_CHAN_NO_HT40 or
5357 * IEEE80211_CHAN_NO_HT40PLUS
5358 */
5359 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5360 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5361 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5362 }
5363}
5364
5365static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5366 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005367{
5368 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005369 struct ieee80211_supported_band *band;
5370 struct ieee80211_channel *channel;
5371 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005372 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005373 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005374 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005375 u8 *pbuf;
5376 u32 i, j;
5377 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005378 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005379 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005380
5381 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5382
5383 if (pbuf == NULL)
5384 return -ENOMEM;
5385
5386 list = (struct brcmf_chanspec_list *)pbuf;
5387
5388 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5389 BRCMF_DCMD_MEDLEN);
5390 if (err) {
5391 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005392 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005393 }
5394
Arend van Sprielb48d8912014-07-12 08:49:41 +02005395 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005396 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5397 if (band)
5398 for (i = 0; i < band->n_channels; i++)
5399 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5400 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5401 if (band)
5402 for (i = 0; i < band->n_channels; i++)
5403 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005404
5405 total = le32_to_cpu(list->count);
5406 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005407 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5408 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005409
Franky Lin83cf17a2013-04-11 13:28:50 +02005410 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005411 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005412 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005413 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005414 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005415 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005416 continue;
5417 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005418 if (!band)
5419 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005420 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005421 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005422 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005423 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005424 ch.bw == BRCMU_CHAN_BW_80)
5425 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005426
5427 channel = band->channels;
5428 index = band->n_channels;
5429 for (j = 0; j < band->n_channels; j++) {
5430 if (channel[j].hw_value == ch.chnum) {
5431 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005432 break;
5433 }
5434 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005435 channel[index].center_freq =
5436 ieee80211_channel_to_frequency(ch.chnum, band->band);
5437 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005438
Arend van Sprielb48d8912014-07-12 08:49:41 +02005439 /* assuming the chanspecs order is HT20,
5440 * HT40 upper, HT40 lower, and VHT80.
5441 */
5442 if (ch.bw == BRCMU_CHAN_BW_80) {
5443 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5444 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5445 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5446 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005447 /* enable the channel and disable other bandwidths
5448 * for now as mentioned order assure they are enabled
5449 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005450 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005451 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5452 IEEE80211_CHAN_NO_80MHZ;
5453 ch.bw = BRCMU_CHAN_BW_20;
5454 cfg->d11inf.encchspec(&ch);
5455 chaninfo = ch.chspec;
5456 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5457 &chaninfo);
5458 if (!err) {
5459 if (chaninfo & WL_CHAN_RADAR)
5460 channel[index].flags |=
5461 (IEEE80211_CHAN_RADAR |
5462 IEEE80211_CHAN_NO_IR);
5463 if (chaninfo & WL_CHAN_PASSIVE)
5464 channel[index].flags |=
5465 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005466 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005467 }
5468 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005469
Arend van Sprielb48d8912014-07-12 08:49:41 +02005470fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005471 kfree(pbuf);
5472 return err;
5473}
5474
Arend van Sprielb48d8912014-07-12 08:49:41 +02005475static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005476{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005477 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5478 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005479 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005480 struct brcmf_chanspec_list *list;
5481 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005482 u32 val;
5483 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005484 struct brcmu_chan ch;
5485 u32 num_chan;
5486 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005487
5488 /* verify support for bw_cap command */
5489 val = WLC_BAND_5G;
5490 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5491
5492 if (!err) {
5493 /* only set 2G bandwidth using bw_cap command */
5494 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5495 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5496 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5497 sizeof(band_bwcap));
5498 } else {
5499 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5500 val = WLC_N_BW_40ALL;
5501 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5502 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005503
5504 if (!err) {
5505 /* update channel info in 2G band */
5506 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5507
5508 if (pbuf == NULL)
5509 return -ENOMEM;
5510
5511 ch.band = BRCMU_CHAN_BAND_2G;
5512 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005513 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005514 ch.chnum = 0;
5515 cfg->d11inf.encchspec(&ch);
5516
5517 /* pass encoded chanspec in query */
5518 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5519
5520 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5521 BRCMF_DCMD_MEDLEN);
5522 if (err) {
5523 brcmf_err("get chanspecs error (%d)\n", err);
5524 kfree(pbuf);
5525 return err;
5526 }
5527
5528 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5529 list = (struct brcmf_chanspec_list *)pbuf;
5530 num_chan = le32_to_cpu(list->count);
5531 for (i = 0; i < num_chan; i++) {
5532 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5533 cfg->d11inf.decchspec(&ch);
5534 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5535 continue;
5536 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5537 continue;
5538 for (j = 0; j < band->n_channels; j++) {
5539 if (band->channels[j].hw_value == ch.chnum)
5540 break;
5541 }
5542 if (WARN_ON(j == band->n_channels))
5543 continue;
5544
5545 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5546 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005547 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005548 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005549 return err;
5550}
5551
Arend van Spriel2375d972014-01-06 12:40:41 +01005552static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5553{
5554 u32 band, mimo_bwcap;
5555 int err;
5556
5557 band = WLC_BAND_2G;
5558 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5559 if (!err) {
5560 bw_cap[IEEE80211_BAND_2GHZ] = band;
5561 band = WLC_BAND_5G;
5562 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5563 if (!err) {
5564 bw_cap[IEEE80211_BAND_5GHZ] = band;
5565 return;
5566 }
5567 WARN_ON(1);
5568 return;
5569 }
5570 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5571 mimo_bwcap = 0;
5572 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5573 if (err)
5574 /* assume 20MHz if firmware does not give a clue */
5575 mimo_bwcap = WLC_N_BW_20ALL;
5576
5577 switch (mimo_bwcap) {
5578 case WLC_N_BW_40ALL:
5579 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5580 /* fall-thru */
5581 case WLC_N_BW_20IN2G_40IN5G:
5582 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5583 /* fall-thru */
5584 case WLC_N_BW_20ALL:
5585 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5586 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5587 break;
5588 default:
5589 brcmf_err("invalid mimo_bw_cap value\n");
5590 }
5591}
Hante Meulemand48200b2013-04-03 12:40:29 +02005592
Arend van Spriel18d6c532014-05-12 10:47:35 +02005593static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5594 u32 bw_cap[2], u32 nchain)
5595{
5596 band->ht_cap.ht_supported = true;
5597 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5598 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5599 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5600 }
5601 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5602 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5603 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5604 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5605 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5606 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5607}
5608
5609static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5610{
5611 u16 mcs_map;
5612 int i;
5613
5614 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5615 mcs_map = (mcs_map << 2) | supp;
5616
5617 return cpu_to_le16(mcs_map);
5618}
5619
5620static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
5621 u32 bw_cap[2], u32 nchain)
5622{
5623 __le16 mcs_map;
5624
5625 /* not allowed in 2.4G band */
5626 if (band->band == IEEE80211_BAND_2GHZ)
5627 return;
5628
5629 band->vht_cap.vht_supported = true;
5630 /* 80MHz is mandatory */
5631 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5632 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5633 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5634 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5635 }
5636 /* all support 256-QAM */
5637 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5638 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5639 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
5640}
5641
Arend van Sprielb48d8912014-07-12 08:49:41 +02005642static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005643{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005644 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005645 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005646 u32 nmode = 0;
5647 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005648 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005649 u32 rxchain;
5650 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005651 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005652 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005653 struct ieee80211_supported_band *band;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005654
Arend van Spriel18d6c532014-05-12 10:47:35 +02005655 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005656 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5657 if (err) {
5658 brcmf_err("nmode error (%d)\n", err);
5659 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005660 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005661 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005662 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5663 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5664 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005665
Daniel Kim4aca7a12014-02-25 20:30:36 +01005666 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5667 if (err) {
5668 brcmf_err("rxchain error (%d)\n", err);
5669 nchain = 1;
5670 } else {
5671 for (nchain = 0; rxchain; nchain++)
5672 rxchain = rxchain & (rxchain - 1);
5673 }
5674 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5675
Arend van Sprielb48d8912014-07-12 08:49:41 +02005676 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005677 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005678 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005679 return err;
5680 }
5681
Arend van Sprielb48d8912014-07-12 08:49:41 +02005682 wiphy = cfg_to_wiphy(cfg);
5683 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5684 band = wiphy->bands[i];
5685 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005686 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005687
Arend van Spriel18d6c532014-05-12 10:47:35 +02005688 if (nmode)
5689 brcmf_update_ht_cap(band, bw_cap, nchain);
5690 if (vhtmode)
5691 brcmf_update_vht_cap(band, bw_cap, nchain);
Hante Meulemand48200b2013-04-03 12:40:29 +02005692 }
5693
Arend van Sprielb48d8912014-07-12 08:49:41 +02005694 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005695}
5696
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005697static const struct ieee80211_txrx_stypes
5698brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5699 [NL80211_IFTYPE_STATION] = {
5700 .tx = 0xffff,
5701 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5702 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5703 },
5704 [NL80211_IFTYPE_P2P_CLIENT] = {
5705 .tx = 0xffff,
5706 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5707 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5708 },
5709 [NL80211_IFTYPE_P2P_GO] = {
5710 .tx = 0xffff,
5711 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5712 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5713 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5714 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5715 BIT(IEEE80211_STYPE_AUTH >> 4) |
5716 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5717 BIT(IEEE80211_STYPE_ACTION >> 4)
5718 },
5719 [NL80211_IFTYPE_P2P_DEVICE] = {
5720 .tx = 0xffff,
5721 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5722 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5723 }
5724};
5725
Arend van Spriel0882dda2015-08-20 22:06:03 +02005726/**
5727 * brcmf_setup_ifmodes() - determine interface modes and combinations.
5728 *
5729 * @wiphy: wiphy object.
5730 * @ifp: interface object needed for feat module api.
5731 *
5732 * The interface modes and combinations are determined dynamically here
5733 * based on firmware functionality.
5734 *
5735 * no p2p and no mbss:
5736 *
5737 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5738 *
5739 * no p2p and mbss:
5740 *
5741 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5742 * #AP <= 4, matching BI, channels = 1, 4 total
5743 *
5744 * p2p, no mchan, and mbss:
5745 *
5746 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
5747 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5748 * #AP <= 4, matching BI, channels = 1, 4 total
5749 *
5750 * p2p, mchan, and mbss:
5751 *
5752 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
5753 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5754 * #AP <= 4, matching BI, channels = 1, 4 total
5755 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005756static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5757{
5758 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02005759 struct ieee80211_iface_limit *c0_limits = NULL;
5760 struct ieee80211_iface_limit *p2p_limits = NULL;
5761 struct ieee80211_iface_limit *mbss_limits = NULL;
5762 bool mbss, p2p;
5763 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005764
Arend van Spriel0882dda2015-08-20 22:06:03 +02005765 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
5766 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
5767
5768 n_combos = 1 + !!p2p + !!mbss;
5769 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005770 if (!combo)
5771 goto err;
5772
Arend van Spriel0882dda2015-08-20 22:06:03 +02005773 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
5774 if (!c0_limits)
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005775 goto err;
5776
Arend van Spriel0882dda2015-08-20 22:06:03 +02005777 if (p2p) {
5778 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
5779 if (!p2p_limits)
5780 goto err;
5781 }
5782
5783 if (mbss) {
5784 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
5785 if (!mbss_limits)
5786 goto err;
5787 }
5788
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005789 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5790 BIT(NL80211_IFTYPE_ADHOC) |
5791 BIT(NL80211_IFTYPE_AP);
5792
Arend van Spriel0882dda2015-08-20 22:06:03 +02005793 c = 0;
5794 i = 0;
5795 combo[c].num_different_channels = 1;
5796 c0_limits[i].max = 1;
5797 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5798 if (p2p) {
5799 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5800 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005801 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5802 BIT(NL80211_IFTYPE_P2P_GO) |
5803 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02005804 c0_limits[i].max = 1;
5805 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5806 c0_limits[i].max = 1;
5807 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5808 BIT(NL80211_IFTYPE_P2P_GO);
5809 } else {
5810 c0_limits[i].max = 1;
5811 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005812 }
Arend van Spriel0882dda2015-08-20 22:06:03 +02005813 combo[c].max_interfaces = i;
5814 combo[c].n_limits = i;
5815 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005816
Arend van Spriel0882dda2015-08-20 22:06:03 +02005817 if (p2p) {
5818 c++;
5819 i = 0;
5820 combo[c].num_different_channels = 1;
5821 p2p_limits[i].max = 1;
5822 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5823 p2p_limits[i].max = 1;
5824 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
5825 p2p_limits[i].max = 1;
5826 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
5827 p2p_limits[i].max = 1;
5828 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5829 combo[c].max_interfaces = i;
5830 combo[c].n_limits = i;
5831 combo[c].limits = p2p_limits;
5832 }
5833
5834 if (mbss) {
5835 c++;
5836 combo[c].beacon_int_infra_match = true;
5837 combo[c].num_different_channels = 1;
5838 mbss_limits[0].max = 4;
5839 mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
5840 combo[c].max_interfaces = 4;
5841 combo[c].n_limits = 1;
5842 combo[c].limits = mbss_limits;
5843 }
5844 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005845 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005846 return 0;
5847
5848err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02005849 kfree(c0_limits);
5850 kfree(p2p_limits);
5851 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005852 kfree(combo);
5853 return -ENOMEM;
5854}
5855
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005856static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
5857{
5858 /* scheduled scan settings */
5859 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
5860 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
5861 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5862 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
5863}
5864
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005865#ifdef CONFIG_PM
5866static const struct wiphy_wowlan_support brcmf_wowlan_support = {
5867 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01005868 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
5869 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
5870 .pattern_min_len = 1,
5871 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005872};
5873#endif
5874
5875static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
5876{
5877#ifdef CONFIG_PM
5878 /* wowl settings */
5879 wiphy->wowlan = &brcmf_wowlan_support;
5880#endif
5881}
5882
Arend van Sprielb48d8912014-07-12 08:49:41 +02005883static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005884{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005885 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005886 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005887 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005888 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005889 __le32 bandlist[3];
5890 u32 n_bands;
5891 int err, i;
5892
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005893 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
5894 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5895 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005896
5897 err = brcmf_setup_ifmodes(wiphy, ifp);
5898 if (err)
5899 return err;
5900
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005901 for (i = 0, combo = wiphy->iface_combinations;
5902 i < wiphy->n_iface_combinations; i++, combo++) {
5903 max_interfaces = max(max_interfaces, combo->max_interfaces);
5904 }
5905
5906 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
5907 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005908 u8 *addr = drvr->addresses[i].addr;
5909
5910 memcpy(addr, drvr->mac, ETH_ALEN);
5911 if (i) {
5912 addr[0] |= BIT(1);
5913 addr[ETH_ALEN - 1] ^= i;
5914 }
5915 }
5916 wiphy->addresses = drvr->addresses;
5917 wiphy->n_addresses = i;
5918
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005919 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
5920 wiphy->cipher_suites = __wl_cipher_suites;
5921 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
5922 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
5923 WIPHY_FLAG_OFFCHAN_TX |
5924 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
5925 WIPHY_FLAG_SUPPORTS_TDLS;
5926 if (!brcmf_roamoff)
5927 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
5928 wiphy->mgmt_stypes = brcmf_txrx_stypes;
5929 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02005930 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
5931 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005932
5933 /* vendor commands/events support */
5934 wiphy->vendor_commands = brcmf_vendor_cmds;
5935 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
5936
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005937 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
5938 brcmf_wiphy_wowl_params(wiphy);
5939
Arend van Spriel58de92d2015-04-14 20:10:24 +02005940 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
5941 sizeof(bandlist));
5942 if (err) {
5943 brcmf_err("could not obtain band info: err=%d\n", err);
5944 return err;
5945 }
5946 /* first entry in bandlist is number of bands */
5947 n_bands = le32_to_cpu(bandlist[0]);
5948 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
5949 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
5950 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
5951 GFP_KERNEL);
5952 if (!band)
5953 return -ENOMEM;
5954
5955 band->channels = kmemdup(&__wl_2ghz_channels,
5956 sizeof(__wl_2ghz_channels),
5957 GFP_KERNEL);
5958 if (!band->channels) {
5959 kfree(band);
5960 return -ENOMEM;
5961 }
5962
5963 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
5964 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
5965 }
5966 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
5967 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
5968 GFP_KERNEL);
5969 if (!band)
5970 return -ENOMEM;
5971
5972 band->channels = kmemdup(&__wl_5ghz_channels,
5973 sizeof(__wl_5ghz_channels),
5974 GFP_KERNEL);
5975 if (!band->channels) {
5976 kfree(band);
5977 return -ENOMEM;
5978 }
5979
5980 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
5981 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
5982 }
5983 }
5984 err = brcmf_setup_wiphybands(wiphy);
5985 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005986}
5987
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005988static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005989{
5990 struct net_device *ndev;
5991 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005992 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005993 s32 power_mode;
5994 s32 err = 0;
5995
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005996 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005997 return err;
5998
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005999 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006000 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006001 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006002
Hante Meuleman40a23292013-01-02 15:22:51 +01006003 /* make sure RF is ready for work */
6004 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6005
6006 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
6007 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006008
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006009 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006010 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006011 if (err)
6012 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006013 brcmf_dbg(INFO, "power save set to %s\n",
6014 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006015
Hante Meuleman68ca3952014-02-25 20:30:26 +01006016 err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006017 if (err)
6018 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006019 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
6020 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006021 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006022 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006023
Hante Meulemanb3657452013-05-27 21:09:53 +02006024 brcmf_configure_arp_offload(ifp, true);
6025
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006026 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006027default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006028
6029 return err;
6030
6031}
6032
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006033static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006034{
Arend van Sprielc1179032012-10-22 13:55:33 -07006035 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006036
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006037 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006038}
6039
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006040static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006041{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006042 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006043
Arend van Spriel5b435de2011-10-05 13:19:03 +02006044 /*
6045 * While going down, if associated with AP disassociate
6046 * from AP to save power
6047 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006048 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006049 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006050
6051 /* Make sure WPA_Supplicant receives all the event
6052 generated due to DISASSOC call to the fw to keep
6053 the state fw and WPA_Supplicant state consistent
6054 */
6055 brcmf_delay(500);
6056 }
6057
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006058 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006059 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006060
Arend van Spriel5b435de2011-10-05 13:19:03 +02006061 return 0;
6062}
6063
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006064s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006065{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006066 struct brcmf_if *ifp = netdev_priv(ndev);
6067 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006068 s32 err = 0;
6069
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006070 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006071 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006072 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006073
6074 return err;
6075}
6076
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006077s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006078{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006079 struct brcmf_if *ifp = netdev_priv(ndev);
6080 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006081 s32 err = 0;
6082
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006083 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006084 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006085 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006086
6087 return err;
6088}
6089
Arend van Spriela7965fb2013-04-11 17:08:37 +02006090enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6091{
6092 struct wireless_dev *wdev = &ifp->vif->wdev;
6093
6094 return wdev->iftype;
6095}
6096
Hante Meulemanbfe81972014-10-28 14:56:16 +01006097bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6098 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006099{
6100 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006101
6102 list_for_each_entry(vif, &cfg->vif_list, list) {
6103 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006104 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006105 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006106 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006107}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006108
6109static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6110 u8 action)
6111{
6112 u8 evt_action;
6113
6114 mutex_lock(&event->vif_event_lock);
6115 evt_action = event->action;
6116 mutex_unlock(&event->vif_event_lock);
6117 return evt_action == action;
6118}
6119
6120void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6121 struct brcmf_cfg80211_vif *vif)
6122{
6123 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6124
6125 mutex_lock(&event->vif_event_lock);
6126 event->vif = vif;
6127 event->action = 0;
6128 mutex_unlock(&event->vif_event_lock);
6129}
6130
6131bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6132{
6133 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6134 bool armed;
6135
6136 mutex_lock(&event->vif_event_lock);
6137 armed = event->vif != NULL;
6138 mutex_unlock(&event->vif_event_lock);
6139
6140 return armed;
6141}
6142int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6143 u8 action, ulong timeout)
6144{
6145 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6146
6147 return wait_event_timeout(event->vif_wq,
6148 vif_event_equals(event, action), timeout);
6149}
6150
Arend van Spriel63db1a42014-12-21 12:43:51 +01006151static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6152 struct regulatory_request *req)
6153{
6154 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6155 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6156 struct brcmf_fil_country_le ccreq;
6157 int i;
6158
6159 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6160 req->alpha2[0], req->alpha2[1]);
6161
6162 /* ignore non-ISO3166 country codes */
6163 for (i = 0; i < sizeof(req->alpha2); i++)
6164 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6165 brcmf_err("not a ISO3166 code\n");
6166 return;
6167 }
6168 memset(&ccreq, 0, sizeof(ccreq));
6169 ccreq.rev = cpu_to_le32(-1);
6170 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006171 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6172 brcmf_err("firmware rejected country setting\n");
6173 return;
6174 }
6175 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006176}
6177
Arend van Sprielb48d8912014-07-12 08:49:41 +02006178static void brcmf_free_wiphy(struct wiphy *wiphy)
6179{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006180 int i;
6181
Arend van Spriel58de92d2015-04-14 20:10:24 +02006182 if (!wiphy)
6183 return;
6184
Arend van Spriel0882dda2015-08-20 22:06:03 +02006185 if (wiphy->iface_combinations) {
6186 for (i = 0; i < wiphy->n_iface_combinations; i++)
6187 kfree(wiphy->iface_combinations[i].limits);
6188 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006189 kfree(wiphy->iface_combinations);
6190 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6191 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6192 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6193 }
6194 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6195 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6196 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6197 }
6198 wiphy_free(wiphy);
6199}
6200
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006201struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006202 struct device *busdev,
6203 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006204{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006205 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006206 struct brcmf_cfg80211_info *cfg;
6207 struct wiphy *wiphy;
6208 struct brcmf_cfg80211_vif *vif;
6209 struct brcmf_if *ifp;
6210 s32 err = 0;
6211 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006212 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006213
6214 if (!ndev) {
6215 brcmf_err("ndev is invalid\n");
6216 return NULL;
6217 }
6218
6219 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006220 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6221 if (!wiphy) {
6222 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006223 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006224 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006225 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006226 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006227
6228 cfg = wiphy_priv(wiphy);
6229 cfg->wiphy = wiphy;
6230 cfg->pub = drvr;
6231 init_vif_event(&cfg->vif_event);
6232 INIT_LIST_HEAD(&cfg->vif_list);
6233
6234 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006235 if (IS_ERR(vif))
6236 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006237
6238 vif->ifp = ifp;
6239 vif->wdev.netdev = ndev;
6240 ndev->ieee80211_ptr = &vif->wdev;
6241 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6242
6243 err = wl_init_priv(cfg);
6244 if (err) {
6245 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006246 brcmf_free_vif(vif);
6247 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006248 }
6249 ifp->vif = vif;
6250
Arend van Sprielb48d8912014-07-12 08:49:41 +02006251 /* determine d11 io type before wiphy setup */
6252 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006253 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006254 brcmf_err("Failed to get D11 version (%d)\n", err);
6255 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006256 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006257 cfg->d11inf.io_type = (u8)io_type;
6258 brcmu_d11_attach(&cfg->d11inf);
6259
6260 err = brcmf_setup_wiphy(wiphy, ifp);
6261 if (err < 0)
6262 goto priv_out;
6263
6264 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006265 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006266 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6267 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6268
6269 /* firmware defaults to 40MHz disabled in 2G band. We signal
6270 * cfg80211 here that we do and have it decide we can enable
6271 * it. But first check if device does support 2G operation.
6272 */
6273 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6274 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6275 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6276 }
6277 err = wiphy_register(wiphy);
6278 if (err < 0) {
6279 brcmf_err("Could not register wiphy device (%d)\n", err);
6280 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006281 }
6282
6283 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6284 * setup 40MHz in 2GHz band and enable OBSS scanning.
6285 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006286 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6287 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006288 if (!err)
6289 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6290 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006291 else
6292 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006293 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006294 /* p2p might require that "if-events" get processed by fweh. So
6295 * activate the already registered event handlers now and activate
6296 * the rest when initialization has completed. drvr->config needs to
6297 * be assigned before activating events.
6298 */
6299 drvr->config = cfg;
6300 err = brcmf_fweh_activate_events(ifp);
6301 if (err) {
6302 brcmf_err("FWEH activation failed (%d)\n", err);
6303 goto wiphy_unreg_out;
6304 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006305
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006306 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006307 if (err) {
6308 brcmf_err("P2P initilisation failed (%d)\n", err);
6309 goto wiphy_unreg_out;
6310 }
6311 err = brcmf_btcoex_attach(cfg);
6312 if (err) {
6313 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6314 brcmf_p2p_detach(&cfg->p2p);
6315 goto wiphy_unreg_out;
6316 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006317
6318 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6319 if (err) {
6320 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6321 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman70b7d942014-07-30 13:20:07 +02006322 } else {
6323 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6324 brcmf_notify_tdls_peer_event);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006325 }
6326
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006327 /* (re-) activate FWEH event handling */
6328 err = brcmf_fweh_activate_events(ifp);
6329 if (err) {
6330 brcmf_err("FWEH activation failed (%d)\n", err);
6331 goto wiphy_unreg_out;
6332 }
6333
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006334 return cfg;
6335
Arend van Sprielb48d8912014-07-12 08:49:41 +02006336wiphy_unreg_out:
6337 wiphy_unregister(cfg->wiphy);
6338priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006339 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006340 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006341 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006342wiphy_out:
6343 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006344 return NULL;
6345}
6346
6347void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6348{
6349 if (!cfg)
6350 return;
6351
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006352 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006353 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006354 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006355 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006356}