blob: 6a7759fcbd86d76794287b6ae018ebeefd69b6ac [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
18
19#include <linux/kernel.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <linux/etherdevice.h>
Hante Meuleman68ca3952014-02-25 20:30:26 +010021#include <linux/module.h>
Franky Lin1bacb042014-06-21 12:11:16 +020022#include <linux/vmalloc.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020024#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020025
26#include <brcmu_utils.h>
27#include <defs.h>
28#include <brcmu_wifi.h>
Hante Meuleman122d3d02014-10-28 14:56:18 +010029#include "core.h"
Hante Meulemana8e8ed32014-10-28 14:56:13 +010030#include "debug.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020031#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010032#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010033#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020034#include "btcoex.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010035#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020036#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070037#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020038#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020039#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010040#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010041#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020042
Arend van Spriele5806072012-09-19 22:21:08 +020043#define BRCMF_SCAN_IE_LEN_MAX 2048
44#define BRCMF_PNO_VERSION 2
45#define BRCMF_PNO_TIME 30
46#define BRCMF_PNO_REPEAT 4
47#define BRCMF_PNO_FREQ_EXPO_MAX 3
48#define BRCMF_PNO_MAX_PFN_COUNT 16
49#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
50#define BRCMF_PNO_HIDDEN_BIT 2
51#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
52#define BRCMF_PNO_SCAN_COMPLETE 1
53#define BRCMF_PNO_SCAN_INCOMPLETE 0
54
Hante Meuleman1a873342012-09-27 14:17:54 +020055#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010059#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020060
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
75#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
76#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
77
78#define VNDR_IE_CMD_LEN 4 /* length of the set command
79 * string :"add", "del" (+ NUL)
80 */
81#define VNDR_IE_COUNT_OFFSET 4
82#define VNDR_IE_PKTFLAG_OFFSET 8
83#define VNDR_IE_VSIE_OFFSET 12
84#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010085#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020086
87#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
88#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020089
Hante Meuleman89286dc2013-02-08 15:53:46 +010090#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
91#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
92#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
93
Hante Meuleman1678ba82015-12-10 13:43:00 +010094#define BRCMF_SCAN_CHANNEL_TIME 40
95#define BRCMF_SCAN_UNASSOC_TIME 40
96#define BRCMF_SCAN_PASSIVE_TIME 120
97
Arend van Spriel5b435de2011-10-05 13:19:03 +020098#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
99 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
100
Arend van Sprielce81e312012-10-22 13:55:37 -0700101static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200102{
Arend van Sprielc1179032012-10-22 13:55:33 -0700103 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100104 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
105 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200106 return false;
107 }
108 return true;
109}
110
Arend van Spriel5b435de2011-10-05 13:19:03 +0200111#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
112#define RATETAB_ENT(_rateid, _flags) \
113 { \
114 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
115 .hw_value = (_rateid), \
116 .flags = (_flags), \
117 }
118
119static struct ieee80211_rate __wl_rates[] = {
120 RATETAB_ENT(BRCM_RATE_1M, 0),
121 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
122 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
123 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
124 RATETAB_ENT(BRCM_RATE_6M, 0),
125 RATETAB_ENT(BRCM_RATE_9M, 0),
126 RATETAB_ENT(BRCM_RATE_12M, 0),
127 RATETAB_ENT(BRCM_RATE_18M, 0),
128 RATETAB_ENT(BRCM_RATE_24M, 0),
129 RATETAB_ENT(BRCM_RATE_36M, 0),
130 RATETAB_ENT(BRCM_RATE_48M, 0),
131 RATETAB_ENT(BRCM_RATE_54M, 0),
132};
133
Arend van Spriel5b435de2011-10-05 13:19:03 +0200134#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200135#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
136#define wl_a_rates (__wl_rates + 4)
137#define wl_a_rates_size (wl_g_rates_size - 4)
138
139#define CHAN2G(_channel, _freq) { \
140 .band = IEEE80211_BAND_2GHZ, \
141 .center_freq = (_freq), \
142 .hw_value = (_channel), \
143 .flags = IEEE80211_CHAN_DISABLED, \
144 .max_antenna_gain = 0, \
145 .max_power = 30, \
146}
147
148#define CHAN5G(_channel) { \
149 .band = IEEE80211_BAND_5GHZ, \
150 .center_freq = 5000 + (5 * (_channel)), \
151 .hw_value = (_channel), \
152 .flags = IEEE80211_CHAN_DISABLED, \
153 .max_antenna_gain = 0, \
154 .max_power = 30, \
155}
156
157static struct ieee80211_channel __wl_2ghz_channels[] = {
158 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
159 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
160 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
161 CHAN2G(13, 2472), CHAN2G(14, 2484)
162};
163
164static struct ieee80211_channel __wl_5ghz_channels[] = {
165 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
166 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
167 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
168 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
169 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
170 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
171};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200172
Arend van Sprielb48d8912014-07-12 08:49:41 +0200173/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200174 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200175 */
176static const struct ieee80211_supported_band __wl_band_2ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200177 .band = IEEE80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200178 .bitrates = wl_g_rates,
179 .n_bitrates = wl_g_rates_size,
180};
181
Arend van Spriel58de92d2015-04-14 20:10:24 +0200182static const struct ieee80211_supported_band __wl_band_5ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200183 .band = IEEE80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200184 .bitrates = wl_a_rates,
185 .n_bitrates = wl_a_rates_size,
186};
187
Hante Meulemand48200b2013-04-03 12:40:29 +0200188/* This is to override regulatory domains defined in cfg80211 module (reg.c)
189 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200190 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
191 * With respect to these flags, wpa_supplicant doesn't * start p2p
192 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200193 * domain are to be done here.
194 */
195static const struct ieee80211_regdomain brcmf_regdom = {
196 .n_reg_rules = 4,
197 .alpha2 = "99",
198 .reg_rules = {
199 /* IEEE 802.11b/g, channels 1..11 */
200 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
201 /* If any */
202 /* IEEE 802.11 channel 14 - Only JP enables
203 * this and for 802.11b only
204 */
205 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
206 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200207 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200208 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200209 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200210};
211
212static const u32 __wl_cipher_suites[] = {
213 WLAN_CIPHER_SUITE_WEP40,
214 WLAN_CIPHER_SUITE_WEP104,
215 WLAN_CIPHER_SUITE_TKIP,
216 WLAN_CIPHER_SUITE_CCMP,
217 WLAN_CIPHER_SUITE_AES_CMAC,
218};
219
Hante Meuleman1a873342012-09-27 14:17:54 +0200220/* Vendor specific ie. id = 221, oui and type defines exact ie */
221struct brcmf_vs_tlv {
222 u8 id;
223 u8 len;
224 u8 oui[3];
225 u8 oui_type;
226};
227
228struct parsed_vndr_ie_info {
229 u8 *ie_ptr;
230 u32 ie_len; /* total length including id & length field */
231 struct brcmf_vs_tlv vndrie;
232};
233
234struct parsed_vndr_ies {
235 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100236 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200237};
238
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;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100395 bool check_combos = false;
396 int ret = 0;
Arend van Spriel39504a22015-08-20 22:06:05 +0200397
398 memset(&iftype_num[0], 0, sizeof(iftype_num));
399 list_for_each_entry(pos, &cfg->vif_list, list)
Arend van Spriel353c46a2015-12-10 13:43:06 +0100400 if (pos == vif) {
Arend van Spriel39504a22015-08-20 22:06:05 +0200401 iftype_num[new_type]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100402 } else {
403 /* concurrent interfaces so need check combinations */
404 check_combos = true;
Arend van Spriel39504a22015-08-20 22:06:05 +0200405 iftype_num[pos->wdev.iftype]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100406 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200407
Arend van Spriel353c46a2015-12-10 13:43:06 +0100408 if (check_combos)
409 ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
410
411 return ret;
Arend van Spriel39504a22015-08-20 22:06:05 +0200412}
413
414static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
415 enum nl80211_iftype new_type)
416{
417 int iftype_num[NUM_NL80211_IFTYPES];
418 struct brcmf_cfg80211_vif *pos;
419
420 memset(&iftype_num[0], 0, sizeof(iftype_num));
421 list_for_each_entry(pos, &cfg->vif_list, list)
422 iftype_num[pos->wdev.iftype]++;
423
424 iftype_num[new_type]++;
425 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
426}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100427
Arend van Spriel5b435de2011-10-05 13:19:03 +0200428static void convert_key_from_CPU(struct brcmf_wsec_key *key,
429 struct brcmf_wsec_key_le *key_le)
430{
431 key_le->index = cpu_to_le32(key->index);
432 key_le->len = cpu_to_le32(key->len);
433 key_le->algo = cpu_to_le32(key->algo);
434 key_le->flags = cpu_to_le32(key->flags);
435 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
436 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
437 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
438 memcpy(key_le->data, key->data, sizeof(key->data));
439 memcpy(key_le->ea, key->ea, sizeof(key->ea));
440}
441
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200442static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100443send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200444{
445 int err;
446 struct brcmf_wsec_key_le key_le;
447
448 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200449
Hante Meuleman118eb302014-12-21 12:43:49 +0100450 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700451
Hante Meuleman118eb302014-12-21 12:43:49 +0100452 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700453 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200454
Arend van Spriel5b435de2011-10-05 13:19:03 +0200455 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100456 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200457 return err;
458}
459
Hante Meulemanb3657452013-05-27 21:09:53 +0200460static s32
461brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
462{
463 s32 err;
464 u32 mode;
465
466 if (enable)
467 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
468 else
469 mode = 0;
470
471 /* Try to set and enable ARP offload feature, this may fail, then it */
472 /* is simply not supported and err 0 will be returned */
473 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
474 if (err) {
475 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
476 mode, err);
477 err = 0;
478 } else {
479 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
480 if (err) {
481 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
482 enable, err);
483 err = 0;
484 } else
485 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
486 enable, mode);
487 }
488
489 return err;
490}
491
Hante Meuleman8851cce2014-07-30 13:20:02 +0200492static void
493brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
494{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200495 struct brcmf_cfg80211_vif *vif;
496 struct brcmf_if *ifp;
497
498 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
499 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200500
501 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
502 (wdev->iftype == NL80211_IFTYPE_AP) ||
503 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
504 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
505 ADDR_DIRECT);
506 else
507 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
508 ADDR_INDIRECT);
509}
510
Hante Meulemana44aa402014-12-03 21:05:33 +0100511static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
512{
513 struct brcmf_mbss_ssid_le mbss_ssid_le;
514 int bsscfgidx;
515 int err;
516
517 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
518 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
519 if (bsscfgidx < 0)
520 return bsscfgidx;
521
522 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
523 mbss_ssid_le.SSID_len = cpu_to_le32(5);
524 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
525
526 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
527 sizeof(mbss_ssid_le));
528 if (err < 0)
529 brcmf_err("setting ssid failed %d\n", err);
530
531 return err;
532}
533
534/**
535 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
536 *
537 * @wiphy: wiphy device of new interface.
538 * @name: name of the new interface.
539 * @flags: not used.
540 * @params: contains mac address for AP device.
541 */
542static
543struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
544 u32 *flags, struct vif_params *params)
545{
546 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
547 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
548 struct brcmf_cfg80211_vif *vif;
549 int err;
550
551 if (brcmf_cfg80211_vif_event_armed(cfg))
552 return ERR_PTR(-EBUSY);
553
554 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
555
556 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
557 if (IS_ERR(vif))
558 return (struct wireless_dev *)vif;
559
560 brcmf_cfg80211_arm_vif_event(cfg, vif);
561
562 err = brcmf_cfg80211_request_ap_if(ifp);
563 if (err) {
564 brcmf_cfg80211_arm_vif_event(cfg, NULL);
565 goto fail;
566 }
567
568 /* wait for firmware event */
569 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
570 msecs_to_jiffies(1500));
571 brcmf_cfg80211_arm_vif_event(cfg, NULL);
572 if (!err) {
573 brcmf_err("timeout occurred\n");
574 err = -EIO;
575 goto fail;
576 }
577
578 /* interface created in firmware */
579 ifp = vif->ifp;
580 if (!ifp) {
581 brcmf_err("no if pointer provided\n");
582 err = -ENOENT;
583 goto fail;
584 }
585
586 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
587 err = brcmf_net_attach(ifp, true);
588 if (err) {
589 brcmf_err("Registering netdevice failed\n");
590 goto fail;
591 }
592
593 return &ifp->vif->wdev;
594
595fail:
596 brcmf_free_vif(vif);
597 return ERR_PTR(err);
598}
599
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100600static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
601{
602 enum nl80211_iftype iftype;
603
604 iftype = vif->wdev.iftype;
605 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
606}
607
608static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
609{
610 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
611}
612
Arend van Spriel9f440b72013-02-08 15:53:36 +0100613static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
614 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100615 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100616 enum nl80211_iftype type,
617 u32 *flags,
618 struct vif_params *params)
619{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200620 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200621 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200622
Arend van Spriel9f440b72013-02-08 15:53:36 +0100623 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200624 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
625 if (err) {
626 brcmf_err("iface validation failed: err=%d\n", err);
627 return ERR_PTR(err);
628 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100629 switch (type) {
630 case NL80211_IFTYPE_ADHOC:
631 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100632 case NL80211_IFTYPE_AP_VLAN:
633 case NL80211_IFTYPE_WDS:
634 case NL80211_IFTYPE_MONITOR:
635 case NL80211_IFTYPE_MESH_POINT:
636 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100637 case NL80211_IFTYPE_AP:
638 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
639 if (!IS_ERR(wdev))
640 brcmf_cfg80211_update_proto_addr_mode(wdev);
641 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100642 case NL80211_IFTYPE_P2P_CLIENT:
643 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200644 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100645 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200646 if (!IS_ERR(wdev))
647 brcmf_cfg80211_update_proto_addr_mode(wdev);
648 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100649 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100650 default:
651 return ERR_PTR(-EINVAL);
652 }
653}
654
Daniel Kim5e787f72014-06-21 12:11:18 +0200655static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
656{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200657 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200658 brcmf_set_mpc(ifp, mpc);
659}
660
Arend van Sprielf96aa072013-04-05 10:57:48 +0200661void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100662{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100663 s32 err = 0;
664
665 if (check_vif_up(ifp->vif)) {
666 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
667 if (err) {
668 brcmf_err("fail to set mpc\n");
669 return;
670 }
671 brcmf_dbg(INFO, "MPC : %d\n", mpc);
672 }
673}
674
Arend van Spriela0f472a2013-04-05 10:57:49 +0200675s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
676 struct brcmf_if *ifp, bool aborted,
677 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100678{
679 struct brcmf_scan_params_le params_le;
680 struct cfg80211_scan_request *scan_request;
681 s32 err = 0;
682
683 brcmf_dbg(SCAN, "Enter\n");
684
685 /* clear scan request, because the FW abort can cause a second call */
686 /* to this functon and might cause a double cfg80211_scan_done */
687 scan_request = cfg->scan_request;
688 cfg->scan_request = NULL;
689
690 if (timer_pending(&cfg->escan_timeout))
691 del_timer_sync(&cfg->escan_timeout);
692
693 if (fw_abort) {
694 /* Do a scan abort to stop the driver's scan engine */
695 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
696 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800697 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100698 params_le.bss_type = DOT11_BSSTYPE_ANY;
699 params_le.scan_type = 0;
700 params_le.channel_num = cpu_to_le32(1);
701 params_le.nprobes = cpu_to_le32(1);
702 params_le.active_time = cpu_to_le32(-1);
703 params_le.passive_time = cpu_to_le32(-1);
704 params_le.home_time = cpu_to_le32(-1);
705 /* Scan is aborted by setting channel_list[0] to -1 */
706 params_le.channel_list[0] = cpu_to_le16(-1);
707 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200708 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100709 &params_le, sizeof(params_le));
710 if (err)
711 brcmf_err("Scan abort failed\n");
712 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200713
Daniel Kim5e787f72014-06-21 12:11:18 +0200714 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200715
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100716 /*
717 * e-scan can be initiated by scheduled scan
718 * which takes precedence.
719 */
720 if (cfg->sched_escan) {
721 brcmf_dbg(SCAN, "scheduled scan completed\n");
722 cfg->sched_escan = false;
723 if (!aborted)
724 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100725 } else if (scan_request) {
726 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
727 aborted ? "Aborted" : "Done");
728 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100729 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100730 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
731 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100732
733 return err;
734}
735
Arend van Spriel9f440b72013-02-08 15:53:36 +0100736static
737int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
738{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100739 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
740 struct net_device *ndev = wdev->netdev;
741
742 /* vif event pending in firmware */
743 if (brcmf_cfg80211_vif_event_armed(cfg))
744 return -EBUSY;
745
746 if (ndev) {
747 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200748 cfg->escan_info.ifp == netdev_priv(ndev))
749 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
750 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100751
752 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
753 }
754
Arend van Spriel9f440b72013-02-08 15:53:36 +0100755 switch (wdev->iftype) {
756 case NL80211_IFTYPE_ADHOC:
757 case NL80211_IFTYPE_STATION:
758 case NL80211_IFTYPE_AP:
759 case NL80211_IFTYPE_AP_VLAN:
760 case NL80211_IFTYPE_WDS:
761 case NL80211_IFTYPE_MONITOR:
762 case NL80211_IFTYPE_MESH_POINT:
763 return -EOPNOTSUPP;
764 case NL80211_IFTYPE_P2P_CLIENT:
765 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200766 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100767 return brcmf_p2p_del_vif(wiphy, wdev);
768 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100769 default:
770 return -EINVAL;
771 }
772 return -EOPNOTSUPP;
773}
774
Arend van Spriel5b435de2011-10-05 13:19:03 +0200775static s32
776brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
777 enum nl80211_iftype type, u32 *flags,
778 struct vif_params *params)
779{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100780 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700781 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100782 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200783 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200784 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200785 s32 err = 0;
786
Hante Meuleman37a869e2015-10-29 20:33:17 +0100787 brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
788 type);
Hante Meuleman178e9ef2015-09-18 22:08:11 +0200789
790 /* WAR: There are a number of p2p interface related problems which
791 * need to be handled initially (before doing the validate).
792 * wpa_supplicant tends to do iface changes on p2p device/client/go
793 * which are not always possible/allowed. However we need to return
794 * OK otherwise the wpa_supplicant wont start. The situation differs
795 * on configuration and setup (p2pon=1 module param). The first check
796 * is to see if the request is a change to station for p2p iface.
797 */
798 if ((type == NL80211_IFTYPE_STATION) &&
799 ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
800 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
801 (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
802 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
803 /* Now depending on whether module param p2pon=1 was used the
804 * response needs to be either 0 or EOPNOTSUPP. The reason is
805 * that if p2pon=1 is used, but a newer supplicant is used then
806 * we should return an error, as this combination wont work.
807 * In other situations 0 is returned and supplicant will start
808 * normally. It will give a trace in cfg80211, but it is the
809 * only way to get it working. Unfortunately this will result
810 * in situation where we wont support new supplicant in
811 * combination with module param p2pon=1, but that is the way
812 * it is. If the user tries this then unloading of driver might
813 * fail/lock.
814 */
815 if (cfg->p2p.p2pdev_dynamically)
816 return -EOPNOTSUPP;
817 else
818 return 0;
819 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200820 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
821 if (err) {
822 brcmf_err("iface validation failed: err=%d\n", err);
823 return err;
824 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200825 switch (type) {
826 case NL80211_IFTYPE_MONITOR:
827 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100828 brcmf_err("type (%d) : currently we do not support this type\n",
829 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200830 return -EOPNOTSUPP;
831 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200832 infra = 0;
833 break;
834 case NL80211_IFTYPE_STATION:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200835 infra = 1;
836 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200837 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100838 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200839 ap = 1;
840 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200841 default:
842 err = -EINVAL;
843 goto done;
844 }
845
Hante Meuleman1a873342012-09-27 14:17:54 +0200846 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100847 if (type == NL80211_IFTYPE_P2P_GO) {
848 brcmf_dbg(INFO, "IF Type = P2P GO\n");
849 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
850 }
851 if (!err) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100852 brcmf_dbg(INFO, "IF Type = AP\n");
853 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200854 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100855 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200856 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100857 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200858 err = -EAGAIN;
859 goto done;
860 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100861 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100862 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200863 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200864 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200865
Hante Meuleman8851cce2014-07-30 13:20:02 +0200866 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
867
Arend van Spriel5b435de2011-10-05 13:19:03 +0200868done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100869 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200870
871 return err;
872}
873
Franky Lin83cf17a2013-04-11 13:28:50 +0200874static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
875 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200876 struct cfg80211_scan_request *request)
877{
878 u32 n_ssids;
879 u32 n_channels;
880 s32 i;
881 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200882 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200883 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200884 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200885
Joe Perches93803b32015-03-02 19:54:49 -0800886 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200887 params_le->bss_type = DOT11_BSSTYPE_ANY;
888 params_le->scan_type = 0;
889 params_le->channel_num = 0;
890 params_le->nprobes = cpu_to_le32(-1);
891 params_le->active_time = cpu_to_le32(-1);
892 params_le->passive_time = cpu_to_le32(-1);
893 params_le->home_time = cpu_to_le32(-1);
894 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
895
896 /* if request is null exit so it will be all channel broadcast scan */
897 if (!request)
898 return;
899
900 n_ssids = request->n_ssids;
901 n_channels = request->n_channels;
902 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100903 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
904 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200905 if (n_channels > 0) {
906 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200907 chanspec = channel_to_chanspec(&cfg->d11inf,
908 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100909 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
910 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200911 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200912 }
913 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100914 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200915 }
916 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100917 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200918 if (n_ssids > 0) {
919 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
920 n_channels * sizeof(u16);
921 offset = roundup(offset, sizeof(u32));
922 ptr = (char *)params_le + offset;
923 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200924 memset(&ssid_le, 0, sizeof(ssid_le));
925 ssid_le.SSID_len =
926 cpu_to_le32(request->ssids[i].ssid_len);
927 memcpy(ssid_le.SSID, request->ssids[i].ssid,
928 request->ssids[i].ssid_len);
929 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100930 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200931 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100932 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
933 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200934 memcpy(ptr, &ssid_le, sizeof(ssid_le));
935 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200936 }
937 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100938 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200939 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100940 brcmf_dbg(SCAN, "SSID %s len=%d\n",
941 params_le->ssid_le.SSID,
942 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200943 params_le->ssid_le.SSID_len =
944 cpu_to_le32(request->ssids->ssid_len);
945 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
946 request->ssids->ssid_len);
947 }
948 }
949 /* Adding mask to channel numbers */
950 params_le->channel_num =
951 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
952 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
953}
954
955static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200956brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemanc4958102015-11-25 11:32:41 +0100957 struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200958{
959 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
960 offsetof(struct brcmf_escan_params_le, params_le);
961 struct brcmf_escan_params_le *params;
962 s32 err = 0;
963
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100964 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200965
966 if (request != NULL) {
967 /* Allocate space for populating ssids in struct */
968 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
969
970 /* Allocate space for populating ssids in struct */
Hante Meulemane9a6ca82015-11-25 11:32:37 +0100971 params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
Hante Meulemane756af52012-09-11 21:18:52 +0200972 }
973
974 params = kzalloc(params_size, GFP_KERNEL);
975 if (!params) {
976 err = -ENOMEM;
977 goto exit;
978 }
979 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200980 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200981 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
Hante Meulemanc4958102015-11-25 11:32:41 +0100982 params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200983 params->sync_id = cpu_to_le16(0x1234);
984
Arend van Spriela0f472a2013-04-05 10:57:49 +0200985 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200986 if (err) {
987 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100988 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200989 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100990 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200991 }
992
993 kfree(params);
994exit:
995 return err;
996}
997
998static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200999brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +02001000 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001001{
1002 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001003 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001004 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001005 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001006
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001007 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001008 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001009 escan->wiphy = wiphy;
1010 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001011 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001012 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001013 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001014 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001015 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001016 return err;
1017 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001018 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001019 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001020 results->version = 0;
1021 results->count = 0;
1022 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1023
Hante Meulemanc4958102015-11-25 11:32:41 +01001024 err = escan->run(cfg, ifp, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001025 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001026 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001027 return err;
1028}
1029
1030static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001031brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001032 struct cfg80211_scan_request *request,
1033 struct cfg80211_ssid *this_ssid)
1034{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001035 struct brcmf_if *ifp = vif->ifp;
1036 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001037 struct cfg80211_ssid *ssids;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001038 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001039 bool escan_req;
1040 bool spec_scan;
1041 s32 err;
Hante Meuleman675f5d82015-12-10 13:43:01 +01001042 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +02001043 u32 SSID_len;
1044
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001045 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001046
Arend van Sprielc1179032012-10-22 13:55:33 -07001047 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001048 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001049 return -EAGAIN;
1050 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001051 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001052 brcmf_err("Scanning being aborted: status (%lu)\n",
1053 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001054 return -EAGAIN;
1055 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001056 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1057 brcmf_err("Scanning suppressed: status (%lu)\n",
1058 cfg->scan_status);
1059 return -EAGAIN;
1060 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001061 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001062 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001063 return -EAGAIN;
1064 }
1065
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001066 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001067 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1068 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001069
Hante Meulemane756af52012-09-11 21:18:52 +02001070 escan_req = false;
1071 if (request) {
1072 /* scan bss */
1073 ssids = request->ssids;
1074 escan_req = true;
1075 } else {
1076 /* scan in ibss */
1077 /* we don't do escan in ibss */
1078 ssids = this_ssid;
1079 }
1080
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001081 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001082 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001083 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001084 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001085 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001086 if (err)
1087 goto scan_out;
1088
Arend van Spriela0f472a2013-04-05 10:57:49 +02001089 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001090 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001091 goto scan_out;
1092 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001093 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1094 ssids->ssid, ssids->ssid_len);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001095 memset(&ssid_le, 0, sizeof(ssid_le));
1096 SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
1097 ssid_le.SSID_len = cpu_to_le32(0);
Hante Meulemane756af52012-09-11 21:18:52 +02001098 spec_scan = false;
1099 if (SSID_len) {
Hante Meuleman675f5d82015-12-10 13:43:01 +01001100 memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
1101 ssid_le.SSID_len = cpu_to_le32(SSID_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001102 spec_scan = true;
1103 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001104 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001105
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001106 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001107 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001108 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001109 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001110 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001111 goto scan_out;
1112 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001113 brcmf_scan_config_mpc(ifp, 0);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001114 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
1115 sizeof(ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001116 if (err) {
1117 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001118 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
Hante Meuleman675f5d82015-12-10 13:43:01 +01001119 ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001120 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001121 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001122
Daniel Kim5e787f72014-06-21 12:11:18 +02001123 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001124 goto scan_out;
1125 }
1126 }
1127
Hante Meuleman661fa952015-02-06 18:36:47 +01001128 /* Arm scan timeout timer */
1129 mod_timer(&cfg->escan_timeout, jiffies +
1130 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1131
Hante Meulemane756af52012-09-11 21:18:52 +02001132 return 0;
1133
1134scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001135 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001136 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001137 return err;
1138}
1139
Arend van Spriel5b435de2011-10-05 13:19:03 +02001140static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001141brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001142{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001143 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001144 s32 err = 0;
1145
Arend van Sprield96b8012012-12-05 15:26:02 +01001146 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001147 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1148 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001149 return -EIO;
1150
Arend van Spriela0f472a2013-04-05 10:57:49 +02001151 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001152
Arend van Spriel5b435de2011-10-05 13:19:03 +02001153 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001154 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001155
Arend van Sprield96b8012012-12-05 15:26:02 +01001156 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001157 return err;
1158}
1159
1160static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1161{
1162 s32 err = 0;
1163
Arend van Sprielac24be62012-10-22 10:36:23 -07001164 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1165 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001166 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001167 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001168
1169 return err;
1170}
1171
1172static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1173{
1174 s32 err = 0;
1175
Arend van Sprielac24be62012-10-22 10:36:23 -07001176 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1177 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001178 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001179 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001180
1181 return err;
1182}
1183
1184static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1185{
1186 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001187 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001188
Arend van Sprielac24be62012-10-22 10:36:23 -07001189 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001190 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001191 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001192 return err;
1193 }
1194 return err;
1195}
1196
1197static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1198{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001199 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1200 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001201 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001202 s32 err = 0;
1203
Arend van Sprield96b8012012-12-05 15:26:02 +01001204 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001205 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001206 return -EIO;
1207
1208 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001209 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1210 cfg->conf->rts_threshold = wiphy->rts_threshold;
1211 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001212 if (!err)
1213 goto done;
1214 }
1215 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001216 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1217 cfg->conf->frag_threshold = wiphy->frag_threshold;
1218 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001219 if (!err)
1220 goto done;
1221 }
1222 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001223 && (cfg->conf->retry_long != wiphy->retry_long)) {
1224 cfg->conf->retry_long = wiphy->retry_long;
1225 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001226 if (!err)
1227 goto done;
1228 }
1229 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001230 && (cfg->conf->retry_short != wiphy->retry_short)) {
1231 cfg->conf->retry_short = wiphy->retry_short;
1232 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001233 if (!err)
1234 goto done;
1235 }
1236
1237done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001238 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001239 return err;
1240}
1241
Arend van Spriel5b435de2011-10-05 13:19:03 +02001242static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1243{
1244 memset(prof, 0, sizeof(*prof));
1245}
1246
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001247static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1248{
1249 u16 reason;
1250
1251 switch (e->event_code) {
1252 case BRCMF_E_DEAUTH:
1253 case BRCMF_E_DEAUTH_IND:
1254 case BRCMF_E_DISASSOC_IND:
1255 reason = e->reason;
1256 break;
1257 case BRCMF_E_LINK:
1258 default:
1259 reason = 0;
1260 break;
1261 }
1262 return reason;
1263}
1264
1265static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001266{
Piotr Haber61730d42013-04-23 12:53:12 +02001267 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001268 s32 err = 0;
1269
Arend van Sprield96b8012012-12-05 15:26:02 +01001270 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001271
Hante Meulemanb0a79082015-12-10 13:43:07 +01001272 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001273 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001274 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001275 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001276 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001277 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001278 }
Hante Meulemanb0a79082015-12-10 13:43:07 +01001279 if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1280 (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
1281 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
1282 true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001283 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001284 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001285 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1286 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001287 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001288}
1289
1290static s32
1291brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1292 struct cfg80211_ibss_params *params)
1293{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001294 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001295 struct brcmf_if *ifp = netdev_priv(ndev);
1296 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001297 struct brcmf_join_params join_params;
1298 size_t join_params_size = 0;
1299 s32 err = 0;
1300 s32 wsec = 0;
1301 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001302 u16 chanspec;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001303 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001304
Arend van Sprield96b8012012-12-05 15:26:02 +01001305 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001306 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001307 return -EIO;
1308
1309 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001310 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001311 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001312 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001313 return -EOPNOTSUPP;
1314 }
1315
Arend van Sprielc1179032012-10-22 13:55:33 -07001316 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001317
1318 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001319 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001320 else
Arend van Spriel16886732012-12-05 15:26:04 +01001321 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001322
Johannes Berg683b6d32012-11-08 21:25:48 +01001323 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001324 brcmf_dbg(CONN, "channel: %d\n",
1325 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001326 else
Arend van Spriel16886732012-12-05 15:26:04 +01001327 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001328
1329 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001330 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001331 else
Arend van Spriel16886732012-12-05 15:26:04 +01001332 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001333
1334 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001335 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001336 else
Arend van Spriel16886732012-12-05 15:26:04 +01001337 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001338
1339 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001340 brcmf_dbg(CONN, "beacon interval: %d\n",
1341 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001342 else
Arend van Spriel16886732012-12-05 15:26:04 +01001343 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001344
1345 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001346 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001347 else
Arend van Spriel16886732012-12-05 15:26:04 +01001348 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001349
1350 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001351 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001352 else
Arend van Spriel16886732012-12-05 15:26:04 +01001353 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001354
1355 /* Configure Privacy for starter */
1356 if (params->privacy)
1357 wsec |= WEP_ENABLED;
1358
Arend van Sprielc1179032012-10-22 13:55:33 -07001359 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001360 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001361 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001362 goto done;
1363 }
1364
1365 /* Configure Beacon Interval for starter */
1366 if (params->beacon_interval)
1367 bcnprd = params->beacon_interval;
1368 else
1369 bcnprd = 100;
1370
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001371 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001372 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001373 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001374 goto done;
1375 }
1376
1377 /* Configure required join parameter */
1378 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1379
1380 /* SSID */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001381 ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1382 memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1383 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001384 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001385
1386 /* BSSID */
1387 if (params->bssid) {
1388 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001389 join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001390 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001391 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001392 eth_broadcast_addr(join_params.params_le.bssid);
1393 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001394 }
1395
Arend van Spriel5b435de2011-10-05 13:19:03 +02001396 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001397 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001398 u32 target_channel;
1399
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001400 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001401 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001402 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001403 if (params->channel_fixed) {
1404 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001405 chanspec = chandef_to_chanspec(&cfg->d11inf,
1406 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001407 join_params.params_le.chanspec_list[0] =
1408 cpu_to_le16(chanspec);
1409 join_params.params_le.chanspec_num = cpu_to_le32(1);
1410 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001411 }
1412
1413 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001414 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001415 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001416 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001417 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001418 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001419 goto done;
1420 }
1421 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001422 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001423
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001424 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001425
1426
Arend van Sprielc1179032012-10-22 13:55:33 -07001427 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001428 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001429 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001430 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001431 goto done;
1432 }
1433
1434done:
1435 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001436 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001437 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001438 return err;
1439}
1440
1441static s32
1442brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1443{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001444 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001445
Arend van Sprield96b8012012-12-05 15:26:02 +01001446 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman6a98d642016-01-02 09:41:40 +01001447 if (!check_vif_up(ifp->vif)) {
1448 /* When driver is being unloaded, it can end up here. If an
1449 * error is returned then later on a debug trace in the wireless
1450 * core module will be printed. To avoid this 0 is returned.
1451 */
1452 return 0;
1453 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001454
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001455 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001456
Arend van Sprield96b8012012-12-05 15:26:02 +01001457 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001458
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001459 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001460}
1461
1462static s32 brcmf_set_wpa_version(struct net_device *ndev,
1463 struct cfg80211_connect_params *sme)
1464{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001465 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001466 struct brcmf_cfg80211_security *sec;
1467 s32 val = 0;
1468 s32 err = 0;
1469
1470 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1471 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1472 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1473 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1474 else
1475 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001476 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001477 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001478 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001479 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001480 return err;
1481 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001482 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001483 sec->wpa_versions = sme->crypto.wpa_versions;
1484 return err;
1485}
1486
1487static s32 brcmf_set_auth_type(struct net_device *ndev,
1488 struct cfg80211_connect_params *sme)
1489{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001490 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001491 struct brcmf_cfg80211_security *sec;
1492 s32 val = 0;
1493 s32 err = 0;
1494
1495 switch (sme->auth_type) {
1496 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1497 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001498 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001499 break;
1500 case NL80211_AUTHTYPE_SHARED_KEY:
1501 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001502 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001503 break;
1504 case NL80211_AUTHTYPE_AUTOMATIC:
1505 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001506 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001507 break;
1508 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001509 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001510 default:
1511 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001512 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001513 break;
1514 }
1515
Hante Meuleman89286dc2013-02-08 15:53:46 +01001516 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001517 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001518 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001519 return err;
1520 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001521 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001522 sec->auth_type = sme->auth_type;
1523 return err;
1524}
1525
1526static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001527brcmf_set_wsec_mode(struct net_device *ndev,
1528 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001529{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001530 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001531 struct brcmf_cfg80211_security *sec;
1532 s32 pval = 0;
1533 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001534 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001535 s32 err = 0;
1536
1537 if (sme->crypto.n_ciphers_pairwise) {
1538 switch (sme->crypto.ciphers_pairwise[0]) {
1539 case WLAN_CIPHER_SUITE_WEP40:
1540 case WLAN_CIPHER_SUITE_WEP104:
1541 pval = WEP_ENABLED;
1542 break;
1543 case WLAN_CIPHER_SUITE_TKIP:
1544 pval = TKIP_ENABLED;
1545 break;
1546 case WLAN_CIPHER_SUITE_CCMP:
1547 pval = AES_ENABLED;
1548 break;
1549 case WLAN_CIPHER_SUITE_AES_CMAC:
1550 pval = AES_ENABLED;
1551 break;
1552 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001553 brcmf_err("invalid cipher pairwise (%d)\n",
1554 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001555 return -EINVAL;
1556 }
1557 }
1558 if (sme->crypto.cipher_group) {
1559 switch (sme->crypto.cipher_group) {
1560 case WLAN_CIPHER_SUITE_WEP40:
1561 case WLAN_CIPHER_SUITE_WEP104:
1562 gval = WEP_ENABLED;
1563 break;
1564 case WLAN_CIPHER_SUITE_TKIP:
1565 gval = TKIP_ENABLED;
1566 break;
1567 case WLAN_CIPHER_SUITE_CCMP:
1568 gval = AES_ENABLED;
1569 break;
1570 case WLAN_CIPHER_SUITE_AES_CMAC:
1571 gval = AES_ENABLED;
1572 break;
1573 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001574 brcmf_err("invalid cipher group (%d)\n",
1575 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001576 return -EINVAL;
1577 }
1578 }
1579
Arend van Spriel16886732012-12-05 15:26:04 +01001580 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001581 /* In case of privacy, but no security and WPS then simulate */
1582 /* setting AES. WPS-2.0 allows no security */
1583 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1584 sme->privacy)
1585 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001586
1587 if (mfp)
1588 wsec = pval | gval | MFP_CAPABLE;
1589 else
1590 wsec = pval | gval;
1591 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001592 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001593 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001594 return err;
1595 }
1596
Arend van Spriel06bb1232012-09-27 14:17:56 +02001597 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001598 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1599 sec->cipher_group = sme->crypto.cipher_group;
1600
1601 return err;
1602}
1603
1604static s32
1605brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1606{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001607 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001608 struct brcmf_cfg80211_security *sec;
1609 s32 val = 0;
1610 s32 err = 0;
1611
1612 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001613 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1614 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001615 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001616 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001617 return err;
1618 }
1619 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1620 switch (sme->crypto.akm_suites[0]) {
1621 case WLAN_AKM_SUITE_8021X:
1622 val = WPA_AUTH_UNSPECIFIED;
1623 break;
1624 case WLAN_AKM_SUITE_PSK:
1625 val = WPA_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 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1633 switch (sme->crypto.akm_suites[0]) {
1634 case WLAN_AKM_SUITE_8021X:
1635 val = WPA2_AUTH_UNSPECIFIED;
1636 break;
1637 case WLAN_AKM_SUITE_PSK:
1638 val = WPA2_AUTH_PSK;
1639 break;
1640 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001641 brcmf_err("invalid cipher group (%d)\n",
1642 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001643 return -EINVAL;
1644 }
1645 }
1646
Arend van Spriel16886732012-12-05 15:26:04 +01001647 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001648 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1649 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001650 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001651 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001652 return err;
1653 }
1654 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001655 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001656 sec->wpa_auth = sme->crypto.akm_suites[0];
1657
1658 return err;
1659}
1660
1661static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001662brcmf_set_sharedkey(struct net_device *ndev,
1663 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001664{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001665 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001666 struct brcmf_cfg80211_security *sec;
1667 struct brcmf_wsec_key key;
1668 s32 val;
1669 s32 err = 0;
1670
Arend van Spriel16886732012-12-05 15:26:04 +01001671 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001672
Roland Vossena718e2f2011-10-12 20:51:24 +02001673 if (sme->key_len == 0)
1674 return 0;
1675
Arend van Spriel06bb1232012-09-27 14:17:56 +02001676 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001677 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1678 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001679
1680 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1681 return 0;
1682
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001683 if (!(sec->cipher_pairwise &
1684 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1685 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001686
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001687 memset(&key, 0, sizeof(key));
1688 key.len = (u32) sme->key_len;
1689 key.index = (u32) sme->key_idx;
1690 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001691 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001692 return -EINVAL;
1693 }
1694 memcpy(key.data, sme->key, key.len);
1695 key.flags = BRCMF_PRIMARY_KEY;
1696 switch (sec->cipher_pairwise) {
1697 case WLAN_CIPHER_SUITE_WEP40:
1698 key.algo = CRYPTO_ALGO_WEP1;
1699 break;
1700 case WLAN_CIPHER_SUITE_WEP104:
1701 key.algo = CRYPTO_ALGO_WEP128;
1702 break;
1703 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001704 brcmf_err("Invalid algorithm (%d)\n",
1705 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001706 return -EINVAL;
1707 }
1708 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001709 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1710 key.len, key.index, key.algo);
1711 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001712 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001713 if (err)
1714 return err;
1715
1716 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001717 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001718 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001719 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001720 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001721 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001722 }
1723 return err;
1724}
1725
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001726static
1727enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1728 enum nl80211_auth_type type)
1729{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001730 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1731 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1732 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1733 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001734 }
1735 return type;
1736}
1737
Arend van Spriel5b435de2011-10-05 13:19:03 +02001738static s32
1739brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001740 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001741{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001742 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001743 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001744 struct ieee80211_channel *chan = sme->channel;
1745 struct brcmf_join_params join_params;
1746 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001747 const struct brcmf_tlv *rsn_ie;
1748 const struct brcmf_vs_tlv *wpa_ie;
1749 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001750 u32 ie_len;
1751 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001752 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001753 s32 err = 0;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001754 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001755
Arend van Sprield96b8012012-12-05 15:26:02 +01001756 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001757 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001758 return -EIO;
1759
1760 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001761 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001762 return -EOPNOTSUPP;
1763 }
1764
Hante Meuleman89286dc2013-02-08 15:53:46 +01001765 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1766 /* A normal (non P2P) connection request setup. */
1767 ie = NULL;
1768 ie_len = 0;
1769 /* find the WPA_IE */
1770 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1771 if (wpa_ie) {
1772 ie = wpa_ie;
1773 ie_len = wpa_ie->len + TLV_HDR_LEN;
1774 } else {
1775 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001776 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1777 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001778 WLAN_EID_RSN);
1779 if (rsn_ie) {
1780 ie = rsn_ie;
1781 ie_len = rsn_ie->len + TLV_HDR_LEN;
1782 }
1783 }
1784 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1785 }
1786
1787 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1788 sme->ie, sme->ie_len);
1789 if (err)
1790 brcmf_err("Set Assoc REQ IE Failed\n");
1791 else
1792 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1793
Arend van Sprielc1179032012-10-22 13:55:33 -07001794 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001795
1796 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001797 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001798 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001799 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001800 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1801 cfg->channel, chan->center_freq, chanspec);
1802 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001803 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001804 chanspec = 0;
1805 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001806
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001807 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001808
1809 err = brcmf_set_wpa_version(ndev, sme);
1810 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001811 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001812 goto done;
1813 }
1814
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001815 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001816 err = brcmf_set_auth_type(ndev, sme);
1817 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001818 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001819 goto done;
1820 }
1821
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001822 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001823 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001824 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001825 goto done;
1826 }
1827
1828 err = brcmf_set_key_mgmt(ndev, sme);
1829 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001830 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001831 goto done;
1832 }
1833
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001834 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001835 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001836 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001837 goto done;
1838 }
1839
Hante Meuleman89286dc2013-02-08 15:53:46 +01001840 /* Join with specific BSSID and cached SSID
1841 * If SSID is zero join based on BSSID only
1842 */
1843 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1844 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1845 if (cfg->channel)
1846 join_params_size += sizeof(u16);
1847 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1848 if (ext_join_params == NULL) {
1849 err = -ENOMEM;
1850 goto done;
1851 }
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001852 ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
1853 ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
1854 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
1855 if (ssid_len < IEEE80211_MAX_SSID_LEN)
1856 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
1857 ext_join_params->ssid_le.SSID, ssid_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001858
Hante Meuleman89286dc2013-02-08 15:53:46 +01001859 /* Set up join scan parameters */
1860 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001861 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1862
1863 if (sme->bssid)
1864 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1865 else
Joe Perches93803b32015-03-02 19:54:49 -08001866 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001867
1868 if (cfg->channel) {
1869 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1870
1871 ext_join_params->assoc_le.chanspec_list[0] =
1872 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001873 /* Increase dwell time to receive probe response or detect
1874 * beacon from target AP at a noisy air only during connect
1875 * command.
1876 */
1877 ext_join_params->scan_le.active_time =
1878 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1879 ext_join_params->scan_le.passive_time =
1880 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1881 /* To sync with presence period of VSDB GO send probe request
1882 * more frequently. Probe request will be stopped when it gets
1883 * probe response from target AP/GO.
1884 */
1885 ext_join_params->scan_le.nprobes =
1886 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1887 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1888 } else {
1889 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1890 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1891 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001892 }
1893
1894 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1895 join_params_size);
1896 kfree(ext_join_params);
1897 if (!err)
1898 /* This is it. join command worked, we are done */
1899 goto done;
1900
1901 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001902 memset(&join_params, 0, sizeof(join_params));
1903 join_params_size = sizeof(join_params.ssid_le);
1904
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001905 memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
1906 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001907
Hante Meuleman89286dc2013-02-08 15:53:46 +01001908 if (sme->bssid)
1909 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1910 else
Joe Perches93803b32015-03-02 19:54:49 -08001911 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001912
Hante Meuleman17012612013-02-06 18:40:44 +01001913 if (cfg->channel) {
1914 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1915 join_params.params_le.chanspec_num = cpu_to_le32(1);
1916 join_params_size += sizeof(join_params.params_le);
1917 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001918 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001919 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001920 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001921 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001922
1923done:
1924 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001925 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001926 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001927 return err;
1928}
1929
1930static s32
1931brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1932 u16 reason_code)
1933{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001934 struct brcmf_if *ifp = netdev_priv(ndev);
1935 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001936 struct brcmf_scb_val_le scbval;
1937 s32 err = 0;
1938
Arend van Sprield96b8012012-12-05 15:26:02 +01001939 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001940 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001941 return -EIO;
1942
Arend van Sprielc1179032012-10-22 13:55:33 -07001943 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001944 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001945 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001946
Arend van Spriel06bb1232012-09-27 14:17:56 +02001947 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001948 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001949 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001950 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001951 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001952 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001953
Arend van Sprield96b8012012-12-05 15:26:02 +01001954 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001955 return err;
1956}
1957
1958static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001959brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001960 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001961{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001962 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001963 struct net_device *ndev = cfg_to_ndev(cfg);
1964 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001965 s32 err;
1966 s32 disable;
1967 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001968
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001969 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07001970 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 return -EIO;
1972
1973 switch (type) {
1974 case NL80211_TX_POWER_AUTOMATIC:
1975 break;
1976 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001977 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001978 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001979 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001980 err = -EINVAL;
1981 goto done;
1982 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001983 qdbm = MBM_TO_DBM(4 * mbm);
1984 if (qdbm > 127)
1985 qdbm = 127;
1986 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001987 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001988 default:
1989 brcmf_err("Unsupported type %d\n", type);
1990 err = -EINVAL;
1991 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001992 }
1993 /* Make sure radio is off or on as far as software is concerned */
1994 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001995 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001996 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001997 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001998
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001999 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002000 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002001 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002002
2003done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002004 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002005 return err;
2006}
2007
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002008static s32
2009brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2010 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002011{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002012 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002013 struct net_device *ndev = cfg_to_ndev(cfg);
2014 struct brcmf_if *ifp = netdev_priv(ndev);
2015 s32 qdbm = 0;
2016 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002017
Arend van Sprield96b8012012-12-05 15:26:02 +01002018 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002019 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002020 return -EIO;
2021
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002022 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002023 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002024 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002025 goto done;
2026 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002027 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002028
2029done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002030 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002031 return err;
2032}
2033
2034static s32
2035brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002036 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002037{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002038 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002039 u32 index;
2040 u32 wsec;
2041 s32 err = 0;
2042
Arend van Sprield96b8012012-12-05 15:26:02 +01002043 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002044 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002045 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002046 return -EIO;
2047
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002048 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002049 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002050 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002051 goto done;
2052 }
2053
2054 if (wsec & WEP_ENABLED) {
2055 /* Just select a new current key */
2056 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002057 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002058 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002059 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002060 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002061 }
2062done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002063 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002064 return err;
2065}
2066
2067static s32
2068brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2069 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2070{
Hante Meuleman992f6062013-04-02 21:06:17 +02002071 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002072 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002073 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002074 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002075
2076 memset(&key, 0, sizeof(key));
2077 key.index = (u32) key_idx;
2078 /* Instead of bcast for ea address for default wep keys,
2079 driver needs it to be Null */
2080 if (!is_multicast_ether_addr(mac_addr))
2081 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2082 key.len = (u32) params->key_len;
2083 /* check for key index change */
2084 if (key.len == 0) {
2085 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002086 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002087 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002088 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002089 } else {
2090 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002091 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002092 return -EINVAL;
2093 }
2094
Arend van Spriel16886732012-12-05 15:26:04 +01002095 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002096 memcpy(key.data, params->key, key.len);
2097
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002098 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002099 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2100 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002101 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2102 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2103 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2104 }
2105
2106 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2107 if (params->seq && params->seq_len == 6) {
2108 /* rx iv */
2109 u8 *ivptr;
2110 ivptr = (u8 *) params->seq;
2111 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2112 (ivptr[3] << 8) | ivptr[2];
2113 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2114 key.iv_initialized = true;
2115 }
2116
2117 switch (params->cipher) {
2118 case WLAN_CIPHER_SUITE_WEP40:
2119 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002120 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002121 break;
2122 case WLAN_CIPHER_SUITE_WEP104:
2123 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002124 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002125 break;
2126 case WLAN_CIPHER_SUITE_TKIP:
2127 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002128 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002129 break;
2130 case WLAN_CIPHER_SUITE_AES_CMAC:
2131 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002132 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002133 break;
2134 case WLAN_CIPHER_SUITE_CCMP:
2135 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002136 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002137 break;
2138 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002139 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002140 return -EINVAL;
2141 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002142 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002143 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002144 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002145 }
2146 return err;
2147}
2148
2149static s32
2150brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2151 u8 key_idx, bool pairwise, const u8 *mac_addr,
2152 struct key_params *params)
2153{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002154 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002155 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002156 s32 val;
2157 s32 wsec;
2158 s32 err = 0;
2159 u8 keybuf[8];
2160
Arend van Sprield96b8012012-12-05 15:26:02 +01002161 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002162 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002163 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002164 return -EIO;
2165
Hante Meuleman118eb302014-12-21 12:43:49 +01002166 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2167 /* we ignore this key index in this case */
2168 brcmf_err("invalid key index (%d)\n", key_idx);
2169 return -EINVAL;
2170 }
2171
Daniel Kim787eb032014-01-29 15:32:23 +01002172 if (mac_addr &&
2173 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2174 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002175 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002176 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2177 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002178
Hante Meuleman118eb302014-12-21 12:43:49 +01002179 key = &ifp->vif->profile.key[key_idx];
2180 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002181
Hante Meuleman118eb302014-12-21 12:43:49 +01002182 if (params->key_len > sizeof(key->data)) {
2183 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002184 err = -EINVAL;
2185 goto done;
2186 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002187 key->len = params->key_len;
2188 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002189
Hante Meuleman118eb302014-12-21 12:43:49 +01002190 memcpy(key->data, params->key, key->len);
2191
2192 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002193 switch (params->cipher) {
2194 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002195 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002196 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002197 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002198 break;
2199 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002200 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002201 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002202 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002203 break;
2204 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002205 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002206 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002207 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2208 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2209 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002210 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002211 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002212 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002213 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002214 break;
2215 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002216 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002217 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002218 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002219 break;
2220 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002221 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002222 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002223 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002224 break;
2225 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002226 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002227 err = -EINVAL;
2228 goto done;
2229 }
2230
Hante Meuleman118eb302014-12-21 12:43:49 +01002231 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002232 if (err)
2233 goto done;
2234
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002235 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002237 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002238 goto done;
2239 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002240 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002241 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002242 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002243 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002244 goto done;
2245 }
2246
Arend van Spriel5b435de2011-10-05 13:19:03 +02002247done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002248 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002249 return err;
2250}
2251
2252static s32
2253brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2254 u8 key_idx, bool pairwise, const u8 *mac_addr)
2255{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002256 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002257 struct brcmf_wsec_key key;
2258 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002259
Arend van Sprield96b8012012-12-05 15:26:02 +01002260 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002261 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002262 return -EIO;
2263
Hante Meuleman118eb302014-12-21 12:43:49 +01002264 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002265 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002266 return -EINVAL;
2267 }
2268
Arend van Spriel5b435de2011-10-05 13:19:03 +02002269 memset(&key, 0, sizeof(key));
2270
2271 key.index = (u32) key_idx;
2272 key.flags = BRCMF_PRIMARY_KEY;
2273 key.algo = CRYPTO_ALGO_OFF;
2274
Arend van Spriel16886732012-12-05 15:26:04 +01002275 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002276
2277 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002278 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002279
Arend van Sprield96b8012012-12-05 15:26:02 +01002280 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002281 return err;
2282}
2283
2284static s32
2285brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2286 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2287 void (*callback) (void *cookie, struct key_params * params))
2288{
2289 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002290 struct brcmf_if *ifp = netdev_priv(ndev);
2291 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002292 struct brcmf_cfg80211_security *sec;
2293 s32 wsec;
2294 s32 err = 0;
2295
Arend van Sprield96b8012012-12-05 15:26:02 +01002296 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002297 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002298 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002299 return -EIO;
2300
2301 memset(&params, 0, sizeof(params));
2302
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002303 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002304 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002305 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002306 /* Ignore this error, may happen during DISASSOC */
2307 err = -EAGAIN;
2308 goto done;
2309 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002310 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002311 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002312 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2313 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002314 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002315 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2316 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002317 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002318 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002319 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002320 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002321 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002322 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002323 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002324 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002325 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002326 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002327 err = -EINVAL;
2328 goto done;
2329 }
2330 callback(cookie, &params);
2331
2332done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002333 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002334 return err;
2335}
2336
2337static s32
2338brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2339 struct net_device *ndev, u8 key_idx)
2340{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002341 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002342
2343 return -EOPNOTSUPP;
2344}
2345
Hante Meuleman118eb302014-12-21 12:43:49 +01002346static void
2347brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2348{
2349 s32 err;
2350 u8 key_idx;
2351 struct brcmf_wsec_key *key;
2352 s32 wsec;
2353
2354 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2355 key = &ifp->vif->profile.key[key_idx];
2356 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2357 (key->algo == CRYPTO_ALGO_WEP128))
2358 break;
2359 }
2360 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2361 return;
2362
2363 err = send_key_to_dongle(ifp, key);
2364 if (err) {
2365 brcmf_err("Setting WEP key failed (%d)\n", err);
2366 return;
2367 }
2368 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2369 if (err) {
2370 brcmf_err("get wsec error (%d)\n", err);
2371 return;
2372 }
2373 wsec |= WEP_ENABLED;
2374 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2375 if (err)
2376 brcmf_err("set wsec error (%d)\n", err);
2377}
2378
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002379static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2380{
2381 struct nl80211_sta_flag_update *sfu;
2382
2383 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2384 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2385 sfu = &si->sta_flags;
2386 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2387 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2388 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2389 BIT(NL80211_STA_FLAG_AUTHORIZED);
2390 if (fw_sta_flags & BRCMF_STA_WME)
2391 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2392 if (fw_sta_flags & BRCMF_STA_AUTHE)
2393 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2394 if (fw_sta_flags & BRCMF_STA_ASSOC)
2395 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2396 if (fw_sta_flags & BRCMF_STA_AUTHO)
2397 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2398}
2399
2400static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2401{
2402 struct {
2403 __le32 len;
2404 struct brcmf_bss_info_le bss_le;
2405 } *buf;
2406 u16 capability;
2407 int err;
2408
2409 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2410 if (!buf)
2411 return;
2412
2413 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2414 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2415 WL_BSS_INFO_MAX);
2416 if (err) {
2417 brcmf_err("Failed to get bss info (%d)\n", err);
2418 return;
2419 }
2420 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2421 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2422 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2423 capability = le16_to_cpu(buf->bss_le.capability);
2424 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2425 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2426 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2427 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2428 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2429 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2430}
2431
Arend van Spriel5b435de2011-10-05 13:19:03 +02002432static s32
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002433brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
2434 struct station_info *sinfo)
2435{
2436 struct brcmf_scb_val_le scbval;
2437 struct brcmf_pktcnt_le pktcnt;
2438 s32 err;
2439 u32 rate;
2440 u32 rssi;
2441
2442 /* Get the current tx rate */
2443 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
2444 if (err < 0) {
2445 brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
2446 return err;
2447 }
2448 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
2449 sinfo->txrate.legacy = rate * 5;
2450
2451 memset(&scbval, 0, sizeof(scbval));
2452 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
2453 sizeof(scbval));
2454 if (err) {
2455 brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
2456 return err;
2457 }
2458 rssi = le32_to_cpu(scbval.val);
2459 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2460 sinfo->signal = rssi;
2461
2462 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
2463 sizeof(pktcnt));
2464 if (err) {
2465 brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
2466 return err;
2467 }
2468 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
2469 BIT(NL80211_STA_INFO_RX_DROP_MISC) |
2470 BIT(NL80211_STA_INFO_TX_PACKETS) |
2471 BIT(NL80211_STA_INFO_TX_FAILED);
2472 sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
2473 sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
2474 sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
2475 sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
2476
2477 return 0;
2478}
2479
2480static s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002481brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002482 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002483{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002484 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002485 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002486 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002487 u32 sta_flags;
2488 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002489 s32 total_rssi;
2490 s32 count_rssi;
2491 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002492
Arend van Sprield96b8012012-12-05 15:26:02 +01002493 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002494 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002495 return -EIO;
2496
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002497 if (brcmf_is_ibssmode(ifp->vif))
2498 return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
2499
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002500 memset(&sta_info_le, 0, sizeof(sta_info_le));
2501 memcpy(&sta_info_le, mac, ETH_ALEN);
2502 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2503 &sta_info_le,
2504 sizeof(sta_info_le));
2505 is_tdls_peer = !err;
2506 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002507 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002508 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002509 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002510 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002511 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002512 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002513 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002514 }
2515 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2516 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2517 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2518 sta_flags = le32_to_cpu(sta_info_le.flags);
2519 brcmf_convert_sta_flags(sta_flags, sinfo);
2520 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2521 if (is_tdls_peer)
2522 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2523 else
2524 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2525 if (sta_flags & BRCMF_STA_ASSOC) {
2526 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2527 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2528 brcmf_fill_bss_param(ifp, sinfo);
2529 }
2530 if (sta_flags & BRCMF_STA_SCBSTATS) {
2531 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2532 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2533 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2534 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2535 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2536 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2537 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2538 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2539 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002540 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002541 sinfo->txrate.legacy =
2542 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002543 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002544 if (sinfo->rx_packets) {
2545 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002546 sinfo->rxrate.legacy =
2547 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002548 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002549 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2550 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2551 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2552 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2553 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2554 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002555 total_rssi = 0;
2556 count_rssi = 0;
2557 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2558 if (sta_info_le.rssi[i]) {
2559 sinfo->chain_signal_avg[count_rssi] =
2560 sta_info_le.rssi[i];
2561 sinfo->chain_signal[count_rssi] =
2562 sta_info_le.rssi[i];
2563 total_rssi += sta_info_le.rssi[i];
2564 count_rssi++;
2565 }
2566 }
2567 if (count_rssi) {
2568 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2569 sinfo->chains = count_rssi;
2570
2571 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2572 total_rssi /= count_rssi;
2573 sinfo->signal = total_rssi;
2574 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002575 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002576done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002577 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002578 return err;
2579}
2580
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002581static int
2582brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2583 int idx, u8 *mac, struct station_info *sinfo)
2584{
2585 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2586 struct brcmf_if *ifp = netdev_priv(ndev);
2587 s32 err;
2588
2589 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2590
2591 if (idx == 0) {
2592 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2593 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2594 &cfg->assoclist,
2595 sizeof(cfg->assoclist));
2596 if (err) {
2597 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2598 err);
2599 cfg->assoclist.count = 0;
2600 return -EOPNOTSUPP;
2601 }
2602 }
2603 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2604 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2605 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2606 }
2607 return -ENOENT;
2608}
2609
Arend van Spriel5b435de2011-10-05 13:19:03 +02002610static s32
2611brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2612 bool enabled, s32 timeout)
2613{
2614 s32 pm;
2615 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002616 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002617 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002618
Arend van Sprield96b8012012-12-05 15:26:02 +01002619 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002620
2621 /*
2622 * Powersave enable/disable request is coming from the
2623 * cfg80211 even before the interface is up. In that
2624 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002625 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002626 * FW later while initializing the dongle
2627 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002628 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002629 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002630
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002631 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002632 goto done;
2633 }
2634
2635 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002636 /* Do not enable the power save after assoc if it is a p2p interface */
2637 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2638 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2639 pm = PM_OFF;
2640 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002641 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002642
Arend van Sprielc1179032012-10-22 13:55:33 -07002643 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002644 if (err) {
2645 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002646 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002647 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002648 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002649 }
2650done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002651 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002652 return err;
2653}
2654
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002655static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002656 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002657{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002658 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002659 struct ieee80211_channel *notify_channel;
2660 struct cfg80211_bss *bss;
2661 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002662 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002663 u16 channel;
2664 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002665 u16 notify_capability;
2666 u16 notify_interval;
2667 u8 *notify_ie;
2668 size_t notify_ielen;
2669 s32 notify_signal;
2670
2671 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002672 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002673 return 0;
2674 }
2675
Franky Lin83cf17a2013-04-11 13:28:50 +02002676 if (!bi->ctl_ch) {
2677 ch.chspec = le16_to_cpu(bi->chanspec);
2678 cfg->d11inf.decchspec(&ch);
2679 bi->ctl_ch = ch.chnum;
2680 }
2681 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002682
2683 if (channel <= CH_MAX_2G_CHANNEL)
2684 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2685 else
2686 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2687
2688 freq = ieee80211_channel_to_frequency(channel, band->band);
2689 notify_channel = ieee80211_get_channel(wiphy, freq);
2690
Arend van Spriel5b435de2011-10-05 13:19:03 +02002691 notify_capability = le16_to_cpu(bi->capability);
2692 notify_interval = le16_to_cpu(bi->beacon_period);
2693 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2694 notify_ielen = le32_to_cpu(bi->ie_length);
2695 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2696
Arend van Spriel16886732012-12-05 15:26:04 +01002697 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2698 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2699 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2700 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2701 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002702
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002703 bss = cfg80211_inform_bss(wiphy, notify_channel,
2704 CFG80211_BSS_FTYPE_UNKNOWN,
2705 (const u8 *)bi->BSSID,
2706 0, notify_capability,
2707 notify_interval, notify_ie,
2708 notify_ielen, notify_signal,
2709 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002710
Franky Line78946e2011-11-10 20:30:34 +01002711 if (!bss)
2712 return -ENOMEM;
2713
Johannes Berg5b112d32013-02-01 01:49:58 +01002714 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002715
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002716 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002717}
2718
Roland Vossen6f09be02011-10-18 14:03:02 +02002719static struct brcmf_bss_info_le *
2720next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2721{
2722 if (bss == NULL)
2723 return list->bss_info_le;
2724 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2725 le32_to_cpu(bss->length));
2726}
2727
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002728static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002729{
2730 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002731 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002732 s32 err = 0;
2733 int i;
2734
Hante Meulemanef8596e2014-09-30 10:23:13 +02002735 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002736 if (bss_list->count != 0 &&
2737 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002738 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2739 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002740 return -EOPNOTSUPP;
2741 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002742 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002743 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002744 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002745 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002746 if (err)
2747 break;
2748 }
2749 return err;
2750}
2751
Hante Meulemanb0a79082015-12-10 13:43:07 +01002752static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
2753 struct net_device *ndev, const u8 *bssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002754{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002755 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002756 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002757 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002758 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002759 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002760 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002761 u8 *buf = NULL;
2762 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002763 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002764 u16 notify_capability;
2765 u16 notify_interval;
2766 u8 *notify_ie;
2767 size_t notify_ielen;
2768 s32 notify_signal;
2769
Arend van Sprield96b8012012-12-05 15:26:02 +01002770 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002771
2772 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2773 if (buf == NULL) {
2774 err = -ENOMEM;
2775 goto CleanUp;
2776 }
2777
2778 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2779
Arend van Sprielac24be62012-10-22 10:36:23 -07002780 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2781 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002782 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002783 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002784 goto CleanUp;
2785 }
2786
Roland Vossend34bf642011-10-18 14:03:01 +02002787 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002788
Franky Lin83cf17a2013-04-11 13:28:50 +02002789 ch.chspec = le16_to_cpu(bi->chanspec);
2790 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002791
Franky Lin83cf17a2013-04-11 13:28:50 +02002792 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002793 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2794 else
2795 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2796
Franky Lin83cf17a2013-04-11 13:28:50 +02002797 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Hante Meulemanb0a79082015-12-10 13:43:07 +01002798 cfg->channel = freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002799 notify_channel = ieee80211_get_channel(wiphy, freq);
2800
Arend van Spriel5b435de2011-10-05 13:19:03 +02002801 notify_capability = le16_to_cpu(bi->capability);
2802 notify_interval = le16_to_cpu(bi->beacon_period);
2803 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2804 notify_ielen = le32_to_cpu(bi->ie_length);
2805 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2806
Franky Lin83cf17a2013-04-11 13:28:50 +02002807 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002808 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2809 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2810 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002811
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002812 bss = cfg80211_inform_bss(wiphy, notify_channel,
2813 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2814 notify_capability, notify_interval,
2815 notify_ie, notify_ielen, notify_signal,
2816 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002817
Franky Line78946e2011-11-10 20:30:34 +01002818 if (!bss) {
2819 err = -ENOMEM;
2820 goto CleanUp;
2821 }
2822
Johannes Berg5b112d32013-02-01 01:49:58 +01002823 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002824
Arend van Spriel5b435de2011-10-05 13:19:03 +02002825CleanUp:
2826
2827 kfree(buf);
2828
Arend van Sprield96b8012012-12-05 15:26:02 +01002829 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002830
2831 return err;
2832}
2833
Hante Meuleman89286dc2013-02-08 15:53:46 +01002834static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2835 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002836{
Roland Vossend34bf642011-10-18 14:03:01 +02002837 struct brcmf_bss_info_le *bi;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002838 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002839 u16 beacon_interval;
2840 u8 dtim_period;
2841 size_t ie_len;
2842 u8 *ie;
2843 s32 err = 0;
2844
Arend van Sprield96b8012012-12-05 15:26:02 +01002845 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002846 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002847 return err;
2848
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002849 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002850 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002851 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002852 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002853 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002854 goto update_bss_info_out;
2855 }
2856
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002857 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2858 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002859 if (err)
2860 goto update_bss_info_out;
2861
2862 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2863 ie_len = le32_to_cpu(bi->ie_length);
2864 beacon_interval = le16_to_cpu(bi->beacon_period);
2865
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002866 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002867 if (tim)
2868 dtim_period = tim->data[1];
2869 else {
2870 /*
2871 * active scan was done so we could not get dtim
2872 * information out of probe response.
2873 * so we speficially query dtim information to dongle.
2874 */
2875 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002876 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002877 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002878 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002879 goto update_bss_info_out;
2880 }
2881 dtim_period = (u8)var;
2882 }
2883
Arend van Spriel5b435de2011-10-05 13:19:03 +02002884update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002885 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002886 return err;
2887}
2888
Hante Meuleman18e2f612013-02-08 15:53:49 +01002889void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002890{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002891 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002892
Arend van Sprielc1179032012-10-22 13:55:33 -07002893 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002894 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002895 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002896 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002897 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002898 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2899 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002900}
2901
Hante Meulemane756af52012-09-11 21:18:52 +02002902static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2903{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002904 struct brcmf_cfg80211_info *cfg =
2905 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002906 escan_timeout_work);
2907
Hante Meulemanef8596e2014-09-30 10:23:13 +02002908 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002909 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002910}
2911
2912static void brcmf_escan_timeout(unsigned long data)
2913{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002914 struct brcmf_cfg80211_info *cfg =
2915 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002916
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002917 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002918 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002919 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002920 }
2921}
2922
2923static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002924brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2925 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002926 struct brcmf_bss_info_le *bss_info_le)
2927{
Franky Lin83cf17a2013-04-11 13:28:50 +02002928 struct brcmu_chan ch_bss, ch_bss_info_le;
2929
2930 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2931 cfg->d11inf.decchspec(&ch_bss);
2932 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2933 cfg->d11inf.decchspec(&ch_bss_info_le);
2934
Hante Meulemane756af52012-09-11 21:18:52 +02002935 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002936 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002937 bss_info_le->SSID_len == bss->SSID_len &&
2938 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002939 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2940 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002941 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2942 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2943
Hante Meulemane756af52012-09-11 21:18:52 +02002944 /* preserve max RSSI if the measurements are
2945 * both on-channel or both off-channel
2946 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002947 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002948 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002949 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2950 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002951 /* preserve the on-channel rssi measurement
2952 * if the new measurement is off channel
2953 */
2954 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002955 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002956 }
2957 return 1;
2958 }
2959 return 0;
2960}
2961
2962static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002963brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002964 const struct brcmf_event_msg *e, void *data)
2965{
Arend van Spriel19937322012-11-05 16:22:32 -08002966 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002967 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002968 struct brcmf_escan_result_le *escan_result_le;
2969 struct brcmf_bss_info_le *bss_info_le;
2970 struct brcmf_bss_info_le *bss = NULL;
2971 u32 bi_length;
2972 struct brcmf_scan_results *list;
2973 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002974 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002975
Arend van Spriel5c36b992012-11-14 18:46:05 -08002976 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002977
Arend van Spriela0f472a2013-04-05 10:57:49 +02002978 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meuleman37a869e2015-10-29 20:33:17 +01002979 brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002980 return -EPERM;
2981 }
2982
2983 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002984 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002985 escan_result_le = (struct brcmf_escan_result_le *) data;
2986 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002987 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002988 goto exit;
2989 }
Hante Meulemane756af52012-09-11 21:18:52 +02002990 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002991 brcmf_err("Invalid bss_count %d: ignoring\n",
2992 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002993 goto exit;
2994 }
2995 bss_info_le = &escan_result_le->bss_info_le;
2996
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002997 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2998 goto exit;
2999
3000 if (!cfg->scan_request) {
3001 brcmf_dbg(SCAN, "result without cfg80211 request\n");
3002 goto exit;
3003 }
3004
Hante Meulemane756af52012-09-11 21:18:52 +02003005 bi_length = le32_to_cpu(bss_info_le->length);
3006 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
3007 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003008 brcmf_err("Invalid bss_info length %d: ignoring\n",
3009 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02003010 goto exit;
3011 }
3012
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003013 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02003014 BIT(NL80211_IFTYPE_ADHOC))) {
3015 if (le16_to_cpu(bss_info_le->capability) &
3016 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003017 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003018 goto exit;
3019 }
3020 }
3021
3022 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003023 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02003024 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003025 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003026 goto exit;
3027 }
3028
3029 for (i = 0; i < list->count; i++) {
3030 bss = bss ? (struct brcmf_bss_info_le *)
3031 ((unsigned char *)bss +
3032 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02003033 if (brcmf_compare_update_same_bss(cfg, bss,
3034 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02003035 goto exit;
3036 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003037 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02003038 bss_info_le, bi_length);
3039 list->version = le32_to_cpu(bss_info_le->version);
3040 list->buflen += bi_length;
3041 list->count++;
3042 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003043 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003044 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
3045 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003046 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003047 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003048 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02003049 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02003050 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003051 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
3052 status);
Hante Meulemane756af52012-09-11 21:18:52 +02003053 }
3054exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003055 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02003056}
3057
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003058static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003059{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003060 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3061 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003062 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3063 /* Init scan_timeout timer */
3064 init_timer(&cfg->escan_timeout);
3065 cfg->escan_timeout.data = (unsigned long) cfg;
3066 cfg->escan_timeout.function = brcmf_escan_timeout;
3067 INIT_WORK(&cfg->escan_timeout_work,
3068 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003069}
3070
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003071static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003072{
3073 if (ms < 1000 / HZ) {
3074 cond_resched();
3075 mdelay(ms);
3076 } else {
3077 msleep(ms);
3078 }
3079}
3080
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003081static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3082 u8 *pattern, u32 patternsize, u8 *mask,
3083 u32 packet_offset)
3084{
3085 struct brcmf_fil_wowl_pattern_le *filter;
3086 u32 masksize;
3087 u32 patternoffset;
3088 u8 *buf;
3089 u32 bufsize;
3090 s32 ret;
3091
3092 masksize = (patternsize + 7) / 8;
3093 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3094
3095 bufsize = sizeof(*filter) + patternsize + masksize;
3096 buf = kzalloc(bufsize, GFP_KERNEL);
3097 if (!buf)
3098 return -ENOMEM;
3099 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3100
3101 memcpy(filter->cmd, cmd, 4);
3102 filter->masksize = cpu_to_le32(masksize);
3103 filter->offset = cpu_to_le32(packet_offset);
3104 filter->patternoffset = cpu_to_le32(patternoffset);
3105 filter->patternsize = cpu_to_le32(patternsize);
3106 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3107
3108 if ((mask) && (masksize))
3109 memcpy(buf + sizeof(*filter), mask, masksize);
3110 if ((pattern) && (patternsize))
3111 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3112
3113 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3114
3115 kfree(buf);
3116 return ret;
3117}
3118
Hante Meulemanaeb64222015-10-29 20:33:19 +01003119#ifdef CONFIG_PM
3120
3121static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3122{
3123 struct brcmf_wowl_wakeind_le wake_ind_le;
3124 struct cfg80211_wowlan_wakeup wakeup_data;
3125 struct cfg80211_wowlan_wakeup *wakeup;
3126 u32 wakeind;
3127 s32 err;
3128
3129 err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
3130 sizeof(wake_ind_le));
3131 if (!err) {
3132 brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
3133 return;
3134 }
3135
3136 wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
3137 if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
3138 BRCMF_WOWL_RETR | BRCMF_WOWL_NET)) {
3139 wakeup = &wakeup_data;
3140 memset(&wakeup_data, 0, sizeof(wakeup_data));
3141 wakeup_data.pattern_idx = -1;
3142
3143 if (wakeind & BRCMF_WOWL_MAGIC) {
3144 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
3145 wakeup_data.magic_pkt = true;
3146 }
3147 if (wakeind & BRCMF_WOWL_DIS) {
3148 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
3149 wakeup_data.disconnect = true;
3150 }
3151 if (wakeind & BRCMF_WOWL_BCN) {
3152 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
3153 wakeup_data.disconnect = true;
3154 }
3155 if (wakeind & BRCMF_WOWL_RETR) {
3156 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
3157 wakeup_data.disconnect = true;
3158 }
3159 if (wakeind & BRCMF_WOWL_NET) {
3160 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
3161 /* For now always map to pattern 0, no API to get
3162 * correct information available at the moment.
3163 */
3164 wakeup_data.pattern_idx = 0;
3165 }
3166 } else {
3167 wakeup = NULL;
3168 }
3169 cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
3170}
3171
3172#else
3173
3174static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3175{
3176}
3177
3178#endif /* CONFIG_PM */
3179
Arend van Spriel5b435de2011-10-05 13:19:03 +02003180static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3181{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003182 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3183 struct net_device *ndev = cfg_to_ndev(cfg);
3184 struct brcmf_if *ifp = netdev_priv(ndev);
3185
Arend van Sprield96b8012012-12-05 15:26:02 +01003186 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003187
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003188 if (cfg->wowl_enabled) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003189 brcmf_report_wowl_wakeind(wiphy, ifp);
3190 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
3191 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003192 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003193 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3194 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003195 cfg->wowl_enabled = false;
3196 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003197 return 0;
3198}
3199
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003200static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3201 struct brcmf_if *ifp,
3202 struct cfg80211_wowlan *wowl)
3203{
3204 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003205 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003206
3207 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3208
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003209 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003210 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3211 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3212
3213 wowl_config = 0;
3214 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003215 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003216 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003217 wowl_config |= BRCMF_WOWL_MAGIC;
3218 if ((wowl->patterns) && (wowl->n_patterns)) {
3219 wowl_config |= BRCMF_WOWL_NET;
3220 for (i = 0; i < wowl->n_patterns; i++) {
3221 brcmf_config_wowl_pattern(ifp, "add",
3222 (u8 *)wowl->patterns[i].pattern,
3223 wowl->patterns[i].pattern_len,
3224 (u8 *)wowl->patterns[i].mask,
3225 wowl->patterns[i].pkt_offset);
3226 }
3227 }
Hante Meulemanaeb64222015-10-29 20:33:19 +01003228 brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear"));
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003229 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3230 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3231 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3232 cfg->wowl_enabled = true;
3233}
3234
Arend van Spriel5b435de2011-10-05 13:19:03 +02003235static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003236 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003237{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003238 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3239 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003240 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003241 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003242
Arend van Sprield96b8012012-12-05 15:26:02 +01003243 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003244
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003245 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003246 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003247 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003248 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003249 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003250
Arend van Spriel7d641072012-10-22 13:55:39 -07003251 /* end any scanning */
3252 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003253 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003254
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003255 if (wowl == NULL) {
3256 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3257 list_for_each_entry(vif, &cfg->vif_list, list) {
3258 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3259 continue;
3260 /* While going to suspend if associated with AP
3261 * disassociate from AP to save power while system is
3262 * in suspended state
3263 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003264 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003265 /* Make sure WPA_Supplicant receives all the event
3266 * generated due to DISASSOC call to the fw to keep
3267 * the state fw and WPA_Supplicant state consistent
3268 */
3269 brcmf_delay(500);
3270 }
3271 /* Configure MPC */
3272 brcmf_set_mpc(ifp, 1);
3273
3274 } else {
3275 /* Configure WOWL paramaters */
3276 brcmf_configure_wowl(cfg, ifp, wowl);
3277 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003278
Arend van Spriel7d641072012-10-22 13:55:39 -07003279exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003280 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003281 /* clear any scanning activity */
3282 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003283 return 0;
3284}
3285
3286static __used s32
Hante Meuleman6c404f32015-12-10 13:43:03 +01003287brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003288{
Hante Meuleman6c404f32015-12-10 13:43:03 +01003289 struct brcmf_pmk_list_le *pmk_list;
3290 int i;
3291 u32 npmk;
3292 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003293
Hante Meuleman6c404f32015-12-10 13:43:03 +01003294 pmk_list = &cfg->pmk_list;
3295 npmk = le32_to_cpu(pmk_list->npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003296
Hante Meuleman6c404f32015-12-10 13:43:03 +01003297 brcmf_dbg(CONN, "No of elements %d\n", npmk);
3298 for (i = 0; i < npmk; i++)
3299 brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003300
Hante Meuleman6c404f32015-12-10 13:43:03 +01003301 err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
3302 sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003303
3304 return err;
3305}
3306
3307static s32
3308brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3309 struct cfg80211_pmksa *pmksa)
3310{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003311 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003312 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003313 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3314 s32 err;
3315 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003316
Arend van Sprield96b8012012-12-05 15:26:02 +01003317 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003318 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003319 return -EIO;
3320
Hante Meuleman6c404f32015-12-10 13:43:03 +01003321 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3322 for (i = 0; i < npmk; i++)
3323 if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003324 break;
Hante Meuleman6c404f32015-12-10 13:43:03 +01003325 if (i < BRCMF_MAXPMKID) {
3326 memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
3327 memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
3328 if (i == npmk) {
3329 npmk++;
3330 cfg->pmk_list.npmk = cpu_to_le32(npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003331 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003332 } else {
3333 brcmf_err("Too many PMKSA entries cached %d\n", npmk);
3334 return -EINVAL;
3335 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003336
Hante Meuleman6c404f32015-12-10 13:43:03 +01003337 brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
3338 for (i = 0; i < WLAN_PMKID_LEN; i += 4)
3339 brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
3340 pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
3341 pmk[npmk].pmkid[i + 3]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003342
Hante Meuleman6c404f32015-12-10 13:43:03 +01003343 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003344
Arend van Sprield96b8012012-12-05 15:26:02 +01003345 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003346 return err;
3347}
3348
3349static s32
3350brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman6c404f32015-12-10 13:43:03 +01003351 struct cfg80211_pmksa *pmksa)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003352{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003353 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003354 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003355 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3356 s32 err;
3357 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003358
Arend van Sprield96b8012012-12-05 15:26:02 +01003359 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003360 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003361 return -EIO;
3362
Hante Meuleman6c404f32015-12-10 13:43:03 +01003363 brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003364
Hante Meuleman6c404f32015-12-10 13:43:03 +01003365 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3366 for (i = 0; i < npmk; i++)
3367 if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003368 break;
3369
Hante Meuleman6c404f32015-12-10 13:43:03 +01003370 if ((npmk > 0) && (i < npmk)) {
3371 for (; i < (npmk - 1); i++) {
3372 memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
3373 memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003374 WLAN_PMKID_LEN);
3375 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003376 memset(&pmk[i], 0, sizeof(*pmk));
3377 cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
3378 } else {
3379 brcmf_err("Cache entry not found\n");
3380 return -EINVAL;
3381 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003382
Hante Meuleman6c404f32015-12-10 13:43:03 +01003383 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003384
Arend van Sprield96b8012012-12-05 15:26:02 +01003385 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003386 return err;
3387
3388}
3389
3390static s32
3391brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3392{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003393 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003394 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003395 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003396
Arend van Sprield96b8012012-12-05 15:26:02 +01003397 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003398 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003399 return -EIO;
3400
Hante Meuleman6c404f32015-12-10 13:43:03 +01003401 memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
3402 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003403
Arend van Sprield96b8012012-12-05 15:26:02 +01003404 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003405 return err;
3406
3407}
3408
Arend van Spriele5806072012-09-19 22:21:08 +02003409/*
3410 * PFN result doesn't have all the info which are
3411 * required by the supplicant
3412 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3413 * via wl_inform_single_bss in the required format. Escan does require the
3414 * scan request in the form of cfg80211_scan_request. For timebeing, create
3415 * cfg80211_scan_request one out of the received PNO event.
3416 */
3417static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003418brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003419 const struct brcmf_event_msg *e, void *data)
3420{
Arend van Spriel19937322012-11-05 16:22:32 -08003421 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003422 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3423 struct cfg80211_scan_request *request = NULL;
3424 struct cfg80211_ssid *ssid = NULL;
3425 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003426 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003427 int err = 0;
3428 int channel_req = 0;
3429 int band = 0;
3430 struct brcmf_pno_scanresults_le *pfn_result;
3431 u32 result_count;
3432 u32 status;
3433
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003434 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003435
Arend van Spriel5c36b992012-11-14 18:46:05 -08003436 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003437 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003438 return 0;
3439 }
3440
3441 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3442 result_count = le32_to_cpu(pfn_result->count);
3443 status = le32_to_cpu(pfn_result->status);
3444
3445 /*
3446 * PFN event is limited to fit 512 bytes so we may get
3447 * multiple NET_FOUND events. For now place a warning here.
3448 */
3449 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003450 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003451 if (result_count > 0) {
3452 int i;
3453
3454 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003455 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3456 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003457 if (!request || !ssid || !channel) {
3458 err = -ENOMEM;
3459 goto out_err;
3460 }
3461
3462 request->wiphy = wiphy;
3463 data += sizeof(struct brcmf_pno_scanresults_le);
3464 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3465
3466 for (i = 0; i < result_count; i++) {
3467 netinfo = &netinfo_start[i];
3468 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003469 brcmf_err("Invalid netinfo ptr. index: %d\n",
3470 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003471 err = -EINVAL;
3472 goto out_err;
3473 }
3474
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003475 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3476 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003477 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3478 ssid[i].ssid_len = netinfo->SSID_len;
3479 request->n_ssids++;
3480
3481 channel_req = netinfo->channel;
3482 if (channel_req <= CH_MAX_2G_CHANNEL)
3483 band = NL80211_BAND_2GHZ;
3484 else
3485 band = NL80211_BAND_5GHZ;
3486 channel[i].center_freq =
3487 ieee80211_channel_to_frequency(channel_req,
3488 band);
3489 channel[i].band = band;
3490 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3491 request->channels[i] = &channel[i];
3492 request->n_channels++;
3493 }
3494
3495 /* assign parsed ssid array */
3496 if (request->n_ssids)
3497 request->ssids = &ssid[0];
3498
Arend van Sprielc1179032012-10-22 13:55:33 -07003499 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003500 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003501 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003502 }
3503
Arend van Sprielc1179032012-10-22 13:55:33 -07003504 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003505 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003506 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003507 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003508 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003509 goto out_err;
3510 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003511 cfg->sched_escan = true;
3512 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003513 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003514 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003515 goto out_err;
3516 }
3517
3518 kfree(ssid);
3519 kfree(channel);
3520 kfree(request);
3521 return 0;
3522
3523out_err:
3524 kfree(ssid);
3525 kfree(channel);
3526 kfree(request);
3527 cfg80211_sched_scan_stopped(wiphy);
3528 return err;
3529}
3530
Arend van Spriele5806072012-09-19 22:21:08 +02003531static int brcmf_dev_pno_clean(struct net_device *ndev)
3532{
Arend van Spriele5806072012-09-19 22:21:08 +02003533 int ret;
3534
3535 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003536 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003537 if (ret == 0) {
3538 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003539 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3540 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003541 }
3542 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003543 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003544
3545 return ret;
3546}
3547
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003548static int brcmf_dev_pno_config(struct brcmf_if *ifp,
3549 struct cfg80211_sched_scan_request *request)
Arend van Spriele5806072012-09-19 22:21:08 +02003550{
3551 struct brcmf_pno_param_le pfn_param;
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003552 struct brcmf_pno_macaddr_le pfn_mac;
3553 s32 err;
3554 u8 *mac_mask;
3555 int i;
Arend van Spriele5806072012-09-19 22:21:08 +02003556
3557 memset(&pfn_param, 0, sizeof(pfn_param));
3558 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3559
3560 /* set extra pno params */
3561 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3562 pfn_param.repeat = BRCMF_PNO_REPEAT;
3563 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3564
3565 /* set up pno scan fr */
3566 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3567
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003568 err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
3569 sizeof(pfn_param));
3570 if (err) {
3571 brcmf_err("pfn_set failed, err=%d\n", err);
3572 return err;
3573 }
3574
3575 /* Find out if mac randomization should be turned on */
3576 if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
3577 return 0;
3578
3579 pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
3580 pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
3581
3582 memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN);
3583 mac_mask = request->mac_addr_mask;
3584 for (i = 0; i < ETH_ALEN; i++) {
3585 pfn_mac.mac[i] &= mac_mask[i];
3586 pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
3587 }
3588 /* Clear multi bit */
3589 pfn_mac.mac[0] &= 0xFE;
3590 /* Set locally administered */
3591 pfn_mac.mac[0] |= 0x02;
3592
3593 err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
3594 sizeof(pfn_mac));
3595 if (err)
3596 brcmf_err("pfn_macaddr failed, err=%d\n", err);
3597
3598 return err;
Arend van Spriele5806072012-09-19 22:21:08 +02003599}
3600
3601static int
3602brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3603 struct net_device *ndev,
3604 struct cfg80211_sched_scan_request *request)
3605{
Arend van Sprielc1179032012-10-22 13:55:33 -07003606 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003607 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003608 struct brcmf_pno_net_param_le pfn;
3609 int i;
3610 int ret = 0;
3611
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003612 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003613 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003614 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003615 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003616 return -EAGAIN;
3617 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003618 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3619 brcmf_err("Scanning suppressed: status (%lu)\n",
3620 cfg->scan_status);
3621 return -EAGAIN;
3622 }
Arend van Spriele5806072012-09-19 22:21:08 +02003623
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003624 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003625 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003626 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003627 return -EINVAL;
3628 }
3629
3630 if (request->n_ssids > 0) {
3631 for (i = 0; i < request->n_ssids; i++) {
3632 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003633 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3634 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003635
3636 /*
3637 * match_set ssids is a supert set of n_ssid list,
3638 * so we need not add these set seperately.
3639 */
3640 }
3641 }
3642
3643 if (request->n_match_sets > 0) {
3644 /* clean up everything */
3645 ret = brcmf_dev_pno_clean(ndev);
3646 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003647 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003648 return ret;
3649 }
3650
3651 /* configure pno */
Hante Meuleman48ed16e2016-01-02 09:41:38 +01003652 if (brcmf_dev_pno_config(ifp, request))
Arend van Spriele5806072012-09-19 22:21:08 +02003653 return -EINVAL;
Arend van Spriele5806072012-09-19 22:21:08 +02003654
3655 /* configure each match set */
3656 for (i = 0; i < request->n_match_sets; i++) {
3657 struct cfg80211_ssid *ssid;
3658 u32 ssid_len;
3659
3660 ssid = &request->match_sets[i].ssid;
3661 ssid_len = ssid->ssid_len;
3662
3663 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003664 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003665 continue;
3666 }
3667 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3668 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3669 pfn.wsec = cpu_to_le32(0);
3670 pfn.infra = cpu_to_le32(1);
3671 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3672 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3673 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003674 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003675 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003676 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3677 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003678 }
3679 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003680 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003681 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003682 return -EINVAL;
3683 }
3684 } else {
3685 return -EINVAL;
3686 }
3687
3688 return 0;
3689}
3690
3691static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3692 struct net_device *ndev)
3693{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003694 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003695
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003696 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003697 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003698 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003699 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003700 return 0;
3701}
Arend van Spriele5806072012-09-19 22:21:08 +02003702
Hante Meuleman1f170112013-02-06 18:40:38 +01003703static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003704{
3705 s32 err;
3706
3707 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003708 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003709 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003710 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003711 return err;
3712 }
3713 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003714 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003715 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003716 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003717 return err;
3718 }
3719 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003720 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003721 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003722 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003723 return err;
3724 }
3725
3726 return 0;
3727}
3728
3729static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3730{
3731 if (is_rsn_ie)
3732 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3733
3734 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3735}
3736
3737static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003738brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003739 const struct brcmf_vs_tlv *wpa_ie,
3740 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003741{
3742 u32 auth = 0; /* d11 open authentication */
3743 u16 count;
3744 s32 err = 0;
3745 s32 len = 0;
3746 u32 i;
3747 u32 wsec;
3748 u32 pval = 0;
3749 u32 gval = 0;
3750 u32 wpa_auth = 0;
3751 u32 offset;
3752 u8 *data;
3753 u16 rsn_cap;
3754 u32 wme_bss_disable;
3755
Arend van Sprield96b8012012-12-05 15:26:02 +01003756 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003757 if (wpa_ie == NULL)
3758 goto exit;
3759
3760 len = wpa_ie->len + TLV_HDR_LEN;
3761 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003762 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003763 if (!is_rsn_ie)
3764 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003765 else
3766 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003767
3768 /* check for multicast cipher suite */
3769 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3770 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003771 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003772 goto exit;
3773 }
3774
3775 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3776 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003777 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003778 goto exit;
3779 }
3780 offset += TLV_OUI_LEN;
3781
3782 /* pick up multicast cipher */
3783 switch (data[offset]) {
3784 case WPA_CIPHER_NONE:
3785 gval = 0;
3786 break;
3787 case WPA_CIPHER_WEP_40:
3788 case WPA_CIPHER_WEP_104:
3789 gval = WEP_ENABLED;
3790 break;
3791 case WPA_CIPHER_TKIP:
3792 gval = TKIP_ENABLED;
3793 break;
3794 case WPA_CIPHER_AES_CCM:
3795 gval = AES_ENABLED;
3796 break;
3797 default:
3798 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003799 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003800 goto exit;
3801 }
3802
3803 offset++;
3804 /* walk thru unicast cipher list and pick up what we recognize */
3805 count = data[offset] + (data[offset + 1] << 8);
3806 offset += WPA_IE_SUITE_COUNT_LEN;
3807 /* Check for unicast suite(s) */
3808 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3809 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003810 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003811 goto exit;
3812 }
3813 for (i = 0; i < count; i++) {
3814 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3815 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003816 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003817 goto exit;
3818 }
3819 offset += TLV_OUI_LEN;
3820 switch (data[offset]) {
3821 case WPA_CIPHER_NONE:
3822 break;
3823 case WPA_CIPHER_WEP_40:
3824 case WPA_CIPHER_WEP_104:
3825 pval |= WEP_ENABLED;
3826 break;
3827 case WPA_CIPHER_TKIP:
3828 pval |= TKIP_ENABLED;
3829 break;
3830 case WPA_CIPHER_AES_CCM:
3831 pval |= AES_ENABLED;
3832 break;
3833 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003834 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003835 }
3836 offset++;
3837 }
3838 /* walk thru auth management suite list and pick up what we recognize */
3839 count = data[offset] + (data[offset + 1] << 8);
3840 offset += WPA_IE_SUITE_COUNT_LEN;
3841 /* Check for auth key management suite(s) */
3842 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3843 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003844 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003845 goto exit;
3846 }
3847 for (i = 0; i < count; i++) {
3848 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3849 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003850 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003851 goto exit;
3852 }
3853 offset += TLV_OUI_LEN;
3854 switch (data[offset]) {
3855 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003856 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003857 wpa_auth |= WPA_AUTH_NONE;
3858 break;
3859 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003860 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003861 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3862 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3863 break;
3864 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003865 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003866 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3867 (wpa_auth |= WPA_AUTH_PSK);
3868 break;
3869 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003870 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003871 }
3872 offset++;
3873 }
3874
3875 if (is_rsn_ie) {
3876 wme_bss_disable = 1;
3877 if ((offset + RSN_CAP_LEN) <= len) {
3878 rsn_cap = data[offset] + (data[offset + 1] << 8);
3879 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3880 wme_bss_disable = 0;
3881 }
3882 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003883 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003884 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003885 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003886 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003887 goto exit;
3888 }
3889 }
3890 /* FOR WPS , set SES_OW_ENABLED */
3891 wsec = (pval | gval | SES_OW_ENABLED);
3892
3893 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003894 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003895 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003896 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003897 goto exit;
3898 }
3899 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003900 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003901 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003902 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003903 goto exit;
3904 }
3905 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003906 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003907 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003908 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003909 goto exit;
3910 }
3911
3912exit:
3913 return err;
3914}
3915
3916static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003917brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003918 struct parsed_vndr_ies *vndr_ies)
3919{
Hante Meuleman1a873342012-09-27 14:17:54 +02003920 struct brcmf_vs_tlv *vndrie;
3921 struct brcmf_tlv *ie;
3922 struct parsed_vndr_ie_info *parsed_info;
3923 s32 remaining_len;
3924
3925 remaining_len = (s32)vndr_ie_len;
3926 memset(vndr_ies, 0, sizeof(*vndr_ies));
3927
3928 ie = (struct brcmf_tlv *)vndr_ie_buf;
3929 while (ie) {
3930 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3931 goto next;
3932 vndrie = (struct brcmf_vs_tlv *)ie;
3933 /* len should be bigger than OUI length + one */
3934 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003935 brcmf_err("invalid vndr ie. length is too small %d\n",
3936 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003937 goto next;
3938 }
3939 /* if wpa or wme ie, do not add ie */
3940 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3941 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3942 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003943 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003944 goto next;
3945 }
3946
3947 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3948
3949 /* save vndr ie information */
3950 parsed_info->ie_ptr = (char *)vndrie;
3951 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3952 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3953
3954 vndr_ies->count++;
3955
Arend van Sprield96b8012012-12-05 15:26:02 +01003956 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3957 parsed_info->vndrie.oui[0],
3958 parsed_info->vndrie.oui[1],
3959 parsed_info->vndrie.oui[2],
3960 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003961
Arend van Spriel9f440b72013-02-08 15:53:36 +01003962 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003963 break;
3964next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003965 remaining_len -= (ie->len + TLV_HDR_LEN);
3966 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003967 ie = NULL;
3968 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003969 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3970 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003971 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003972 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003973}
3974
3975static u32
3976brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3977{
3978
Hante Meuleman1a873342012-09-27 14:17:54 +02003979 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3980 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3981
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303982 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003983
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303984 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003985
3986 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3987
3988 return ie_len + VNDR_IE_HDR_SIZE;
3989}
3990
Arend van Spriel1332e262012-11-05 16:22:18 -08003991s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3992 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003993{
Arend van Spriel1332e262012-11-05 16:22:18 -08003994 struct brcmf_if *ifp;
3995 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003996 s32 err = 0;
3997 u8 *iovar_ie_buf;
3998 u8 *curr_ie_buf;
3999 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004000 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07004001 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004002 u32 del_add_ie_buf_len = 0;
4003 u32 total_ie_buf_len = 0;
4004 u32 parsed_ie_buf_len = 0;
4005 struct parsed_vndr_ies old_vndr_ies;
4006 struct parsed_vndr_ies new_vndr_ies;
4007 struct parsed_vndr_ie_info *vndrie_info;
4008 s32 i;
4009 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004010 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004011
Arend van Spriel1332e262012-11-05 16:22:18 -08004012 if (!vif)
4013 return -ENODEV;
4014 ifp = vif->ifp;
4015 saved_ie = &vif->saved_ie;
4016
Hante Meuleman37a869e2015-10-29 20:33:17 +01004017 brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
4018 pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02004019 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4020 if (!iovar_ie_buf)
4021 return -ENOMEM;
4022 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01004023 switch (pktflag) {
4024 case BRCMF_VNDR_IE_PRBREQ_FLAG:
4025 mgmt_ie_buf = saved_ie->probe_req_ie;
4026 mgmt_ie_len = &saved_ie->probe_req_ie_len;
4027 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
4028 break;
4029 case BRCMF_VNDR_IE_PRBRSP_FLAG:
4030 mgmt_ie_buf = saved_ie->probe_res_ie;
4031 mgmt_ie_len = &saved_ie->probe_res_ie_len;
4032 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
4033 break;
4034 case BRCMF_VNDR_IE_BEACON_FLAG:
4035 mgmt_ie_buf = saved_ie->beacon_ie;
4036 mgmt_ie_len = &saved_ie->beacon_ie_len;
4037 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
4038 break;
4039 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
4040 mgmt_ie_buf = saved_ie->assoc_req_ie;
4041 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
4042 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
4043 break;
4044 default:
4045 err = -EPERM;
4046 brcmf_err("not suitable type\n");
4047 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004048 }
4049
4050 if (vndr_ie_len > mgmt_ie_buf_len) {
4051 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004052 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004053 goto exit;
4054 }
4055
4056 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
4057 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
4058 ptr = curr_ie_buf;
4059 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
4060 for (i = 0; i < new_vndr_ies.count; i++) {
4061 vndrie_info = &new_vndr_ies.ie_info[i];
4062 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
4063 vndrie_info->ie_len);
4064 parsed_ie_buf_len += vndrie_info->ie_len;
4065 }
4066 }
4067
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004068 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004069 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
4070 (memcmp(mgmt_ie_buf, curr_ie_buf,
4071 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004072 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004073 goto exit;
4074 }
4075
4076 /* parse old vndr_ie */
4077 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
4078
4079 /* make a command to delete old ie */
4080 for (i = 0; i < old_vndr_ies.count; i++) {
4081 vndrie_info = &old_vndr_ies.ie_info[i];
4082
Arend van Sprield96b8012012-12-05 15:26:02 +01004083 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4084 vndrie_info->vndrie.id,
4085 vndrie_info->vndrie.len,
4086 vndrie_info->vndrie.oui[0],
4087 vndrie_info->vndrie.oui[1],
4088 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004089
4090 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4091 vndrie_info->ie_ptr,
4092 vndrie_info->ie_len,
4093 "del");
4094 curr_ie_buf += del_add_ie_buf_len;
4095 total_ie_buf_len += del_add_ie_buf_len;
4096 }
4097 }
4098
4099 *mgmt_ie_len = 0;
4100 /* Add if there is any extra IE */
4101 if (mgmt_ie_buf && parsed_ie_buf_len) {
4102 ptr = mgmt_ie_buf;
4103
4104 remained_buf_len = mgmt_ie_buf_len;
4105
4106 /* make a command to add new ie */
4107 for (i = 0; i < new_vndr_ies.count; i++) {
4108 vndrie_info = &new_vndr_ies.ie_info[i];
4109
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004110 /* verify remained buf size before copy data */
4111 if (remained_buf_len < (vndrie_info->vndrie.len +
4112 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004113 brcmf_err("no space in mgmt_ie_buf: len left %d",
4114 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004115 break;
4116 }
4117 remained_buf_len -= (vndrie_info->ie_len +
4118 VNDR_IE_VSIE_OFFSET);
4119
Arend van Sprield96b8012012-12-05 15:26:02 +01004120 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4121 vndrie_info->vndrie.id,
4122 vndrie_info->vndrie.len,
4123 vndrie_info->vndrie.oui[0],
4124 vndrie_info->vndrie.oui[1],
4125 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004126
4127 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4128 vndrie_info->ie_ptr,
4129 vndrie_info->ie_len,
4130 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02004131
4132 /* save the parsed IE in wl struct */
4133 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4134 vndrie_info->ie_len);
4135 *mgmt_ie_len += vndrie_info->ie_len;
4136
4137 curr_ie_buf += del_add_ie_buf_len;
4138 total_ie_buf_len += del_add_ie_buf_len;
4139 }
4140 }
4141 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004142 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004143 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004144 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004145 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004146 }
4147
4148exit:
4149 kfree(iovar_ie_buf);
4150 return err;
4151}
4152
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004153s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4154{
4155 s32 pktflags[] = {
4156 BRCMF_VNDR_IE_PRBREQ_FLAG,
4157 BRCMF_VNDR_IE_PRBRSP_FLAG,
4158 BRCMF_VNDR_IE_BEACON_FLAG
4159 };
4160 int i;
4161
4162 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4163 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4164
4165 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4166 return 0;
4167}
4168
Hante Meuleman1a873342012-09-27 14:17:54 +02004169static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004170brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4171 struct cfg80211_beacon_data *beacon)
4172{
4173 s32 err;
4174
4175 /* Set Beacon IEs to FW */
4176 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4177 beacon->tail, beacon->tail_len);
4178 if (err) {
4179 brcmf_err("Set Beacon IE Failed\n");
4180 return err;
4181 }
4182 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4183
4184 /* Set Probe Response IEs to FW */
4185 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4186 beacon->proberesp_ies,
4187 beacon->proberesp_ies_len);
4188 if (err)
4189 brcmf_err("Set Probe Resp IE Failed\n");
4190 else
4191 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4192
4193 return err;
4194}
4195
4196static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004197brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4198 struct cfg80211_ap_settings *settings)
4199{
4200 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004201 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004202 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004203 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004204 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004205 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004206 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004207 const struct brcmf_tlv *rsn_ie;
4208 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004209 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004210 enum nl80211_iftype dev_role;
4211 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004212 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004213 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004214 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004215
Arend van Spriel06c01582014-05-12 10:47:37 +02004216 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4217 settings->chandef.chan->hw_value,
4218 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004219 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004220 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4221 settings->ssid, settings->ssid_len, settings->auth_type,
4222 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004223 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004224 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004225
Arend van Spriel98027762014-12-21 12:43:53 +01004226 /* store current 11d setting */
4227 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4228 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4229 settings->beacon.tail_len,
4230 WLAN_EID_COUNTRY);
4231 is_11d = country_ie ? 1 : 0;
4232
Hante Meuleman1a873342012-09-27 14:17:54 +02004233 memset(&ssid_le, 0, sizeof(ssid_le));
4234 if (settings->ssid == NULL || settings->ssid_len == 0) {
4235 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4236 ssid_ie = brcmf_parse_tlvs(
4237 (u8 *)&settings->beacon.head[ie_offset],
4238 settings->beacon.head_len - ie_offset,
4239 WLAN_EID_SSID);
4240 if (!ssid_ie)
4241 return -EINVAL;
4242
4243 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4244 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004245 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004246 } else {
4247 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4248 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4249 }
4250
Hante Meulemana44aa402014-12-03 21:05:33 +01004251 if (!mbss) {
4252 brcmf_set_mpc(ifp, 0);
4253 brcmf_configure_arp_offload(ifp, false);
4254 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004255
4256 /* find the RSN_IE */
4257 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4258 settings->beacon.tail_len, WLAN_EID_RSN);
4259
4260 /* find the WPA_IE */
4261 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4262 settings->beacon.tail_len);
4263
Hante Meuleman1a873342012-09-27 14:17:54 +02004264 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004265 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004266 if (wpa_ie != NULL) {
4267 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004268 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004269 if (err < 0)
4270 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004271 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004272 struct brcmf_vs_tlv *tmp_ie;
4273
4274 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4275
Hante Meuleman1a873342012-09-27 14:17:54 +02004276 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004277 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004278 if (err < 0)
4279 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004280 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004281 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004282 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004283 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004284 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004285
Hante Meulemana0f07952013-02-08 15:53:47 +01004286 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004287
Hante Meulemana44aa402014-12-03 21:05:33 +01004288 if (!mbss) {
4289 chanspec = chandef_to_chanspec(&cfg->d11inf,
4290 &settings->chandef);
4291 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004292 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004293 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4294 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004295 goto exit;
4296 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004297
Arend van Spriel98027762014-12-21 12:43:53 +01004298 if (is_11d != ifp->vif->is_11d) {
4299 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4300 is_11d);
4301 if (err < 0) {
4302 brcmf_err("Regulatory Set Error, %d\n", err);
4303 goto exit;
4304 }
4305 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004306 if (settings->beacon_interval) {
4307 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4308 settings->beacon_interval);
4309 if (err < 0) {
4310 brcmf_err("Beacon Interval Set Error, %d\n",
4311 err);
4312 goto exit;
4313 }
4314 }
4315 if (settings->dtim_period) {
4316 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4317 settings->dtim_period);
4318 if (err < 0) {
4319 brcmf_err("DTIM Interval Set Error, %d\n", err);
4320 goto exit;
4321 }
4322 }
4323
Hante Meuleman8abffd82015-10-29 20:33:16 +01004324 if ((dev_role == NL80211_IFTYPE_AP) &&
4325 ((ifp->ifidx == 0) ||
4326 !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004327 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4328 if (err < 0) {
4329 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4330 goto exit;
4331 }
4332 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4333 }
4334
4335 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004336 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004337 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004338 goto exit;
4339 }
Arend van Spriel98027762014-12-21 12:43:53 +01004340 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4341 /* Multiple-BSS should use same 11d configuration */
4342 err = -EINVAL;
4343 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004344 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004345 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004346 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4347 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4348
Hante Meulemana0f07952013-02-08 15:53:47 +01004349 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4350 if (err < 0) {
4351 brcmf_err("setting AP mode failed %d\n", err);
4352 goto exit;
4353 }
4354 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4355 if (err < 0) {
4356 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4357 goto exit;
4358 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004359 /* On DOWN the firmware removes the WEP keys, reconfigure
4360 * them if they were set.
4361 */
4362 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004363
4364 memset(&join_params, 0, sizeof(join_params));
4365 /* join parameters starts with ssid */
4366 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4367 /* create softap */
4368 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4369 &join_params, sizeof(join_params));
4370 if (err < 0) {
4371 brcmf_err("SET SSID error (%d)\n", err);
4372 goto exit;
4373 }
4374 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4375 } else {
4376 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4377 sizeof(ssid_le));
4378 if (err < 0) {
4379 brcmf_err("setting ssid failed %d\n", err);
4380 goto exit;
4381 }
Hante Meuleman37a869e2015-10-29 20:33:17 +01004382 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meulemana0f07952013-02-08 15:53:47 +01004383 bss_enable.enable = cpu_to_le32(1);
4384 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4385 sizeof(bss_enable));
4386 if (err < 0) {
4387 brcmf_err("bss_enable config failed %d\n", err);
4388 goto exit;
4389 }
4390
4391 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4392 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004393 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004394 brcmf_net_setcarrier(ifp, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004395
4396exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004397 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004398 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004399 brcmf_configure_arp_offload(ifp, true);
4400 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004401 return err;
4402}
4403
4404static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4405{
Arend van Sprielc1179032012-10-22 13:55:33 -07004406 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004407 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004408 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004409 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004410
Arend van Sprield96b8012012-12-05 15:26:02 +01004411 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004412
Hante Meuleman426d0a52013-02-08 15:53:53 +01004413 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004414 /* Due to most likely deauths outstanding we sleep */
4415 /* first to make sure they get processed by fw. */
4416 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004417
Hante Meulemana44aa402014-12-03 21:05:33 +01004418 if (ifp->vif->mbss) {
4419 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4420 return err;
4421 }
4422
Hante Meuleman5c33a942013-04-02 21:06:18 +02004423 memset(&join_params, 0, sizeof(join_params));
4424 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4425 &join_params, sizeof(join_params));
4426 if (err < 0)
4427 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004428 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004429 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004430 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004431 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4432 if (err < 0)
4433 brcmf_err("setting AP mode failed %d\n", err);
4434 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4435 if (err < 0)
4436 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004437 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4438 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004439 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4440 ifp->vif->is_11d);
4441 if (err < 0)
4442 brcmf_err("restoring REGULATORY setting failed %d\n",
4443 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004444 /* Bring device back up so it can be used again */
4445 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4446 if (err < 0)
4447 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004448 } else {
Hante Meuleman37a869e2015-10-29 20:33:17 +01004449 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004450 bss_enable.enable = cpu_to_le32(0);
4451 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4452 sizeof(bss_enable));
4453 if (err < 0)
4454 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004455 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004456 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004457 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004458 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004459 brcmf_net_setcarrier(ifp, false);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004460
Hante Meuleman1a873342012-09-27 14:17:54 +02004461 return err;
4462}
4463
Hante Meulemana0f07952013-02-08 15:53:47 +01004464static s32
4465brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4466 struct cfg80211_beacon_data *info)
4467{
Hante Meulemana0f07952013-02-08 15:53:47 +01004468 struct brcmf_if *ifp = netdev_priv(ndev);
4469 s32 err;
4470
4471 brcmf_dbg(TRACE, "Enter\n");
4472
Hante Meulemana0f07952013-02-08 15:53:47 +01004473 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4474
4475 return err;
4476}
4477
Hante Meuleman1a873342012-09-27 14:17:54 +02004478static int
4479brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004480 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004481{
Hante Meulemana0f07952013-02-08 15:53:47 +01004482 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004483 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004484 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004485 s32 err;
4486
Jouni Malinen89c771e2014-10-10 20:52:40 +03004487 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004488 return -EFAULT;
4489
Jouni Malinen89c771e2014-10-10 20:52:40 +03004490 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004491
Hante Meulemana0f07952013-02-08 15:53:47 +01004492 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4493 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004494 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004495 return -EIO;
4496
Jouni Malinen89c771e2014-10-10 20:52:40 +03004497 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004498 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004499 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004500 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004501 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004502 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004503
Arend van Sprield96b8012012-12-05 15:26:02 +01004504 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004505 return err;
4506}
4507
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004508static int
4509brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4510 const u8 *mac, struct station_parameters *params)
4511{
4512 struct brcmf_if *ifp = netdev_priv(ndev);
4513 s32 err;
4514
4515 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4516 params->sta_flags_mask, params->sta_flags_set);
4517
4518 /* Ignore all 00 MAC */
4519 if (is_zero_ether_addr(mac))
4520 return 0;
4521
4522 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4523 return 0;
4524
4525 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4526 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4527 (void *)mac, ETH_ALEN);
4528 else
4529 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4530 (void *)mac, ETH_ALEN);
4531 if (err < 0)
4532 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4533
4534 return err;
4535}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004536
4537static void
4538brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4539 struct wireless_dev *wdev,
4540 u16 frame_type, bool reg)
4541{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004542 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004543 u16 mgmt_type;
4544
4545 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4546
4547 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004548 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004549 if (reg)
4550 vif->mgmt_rx_reg |= BIT(mgmt_type);
4551 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004552 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004553}
4554
4555
4556static int
4557brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004558 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004559{
4560 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004561 struct ieee80211_channel *chan = params->chan;
4562 const u8 *buf = params->buf;
4563 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004564 const struct ieee80211_mgmt *mgmt;
4565 struct brcmf_cfg80211_vif *vif;
4566 s32 err = 0;
4567 s32 ie_offset;
4568 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004569 struct brcmf_fil_action_frame_le *action_frame;
4570 struct brcmf_fil_af_params_le *af_params;
4571 bool ack;
4572 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004573 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004574
4575 brcmf_dbg(TRACE, "Enter\n");
4576
4577 *cookie = 0;
4578
4579 mgmt = (const struct ieee80211_mgmt *)buf;
4580
Hante Meulemana0f07952013-02-08 15:53:47 +01004581 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4582 brcmf_err("Driver only allows MGMT packet type\n");
4583 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004584 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004585
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004586 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4587
Hante Meulemana0f07952013-02-08 15:53:47 +01004588 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4589 /* Right now the only reason to get a probe response */
4590 /* is for p2p listen response or for p2p GO from */
4591 /* wpa_supplicant. Unfortunately the probe is send */
4592 /* on primary ndev, while dongle wants it on the p2p */
4593 /* vif. Since this is only reason for a probe */
4594 /* response to be sent, the vif is taken from cfg. */
4595 /* If ever desired to send proberesp for non p2p */
4596 /* response then data should be checked for */
4597 /* "DIRECT-". Note in future supplicant will take */
4598 /* dedicated p2p wdev to do this and then this 'hack'*/
4599 /* is not needed anymore. */
4600 ie_offset = DOT11_MGMT_HDR_LEN +
4601 DOT11_BCN_PRB_FIXED_LEN;
4602 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004603 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4604 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4605 err = brcmf_vif_set_mgmt_ie(vif,
4606 BRCMF_VNDR_IE_PRBRSP_FLAG,
4607 &buf[ie_offset],
4608 ie_len);
4609 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4610 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004611 } else if (ieee80211_is_action(mgmt->frame_control)) {
4612 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4613 if (af_params == NULL) {
4614 brcmf_err("unable to allocate frame\n");
4615 err = -ENOMEM;
4616 goto exit;
4617 }
4618 action_frame = &af_params->action_frame;
4619 /* Add the packet Id */
4620 action_frame->packet_id = cpu_to_le32(*cookie);
4621 /* Add BSSID */
4622 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4623 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4624 /* Add the length exepted for 802.11 header */
4625 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004626 /* Add the channel. Use the one specified as parameter if any or
4627 * the current one (got from the firmware) otherwise
4628 */
4629 if (chan)
4630 freq = chan->center_freq;
4631 else
4632 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4633 &freq);
4634 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004635 af_params->channel = cpu_to_le32(chan_nr);
4636
4637 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4638 le16_to_cpu(action_frame->len));
4639
4640 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004641 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004642
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004643 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004644 af_params);
4645
4646 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4647 GFP_KERNEL);
4648 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004649 } else {
4650 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4651 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4652 }
4653
Hante Meuleman18e2f612013-02-08 15:53:49 +01004654exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004655 return err;
4656}
4657
4658
4659static int
4660brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4661 struct wireless_dev *wdev,
4662 u64 cookie)
4663{
4664 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4665 struct brcmf_cfg80211_vif *vif;
4666 int err = 0;
4667
4668 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4669
4670 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4671 if (vif == NULL) {
4672 brcmf_err("No p2p device available for probe response\n");
4673 err = -ENODEV;
4674 goto exit;
4675 }
4676 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4677exit:
4678 return err;
4679}
4680
Piotr Haber61730d42013-04-23 12:53:12 +02004681static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4682 struct wireless_dev *wdev,
4683 enum nl80211_crit_proto_id proto,
4684 u16 duration)
4685{
4686 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4687 struct brcmf_cfg80211_vif *vif;
4688
4689 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4690
4691 /* only DHCP support for now */
4692 if (proto != NL80211_CRIT_PROTO_DHCP)
4693 return -EINVAL;
4694
4695 /* suppress and abort scanning */
4696 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4697 brcmf_abort_scanning(cfg);
4698
4699 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4700}
4701
4702static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4703 struct wireless_dev *wdev)
4704{
4705 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4706 struct brcmf_cfg80211_vif *vif;
4707
4708 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4709
4710 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4711 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4712}
4713
Hante Meuleman70b7d942014-07-30 13:20:07 +02004714static s32
4715brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4716 const struct brcmf_event_msg *e, void *data)
4717{
4718 switch (e->reason) {
4719 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4720 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4721 break;
4722 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4723 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4724 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4725 break;
4726 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4727 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4728 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4729 break;
4730 }
4731
4732 return 0;
4733}
4734
Arend van Spriel89c2f382013-08-10 12:27:25 +02004735static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4736{
4737 int ret;
4738
4739 switch (oper) {
4740 case NL80211_TDLS_DISCOVERY_REQ:
4741 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4742 break;
4743 case NL80211_TDLS_SETUP:
4744 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4745 break;
4746 case NL80211_TDLS_TEARDOWN:
4747 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4748 break;
4749 default:
4750 brcmf_err("unsupported operation: %d\n", oper);
4751 ret = -EOPNOTSUPP;
4752 }
4753 return ret;
4754}
4755
4756static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004757 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004758 enum nl80211_tdls_operation oper)
4759{
4760 struct brcmf_if *ifp;
4761 struct brcmf_tdls_iovar_le info;
4762 int ret = 0;
4763
4764 ret = brcmf_convert_nl80211_tdls_oper(oper);
4765 if (ret < 0)
4766 return ret;
4767
4768 ifp = netdev_priv(ndev);
4769 memset(&info, 0, sizeof(info));
4770 info.mode = (u8)ret;
4771 if (peer)
4772 memcpy(info.ea, peer, ETH_ALEN);
4773
4774 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4775 &info, sizeof(info));
4776 if (ret < 0)
4777 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4778
4779 return ret;
4780}
4781
Arend van Spriel5b435de2011-10-05 13:19:03 +02004782static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004783 .add_virtual_intf = brcmf_cfg80211_add_iface,
4784 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004785 .change_virtual_intf = brcmf_cfg80211_change_iface,
4786 .scan = brcmf_cfg80211_scan,
4787 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4788 .join_ibss = brcmf_cfg80211_join_ibss,
4789 .leave_ibss = brcmf_cfg80211_leave_ibss,
4790 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02004791 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004792 .set_tx_power = brcmf_cfg80211_set_tx_power,
4793 .get_tx_power = brcmf_cfg80211_get_tx_power,
4794 .add_key = brcmf_cfg80211_add_key,
4795 .del_key = brcmf_cfg80211_del_key,
4796 .get_key = brcmf_cfg80211_get_key,
4797 .set_default_key = brcmf_cfg80211_config_default_key,
4798 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4799 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004800 .connect = brcmf_cfg80211_connect,
4801 .disconnect = brcmf_cfg80211_disconnect,
4802 .suspend = brcmf_cfg80211_suspend,
4803 .resume = brcmf_cfg80211_resume,
4804 .set_pmksa = brcmf_cfg80211_set_pmksa,
4805 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004806 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004807 .start_ap = brcmf_cfg80211_start_ap,
4808 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004809 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004810 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004811 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004812 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4813 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004814 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4815 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4816 .remain_on_channel = brcmf_p2p_remain_on_channel,
4817 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004818 .start_p2p_device = brcmf_p2p_start_device,
4819 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004820 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4821 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004822 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004823};
4824
Arend van Spriel3eacf862012-10-22 13:55:30 -07004825struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004826 enum nl80211_iftype type,
4827 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004828{
Hante Meulemana44aa402014-12-03 21:05:33 +01004829 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004830 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004831 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004832
Arend van Spriel33a6b152013-02-08 15:53:39 +01004833 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004834 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004835 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4836 if (!vif)
4837 return ERR_PTR(-ENOMEM);
4838
4839 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004840 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004841
Arend van Spriel3eacf862012-10-22 13:55:30 -07004842 vif->pm_block = pm_block;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004843
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004844 brcmf_init_prof(&vif->profile);
4845
Hante Meulemana44aa402014-12-03 21:05:33 +01004846 if (type == NL80211_IFTYPE_AP) {
4847 mbss = false;
4848 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4849 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4850 mbss = true;
4851 break;
4852 }
4853 }
4854 vif->mbss = mbss;
4855 }
4856
Arend van Spriel3eacf862012-10-22 13:55:30 -07004857 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004858 return vif;
4859}
4860
Arend van Spriel427dec52014-01-06 12:40:47 +01004861void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004862{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004863 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004864 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004865}
4866
Arend van Spriel9df4d542014-01-06 12:40:49 +01004867void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4868{
4869 struct brcmf_cfg80211_vif *vif;
4870 struct brcmf_if *ifp;
4871
4872 ifp = netdev_priv(ndev);
4873 vif = ifp->vif;
4874
Arend van Spriel95ef1232015-08-26 22:15:04 +02004875 if (vif)
4876 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01004877 free_netdev(ndev);
4878}
4879
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004880static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004881{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004882 u32 event = e->event_code;
4883 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004884
4885 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004886 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004887 return true;
4888 }
4889
4890 return false;
4891}
4892
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004893static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004894{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004895 u32 event = e->event_code;
4896 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004897
Hante Meuleman68ca3952014-02-25 20:30:26 +01004898 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4899 (event == BRCMF_E_DISASSOC_IND) ||
4900 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004901 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004902 return true;
4903 }
4904 return false;
4905}
4906
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004907static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004908 const struct brcmf_event_msg *e)
4909{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004910 u32 event = e->event_code;
4911 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004912
4913 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004914 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4915 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004916 return true;
4917 }
4918
4919 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004920 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004921 return true;
4922 }
4923
4924 return false;
4925}
4926
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004927static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004928{
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
4931 kfree(conn_info->req_ie);
4932 conn_info->req_ie = NULL;
4933 conn_info->req_ie_len = 0;
4934 kfree(conn_info->resp_ie);
4935 conn_info->resp_ie = NULL;
4936 conn_info->resp_ie_len = 0;
4937}
4938
Hante Meuleman89286dc2013-02-08 15:53:46 +01004939static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4940 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004941{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004942 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004943 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004944 u32 req_len;
4945 u32 resp_len;
4946 s32 err = 0;
4947
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004948 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004949
Arend van Sprielac24be62012-10-22 10:36:23 -07004950 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4951 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004952 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004953 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004954 return err;
4955 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004956 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004957 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004958 req_len = le32_to_cpu(assoc_info->req_len);
4959 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004960 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004961 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004962 cfg->extra_buf,
4963 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004964 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004965 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004966 return err;
4967 }
4968 conn_info->req_ie_len = req_len;
4969 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004970 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004971 GFP_KERNEL);
4972 } else {
4973 conn_info->req_ie_len = 0;
4974 conn_info->req_ie = NULL;
4975 }
4976 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004977 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004978 cfg->extra_buf,
4979 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004980 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004981 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004982 return err;
4983 }
4984 conn_info->resp_ie_len = resp_len;
4985 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004986 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004987 GFP_KERNEL);
4988 } else {
4989 conn_info->resp_ie_len = 0;
4990 conn_info->resp_ie = NULL;
4991 }
Arend van Spriel16886732012-12-05 15:26:04 +01004992 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4993 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004994
4995 return err;
4996}
4997
4998static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004999brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005000 struct net_device *ndev,
5001 const struct brcmf_event_msg *e)
5002{
Arend van Sprielc1179032012-10-22 13:55:33 -07005003 struct brcmf_if *ifp = netdev_priv(ndev);
5004 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005005 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
5006 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07005007 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005008 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07005009 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02005010 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005011 u32 freq;
5012 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07005013 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005014
Arend van Sprield96b8012012-12-05 15:26:02 +01005015 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005016
Hante Meuleman89286dc2013-02-08 15:53:46 +01005017 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005018 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005019 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005020
Franky Lina180b832012-10-10 11:13:09 -07005021 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
5022 if (buf == NULL) {
5023 err = -ENOMEM;
5024 goto done;
5025 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005026
Franky Lina180b832012-10-10 11:13:09 -07005027 /* data sent to dongle has to be little endian */
5028 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07005029 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07005030 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07005031
5032 if (err)
5033 goto done;
5034
5035 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02005036 ch.chspec = le16_to_cpu(bi->chanspec);
5037 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005038
Franky Lin83cf17a2013-04-11 13:28:50 +02005039 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005040 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5041 else
5042 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5043
Franky Lin83cf17a2013-04-11 13:28:50 +02005044 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005045 notify_channel = ieee80211_get_channel(wiphy, freq);
5046
Franky Lina180b832012-10-10 11:13:09 -07005047done:
5048 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02005049 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005050 conn_info->req_ie, conn_info->req_ie_len,
5051 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005052 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005053
Arend van Sprielc1179032012-10-22 13:55:33 -07005054 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01005055 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005056 return err;
5057}
5058
5059static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005060brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005061 struct net_device *ndev, const struct brcmf_event_msg *e,
5062 bool completed)
5063{
Arend van Sprielc1179032012-10-22 13:55:33 -07005064 struct brcmf_if *ifp = netdev_priv(ndev);
5065 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005066 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005067
Arend van Sprield96b8012012-12-05 15:26:02 +01005068 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005069
Arend van Sprielc1179032012-10-22 13:55:33 -07005070 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5071 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005072 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01005073 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005074 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005075 brcmf_update_bss_info(cfg, ifp);
5076 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5077 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005078 }
5079 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02005080 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005081 conn_info->req_ie,
5082 conn_info->req_ie_len,
5083 conn_info->resp_ie,
5084 conn_info->resp_ie_len,
5085 completed ? WLAN_STATUS_SUCCESS :
5086 WLAN_STATUS_AUTH_TIMEOUT,
5087 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005088 brcmf_dbg(CONN, "Report connect result - connection %s\n",
5089 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005090 }
Arend van Sprield96b8012012-12-05 15:26:02 +01005091 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005092 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005093}
5094
5095static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005096brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02005097 struct net_device *ndev,
5098 const struct brcmf_event_msg *e, void *data)
5099{
Hante Meulemana44aa402014-12-03 21:05:33 +01005100 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01005101 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005102 u32 event = e->event_code;
5103 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02005104 struct station_info sinfo;
5105
Arend van Spriel16886732012-12-05 15:26:04 +01005106 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005107 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
5108 ndev != cfg_to_ndev(cfg)) {
5109 brcmf_dbg(CONN, "AP mode link down\n");
5110 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01005111 if (ifp->vif->mbss)
Arend van Sprielee6e3a32015-08-26 22:14:55 +02005112 brcmf_remove_interface(ifp);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005113 return 0;
5114 }
Hante Meuleman1a873342012-09-27 14:17:54 +02005115
Hante Meuleman1a873342012-09-27 14:17:54 +02005116 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01005117 (reason == BRCMF_E_STATUS_SUCCESS)) {
5118 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02005119 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005120 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02005121 return -EINVAL;
5122 }
5123 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005124 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02005125 generation++;
5126 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005127 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005128 } else if ((event == BRCMF_E_DISASSOC_IND) ||
5129 (event == BRCMF_E_DEAUTH_IND) ||
5130 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01005131 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005132 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01005133 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02005134}
5135
5136static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005137brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005138 const struct brcmf_event_msg *e, void *data)
5139{
Arend van Spriel19937322012-11-05 16:22:32 -08005140 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5141 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005142 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005143 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005144 s32 err = 0;
5145
Hante Meuleman8851cce2014-07-30 13:20:02 +02005146 if ((e->event_code == BRCMF_E_DEAUTH) ||
5147 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5148 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5149 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5150 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5151 }
5152
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005153 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005154 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005155 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005156 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005157 if (brcmf_is_ibssmode(ifp->vif)) {
Hante Meulemanb0a79082015-12-10 13:43:07 +01005158 brcmf_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005159 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005160 memcpy(profile->bssid, e->addr, ETH_ALEN);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005161 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005162 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5163 &ifp->vif->sme_state);
5164 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5165 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005166 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005167 brcmf_bss_connect_done(cfg, ndev, e, true);
Hante Meuleman92121e62015-10-08 20:33:21 +02005168 brcmf_net_setcarrier(ifp, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005169 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005170 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005171 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005172 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005173 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005174 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005175 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005176 if (ndev != cfg_to_ndev(cfg))
5177 complete(&cfg->vif_disabled);
Hante Meuleman92121e62015-10-08 20:33:21 +02005178 brcmf_net_setcarrier(ifp, false);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005179 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005180 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005181 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5182 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005183 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005184 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005185 }
5186
5187 return err;
5188}
5189
5190static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005191brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005192 const struct brcmf_event_msg *e, void *data)
5193{
Arend van Spriel19937322012-11-05 16:22:32 -08005194 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005195 u32 event = e->event_code;
5196 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005197
5198 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005199 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005200 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201 else
Arend van Spriel19937322012-11-05 16:22:32 -08005202 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005203 }
5204
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005205 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005206}
5207
5208static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005209brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005210 const struct brcmf_event_msg *e, void *data)
5211{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005212 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005213 enum nl80211_key_type key_type;
5214
5215 if (flags & BRCMF_EVENT_MSG_GROUP)
5216 key_type = NL80211_KEYTYPE_GROUP;
5217 else
5218 key_type = NL80211_KEYTYPE_PAIRWISE;
5219
Arend van Spriel19937322012-11-05 16:22:32 -08005220 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005221 NULL, GFP_KERNEL);
5222
5223 return 0;
5224}
5225
Arend van Sprield3c0b632013-02-08 15:53:37 +01005226static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5227 const struct brcmf_event_msg *e, void *data)
5228{
5229 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5230 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5231 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5232 struct brcmf_cfg80211_vif *vif;
5233
Hante Meuleman37a869e2015-10-29 20:33:17 +01005234 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
Arend van Sprield3c0b632013-02-08 15:53:37 +01005235 ifevent->action, ifevent->flags, ifevent->ifidx,
Hante Meuleman37a869e2015-10-29 20:33:17 +01005236 ifevent->bsscfgidx);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005237
Arend van Sprield3c0b632013-02-08 15:53:37 +01005238 mutex_lock(&event->vif_event_lock);
5239 event->action = ifevent->action;
5240 vif = event->vif;
5241
5242 switch (ifevent->action) {
5243 case BRCMF_E_IF_ADD:
5244 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005245 if (!cfg->vif_event.vif) {
5246 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005247 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005248 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005249
5250 ifp->vif = vif;
5251 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005252 if (ifp->ndev) {
5253 vif->wdev.netdev = ifp->ndev;
5254 ifp->ndev->ieee80211_ptr = &vif->wdev;
5255 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5256 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005257 mutex_unlock(&event->vif_event_lock);
5258 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005259 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005260
5261 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005262 mutex_unlock(&event->vif_event_lock);
5263 /* event may not be upon user request */
5264 if (brcmf_cfg80211_vif_event_armed(cfg))
5265 wake_up(&event->vif_wq);
5266 return 0;
5267
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005268 case BRCMF_E_IF_CHANGE:
5269 mutex_unlock(&event->vif_event_lock);
5270 wake_up(&event->vif_wq);
5271 return 0;
5272
Arend van Sprield3c0b632013-02-08 15:53:37 +01005273 default:
5274 mutex_unlock(&event->vif_event_lock);
5275 break;
5276 }
5277 return -EINVAL;
5278}
5279
Arend van Spriel5b435de2011-10-05 13:19:03 +02005280static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5281{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005282 conf->frag_threshold = (u32)-1;
5283 conf->rts_threshold = (u32)-1;
5284 conf->retry_short = (u32)-1;
5285 conf->retry_long = (u32)-1;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005286}
5287
Arend van Spriel5c36b992012-11-14 18:46:05 -08005288static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005289{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005290 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5291 brcmf_notify_connect_status);
5292 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5293 brcmf_notify_connect_status);
5294 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5295 brcmf_notify_connect_status);
5296 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5297 brcmf_notify_connect_status);
5298 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5299 brcmf_notify_connect_status);
5300 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5301 brcmf_notify_connect_status);
5302 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5303 brcmf_notify_roaming_status);
5304 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5305 brcmf_notify_mic_status);
5306 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5307 brcmf_notify_connect_status);
5308 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5309 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005310 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5311 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005312 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005313 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005314 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5315 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005316 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5317 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005318 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5319 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005320 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5321 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005322}
5323
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005324static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005325{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005326 kfree(cfg->conf);
5327 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005328 kfree(cfg->escan_ioctl_buf);
5329 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005330 kfree(cfg->extra_buf);
5331 cfg->extra_buf = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005332}
5333
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005334static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005335{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005336 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5337 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005338 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005339 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5340 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005341 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005342 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5343 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005344 goto init_priv_mem_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005345
5346 return 0;
5347
5348init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005349 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005350
5351 return -ENOMEM;
5352}
5353
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005354static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005355{
5356 s32 err = 0;
5357
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005358 cfg->scan_request = NULL;
5359 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005360 cfg->active_scan = true; /* we do active scan per default */
5361 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005362 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005363 if (err)
5364 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005365 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005366 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005367 brcmf_init_escan(cfg);
5368 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005369 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005370 return err;
5371}
5372
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005373static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005374{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005375 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005376 brcmf_abort_scanning(cfg);
5377 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005378}
5379
Arend van Sprield3c0b632013-02-08 15:53:37 +01005380static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5381{
5382 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005383 mutex_init(&event->vif_event_lock);
5384}
5385
Hante Meuleman1119e232015-11-25 11:32:42 +01005386static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005387{
Hante Meuleman1119e232015-11-25 11:32:42 +01005388 s32 err;
5389 u32 bcn_timeout;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005390 __le32 roamtrigger[2];
5391 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005392
Hante Meuleman1119e232015-11-25 11:32:42 +01005393 /* Configure beacon timeout value based upon roaming setting */
Hante Meuleman7d34b052016-01-02 09:41:41 +01005394 if (ifp->drvr->settings->roamoff)
Hante Meuleman1119e232015-11-25 11:32:42 +01005395 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
5396 else
5397 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
5398 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
5399 if (err) {
5400 brcmf_err("bcn_timeout error (%d)\n", err);
5401 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005402 }
5403
Hante Meuleman1119e232015-11-25 11:32:42 +01005404 /* Enable/Disable built-in roaming to allow supplicant to take care of
5405 * roaming.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005406 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005407 brcmf_dbg(INFO, "Internal Roaming = %s\n",
Hante Meuleman7d34b052016-01-02 09:41:41 +01005408 ifp->drvr->settings->roamoff ? "Off" : "On");
5409 err = brcmf_fil_iovar_int_set(ifp, "roam_off",
5410 ifp->drvr->settings->roamoff);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005411 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005412 brcmf_err("roam_off error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005413 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005414 }
5415
Arend van Sprielf588bc02011-10-12 20:51:22 +02005416 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5417 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005418 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005419 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005420 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005421 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005422 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005423 }
5424
Arend van Sprielf588bc02011-10-12 20:51:22 +02005425 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5426 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005427 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005428 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005429 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005430 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005431 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005432 }
5433
Hante Meuleman1119e232015-11-25 11:32:42 +01005434roam_setup_done:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005435 return err;
5436}
5437
5438static s32
Hante Meuleman1678ba82015-12-10 13:43:00 +01005439brcmf_dongle_scantime(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005440{
5441 s32 err = 0;
5442
Arend van Sprielac24be62012-10-22 10:36:23 -07005443 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005444 BRCMF_SCAN_CHANNEL_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005445 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005446 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005447 goto dongle_scantime_out;
5448 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005449 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005450 BRCMF_SCAN_UNASSOC_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005451 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005452 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005453 goto dongle_scantime_out;
5454 }
5455
Arend van Sprielac24be62012-10-22 10:36:23 -07005456 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005457 BRCMF_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005458 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005459 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005460 goto dongle_scantime_out;
5461 }
5462
5463dongle_scantime_out:
5464 return err;
5465}
5466
Arend van Sprielb48d8912014-07-12 08:49:41 +02005467static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5468 struct brcmu_chan *ch)
5469{
5470 u32 ht40_flag;
5471
5472 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5473 if (ch->sb == BRCMU_CHAN_SB_U) {
5474 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5475 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5476 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5477 } else {
5478 /* It should be one of
5479 * IEEE80211_CHAN_NO_HT40 or
5480 * IEEE80211_CHAN_NO_HT40PLUS
5481 */
5482 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5483 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5484 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5485 }
5486}
5487
5488static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5489 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005490{
5491 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005492 struct ieee80211_supported_band *band;
5493 struct ieee80211_channel *channel;
5494 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005495 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005496 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005497 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005498 u8 *pbuf;
5499 u32 i, j;
5500 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005501 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005502 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005503
5504 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5505
5506 if (pbuf == NULL)
5507 return -ENOMEM;
5508
5509 list = (struct brcmf_chanspec_list *)pbuf;
5510
5511 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5512 BRCMF_DCMD_MEDLEN);
5513 if (err) {
5514 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005515 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005516 }
5517
Arend van Sprielb48d8912014-07-12 08:49:41 +02005518 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005519 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5520 if (band)
5521 for (i = 0; i < band->n_channels; i++)
5522 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5523 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5524 if (band)
5525 for (i = 0; i < band->n_channels; i++)
5526 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005527
5528 total = le32_to_cpu(list->count);
5529 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005530 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5531 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005532
Franky Lin83cf17a2013-04-11 13:28:50 +02005533 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005534 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005535 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005536 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005537 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005538 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005539 continue;
5540 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005541 if (!band)
5542 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005543 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005544 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005545 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005546 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005547 ch.bw == BRCMU_CHAN_BW_80)
5548 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005549
5550 channel = band->channels;
5551 index = band->n_channels;
5552 for (j = 0; j < band->n_channels; j++) {
5553 if (channel[j].hw_value == ch.chnum) {
5554 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005555 break;
5556 }
5557 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005558 channel[index].center_freq =
5559 ieee80211_channel_to_frequency(ch.chnum, band->band);
5560 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005561
Arend van Sprielb48d8912014-07-12 08:49:41 +02005562 /* assuming the chanspecs order is HT20,
5563 * HT40 upper, HT40 lower, and VHT80.
5564 */
5565 if (ch.bw == BRCMU_CHAN_BW_80) {
5566 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5567 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5568 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5569 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005570 /* enable the channel and disable other bandwidths
5571 * for now as mentioned order assure they are enabled
5572 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005573 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005574 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5575 IEEE80211_CHAN_NO_80MHZ;
5576 ch.bw = BRCMU_CHAN_BW_20;
5577 cfg->d11inf.encchspec(&ch);
5578 chaninfo = ch.chspec;
5579 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5580 &chaninfo);
5581 if (!err) {
5582 if (chaninfo & WL_CHAN_RADAR)
5583 channel[index].flags |=
5584 (IEEE80211_CHAN_RADAR |
5585 IEEE80211_CHAN_NO_IR);
5586 if (chaninfo & WL_CHAN_PASSIVE)
5587 channel[index].flags |=
5588 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005589 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005590 }
5591 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005592
Arend van Sprielb48d8912014-07-12 08:49:41 +02005593fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005594 kfree(pbuf);
5595 return err;
5596}
5597
Arend van Sprielb48d8912014-07-12 08:49:41 +02005598static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005599{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005600 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5601 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005602 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005603 struct brcmf_chanspec_list *list;
5604 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005605 u32 val;
5606 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005607 struct brcmu_chan ch;
5608 u32 num_chan;
5609 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005610
5611 /* verify support for bw_cap command */
5612 val = WLC_BAND_5G;
5613 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5614
5615 if (!err) {
5616 /* only set 2G bandwidth using bw_cap command */
5617 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5618 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5619 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5620 sizeof(band_bwcap));
5621 } else {
5622 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5623 val = WLC_N_BW_40ALL;
5624 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5625 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005626
5627 if (!err) {
5628 /* update channel info in 2G band */
5629 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5630
5631 if (pbuf == NULL)
5632 return -ENOMEM;
5633
5634 ch.band = BRCMU_CHAN_BAND_2G;
5635 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005636 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005637 ch.chnum = 0;
5638 cfg->d11inf.encchspec(&ch);
5639
5640 /* pass encoded chanspec in query */
5641 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5642
5643 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5644 BRCMF_DCMD_MEDLEN);
5645 if (err) {
5646 brcmf_err("get chanspecs error (%d)\n", err);
5647 kfree(pbuf);
5648 return err;
5649 }
5650
5651 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5652 list = (struct brcmf_chanspec_list *)pbuf;
5653 num_chan = le32_to_cpu(list->count);
5654 for (i = 0; i < num_chan; i++) {
5655 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5656 cfg->d11inf.decchspec(&ch);
5657 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5658 continue;
5659 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5660 continue;
5661 for (j = 0; j < band->n_channels; j++) {
5662 if (band->channels[j].hw_value == ch.chnum)
5663 break;
5664 }
5665 if (WARN_ON(j == band->n_channels))
5666 continue;
5667
5668 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5669 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005670 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005671 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005672 return err;
5673}
5674
Arend van Spriel2375d972014-01-06 12:40:41 +01005675static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5676{
5677 u32 band, mimo_bwcap;
5678 int err;
5679
5680 band = WLC_BAND_2G;
5681 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5682 if (!err) {
5683 bw_cap[IEEE80211_BAND_2GHZ] = band;
5684 band = WLC_BAND_5G;
5685 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5686 if (!err) {
5687 bw_cap[IEEE80211_BAND_5GHZ] = band;
5688 return;
5689 }
5690 WARN_ON(1);
5691 return;
5692 }
5693 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5694 mimo_bwcap = 0;
5695 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5696 if (err)
5697 /* assume 20MHz if firmware does not give a clue */
5698 mimo_bwcap = WLC_N_BW_20ALL;
5699
5700 switch (mimo_bwcap) {
5701 case WLC_N_BW_40ALL:
5702 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5703 /* fall-thru */
5704 case WLC_N_BW_20IN2G_40IN5G:
5705 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5706 /* fall-thru */
5707 case WLC_N_BW_20ALL:
5708 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5709 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5710 break;
5711 default:
5712 brcmf_err("invalid mimo_bw_cap value\n");
5713 }
5714}
Hante Meulemand48200b2013-04-03 12:40:29 +02005715
Arend van Spriel18d6c532014-05-12 10:47:35 +02005716static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5717 u32 bw_cap[2], u32 nchain)
5718{
5719 band->ht_cap.ht_supported = true;
5720 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5721 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5722 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5723 }
5724 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5725 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5726 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5727 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5728 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5729 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5730}
5731
5732static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5733{
5734 u16 mcs_map;
5735 int i;
5736
5737 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5738 mcs_map = (mcs_map << 2) | supp;
5739
5740 return cpu_to_le16(mcs_map);
5741}
5742
5743static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005744 u32 bw_cap[2], u32 nchain, u32 txstreams,
5745 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
Arend van Spriel18d6c532014-05-12 10:47:35 +02005746{
5747 __le16 mcs_map;
5748
5749 /* not allowed in 2.4G band */
5750 if (band->band == IEEE80211_BAND_2GHZ)
5751 return;
5752
5753 band->vht_cap.vht_supported = true;
5754 /* 80MHz is mandatory */
5755 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5756 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5757 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5758 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5759 }
5760 /* all support 256-QAM */
5761 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5762 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5763 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005764
5765 /* Beamforming support information */
5766 if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
5767 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
5768 if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
5769 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
5770 if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
5771 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
5772 if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
5773 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
5774
5775 if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
5776 band->vht_cap.cap |=
5777 (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
5778 band->vht_cap.cap |= ((txstreams - 1) <<
5779 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
5780 band->vht_cap.cap |=
5781 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
5782 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005783}
5784
Arend van Sprielb48d8912014-07-12 08:49:41 +02005785static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005786{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005787 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005788 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005789 u32 nmode = 0;
5790 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005791 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005792 u32 rxchain;
5793 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005794 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005795 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005796 struct ieee80211_supported_band *band;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005797 u32 txstreams = 0;
5798 u32 txbf_bfe_cap = 0;
5799 u32 txbf_bfr_cap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005800
Arend van Spriel18d6c532014-05-12 10:47:35 +02005801 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005802 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5803 if (err) {
5804 brcmf_err("nmode error (%d)\n", err);
5805 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005806 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005807 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005808 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5809 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5810 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005811
Daniel Kim4aca7a12014-02-25 20:30:36 +01005812 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5813 if (err) {
5814 brcmf_err("rxchain error (%d)\n", err);
5815 nchain = 1;
5816 } else {
5817 for (nchain = 0; rxchain; nchain++)
5818 rxchain = rxchain & (rxchain - 1);
5819 }
5820 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5821
Arend van Sprielb48d8912014-07-12 08:49:41 +02005822 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005823 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005824 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005825 return err;
5826 }
5827
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005828 if (vhtmode) {
5829 (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
5830 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
5831 &txbf_bfe_cap);
5832 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
5833 &txbf_bfr_cap);
5834 }
5835
Arend van Sprielb48d8912014-07-12 08:49:41 +02005836 wiphy = cfg_to_wiphy(cfg);
5837 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5838 band = wiphy->bands[i];
5839 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005840 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005841
Arend van Spriel18d6c532014-05-12 10:47:35 +02005842 if (nmode)
5843 brcmf_update_ht_cap(band, bw_cap, nchain);
5844 if (vhtmode)
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005845 brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
5846 txbf_bfe_cap, txbf_bfr_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005847 }
5848
Arend van Sprielb48d8912014-07-12 08:49:41 +02005849 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005850}
5851
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005852static const struct ieee80211_txrx_stypes
5853brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5854 [NL80211_IFTYPE_STATION] = {
5855 .tx = 0xffff,
5856 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5857 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5858 },
5859 [NL80211_IFTYPE_P2P_CLIENT] = {
5860 .tx = 0xffff,
5861 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5862 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5863 },
5864 [NL80211_IFTYPE_P2P_GO] = {
5865 .tx = 0xffff,
5866 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5867 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5868 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5869 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5870 BIT(IEEE80211_STYPE_AUTH >> 4) |
5871 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5872 BIT(IEEE80211_STYPE_ACTION >> 4)
5873 },
5874 [NL80211_IFTYPE_P2P_DEVICE] = {
5875 .tx = 0xffff,
5876 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5877 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5878 }
5879};
5880
Arend van Spriel0882dda2015-08-20 22:06:03 +02005881/**
5882 * brcmf_setup_ifmodes() - determine interface modes and combinations.
5883 *
5884 * @wiphy: wiphy object.
5885 * @ifp: interface object needed for feat module api.
5886 *
5887 * The interface modes and combinations are determined dynamically here
5888 * based on firmware functionality.
5889 *
5890 * no p2p and no mbss:
5891 *
5892 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5893 *
5894 * no p2p and mbss:
5895 *
5896 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5897 * #AP <= 4, matching BI, channels = 1, 4 total
5898 *
5899 * p2p, no mchan, and mbss:
5900 *
5901 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
5902 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5903 * #AP <= 4, matching BI, channels = 1, 4 total
5904 *
5905 * p2p, mchan, and mbss:
5906 *
5907 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
5908 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5909 * #AP <= 4, matching BI, channels = 1, 4 total
5910 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005911static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5912{
5913 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02005914 struct ieee80211_iface_limit *c0_limits = NULL;
5915 struct ieee80211_iface_limit *p2p_limits = NULL;
5916 struct ieee80211_iface_limit *mbss_limits = NULL;
5917 bool mbss, p2p;
5918 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005919
Arend van Spriel0882dda2015-08-20 22:06:03 +02005920 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
5921 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
5922
5923 n_combos = 1 + !!p2p + !!mbss;
5924 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005925 if (!combo)
5926 goto err;
5927
Arend van Spriel0882dda2015-08-20 22:06:03 +02005928 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
5929 if (!c0_limits)
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005930 goto err;
5931
Arend van Spriel0882dda2015-08-20 22:06:03 +02005932 if (p2p) {
5933 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
5934 if (!p2p_limits)
5935 goto err;
5936 }
5937
5938 if (mbss) {
5939 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
5940 if (!mbss_limits)
5941 goto err;
5942 }
5943
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005944 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5945 BIT(NL80211_IFTYPE_ADHOC) |
5946 BIT(NL80211_IFTYPE_AP);
5947
Arend van Spriel0882dda2015-08-20 22:06:03 +02005948 c = 0;
5949 i = 0;
5950 combo[c].num_different_channels = 1;
5951 c0_limits[i].max = 1;
5952 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5953 if (p2p) {
5954 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5955 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005956 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5957 BIT(NL80211_IFTYPE_P2P_GO) |
5958 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02005959 c0_limits[i].max = 1;
5960 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5961 c0_limits[i].max = 1;
5962 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5963 BIT(NL80211_IFTYPE_P2P_GO);
5964 } else {
5965 c0_limits[i].max = 1;
5966 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005967 }
Arend van Spriel0882dda2015-08-20 22:06:03 +02005968 combo[c].max_interfaces = i;
5969 combo[c].n_limits = i;
5970 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005971
Arend van Spriel0882dda2015-08-20 22:06:03 +02005972 if (p2p) {
5973 c++;
5974 i = 0;
5975 combo[c].num_different_channels = 1;
5976 p2p_limits[i].max = 1;
5977 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5978 p2p_limits[i].max = 1;
5979 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
5980 p2p_limits[i].max = 1;
5981 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
5982 p2p_limits[i].max = 1;
5983 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5984 combo[c].max_interfaces = i;
5985 combo[c].n_limits = i;
5986 combo[c].limits = p2p_limits;
5987 }
5988
5989 if (mbss) {
5990 c++;
5991 combo[c].beacon_int_infra_match = true;
5992 combo[c].num_different_channels = 1;
5993 mbss_limits[0].max = 4;
5994 mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
5995 combo[c].max_interfaces = 4;
5996 combo[c].n_limits = 1;
5997 combo[c].limits = mbss_limits;
5998 }
5999 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006000 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006001 return 0;
6002
6003err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02006004 kfree(c0_limits);
6005 kfree(p2p_limits);
6006 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006007 kfree(combo);
6008 return -ENOMEM;
6009}
6010
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006011static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
6012{
6013 /* scheduled scan settings */
6014 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
6015 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
6016 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
6017 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
6018}
6019
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006020#ifdef CONFIG_PM
6021static const struct wiphy_wowlan_support brcmf_wowlan_support = {
6022 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01006023 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
6024 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
6025 .pattern_min_len = 1,
6026 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006027};
6028#endif
6029
6030static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
6031{
6032#ifdef CONFIG_PM
6033 /* wowl settings */
6034 wiphy->wowlan = &brcmf_wowlan_support;
6035#endif
6036}
6037
Arend van Sprielb48d8912014-07-12 08:49:41 +02006038static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006039{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006040 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006041 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006042 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006043 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006044 __le32 bandlist[3];
6045 u32 n_bands;
6046 int err, i;
6047
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006048 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
6049 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
Hante Meuleman6c404f32015-12-10 13:43:03 +01006050 wiphy->max_num_pmkids = BRCMF_MAXPMKID;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006051
6052 err = brcmf_setup_ifmodes(wiphy, ifp);
6053 if (err)
6054 return err;
6055
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006056 for (i = 0, combo = wiphy->iface_combinations;
6057 i < wiphy->n_iface_combinations; i++, combo++) {
6058 max_interfaces = max(max_interfaces, combo->max_interfaces);
6059 }
6060
6061 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
6062 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006063 u8 *addr = drvr->addresses[i].addr;
6064
6065 memcpy(addr, drvr->mac, ETH_ALEN);
6066 if (i) {
6067 addr[0] |= BIT(1);
6068 addr[ETH_ALEN - 1] ^= i;
6069 }
6070 }
6071 wiphy->addresses = drvr->addresses;
6072 wiphy->n_addresses = i;
6073
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006074 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
6075 wiphy->cipher_suites = __wl_cipher_suites;
6076 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
6077 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
6078 WIPHY_FLAG_OFFCHAN_TX |
Hante Meulemana7b82d42015-12-10 13:43:04 +01006079 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
6080 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
6081 wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman7d34b052016-01-02 09:41:41 +01006082 if (!ifp->drvr->settings->roamoff)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006083 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
6084 wiphy->mgmt_stypes = brcmf_txrx_stypes;
6085 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02006086 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
6087 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006088
6089 /* vendor commands/events support */
6090 wiphy->vendor_commands = brcmf_vendor_cmds;
6091 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
6092
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006093 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
6094 brcmf_wiphy_wowl_params(wiphy);
6095
Arend van Spriel58de92d2015-04-14 20:10:24 +02006096 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
6097 sizeof(bandlist));
6098 if (err) {
6099 brcmf_err("could not obtain band info: err=%d\n", err);
6100 return err;
6101 }
6102 /* first entry in bandlist is number of bands */
6103 n_bands = le32_to_cpu(bandlist[0]);
6104 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
6105 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
6106 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
6107 GFP_KERNEL);
6108 if (!band)
6109 return -ENOMEM;
6110
6111 band->channels = kmemdup(&__wl_2ghz_channels,
6112 sizeof(__wl_2ghz_channels),
6113 GFP_KERNEL);
6114 if (!band->channels) {
6115 kfree(band);
6116 return -ENOMEM;
6117 }
6118
6119 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
6120 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
6121 }
6122 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
6123 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
6124 GFP_KERNEL);
6125 if (!band)
6126 return -ENOMEM;
6127
6128 band->channels = kmemdup(&__wl_5ghz_channels,
6129 sizeof(__wl_5ghz_channels),
6130 GFP_KERNEL);
6131 if (!band->channels) {
6132 kfree(band);
6133 return -ENOMEM;
6134 }
6135
6136 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
6137 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
6138 }
6139 }
6140 err = brcmf_setup_wiphybands(wiphy);
6141 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006142}
6143
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006144static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006145{
6146 struct net_device *ndev;
6147 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01006148 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006149 s32 power_mode;
6150 s32 err = 0;
6151
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006152 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006153 return err;
6154
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006155 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006156 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006157 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006158
Hante Meuleman40a23292013-01-02 15:22:51 +01006159 /* make sure RF is ready for work */
6160 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6161
Hante Meuleman1678ba82015-12-10 13:43:00 +01006162 brcmf_dongle_scantime(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006163
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006164 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006165 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006166 if (err)
6167 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006168 brcmf_dbg(INFO, "power save set to %s\n",
6169 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006170
Hante Meuleman1119e232015-11-25 11:32:42 +01006171 err = brcmf_dongle_roam(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006172 if (err)
6173 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006174 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
6175 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006176 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006177 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006178
Hante Meulemanb3657452013-05-27 21:09:53 +02006179 brcmf_configure_arp_offload(ifp, true);
6180
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006181 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006182default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006183
6184 return err;
6185
6186}
6187
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006188static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006189{
Arend van Sprielc1179032012-10-22 13:55:33 -07006190 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006191
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006192 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006193}
6194
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006195static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006196{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006197 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006198
Arend van Spriel5b435de2011-10-05 13:19:03 +02006199 /*
6200 * While going down, if associated with AP disassociate
6201 * from AP to save power
6202 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006203 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006204 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006205
6206 /* Make sure WPA_Supplicant receives all the event
6207 generated due to DISASSOC call to the fw to keep
6208 the state fw and WPA_Supplicant state consistent
6209 */
6210 brcmf_delay(500);
6211 }
6212
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006213 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006214 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006215
Arend van Spriel5b435de2011-10-05 13:19:03 +02006216 return 0;
6217}
6218
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006219s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006220{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006221 struct brcmf_if *ifp = netdev_priv(ndev);
6222 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006223 s32 err = 0;
6224
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006225 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006226 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006227 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006228
6229 return err;
6230}
6231
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006232s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006233{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006234 struct brcmf_if *ifp = netdev_priv(ndev);
6235 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006236 s32 err = 0;
6237
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006238 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006239 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006240 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006241
6242 return err;
6243}
6244
Arend van Spriela7965fb2013-04-11 17:08:37 +02006245enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6246{
6247 struct wireless_dev *wdev = &ifp->vif->wdev;
6248
6249 return wdev->iftype;
6250}
6251
Hante Meulemanbfe81972014-10-28 14:56:16 +01006252bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6253 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006254{
6255 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006256
6257 list_for_each_entry(vif, &cfg->vif_list, list) {
6258 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006259 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006260 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006261 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006262}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006263
6264static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6265 u8 action)
6266{
6267 u8 evt_action;
6268
6269 mutex_lock(&event->vif_event_lock);
6270 evt_action = event->action;
6271 mutex_unlock(&event->vif_event_lock);
6272 return evt_action == action;
6273}
6274
6275void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6276 struct brcmf_cfg80211_vif *vif)
6277{
6278 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6279
6280 mutex_lock(&event->vif_event_lock);
6281 event->vif = vif;
6282 event->action = 0;
6283 mutex_unlock(&event->vif_event_lock);
6284}
6285
6286bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6287{
6288 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6289 bool armed;
6290
6291 mutex_lock(&event->vif_event_lock);
6292 armed = event->vif != NULL;
6293 mutex_unlock(&event->vif_event_lock);
6294
6295 return armed;
6296}
6297int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6298 u8 action, ulong timeout)
6299{
6300 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6301
6302 return wait_event_timeout(event->vif_wq,
6303 vif_event_equals(event, action), timeout);
6304}
6305
Arend van Spriel63db1a42014-12-21 12:43:51 +01006306static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6307 struct regulatory_request *req)
6308{
6309 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6310 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6311 struct brcmf_fil_country_le ccreq;
6312 int i;
6313
6314 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6315 req->alpha2[0], req->alpha2[1]);
6316
6317 /* ignore non-ISO3166 country codes */
6318 for (i = 0; i < sizeof(req->alpha2); i++)
6319 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6320 brcmf_err("not a ISO3166 code\n");
6321 return;
6322 }
6323 memset(&ccreq, 0, sizeof(ccreq));
6324 ccreq.rev = cpu_to_le32(-1);
6325 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006326 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6327 brcmf_err("firmware rejected country setting\n");
6328 return;
6329 }
6330 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006331}
6332
Arend van Sprielb48d8912014-07-12 08:49:41 +02006333static void brcmf_free_wiphy(struct wiphy *wiphy)
6334{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006335 int i;
6336
Arend van Spriel58de92d2015-04-14 20:10:24 +02006337 if (!wiphy)
6338 return;
6339
Arend van Spriel0882dda2015-08-20 22:06:03 +02006340 if (wiphy->iface_combinations) {
6341 for (i = 0; i < wiphy->n_iface_combinations; i++)
6342 kfree(wiphy->iface_combinations[i].limits);
6343 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006344 kfree(wiphy->iface_combinations);
6345 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6346 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6347 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6348 }
6349 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6350 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6351 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6352 }
6353 wiphy_free(wiphy);
6354}
6355
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006356struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006357 struct device *busdev,
6358 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006359{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006360 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006361 struct brcmf_cfg80211_info *cfg;
6362 struct wiphy *wiphy;
6363 struct brcmf_cfg80211_vif *vif;
6364 struct brcmf_if *ifp;
6365 s32 err = 0;
6366 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006367 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006368
6369 if (!ndev) {
6370 brcmf_err("ndev is invalid\n");
6371 return NULL;
6372 }
6373
6374 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006375 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6376 if (!wiphy) {
6377 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006378 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006379 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006380 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006381 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006382
6383 cfg = wiphy_priv(wiphy);
6384 cfg->wiphy = wiphy;
6385 cfg->pub = drvr;
6386 init_vif_event(&cfg->vif_event);
6387 INIT_LIST_HEAD(&cfg->vif_list);
6388
6389 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006390 if (IS_ERR(vif))
6391 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006392
6393 vif->ifp = ifp;
6394 vif->wdev.netdev = ndev;
6395 ndev->ieee80211_ptr = &vif->wdev;
6396 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6397
6398 err = wl_init_priv(cfg);
6399 if (err) {
6400 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006401 brcmf_free_vif(vif);
6402 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006403 }
6404 ifp->vif = vif;
6405
Arend van Sprielb48d8912014-07-12 08:49:41 +02006406 /* determine d11 io type before wiphy setup */
6407 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006408 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006409 brcmf_err("Failed to get D11 version (%d)\n", err);
6410 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006411 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006412 cfg->d11inf.io_type = (u8)io_type;
6413 brcmu_d11_attach(&cfg->d11inf);
6414
6415 err = brcmf_setup_wiphy(wiphy, ifp);
6416 if (err < 0)
6417 goto priv_out;
6418
6419 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006420 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006421 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6422 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6423
6424 /* firmware defaults to 40MHz disabled in 2G band. We signal
6425 * cfg80211 here that we do and have it decide we can enable
6426 * it. But first check if device does support 2G operation.
6427 */
6428 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6429 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6430 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6431 }
6432 err = wiphy_register(wiphy);
6433 if (err < 0) {
6434 brcmf_err("Could not register wiphy device (%d)\n", err);
6435 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006436 }
6437
6438 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6439 * setup 40MHz in 2GHz band and enable OBSS scanning.
6440 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006441 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6442 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006443 if (!err)
6444 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6445 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006446 else
6447 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006448 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006449 /* p2p might require that "if-events" get processed by fweh. So
6450 * activate the already registered event handlers now and activate
6451 * the rest when initialization has completed. drvr->config needs to
6452 * be assigned before activating events.
6453 */
6454 drvr->config = cfg;
6455 err = brcmf_fweh_activate_events(ifp);
6456 if (err) {
6457 brcmf_err("FWEH activation failed (%d)\n", err);
6458 goto wiphy_unreg_out;
6459 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006460
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006461 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006462 if (err) {
6463 brcmf_err("P2P initilisation failed (%d)\n", err);
6464 goto wiphy_unreg_out;
6465 }
6466 err = brcmf_btcoex_attach(cfg);
6467 if (err) {
6468 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6469 brcmf_p2p_detach(&cfg->p2p);
6470 goto wiphy_unreg_out;
6471 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006472
Hante Meulemana7b82d42015-12-10 13:43:04 +01006473 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
6474 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6475 if (err) {
6476 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6477 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
6478 } else {
6479 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6480 brcmf_notify_tdls_peer_event);
6481 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006482 }
6483
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006484 /* (re-) activate FWEH event handling */
6485 err = brcmf_fweh_activate_events(ifp);
6486 if (err) {
6487 brcmf_err("FWEH activation failed (%d)\n", err);
6488 goto wiphy_unreg_out;
6489 }
6490
Hante Meuleman48ed16e2016-01-02 09:41:38 +01006491 /* Fill in some of the advertised nl80211 supported features */
6492 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
6493 wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
6494#ifdef CONFIG_PM
6495 if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
6496 wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
6497#endif
6498 }
6499
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006500 return cfg;
6501
Arend van Sprielb48d8912014-07-12 08:49:41 +02006502wiphy_unreg_out:
6503 wiphy_unregister(cfg->wiphy);
6504priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006505 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006506 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006507 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006508wiphy_out:
6509 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006510 return NULL;
6511}
6512
6513void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6514{
6515 if (!cfg)
6516 return;
6517
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006518 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006519 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006520 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006521 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006522}