blob: 8a3c9fa171ee316481545a55d2b0d3310cf59ada [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) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100843 brcmf_dbg(INFO, "IF Type = AP\n");
844 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200845 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100846 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200847 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100848 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200849 err = -EAGAIN;
850 goto done;
851 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100852 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100853 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200854 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200855 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200856
Hante Meuleman8851cce2014-07-30 13:20:02 +0200857 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
858
Arend van Spriel5b435de2011-10-05 13:19:03 +0200859done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100860 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200861
862 return err;
863}
864
Franky Lin83cf17a2013-04-11 13:28:50 +0200865static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
866 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200867 struct cfg80211_scan_request *request)
868{
869 u32 n_ssids;
870 u32 n_channels;
871 s32 i;
872 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200873 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200874 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200875 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200876
Joe Perches93803b32015-03-02 19:54:49 -0800877 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200878 params_le->bss_type = DOT11_BSSTYPE_ANY;
879 params_le->scan_type = 0;
880 params_le->channel_num = 0;
881 params_le->nprobes = cpu_to_le32(-1);
882 params_le->active_time = cpu_to_le32(-1);
883 params_le->passive_time = cpu_to_le32(-1);
884 params_le->home_time = cpu_to_le32(-1);
885 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
886
887 /* if request is null exit so it will be all channel broadcast scan */
888 if (!request)
889 return;
890
891 n_ssids = request->n_ssids;
892 n_channels = request->n_channels;
893 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100894 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
895 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200896 if (n_channels > 0) {
897 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200898 chanspec = channel_to_chanspec(&cfg->d11inf,
899 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100900 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
901 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200902 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200903 }
904 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100905 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200906 }
907 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100908 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200909 if (n_ssids > 0) {
910 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
911 n_channels * sizeof(u16);
912 offset = roundup(offset, sizeof(u32));
913 ptr = (char *)params_le + offset;
914 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200915 memset(&ssid_le, 0, sizeof(ssid_le));
916 ssid_le.SSID_len =
917 cpu_to_le32(request->ssids[i].ssid_len);
918 memcpy(ssid_le.SSID, request->ssids[i].ssid,
919 request->ssids[i].ssid_len);
920 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100921 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200922 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100923 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
924 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200925 memcpy(ptr, &ssid_le, sizeof(ssid_le));
926 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200927 }
928 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100929 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200930 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100931 brcmf_dbg(SCAN, "SSID %s len=%d\n",
932 params_le->ssid_le.SSID,
933 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200934 params_le->ssid_le.SSID_len =
935 cpu_to_le32(request->ssids->ssid_len);
936 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
937 request->ssids->ssid_len);
938 }
939 }
940 /* Adding mask to channel numbers */
941 params_le->channel_num =
942 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
943 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
944}
945
946static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200947brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +0200948 struct cfg80211_scan_request *request, u16 action)
949{
950 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
951 offsetof(struct brcmf_escan_params_le, params_le);
952 struct brcmf_escan_params_le *params;
953 s32 err = 0;
954
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100955 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200956
957 if (request != NULL) {
958 /* Allocate space for populating ssids in struct */
959 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
960
961 /* Allocate space for populating ssids in struct */
962 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
963 }
964
965 params = kzalloc(params_size, GFP_KERNEL);
966 if (!params) {
967 err = -ENOMEM;
968 goto exit;
969 }
970 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200971 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200972 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
973 params->action = cpu_to_le16(action);
974 params->sync_id = cpu_to_le16(0x1234);
975
Arend van Spriela0f472a2013-04-05 10:57:49 +0200976 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200977 if (err) {
978 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100979 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200980 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100981 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200982 }
983
984 kfree(params);
985exit:
986 return err;
987}
988
989static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200990brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +0200991 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200992{
993 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700994 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200995 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100996 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +0200997
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100998 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +0200999 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001000 escan->wiphy = wiphy;
1001 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001002 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001003 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001004 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001005 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001006 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001007 return err;
1008 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001009 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001010 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001011 results->version = 0;
1012 results->count = 0;
1013 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1014
Arend van Spriela0f472a2013-04-05 10:57:49 +02001015 err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001016 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001017 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001018 return err;
1019}
1020
1021static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001022brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001023 struct cfg80211_scan_request *request,
1024 struct cfg80211_ssid *this_ssid)
1025{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001026 struct brcmf_if *ifp = vif->ifp;
1027 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001028 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -08001029 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001030 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001031 bool escan_req;
1032 bool spec_scan;
1033 s32 err;
1034 u32 SSID_len;
1035
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001036 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001037
Arend van Sprielc1179032012-10-22 13:55:33 -07001038 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001039 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001040 return -EAGAIN;
1041 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001042 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001043 brcmf_err("Scanning being aborted: status (%lu)\n",
1044 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001045 return -EAGAIN;
1046 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001047 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1048 brcmf_err("Scanning suppressed: status (%lu)\n",
1049 cfg->scan_status);
1050 return -EAGAIN;
1051 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001052 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001053 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001054 return -EAGAIN;
1055 }
1056
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001057 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001058 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1059 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001060
Hante Meulemane756af52012-09-11 21:18:52 +02001061 escan_req = false;
1062 if (request) {
1063 /* scan bss */
1064 ssids = request->ssids;
1065 escan_req = true;
1066 } else {
1067 /* scan in ibss */
1068 /* we don't do escan in ibss */
1069 ssids = this_ssid;
1070 }
1071
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001072 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001073 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001074 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001075 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001076 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001077 if (err)
1078 goto scan_out;
1079
Arend van Spriela0f472a2013-04-05 10:57:49 +02001080 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001081 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001082 goto scan_out;
1083 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001084 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1085 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001086 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1087 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1088 sr->ssid_le.SSID_len = cpu_to_le32(0);
1089 spec_scan = false;
1090 if (SSID_len) {
1091 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1092 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1093 spec_scan = true;
1094 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001095 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001096
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001097 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001098 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001099 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001100 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001101 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001102 goto scan_out;
1103 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001104 brcmf_scan_config_mpc(ifp, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -07001105 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -07001106 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001107 if (err) {
1108 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001109 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
1110 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001111 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001112 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001113
Daniel Kim5e787f72014-06-21 12:11:18 +02001114 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001115 goto scan_out;
1116 }
1117 }
1118
Hante Meuleman661fa952015-02-06 18:36:47 +01001119 /* Arm scan timeout timer */
1120 mod_timer(&cfg->escan_timeout, jiffies +
1121 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1122
Hante Meulemane756af52012-09-11 21:18:52 +02001123 return 0;
1124
1125scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001126 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001127 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001128 return err;
1129}
1130
Arend van Spriel5b435de2011-10-05 13:19:03 +02001131static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001132brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001133{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001134 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001135 s32 err = 0;
1136
Arend van Sprield96b8012012-12-05 15:26:02 +01001137 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001138 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1139 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001140 return -EIO;
1141
Arend van Spriela0f472a2013-04-05 10:57:49 +02001142 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001143
Arend van Spriel5b435de2011-10-05 13:19:03 +02001144 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001145 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001146
Arend van Sprield96b8012012-12-05 15:26:02 +01001147 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001148 return err;
1149}
1150
1151static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1152{
1153 s32 err = 0;
1154
Arend van Sprielac24be62012-10-22 10:36:23 -07001155 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1156 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001157 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001158 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001159
1160 return err;
1161}
1162
1163static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1164{
1165 s32 err = 0;
1166
Arend van Sprielac24be62012-10-22 10:36:23 -07001167 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1168 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001169 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001170 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001171
1172 return err;
1173}
1174
1175static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1176{
1177 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001178 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001179
Arend van Sprielac24be62012-10-22 10:36:23 -07001180 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001181 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001182 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001183 return err;
1184 }
1185 return err;
1186}
1187
1188static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1189{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001190 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1191 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001192 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001193 s32 err = 0;
1194
Arend van Sprield96b8012012-12-05 15:26:02 +01001195 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001196 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001197 return -EIO;
1198
1199 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001200 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1201 cfg->conf->rts_threshold = wiphy->rts_threshold;
1202 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001203 if (!err)
1204 goto done;
1205 }
1206 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001207 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1208 cfg->conf->frag_threshold = wiphy->frag_threshold;
1209 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001210 if (!err)
1211 goto done;
1212 }
1213 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001214 && (cfg->conf->retry_long != wiphy->retry_long)) {
1215 cfg->conf->retry_long = wiphy->retry_long;
1216 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001217 if (!err)
1218 goto done;
1219 }
1220 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001221 && (cfg->conf->retry_short != wiphy->retry_short)) {
1222 cfg->conf->retry_short = wiphy->retry_short;
1223 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001224 if (!err)
1225 goto done;
1226 }
1227
1228done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001229 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001230 return err;
1231}
1232
Arend van Spriel5b435de2011-10-05 13:19:03 +02001233static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1234{
1235 memset(prof, 0, sizeof(*prof));
1236}
1237
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001238static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1239{
1240 u16 reason;
1241
1242 switch (e->event_code) {
1243 case BRCMF_E_DEAUTH:
1244 case BRCMF_E_DEAUTH_IND:
1245 case BRCMF_E_DISASSOC_IND:
1246 reason = e->reason;
1247 break;
1248 case BRCMF_E_LINK:
1249 default:
1250 reason = 0;
1251 break;
1252 }
1253 return reason;
1254}
1255
1256static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001257{
Piotr Haber61730d42013-04-23 12:53:12 +02001258 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001259 s32 err = 0;
1260
Arend van Sprield96b8012012-12-05 15:26:02 +01001261 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001262
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001263 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001264 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001265 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001266 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001267 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001268 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001269 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001270 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001271 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
Johannes Berg80279fb2015-05-22 16:22:20 +02001272 true, GFP_KERNEL);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001273
Arend van Spriel5b435de2011-10-05 13:19:03 +02001274 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001275 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001276 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1277 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001278 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001279}
1280
1281static s32
1282brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1283 struct cfg80211_ibss_params *params)
1284{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001285 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001286 struct brcmf_if *ifp = netdev_priv(ndev);
1287 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001288 struct brcmf_join_params join_params;
1289 size_t join_params_size = 0;
1290 s32 err = 0;
1291 s32 wsec = 0;
1292 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001293 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001294
Arend van Sprield96b8012012-12-05 15:26:02 +01001295 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001296 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001297 return -EIO;
1298
1299 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001300 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001301 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001302 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001303 return -EOPNOTSUPP;
1304 }
1305
Arend van Sprielc1179032012-10-22 13:55:33 -07001306 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001307
1308 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001309 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001310 else
Arend van Spriel16886732012-12-05 15:26:04 +01001311 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001312
Johannes Berg683b6d32012-11-08 21:25:48 +01001313 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001314 brcmf_dbg(CONN, "channel: %d\n",
1315 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001316 else
Arend van Spriel16886732012-12-05 15:26:04 +01001317 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001318
1319 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001320 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321 else
Arend van Spriel16886732012-12-05 15:26:04 +01001322 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001323
1324 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001325 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001326 else
Arend van Spriel16886732012-12-05 15:26:04 +01001327 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001328
1329 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001330 brcmf_dbg(CONN, "beacon interval: %d\n",
1331 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001332 else
Arend van Spriel16886732012-12-05 15:26:04 +01001333 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334
1335 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001336 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337 else
Arend van Spriel16886732012-12-05 15:26:04 +01001338 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001339
1340 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001341 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001342 else
Arend van Spriel16886732012-12-05 15:26:04 +01001343 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001344
1345 /* Configure Privacy for starter */
1346 if (params->privacy)
1347 wsec |= WEP_ENABLED;
1348
Arend van Sprielc1179032012-10-22 13:55:33 -07001349 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001350 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001351 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001352 goto done;
1353 }
1354
1355 /* Configure Beacon Interval for starter */
1356 if (params->beacon_interval)
1357 bcnprd = params->beacon_interval;
1358 else
1359 bcnprd = 100;
1360
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001361 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001362 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001363 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001364 goto done;
1365 }
1366
1367 /* Configure required join parameter */
1368 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1369
1370 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001371 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1372 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1373 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1374 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001375 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001376
1377 /* BSSID */
1378 if (params->bssid) {
1379 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1380 join_params_size = sizeof(join_params.ssid_le) +
1381 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001382 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001383 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001384 eth_broadcast_addr(join_params.params_le.bssid);
1385 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001386 }
1387
Arend van Spriel5b435de2011-10-05 13:19:03 +02001388 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001389 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001390 u32 target_channel;
1391
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001392 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001393 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001394 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001395 if (params->channel_fixed) {
1396 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001397 chanspec = chandef_to_chanspec(&cfg->d11inf,
1398 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001399 join_params.params_le.chanspec_list[0] =
1400 cpu_to_le16(chanspec);
1401 join_params.params_le.chanspec_num = cpu_to_le32(1);
1402 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001403 }
1404
1405 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001406 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001407 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001408 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001409 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001410 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001411 goto done;
1412 }
1413 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001414 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001416 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001417
1418
Arend van Sprielc1179032012-10-22 13:55:33 -07001419 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001420 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001421 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001422 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001423 goto done;
1424 }
1425
1426done:
1427 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001428 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001429 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001430 return err;
1431}
1432
1433static s32
1434brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1435{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001436 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001437
Arend van Sprield96b8012012-12-05 15:26:02 +01001438 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001439 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001440 return -EIO;
1441
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001442 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001443
Arend van Sprield96b8012012-12-05 15:26:02 +01001444 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001445
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001446 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001447}
1448
1449static s32 brcmf_set_wpa_version(struct net_device *ndev,
1450 struct cfg80211_connect_params *sme)
1451{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001452 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001453 struct brcmf_cfg80211_security *sec;
1454 s32 val = 0;
1455 s32 err = 0;
1456
1457 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1458 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1459 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1460 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1461 else
1462 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001463 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001464 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001465 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001466 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001467 return err;
1468 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001469 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001470 sec->wpa_versions = sme->crypto.wpa_versions;
1471 return err;
1472}
1473
1474static s32 brcmf_set_auth_type(struct net_device *ndev,
1475 struct cfg80211_connect_params *sme)
1476{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001477 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001478 struct brcmf_cfg80211_security *sec;
1479 s32 val = 0;
1480 s32 err = 0;
1481
1482 switch (sme->auth_type) {
1483 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1484 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001485 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001486 break;
1487 case NL80211_AUTHTYPE_SHARED_KEY:
1488 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001489 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001490 break;
1491 case NL80211_AUTHTYPE_AUTOMATIC:
1492 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001493 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001494 break;
1495 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001496 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001497 default:
1498 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001499 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001500 break;
1501 }
1502
Hante Meuleman89286dc2013-02-08 15:53:46 +01001503 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001504 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001505 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001506 return err;
1507 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001508 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001509 sec->auth_type = sme->auth_type;
1510 return err;
1511}
1512
1513static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001514brcmf_set_wsec_mode(struct net_device *ndev,
1515 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001516{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001517 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001518 struct brcmf_cfg80211_security *sec;
1519 s32 pval = 0;
1520 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001521 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001522 s32 err = 0;
1523
1524 if (sme->crypto.n_ciphers_pairwise) {
1525 switch (sme->crypto.ciphers_pairwise[0]) {
1526 case WLAN_CIPHER_SUITE_WEP40:
1527 case WLAN_CIPHER_SUITE_WEP104:
1528 pval = WEP_ENABLED;
1529 break;
1530 case WLAN_CIPHER_SUITE_TKIP:
1531 pval = TKIP_ENABLED;
1532 break;
1533 case WLAN_CIPHER_SUITE_CCMP:
1534 pval = AES_ENABLED;
1535 break;
1536 case WLAN_CIPHER_SUITE_AES_CMAC:
1537 pval = AES_ENABLED;
1538 break;
1539 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001540 brcmf_err("invalid cipher pairwise (%d)\n",
1541 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001542 return -EINVAL;
1543 }
1544 }
1545 if (sme->crypto.cipher_group) {
1546 switch (sme->crypto.cipher_group) {
1547 case WLAN_CIPHER_SUITE_WEP40:
1548 case WLAN_CIPHER_SUITE_WEP104:
1549 gval = WEP_ENABLED;
1550 break;
1551 case WLAN_CIPHER_SUITE_TKIP:
1552 gval = TKIP_ENABLED;
1553 break;
1554 case WLAN_CIPHER_SUITE_CCMP:
1555 gval = AES_ENABLED;
1556 break;
1557 case WLAN_CIPHER_SUITE_AES_CMAC:
1558 gval = AES_ENABLED;
1559 break;
1560 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001561 brcmf_err("invalid cipher group (%d)\n",
1562 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001563 return -EINVAL;
1564 }
1565 }
1566
Arend van Spriel16886732012-12-05 15:26:04 +01001567 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001568 /* In case of privacy, but no security and WPS then simulate */
1569 /* setting AES. WPS-2.0 allows no security */
1570 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1571 sme->privacy)
1572 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001573
1574 if (mfp)
1575 wsec = pval | gval | MFP_CAPABLE;
1576 else
1577 wsec = pval | gval;
1578 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001579 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001580 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001581 return err;
1582 }
1583
Arend van Spriel06bb1232012-09-27 14:17:56 +02001584 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001585 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1586 sec->cipher_group = sme->crypto.cipher_group;
1587
1588 return err;
1589}
1590
1591static s32
1592brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1593{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001594 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001595 struct brcmf_cfg80211_security *sec;
1596 s32 val = 0;
1597 s32 err = 0;
1598
1599 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001600 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1601 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001602 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001603 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001604 return err;
1605 }
1606 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1607 switch (sme->crypto.akm_suites[0]) {
1608 case WLAN_AKM_SUITE_8021X:
1609 val = WPA_AUTH_UNSPECIFIED;
1610 break;
1611 case WLAN_AKM_SUITE_PSK:
1612 val = WPA_AUTH_PSK;
1613 break;
1614 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001615 brcmf_err("invalid cipher group (%d)\n",
1616 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001617 return -EINVAL;
1618 }
1619 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1620 switch (sme->crypto.akm_suites[0]) {
1621 case WLAN_AKM_SUITE_8021X:
1622 val = WPA2_AUTH_UNSPECIFIED;
1623 break;
1624 case WLAN_AKM_SUITE_PSK:
1625 val = WPA2_AUTH_PSK;
1626 break;
1627 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001628 brcmf_err("invalid cipher group (%d)\n",
1629 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001630 return -EINVAL;
1631 }
1632 }
1633
Arend van Spriel16886732012-12-05 15:26:04 +01001634 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001635 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1636 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001637 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001638 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001639 return err;
1640 }
1641 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001642 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001643 sec->wpa_auth = sme->crypto.akm_suites[0];
1644
1645 return err;
1646}
1647
1648static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001649brcmf_set_sharedkey(struct net_device *ndev,
1650 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001651{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001652 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001653 struct brcmf_cfg80211_security *sec;
1654 struct brcmf_wsec_key key;
1655 s32 val;
1656 s32 err = 0;
1657
Arend van Spriel16886732012-12-05 15:26:04 +01001658 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001659
Roland Vossena718e2f2011-10-12 20:51:24 +02001660 if (sme->key_len == 0)
1661 return 0;
1662
Arend van Spriel06bb1232012-09-27 14:17:56 +02001663 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001664 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1665 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001666
1667 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1668 return 0;
1669
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001670 if (!(sec->cipher_pairwise &
1671 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1672 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001673
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001674 memset(&key, 0, sizeof(key));
1675 key.len = (u32) sme->key_len;
1676 key.index = (u32) sme->key_idx;
1677 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001678 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001679 return -EINVAL;
1680 }
1681 memcpy(key.data, sme->key, key.len);
1682 key.flags = BRCMF_PRIMARY_KEY;
1683 switch (sec->cipher_pairwise) {
1684 case WLAN_CIPHER_SUITE_WEP40:
1685 key.algo = CRYPTO_ALGO_WEP1;
1686 break;
1687 case WLAN_CIPHER_SUITE_WEP104:
1688 key.algo = CRYPTO_ALGO_WEP128;
1689 break;
1690 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001691 brcmf_err("Invalid algorithm (%d)\n",
1692 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001693 return -EINVAL;
1694 }
1695 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001696 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1697 key.len, key.index, key.algo);
1698 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001699 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001700 if (err)
1701 return err;
1702
1703 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001704 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001705 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001706 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001707 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001708 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001709 }
1710 return err;
1711}
1712
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001713static
1714enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1715 enum nl80211_auth_type type)
1716{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001717 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1718 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1719 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1720 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001721 }
1722 return type;
1723}
1724
Arend van Spriel5b435de2011-10-05 13:19:03 +02001725static s32
1726brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001727 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001728{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001729 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001730 struct brcmf_if *ifp = netdev_priv(ndev);
1731 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001732 struct ieee80211_channel *chan = sme->channel;
1733 struct brcmf_join_params join_params;
1734 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001735 const struct brcmf_tlv *rsn_ie;
1736 const struct brcmf_vs_tlv *wpa_ie;
1737 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001738 u32 ie_len;
1739 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001740 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001741 s32 err = 0;
1742
Arend van Sprield96b8012012-12-05 15:26:02 +01001743 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001744 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001745 return -EIO;
1746
1747 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001748 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001749 return -EOPNOTSUPP;
1750 }
1751
Hante Meuleman89286dc2013-02-08 15:53:46 +01001752 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1753 /* A normal (non P2P) connection request setup. */
1754 ie = NULL;
1755 ie_len = 0;
1756 /* find the WPA_IE */
1757 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1758 if (wpa_ie) {
1759 ie = wpa_ie;
1760 ie_len = wpa_ie->len + TLV_HDR_LEN;
1761 } else {
1762 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001763 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1764 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001765 WLAN_EID_RSN);
1766 if (rsn_ie) {
1767 ie = rsn_ie;
1768 ie_len = rsn_ie->len + TLV_HDR_LEN;
1769 }
1770 }
1771 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1772 }
1773
1774 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1775 sme->ie, sme->ie_len);
1776 if (err)
1777 brcmf_err("Set Assoc REQ IE Failed\n");
1778 else
1779 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1780
Arend van Sprielc1179032012-10-22 13:55:33 -07001781 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001782
1783 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001784 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001785 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001786 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001787 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1788 cfg->channel, chan->center_freq, chanspec);
1789 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001790 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001791 chanspec = 0;
1792 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001793
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001794 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001795
1796 err = brcmf_set_wpa_version(ndev, sme);
1797 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001798 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001799 goto done;
1800 }
1801
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001802 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001803 err = brcmf_set_auth_type(ndev, sme);
1804 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001805 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001806 goto done;
1807 }
1808
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001809 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001810 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001811 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001812 goto done;
1813 }
1814
1815 err = brcmf_set_key_mgmt(ndev, sme);
1816 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001817 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001818 goto done;
1819 }
1820
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001821 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001822 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001823 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001824 goto done;
1825 }
1826
Hante Meuleman89286dc2013-02-08 15:53:46 +01001827 profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
1828 (u32)sme->ssid_len);
1829 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1830 if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1831 profile->ssid.SSID[profile->ssid.SSID_len] = 0;
1832 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
1833 profile->ssid.SSID_len);
1834 }
1835
1836 /* Join with specific BSSID and cached SSID
1837 * If SSID is zero join based on BSSID only
1838 */
1839 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1840 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1841 if (cfg->channel)
1842 join_params_size += sizeof(u16);
1843 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1844 if (ext_join_params == NULL) {
1845 err = -ENOMEM;
1846 goto done;
1847 }
1848 ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
1849 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
1850 profile->ssid.SSID_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001851
Hante Meuleman89286dc2013-02-08 15:53:46 +01001852 /* Set up join scan parameters */
1853 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001854 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1855
1856 if (sme->bssid)
1857 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1858 else
Joe Perches93803b32015-03-02 19:54:49 -08001859 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001860
1861 if (cfg->channel) {
1862 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1863
1864 ext_join_params->assoc_le.chanspec_list[0] =
1865 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001866 /* Increase dwell time to receive probe response or detect
1867 * beacon from target AP at a noisy air only during connect
1868 * command.
1869 */
1870 ext_join_params->scan_le.active_time =
1871 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1872 ext_join_params->scan_le.passive_time =
1873 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1874 /* To sync with presence period of VSDB GO send probe request
1875 * more frequently. Probe request will be stopped when it gets
1876 * probe response from target AP/GO.
1877 */
1878 ext_join_params->scan_le.nprobes =
1879 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1880 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1881 } else {
1882 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1883 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1884 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001885 }
1886
1887 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1888 join_params_size);
1889 kfree(ext_join_params);
1890 if (!err)
1891 /* This is it. join command worked, we are done */
1892 goto done;
1893
1894 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001895 memset(&join_params, 0, sizeof(join_params));
1896 join_params_size = sizeof(join_params.ssid_le);
1897
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001898 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001899 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001900
Hante Meuleman89286dc2013-02-08 15:53:46 +01001901 if (sme->bssid)
1902 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1903 else
Joe Perches93803b32015-03-02 19:54:49 -08001904 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001905
Hante Meuleman17012612013-02-06 18:40:44 +01001906 if (cfg->channel) {
1907 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1908 join_params.params_le.chanspec_num = cpu_to_le32(1);
1909 join_params_size += sizeof(join_params.params_le);
1910 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001911 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001912 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001913 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001914 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001915
1916done:
1917 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001918 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001919 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001920 return err;
1921}
1922
1923static s32
1924brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1925 u16 reason_code)
1926{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001927 struct brcmf_if *ifp = netdev_priv(ndev);
1928 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001929 struct brcmf_scb_val_le scbval;
1930 s32 err = 0;
1931
Arend van Sprield96b8012012-12-05 15:26:02 +01001932 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001933 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001934 return -EIO;
1935
Arend van Sprielc1179032012-10-22 13:55:33 -07001936 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001937 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001938 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001939
Arend van Spriel06bb1232012-09-27 14:17:56 +02001940 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001941 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001942 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001943 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001944 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001945 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001946
Arend van Sprield96b8012012-12-05 15:26:02 +01001947 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001948 return err;
1949}
1950
1951static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001952brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001953 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001954{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001955 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001956 struct net_device *ndev = cfg_to_ndev(cfg);
1957 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001958 s32 err;
1959 s32 disable;
1960 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001961
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001962 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07001963 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001964 return -EIO;
1965
1966 switch (type) {
1967 case NL80211_TX_POWER_AUTOMATIC:
1968 break;
1969 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001970 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001971 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001972 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001973 err = -EINVAL;
1974 goto done;
1975 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001976 qdbm = MBM_TO_DBM(4 * mbm);
1977 if (qdbm > 127)
1978 qdbm = 127;
1979 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001980 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001981 default:
1982 brcmf_err("Unsupported type %d\n", type);
1983 err = -EINVAL;
1984 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001985 }
1986 /* Make sure radio is off or on as far as software is concerned */
1987 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001988 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001989 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001990 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001991
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001992 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001993 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001994 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001995
1996done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001997 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001998 return err;
1999}
2000
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002001static s32
2002brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2003 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002004{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002005 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002006 struct net_device *ndev = cfg_to_ndev(cfg);
2007 struct brcmf_if *ifp = netdev_priv(ndev);
2008 s32 qdbm = 0;
2009 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002010
Arend van Sprield96b8012012-12-05 15:26:02 +01002011 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002012 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002013 return -EIO;
2014
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002015 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002016 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002017 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002018 goto done;
2019 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002020 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002021
2022done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002023 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002024 return err;
2025}
2026
2027static s32
2028brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002029 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002030{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002031 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002032 u32 index;
2033 u32 wsec;
2034 s32 err = 0;
2035
Arend van Sprield96b8012012-12-05 15:26:02 +01002036 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002037 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002038 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002039 return -EIO;
2040
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002041 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002042 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002043 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002044 goto done;
2045 }
2046
2047 if (wsec & WEP_ENABLED) {
2048 /* Just select a new current key */
2049 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002050 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002051 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002052 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002053 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002054 }
2055done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002056 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002057 return err;
2058}
2059
2060static s32
2061brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2062 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2063{
Hante Meuleman992f6062013-04-02 21:06:17 +02002064 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002065 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002066 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002067 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002068
2069 memset(&key, 0, sizeof(key));
2070 key.index = (u32) key_idx;
2071 /* Instead of bcast for ea address for default wep keys,
2072 driver needs it to be Null */
2073 if (!is_multicast_ether_addr(mac_addr))
2074 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2075 key.len = (u32) params->key_len;
2076 /* check for key index change */
2077 if (key.len == 0) {
2078 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002079 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002080 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002081 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002082 } else {
2083 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002084 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002085 return -EINVAL;
2086 }
2087
Arend van Spriel16886732012-12-05 15:26:04 +01002088 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002089 memcpy(key.data, params->key, key.len);
2090
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002091 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002092 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2093 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002094 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2095 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2096 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2097 }
2098
2099 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2100 if (params->seq && params->seq_len == 6) {
2101 /* rx iv */
2102 u8 *ivptr;
2103 ivptr = (u8 *) params->seq;
2104 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2105 (ivptr[3] << 8) | ivptr[2];
2106 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2107 key.iv_initialized = true;
2108 }
2109
2110 switch (params->cipher) {
2111 case WLAN_CIPHER_SUITE_WEP40:
2112 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002113 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002114 break;
2115 case WLAN_CIPHER_SUITE_WEP104:
2116 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002117 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002118 break;
2119 case WLAN_CIPHER_SUITE_TKIP:
2120 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002121 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002122 break;
2123 case WLAN_CIPHER_SUITE_AES_CMAC:
2124 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002125 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002126 break;
2127 case WLAN_CIPHER_SUITE_CCMP:
2128 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002129 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002130 break;
2131 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002132 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002133 return -EINVAL;
2134 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002135 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002136 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002137 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002138 }
2139 return err;
2140}
2141
2142static s32
2143brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2144 u8 key_idx, bool pairwise, const u8 *mac_addr,
2145 struct key_params *params)
2146{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002147 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002148 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002149 s32 val;
2150 s32 wsec;
2151 s32 err = 0;
2152 u8 keybuf[8];
2153
Arend van Sprield96b8012012-12-05 15:26:02 +01002154 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002155 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002156 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002157 return -EIO;
2158
Hante Meuleman118eb302014-12-21 12:43:49 +01002159 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2160 /* we ignore this key index in this case */
2161 brcmf_err("invalid key index (%d)\n", key_idx);
2162 return -EINVAL;
2163 }
2164
Daniel Kim787eb032014-01-29 15:32:23 +01002165 if (mac_addr &&
2166 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2167 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002168 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002169 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2170 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002171
Hante Meuleman118eb302014-12-21 12:43:49 +01002172 key = &ifp->vif->profile.key[key_idx];
2173 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002174
Hante Meuleman118eb302014-12-21 12:43:49 +01002175 if (params->key_len > sizeof(key->data)) {
2176 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002177 err = -EINVAL;
2178 goto done;
2179 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002180 key->len = params->key_len;
2181 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002182
Hante Meuleman118eb302014-12-21 12:43:49 +01002183 memcpy(key->data, params->key, key->len);
2184
2185 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002186 switch (params->cipher) {
2187 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002188 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002189 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002190 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002191 break;
2192 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002193 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002194 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002195 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002196 break;
2197 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002198 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002199 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002200 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2201 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2202 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002203 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002204 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002205 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002206 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002207 break;
2208 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002209 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002210 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002211 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002212 break;
2213 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002214 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002215 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002216 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002217 break;
2218 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002219 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002220 err = -EINVAL;
2221 goto done;
2222 }
2223
Hante Meuleman118eb302014-12-21 12:43:49 +01002224 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002225 if (err)
2226 goto done;
2227
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002228 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002229 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002230 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002231 goto done;
2232 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002233 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002234 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002235 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002236 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002237 goto done;
2238 }
2239
Arend van Spriel5b435de2011-10-05 13:19:03 +02002240done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002241 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002242 return err;
2243}
2244
2245static s32
2246brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2247 u8 key_idx, bool pairwise, const u8 *mac_addr)
2248{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002249 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002250 struct brcmf_wsec_key key;
2251 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002252
Arend van Sprield96b8012012-12-05 15:26:02 +01002253 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002254 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002255 return -EIO;
2256
Hante Meuleman118eb302014-12-21 12:43:49 +01002257 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002258 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002259 return -EINVAL;
2260 }
2261
Arend van Spriel5b435de2011-10-05 13:19:03 +02002262 memset(&key, 0, sizeof(key));
2263
2264 key.index = (u32) key_idx;
2265 key.flags = BRCMF_PRIMARY_KEY;
2266 key.algo = CRYPTO_ALGO_OFF;
2267
Arend van Spriel16886732012-12-05 15:26:04 +01002268 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002269
2270 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002271 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002272
Arend van Sprield96b8012012-12-05 15:26:02 +01002273 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002274 return err;
2275}
2276
2277static s32
2278brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2279 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2280 void (*callback) (void *cookie, struct key_params * params))
2281{
2282 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002283 struct brcmf_if *ifp = netdev_priv(ndev);
2284 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002285 struct brcmf_cfg80211_security *sec;
2286 s32 wsec;
2287 s32 err = 0;
2288
Arend van Sprield96b8012012-12-05 15:26:02 +01002289 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002290 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002291 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002292 return -EIO;
2293
2294 memset(&params, 0, sizeof(params));
2295
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002296 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002297 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002298 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002299 /* Ignore this error, may happen during DISASSOC */
2300 err = -EAGAIN;
2301 goto done;
2302 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002303 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002304 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002305 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2306 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002307 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002308 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2309 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002310 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002311 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002312 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002313 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002314 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002315 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002316 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002317 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002318 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002319 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002320 err = -EINVAL;
2321 goto done;
2322 }
2323 callback(cookie, &params);
2324
2325done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002326 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002327 return err;
2328}
2329
2330static s32
2331brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2332 struct net_device *ndev, u8 key_idx)
2333{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002334 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002335
2336 return -EOPNOTSUPP;
2337}
2338
Hante Meuleman118eb302014-12-21 12:43:49 +01002339static void
2340brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2341{
2342 s32 err;
2343 u8 key_idx;
2344 struct brcmf_wsec_key *key;
2345 s32 wsec;
2346
2347 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2348 key = &ifp->vif->profile.key[key_idx];
2349 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2350 (key->algo == CRYPTO_ALGO_WEP128))
2351 break;
2352 }
2353 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2354 return;
2355
2356 err = send_key_to_dongle(ifp, key);
2357 if (err) {
2358 brcmf_err("Setting WEP key failed (%d)\n", err);
2359 return;
2360 }
2361 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2362 if (err) {
2363 brcmf_err("get wsec error (%d)\n", err);
2364 return;
2365 }
2366 wsec |= WEP_ENABLED;
2367 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2368 if (err)
2369 brcmf_err("set wsec error (%d)\n", err);
2370}
2371
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002372static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2373{
2374 struct nl80211_sta_flag_update *sfu;
2375
2376 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2377 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2378 sfu = &si->sta_flags;
2379 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2380 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2381 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2382 BIT(NL80211_STA_FLAG_AUTHORIZED);
2383 if (fw_sta_flags & BRCMF_STA_WME)
2384 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2385 if (fw_sta_flags & BRCMF_STA_AUTHE)
2386 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2387 if (fw_sta_flags & BRCMF_STA_ASSOC)
2388 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2389 if (fw_sta_flags & BRCMF_STA_AUTHO)
2390 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2391}
2392
2393static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2394{
2395 struct {
2396 __le32 len;
2397 struct brcmf_bss_info_le bss_le;
2398 } *buf;
2399 u16 capability;
2400 int err;
2401
2402 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2403 if (!buf)
2404 return;
2405
2406 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2407 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2408 WL_BSS_INFO_MAX);
2409 if (err) {
2410 brcmf_err("Failed to get bss info (%d)\n", err);
2411 return;
2412 }
2413 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2414 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2415 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2416 capability = le16_to_cpu(buf->bss_le.capability);
2417 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2418 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2419 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2420 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2421 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2422 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2423}
2424
Arend van Spriel5b435de2011-10-05 13:19:03 +02002425static s32
2426brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002427 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002428{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002429 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002430 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002431 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002432 u32 sta_flags;
2433 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002434 s32 total_rssi;
2435 s32 count_rssi;
2436 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002437
Arend van Sprield96b8012012-12-05 15:26:02 +01002438 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002439 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002440 return -EIO;
2441
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002442 memset(&sta_info_le, 0, sizeof(sta_info_le));
2443 memcpy(&sta_info_le, mac, ETH_ALEN);
2444 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2445 &sta_info_le,
2446 sizeof(sta_info_le));
2447 is_tdls_peer = !err;
2448 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002449 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002450 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002451 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002452 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002453 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002454 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002455 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002456 }
2457 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2458 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2459 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2460 sta_flags = le32_to_cpu(sta_info_le.flags);
2461 brcmf_convert_sta_flags(sta_flags, sinfo);
2462 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2463 if (is_tdls_peer)
2464 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2465 else
2466 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2467 if (sta_flags & BRCMF_STA_ASSOC) {
2468 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2469 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2470 brcmf_fill_bss_param(ifp, sinfo);
2471 }
2472 if (sta_flags & BRCMF_STA_SCBSTATS) {
2473 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2474 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2475 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2476 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2477 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2478 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2479 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2480 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2481 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002482 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002483 sinfo->txrate.legacy =
2484 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002485 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002486 if (sinfo->rx_packets) {
2487 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002488 sinfo->rxrate.legacy =
2489 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002490 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002491 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2492 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2493 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2494 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2495 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2496 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002497 total_rssi = 0;
2498 count_rssi = 0;
2499 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2500 if (sta_info_le.rssi[i]) {
2501 sinfo->chain_signal_avg[count_rssi] =
2502 sta_info_le.rssi[i];
2503 sinfo->chain_signal[count_rssi] =
2504 sta_info_le.rssi[i];
2505 total_rssi += sta_info_le.rssi[i];
2506 count_rssi++;
2507 }
2508 }
2509 if (count_rssi) {
2510 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2511 sinfo->chains = count_rssi;
2512
2513 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2514 total_rssi /= count_rssi;
2515 sinfo->signal = total_rssi;
2516 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002517 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002518done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002519 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002520 return err;
2521}
2522
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002523static int
2524brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2525 int idx, u8 *mac, struct station_info *sinfo)
2526{
2527 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2528 struct brcmf_if *ifp = netdev_priv(ndev);
2529 s32 err;
2530
2531 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2532
2533 if (idx == 0) {
2534 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2535 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2536 &cfg->assoclist,
2537 sizeof(cfg->assoclist));
2538 if (err) {
2539 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2540 err);
2541 cfg->assoclist.count = 0;
2542 return -EOPNOTSUPP;
2543 }
2544 }
2545 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2546 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2547 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2548 }
2549 return -ENOENT;
2550}
2551
Arend van Spriel5b435de2011-10-05 13:19:03 +02002552static s32
2553brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2554 bool enabled, s32 timeout)
2555{
2556 s32 pm;
2557 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002558 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002559 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002560
Arend van Sprield96b8012012-12-05 15:26:02 +01002561 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002562
2563 /*
2564 * Powersave enable/disable request is coming from the
2565 * cfg80211 even before the interface is up. In that
2566 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002567 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002568 * FW later while initializing the dongle
2569 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002570 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002571 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002572
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002573 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002574 goto done;
2575 }
2576
2577 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002578 /* Do not enable the power save after assoc if it is a p2p interface */
2579 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2580 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2581 pm = PM_OFF;
2582 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002583 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002584
Arend van Sprielc1179032012-10-22 13:55:33 -07002585 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002586 if (err) {
2587 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002588 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002589 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002590 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002591 }
2592done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002593 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002594 return err;
2595}
2596
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002597static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002598 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002599{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002600 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002601 struct ieee80211_channel *notify_channel;
2602 struct cfg80211_bss *bss;
2603 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002604 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002605 u16 channel;
2606 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002607 u16 notify_capability;
2608 u16 notify_interval;
2609 u8 *notify_ie;
2610 size_t notify_ielen;
2611 s32 notify_signal;
2612
2613 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002614 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002615 return 0;
2616 }
2617
Franky Lin83cf17a2013-04-11 13:28:50 +02002618 if (!bi->ctl_ch) {
2619 ch.chspec = le16_to_cpu(bi->chanspec);
2620 cfg->d11inf.decchspec(&ch);
2621 bi->ctl_ch = ch.chnum;
2622 }
2623 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002624
2625 if (channel <= CH_MAX_2G_CHANNEL)
2626 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2627 else
2628 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2629
2630 freq = ieee80211_channel_to_frequency(channel, band->band);
2631 notify_channel = ieee80211_get_channel(wiphy, freq);
2632
Arend van Spriel5b435de2011-10-05 13:19:03 +02002633 notify_capability = le16_to_cpu(bi->capability);
2634 notify_interval = le16_to_cpu(bi->beacon_period);
2635 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2636 notify_ielen = le32_to_cpu(bi->ie_length);
2637 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2638
Arend van Spriel16886732012-12-05 15:26:04 +01002639 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2640 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2641 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2642 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2643 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002644
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002645 bss = cfg80211_inform_bss(wiphy, notify_channel,
2646 CFG80211_BSS_FTYPE_UNKNOWN,
2647 (const u8 *)bi->BSSID,
2648 0, notify_capability,
2649 notify_interval, notify_ie,
2650 notify_ielen, notify_signal,
2651 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002652
Franky Line78946e2011-11-10 20:30:34 +01002653 if (!bss)
2654 return -ENOMEM;
2655
Johannes Berg5b112d32013-02-01 01:49:58 +01002656 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002657
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002658 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002659}
2660
Roland Vossen6f09be02011-10-18 14:03:02 +02002661static struct brcmf_bss_info_le *
2662next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2663{
2664 if (bss == NULL)
2665 return list->bss_info_le;
2666 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2667 le32_to_cpu(bss->length));
2668}
2669
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002670static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002671{
2672 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002673 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002674 s32 err = 0;
2675 int i;
2676
Hante Meulemanef8596e2014-09-30 10:23:13 +02002677 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002678 if (bss_list->count != 0 &&
2679 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002680 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2681 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002682 return -EOPNOTSUPP;
2683 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002684 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002685 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002686 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002687 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002688 if (err)
2689 break;
2690 }
2691 return err;
2692}
2693
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002694static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002695 struct net_device *ndev, const u8 *bssid)
2696{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002697 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002698 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002699 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002700 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002701 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002702 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002703 u8 *buf = NULL;
2704 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002705 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002706 u16 notify_capability;
2707 u16 notify_interval;
2708 u8 *notify_ie;
2709 size_t notify_ielen;
2710 s32 notify_signal;
2711
Arend van Sprield96b8012012-12-05 15:26:02 +01002712 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002713
2714 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2715 if (buf == NULL) {
2716 err = -ENOMEM;
2717 goto CleanUp;
2718 }
2719
2720 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2721
Arend van Sprielac24be62012-10-22 10:36:23 -07002722 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2723 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002724 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002725 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002726 goto CleanUp;
2727 }
2728
Roland Vossend34bf642011-10-18 14:03:01 +02002729 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002730
Franky Lin83cf17a2013-04-11 13:28:50 +02002731 ch.chspec = le16_to_cpu(bi->chanspec);
2732 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002733
Franky Lin83cf17a2013-04-11 13:28:50 +02002734 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002735 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2736 else
2737 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2738
Franky Lin83cf17a2013-04-11 13:28:50 +02002739 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002740 notify_channel = ieee80211_get_channel(wiphy, freq);
2741
Arend van Spriel5b435de2011-10-05 13:19:03 +02002742 notify_capability = le16_to_cpu(bi->capability);
2743 notify_interval = le16_to_cpu(bi->beacon_period);
2744 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2745 notify_ielen = le32_to_cpu(bi->ie_length);
2746 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2747
Franky Lin83cf17a2013-04-11 13:28:50 +02002748 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002749 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2750 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2751 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002752
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002753 bss = cfg80211_inform_bss(wiphy, notify_channel,
2754 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2755 notify_capability, notify_interval,
2756 notify_ie, notify_ielen, notify_signal,
2757 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002758
Franky Line78946e2011-11-10 20:30:34 +01002759 if (!bss) {
2760 err = -ENOMEM;
2761 goto CleanUp;
2762 }
2763
Johannes Berg5b112d32013-02-01 01:49:58 +01002764 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002765
Arend van Spriel5b435de2011-10-05 13:19:03 +02002766CleanUp:
2767
2768 kfree(buf);
2769
Arend van Sprield96b8012012-12-05 15:26:02 +01002770 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002771
2772 return err;
2773}
2774
Hante Meuleman89286dc2013-02-08 15:53:46 +01002775static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2776 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002777{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002778 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002779 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002780 struct brcmf_ssid *ssid;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002781 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002782 u16 beacon_interval;
2783 u8 dtim_period;
2784 size_t ie_len;
2785 u8 *ie;
2786 s32 err = 0;
2787
Arend van Sprield96b8012012-12-05 15:26:02 +01002788 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002789 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002790 return err;
2791
Arend van Spriel06bb1232012-09-27 14:17:56 +02002792 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002793
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002794 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002795 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002796 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002797 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002798 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002799 goto update_bss_info_out;
2800 }
2801
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002802 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2803 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002804 if (err)
2805 goto update_bss_info_out;
2806
2807 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2808 ie_len = le32_to_cpu(bi->ie_length);
2809 beacon_interval = le16_to_cpu(bi->beacon_period);
2810
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002811 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002812 if (tim)
2813 dtim_period = tim->data[1];
2814 else {
2815 /*
2816 * active scan was done so we could not get dtim
2817 * information out of probe response.
2818 * so we speficially query dtim information to dongle.
2819 */
2820 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002821 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002822 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002823 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002824 goto update_bss_info_out;
2825 }
2826 dtim_period = (u8)var;
2827 }
2828
Arend van Spriel5b435de2011-10-05 13:19:03 +02002829update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002830 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002831 return err;
2832}
2833
Hante Meuleman18e2f612013-02-08 15:53:49 +01002834void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002835{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002836 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002837
Arend van Sprielc1179032012-10-22 13:55:33 -07002838 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002839 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002840 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002841 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002842 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002843 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2844 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002845}
2846
Hante Meulemane756af52012-09-11 21:18:52 +02002847static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2848{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002849 struct brcmf_cfg80211_info *cfg =
2850 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002851 escan_timeout_work);
2852
Hante Meulemanef8596e2014-09-30 10:23:13 +02002853 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002854 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002855}
2856
2857static void brcmf_escan_timeout(unsigned long data)
2858{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002859 struct brcmf_cfg80211_info *cfg =
2860 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002861
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002862 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002863 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002864 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002865 }
2866}
2867
2868static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002869brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2870 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002871 struct brcmf_bss_info_le *bss_info_le)
2872{
Franky Lin83cf17a2013-04-11 13:28:50 +02002873 struct brcmu_chan ch_bss, ch_bss_info_le;
2874
2875 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2876 cfg->d11inf.decchspec(&ch_bss);
2877 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2878 cfg->d11inf.decchspec(&ch_bss_info_le);
2879
Hante Meulemane756af52012-09-11 21:18:52 +02002880 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002881 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002882 bss_info_le->SSID_len == bss->SSID_len &&
2883 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002884 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2885 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002886 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2887 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2888
Hante Meulemane756af52012-09-11 21:18:52 +02002889 /* preserve max RSSI if the measurements are
2890 * both on-channel or both off-channel
2891 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002892 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002893 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002894 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2895 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002896 /* preserve the on-channel rssi measurement
2897 * if the new measurement is off channel
2898 */
2899 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002900 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002901 }
2902 return 1;
2903 }
2904 return 0;
2905}
2906
2907static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002908brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002909 const struct brcmf_event_msg *e, void *data)
2910{
Arend van Spriel19937322012-11-05 16:22:32 -08002911 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002912 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002913 struct brcmf_escan_result_le *escan_result_le;
2914 struct brcmf_bss_info_le *bss_info_le;
2915 struct brcmf_bss_info_le *bss = NULL;
2916 u32 bi_length;
2917 struct brcmf_scan_results *list;
2918 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002919 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002920
Arend van Spriel5c36b992012-11-14 18:46:05 -08002921 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002922
Arend van Spriela0f472a2013-04-05 10:57:49 +02002923 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2924 brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002925 return -EPERM;
2926 }
2927
2928 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002929 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002930 escan_result_le = (struct brcmf_escan_result_le *) data;
2931 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002932 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002933 goto exit;
2934 }
Hante Meulemane756af52012-09-11 21:18:52 +02002935 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002936 brcmf_err("Invalid bss_count %d: ignoring\n",
2937 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002938 goto exit;
2939 }
2940 bss_info_le = &escan_result_le->bss_info_le;
2941
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002942 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2943 goto exit;
2944
2945 if (!cfg->scan_request) {
2946 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2947 goto exit;
2948 }
2949
Hante Meulemane756af52012-09-11 21:18:52 +02002950 bi_length = le32_to_cpu(bss_info_le->length);
2951 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2952 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002953 brcmf_err("Invalid bss_info length %d: ignoring\n",
2954 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002955 goto exit;
2956 }
2957
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002958 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002959 BIT(NL80211_IFTYPE_ADHOC))) {
2960 if (le16_to_cpu(bss_info_le->capability) &
2961 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002962 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002963 goto exit;
2964 }
2965 }
2966
2967 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002968 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002969 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002970 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002971 goto exit;
2972 }
2973
2974 for (i = 0; i < list->count; i++) {
2975 bss = bss ? (struct brcmf_bss_info_le *)
2976 ((unsigned char *)bss +
2977 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002978 if (brcmf_compare_update_same_bss(cfg, bss,
2979 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002980 goto exit;
2981 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002982 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002983 bss_info_le, bi_length);
2984 list->version = le32_to_cpu(bss_info_le->version);
2985 list->buflen += bi_length;
2986 list->count++;
2987 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002988 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002989 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2990 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002991 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002992 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002993 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02002994 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02002995 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002996 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2997 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002998 }
2999exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003000 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02003001}
3002
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003003static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003004{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003005 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3006 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003007 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3008 /* Init scan_timeout timer */
3009 init_timer(&cfg->escan_timeout);
3010 cfg->escan_timeout.data = (unsigned long) cfg;
3011 cfg->escan_timeout.function = brcmf_escan_timeout;
3012 INIT_WORK(&cfg->escan_timeout_work,
3013 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003014}
3015
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003016static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003017{
3018 if (ms < 1000 / HZ) {
3019 cond_resched();
3020 mdelay(ms);
3021 } else {
3022 msleep(ms);
3023 }
3024}
3025
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003026static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3027 u8 *pattern, u32 patternsize, u8 *mask,
3028 u32 packet_offset)
3029{
3030 struct brcmf_fil_wowl_pattern_le *filter;
3031 u32 masksize;
3032 u32 patternoffset;
3033 u8 *buf;
3034 u32 bufsize;
3035 s32 ret;
3036
3037 masksize = (patternsize + 7) / 8;
3038 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3039
3040 bufsize = sizeof(*filter) + patternsize + masksize;
3041 buf = kzalloc(bufsize, GFP_KERNEL);
3042 if (!buf)
3043 return -ENOMEM;
3044 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3045
3046 memcpy(filter->cmd, cmd, 4);
3047 filter->masksize = cpu_to_le32(masksize);
3048 filter->offset = cpu_to_le32(packet_offset);
3049 filter->patternoffset = cpu_to_le32(patternoffset);
3050 filter->patternsize = cpu_to_le32(patternsize);
3051 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3052
3053 if ((mask) && (masksize))
3054 memcpy(buf + sizeof(*filter), mask, masksize);
3055 if ((pattern) && (patternsize))
3056 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3057
3058 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3059
3060 kfree(buf);
3061 return ret;
3062}
3063
Arend van Spriel5b435de2011-10-05 13:19:03 +02003064static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3065{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003066 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3067 struct net_device *ndev = cfg_to_ndev(cfg);
3068 struct brcmf_if *ifp = netdev_priv(ndev);
3069
Arend van Sprield96b8012012-12-05 15:26:02 +01003070 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003071
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003072 if (cfg->wowl_enabled) {
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003073 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003074 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3075 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003076 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003077 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003078 cfg->wowl_enabled = false;
3079 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003080 return 0;
3081}
3082
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003083static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3084 struct brcmf_if *ifp,
3085 struct cfg80211_wowlan *wowl)
3086{
3087 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003088 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003089
3090 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3091
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003092 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003093 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3094 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3095
3096 wowl_config = 0;
3097 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003098 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003099 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003100 wowl_config |= BRCMF_WOWL_MAGIC;
3101 if ((wowl->patterns) && (wowl->n_patterns)) {
3102 wowl_config |= BRCMF_WOWL_NET;
3103 for (i = 0; i < wowl->n_patterns; i++) {
3104 brcmf_config_wowl_pattern(ifp, "add",
3105 (u8 *)wowl->patterns[i].pattern,
3106 wowl->patterns[i].pattern_len,
3107 (u8 *)wowl->patterns[i].mask,
3108 wowl->patterns[i].pkt_offset);
3109 }
3110 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003111 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3112 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3113 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3114 cfg->wowl_enabled = true;
3115}
3116
Arend van Spriel5b435de2011-10-05 13:19:03 +02003117static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003118 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003119{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003120 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3121 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003122 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003123 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003124
Arend van Sprield96b8012012-12-05 15:26:02 +01003125 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003126
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003127 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003128 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003129 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003130 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003131 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003132
Arend van Spriel7d641072012-10-22 13:55:39 -07003133 /* end any scanning */
3134 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003135 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003136
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003137 if (wowl == NULL) {
3138 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3139 list_for_each_entry(vif, &cfg->vif_list, list) {
3140 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3141 continue;
3142 /* While going to suspend if associated with AP
3143 * disassociate from AP to save power while system is
3144 * in suspended state
3145 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003146 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003147 /* Make sure WPA_Supplicant receives all the event
3148 * generated due to DISASSOC call to the fw to keep
3149 * the state fw and WPA_Supplicant state consistent
3150 */
3151 brcmf_delay(500);
3152 }
3153 /* Configure MPC */
3154 brcmf_set_mpc(ifp, 1);
3155
3156 } else {
3157 /* Configure WOWL paramaters */
3158 brcmf_configure_wowl(cfg, ifp, wowl);
3159 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003160
Arend van Spriel7d641072012-10-22 13:55:39 -07003161exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003162 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003163 /* clear any scanning activity */
3164 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003165 return 0;
3166}
3167
3168static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003169brcmf_update_pmklist(struct net_device *ndev,
3170 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3171{
3172 int i, j;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003173 u32 pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003174
Arend van Spriel40c8e952011-10-12 20:51:20 +02003175 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3176
Arend van Spriel16886732012-12-05 15:26:04 +01003177 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003178 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01003179 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
3180 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003181 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01003182 brcmf_dbg(CONN, "%02x\n",
3183 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003184 }
3185
3186 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07003187 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
3188 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003189
3190 return err;
3191}
3192
3193static s32
3194brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3195 struct cfg80211_pmksa *pmksa)
3196{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003197 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003198 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003199 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003200 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003201 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003202
Arend van Sprield96b8012012-12-05 15:26:02 +01003203 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003204 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003205 return -EIO;
3206
Arend van Spriel40c8e952011-10-12 20:51:20 +02003207 pmkid_len = le32_to_cpu(pmkids->npmkid);
3208 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003209 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3210 break;
3211 if (i < WL_NUM_PMKIDS_MAX) {
3212 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3213 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003214 if (i == pmkid_len) {
3215 pmkid_len++;
3216 pmkids->npmkid = cpu_to_le32(pmkid_len);
3217 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003218 } else
3219 err = -EINVAL;
3220
Arend van Spriel16886732012-12-05 15:26:04 +01003221 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
3222 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003223 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003224 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003225
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003226 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003227
Arend van Sprield96b8012012-12-05 15:26:02 +01003228 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003229 return err;
3230}
3231
3232static s32
3233brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3234 struct cfg80211_pmksa *pmksa)
3235{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003236 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003237 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003238 struct pmkid_list pmkid;
3239 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003240 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003241
Arend van Sprield96b8012012-12-05 15:26:02 +01003242 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003243 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003244 return -EIO;
3245
3246 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3247 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3248
Arend van Spriel16886732012-12-05 15:26:04 +01003249 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3250 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003251 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003252 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003253
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003254 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003255 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003256 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003257 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003258 ETH_ALEN))
3259 break;
3260
Arend van Spriel40c8e952011-10-12 20:51:20 +02003261 if ((pmkid_len > 0)
3262 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003263 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003264 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003265 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003266 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3267 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003268 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003269 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3270 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003271 WLAN_PMKID_LEN);
3272 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003273 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003274 } else
3275 err = -EINVAL;
3276
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003277 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003278
Arend van Sprield96b8012012-12-05 15:26:02 +01003279 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003280 return err;
3281
3282}
3283
3284static s32
3285brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3286{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003287 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003288 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003289 s32 err = 0;
3290
Arend van Sprield96b8012012-12-05 15:26:02 +01003291 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003292 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003293 return -EIO;
3294
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003295 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3296 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003297
Arend van Sprield96b8012012-12-05 15:26:02 +01003298 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003299 return err;
3300
3301}
3302
Arend van Spriele5806072012-09-19 22:21:08 +02003303/*
3304 * PFN result doesn't have all the info which are
3305 * required by the supplicant
3306 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3307 * via wl_inform_single_bss in the required format. Escan does require the
3308 * scan request in the form of cfg80211_scan_request. For timebeing, create
3309 * cfg80211_scan_request one out of the received PNO event.
3310 */
3311static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003312brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003313 const struct brcmf_event_msg *e, void *data)
3314{
Arend van Spriel19937322012-11-05 16:22:32 -08003315 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003316 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3317 struct cfg80211_scan_request *request = NULL;
3318 struct cfg80211_ssid *ssid = NULL;
3319 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003320 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003321 int err = 0;
3322 int channel_req = 0;
3323 int band = 0;
3324 struct brcmf_pno_scanresults_le *pfn_result;
3325 u32 result_count;
3326 u32 status;
3327
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003328 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003329
Arend van Spriel5c36b992012-11-14 18:46:05 -08003330 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003331 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003332 return 0;
3333 }
3334
3335 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3336 result_count = le32_to_cpu(pfn_result->count);
3337 status = le32_to_cpu(pfn_result->status);
3338
3339 /*
3340 * PFN event is limited to fit 512 bytes so we may get
3341 * multiple NET_FOUND events. For now place a warning here.
3342 */
3343 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003344 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003345 if (result_count > 0) {
3346 int i;
3347
3348 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003349 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3350 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003351 if (!request || !ssid || !channel) {
3352 err = -ENOMEM;
3353 goto out_err;
3354 }
3355
3356 request->wiphy = wiphy;
3357 data += sizeof(struct brcmf_pno_scanresults_le);
3358 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3359
3360 for (i = 0; i < result_count; i++) {
3361 netinfo = &netinfo_start[i];
3362 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003363 brcmf_err("Invalid netinfo ptr. index: %d\n",
3364 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003365 err = -EINVAL;
3366 goto out_err;
3367 }
3368
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003369 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3370 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003371 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3372 ssid[i].ssid_len = netinfo->SSID_len;
3373 request->n_ssids++;
3374
3375 channel_req = netinfo->channel;
3376 if (channel_req <= CH_MAX_2G_CHANNEL)
3377 band = NL80211_BAND_2GHZ;
3378 else
3379 band = NL80211_BAND_5GHZ;
3380 channel[i].center_freq =
3381 ieee80211_channel_to_frequency(channel_req,
3382 band);
3383 channel[i].band = band;
3384 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3385 request->channels[i] = &channel[i];
3386 request->n_channels++;
3387 }
3388
3389 /* assign parsed ssid array */
3390 if (request->n_ssids)
3391 request->ssids = &ssid[0];
3392
Arend van Sprielc1179032012-10-22 13:55:33 -07003393 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003394 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003395 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003396 }
3397
Arend van Sprielc1179032012-10-22 13:55:33 -07003398 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003399 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003400 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003401 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003402 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003403 goto out_err;
3404 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003405 cfg->sched_escan = true;
3406 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003407 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003408 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003409 goto out_err;
3410 }
3411
3412 kfree(ssid);
3413 kfree(channel);
3414 kfree(request);
3415 return 0;
3416
3417out_err:
3418 kfree(ssid);
3419 kfree(channel);
3420 kfree(request);
3421 cfg80211_sched_scan_stopped(wiphy);
3422 return err;
3423}
3424
Arend van Spriele5806072012-09-19 22:21:08 +02003425static int brcmf_dev_pno_clean(struct net_device *ndev)
3426{
Arend van Spriele5806072012-09-19 22:21:08 +02003427 int ret;
3428
3429 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003430 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003431 if (ret == 0) {
3432 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003433 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3434 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003435 }
3436 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003437 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003438
3439 return ret;
3440}
3441
3442static int brcmf_dev_pno_config(struct net_device *ndev)
3443{
3444 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003445
3446 memset(&pfn_param, 0, sizeof(pfn_param));
3447 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3448
3449 /* set extra pno params */
3450 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3451 pfn_param.repeat = BRCMF_PNO_REPEAT;
3452 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3453
3454 /* set up pno scan fr */
3455 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3456
Arend van Sprielac24be62012-10-22 10:36:23 -07003457 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3458 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003459}
3460
3461static int
3462brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3463 struct net_device *ndev,
3464 struct cfg80211_sched_scan_request *request)
3465{
Arend van Sprielc1179032012-10-22 13:55:33 -07003466 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003467 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003468 struct brcmf_pno_net_param_le pfn;
3469 int i;
3470 int ret = 0;
3471
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003472 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003473 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003474 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003475 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003476 return -EAGAIN;
3477 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003478 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3479 brcmf_err("Scanning suppressed: status (%lu)\n",
3480 cfg->scan_status);
3481 return -EAGAIN;
3482 }
Arend van Spriele5806072012-09-19 22:21:08 +02003483
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003484 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003485 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003486 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003487 return -EINVAL;
3488 }
3489
3490 if (request->n_ssids > 0) {
3491 for (i = 0; i < request->n_ssids; i++) {
3492 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003493 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3494 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003495
3496 /*
3497 * match_set ssids is a supert set of n_ssid list,
3498 * so we need not add these set seperately.
3499 */
3500 }
3501 }
3502
3503 if (request->n_match_sets > 0) {
3504 /* clean up everything */
3505 ret = brcmf_dev_pno_clean(ndev);
3506 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003507 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003508 return ret;
3509 }
3510
3511 /* configure pno */
3512 ret = brcmf_dev_pno_config(ndev);
3513 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003514 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003515 return -EINVAL;
3516 }
3517
3518 /* configure each match set */
3519 for (i = 0; i < request->n_match_sets; i++) {
3520 struct cfg80211_ssid *ssid;
3521 u32 ssid_len;
3522
3523 ssid = &request->match_sets[i].ssid;
3524 ssid_len = ssid->ssid_len;
3525
3526 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003527 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003528 continue;
3529 }
3530 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3531 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3532 pfn.wsec = cpu_to_le32(0);
3533 pfn.infra = cpu_to_le32(1);
3534 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3535 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3536 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003537 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003538 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003539 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3540 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003541 }
3542 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003543 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003544 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003545 return -EINVAL;
3546 }
3547 } else {
3548 return -EINVAL;
3549 }
3550
3551 return 0;
3552}
3553
3554static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3555 struct net_device *ndev)
3556{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003557 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003558
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003559 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003560 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003561 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003562 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003563 return 0;
3564}
Arend van Spriele5806072012-09-19 22:21:08 +02003565
Hante Meuleman1f170112013-02-06 18:40:38 +01003566static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003567{
3568 s32 err;
3569
3570 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003571 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003572 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003573 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003574 return err;
3575 }
3576 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003577 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003578 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003579 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003580 return err;
3581 }
3582 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003583 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003584 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003585 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003586 return err;
3587 }
3588
3589 return 0;
3590}
3591
3592static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3593{
3594 if (is_rsn_ie)
3595 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3596
3597 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3598}
3599
3600static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003601brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003602 const struct brcmf_vs_tlv *wpa_ie,
3603 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003604{
3605 u32 auth = 0; /* d11 open authentication */
3606 u16 count;
3607 s32 err = 0;
3608 s32 len = 0;
3609 u32 i;
3610 u32 wsec;
3611 u32 pval = 0;
3612 u32 gval = 0;
3613 u32 wpa_auth = 0;
3614 u32 offset;
3615 u8 *data;
3616 u16 rsn_cap;
3617 u32 wme_bss_disable;
3618
Arend van Sprield96b8012012-12-05 15:26:02 +01003619 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003620 if (wpa_ie == NULL)
3621 goto exit;
3622
3623 len = wpa_ie->len + TLV_HDR_LEN;
3624 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003625 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003626 if (!is_rsn_ie)
3627 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003628 else
3629 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003630
3631 /* check for multicast cipher suite */
3632 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3633 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003634 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003635 goto exit;
3636 }
3637
3638 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3639 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003640 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003641 goto exit;
3642 }
3643 offset += TLV_OUI_LEN;
3644
3645 /* pick up multicast cipher */
3646 switch (data[offset]) {
3647 case WPA_CIPHER_NONE:
3648 gval = 0;
3649 break;
3650 case WPA_CIPHER_WEP_40:
3651 case WPA_CIPHER_WEP_104:
3652 gval = WEP_ENABLED;
3653 break;
3654 case WPA_CIPHER_TKIP:
3655 gval = TKIP_ENABLED;
3656 break;
3657 case WPA_CIPHER_AES_CCM:
3658 gval = AES_ENABLED;
3659 break;
3660 default:
3661 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003662 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003663 goto exit;
3664 }
3665
3666 offset++;
3667 /* walk thru unicast cipher list and pick up what we recognize */
3668 count = data[offset] + (data[offset + 1] << 8);
3669 offset += WPA_IE_SUITE_COUNT_LEN;
3670 /* Check for unicast suite(s) */
3671 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3672 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003673 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003674 goto exit;
3675 }
3676 for (i = 0; i < count; i++) {
3677 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3678 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003679 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003680 goto exit;
3681 }
3682 offset += TLV_OUI_LEN;
3683 switch (data[offset]) {
3684 case WPA_CIPHER_NONE:
3685 break;
3686 case WPA_CIPHER_WEP_40:
3687 case WPA_CIPHER_WEP_104:
3688 pval |= WEP_ENABLED;
3689 break;
3690 case WPA_CIPHER_TKIP:
3691 pval |= TKIP_ENABLED;
3692 break;
3693 case WPA_CIPHER_AES_CCM:
3694 pval |= AES_ENABLED;
3695 break;
3696 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003697 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003698 }
3699 offset++;
3700 }
3701 /* walk thru auth management suite list and pick up what we recognize */
3702 count = data[offset] + (data[offset + 1] << 8);
3703 offset += WPA_IE_SUITE_COUNT_LEN;
3704 /* Check for auth key management suite(s) */
3705 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3706 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003707 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003708 goto exit;
3709 }
3710 for (i = 0; i < count; i++) {
3711 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3712 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003713 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003714 goto exit;
3715 }
3716 offset += TLV_OUI_LEN;
3717 switch (data[offset]) {
3718 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003719 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003720 wpa_auth |= WPA_AUTH_NONE;
3721 break;
3722 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003723 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003724 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3725 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3726 break;
3727 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003728 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003729 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3730 (wpa_auth |= WPA_AUTH_PSK);
3731 break;
3732 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003733 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003734 }
3735 offset++;
3736 }
3737
3738 if (is_rsn_ie) {
3739 wme_bss_disable = 1;
3740 if ((offset + RSN_CAP_LEN) <= len) {
3741 rsn_cap = data[offset] + (data[offset + 1] << 8);
3742 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3743 wme_bss_disable = 0;
3744 }
3745 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003746 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003747 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003748 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003749 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003750 goto exit;
3751 }
3752 }
3753 /* FOR WPS , set SES_OW_ENABLED */
3754 wsec = (pval | gval | SES_OW_ENABLED);
3755
3756 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003757 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003758 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003759 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003760 goto exit;
3761 }
3762 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003763 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003764 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003765 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003766 goto exit;
3767 }
3768 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003769 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003770 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003771 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003772 goto exit;
3773 }
3774
3775exit:
3776 return err;
3777}
3778
3779static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003780brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003781 struct parsed_vndr_ies *vndr_ies)
3782{
Hante Meuleman1a873342012-09-27 14:17:54 +02003783 struct brcmf_vs_tlv *vndrie;
3784 struct brcmf_tlv *ie;
3785 struct parsed_vndr_ie_info *parsed_info;
3786 s32 remaining_len;
3787
3788 remaining_len = (s32)vndr_ie_len;
3789 memset(vndr_ies, 0, sizeof(*vndr_ies));
3790
3791 ie = (struct brcmf_tlv *)vndr_ie_buf;
3792 while (ie) {
3793 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3794 goto next;
3795 vndrie = (struct brcmf_vs_tlv *)ie;
3796 /* len should be bigger than OUI length + one */
3797 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003798 brcmf_err("invalid vndr ie. length is too small %d\n",
3799 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003800 goto next;
3801 }
3802 /* if wpa or wme ie, do not add ie */
3803 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3804 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3805 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003806 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003807 goto next;
3808 }
3809
3810 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3811
3812 /* save vndr ie information */
3813 parsed_info->ie_ptr = (char *)vndrie;
3814 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3815 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3816
3817 vndr_ies->count++;
3818
Arend van Sprield96b8012012-12-05 15:26:02 +01003819 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3820 parsed_info->vndrie.oui[0],
3821 parsed_info->vndrie.oui[1],
3822 parsed_info->vndrie.oui[2],
3823 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003824
Arend van Spriel9f440b72013-02-08 15:53:36 +01003825 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003826 break;
3827next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003828 remaining_len -= (ie->len + TLV_HDR_LEN);
3829 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003830 ie = NULL;
3831 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003832 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3833 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003834 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003835 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003836}
3837
3838static u32
3839brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3840{
3841
Hante Meuleman1a873342012-09-27 14:17:54 +02003842 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3843 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3844
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303845 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003846
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303847 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003848
3849 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3850
3851 return ie_len + VNDR_IE_HDR_SIZE;
3852}
3853
Arend van Spriel1332e262012-11-05 16:22:18 -08003854s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3855 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003856{
Arend van Spriel1332e262012-11-05 16:22:18 -08003857 struct brcmf_if *ifp;
3858 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003859 s32 err = 0;
3860 u8 *iovar_ie_buf;
3861 u8 *curr_ie_buf;
3862 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003863 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003864 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003865 u32 del_add_ie_buf_len = 0;
3866 u32 total_ie_buf_len = 0;
3867 u32 parsed_ie_buf_len = 0;
3868 struct parsed_vndr_ies old_vndr_ies;
3869 struct parsed_vndr_ies new_vndr_ies;
3870 struct parsed_vndr_ie_info *vndrie_info;
3871 s32 i;
3872 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003873 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003874
Arend van Spriel1332e262012-11-05 16:22:18 -08003875 if (!vif)
3876 return -ENODEV;
3877 ifp = vif->ifp;
3878 saved_ie = &vif->saved_ie;
3879
Arend van Sprield96b8012012-12-05 15:26:02 +01003880 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003881 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3882 if (!iovar_ie_buf)
3883 return -ENOMEM;
3884 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003885 switch (pktflag) {
3886 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3887 mgmt_ie_buf = saved_ie->probe_req_ie;
3888 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3889 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3890 break;
3891 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3892 mgmt_ie_buf = saved_ie->probe_res_ie;
3893 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3894 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3895 break;
3896 case BRCMF_VNDR_IE_BEACON_FLAG:
3897 mgmt_ie_buf = saved_ie->beacon_ie;
3898 mgmt_ie_len = &saved_ie->beacon_ie_len;
3899 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3900 break;
3901 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3902 mgmt_ie_buf = saved_ie->assoc_req_ie;
3903 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3904 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3905 break;
3906 default:
3907 err = -EPERM;
3908 brcmf_err("not suitable type\n");
3909 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003910 }
3911
3912 if (vndr_ie_len > mgmt_ie_buf_len) {
3913 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003914 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003915 goto exit;
3916 }
3917
3918 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3919 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3920 ptr = curr_ie_buf;
3921 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3922 for (i = 0; i < new_vndr_ies.count; i++) {
3923 vndrie_info = &new_vndr_ies.ie_info[i];
3924 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3925 vndrie_info->ie_len);
3926 parsed_ie_buf_len += vndrie_info->ie_len;
3927 }
3928 }
3929
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003930 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003931 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3932 (memcmp(mgmt_ie_buf, curr_ie_buf,
3933 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003934 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003935 goto exit;
3936 }
3937
3938 /* parse old vndr_ie */
3939 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3940
3941 /* make a command to delete old ie */
3942 for (i = 0; i < old_vndr_ies.count; i++) {
3943 vndrie_info = &old_vndr_ies.ie_info[i];
3944
Arend van Sprield96b8012012-12-05 15:26:02 +01003945 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3946 vndrie_info->vndrie.id,
3947 vndrie_info->vndrie.len,
3948 vndrie_info->vndrie.oui[0],
3949 vndrie_info->vndrie.oui[1],
3950 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003951
3952 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3953 vndrie_info->ie_ptr,
3954 vndrie_info->ie_len,
3955 "del");
3956 curr_ie_buf += del_add_ie_buf_len;
3957 total_ie_buf_len += del_add_ie_buf_len;
3958 }
3959 }
3960
3961 *mgmt_ie_len = 0;
3962 /* Add if there is any extra IE */
3963 if (mgmt_ie_buf && parsed_ie_buf_len) {
3964 ptr = mgmt_ie_buf;
3965
3966 remained_buf_len = mgmt_ie_buf_len;
3967
3968 /* make a command to add new ie */
3969 for (i = 0; i < new_vndr_ies.count; i++) {
3970 vndrie_info = &new_vndr_ies.ie_info[i];
3971
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003972 /* verify remained buf size before copy data */
3973 if (remained_buf_len < (vndrie_info->vndrie.len +
3974 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003975 brcmf_err("no space in mgmt_ie_buf: len left %d",
3976 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003977 break;
3978 }
3979 remained_buf_len -= (vndrie_info->ie_len +
3980 VNDR_IE_VSIE_OFFSET);
3981
Arend van Sprield96b8012012-12-05 15:26:02 +01003982 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3983 vndrie_info->vndrie.id,
3984 vndrie_info->vndrie.len,
3985 vndrie_info->vndrie.oui[0],
3986 vndrie_info->vndrie.oui[1],
3987 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003988
3989 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3990 vndrie_info->ie_ptr,
3991 vndrie_info->ie_len,
3992 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003993
3994 /* save the parsed IE in wl struct */
3995 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3996 vndrie_info->ie_len);
3997 *mgmt_ie_len += vndrie_info->ie_len;
3998
3999 curr_ie_buf += del_add_ie_buf_len;
4000 total_ie_buf_len += del_add_ie_buf_len;
4001 }
4002 }
4003 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004004 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004005 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004006 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004007 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004008 }
4009
4010exit:
4011 kfree(iovar_ie_buf);
4012 return err;
4013}
4014
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004015s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4016{
4017 s32 pktflags[] = {
4018 BRCMF_VNDR_IE_PRBREQ_FLAG,
4019 BRCMF_VNDR_IE_PRBRSP_FLAG,
4020 BRCMF_VNDR_IE_BEACON_FLAG
4021 };
4022 int i;
4023
4024 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4025 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4026
4027 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4028 return 0;
4029}
4030
Hante Meuleman1a873342012-09-27 14:17:54 +02004031static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004032brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4033 struct cfg80211_beacon_data *beacon)
4034{
4035 s32 err;
4036
4037 /* Set Beacon IEs to FW */
4038 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4039 beacon->tail, beacon->tail_len);
4040 if (err) {
4041 brcmf_err("Set Beacon IE Failed\n");
4042 return err;
4043 }
4044 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4045
4046 /* Set Probe Response IEs to FW */
4047 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4048 beacon->proberesp_ies,
4049 beacon->proberesp_ies_len);
4050 if (err)
4051 brcmf_err("Set Probe Resp IE Failed\n");
4052 else
4053 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4054
4055 return err;
4056}
4057
4058static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004059brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4060 struct cfg80211_ap_settings *settings)
4061{
4062 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004063 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004064 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004065 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004066 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004067 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004068 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004069 const struct brcmf_tlv *rsn_ie;
4070 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004071 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004072 enum nl80211_iftype dev_role;
4073 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004074 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004075 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004076 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004077
Arend van Spriel06c01582014-05-12 10:47:37 +02004078 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4079 settings->chandef.chan->hw_value,
4080 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004081 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004082 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4083 settings->ssid, settings->ssid_len, settings->auth_type,
4084 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004085 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004086 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004087
Arend van Spriel98027762014-12-21 12:43:53 +01004088 /* store current 11d setting */
4089 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4090 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4091 settings->beacon.tail_len,
4092 WLAN_EID_COUNTRY);
4093 is_11d = country_ie ? 1 : 0;
4094
Hante Meuleman1a873342012-09-27 14:17:54 +02004095 memset(&ssid_le, 0, sizeof(ssid_le));
4096 if (settings->ssid == NULL || settings->ssid_len == 0) {
4097 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4098 ssid_ie = brcmf_parse_tlvs(
4099 (u8 *)&settings->beacon.head[ie_offset],
4100 settings->beacon.head_len - ie_offset,
4101 WLAN_EID_SSID);
4102 if (!ssid_ie)
4103 return -EINVAL;
4104
4105 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4106 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004107 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004108 } else {
4109 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4110 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4111 }
4112
Hante Meulemana44aa402014-12-03 21:05:33 +01004113 if (!mbss) {
4114 brcmf_set_mpc(ifp, 0);
4115 brcmf_configure_arp_offload(ifp, false);
4116 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004117
4118 /* find the RSN_IE */
4119 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4120 settings->beacon.tail_len, WLAN_EID_RSN);
4121
4122 /* find the WPA_IE */
4123 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4124 settings->beacon.tail_len);
4125
Hante Meuleman1a873342012-09-27 14:17:54 +02004126 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004127 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004128 if (wpa_ie != NULL) {
4129 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004130 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004131 if (err < 0)
4132 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004133 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004134 struct brcmf_vs_tlv *tmp_ie;
4135
4136 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4137
Hante Meuleman1a873342012-09-27 14:17:54 +02004138 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004139 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004140 if (err < 0)
4141 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004142 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004143 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004144 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004145 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004146 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004147
Hante Meulemana0f07952013-02-08 15:53:47 +01004148 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004149
Hante Meulemana44aa402014-12-03 21:05:33 +01004150 if (!mbss) {
4151 chanspec = chandef_to_chanspec(&cfg->d11inf,
4152 &settings->chandef);
4153 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004154 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004155 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4156 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004157 goto exit;
4158 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004159
Arend van Spriel98027762014-12-21 12:43:53 +01004160 if (is_11d != ifp->vif->is_11d) {
4161 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4162 is_11d);
4163 if (err < 0) {
4164 brcmf_err("Regulatory Set Error, %d\n", err);
4165 goto exit;
4166 }
4167 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004168 if (settings->beacon_interval) {
4169 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4170 settings->beacon_interval);
4171 if (err < 0) {
4172 brcmf_err("Beacon Interval Set Error, %d\n",
4173 err);
4174 goto exit;
4175 }
4176 }
4177 if (settings->dtim_period) {
4178 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4179 settings->dtim_period);
4180 if (err < 0) {
4181 brcmf_err("DTIM Interval Set Error, %d\n", err);
4182 goto exit;
4183 }
4184 }
4185
Hante Meuleman8abffd82015-10-29 20:33:16 +01004186 if ((dev_role == NL80211_IFTYPE_AP) &&
4187 ((ifp->ifidx == 0) ||
4188 !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004189 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4190 if (err < 0) {
4191 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4192 goto exit;
4193 }
4194 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4195 }
4196
4197 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004198 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004199 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004200 goto exit;
4201 }
Arend van Spriel98027762014-12-21 12:43:53 +01004202 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4203 /* Multiple-BSS should use same 11d configuration */
4204 err = -EINVAL;
4205 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004206 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004207 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004208 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4209 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4210
Hante Meulemana0f07952013-02-08 15:53:47 +01004211 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4212 if (err < 0) {
4213 brcmf_err("setting AP mode failed %d\n", err);
4214 goto exit;
4215 }
4216 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4217 if (err < 0) {
4218 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4219 goto exit;
4220 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004221 /* On DOWN the firmware removes the WEP keys, reconfigure
4222 * them if they were set.
4223 */
4224 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004225
4226 memset(&join_params, 0, sizeof(join_params));
4227 /* join parameters starts with ssid */
4228 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4229 /* create softap */
4230 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4231 &join_params, sizeof(join_params));
4232 if (err < 0) {
4233 brcmf_err("SET SSID error (%d)\n", err);
4234 goto exit;
4235 }
4236 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4237 } else {
4238 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4239 sizeof(ssid_le));
4240 if (err < 0) {
4241 brcmf_err("setting ssid failed %d\n", err);
4242 goto exit;
4243 }
4244 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4245 bss_enable.enable = cpu_to_le32(1);
4246 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4247 sizeof(bss_enable));
4248 if (err < 0) {
4249 brcmf_err("bss_enable config failed %d\n", err);
4250 goto exit;
4251 }
4252
4253 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4254 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004255 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004256 brcmf_net_setcarrier(ifp, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004257
4258exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004259 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004260 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004261 brcmf_configure_arp_offload(ifp, true);
4262 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004263 return err;
4264}
4265
4266static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4267{
Arend van Sprielc1179032012-10-22 13:55:33 -07004268 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004269 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004270 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004271 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004272
Arend van Sprield96b8012012-12-05 15:26:02 +01004273 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004274
Hante Meuleman426d0a52013-02-08 15:53:53 +01004275 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004276 /* Due to most likely deauths outstanding we sleep */
4277 /* first to make sure they get processed by fw. */
4278 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004279
Hante Meulemana44aa402014-12-03 21:05:33 +01004280 if (ifp->vif->mbss) {
4281 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4282 return err;
4283 }
4284
Hante Meuleman5c33a942013-04-02 21:06:18 +02004285 memset(&join_params, 0, sizeof(join_params));
4286 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4287 &join_params, sizeof(join_params));
4288 if (err < 0)
4289 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004290 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004291 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004292 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004293 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4294 if (err < 0)
4295 brcmf_err("setting AP mode failed %d\n", err);
4296 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4297 if (err < 0)
4298 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004299 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4300 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004301 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4302 ifp->vif->is_11d);
4303 if (err < 0)
4304 brcmf_err("restoring REGULATORY setting failed %d\n",
4305 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004306 /* Bring device back up so it can be used again */
4307 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4308 if (err < 0)
4309 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004310 } else {
4311 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4312 bss_enable.enable = cpu_to_le32(0);
4313 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4314 sizeof(bss_enable));
4315 if (err < 0)
4316 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004317 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004318 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004319 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004320 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004321 brcmf_net_setcarrier(ifp, false);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004322
Hante Meuleman1a873342012-09-27 14:17:54 +02004323 return err;
4324}
4325
Hante Meulemana0f07952013-02-08 15:53:47 +01004326static s32
4327brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4328 struct cfg80211_beacon_data *info)
4329{
Hante Meulemana0f07952013-02-08 15:53:47 +01004330 struct brcmf_if *ifp = netdev_priv(ndev);
4331 s32 err;
4332
4333 brcmf_dbg(TRACE, "Enter\n");
4334
Hante Meulemana0f07952013-02-08 15:53:47 +01004335 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4336
4337 return err;
4338}
4339
Hante Meuleman1a873342012-09-27 14:17:54 +02004340static int
4341brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004342 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004343{
Hante Meulemana0f07952013-02-08 15:53:47 +01004344 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004345 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004346 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004347 s32 err;
4348
Jouni Malinen89c771e2014-10-10 20:52:40 +03004349 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004350 return -EFAULT;
4351
Jouni Malinen89c771e2014-10-10 20:52:40 +03004352 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004353
Hante Meulemana0f07952013-02-08 15:53:47 +01004354 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4355 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004356 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004357 return -EIO;
4358
Jouni Malinen89c771e2014-10-10 20:52:40 +03004359 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004360 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004361 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004362 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004363 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004364 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004365
Arend van Sprield96b8012012-12-05 15:26:02 +01004366 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004367 return err;
4368}
4369
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004370static int
4371brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4372 const u8 *mac, struct station_parameters *params)
4373{
4374 struct brcmf_if *ifp = netdev_priv(ndev);
4375 s32 err;
4376
4377 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4378 params->sta_flags_mask, params->sta_flags_set);
4379
4380 /* Ignore all 00 MAC */
4381 if (is_zero_ether_addr(mac))
4382 return 0;
4383
4384 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4385 return 0;
4386
4387 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4388 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4389 (void *)mac, ETH_ALEN);
4390 else
4391 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4392 (void *)mac, ETH_ALEN);
4393 if (err < 0)
4394 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4395
4396 return err;
4397}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004398
4399static void
4400brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4401 struct wireless_dev *wdev,
4402 u16 frame_type, bool reg)
4403{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004404 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004405 u16 mgmt_type;
4406
4407 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4408
4409 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004410 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004411 if (reg)
4412 vif->mgmt_rx_reg |= BIT(mgmt_type);
4413 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004414 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004415}
4416
4417
4418static int
4419brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004420 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004421{
4422 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004423 struct ieee80211_channel *chan = params->chan;
4424 const u8 *buf = params->buf;
4425 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004426 const struct ieee80211_mgmt *mgmt;
4427 struct brcmf_cfg80211_vif *vif;
4428 s32 err = 0;
4429 s32 ie_offset;
4430 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004431 struct brcmf_fil_action_frame_le *action_frame;
4432 struct brcmf_fil_af_params_le *af_params;
4433 bool ack;
4434 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004435 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004436
4437 brcmf_dbg(TRACE, "Enter\n");
4438
4439 *cookie = 0;
4440
4441 mgmt = (const struct ieee80211_mgmt *)buf;
4442
Hante Meulemana0f07952013-02-08 15:53:47 +01004443 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4444 brcmf_err("Driver only allows MGMT packet type\n");
4445 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004446 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004447
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004448 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4449
Hante Meulemana0f07952013-02-08 15:53:47 +01004450 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4451 /* Right now the only reason to get a probe response */
4452 /* is for p2p listen response or for p2p GO from */
4453 /* wpa_supplicant. Unfortunately the probe is send */
4454 /* on primary ndev, while dongle wants it on the p2p */
4455 /* vif. Since this is only reason for a probe */
4456 /* response to be sent, the vif is taken from cfg. */
4457 /* If ever desired to send proberesp for non p2p */
4458 /* response then data should be checked for */
4459 /* "DIRECT-". Note in future supplicant will take */
4460 /* dedicated p2p wdev to do this and then this 'hack'*/
4461 /* is not needed anymore. */
4462 ie_offset = DOT11_MGMT_HDR_LEN +
4463 DOT11_BCN_PRB_FIXED_LEN;
4464 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004465 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4466 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4467 err = brcmf_vif_set_mgmt_ie(vif,
4468 BRCMF_VNDR_IE_PRBRSP_FLAG,
4469 &buf[ie_offset],
4470 ie_len);
4471 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4472 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004473 } else if (ieee80211_is_action(mgmt->frame_control)) {
4474 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4475 if (af_params == NULL) {
4476 brcmf_err("unable to allocate frame\n");
4477 err = -ENOMEM;
4478 goto exit;
4479 }
4480 action_frame = &af_params->action_frame;
4481 /* Add the packet Id */
4482 action_frame->packet_id = cpu_to_le32(*cookie);
4483 /* Add BSSID */
4484 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4485 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4486 /* Add the length exepted for 802.11 header */
4487 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004488 /* Add the channel. Use the one specified as parameter if any or
4489 * the current one (got from the firmware) otherwise
4490 */
4491 if (chan)
4492 freq = chan->center_freq;
4493 else
4494 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4495 &freq);
4496 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004497 af_params->channel = cpu_to_le32(chan_nr);
4498
4499 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4500 le16_to_cpu(action_frame->len));
4501
4502 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004503 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004504
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004505 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004506 af_params);
4507
4508 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4509 GFP_KERNEL);
4510 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004511 } else {
4512 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4513 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4514 }
4515
Hante Meuleman18e2f612013-02-08 15:53:49 +01004516exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004517 return err;
4518}
4519
4520
4521static int
4522brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4523 struct wireless_dev *wdev,
4524 u64 cookie)
4525{
4526 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4527 struct brcmf_cfg80211_vif *vif;
4528 int err = 0;
4529
4530 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4531
4532 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4533 if (vif == NULL) {
4534 brcmf_err("No p2p device available for probe response\n");
4535 err = -ENODEV;
4536 goto exit;
4537 }
4538 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4539exit:
4540 return err;
4541}
4542
Piotr Haber61730d42013-04-23 12:53:12 +02004543static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4544 struct wireless_dev *wdev,
4545 enum nl80211_crit_proto_id proto,
4546 u16 duration)
4547{
4548 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4549 struct brcmf_cfg80211_vif *vif;
4550
4551 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4552
4553 /* only DHCP support for now */
4554 if (proto != NL80211_CRIT_PROTO_DHCP)
4555 return -EINVAL;
4556
4557 /* suppress and abort scanning */
4558 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4559 brcmf_abort_scanning(cfg);
4560
4561 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4562}
4563
4564static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4565 struct wireless_dev *wdev)
4566{
4567 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4568 struct brcmf_cfg80211_vif *vif;
4569
4570 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4571
4572 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4573 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4574}
4575
Hante Meuleman70b7d942014-07-30 13:20:07 +02004576static s32
4577brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4578 const struct brcmf_event_msg *e, void *data)
4579{
4580 switch (e->reason) {
4581 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4582 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4583 break;
4584 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4585 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4586 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4587 break;
4588 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4589 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4590 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4591 break;
4592 }
4593
4594 return 0;
4595}
4596
Arend van Spriel89c2f382013-08-10 12:27:25 +02004597static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4598{
4599 int ret;
4600
4601 switch (oper) {
4602 case NL80211_TDLS_DISCOVERY_REQ:
4603 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4604 break;
4605 case NL80211_TDLS_SETUP:
4606 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4607 break;
4608 case NL80211_TDLS_TEARDOWN:
4609 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4610 break;
4611 default:
4612 brcmf_err("unsupported operation: %d\n", oper);
4613 ret = -EOPNOTSUPP;
4614 }
4615 return ret;
4616}
4617
4618static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004619 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004620 enum nl80211_tdls_operation oper)
4621{
4622 struct brcmf_if *ifp;
4623 struct brcmf_tdls_iovar_le info;
4624 int ret = 0;
4625
4626 ret = brcmf_convert_nl80211_tdls_oper(oper);
4627 if (ret < 0)
4628 return ret;
4629
4630 ifp = netdev_priv(ndev);
4631 memset(&info, 0, sizeof(info));
4632 info.mode = (u8)ret;
4633 if (peer)
4634 memcpy(info.ea, peer, ETH_ALEN);
4635
4636 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4637 &info, sizeof(info));
4638 if (ret < 0)
4639 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4640
4641 return ret;
4642}
4643
Arend van Spriel5b435de2011-10-05 13:19:03 +02004644static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004645 .add_virtual_intf = brcmf_cfg80211_add_iface,
4646 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004647 .change_virtual_intf = brcmf_cfg80211_change_iface,
4648 .scan = brcmf_cfg80211_scan,
4649 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4650 .join_ibss = brcmf_cfg80211_join_ibss,
4651 .leave_ibss = brcmf_cfg80211_leave_ibss,
4652 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02004653 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004654 .set_tx_power = brcmf_cfg80211_set_tx_power,
4655 .get_tx_power = brcmf_cfg80211_get_tx_power,
4656 .add_key = brcmf_cfg80211_add_key,
4657 .del_key = brcmf_cfg80211_del_key,
4658 .get_key = brcmf_cfg80211_get_key,
4659 .set_default_key = brcmf_cfg80211_config_default_key,
4660 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4661 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004662 .connect = brcmf_cfg80211_connect,
4663 .disconnect = brcmf_cfg80211_disconnect,
4664 .suspend = brcmf_cfg80211_suspend,
4665 .resume = brcmf_cfg80211_resume,
4666 .set_pmksa = brcmf_cfg80211_set_pmksa,
4667 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004668 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004669 .start_ap = brcmf_cfg80211_start_ap,
4670 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004671 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004672 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004673 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004674 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4675 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004676 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4677 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4678 .remain_on_channel = brcmf_p2p_remain_on_channel,
4679 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004680 .start_p2p_device = brcmf_p2p_start_device,
4681 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004682 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4683 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004684 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004685};
4686
Arend van Spriel3eacf862012-10-22 13:55:30 -07004687struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004688 enum nl80211_iftype type,
4689 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004690{
Hante Meulemana44aa402014-12-03 21:05:33 +01004691 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004692 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004693 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004694
Arend van Spriel33a6b152013-02-08 15:53:39 +01004695 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004696 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004697 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4698 if (!vif)
4699 return ERR_PTR(-ENOMEM);
4700
4701 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004702 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004703
Arend van Spriel3eacf862012-10-22 13:55:30 -07004704 vif->pm_block = pm_block;
4705 vif->roam_off = -1;
4706
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004707 brcmf_init_prof(&vif->profile);
4708
Hante Meulemana44aa402014-12-03 21:05:33 +01004709 if (type == NL80211_IFTYPE_AP) {
4710 mbss = false;
4711 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4712 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4713 mbss = true;
4714 break;
4715 }
4716 }
4717 vif->mbss = mbss;
4718 }
4719
Arend van Spriel3eacf862012-10-22 13:55:30 -07004720 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004721 return vif;
4722}
4723
Arend van Spriel427dec52014-01-06 12:40:47 +01004724void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004725{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004726 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004727 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004728}
4729
Arend van Spriel9df4d542014-01-06 12:40:49 +01004730void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4731{
4732 struct brcmf_cfg80211_vif *vif;
4733 struct brcmf_if *ifp;
4734
4735 ifp = netdev_priv(ndev);
4736 vif = ifp->vif;
4737
Arend van Spriel95ef1232015-08-26 22:15:04 +02004738 if (vif)
4739 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01004740 free_netdev(ndev);
4741}
4742
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004743static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004744{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004745 u32 event = e->event_code;
4746 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004747
4748 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004749 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004750 return true;
4751 }
4752
4753 return false;
4754}
4755
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004756static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004757{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004758 u32 event = e->event_code;
4759 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004760
Hante Meuleman68ca3952014-02-25 20:30:26 +01004761 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4762 (event == BRCMF_E_DISASSOC_IND) ||
4763 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004764 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004765 return true;
4766 }
4767 return false;
4768}
4769
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004770static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004771 const struct brcmf_event_msg *e)
4772{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004773 u32 event = e->event_code;
4774 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004775
4776 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004777 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4778 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004779 return true;
4780 }
4781
4782 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004783 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004784 return true;
4785 }
4786
4787 return false;
4788}
4789
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004790static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004791{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004792 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004793
4794 kfree(conn_info->req_ie);
4795 conn_info->req_ie = NULL;
4796 conn_info->req_ie_len = 0;
4797 kfree(conn_info->resp_ie);
4798 conn_info->resp_ie = NULL;
4799 conn_info->resp_ie_len = 0;
4800}
4801
Hante Meuleman89286dc2013-02-08 15:53:46 +01004802static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4803 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004804{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004805 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004806 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004807 u32 req_len;
4808 u32 resp_len;
4809 s32 err = 0;
4810
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004811 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004812
Arend van Sprielac24be62012-10-22 10:36:23 -07004813 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4814 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004815 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004816 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004817 return err;
4818 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004819 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004820 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004821 req_len = le32_to_cpu(assoc_info->req_len);
4822 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004823 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004824 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004825 cfg->extra_buf,
4826 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004827 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004828 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004829 return err;
4830 }
4831 conn_info->req_ie_len = req_len;
4832 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004833 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004834 GFP_KERNEL);
4835 } else {
4836 conn_info->req_ie_len = 0;
4837 conn_info->req_ie = NULL;
4838 }
4839 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004840 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004841 cfg->extra_buf,
4842 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004843 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004844 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004845 return err;
4846 }
4847 conn_info->resp_ie_len = resp_len;
4848 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004849 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004850 GFP_KERNEL);
4851 } else {
4852 conn_info->resp_ie_len = 0;
4853 conn_info->resp_ie = NULL;
4854 }
Arend van Spriel16886732012-12-05 15:26:04 +01004855 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4856 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004857
4858 return err;
4859}
4860
4861static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004862brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004863 struct net_device *ndev,
4864 const struct brcmf_event_msg *e)
4865{
Arend van Sprielc1179032012-10-22 13:55:33 -07004866 struct brcmf_if *ifp = netdev_priv(ndev);
4867 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004868 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4869 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004870 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004871 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004872 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004873 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004874 u32 freq;
4875 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004876 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004877
Arend van Sprield96b8012012-12-05 15:26:02 +01004878 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004879
Hante Meuleman89286dc2013-02-08 15:53:46 +01004880 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004881 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004882 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004883
Franky Lina180b832012-10-10 11:13:09 -07004884 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4885 if (buf == NULL) {
4886 err = -ENOMEM;
4887 goto done;
4888 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004889
Franky Lina180b832012-10-10 11:13:09 -07004890 /* data sent to dongle has to be little endian */
4891 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004892 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004893 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004894
4895 if (err)
4896 goto done;
4897
4898 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004899 ch.chspec = le16_to_cpu(bi->chanspec);
4900 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004901
Franky Lin83cf17a2013-04-11 13:28:50 +02004902 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004903 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4904 else
4905 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4906
Franky Lin83cf17a2013-04-11 13:28:50 +02004907 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004908 notify_channel = ieee80211_get_channel(wiphy, freq);
4909
Franky Lina180b832012-10-10 11:13:09 -07004910done:
4911 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004912 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004913 conn_info->req_ie, conn_info->req_ie_len,
4914 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004915 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004916
Arend van Sprielc1179032012-10-22 13:55:33 -07004917 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004918 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004919 return err;
4920}
4921
4922static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004923brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004924 struct net_device *ndev, const struct brcmf_event_msg *e,
4925 bool completed)
4926{
Arend van Sprielc1179032012-10-22 13:55:33 -07004927 struct brcmf_if *ifp = netdev_priv(ndev);
4928 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004929 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004930
Arend van Sprield96b8012012-12-05 15:26:02 +01004931 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004932
Arend van Sprielc1179032012-10-22 13:55:33 -07004933 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4934 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004935 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004936 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004937 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004938 brcmf_update_bss_info(cfg, ifp);
4939 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4940 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004941 }
4942 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004943 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004944 conn_info->req_ie,
4945 conn_info->req_ie_len,
4946 conn_info->resp_ie,
4947 conn_info->resp_ie_len,
4948 completed ? WLAN_STATUS_SUCCESS :
4949 WLAN_STATUS_AUTH_TIMEOUT,
4950 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004951 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4952 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004953 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004954 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004955 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004956}
4957
4958static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004959brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004960 struct net_device *ndev,
4961 const struct brcmf_event_msg *e, void *data)
4962{
Hante Meulemana44aa402014-12-03 21:05:33 +01004963 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01004964 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004965 u32 event = e->event_code;
4966 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004967 struct station_info sinfo;
4968
Arend van Spriel16886732012-12-05 15:26:04 +01004969 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004970 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4971 ndev != cfg_to_ndev(cfg)) {
4972 brcmf_dbg(CONN, "AP mode link down\n");
4973 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01004974 if (ifp->vif->mbss)
Arend van Sprielee6e3a32015-08-26 22:14:55 +02004975 brcmf_remove_interface(ifp);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004976 return 0;
4977 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004978
Hante Meuleman1a873342012-09-27 14:17:54 +02004979 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004980 (reason == BRCMF_E_STATUS_SUCCESS)) {
4981 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004982 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004983 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004984 return -EINVAL;
4985 }
4986 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004987 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004988 generation++;
4989 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004990 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004991 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4992 (event == BRCMF_E_DEAUTH_IND) ||
4993 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004994 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004995 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004996 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004997}
4998
4999static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005000brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005001 const struct brcmf_event_msg *e, void *data)
5002{
Arend van Spriel19937322012-11-05 16:22:32 -08005003 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5004 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005005 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005006 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005007 s32 err = 0;
5008
Hante Meuleman8851cce2014-07-30 13:20:02 +02005009 if ((e->event_code == BRCMF_E_DEAUTH) ||
5010 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5011 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5012 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5013 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5014 }
5015
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005016 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005017 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005018 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005019 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005020 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005021 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005022 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005023 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005024 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005025 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5026 &ifp->vif->sme_state);
5027 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5028 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005029 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005030 brcmf_bss_connect_done(cfg, ndev, e, true);
Hante Meuleman92121e62015-10-08 20:33:21 +02005031 brcmf_net_setcarrier(ifp, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005032 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005033 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005034 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005035 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005036 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005037 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005038 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005039 if (ndev != cfg_to_ndev(cfg))
5040 complete(&cfg->vif_disabled);
Hante Meuleman92121e62015-10-08 20:33:21 +02005041 brcmf_net_setcarrier(ifp, false);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005042 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005043 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005044 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5045 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005046 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005047 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005048 }
5049
5050 return err;
5051}
5052
5053static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005054brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005055 const struct brcmf_event_msg *e, void *data)
5056{
Arend van Spriel19937322012-11-05 16:22:32 -08005057 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005058 u32 event = e->event_code;
5059 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005060
5061 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005062 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005063 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005064 else
Arend van Spriel19937322012-11-05 16:22:32 -08005065 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005066 }
5067
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005068 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005069}
5070
5071static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005072brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005073 const struct brcmf_event_msg *e, void *data)
5074{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005075 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005076 enum nl80211_key_type key_type;
5077
5078 if (flags & BRCMF_EVENT_MSG_GROUP)
5079 key_type = NL80211_KEYTYPE_GROUP;
5080 else
5081 key_type = NL80211_KEYTYPE_PAIRWISE;
5082
Arend van Spriel19937322012-11-05 16:22:32 -08005083 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005084 NULL, GFP_KERNEL);
5085
5086 return 0;
5087}
5088
Arend van Sprield3c0b632013-02-08 15:53:37 +01005089static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5090 const struct brcmf_event_msg *e, void *data)
5091{
5092 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5093 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5094 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5095 struct brcmf_cfg80211_vif *vif;
5096
5097 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
5098 ifevent->action, ifevent->flags, ifevent->ifidx,
5099 ifevent->bssidx);
5100
Arend van Sprield3c0b632013-02-08 15:53:37 +01005101 mutex_lock(&event->vif_event_lock);
5102 event->action = ifevent->action;
5103 vif = event->vif;
5104
5105 switch (ifevent->action) {
5106 case BRCMF_E_IF_ADD:
5107 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005108 if (!cfg->vif_event.vif) {
5109 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005110 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005111 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005112
5113 ifp->vif = vif;
5114 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005115 if (ifp->ndev) {
5116 vif->wdev.netdev = ifp->ndev;
5117 ifp->ndev->ieee80211_ptr = &vif->wdev;
5118 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5119 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005120 mutex_unlock(&event->vif_event_lock);
5121 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005122 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005123
5124 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005125 mutex_unlock(&event->vif_event_lock);
5126 /* event may not be upon user request */
5127 if (brcmf_cfg80211_vif_event_armed(cfg))
5128 wake_up(&event->vif_wq);
5129 return 0;
5130
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005131 case BRCMF_E_IF_CHANGE:
5132 mutex_unlock(&event->vif_event_lock);
5133 wake_up(&event->vif_wq);
5134 return 0;
5135
Arend van Sprield3c0b632013-02-08 15:53:37 +01005136 default:
5137 mutex_unlock(&event->vif_event_lock);
5138 break;
5139 }
5140 return -EINVAL;
5141}
5142
Arend van Spriel5b435de2011-10-05 13:19:03 +02005143static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5144{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005145 conf->frag_threshold = (u32)-1;
5146 conf->rts_threshold = (u32)-1;
5147 conf->retry_short = (u32)-1;
5148 conf->retry_long = (u32)-1;
5149 conf->tx_power = -1;
5150}
5151
Arend van Spriel5c36b992012-11-14 18:46:05 -08005152static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005153{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005154 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5155 brcmf_notify_connect_status);
5156 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5157 brcmf_notify_connect_status);
5158 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5159 brcmf_notify_connect_status);
5160 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5161 brcmf_notify_connect_status);
5162 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5163 brcmf_notify_connect_status);
5164 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5165 brcmf_notify_connect_status);
5166 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5167 brcmf_notify_roaming_status);
5168 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5169 brcmf_notify_mic_status);
5170 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5171 brcmf_notify_connect_status);
5172 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5173 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005174 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5175 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005176 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005177 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005178 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5179 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005180 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5181 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005182 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5183 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005184 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5185 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005186}
5187
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005188static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005189{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005190 kfree(cfg->conf);
5191 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005192 kfree(cfg->escan_ioctl_buf);
5193 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005194 kfree(cfg->extra_buf);
5195 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005196 kfree(cfg->pmk_list);
5197 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005198}
5199
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005200static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005202 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5203 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005204 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005205 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5206 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005207 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005208 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5209 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005211 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5212 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005213 goto init_priv_mem_out;
5214
5215 return 0;
5216
5217init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005218 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005219
5220 return -ENOMEM;
5221}
5222
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005223static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005224{
5225 s32 err = 0;
5226
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005227 cfg->scan_request = NULL;
5228 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005229 cfg->active_scan = true; /* we do active scan per default */
5230 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005231 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005232 if (err)
5233 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005234 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005235 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005236 brcmf_init_escan(cfg);
5237 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005238 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005239 return err;
5240}
5241
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005242static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005243{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005244 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005245 brcmf_abort_scanning(cfg);
5246 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005247}
5248
Arend van Sprield3c0b632013-02-08 15:53:37 +01005249static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5250{
5251 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005252 mutex_init(&event->vif_event_lock);
5253}
5254
Arend van Spriel5b435de2011-10-05 13:19:03 +02005255static s32
Hante Meuleman68ca3952014-02-25 20:30:26 +01005256brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005257{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005258 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005259 __le32 roamtrigger[2];
5260 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005261
5262 /*
5263 * Setup timeout if Beacons are lost and roam is
5264 * off to report link down
5265 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005266 if (brcmf_roamoff) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005267 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005268 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005269 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005270 goto dongle_rom_out;
5271 }
5272 }
5273
5274 /*
5275 * Enable/Disable built-in roaming to allow supplicant
5276 * to take care of roaming
5277 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005278 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5279 brcmf_roamoff ? "Off" : "On");
5280 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005281 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005282 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005283 goto dongle_rom_out;
5284 }
5285
Arend van Sprielf588bc02011-10-12 20:51:22 +02005286 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5287 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005288 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005289 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005290 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005291 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005292 goto dongle_rom_out;
5293 }
5294
Arend van Sprielf588bc02011-10-12 20:51:22 +02005295 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5296 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005297 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005298 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005299 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005300 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005301 goto dongle_rom_out;
5302 }
5303
5304dongle_rom_out:
5305 return err;
5306}
5307
5308static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01005309brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005310 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005311{
5312 s32 err = 0;
5313
Arend van Sprielac24be62012-10-22 10:36:23 -07005314 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005315 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005316 if (err) {
5317 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005318 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005319 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005320 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005321 goto dongle_scantime_out;
5322 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005323 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005324 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005325 if (err) {
5326 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005327 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005328 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005329 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005330 goto dongle_scantime_out;
5331 }
5332
Arend van Sprielac24be62012-10-22 10:36:23 -07005333 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005334 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005335 if (err) {
5336 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005337 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005338 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005339 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005340 goto dongle_scantime_out;
5341 }
5342
5343dongle_scantime_out:
5344 return err;
5345}
5346
Arend van Sprielb48d8912014-07-12 08:49:41 +02005347static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5348 struct brcmu_chan *ch)
5349{
5350 u32 ht40_flag;
5351
5352 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5353 if (ch->sb == BRCMU_CHAN_SB_U) {
5354 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5355 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5356 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5357 } else {
5358 /* It should be one of
5359 * IEEE80211_CHAN_NO_HT40 or
5360 * IEEE80211_CHAN_NO_HT40PLUS
5361 */
5362 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5363 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5364 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5365 }
5366}
5367
5368static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5369 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005370{
5371 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005372 struct ieee80211_supported_band *band;
5373 struct ieee80211_channel *channel;
5374 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005375 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005376 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005377 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005378 u8 *pbuf;
5379 u32 i, j;
5380 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005381 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005382 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005383
5384 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5385
5386 if (pbuf == NULL)
5387 return -ENOMEM;
5388
5389 list = (struct brcmf_chanspec_list *)pbuf;
5390
5391 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5392 BRCMF_DCMD_MEDLEN);
5393 if (err) {
5394 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005395 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005396 }
5397
Arend van Sprielb48d8912014-07-12 08:49:41 +02005398 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005399 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5400 if (band)
5401 for (i = 0; i < band->n_channels; i++)
5402 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5403 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5404 if (band)
5405 for (i = 0; i < band->n_channels; i++)
5406 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005407
5408 total = le32_to_cpu(list->count);
5409 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005410 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5411 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005412
Franky Lin83cf17a2013-04-11 13:28:50 +02005413 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005414 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005415 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005416 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005417 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005418 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005419 continue;
5420 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005421 if (!band)
5422 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005423 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005424 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005425 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005426 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005427 ch.bw == BRCMU_CHAN_BW_80)
5428 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005429
5430 channel = band->channels;
5431 index = band->n_channels;
5432 for (j = 0; j < band->n_channels; j++) {
5433 if (channel[j].hw_value == ch.chnum) {
5434 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005435 break;
5436 }
5437 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005438 channel[index].center_freq =
5439 ieee80211_channel_to_frequency(ch.chnum, band->band);
5440 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005441
Arend van Sprielb48d8912014-07-12 08:49:41 +02005442 /* assuming the chanspecs order is HT20,
5443 * HT40 upper, HT40 lower, and VHT80.
5444 */
5445 if (ch.bw == BRCMU_CHAN_BW_80) {
5446 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5447 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5448 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5449 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005450 /* enable the channel and disable other bandwidths
5451 * for now as mentioned order assure they are enabled
5452 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005453 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005454 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5455 IEEE80211_CHAN_NO_80MHZ;
5456 ch.bw = BRCMU_CHAN_BW_20;
5457 cfg->d11inf.encchspec(&ch);
5458 chaninfo = ch.chspec;
5459 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5460 &chaninfo);
5461 if (!err) {
5462 if (chaninfo & WL_CHAN_RADAR)
5463 channel[index].flags |=
5464 (IEEE80211_CHAN_RADAR |
5465 IEEE80211_CHAN_NO_IR);
5466 if (chaninfo & WL_CHAN_PASSIVE)
5467 channel[index].flags |=
5468 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005469 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005470 }
5471 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005472
Arend van Sprielb48d8912014-07-12 08:49:41 +02005473fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005474 kfree(pbuf);
5475 return err;
5476}
5477
Arend van Sprielb48d8912014-07-12 08:49:41 +02005478static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005479{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005480 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5481 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005482 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005483 struct brcmf_chanspec_list *list;
5484 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005485 u32 val;
5486 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005487 struct brcmu_chan ch;
5488 u32 num_chan;
5489 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005490
5491 /* verify support for bw_cap command */
5492 val = WLC_BAND_5G;
5493 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5494
5495 if (!err) {
5496 /* only set 2G bandwidth using bw_cap command */
5497 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5498 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5499 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5500 sizeof(band_bwcap));
5501 } else {
5502 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5503 val = WLC_N_BW_40ALL;
5504 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5505 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005506
5507 if (!err) {
5508 /* update channel info in 2G band */
5509 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5510
5511 if (pbuf == NULL)
5512 return -ENOMEM;
5513
5514 ch.band = BRCMU_CHAN_BAND_2G;
5515 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005516 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005517 ch.chnum = 0;
5518 cfg->d11inf.encchspec(&ch);
5519
5520 /* pass encoded chanspec in query */
5521 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5522
5523 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5524 BRCMF_DCMD_MEDLEN);
5525 if (err) {
5526 brcmf_err("get chanspecs error (%d)\n", err);
5527 kfree(pbuf);
5528 return err;
5529 }
5530
5531 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5532 list = (struct brcmf_chanspec_list *)pbuf;
5533 num_chan = le32_to_cpu(list->count);
5534 for (i = 0; i < num_chan; i++) {
5535 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5536 cfg->d11inf.decchspec(&ch);
5537 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5538 continue;
5539 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5540 continue;
5541 for (j = 0; j < band->n_channels; j++) {
5542 if (band->channels[j].hw_value == ch.chnum)
5543 break;
5544 }
5545 if (WARN_ON(j == band->n_channels))
5546 continue;
5547
5548 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5549 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005550 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005551 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005552 return err;
5553}
5554
Arend van Spriel2375d972014-01-06 12:40:41 +01005555static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5556{
5557 u32 band, mimo_bwcap;
5558 int err;
5559
5560 band = WLC_BAND_2G;
5561 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5562 if (!err) {
5563 bw_cap[IEEE80211_BAND_2GHZ] = band;
5564 band = WLC_BAND_5G;
5565 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5566 if (!err) {
5567 bw_cap[IEEE80211_BAND_5GHZ] = band;
5568 return;
5569 }
5570 WARN_ON(1);
5571 return;
5572 }
5573 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5574 mimo_bwcap = 0;
5575 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5576 if (err)
5577 /* assume 20MHz if firmware does not give a clue */
5578 mimo_bwcap = WLC_N_BW_20ALL;
5579
5580 switch (mimo_bwcap) {
5581 case WLC_N_BW_40ALL:
5582 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5583 /* fall-thru */
5584 case WLC_N_BW_20IN2G_40IN5G:
5585 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5586 /* fall-thru */
5587 case WLC_N_BW_20ALL:
5588 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5589 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5590 break;
5591 default:
5592 brcmf_err("invalid mimo_bw_cap value\n");
5593 }
5594}
Hante Meulemand48200b2013-04-03 12:40:29 +02005595
Arend van Spriel18d6c532014-05-12 10:47:35 +02005596static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5597 u32 bw_cap[2], u32 nchain)
5598{
5599 band->ht_cap.ht_supported = true;
5600 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5601 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5602 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5603 }
5604 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5605 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5606 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5607 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5608 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5609 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5610}
5611
5612static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5613{
5614 u16 mcs_map;
5615 int i;
5616
5617 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5618 mcs_map = (mcs_map << 2) | supp;
5619
5620 return cpu_to_le16(mcs_map);
5621}
5622
5623static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
5624 u32 bw_cap[2], u32 nchain)
5625{
5626 __le16 mcs_map;
5627
5628 /* not allowed in 2.4G band */
5629 if (band->band == IEEE80211_BAND_2GHZ)
5630 return;
5631
5632 band->vht_cap.vht_supported = true;
5633 /* 80MHz is mandatory */
5634 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5635 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5636 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5637 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5638 }
5639 /* all support 256-QAM */
5640 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5641 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5642 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
5643}
5644
Arend van Sprielb48d8912014-07-12 08:49:41 +02005645static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005646{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005647 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005648 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005649 u32 nmode = 0;
5650 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005651 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005652 u32 rxchain;
5653 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005654 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005655 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005656 struct ieee80211_supported_band *band;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005657
Arend van Spriel18d6c532014-05-12 10:47:35 +02005658 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005659 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5660 if (err) {
5661 brcmf_err("nmode error (%d)\n", err);
5662 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005663 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005664 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005665 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5666 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5667 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005668
Daniel Kim4aca7a12014-02-25 20:30:36 +01005669 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5670 if (err) {
5671 brcmf_err("rxchain error (%d)\n", err);
5672 nchain = 1;
5673 } else {
5674 for (nchain = 0; rxchain; nchain++)
5675 rxchain = rxchain & (rxchain - 1);
5676 }
5677 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5678
Arend van Sprielb48d8912014-07-12 08:49:41 +02005679 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005680 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005681 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005682 return err;
5683 }
5684
Arend van Sprielb48d8912014-07-12 08:49:41 +02005685 wiphy = cfg_to_wiphy(cfg);
5686 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5687 band = wiphy->bands[i];
5688 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005689 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005690
Arend van Spriel18d6c532014-05-12 10:47:35 +02005691 if (nmode)
5692 brcmf_update_ht_cap(band, bw_cap, nchain);
5693 if (vhtmode)
5694 brcmf_update_vht_cap(band, bw_cap, nchain);
Hante Meulemand48200b2013-04-03 12:40:29 +02005695 }
5696
Arend van Sprielb48d8912014-07-12 08:49:41 +02005697 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005698}
5699
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005700static const struct ieee80211_txrx_stypes
5701brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5702 [NL80211_IFTYPE_STATION] = {
5703 .tx = 0xffff,
5704 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5705 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5706 },
5707 [NL80211_IFTYPE_P2P_CLIENT] = {
5708 .tx = 0xffff,
5709 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5710 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5711 },
5712 [NL80211_IFTYPE_P2P_GO] = {
5713 .tx = 0xffff,
5714 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5715 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5716 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5717 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5718 BIT(IEEE80211_STYPE_AUTH >> 4) |
5719 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5720 BIT(IEEE80211_STYPE_ACTION >> 4)
5721 },
5722 [NL80211_IFTYPE_P2P_DEVICE] = {
5723 .tx = 0xffff,
5724 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5725 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5726 }
5727};
5728
Arend van Spriel0882dda2015-08-20 22:06:03 +02005729/**
5730 * brcmf_setup_ifmodes() - determine interface modes and combinations.
5731 *
5732 * @wiphy: wiphy object.
5733 * @ifp: interface object needed for feat module api.
5734 *
5735 * The interface modes and combinations are determined dynamically here
5736 * based on firmware functionality.
5737 *
5738 * no p2p and no mbss:
5739 *
5740 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5741 *
5742 * no p2p and mbss:
5743 *
5744 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5745 * #AP <= 4, matching BI, channels = 1, 4 total
5746 *
5747 * p2p, no mchan, and mbss:
5748 *
5749 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
5750 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5751 * #AP <= 4, matching BI, channels = 1, 4 total
5752 *
5753 * p2p, mchan, and mbss:
5754 *
5755 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
5756 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5757 * #AP <= 4, matching BI, channels = 1, 4 total
5758 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005759static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5760{
5761 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02005762 struct ieee80211_iface_limit *c0_limits = NULL;
5763 struct ieee80211_iface_limit *p2p_limits = NULL;
5764 struct ieee80211_iface_limit *mbss_limits = NULL;
5765 bool mbss, p2p;
5766 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005767
Arend van Spriel0882dda2015-08-20 22:06:03 +02005768 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
5769 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
5770
5771 n_combos = 1 + !!p2p + !!mbss;
5772 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005773 if (!combo)
5774 goto err;
5775
Arend van Spriel0882dda2015-08-20 22:06:03 +02005776 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
5777 if (!c0_limits)
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005778 goto err;
5779
Arend van Spriel0882dda2015-08-20 22:06:03 +02005780 if (p2p) {
5781 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
5782 if (!p2p_limits)
5783 goto err;
5784 }
5785
5786 if (mbss) {
5787 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
5788 if (!mbss_limits)
5789 goto err;
5790 }
5791
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005792 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5793 BIT(NL80211_IFTYPE_ADHOC) |
5794 BIT(NL80211_IFTYPE_AP);
5795
Arend van Spriel0882dda2015-08-20 22:06:03 +02005796 c = 0;
5797 i = 0;
5798 combo[c].num_different_channels = 1;
5799 c0_limits[i].max = 1;
5800 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5801 if (p2p) {
5802 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5803 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005804 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5805 BIT(NL80211_IFTYPE_P2P_GO) |
5806 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02005807 c0_limits[i].max = 1;
5808 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5809 c0_limits[i].max = 1;
5810 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5811 BIT(NL80211_IFTYPE_P2P_GO);
5812 } else {
5813 c0_limits[i].max = 1;
5814 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005815 }
Arend van Spriel0882dda2015-08-20 22:06:03 +02005816 combo[c].max_interfaces = i;
5817 combo[c].n_limits = i;
5818 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005819
Arend van Spriel0882dda2015-08-20 22:06:03 +02005820 if (p2p) {
5821 c++;
5822 i = 0;
5823 combo[c].num_different_channels = 1;
5824 p2p_limits[i].max = 1;
5825 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5826 p2p_limits[i].max = 1;
5827 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
5828 p2p_limits[i].max = 1;
5829 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
5830 p2p_limits[i].max = 1;
5831 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5832 combo[c].max_interfaces = i;
5833 combo[c].n_limits = i;
5834 combo[c].limits = p2p_limits;
5835 }
5836
5837 if (mbss) {
5838 c++;
5839 combo[c].beacon_int_infra_match = true;
5840 combo[c].num_different_channels = 1;
5841 mbss_limits[0].max = 4;
5842 mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
5843 combo[c].max_interfaces = 4;
5844 combo[c].n_limits = 1;
5845 combo[c].limits = mbss_limits;
5846 }
5847 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005848 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005849 return 0;
5850
5851err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02005852 kfree(c0_limits);
5853 kfree(p2p_limits);
5854 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005855 kfree(combo);
5856 return -ENOMEM;
5857}
5858
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005859static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
5860{
5861 /* scheduled scan settings */
5862 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
5863 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
5864 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5865 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
5866}
5867
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005868#ifdef CONFIG_PM
5869static const struct wiphy_wowlan_support brcmf_wowlan_support = {
5870 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01005871 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
5872 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
5873 .pattern_min_len = 1,
5874 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005875};
5876#endif
5877
5878static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
5879{
5880#ifdef CONFIG_PM
5881 /* wowl settings */
5882 wiphy->wowlan = &brcmf_wowlan_support;
5883#endif
5884}
5885
Arend van Sprielb48d8912014-07-12 08:49:41 +02005886static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005887{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005888 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005889 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005890 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005891 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005892 __le32 bandlist[3];
5893 u32 n_bands;
5894 int err, i;
5895
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005896 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
5897 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5898 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005899
5900 err = brcmf_setup_ifmodes(wiphy, ifp);
5901 if (err)
5902 return err;
5903
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005904 for (i = 0, combo = wiphy->iface_combinations;
5905 i < wiphy->n_iface_combinations; i++, combo++) {
5906 max_interfaces = max(max_interfaces, combo->max_interfaces);
5907 }
5908
5909 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
5910 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005911 u8 *addr = drvr->addresses[i].addr;
5912
5913 memcpy(addr, drvr->mac, ETH_ALEN);
5914 if (i) {
5915 addr[0] |= BIT(1);
5916 addr[ETH_ALEN - 1] ^= i;
5917 }
5918 }
5919 wiphy->addresses = drvr->addresses;
5920 wiphy->n_addresses = i;
5921
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005922 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
5923 wiphy->cipher_suites = __wl_cipher_suites;
5924 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
5925 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
5926 WIPHY_FLAG_OFFCHAN_TX |
5927 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
5928 WIPHY_FLAG_SUPPORTS_TDLS;
5929 if (!brcmf_roamoff)
5930 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
5931 wiphy->mgmt_stypes = brcmf_txrx_stypes;
5932 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02005933 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
5934 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005935
5936 /* vendor commands/events support */
5937 wiphy->vendor_commands = brcmf_vendor_cmds;
5938 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
5939
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005940 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
5941 brcmf_wiphy_wowl_params(wiphy);
5942
Arend van Spriel58de92d2015-04-14 20:10:24 +02005943 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
5944 sizeof(bandlist));
5945 if (err) {
5946 brcmf_err("could not obtain band info: err=%d\n", err);
5947 return err;
5948 }
5949 /* first entry in bandlist is number of bands */
5950 n_bands = le32_to_cpu(bandlist[0]);
5951 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
5952 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
5953 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
5954 GFP_KERNEL);
5955 if (!band)
5956 return -ENOMEM;
5957
5958 band->channels = kmemdup(&__wl_2ghz_channels,
5959 sizeof(__wl_2ghz_channels),
5960 GFP_KERNEL);
5961 if (!band->channels) {
5962 kfree(band);
5963 return -ENOMEM;
5964 }
5965
5966 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
5967 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
5968 }
5969 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
5970 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
5971 GFP_KERNEL);
5972 if (!band)
5973 return -ENOMEM;
5974
5975 band->channels = kmemdup(&__wl_5ghz_channels,
5976 sizeof(__wl_5ghz_channels),
5977 GFP_KERNEL);
5978 if (!band->channels) {
5979 kfree(band);
5980 return -ENOMEM;
5981 }
5982
5983 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
5984 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
5985 }
5986 }
5987 err = brcmf_setup_wiphybands(wiphy);
5988 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005989}
5990
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005991static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005992{
5993 struct net_device *ndev;
5994 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005995 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005996 s32 power_mode;
5997 s32 err = 0;
5998
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005999 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006000 return err;
6001
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006002 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006003 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006004 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006005
Hante Meuleman40a23292013-01-02 15:22:51 +01006006 /* make sure RF is ready for work */
6007 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6008
6009 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
6010 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006011
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006012 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006013 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006014 if (err)
6015 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006016 brcmf_dbg(INFO, "power save set to %s\n",
6017 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006018
Hante Meuleman68ca3952014-02-25 20:30:26 +01006019 err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006020 if (err)
6021 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006022 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
6023 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006024 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006025 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006026
Hante Meulemanb3657452013-05-27 21:09:53 +02006027 brcmf_configure_arp_offload(ifp, true);
6028
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006029 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006030default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006031
6032 return err;
6033
6034}
6035
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006036static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006037{
Arend van Sprielc1179032012-10-22 13:55:33 -07006038 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006039
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006040 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006041}
6042
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006043static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006044{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006045 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006046
Arend van Spriel5b435de2011-10-05 13:19:03 +02006047 /*
6048 * While going down, if associated with AP disassociate
6049 * from AP to save power
6050 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006051 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006052 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006053
6054 /* Make sure WPA_Supplicant receives all the event
6055 generated due to DISASSOC call to the fw to keep
6056 the state fw and WPA_Supplicant state consistent
6057 */
6058 brcmf_delay(500);
6059 }
6060
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006061 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006062 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006063
Arend van Spriel5b435de2011-10-05 13:19:03 +02006064 return 0;
6065}
6066
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006067s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006068{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006069 struct brcmf_if *ifp = netdev_priv(ndev);
6070 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006071 s32 err = 0;
6072
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006073 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006074 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006075 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006076
6077 return err;
6078}
6079
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006080s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006081{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006082 struct brcmf_if *ifp = netdev_priv(ndev);
6083 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006084 s32 err = 0;
6085
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006086 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006087 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006088 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006089
6090 return err;
6091}
6092
Arend van Spriela7965fb2013-04-11 17:08:37 +02006093enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6094{
6095 struct wireless_dev *wdev = &ifp->vif->wdev;
6096
6097 return wdev->iftype;
6098}
6099
Hante Meulemanbfe81972014-10-28 14:56:16 +01006100bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6101 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006102{
6103 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006104
6105 list_for_each_entry(vif, &cfg->vif_list, list) {
6106 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006107 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006108 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006109 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006110}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006111
6112static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6113 u8 action)
6114{
6115 u8 evt_action;
6116
6117 mutex_lock(&event->vif_event_lock);
6118 evt_action = event->action;
6119 mutex_unlock(&event->vif_event_lock);
6120 return evt_action == action;
6121}
6122
6123void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6124 struct brcmf_cfg80211_vif *vif)
6125{
6126 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6127
6128 mutex_lock(&event->vif_event_lock);
6129 event->vif = vif;
6130 event->action = 0;
6131 mutex_unlock(&event->vif_event_lock);
6132}
6133
6134bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6135{
6136 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6137 bool armed;
6138
6139 mutex_lock(&event->vif_event_lock);
6140 armed = event->vif != NULL;
6141 mutex_unlock(&event->vif_event_lock);
6142
6143 return armed;
6144}
6145int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6146 u8 action, ulong timeout)
6147{
6148 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6149
6150 return wait_event_timeout(event->vif_wq,
6151 vif_event_equals(event, action), timeout);
6152}
6153
Arend van Spriel63db1a42014-12-21 12:43:51 +01006154static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6155 struct regulatory_request *req)
6156{
6157 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6158 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6159 struct brcmf_fil_country_le ccreq;
6160 int i;
6161
6162 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6163 req->alpha2[0], req->alpha2[1]);
6164
6165 /* ignore non-ISO3166 country codes */
6166 for (i = 0; i < sizeof(req->alpha2); i++)
6167 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6168 brcmf_err("not a ISO3166 code\n");
6169 return;
6170 }
6171 memset(&ccreq, 0, sizeof(ccreq));
6172 ccreq.rev = cpu_to_le32(-1);
6173 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006174 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6175 brcmf_err("firmware rejected country setting\n");
6176 return;
6177 }
6178 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006179}
6180
Arend van Sprielb48d8912014-07-12 08:49:41 +02006181static void brcmf_free_wiphy(struct wiphy *wiphy)
6182{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006183 int i;
6184
Arend van Spriel58de92d2015-04-14 20:10:24 +02006185 if (!wiphy)
6186 return;
6187
Arend van Spriel0882dda2015-08-20 22:06:03 +02006188 if (wiphy->iface_combinations) {
6189 for (i = 0; i < wiphy->n_iface_combinations; i++)
6190 kfree(wiphy->iface_combinations[i].limits);
6191 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006192 kfree(wiphy->iface_combinations);
6193 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6194 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6195 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6196 }
6197 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6198 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6199 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6200 }
6201 wiphy_free(wiphy);
6202}
6203
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006204struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006205 struct device *busdev,
6206 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006207{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006208 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006209 struct brcmf_cfg80211_info *cfg;
6210 struct wiphy *wiphy;
6211 struct brcmf_cfg80211_vif *vif;
6212 struct brcmf_if *ifp;
6213 s32 err = 0;
6214 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006215 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006216
6217 if (!ndev) {
6218 brcmf_err("ndev is invalid\n");
6219 return NULL;
6220 }
6221
6222 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006223 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6224 if (!wiphy) {
6225 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006226 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006227 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006228 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006229 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006230
6231 cfg = wiphy_priv(wiphy);
6232 cfg->wiphy = wiphy;
6233 cfg->pub = drvr;
6234 init_vif_event(&cfg->vif_event);
6235 INIT_LIST_HEAD(&cfg->vif_list);
6236
6237 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006238 if (IS_ERR(vif))
6239 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006240
6241 vif->ifp = ifp;
6242 vif->wdev.netdev = ndev;
6243 ndev->ieee80211_ptr = &vif->wdev;
6244 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6245
6246 err = wl_init_priv(cfg);
6247 if (err) {
6248 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006249 brcmf_free_vif(vif);
6250 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006251 }
6252 ifp->vif = vif;
6253
Arend van Sprielb48d8912014-07-12 08:49:41 +02006254 /* determine d11 io type before wiphy setup */
6255 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006256 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006257 brcmf_err("Failed to get D11 version (%d)\n", err);
6258 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006259 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006260 cfg->d11inf.io_type = (u8)io_type;
6261 brcmu_d11_attach(&cfg->d11inf);
6262
6263 err = brcmf_setup_wiphy(wiphy, ifp);
6264 if (err < 0)
6265 goto priv_out;
6266
6267 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006268 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006269 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6270 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6271
6272 /* firmware defaults to 40MHz disabled in 2G band. We signal
6273 * cfg80211 here that we do and have it decide we can enable
6274 * it. But first check if device does support 2G operation.
6275 */
6276 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6277 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6278 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6279 }
6280 err = wiphy_register(wiphy);
6281 if (err < 0) {
6282 brcmf_err("Could not register wiphy device (%d)\n", err);
6283 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006284 }
6285
6286 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6287 * setup 40MHz in 2GHz band and enable OBSS scanning.
6288 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006289 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6290 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006291 if (!err)
6292 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6293 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006294 else
6295 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006296 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006297 /* p2p might require that "if-events" get processed by fweh. So
6298 * activate the already registered event handlers now and activate
6299 * the rest when initialization has completed. drvr->config needs to
6300 * be assigned before activating events.
6301 */
6302 drvr->config = cfg;
6303 err = brcmf_fweh_activate_events(ifp);
6304 if (err) {
6305 brcmf_err("FWEH activation failed (%d)\n", err);
6306 goto wiphy_unreg_out;
6307 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006308
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006309 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006310 if (err) {
6311 brcmf_err("P2P initilisation failed (%d)\n", err);
6312 goto wiphy_unreg_out;
6313 }
6314 err = brcmf_btcoex_attach(cfg);
6315 if (err) {
6316 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6317 brcmf_p2p_detach(&cfg->p2p);
6318 goto wiphy_unreg_out;
6319 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006320
6321 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6322 if (err) {
6323 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6324 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman70b7d942014-07-30 13:20:07 +02006325 } else {
6326 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6327 brcmf_notify_tdls_peer_event);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006328 }
6329
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006330 /* (re-) activate FWEH event handling */
6331 err = brcmf_fweh_activate_events(ifp);
6332 if (err) {
6333 brcmf_err("FWEH activation failed (%d)\n", err);
6334 goto wiphy_unreg_out;
6335 }
6336
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006337 return cfg;
6338
Arend van Sprielb48d8912014-07-12 08:49:41 +02006339wiphy_unreg_out:
6340 wiphy_unregister(cfg->wiphy);
6341priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006342 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006343 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006344 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006345wiphy_out:
6346 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006347 return NULL;
6348}
6349
6350void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6351{
6352 if (!cfg)
6353 return;
6354
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006355 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006356 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006357 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006358 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006359}