blob: 0a97e61025fbad3a291a66761259d7a9cc5110a3 [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
18
19#include <linux/kernel.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <linux/etherdevice.h>
Hante Meuleman68ca3952014-02-25 20:30:26 +010021#include <linux/module.h>
Franky Lin1bacb042014-06-21 12:11:16 +020022#include <linux/vmalloc.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020024#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020025
26#include <brcmu_utils.h>
27#include <defs.h>
28#include <brcmu_wifi.h>
Hante Meuleman122d3d02014-10-28 14:56:18 +010029#include "core.h"
Hante Meulemana8e8ed32014-10-28 14:56:13 +010030#include "debug.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020031#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010032#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010033#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020034#include "btcoex.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010035#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020036#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070037#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020038#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020039#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010040#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010041#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020042
Arend van Spriele5806072012-09-19 22:21:08 +020043#define BRCMF_SCAN_IE_LEN_MAX 2048
44#define BRCMF_PNO_VERSION 2
45#define BRCMF_PNO_TIME 30
46#define BRCMF_PNO_REPEAT 4
47#define BRCMF_PNO_FREQ_EXPO_MAX 3
48#define BRCMF_PNO_MAX_PFN_COUNT 16
49#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
50#define BRCMF_PNO_HIDDEN_BIT 2
51#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
52#define BRCMF_PNO_SCAN_COMPLETE 1
53#define BRCMF_PNO_SCAN_INCOMPLETE 0
54
Hante Meuleman1a873342012-09-27 14:17:54 +020055#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010059#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020060
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
75#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
76#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
77
78#define VNDR_IE_CMD_LEN 4 /* length of the set command
79 * string :"add", "del" (+ NUL)
80 */
81#define VNDR_IE_COUNT_OFFSET 4
82#define VNDR_IE_PKTFLAG_OFFSET 8
83#define VNDR_IE_VSIE_OFFSET 12
84#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010085#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020086
87#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
88#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020089
Hante Meuleman89286dc2013-02-08 15:53:46 +010090#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
91#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
92#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
93
Arend van Spriel5b435de2011-10-05 13:19:03 +020094#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
95 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
96
Arend van Sprielce81e312012-10-22 13:55:37 -070097static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +020098{
Arend van Sprielc1179032012-10-22 13:55:33 -070099 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100100 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
101 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200102 return false;
103 }
104 return true;
105}
106
Arend van Spriel5b435de2011-10-05 13:19:03 +0200107#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
108#define RATETAB_ENT(_rateid, _flags) \
109 { \
110 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
111 .hw_value = (_rateid), \
112 .flags = (_flags), \
113 }
114
115static struct ieee80211_rate __wl_rates[] = {
116 RATETAB_ENT(BRCM_RATE_1M, 0),
117 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
118 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
119 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
120 RATETAB_ENT(BRCM_RATE_6M, 0),
121 RATETAB_ENT(BRCM_RATE_9M, 0),
122 RATETAB_ENT(BRCM_RATE_12M, 0),
123 RATETAB_ENT(BRCM_RATE_18M, 0),
124 RATETAB_ENT(BRCM_RATE_24M, 0),
125 RATETAB_ENT(BRCM_RATE_36M, 0),
126 RATETAB_ENT(BRCM_RATE_48M, 0),
127 RATETAB_ENT(BRCM_RATE_54M, 0),
128};
129
Arend van Spriel5b435de2011-10-05 13:19:03 +0200130#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200131#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
132#define wl_a_rates (__wl_rates + 4)
133#define wl_a_rates_size (wl_g_rates_size - 4)
134
135#define CHAN2G(_channel, _freq) { \
136 .band = IEEE80211_BAND_2GHZ, \
137 .center_freq = (_freq), \
138 .hw_value = (_channel), \
139 .flags = IEEE80211_CHAN_DISABLED, \
140 .max_antenna_gain = 0, \
141 .max_power = 30, \
142}
143
144#define CHAN5G(_channel) { \
145 .band = IEEE80211_BAND_5GHZ, \
146 .center_freq = 5000 + (5 * (_channel)), \
147 .hw_value = (_channel), \
148 .flags = IEEE80211_CHAN_DISABLED, \
149 .max_antenna_gain = 0, \
150 .max_power = 30, \
151}
152
153static struct ieee80211_channel __wl_2ghz_channels[] = {
154 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
155 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
156 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
157 CHAN2G(13, 2472), CHAN2G(14, 2484)
158};
159
160static struct ieee80211_channel __wl_5ghz_channels[] = {
161 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
162 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
163 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
164 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
165 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
166 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
167};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200168
Arend van Sprielb48d8912014-07-12 08:49:41 +0200169/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200170 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200171 */
172static const struct ieee80211_supported_band __wl_band_2ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200173 .band = IEEE80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200174 .bitrates = wl_g_rates,
175 .n_bitrates = wl_g_rates_size,
176};
177
Arend van Spriel58de92d2015-04-14 20:10:24 +0200178static const struct ieee80211_supported_band __wl_band_5ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200179 .band = IEEE80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200180 .bitrates = wl_a_rates,
181 .n_bitrates = wl_a_rates_size,
182};
183
Hante Meulemand48200b2013-04-03 12:40:29 +0200184/* This is to override regulatory domains defined in cfg80211 module (reg.c)
185 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200186 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
187 * With respect to these flags, wpa_supplicant doesn't * start p2p
188 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200189 * domain are to be done here.
190 */
191static const struct ieee80211_regdomain brcmf_regdom = {
192 .n_reg_rules = 4,
193 .alpha2 = "99",
194 .reg_rules = {
195 /* IEEE 802.11b/g, channels 1..11 */
196 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
197 /* If any */
198 /* IEEE 802.11 channel 14 - Only JP enables
199 * this and for 802.11b only
200 */
201 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
202 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200203 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200204 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200205 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200206};
207
208static const u32 __wl_cipher_suites[] = {
209 WLAN_CIPHER_SUITE_WEP40,
210 WLAN_CIPHER_SUITE_WEP104,
211 WLAN_CIPHER_SUITE_TKIP,
212 WLAN_CIPHER_SUITE_CCMP,
213 WLAN_CIPHER_SUITE_AES_CMAC,
214};
215
Hante Meuleman1a873342012-09-27 14:17:54 +0200216/* Vendor specific ie. id = 221, oui and type defines exact ie */
217struct brcmf_vs_tlv {
218 u8 id;
219 u8 len;
220 u8 oui[3];
221 u8 oui_type;
222};
223
224struct parsed_vndr_ie_info {
225 u8 *ie_ptr;
226 u32 ie_len; /* total length including id & length field */
227 struct brcmf_vs_tlv vndrie;
228};
229
230struct parsed_vndr_ies {
231 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100232 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200233};
234
Hante Meuleman68ca3952014-02-25 20:30:26 +0100235static int brcmf_roamoff;
236module_param_named(roamoff, brcmf_roamoff, int, S_IRUSR);
237MODULE_PARM_DESC(roamoff, "do not use internal roaming engine");
238
Alwin Beukersef6ac172011-10-12 20:51:26 +0200239
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200240static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
241 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200242{
243 struct brcmu_chan ch_inf;
244 s32 primary_offset;
245
246 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
247 ch->chan->center_freq, ch->center_freq1, ch->width);
248 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
249 primary_offset = ch->center_freq1 - ch->chan->center_freq;
250 switch (ch->width) {
251 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100252 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200253 ch_inf.bw = BRCMU_CHAN_BW_20;
254 WARN_ON(primary_offset != 0);
255 break;
256 case NL80211_CHAN_WIDTH_40:
257 ch_inf.bw = BRCMU_CHAN_BW_40;
258 if (primary_offset < 0)
259 ch_inf.sb = BRCMU_CHAN_SB_U;
260 else
261 ch_inf.sb = BRCMU_CHAN_SB_L;
262 break;
263 case NL80211_CHAN_WIDTH_80:
264 ch_inf.bw = BRCMU_CHAN_BW_80;
265 if (primary_offset < 0) {
266 if (primary_offset < -CH_10MHZ_APART)
267 ch_inf.sb = BRCMU_CHAN_SB_UU;
268 else
269 ch_inf.sb = BRCMU_CHAN_SB_UL;
270 } else {
271 if (primary_offset > CH_10MHZ_APART)
272 ch_inf.sb = BRCMU_CHAN_SB_LL;
273 else
274 ch_inf.sb = BRCMU_CHAN_SB_LU;
275 }
276 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100277 case NL80211_CHAN_WIDTH_80P80:
278 case NL80211_CHAN_WIDTH_160:
279 case NL80211_CHAN_WIDTH_5:
280 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200281 default:
282 WARN_ON_ONCE(1);
283 }
284 switch (ch->chan->band) {
285 case IEEE80211_BAND_2GHZ:
286 ch_inf.band = BRCMU_CHAN_BAND_2G;
287 break;
288 case IEEE80211_BAND_5GHZ:
289 ch_inf.band = BRCMU_CHAN_BAND_5G;
290 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100291 case IEEE80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200292 default:
293 WARN_ON_ONCE(1);
294 }
295 d11inf->encchspec(&ch_inf);
296
297 return ch_inf.chspec;
298}
299
Franky Lin83cf17a2013-04-11 13:28:50 +0200300u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
301 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700302{
Franky Lin83cf17a2013-04-11 13:28:50 +0200303 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700304
Franky Lin83cf17a2013-04-11 13:28:50 +0200305 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
306 ch_inf.bw = BRCMU_CHAN_BW_20;
307 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700308
Franky Lin83cf17a2013-04-11 13:28:50 +0200309 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700310}
311
Hante Meuleman89286dc2013-02-08 15:53:46 +0100312/* Traverse a string of 1-byte tag/1-byte length/variable-length value
313 * triples, returning a pointer to the substring whose first element
314 * matches tag
315 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100316const struct brcmf_tlv *
317brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100318{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100319 const struct brcmf_tlv *elt = buf;
320 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100321
322 /* find tagged parameter */
323 while (totlen >= TLV_HDR_LEN) {
324 int len = elt->len;
325
326 /* validate remaining totlen */
327 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
328 return elt;
329
330 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
331 totlen -= (len + TLV_HDR_LEN);
332 }
333
334 return NULL;
335}
336
337/* Is any of the tlvs the expected entry? If
338 * not update the tlvs buffer pointer/length.
339 */
340static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100341brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
342 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100343{
344 /* If the contents match the OUI and the type */
345 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
346 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
347 type == ie[TLV_BODY_OFF + oui_len]) {
348 return true;
349 }
350
351 if (tlvs == NULL)
352 return false;
353 /* point to the next ie */
354 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
355 /* calculate the length of the rest of the buffer */
356 *tlvs_len -= (int)(ie - *tlvs);
357 /* update the pointer to the start of the buffer */
358 *tlvs = ie;
359
360 return false;
361}
362
363static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100364brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100365{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100366 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100367
368 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100369 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100370 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
371 return (struct brcmf_vs_tlv *)ie;
372 }
373 return NULL;
374}
375
376static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100377brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100378{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100379 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100380
381 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
382 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
383 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
384 return (struct brcmf_vs_tlv *)ie;
385 }
386 return NULL;
387}
388
Arend van Spriel39504a22015-08-20 22:06:05 +0200389static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
390 struct brcmf_cfg80211_vif *vif,
391 enum nl80211_iftype new_type)
392{
393 int iftype_num[NUM_NL80211_IFTYPES];
394 struct brcmf_cfg80211_vif *pos;
395
396 memset(&iftype_num[0], 0, sizeof(iftype_num));
397 list_for_each_entry(pos, &cfg->vif_list, list)
398 if (pos == vif)
399 iftype_num[new_type]++;
400 else
401 iftype_num[pos->wdev.iftype]++;
402
403 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
404}
405
406static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
407 enum nl80211_iftype new_type)
408{
409 int iftype_num[NUM_NL80211_IFTYPES];
410 struct brcmf_cfg80211_vif *pos;
411
412 memset(&iftype_num[0], 0, sizeof(iftype_num));
413 list_for_each_entry(pos, &cfg->vif_list, list)
414 iftype_num[pos->wdev.iftype]++;
415
416 iftype_num[new_type]++;
417 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
418}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100419
Arend van Spriel5b435de2011-10-05 13:19:03 +0200420static void convert_key_from_CPU(struct brcmf_wsec_key *key,
421 struct brcmf_wsec_key_le *key_le)
422{
423 key_le->index = cpu_to_le32(key->index);
424 key_le->len = cpu_to_le32(key->len);
425 key_le->algo = cpu_to_le32(key->algo);
426 key_le->flags = cpu_to_le32(key->flags);
427 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
428 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
429 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
430 memcpy(key_le->data, key->data, sizeof(key->data));
431 memcpy(key_le->ea, key->ea, sizeof(key->ea));
432}
433
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200434static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100435send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200436{
437 int err;
438 struct brcmf_wsec_key_le key_le;
439
440 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200441
Hante Meuleman118eb302014-12-21 12:43:49 +0100442 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700443
Hante Meuleman118eb302014-12-21 12:43:49 +0100444 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700445 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200446
Arend van Spriel5b435de2011-10-05 13:19:03 +0200447 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100448 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200449 return err;
450}
451
Hante Meulemanb3657452013-05-27 21:09:53 +0200452static s32
453brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
454{
455 s32 err;
456 u32 mode;
457
458 if (enable)
459 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
460 else
461 mode = 0;
462
463 /* Try to set and enable ARP offload feature, this may fail, then it */
464 /* is simply not supported and err 0 will be returned */
465 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
466 if (err) {
467 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
468 mode, err);
469 err = 0;
470 } else {
471 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
472 if (err) {
473 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
474 enable, err);
475 err = 0;
476 } else
477 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
478 enable, mode);
479 }
480
481 return err;
482}
483
Hante Meuleman8851cce2014-07-30 13:20:02 +0200484static void
485brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
486{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200487 struct brcmf_cfg80211_vif *vif;
488 struct brcmf_if *ifp;
489
490 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
491 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200492
493 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
494 (wdev->iftype == NL80211_IFTYPE_AP) ||
495 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
496 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
497 ADDR_DIRECT);
498 else
499 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
500 ADDR_INDIRECT);
501}
502
Hante Meulemana44aa402014-12-03 21:05:33 +0100503static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
504{
505 struct brcmf_mbss_ssid_le mbss_ssid_le;
506 int bsscfgidx;
507 int err;
508
509 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
510 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
511 if (bsscfgidx < 0)
512 return bsscfgidx;
513
514 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
515 mbss_ssid_le.SSID_len = cpu_to_le32(5);
516 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
517
518 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
519 sizeof(mbss_ssid_le));
520 if (err < 0)
521 brcmf_err("setting ssid failed %d\n", err);
522
523 return err;
524}
525
526/**
527 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
528 *
529 * @wiphy: wiphy device of new interface.
530 * @name: name of the new interface.
531 * @flags: not used.
532 * @params: contains mac address for AP device.
533 */
534static
535struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
536 u32 *flags, struct vif_params *params)
537{
538 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
539 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
540 struct brcmf_cfg80211_vif *vif;
541 int err;
542
543 if (brcmf_cfg80211_vif_event_armed(cfg))
544 return ERR_PTR(-EBUSY);
545
546 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
547
548 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
549 if (IS_ERR(vif))
550 return (struct wireless_dev *)vif;
551
552 brcmf_cfg80211_arm_vif_event(cfg, vif);
553
554 err = brcmf_cfg80211_request_ap_if(ifp);
555 if (err) {
556 brcmf_cfg80211_arm_vif_event(cfg, NULL);
557 goto fail;
558 }
559
560 /* wait for firmware event */
561 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
562 msecs_to_jiffies(1500));
563 brcmf_cfg80211_arm_vif_event(cfg, NULL);
564 if (!err) {
565 brcmf_err("timeout occurred\n");
566 err = -EIO;
567 goto fail;
568 }
569
570 /* interface created in firmware */
571 ifp = vif->ifp;
572 if (!ifp) {
573 brcmf_err("no if pointer provided\n");
574 err = -ENOENT;
575 goto fail;
576 }
577
578 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
579 err = brcmf_net_attach(ifp, true);
580 if (err) {
581 brcmf_err("Registering netdevice failed\n");
582 goto fail;
583 }
584
585 return &ifp->vif->wdev;
586
587fail:
588 brcmf_free_vif(vif);
589 return ERR_PTR(err);
590}
591
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100592static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
593{
594 enum nl80211_iftype iftype;
595
596 iftype = vif->wdev.iftype;
597 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
598}
599
600static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
601{
602 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
603}
604
Arend van Spriel9f440b72013-02-08 15:53:36 +0100605static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
606 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100607 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100608 enum nl80211_iftype type,
609 u32 *flags,
610 struct vif_params *params)
611{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200612 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200613 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200614
Arend van Spriel9f440b72013-02-08 15:53:36 +0100615 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200616 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
617 if (err) {
618 brcmf_err("iface validation failed: err=%d\n", err);
619 return ERR_PTR(err);
620 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100621 switch (type) {
622 case NL80211_IFTYPE_ADHOC:
623 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100624 case NL80211_IFTYPE_AP_VLAN:
625 case NL80211_IFTYPE_WDS:
626 case NL80211_IFTYPE_MONITOR:
627 case NL80211_IFTYPE_MESH_POINT:
628 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100629 case NL80211_IFTYPE_AP:
630 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
631 if (!IS_ERR(wdev))
632 brcmf_cfg80211_update_proto_addr_mode(wdev);
633 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100634 case NL80211_IFTYPE_P2P_CLIENT:
635 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200636 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100637 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200638 if (!IS_ERR(wdev))
639 brcmf_cfg80211_update_proto_addr_mode(wdev);
640 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100641 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100642 default:
643 return ERR_PTR(-EINVAL);
644 }
645}
646
Daniel Kim5e787f72014-06-21 12:11:18 +0200647static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
648{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200649 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200650 brcmf_set_mpc(ifp, mpc);
651}
652
Arend van Sprielf96aa072013-04-05 10:57:48 +0200653void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100654{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100655 s32 err = 0;
656
657 if (check_vif_up(ifp->vif)) {
658 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
659 if (err) {
660 brcmf_err("fail to set mpc\n");
661 return;
662 }
663 brcmf_dbg(INFO, "MPC : %d\n", mpc);
664 }
665}
666
Arend van Spriela0f472a2013-04-05 10:57:49 +0200667s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
668 struct brcmf_if *ifp, bool aborted,
669 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100670{
671 struct brcmf_scan_params_le params_le;
672 struct cfg80211_scan_request *scan_request;
673 s32 err = 0;
674
675 brcmf_dbg(SCAN, "Enter\n");
676
677 /* clear scan request, because the FW abort can cause a second call */
678 /* to this functon and might cause a double cfg80211_scan_done */
679 scan_request = cfg->scan_request;
680 cfg->scan_request = NULL;
681
682 if (timer_pending(&cfg->escan_timeout))
683 del_timer_sync(&cfg->escan_timeout);
684
685 if (fw_abort) {
686 /* Do a scan abort to stop the driver's scan engine */
687 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
688 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800689 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100690 params_le.bss_type = DOT11_BSSTYPE_ANY;
691 params_le.scan_type = 0;
692 params_le.channel_num = cpu_to_le32(1);
693 params_le.nprobes = cpu_to_le32(1);
694 params_le.active_time = cpu_to_le32(-1);
695 params_le.passive_time = cpu_to_le32(-1);
696 params_le.home_time = cpu_to_le32(-1);
697 /* Scan is aborted by setting channel_list[0] to -1 */
698 params_le.channel_list[0] = cpu_to_le16(-1);
699 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200700 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100701 &params_le, sizeof(params_le));
702 if (err)
703 brcmf_err("Scan abort failed\n");
704 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200705
Daniel Kim5e787f72014-06-21 12:11:18 +0200706 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200707
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100708 /*
709 * e-scan can be initiated by scheduled scan
710 * which takes precedence.
711 */
712 if (cfg->sched_escan) {
713 brcmf_dbg(SCAN, "scheduled scan completed\n");
714 cfg->sched_escan = false;
715 if (!aborted)
716 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100717 } else if (scan_request) {
718 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
719 aborted ? "Aborted" : "Done");
720 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100721 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100722 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
723 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100724
725 return err;
726}
727
Arend van Spriel9f440b72013-02-08 15:53:36 +0100728static
729int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
730{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100731 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
732 struct net_device *ndev = wdev->netdev;
733
734 /* vif event pending in firmware */
735 if (brcmf_cfg80211_vif_event_armed(cfg))
736 return -EBUSY;
737
738 if (ndev) {
739 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200740 cfg->escan_info.ifp == netdev_priv(ndev))
741 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
742 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100743
744 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
745 }
746
Arend van Spriel9f440b72013-02-08 15:53:36 +0100747 switch (wdev->iftype) {
748 case NL80211_IFTYPE_ADHOC:
749 case NL80211_IFTYPE_STATION:
750 case NL80211_IFTYPE_AP:
751 case NL80211_IFTYPE_AP_VLAN:
752 case NL80211_IFTYPE_WDS:
753 case NL80211_IFTYPE_MONITOR:
754 case NL80211_IFTYPE_MESH_POINT:
755 return -EOPNOTSUPP;
756 case NL80211_IFTYPE_P2P_CLIENT:
757 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200758 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100759 return brcmf_p2p_del_vif(wiphy, wdev);
760 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100761 default:
762 return -EINVAL;
763 }
764 return -EOPNOTSUPP;
765}
766
Arend van Spriel5b435de2011-10-05 13:19:03 +0200767static s32
768brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
769 enum nl80211_iftype type, u32 *flags,
770 struct vif_params *params)
771{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100772 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700773 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100774 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200775 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200776 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200777 s32 err = 0;
778
Arend van Spriel39504a22015-08-20 22:06:05 +0200779 brcmf_dbg(TRACE, "Enter, idx=%d, type=%d\n", ifp->bssidx, type);
780 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
781 if (err) {
782 brcmf_err("iface validation failed: err=%d\n", err);
783 return err;
784 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200785 switch (type) {
786 case NL80211_IFTYPE_MONITOR:
787 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100788 brcmf_err("type (%d) : currently we do not support this type\n",
789 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200790 return -EOPNOTSUPP;
791 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200792 infra = 0;
793 break;
794 case NL80211_IFTYPE_STATION:
Hante Meuleman1bc7c652013-02-08 15:53:56 +0100795 /* Ignore change for p2p IF. Unclear why supplicant does this */
796 if ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
797 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO)) {
798 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
799 /* WAR: It is unexpected to get a change of VIF for P2P
800 * IF, but it happens. The request can not be handled
801 * but returning EPERM causes a crash. Returning 0
802 * without setting ieee80211_ptr->iftype causes trace
803 * (WARN_ON) but it works with wpa_supplicant
804 */
805 return 0;
806 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200807 infra = 1;
808 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200809 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100810 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200811 ap = 1;
812 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200813 default:
814 err = -EINVAL;
815 goto done;
816 }
817
Hante Meuleman1a873342012-09-27 14:17:54 +0200818 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100819 if (type == NL80211_IFTYPE_P2P_GO) {
820 brcmf_dbg(INFO, "IF Type = P2P GO\n");
821 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
822 }
823 if (!err) {
824 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &vif->sme_state);
825 brcmf_dbg(INFO, "IF Type = AP\n");
826 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200827 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100828 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200829 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100830 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200831 err = -EAGAIN;
832 goto done;
833 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100834 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100835 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200836 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200837 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200838
Hante Meuleman8851cce2014-07-30 13:20:02 +0200839 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
840
Arend van Spriel5b435de2011-10-05 13:19:03 +0200841done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100842 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200843
844 return err;
845}
846
Franky Lin83cf17a2013-04-11 13:28:50 +0200847static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
848 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200849 struct cfg80211_scan_request *request)
850{
851 u32 n_ssids;
852 u32 n_channels;
853 s32 i;
854 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200855 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200856 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200857 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200858
Joe Perches93803b32015-03-02 19:54:49 -0800859 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200860 params_le->bss_type = DOT11_BSSTYPE_ANY;
861 params_le->scan_type = 0;
862 params_le->channel_num = 0;
863 params_le->nprobes = cpu_to_le32(-1);
864 params_le->active_time = cpu_to_le32(-1);
865 params_le->passive_time = cpu_to_le32(-1);
866 params_le->home_time = cpu_to_le32(-1);
867 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
868
869 /* if request is null exit so it will be all channel broadcast scan */
870 if (!request)
871 return;
872
873 n_ssids = request->n_ssids;
874 n_channels = request->n_channels;
875 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100876 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
877 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200878 if (n_channels > 0) {
879 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200880 chanspec = channel_to_chanspec(&cfg->d11inf,
881 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100882 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
883 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200884 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200885 }
886 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100887 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200888 }
889 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100890 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200891 if (n_ssids > 0) {
892 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
893 n_channels * sizeof(u16);
894 offset = roundup(offset, sizeof(u32));
895 ptr = (char *)params_le + offset;
896 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200897 memset(&ssid_le, 0, sizeof(ssid_le));
898 ssid_le.SSID_len =
899 cpu_to_le32(request->ssids[i].ssid_len);
900 memcpy(ssid_le.SSID, request->ssids[i].ssid,
901 request->ssids[i].ssid_len);
902 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100903 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200904 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100905 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
906 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200907 memcpy(ptr, &ssid_le, sizeof(ssid_le));
908 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200909 }
910 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100911 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200912 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100913 brcmf_dbg(SCAN, "SSID %s len=%d\n",
914 params_le->ssid_le.SSID,
915 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200916 params_le->ssid_le.SSID_len =
917 cpu_to_le32(request->ssids->ssid_len);
918 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
919 request->ssids->ssid_len);
920 }
921 }
922 /* Adding mask to channel numbers */
923 params_le->channel_num =
924 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
925 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
926}
927
928static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200929brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +0200930 struct cfg80211_scan_request *request, u16 action)
931{
932 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
933 offsetof(struct brcmf_escan_params_le, params_le);
934 struct brcmf_escan_params_le *params;
935 s32 err = 0;
936
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100937 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200938
939 if (request != NULL) {
940 /* Allocate space for populating ssids in struct */
941 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
942
943 /* Allocate space for populating ssids in struct */
944 params_size += sizeof(struct brcmf_ssid) * request->n_ssids;
945 }
946
947 params = kzalloc(params_size, GFP_KERNEL);
948 if (!params) {
949 err = -ENOMEM;
950 goto exit;
951 }
952 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200953 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200954 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
955 params->action = cpu_to_le16(action);
956 params->sync_id = cpu_to_le16(0x1234);
957
Arend van Spriela0f472a2013-04-05 10:57:49 +0200958 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200959 if (err) {
960 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100961 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200962 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100963 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200964 }
965
966 kfree(params);
967exit:
968 return err;
969}
970
971static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200972brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +0200973 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200974{
975 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700976 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +0200977 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100978 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +0200979
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100980 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +0200981 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100982 escan->wiphy = wiphy;
983 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700984 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +0200985 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700986 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +0200987 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100988 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200989 return err;
990 }
Daniel Kim5e787f72014-06-21 12:11:18 +0200991 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +0200992 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +0200993 results->version = 0;
994 results->count = 0;
995 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
996
Arend van Spriela0f472a2013-04-05 10:57:49 +0200997 err = escan->run(cfg, ifp, request, WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200998 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +0200999 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001000 return err;
1001}
1002
1003static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001004brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001005 struct cfg80211_scan_request *request,
1006 struct cfg80211_ssid *this_ssid)
1007{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001008 struct brcmf_if *ifp = vif->ifp;
1009 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001010 struct cfg80211_ssid *ssids;
Hante Meulemanf07998952012-11-05 16:22:13 -08001011 struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001012 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001013 bool escan_req;
1014 bool spec_scan;
1015 s32 err;
1016 u32 SSID_len;
1017
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001018 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001019
Arend van Sprielc1179032012-10-22 13:55:33 -07001020 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001021 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001022 return -EAGAIN;
1023 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001024 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001025 brcmf_err("Scanning being aborted: status (%lu)\n",
1026 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001027 return -EAGAIN;
1028 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001029 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1030 brcmf_err("Scanning suppressed: status (%lu)\n",
1031 cfg->scan_status);
1032 return -EAGAIN;
1033 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001034 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001035 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001036 return -EAGAIN;
1037 }
1038
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001039 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001040 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1041 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001042
Hante Meulemane756af52012-09-11 21:18:52 +02001043 escan_req = false;
1044 if (request) {
1045 /* scan bss */
1046 ssids = request->ssids;
1047 escan_req = true;
1048 } else {
1049 /* scan in ibss */
1050 /* we don't do escan in ibss */
1051 ssids = this_ssid;
1052 }
1053
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001054 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001055 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001056 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001057 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001058 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001059 if (err)
1060 goto scan_out;
1061
Arend van Spriela0f472a2013-04-05 10:57:49 +02001062 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001063 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001064 goto scan_out;
1065 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001066 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1067 ssids->ssid, ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001068 memset(&sr->ssid_le, 0, sizeof(sr->ssid_le));
1069 SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len);
1070 sr->ssid_le.SSID_len = cpu_to_le32(0);
1071 spec_scan = false;
1072 if (SSID_len) {
1073 memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len);
1074 sr->ssid_le.SSID_len = cpu_to_le32(SSID_len);
1075 spec_scan = true;
1076 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001077 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001078
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001079 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001080 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001081 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001082 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001083 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001084 goto scan_out;
1085 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001086 brcmf_scan_config_mpc(ifp, 0);
Arend van Sprielc1179032012-10-22 13:55:33 -07001087 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Sprielac24be62012-10-22 10:36:23 -07001088 &sr->ssid_le, sizeof(sr->ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001089 if (err) {
1090 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001091 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
1092 sr->ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001093 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001094 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001095
Daniel Kim5e787f72014-06-21 12:11:18 +02001096 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001097 goto scan_out;
1098 }
1099 }
1100
Hante Meuleman661fa952015-02-06 18:36:47 +01001101 /* Arm scan timeout timer */
1102 mod_timer(&cfg->escan_timeout, jiffies +
1103 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1104
Hante Meulemane756af52012-09-11 21:18:52 +02001105 return 0;
1106
1107scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001108 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001109 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001110 return err;
1111}
1112
Arend van Spriel5b435de2011-10-05 13:19:03 +02001113static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001114brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001115{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001116 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001117 s32 err = 0;
1118
Arend van Sprield96b8012012-12-05 15:26:02 +01001119 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001120 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1121 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001122 return -EIO;
1123
Arend van Spriela0f472a2013-04-05 10:57:49 +02001124 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001125
Arend van Spriel5b435de2011-10-05 13:19:03 +02001126 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001127 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001128
Arend van Sprield96b8012012-12-05 15:26:02 +01001129 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001130 return err;
1131}
1132
1133static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1134{
1135 s32 err = 0;
1136
Arend van Sprielac24be62012-10-22 10:36:23 -07001137 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1138 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001139 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001140 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001141
1142 return err;
1143}
1144
1145static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1146{
1147 s32 err = 0;
1148
Arend van Sprielac24be62012-10-22 10:36:23 -07001149 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1150 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001151 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001152 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001153
1154 return err;
1155}
1156
1157static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1158{
1159 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001160 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001161
Arend van Sprielac24be62012-10-22 10:36:23 -07001162 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001163 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001164 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001165 return err;
1166 }
1167 return err;
1168}
1169
1170static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1171{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001172 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1173 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001174 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001175 s32 err = 0;
1176
Arend van Sprield96b8012012-12-05 15:26:02 +01001177 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001178 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001179 return -EIO;
1180
1181 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001182 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1183 cfg->conf->rts_threshold = wiphy->rts_threshold;
1184 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001185 if (!err)
1186 goto done;
1187 }
1188 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001189 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1190 cfg->conf->frag_threshold = wiphy->frag_threshold;
1191 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001192 if (!err)
1193 goto done;
1194 }
1195 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001196 && (cfg->conf->retry_long != wiphy->retry_long)) {
1197 cfg->conf->retry_long = wiphy->retry_long;
1198 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001199 if (!err)
1200 goto done;
1201 }
1202 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001203 && (cfg->conf->retry_short != wiphy->retry_short)) {
1204 cfg->conf->retry_short = wiphy->retry_short;
1205 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001206 if (!err)
1207 goto done;
1208 }
1209
1210done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001211 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001212 return err;
1213}
1214
Arend van Spriel5b435de2011-10-05 13:19:03 +02001215static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1216{
1217 memset(prof, 0, sizeof(*prof));
1218}
1219
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001220static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1221{
1222 u16 reason;
1223
1224 switch (e->event_code) {
1225 case BRCMF_E_DEAUTH:
1226 case BRCMF_E_DEAUTH_IND:
1227 case BRCMF_E_DISASSOC_IND:
1228 reason = e->reason;
1229 break;
1230 case BRCMF_E_LINK:
1231 default:
1232 reason = 0;
1233 break;
1234 }
1235 return reason;
1236}
1237
1238static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001239{
Piotr Haber61730d42013-04-23 12:53:12 +02001240 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001241 s32 err = 0;
1242
Arend van Sprield96b8012012-12-05 15:26:02 +01001243 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001244
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001245 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001246 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001247 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001248 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001249 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001250 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001251 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001252 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state);
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001253 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
Johannes Berg80279fb2015-05-22 16:22:20 +02001254 true, GFP_KERNEL);
Arend van Spriel43dffbc2014-01-06 12:40:46 +01001255
Arend van Spriel5b435de2011-10-05 13:19:03 +02001256 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001257 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001258 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1259 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001260 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001261}
1262
1263static s32
1264brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1265 struct cfg80211_ibss_params *params)
1266{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001267 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001268 struct brcmf_if *ifp = netdev_priv(ndev);
1269 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001270 struct brcmf_join_params join_params;
1271 size_t join_params_size = 0;
1272 s32 err = 0;
1273 s32 wsec = 0;
1274 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001275 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001276
Arend van Sprield96b8012012-12-05 15:26:02 +01001277 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001278 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001279 return -EIO;
1280
1281 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001282 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001283 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001284 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001285 return -EOPNOTSUPP;
1286 }
1287
Arend van Sprielc1179032012-10-22 13:55:33 -07001288 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001289
1290 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001291 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001292 else
Arend van Spriel16886732012-12-05 15:26:04 +01001293 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001294
Johannes Berg683b6d32012-11-08 21:25:48 +01001295 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001296 brcmf_dbg(CONN, "channel: %d\n",
1297 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001298 else
Arend van Spriel16886732012-12-05 15:26:04 +01001299 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001300
1301 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001302 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001303 else
Arend van Spriel16886732012-12-05 15:26:04 +01001304 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001305
1306 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001307 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001308 else
Arend van Spriel16886732012-12-05 15:26:04 +01001309 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001310
1311 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001312 brcmf_dbg(CONN, "beacon interval: %d\n",
1313 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001314 else
Arend van Spriel16886732012-12-05 15:26:04 +01001315 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001316
1317 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001318 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001319 else
Arend van Spriel16886732012-12-05 15:26:04 +01001320 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321
1322 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001323 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001324 else
Arend van Spriel16886732012-12-05 15:26:04 +01001325 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001326
1327 /* Configure Privacy for starter */
1328 if (params->privacy)
1329 wsec |= WEP_ENABLED;
1330
Arend van Sprielc1179032012-10-22 13:55:33 -07001331 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001332 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001333 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334 goto done;
1335 }
1336
1337 /* Configure Beacon Interval for starter */
1338 if (params->beacon_interval)
1339 bcnprd = params->beacon_interval;
1340 else
1341 bcnprd = 100;
1342
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001343 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001344 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001345 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001346 goto done;
1347 }
1348
1349 /* Configure required join parameter */
1350 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1351
1352 /* SSID */
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001353 profile->ssid.SSID_len = min_t(u32, params->ssid_len, 32);
1354 memcpy(profile->ssid.SSID, params->ssid, profile->ssid.SSID_len);
1355 memcpy(join_params.ssid_le.SSID, params->ssid, profile->ssid.SSID_len);
1356 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001357 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001358
1359 /* BSSID */
1360 if (params->bssid) {
1361 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1362 join_params_size = sizeof(join_params.ssid_le) +
1363 BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001364 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001365 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001366 eth_broadcast_addr(join_params.params_le.bssid);
1367 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001368 }
1369
Arend van Spriel5b435de2011-10-05 13:19:03 +02001370 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001371 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001372 u32 target_channel;
1373
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001374 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001375 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001376 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001377 if (params->channel_fixed) {
1378 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001379 chanspec = chandef_to_chanspec(&cfg->d11inf,
1380 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001381 join_params.params_le.chanspec_list[0] =
1382 cpu_to_le16(chanspec);
1383 join_params.params_le.chanspec_num = cpu_to_le32(1);
1384 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001385 }
1386
1387 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001388 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001389 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001390 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001391 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001392 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001393 goto done;
1394 }
1395 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001396 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001397
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001398 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001399
1400
Arend van Sprielc1179032012-10-22 13:55:33 -07001401 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001402 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001403 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001404 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001405 goto done;
1406 }
1407
1408done:
1409 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001410 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001411 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001412 return err;
1413}
1414
1415static s32
1416brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1417{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001418 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001419
Arend van Sprield96b8012012-12-05 15:26:02 +01001420 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001421 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001422 return -EIO;
1423
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001424 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001425
Arend van Sprield96b8012012-12-05 15:26:02 +01001426 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001427
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001428 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001429}
1430
1431static s32 brcmf_set_wpa_version(struct net_device *ndev,
1432 struct cfg80211_connect_params *sme)
1433{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001434 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001435 struct brcmf_cfg80211_security *sec;
1436 s32 val = 0;
1437 s32 err = 0;
1438
1439 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1440 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1441 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1442 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1443 else
1444 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001445 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001446 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001447 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001448 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001449 return err;
1450 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001451 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001452 sec->wpa_versions = sme->crypto.wpa_versions;
1453 return err;
1454}
1455
1456static s32 brcmf_set_auth_type(struct net_device *ndev,
1457 struct cfg80211_connect_params *sme)
1458{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001459 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001460 struct brcmf_cfg80211_security *sec;
1461 s32 val = 0;
1462 s32 err = 0;
1463
1464 switch (sme->auth_type) {
1465 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1466 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001467 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001468 break;
1469 case NL80211_AUTHTYPE_SHARED_KEY:
1470 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001471 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001472 break;
1473 case NL80211_AUTHTYPE_AUTOMATIC:
1474 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001475 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001476 break;
1477 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001478 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001479 default:
1480 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001481 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001482 break;
1483 }
1484
Hante Meuleman89286dc2013-02-08 15:53:46 +01001485 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001486 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001487 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001488 return err;
1489 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001490 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001491 sec->auth_type = sme->auth_type;
1492 return err;
1493}
1494
1495static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001496brcmf_set_wsec_mode(struct net_device *ndev,
1497 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001498{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001499 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001500 struct brcmf_cfg80211_security *sec;
1501 s32 pval = 0;
1502 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001503 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001504 s32 err = 0;
1505
1506 if (sme->crypto.n_ciphers_pairwise) {
1507 switch (sme->crypto.ciphers_pairwise[0]) {
1508 case WLAN_CIPHER_SUITE_WEP40:
1509 case WLAN_CIPHER_SUITE_WEP104:
1510 pval = WEP_ENABLED;
1511 break;
1512 case WLAN_CIPHER_SUITE_TKIP:
1513 pval = TKIP_ENABLED;
1514 break;
1515 case WLAN_CIPHER_SUITE_CCMP:
1516 pval = AES_ENABLED;
1517 break;
1518 case WLAN_CIPHER_SUITE_AES_CMAC:
1519 pval = AES_ENABLED;
1520 break;
1521 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001522 brcmf_err("invalid cipher pairwise (%d)\n",
1523 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001524 return -EINVAL;
1525 }
1526 }
1527 if (sme->crypto.cipher_group) {
1528 switch (sme->crypto.cipher_group) {
1529 case WLAN_CIPHER_SUITE_WEP40:
1530 case WLAN_CIPHER_SUITE_WEP104:
1531 gval = WEP_ENABLED;
1532 break;
1533 case WLAN_CIPHER_SUITE_TKIP:
1534 gval = TKIP_ENABLED;
1535 break;
1536 case WLAN_CIPHER_SUITE_CCMP:
1537 gval = AES_ENABLED;
1538 break;
1539 case WLAN_CIPHER_SUITE_AES_CMAC:
1540 gval = AES_ENABLED;
1541 break;
1542 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001543 brcmf_err("invalid cipher group (%d)\n",
1544 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001545 return -EINVAL;
1546 }
1547 }
1548
Arend van Spriel16886732012-12-05 15:26:04 +01001549 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001550 /* In case of privacy, but no security and WPS then simulate */
1551 /* setting AES. WPS-2.0 allows no security */
1552 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1553 sme->privacy)
1554 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001555
1556 if (mfp)
1557 wsec = pval | gval | MFP_CAPABLE;
1558 else
1559 wsec = pval | gval;
1560 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001561 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001562 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001563 return err;
1564 }
1565
Arend van Spriel06bb1232012-09-27 14:17:56 +02001566 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001567 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1568 sec->cipher_group = sme->crypto.cipher_group;
1569
1570 return err;
1571}
1572
1573static s32
1574brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1575{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001576 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001577 struct brcmf_cfg80211_security *sec;
1578 s32 val = 0;
1579 s32 err = 0;
1580
1581 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001582 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1583 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001584 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001585 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001586 return err;
1587 }
1588 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1589 switch (sme->crypto.akm_suites[0]) {
1590 case WLAN_AKM_SUITE_8021X:
1591 val = WPA_AUTH_UNSPECIFIED;
1592 break;
1593 case WLAN_AKM_SUITE_PSK:
1594 val = WPA_AUTH_PSK;
1595 break;
1596 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001597 brcmf_err("invalid cipher group (%d)\n",
1598 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001599 return -EINVAL;
1600 }
1601 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1602 switch (sme->crypto.akm_suites[0]) {
1603 case WLAN_AKM_SUITE_8021X:
1604 val = WPA2_AUTH_UNSPECIFIED;
1605 break;
1606 case WLAN_AKM_SUITE_PSK:
1607 val = WPA2_AUTH_PSK;
1608 break;
1609 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001610 brcmf_err("invalid cipher group (%d)\n",
1611 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001612 return -EINVAL;
1613 }
1614 }
1615
Arend van Spriel16886732012-12-05 15:26:04 +01001616 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001617 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1618 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001619 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001620 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001621 return err;
1622 }
1623 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001624 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001625 sec->wpa_auth = sme->crypto.akm_suites[0];
1626
1627 return err;
1628}
1629
1630static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001631brcmf_set_sharedkey(struct net_device *ndev,
1632 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001633{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001634 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001635 struct brcmf_cfg80211_security *sec;
1636 struct brcmf_wsec_key key;
1637 s32 val;
1638 s32 err = 0;
1639
Arend van Spriel16886732012-12-05 15:26:04 +01001640 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001641
Roland Vossena718e2f2011-10-12 20:51:24 +02001642 if (sme->key_len == 0)
1643 return 0;
1644
Arend van Spriel06bb1232012-09-27 14:17:56 +02001645 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001646 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1647 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001648
1649 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1650 return 0;
1651
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001652 if (!(sec->cipher_pairwise &
1653 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1654 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001655
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001656 memset(&key, 0, sizeof(key));
1657 key.len = (u32) sme->key_len;
1658 key.index = (u32) sme->key_idx;
1659 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001660 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001661 return -EINVAL;
1662 }
1663 memcpy(key.data, sme->key, key.len);
1664 key.flags = BRCMF_PRIMARY_KEY;
1665 switch (sec->cipher_pairwise) {
1666 case WLAN_CIPHER_SUITE_WEP40:
1667 key.algo = CRYPTO_ALGO_WEP1;
1668 break;
1669 case WLAN_CIPHER_SUITE_WEP104:
1670 key.algo = CRYPTO_ALGO_WEP128;
1671 break;
1672 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001673 brcmf_err("Invalid algorithm (%d)\n",
1674 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001675 return -EINVAL;
1676 }
1677 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001678 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1679 key.len, key.index, key.algo);
1680 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001681 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001682 if (err)
1683 return err;
1684
1685 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001686 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001687 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001688 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001689 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001690 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001691 }
1692 return err;
1693}
1694
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001695static
1696enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1697 enum nl80211_auth_type type)
1698{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001699 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1700 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1701 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1702 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001703 }
1704 return type;
1705}
1706
Arend van Spriel5b435de2011-10-05 13:19:03 +02001707static s32
1708brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001709 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001710{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001711 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001712 struct brcmf_if *ifp = netdev_priv(ndev);
1713 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001714 struct ieee80211_channel *chan = sme->channel;
1715 struct brcmf_join_params join_params;
1716 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001717 const struct brcmf_tlv *rsn_ie;
1718 const struct brcmf_vs_tlv *wpa_ie;
1719 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001720 u32 ie_len;
1721 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001722 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001723 s32 err = 0;
1724
Arend van Sprield96b8012012-12-05 15:26:02 +01001725 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001726 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001727 return -EIO;
1728
1729 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001730 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001731 return -EOPNOTSUPP;
1732 }
1733
Hante Meuleman89286dc2013-02-08 15:53:46 +01001734 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1735 /* A normal (non P2P) connection request setup. */
1736 ie = NULL;
1737 ie_len = 0;
1738 /* find the WPA_IE */
1739 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1740 if (wpa_ie) {
1741 ie = wpa_ie;
1742 ie_len = wpa_ie->len + TLV_HDR_LEN;
1743 } else {
1744 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001745 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1746 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001747 WLAN_EID_RSN);
1748 if (rsn_ie) {
1749 ie = rsn_ie;
1750 ie_len = rsn_ie->len + TLV_HDR_LEN;
1751 }
1752 }
1753 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1754 }
1755
1756 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1757 sme->ie, sme->ie_len);
1758 if (err)
1759 brcmf_err("Set Assoc REQ IE Failed\n");
1760 else
1761 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1762
Arend van Sprielc1179032012-10-22 13:55:33 -07001763 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001764
1765 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001766 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001767 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001768 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001769 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1770 cfg->channel, chan->center_freq, chanspec);
1771 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001772 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001773 chanspec = 0;
1774 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001775
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001776 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001777
1778 err = brcmf_set_wpa_version(ndev, sme);
1779 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001780 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001781 goto done;
1782 }
1783
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001784 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001785 err = brcmf_set_auth_type(ndev, sme);
1786 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001787 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001788 goto done;
1789 }
1790
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001791 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001792 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001793 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001794 goto done;
1795 }
1796
1797 err = brcmf_set_key_mgmt(ndev, sme);
1798 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001799 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001800 goto done;
1801 }
1802
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001803 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001804 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001805 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001806 goto done;
1807 }
1808
Hante Meuleman89286dc2013-02-08 15:53:46 +01001809 profile->ssid.SSID_len = min_t(u32, (u32)sizeof(profile->ssid.SSID),
1810 (u32)sme->ssid_len);
1811 memcpy(&profile->ssid.SSID, sme->ssid, profile->ssid.SSID_len);
1812 if (profile->ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
1813 profile->ssid.SSID[profile->ssid.SSID_len] = 0;
1814 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", profile->ssid.SSID,
1815 profile->ssid.SSID_len);
1816 }
1817
1818 /* Join with specific BSSID and cached SSID
1819 * If SSID is zero join based on BSSID only
1820 */
1821 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1822 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1823 if (cfg->channel)
1824 join_params_size += sizeof(u16);
1825 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1826 if (ext_join_params == NULL) {
1827 err = -ENOMEM;
1828 goto done;
1829 }
1830 ext_join_params->ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
1831 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid,
1832 profile->ssid.SSID_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001833
Hante Meuleman89286dc2013-02-08 15:53:46 +01001834 /* Set up join scan parameters */
1835 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001836 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1837
1838 if (sme->bssid)
1839 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1840 else
Joe Perches93803b32015-03-02 19:54:49 -08001841 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001842
1843 if (cfg->channel) {
1844 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1845
1846 ext_join_params->assoc_le.chanspec_list[0] =
1847 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001848 /* Increase dwell time to receive probe response or detect
1849 * beacon from target AP at a noisy air only during connect
1850 * command.
1851 */
1852 ext_join_params->scan_le.active_time =
1853 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1854 ext_join_params->scan_le.passive_time =
1855 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1856 /* To sync with presence period of VSDB GO send probe request
1857 * more frequently. Probe request will be stopped when it gets
1858 * probe response from target AP/GO.
1859 */
1860 ext_join_params->scan_le.nprobes =
1861 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1862 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1863 } else {
1864 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1865 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1866 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001867 }
1868
1869 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1870 join_params_size);
1871 kfree(ext_join_params);
1872 if (!err)
1873 /* This is it. join command worked, we are done */
1874 goto done;
1875
1876 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001877 memset(&join_params, 0, sizeof(join_params));
1878 join_params_size = sizeof(join_params.ssid_le);
1879
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001880 memcpy(&join_params.ssid_le.SSID, sme->ssid, profile->ssid.SSID_len);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001881 join_params.ssid_le.SSID_len = cpu_to_le32(profile->ssid.SSID_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001882
Hante Meuleman89286dc2013-02-08 15:53:46 +01001883 if (sme->bssid)
1884 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1885 else
Joe Perches93803b32015-03-02 19:54:49 -08001886 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001887
Hante Meuleman17012612013-02-06 18:40:44 +01001888 if (cfg->channel) {
1889 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1890 join_params.params_le.chanspec_num = cpu_to_le32(1);
1891 join_params_size += sizeof(join_params.params_le);
1892 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001893 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001894 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001895 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001896 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001897
1898done:
1899 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001900 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001901 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001902 return err;
1903}
1904
1905static s32
1906brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1907 u16 reason_code)
1908{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001909 struct brcmf_if *ifp = netdev_priv(ndev);
1910 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001911 struct brcmf_scb_val_le scbval;
1912 s32 err = 0;
1913
Arend van Sprield96b8012012-12-05 15:26:02 +01001914 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001915 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001916 return -EIO;
1917
Arend van Sprielc1179032012-10-22 13:55:33 -07001918 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001919 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001920 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001921
Arend van Spriel06bb1232012-09-27 14:17:56 +02001922 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001923 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001924 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001925 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001926 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001927 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001928
Arend van Sprield96b8012012-12-05 15:26:02 +01001929 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001930 return err;
1931}
1932
1933static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001934brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001935 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001936{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001937 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001938 struct net_device *ndev = cfg_to_ndev(cfg);
1939 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001940 s32 err;
1941 s32 disable;
1942 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001943
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001944 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07001945 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001946 return -EIO;
1947
1948 switch (type) {
1949 case NL80211_TX_POWER_AUTOMATIC:
1950 break;
1951 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001952 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001953 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001954 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001955 err = -EINVAL;
1956 goto done;
1957 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001958 qdbm = MBM_TO_DBM(4 * mbm);
1959 if (qdbm > 127)
1960 qdbm = 127;
1961 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001962 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001963 default:
1964 brcmf_err("Unsupported type %d\n", type);
1965 err = -EINVAL;
1966 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001967 }
1968 /* Make sure radio is off or on as far as software is concerned */
1969 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001970 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001971 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001972 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001973
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001974 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001975 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001976 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001977
1978done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001979 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001980 return err;
1981}
1982
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001983static s32
1984brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
1985 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001986{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001987 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001988 struct net_device *ndev = cfg_to_ndev(cfg);
1989 struct brcmf_if *ifp = netdev_priv(ndev);
1990 s32 qdbm = 0;
1991 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001992
Arend van Sprield96b8012012-12-05 15:26:02 +01001993 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001994 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001995 return -EIO;
1996
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001997 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001998 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001999 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002000 goto done;
2001 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002002 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002003
2004done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002005 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002006 return err;
2007}
2008
2009static s32
2010brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002011 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002012{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002013 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002014 u32 index;
2015 u32 wsec;
2016 s32 err = 0;
2017
Arend van Sprield96b8012012-12-05 15:26:02 +01002018 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002019 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002020 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002021 return -EIO;
2022
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002023 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002024 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002025 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002026 goto done;
2027 }
2028
2029 if (wsec & WEP_ENABLED) {
2030 /* Just select a new current key */
2031 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002032 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002033 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002034 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002035 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002036 }
2037done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002038 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002039 return err;
2040}
2041
2042static s32
2043brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2044 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2045{
Hante Meuleman992f6062013-04-02 21:06:17 +02002046 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002047 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002048 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002049 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002050
2051 memset(&key, 0, sizeof(key));
2052 key.index = (u32) key_idx;
2053 /* Instead of bcast for ea address for default wep keys,
2054 driver needs it to be Null */
2055 if (!is_multicast_ether_addr(mac_addr))
2056 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2057 key.len = (u32) params->key_len;
2058 /* check for key index change */
2059 if (key.len == 0) {
2060 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002061 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002062 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002063 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002064 } else {
2065 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002066 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002067 return -EINVAL;
2068 }
2069
Arend van Spriel16886732012-12-05 15:26:04 +01002070 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002071 memcpy(key.data, params->key, key.len);
2072
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002073 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002074 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2075 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002076 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2077 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2078 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2079 }
2080
2081 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2082 if (params->seq && params->seq_len == 6) {
2083 /* rx iv */
2084 u8 *ivptr;
2085 ivptr = (u8 *) params->seq;
2086 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2087 (ivptr[3] << 8) | ivptr[2];
2088 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2089 key.iv_initialized = true;
2090 }
2091
2092 switch (params->cipher) {
2093 case WLAN_CIPHER_SUITE_WEP40:
2094 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002095 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002096 break;
2097 case WLAN_CIPHER_SUITE_WEP104:
2098 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002099 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002100 break;
2101 case WLAN_CIPHER_SUITE_TKIP:
2102 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002103 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002104 break;
2105 case WLAN_CIPHER_SUITE_AES_CMAC:
2106 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002107 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002108 break;
2109 case WLAN_CIPHER_SUITE_CCMP:
2110 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002111 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002112 break;
2113 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002114 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002115 return -EINVAL;
2116 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002117 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002118 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002119 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002120 }
2121 return err;
2122}
2123
2124static s32
2125brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2126 u8 key_idx, bool pairwise, const u8 *mac_addr,
2127 struct key_params *params)
2128{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002129 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002130 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002131 s32 val;
2132 s32 wsec;
2133 s32 err = 0;
2134 u8 keybuf[8];
2135
Arend van Sprield96b8012012-12-05 15:26:02 +01002136 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002137 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002138 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002139 return -EIO;
2140
Hante Meuleman118eb302014-12-21 12:43:49 +01002141 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2142 /* we ignore this key index in this case */
2143 brcmf_err("invalid key index (%d)\n", key_idx);
2144 return -EINVAL;
2145 }
2146
Daniel Kim787eb032014-01-29 15:32:23 +01002147 if (mac_addr &&
2148 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2149 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002150 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002151 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2152 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002153
Hante Meuleman118eb302014-12-21 12:43:49 +01002154 key = &ifp->vif->profile.key[key_idx];
2155 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002156
Hante Meuleman118eb302014-12-21 12:43:49 +01002157 if (params->key_len > sizeof(key->data)) {
2158 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002159 err = -EINVAL;
2160 goto done;
2161 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002162 key->len = params->key_len;
2163 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002164
Hante Meuleman118eb302014-12-21 12:43:49 +01002165 memcpy(key->data, params->key, key->len);
2166
2167 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002168 switch (params->cipher) {
2169 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002170 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002171 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002172 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002173 break;
2174 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002175 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002176 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002177 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002178 break;
2179 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002180 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002181 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002182 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2183 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2184 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002185 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002186 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002187 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002188 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002189 break;
2190 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002191 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002192 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002193 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002194 break;
2195 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002196 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002197 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002198 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002199 break;
2200 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002201 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002202 err = -EINVAL;
2203 goto done;
2204 }
2205
Hante Meuleman118eb302014-12-21 12:43:49 +01002206 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002207 if (err)
2208 goto done;
2209
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002210 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002211 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002212 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002213 goto done;
2214 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002215 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002216 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002217 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002218 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002219 goto done;
2220 }
2221
Arend van Spriel5b435de2011-10-05 13:19:03 +02002222done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002223 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002224 return err;
2225}
2226
2227static s32
2228brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2229 u8 key_idx, bool pairwise, const u8 *mac_addr)
2230{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002231 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002232 struct brcmf_wsec_key key;
2233 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002234
Arend van Sprield96b8012012-12-05 15:26:02 +01002235 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002236 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002237 return -EIO;
2238
Hante Meuleman118eb302014-12-21 12:43:49 +01002239 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002240 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002241 return -EINVAL;
2242 }
2243
Arend van Spriel5b435de2011-10-05 13:19:03 +02002244 memset(&key, 0, sizeof(key));
2245
2246 key.index = (u32) key_idx;
2247 key.flags = BRCMF_PRIMARY_KEY;
2248 key.algo = CRYPTO_ALGO_OFF;
2249
Arend van Spriel16886732012-12-05 15:26:04 +01002250 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002251
2252 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002253 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002254
Arend van Sprield96b8012012-12-05 15:26:02 +01002255 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002256 return err;
2257}
2258
2259static s32
2260brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2261 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2262 void (*callback) (void *cookie, struct key_params * params))
2263{
2264 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002265 struct brcmf_if *ifp = netdev_priv(ndev);
2266 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002267 struct brcmf_cfg80211_security *sec;
2268 s32 wsec;
2269 s32 err = 0;
2270
Arend van Sprield96b8012012-12-05 15:26:02 +01002271 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002272 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002273 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002274 return -EIO;
2275
2276 memset(&params, 0, sizeof(params));
2277
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002278 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002279 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002280 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002281 /* Ignore this error, may happen during DISASSOC */
2282 err = -EAGAIN;
2283 goto done;
2284 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002285 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002286 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002287 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2288 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002289 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002290 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2291 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002292 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002293 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002294 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002295 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002296 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002297 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002298 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002299 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002300 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002301 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002302 err = -EINVAL;
2303 goto done;
2304 }
2305 callback(cookie, &params);
2306
2307done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002308 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002309 return err;
2310}
2311
2312static s32
2313brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2314 struct net_device *ndev, u8 key_idx)
2315{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002316 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002317
2318 return -EOPNOTSUPP;
2319}
2320
Hante Meuleman118eb302014-12-21 12:43:49 +01002321static void
2322brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2323{
2324 s32 err;
2325 u8 key_idx;
2326 struct brcmf_wsec_key *key;
2327 s32 wsec;
2328
2329 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2330 key = &ifp->vif->profile.key[key_idx];
2331 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2332 (key->algo == CRYPTO_ALGO_WEP128))
2333 break;
2334 }
2335 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2336 return;
2337
2338 err = send_key_to_dongle(ifp, key);
2339 if (err) {
2340 brcmf_err("Setting WEP key failed (%d)\n", err);
2341 return;
2342 }
2343 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2344 if (err) {
2345 brcmf_err("get wsec error (%d)\n", err);
2346 return;
2347 }
2348 wsec |= WEP_ENABLED;
2349 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2350 if (err)
2351 brcmf_err("set wsec error (%d)\n", err);
2352}
2353
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002354static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2355{
2356 struct nl80211_sta_flag_update *sfu;
2357
2358 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2359 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2360 sfu = &si->sta_flags;
2361 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2362 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2363 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2364 BIT(NL80211_STA_FLAG_AUTHORIZED);
2365 if (fw_sta_flags & BRCMF_STA_WME)
2366 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2367 if (fw_sta_flags & BRCMF_STA_AUTHE)
2368 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2369 if (fw_sta_flags & BRCMF_STA_ASSOC)
2370 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2371 if (fw_sta_flags & BRCMF_STA_AUTHO)
2372 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2373}
2374
2375static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2376{
2377 struct {
2378 __le32 len;
2379 struct brcmf_bss_info_le bss_le;
2380 } *buf;
2381 u16 capability;
2382 int err;
2383
2384 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2385 if (!buf)
2386 return;
2387
2388 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2389 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2390 WL_BSS_INFO_MAX);
2391 if (err) {
2392 brcmf_err("Failed to get bss info (%d)\n", err);
2393 return;
2394 }
2395 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2396 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2397 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2398 capability = le16_to_cpu(buf->bss_le.capability);
2399 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2400 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2401 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2402 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2403 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2404 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2405}
2406
Arend van Spriel5b435de2011-10-05 13:19:03 +02002407static s32
2408brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002409 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002410{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002411 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002412 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002413 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002414 u32 sta_flags;
2415 u32 is_tdls_peer;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002416
Arend van Sprield96b8012012-12-05 15:26:02 +01002417 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002418 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002419 return -EIO;
2420
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002421 memset(&sta_info_le, 0, sizeof(sta_info_le));
2422 memcpy(&sta_info_le, mac, ETH_ALEN);
2423 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2424 &sta_info_le,
2425 sizeof(sta_info_le));
2426 is_tdls_peer = !err;
2427 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002428 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002429 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002430 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002431 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002432 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002433 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002434 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002435 }
2436 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2437 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2438 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2439 sta_flags = le32_to_cpu(sta_info_le.flags);
2440 brcmf_convert_sta_flags(sta_flags, sinfo);
2441 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2442 if (is_tdls_peer)
2443 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2444 else
2445 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2446 if (sta_flags & BRCMF_STA_ASSOC) {
2447 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2448 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2449 brcmf_fill_bss_param(ifp, sinfo);
2450 }
2451 if (sta_flags & BRCMF_STA_SCBSTATS) {
2452 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2453 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2454 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2455 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2456 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2457 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2458 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2459 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2460 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002461 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002462 sinfo->txrate.legacy = le32_to_cpu(sta_info_le.tx_rate);
2463 sinfo->txrate.legacy /= 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002464 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002465 if (sinfo->rx_packets) {
2466 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
2467 sinfo->rxrate.legacy = le32_to_cpu(sta_info_le.rx_rate);
2468 sinfo->rxrate.legacy /= 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002469 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002470 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2471 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2472 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2473 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2474 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2475 }
2476 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002477done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002478 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002479 return err;
2480}
2481
2482static s32
2483brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2484 bool enabled, s32 timeout)
2485{
2486 s32 pm;
2487 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002488 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002489 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002490
Arend van Sprield96b8012012-12-05 15:26:02 +01002491 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002492
2493 /*
2494 * Powersave enable/disable request is coming from the
2495 * cfg80211 even before the interface is up. In that
2496 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002497 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002498 * FW later while initializing the dongle
2499 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002500 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002501 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002502
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002503 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002504 goto done;
2505 }
2506
2507 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002508 /* Do not enable the power save after assoc if it is a p2p interface */
2509 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2510 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2511 pm = PM_OFF;
2512 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002513 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002514
Arend van Sprielc1179032012-10-22 13:55:33 -07002515 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002516 if (err) {
2517 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002518 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002519 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002520 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002521 }
2522done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002523 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002524 return err;
2525}
2526
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002527static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002528 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002529{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002530 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002531 struct ieee80211_channel *notify_channel;
2532 struct cfg80211_bss *bss;
2533 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002534 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002535 u16 channel;
2536 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002537 u16 notify_capability;
2538 u16 notify_interval;
2539 u8 *notify_ie;
2540 size_t notify_ielen;
2541 s32 notify_signal;
2542
2543 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002544 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002545 return 0;
2546 }
2547
Franky Lin83cf17a2013-04-11 13:28:50 +02002548 if (!bi->ctl_ch) {
2549 ch.chspec = le16_to_cpu(bi->chanspec);
2550 cfg->d11inf.decchspec(&ch);
2551 bi->ctl_ch = ch.chnum;
2552 }
2553 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002554
2555 if (channel <= CH_MAX_2G_CHANNEL)
2556 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2557 else
2558 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2559
2560 freq = ieee80211_channel_to_frequency(channel, band->band);
2561 notify_channel = ieee80211_get_channel(wiphy, freq);
2562
Arend van Spriel5b435de2011-10-05 13:19:03 +02002563 notify_capability = le16_to_cpu(bi->capability);
2564 notify_interval = le16_to_cpu(bi->beacon_period);
2565 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2566 notify_ielen = le32_to_cpu(bi->ie_length);
2567 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2568
Arend van Spriel16886732012-12-05 15:26:04 +01002569 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2570 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2571 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2572 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2573 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002574
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002575 bss = cfg80211_inform_bss(wiphy, notify_channel,
2576 CFG80211_BSS_FTYPE_UNKNOWN,
2577 (const u8 *)bi->BSSID,
2578 0, notify_capability,
2579 notify_interval, notify_ie,
2580 notify_ielen, notify_signal,
2581 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002582
Franky Line78946e2011-11-10 20:30:34 +01002583 if (!bss)
2584 return -ENOMEM;
2585
Johannes Berg5b112d32013-02-01 01:49:58 +01002586 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002587
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002588 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002589}
2590
Roland Vossen6f09be02011-10-18 14:03:02 +02002591static struct brcmf_bss_info_le *
2592next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2593{
2594 if (bss == NULL)
2595 return list->bss_info_le;
2596 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2597 le32_to_cpu(bss->length));
2598}
2599
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002600static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002601{
2602 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002603 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002604 s32 err = 0;
2605 int i;
2606
Hante Meulemanef8596e2014-09-30 10:23:13 +02002607 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002608 if (bss_list->count != 0 &&
2609 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002610 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2611 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002612 return -EOPNOTSUPP;
2613 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002614 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002615 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002616 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002617 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002618 if (err)
2619 break;
2620 }
2621 return err;
2622}
2623
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002624static s32 wl_inform_ibss(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02002625 struct net_device *ndev, const u8 *bssid)
2626{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002627 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002628 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002629 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002630 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002631 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002632 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002633 u8 *buf = NULL;
2634 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002635 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002636 u16 notify_capability;
2637 u16 notify_interval;
2638 u8 *notify_ie;
2639 size_t notify_ielen;
2640 s32 notify_signal;
2641
Arend van Sprield96b8012012-12-05 15:26:02 +01002642 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002643
2644 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2645 if (buf == NULL) {
2646 err = -ENOMEM;
2647 goto CleanUp;
2648 }
2649
2650 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2651
Arend van Sprielac24be62012-10-22 10:36:23 -07002652 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2653 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002654 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002655 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002656 goto CleanUp;
2657 }
2658
Roland Vossend34bf642011-10-18 14:03:01 +02002659 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002660
Franky Lin83cf17a2013-04-11 13:28:50 +02002661 ch.chspec = le16_to_cpu(bi->chanspec);
2662 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002663
Franky Lin83cf17a2013-04-11 13:28:50 +02002664 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002665 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2666 else
2667 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2668
Franky Lin83cf17a2013-04-11 13:28:50 +02002669 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002670 notify_channel = ieee80211_get_channel(wiphy, freq);
2671
Arend van Spriel5b435de2011-10-05 13:19:03 +02002672 notify_capability = le16_to_cpu(bi->capability);
2673 notify_interval = le16_to_cpu(bi->beacon_period);
2674 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2675 notify_ielen = le32_to_cpu(bi->ie_length);
2676 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2677
Franky Lin83cf17a2013-04-11 13:28:50 +02002678 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002679 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2680 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2681 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002682
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002683 bss = cfg80211_inform_bss(wiphy, notify_channel,
2684 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2685 notify_capability, notify_interval,
2686 notify_ie, notify_ielen, notify_signal,
2687 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002688
Franky Line78946e2011-11-10 20:30:34 +01002689 if (!bss) {
2690 err = -ENOMEM;
2691 goto CleanUp;
2692 }
2693
Johannes Berg5b112d32013-02-01 01:49:58 +01002694 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002695
Arend van Spriel5b435de2011-10-05 13:19:03 +02002696CleanUp:
2697
2698 kfree(buf);
2699
Arend van Sprield96b8012012-12-05 15:26:02 +01002700 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002701
2702 return err;
2703}
2704
Hante Meuleman89286dc2013-02-08 15:53:46 +01002705static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2706 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002707{
Hante Meuleman89286dc2013-02-08 15:53:46 +01002708 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ifp->ndev);
Roland Vossend34bf642011-10-18 14:03:01 +02002709 struct brcmf_bss_info_le *bi;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002710 struct brcmf_ssid *ssid;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002711 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002712 u16 beacon_interval;
2713 u8 dtim_period;
2714 size_t ie_len;
2715 u8 *ie;
2716 s32 err = 0;
2717
Arend van Sprield96b8012012-12-05 15:26:02 +01002718 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002719 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002720 return err;
2721
Arend van Spriel06bb1232012-09-27 14:17:56 +02002722 ssid = &profile->ssid;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002723
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002724 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002725 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002726 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002727 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002728 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002729 goto update_bss_info_out;
2730 }
2731
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002732 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2733 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002734 if (err)
2735 goto update_bss_info_out;
2736
2737 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2738 ie_len = le32_to_cpu(bi->ie_length);
2739 beacon_interval = le16_to_cpu(bi->beacon_period);
2740
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002741 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002742 if (tim)
2743 dtim_period = tim->data[1];
2744 else {
2745 /*
2746 * active scan was done so we could not get dtim
2747 * information out of probe response.
2748 * so we speficially query dtim information to dongle.
2749 */
2750 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002751 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002752 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002753 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002754 goto update_bss_info_out;
2755 }
2756 dtim_period = (u8)var;
2757 }
2758
Arend van Spriel5b435de2011-10-05 13:19:03 +02002759update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002760 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002761 return err;
2762}
2763
Hante Meuleman18e2f612013-02-08 15:53:49 +01002764void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002765{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002766 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002767
Arend van Sprielc1179032012-10-22 13:55:33 -07002768 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002769 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002770 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002771 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002772 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002773 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2774 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002775}
2776
Hante Meulemane756af52012-09-11 21:18:52 +02002777static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2778{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002779 struct brcmf_cfg80211_info *cfg =
2780 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002781 escan_timeout_work);
2782
Hante Meulemanef8596e2014-09-30 10:23:13 +02002783 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002784 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002785}
2786
2787static void brcmf_escan_timeout(unsigned long data)
2788{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002789 struct brcmf_cfg80211_info *cfg =
2790 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002791
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002792 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002793 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002794 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002795 }
2796}
2797
2798static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002799brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2800 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002801 struct brcmf_bss_info_le *bss_info_le)
2802{
Franky Lin83cf17a2013-04-11 13:28:50 +02002803 struct brcmu_chan ch_bss, ch_bss_info_le;
2804
2805 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2806 cfg->d11inf.decchspec(&ch_bss);
2807 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2808 cfg->d11inf.decchspec(&ch_bss_info_le);
2809
Hante Meulemane756af52012-09-11 21:18:52 +02002810 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002811 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002812 bss_info_le->SSID_len == bss->SSID_len &&
2813 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002814 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2815 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002816 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2817 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2818
Hante Meulemane756af52012-09-11 21:18:52 +02002819 /* preserve max RSSI if the measurements are
2820 * both on-channel or both off-channel
2821 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002822 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002823 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002824 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2825 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002826 /* preserve the on-channel rssi measurement
2827 * if the new measurement is off channel
2828 */
2829 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002830 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002831 }
2832 return 1;
2833 }
2834 return 0;
2835}
2836
2837static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002838brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002839 const struct brcmf_event_msg *e, void *data)
2840{
Arend van Spriel19937322012-11-05 16:22:32 -08002841 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002842 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002843 struct brcmf_escan_result_le *escan_result_le;
2844 struct brcmf_bss_info_le *bss_info_le;
2845 struct brcmf_bss_info_le *bss = NULL;
2846 u32 bi_length;
2847 struct brcmf_scan_results *list;
2848 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002849 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002850
Arend van Spriel5c36b992012-11-14 18:46:05 -08002851 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002852
Arend van Spriela0f472a2013-04-05 10:57:49 +02002853 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
2854 brcmf_err("scan not ready, bssidx=%d\n", ifp->bssidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002855 return -EPERM;
2856 }
2857
2858 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002859 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002860 escan_result_le = (struct brcmf_escan_result_le *) data;
2861 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002862 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002863 goto exit;
2864 }
Hante Meulemane756af52012-09-11 21:18:52 +02002865 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002866 brcmf_err("Invalid bss_count %d: ignoring\n",
2867 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002868 goto exit;
2869 }
2870 bss_info_le = &escan_result_le->bss_info_le;
2871
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002872 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2873 goto exit;
2874
2875 if (!cfg->scan_request) {
2876 brcmf_dbg(SCAN, "result without cfg80211 request\n");
2877 goto exit;
2878 }
2879
Hante Meulemane756af52012-09-11 21:18:52 +02002880 bi_length = le32_to_cpu(bss_info_le->length);
2881 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
2882 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002883 brcmf_err("Invalid bss_info length %d: ignoring\n",
2884 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02002885 goto exit;
2886 }
2887
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002888 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02002889 BIT(NL80211_IFTYPE_ADHOC))) {
2890 if (le16_to_cpu(bss_info_le->capability) &
2891 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002892 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002893 goto exit;
2894 }
2895 }
2896
2897 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002898 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02002899 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002900 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002901 goto exit;
2902 }
2903
2904 for (i = 0; i < list->count; i++) {
2905 bss = bss ? (struct brcmf_bss_info_le *)
2906 ((unsigned char *)bss +
2907 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02002908 if (brcmf_compare_update_same_bss(cfg, bss,
2909 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02002910 goto exit;
2911 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002912 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02002913 bss_info_le, bi_length);
2914 list->version = le32_to_cpu(bss_info_le->version);
2915 list->buflen += bi_length;
2916 list->count++;
2917 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002918 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002919 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
2920 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002921 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002922 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002923 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02002924 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02002925 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002926 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
2927 status);
Hante Meulemane756af52012-09-11 21:18:52 +02002928 }
2929exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002930 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02002931}
2932
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002933static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02002934{
Arend van Spriel5c36b992012-11-14 18:46:05 -08002935 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
2936 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08002937 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
2938 /* Init scan_timeout timer */
2939 init_timer(&cfg->escan_timeout);
2940 cfg->escan_timeout.data = (unsigned long) cfg;
2941 cfg->escan_timeout.function = brcmf_escan_timeout;
2942 INIT_WORK(&cfg->escan_timeout_work,
2943 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02002944}
2945
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05002946static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002947{
2948 if (ms < 1000 / HZ) {
2949 cond_resched();
2950 mdelay(ms);
2951 } else {
2952 msleep(ms);
2953 }
2954}
2955
Hante Meulemanb9a82f82014-10-28 14:56:06 +01002956static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
2957 u8 *pattern, u32 patternsize, u8 *mask,
2958 u32 packet_offset)
2959{
2960 struct brcmf_fil_wowl_pattern_le *filter;
2961 u32 masksize;
2962 u32 patternoffset;
2963 u8 *buf;
2964 u32 bufsize;
2965 s32 ret;
2966
2967 masksize = (patternsize + 7) / 8;
2968 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
2969
2970 bufsize = sizeof(*filter) + patternsize + masksize;
2971 buf = kzalloc(bufsize, GFP_KERNEL);
2972 if (!buf)
2973 return -ENOMEM;
2974 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
2975
2976 memcpy(filter->cmd, cmd, 4);
2977 filter->masksize = cpu_to_le32(masksize);
2978 filter->offset = cpu_to_le32(packet_offset);
2979 filter->patternoffset = cpu_to_le32(patternoffset);
2980 filter->patternsize = cpu_to_le32(patternsize);
2981 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
2982
2983 if ((mask) && (masksize))
2984 memcpy(buf + sizeof(*filter), mask, masksize);
2985 if ((pattern) && (patternsize))
2986 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
2987
2988 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
2989
2990 kfree(buf);
2991 return ret;
2992}
2993
Arend van Spriel5b435de2011-10-05 13:19:03 +02002994static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
2995{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02002996 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2997 struct net_device *ndev = cfg_to_ndev(cfg);
2998 struct brcmf_if *ifp = netdev_priv(ndev);
2999
Arend van Sprield96b8012012-12-05 15:26:02 +01003000 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003001
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003002 if (cfg->wowl_enabled) {
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003003 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003004 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
3005 cfg->pre_wowl_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003006 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003007 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003008 cfg->wowl_enabled = false;
3009 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003010 return 0;
3011}
3012
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003013static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3014 struct brcmf_if *ifp,
3015 struct cfg80211_wowlan *wowl)
3016{
3017 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003018 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003019
3020 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3021
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003022 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003023 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->pre_wowl_pmmode);
3024 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3025
3026 wowl_config = 0;
3027 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003028 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003029 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003030 wowl_config |= BRCMF_WOWL_MAGIC;
3031 if ((wowl->patterns) && (wowl->n_patterns)) {
3032 wowl_config |= BRCMF_WOWL_NET;
3033 for (i = 0; i < wowl->n_patterns; i++) {
3034 brcmf_config_wowl_pattern(ifp, "add",
3035 (u8 *)wowl->patterns[i].pattern,
3036 wowl->patterns[i].pattern_len,
3037 (u8 *)wowl->patterns[i].mask,
3038 wowl->patterns[i].pkt_offset);
3039 }
3040 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003041 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3042 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3043 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
3044 cfg->wowl_enabled = true;
3045}
3046
Arend van Spriel5b435de2011-10-05 13:19:03 +02003047static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003048 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003049{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003050 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3051 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003052 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003053 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003054
Arend van Sprield96b8012012-12-05 15:26:02 +01003055 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003056
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003057 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003058 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003059 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003060 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003061 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003062
Arend van Spriel7d641072012-10-22 13:55:39 -07003063 /* end any scanning */
3064 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003065 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003066
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003067 if (wowl == NULL) {
3068 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3069 list_for_each_entry(vif, &cfg->vif_list, list) {
3070 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3071 continue;
3072 /* While going to suspend if associated with AP
3073 * disassociate from AP to save power while system is
3074 * in suspended state
3075 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003076 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003077 /* Make sure WPA_Supplicant receives all the event
3078 * generated due to DISASSOC call to the fw to keep
3079 * the state fw and WPA_Supplicant state consistent
3080 */
3081 brcmf_delay(500);
3082 }
3083 /* Configure MPC */
3084 brcmf_set_mpc(ifp, 1);
3085
3086 } else {
3087 /* Configure WOWL paramaters */
3088 brcmf_configure_wowl(cfg, ifp, wowl);
3089 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003090
Arend van Spriel7d641072012-10-22 13:55:39 -07003091exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003092 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003093 /* clear any scanning activity */
3094 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003095 return 0;
3096}
3097
3098static __used s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02003099brcmf_update_pmklist(struct net_device *ndev,
3100 struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
3101{
3102 int i, j;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003103 u32 pmkid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003104
Arend van Spriel40c8e952011-10-12 20:51:20 +02003105 pmkid_len = le32_to_cpu(pmk_list->pmkids.npmkid);
3106
Arend van Spriel16886732012-12-05 15:26:04 +01003107 brcmf_dbg(CONN, "No of elements %d\n", pmkid_len);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003108 for (i = 0; i < pmkid_len; i++) {
Arend van Spriel16886732012-12-05 15:26:04 +01003109 brcmf_dbg(CONN, "PMKID[%d]: %pM =\n", i,
3110 &pmk_list->pmkids.pmkid[i].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003111 for (j = 0; j < WLAN_PMKID_LEN; j++)
Arend van Spriel16886732012-12-05 15:26:04 +01003112 brcmf_dbg(CONN, "%02x\n",
3113 pmk_list->pmkids.pmkid[i].PMKID[j]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003114 }
3115
3116 if (!err)
Arend van Sprielac24be62012-10-22 10:36:23 -07003117 brcmf_fil_iovar_data_set(netdev_priv(ndev), "pmkid_info",
3118 (char *)pmk_list, sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003119
3120 return err;
3121}
3122
3123static s32
3124brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3125 struct cfg80211_pmksa *pmksa)
3126{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003127 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003128 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003129 struct pmkid_list *pmkids = &cfg->pmk_list->pmkids;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003130 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003131 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003132
Arend van Sprield96b8012012-12-05 15:26:02 +01003133 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003134 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003135 return -EIO;
3136
Arend van Spriel40c8e952011-10-12 20:51:20 +02003137 pmkid_len = le32_to_cpu(pmkids->npmkid);
3138 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003139 if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
3140 break;
3141 if (i < WL_NUM_PMKIDS_MAX) {
3142 memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
3143 memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003144 if (i == pmkid_len) {
3145 pmkid_len++;
3146 pmkids->npmkid = cpu_to_le32(pmkid_len);
3147 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003148 } else
3149 err = -EINVAL;
3150
Arend van Spriel16886732012-12-05 15:26:04 +01003151 brcmf_dbg(CONN, "set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
3152 pmkids->pmkid[pmkid_len].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003153 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003154 brcmf_dbg(CONN, "%02x\n", pmkids->pmkid[pmkid_len].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003155
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003156 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003157
Arend van Sprield96b8012012-12-05 15:26:02 +01003158 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003159 return err;
3160}
3161
3162static s32
3163brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3164 struct cfg80211_pmksa *pmksa)
3165{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003166 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003167 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003168 struct pmkid_list pmkid;
3169 s32 err = 0;
Arend van Sprielc15d7892014-11-20 22:26:59 +01003170 u32 pmkid_len, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003171
Arend van Sprield96b8012012-12-05 15:26:02 +01003172 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003173 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003174 return -EIO;
3175
3176 memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
3177 memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
3178
Arend van Spriel16886732012-12-05 15:26:04 +01003179 brcmf_dbg(CONN, "del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
3180 &pmkid.pmkid[0].BSSID);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003181 for (i = 0; i < WLAN_PMKID_LEN; i++)
Arend van Spriel16886732012-12-05 15:26:04 +01003182 brcmf_dbg(CONN, "%02x\n", pmkid.pmkid[0].PMKID[i]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003183
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003184 pmkid_len = le32_to_cpu(cfg->pmk_list->pmkids.npmkid);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003185 for (i = 0; i < pmkid_len; i++)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003186 if (!memcmp
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003187 (pmksa->bssid, &cfg->pmk_list->pmkids.pmkid[i].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003188 ETH_ALEN))
3189 break;
3190
Arend van Spriel40c8e952011-10-12 20:51:20 +02003191 if ((pmkid_len > 0)
3192 && (i < pmkid_len)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003193 memset(&cfg->pmk_list->pmkids.pmkid[i], 0,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003194 sizeof(struct pmkid));
Arend van Spriel40c8e952011-10-12 20:51:20 +02003195 for (; i < (pmkid_len - 1); i++) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003196 memcpy(&cfg->pmk_list->pmkids.pmkid[i].BSSID,
3197 &cfg->pmk_list->pmkids.pmkid[i + 1].BSSID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003198 ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003199 memcpy(&cfg->pmk_list->pmkids.pmkid[i].PMKID,
3200 &cfg->pmk_list->pmkids.pmkid[i + 1].PMKID,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003201 WLAN_PMKID_LEN);
3202 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003203 cfg->pmk_list->pmkids.npmkid = cpu_to_le32(pmkid_len - 1);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003204 } else
3205 err = -EINVAL;
3206
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003207 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003208
Arend van Sprield96b8012012-12-05 15:26:02 +01003209 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003210 return err;
3211
3212}
3213
3214static s32
3215brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3216{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003217 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003218 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003219 s32 err = 0;
3220
Arend van Sprield96b8012012-12-05 15:26:02 +01003221 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003222 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003223 return -EIO;
3224
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003225 memset(cfg->pmk_list, 0, sizeof(*cfg->pmk_list));
3226 err = brcmf_update_pmklist(ndev, cfg->pmk_list, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003227
Arend van Sprield96b8012012-12-05 15:26:02 +01003228 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003229 return err;
3230
3231}
3232
Arend van Spriele5806072012-09-19 22:21:08 +02003233/*
3234 * PFN result doesn't have all the info which are
3235 * required by the supplicant
3236 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3237 * via wl_inform_single_bss in the required format. Escan does require the
3238 * scan request in the form of cfg80211_scan_request. For timebeing, create
3239 * cfg80211_scan_request one out of the received PNO event.
3240 */
3241static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003242brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
Arend van Spriele5806072012-09-19 22:21:08 +02003243 const struct brcmf_event_msg *e, void *data)
3244{
Arend van Spriel19937322012-11-05 16:22:32 -08003245 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriele5806072012-09-19 22:21:08 +02003246 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3247 struct cfg80211_scan_request *request = NULL;
3248 struct cfg80211_ssid *ssid = NULL;
3249 struct ieee80211_channel *channel = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003250 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003251 int err = 0;
3252 int channel_req = 0;
3253 int band = 0;
3254 struct brcmf_pno_scanresults_le *pfn_result;
3255 u32 result_count;
3256 u32 status;
3257
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003258 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003259
Arend van Spriel5c36b992012-11-14 18:46:05 -08003260 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003261 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003262 return 0;
3263 }
3264
3265 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3266 result_count = le32_to_cpu(pfn_result->count);
3267 status = le32_to_cpu(pfn_result->status);
3268
3269 /*
3270 * PFN event is limited to fit 512 bytes so we may get
3271 * multiple NET_FOUND events. For now place a warning here.
3272 */
3273 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003274 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend van Spriele5806072012-09-19 22:21:08 +02003275 if (result_count > 0) {
3276 int i;
3277
3278 request = kzalloc(sizeof(*request), GFP_KERNEL);
Dan Carpenter58901d12012-09-26 10:21:48 +03003279 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3280 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
Arend van Spriele5806072012-09-19 22:21:08 +02003281 if (!request || !ssid || !channel) {
3282 err = -ENOMEM;
3283 goto out_err;
3284 }
3285
3286 request->wiphy = wiphy;
3287 data += sizeof(struct brcmf_pno_scanresults_le);
3288 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3289
3290 for (i = 0; i < result_count; i++) {
3291 netinfo = &netinfo_start[i];
3292 if (!netinfo) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003293 brcmf_err("Invalid netinfo ptr. index: %d\n",
3294 i);
Arend van Spriele5806072012-09-19 22:21:08 +02003295 err = -EINVAL;
3296 goto out_err;
3297 }
3298
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003299 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3300 netinfo->SSID, netinfo->channel);
Arend van Spriele5806072012-09-19 22:21:08 +02003301 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3302 ssid[i].ssid_len = netinfo->SSID_len;
3303 request->n_ssids++;
3304
3305 channel_req = netinfo->channel;
3306 if (channel_req <= CH_MAX_2G_CHANNEL)
3307 band = NL80211_BAND_2GHZ;
3308 else
3309 band = NL80211_BAND_5GHZ;
3310 channel[i].center_freq =
3311 ieee80211_channel_to_frequency(channel_req,
3312 band);
3313 channel[i].band = band;
3314 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3315 request->channels[i] = &channel[i];
3316 request->n_channels++;
3317 }
3318
3319 /* assign parsed ssid array */
3320 if (request->n_ssids)
3321 request->ssids = &ssid[0];
3322
Arend van Sprielc1179032012-10-22 13:55:33 -07003323 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriele5806072012-09-19 22:21:08 +02003324 /* Abort any on-going scan */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003325 brcmf_abort_scanning(cfg);
Arend van Spriele5806072012-09-19 22:21:08 +02003326 }
3327
Arend van Sprielc1179032012-10-22 13:55:33 -07003328 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel2668b0b2014-01-13 22:20:28 +01003329 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003330 err = brcmf_do_escan(cfg, wiphy, ifp, request);
Arend van Spriele5806072012-09-19 22:21:08 +02003331 if (err) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003332 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003333 goto out_err;
3334 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003335 cfg->sched_escan = true;
3336 cfg->scan_request = request;
Arend van Spriele5806072012-09-19 22:21:08 +02003337 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003338 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003339 goto out_err;
3340 }
3341
3342 kfree(ssid);
3343 kfree(channel);
3344 kfree(request);
3345 return 0;
3346
3347out_err:
3348 kfree(ssid);
3349 kfree(channel);
3350 kfree(request);
3351 cfg80211_sched_scan_stopped(wiphy);
3352 return err;
3353}
3354
Arend van Spriele5806072012-09-19 22:21:08 +02003355static int brcmf_dev_pno_clean(struct net_device *ndev)
3356{
Arend van Spriele5806072012-09-19 22:21:08 +02003357 int ret;
3358
3359 /* Disable pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003360 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003361 if (ret == 0) {
3362 /* clear pfn */
Arend van Sprielac24be62012-10-22 10:36:23 -07003363 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3364 NULL, 0);
Arend van Spriele5806072012-09-19 22:21:08 +02003365 }
3366 if (ret < 0)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003367 brcmf_err("failed code %d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003368
3369 return ret;
3370}
3371
3372static int brcmf_dev_pno_config(struct net_device *ndev)
3373{
3374 struct brcmf_pno_param_le pfn_param;
Arend van Spriele5806072012-09-19 22:21:08 +02003375
3376 memset(&pfn_param, 0, sizeof(pfn_param));
3377 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3378
3379 /* set extra pno params */
3380 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3381 pfn_param.repeat = BRCMF_PNO_REPEAT;
3382 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3383
3384 /* set up pno scan fr */
3385 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3386
Arend van Sprielac24be62012-10-22 10:36:23 -07003387 return brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfn_set",
3388 &pfn_param, sizeof(pfn_param));
Arend van Spriele5806072012-09-19 22:21:08 +02003389}
3390
3391static int
3392brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3393 struct net_device *ndev,
3394 struct cfg80211_sched_scan_request *request)
3395{
Arend van Sprielc1179032012-10-22 13:55:33 -07003396 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003397 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003398 struct brcmf_pno_net_param_le pfn;
3399 int i;
3400 int ret = 0;
3401
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003402 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003403 request->n_match_sets, request->n_ssids);
Arend van Sprielc1179032012-10-22 13:55:33 -07003404 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003405 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Arend van Spriele5806072012-09-19 22:21:08 +02003406 return -EAGAIN;
3407 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02003408 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3409 brcmf_err("Scanning suppressed: status (%lu)\n",
3410 cfg->scan_status);
3411 return -EAGAIN;
3412 }
Arend van Spriele5806072012-09-19 22:21:08 +02003413
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003414 if (!request->n_ssids || !request->n_match_sets) {
Arend van Spriel181f2d12014-05-27 12:56:13 +02003415 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
Arend van Sprieldc7bdbf2013-03-03 12:45:25 +01003416 request->n_ssids);
Arend van Spriele5806072012-09-19 22:21:08 +02003417 return -EINVAL;
3418 }
3419
3420 if (request->n_ssids > 0) {
3421 for (i = 0; i < request->n_ssids; i++) {
3422 /* Active scan req for ssids */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003423 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3424 request->ssids[i].ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003425
3426 /*
3427 * match_set ssids is a supert set of n_ssid list,
3428 * so we need not add these set seperately.
3429 */
3430 }
3431 }
3432
3433 if (request->n_match_sets > 0) {
3434 /* clean up everything */
3435 ret = brcmf_dev_pno_clean(ndev);
3436 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003437 brcmf_err("failed error=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003438 return ret;
3439 }
3440
3441 /* configure pno */
3442 ret = brcmf_dev_pno_config(ndev);
3443 if (ret < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003444 brcmf_err("PNO setup failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003445 return -EINVAL;
3446 }
3447
3448 /* configure each match set */
3449 for (i = 0; i < request->n_match_sets; i++) {
3450 struct cfg80211_ssid *ssid;
3451 u32 ssid_len;
3452
3453 ssid = &request->match_sets[i].ssid;
3454 ssid_len = ssid->ssid_len;
3455
3456 if (!ssid_len) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003457 brcmf_err("skip broadcast ssid\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003458 continue;
3459 }
3460 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3461 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3462 pfn.wsec = cpu_to_le32(0);
3463 pfn.infra = cpu_to_le32(1);
3464 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3465 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3466 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
Arend van Sprielc1179032012-10-22 13:55:33 -07003467 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
Arend van Sprielac24be62012-10-22 10:36:23 -07003468 sizeof(pfn));
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003469 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3470 ret == 0 ? "set" : "failed", ssid->ssid);
Arend van Spriele5806072012-09-19 22:21:08 +02003471 }
3472 /* Enable the PNO */
Arend van Sprielc1179032012-10-22 13:55:33 -07003473 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003474 brcmf_err("PNO enable failed!! ret=%d\n", ret);
Arend van Spriele5806072012-09-19 22:21:08 +02003475 return -EINVAL;
3476 }
3477 } else {
3478 return -EINVAL;
3479 }
3480
3481 return 0;
3482}
3483
3484static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3485 struct net_device *ndev)
3486{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003487 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriele5806072012-09-19 22:21:08 +02003488
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003489 brcmf_dbg(SCAN, "enter\n");
Arend van Spriele5806072012-09-19 22:21:08 +02003490 brcmf_dev_pno_clean(ndev);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003491 if (cfg->sched_escan)
Arend van Spriela0f472a2013-04-05 10:57:49 +02003492 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
Arend van Spriele5806072012-09-19 22:21:08 +02003493 return 0;
3494}
Arend van Spriele5806072012-09-19 22:21:08 +02003495
Hante Meuleman1f170112013-02-06 18:40:38 +01003496static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003497{
3498 s32 err;
3499
3500 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003501 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003502 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003503 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003504 return err;
3505 }
3506 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003507 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003508 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003509 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003510 return err;
3511 }
3512 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003513 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003514 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003515 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003516 return err;
3517 }
3518
3519 return 0;
3520}
3521
3522static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3523{
3524 if (is_rsn_ie)
3525 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3526
3527 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3528}
3529
3530static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003531brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003532 const struct brcmf_vs_tlv *wpa_ie,
3533 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003534{
3535 u32 auth = 0; /* d11 open authentication */
3536 u16 count;
3537 s32 err = 0;
3538 s32 len = 0;
3539 u32 i;
3540 u32 wsec;
3541 u32 pval = 0;
3542 u32 gval = 0;
3543 u32 wpa_auth = 0;
3544 u32 offset;
3545 u8 *data;
3546 u16 rsn_cap;
3547 u32 wme_bss_disable;
3548
Arend van Sprield96b8012012-12-05 15:26:02 +01003549 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003550 if (wpa_ie == NULL)
3551 goto exit;
3552
3553 len = wpa_ie->len + TLV_HDR_LEN;
3554 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003555 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003556 if (!is_rsn_ie)
3557 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003558 else
3559 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003560
3561 /* check for multicast cipher suite */
3562 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3563 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003564 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003565 goto exit;
3566 }
3567
3568 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3569 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003570 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003571 goto exit;
3572 }
3573 offset += TLV_OUI_LEN;
3574
3575 /* pick up multicast cipher */
3576 switch (data[offset]) {
3577 case WPA_CIPHER_NONE:
3578 gval = 0;
3579 break;
3580 case WPA_CIPHER_WEP_40:
3581 case WPA_CIPHER_WEP_104:
3582 gval = WEP_ENABLED;
3583 break;
3584 case WPA_CIPHER_TKIP:
3585 gval = TKIP_ENABLED;
3586 break;
3587 case WPA_CIPHER_AES_CCM:
3588 gval = AES_ENABLED;
3589 break;
3590 default:
3591 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003592 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003593 goto exit;
3594 }
3595
3596 offset++;
3597 /* walk thru unicast cipher list and pick up what we recognize */
3598 count = data[offset] + (data[offset + 1] << 8);
3599 offset += WPA_IE_SUITE_COUNT_LEN;
3600 /* Check for unicast suite(s) */
3601 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3602 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003603 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003604 goto exit;
3605 }
3606 for (i = 0; i < count; i++) {
3607 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3608 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003609 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003610 goto exit;
3611 }
3612 offset += TLV_OUI_LEN;
3613 switch (data[offset]) {
3614 case WPA_CIPHER_NONE:
3615 break;
3616 case WPA_CIPHER_WEP_40:
3617 case WPA_CIPHER_WEP_104:
3618 pval |= WEP_ENABLED;
3619 break;
3620 case WPA_CIPHER_TKIP:
3621 pval |= TKIP_ENABLED;
3622 break;
3623 case WPA_CIPHER_AES_CCM:
3624 pval |= AES_ENABLED;
3625 break;
3626 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003627 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003628 }
3629 offset++;
3630 }
3631 /* walk thru auth management suite list and pick up what we recognize */
3632 count = data[offset] + (data[offset + 1] << 8);
3633 offset += WPA_IE_SUITE_COUNT_LEN;
3634 /* Check for auth key management suite(s) */
3635 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3636 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003637 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003638 goto exit;
3639 }
3640 for (i = 0; i < count; i++) {
3641 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3642 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003643 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003644 goto exit;
3645 }
3646 offset += TLV_OUI_LEN;
3647 switch (data[offset]) {
3648 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003649 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003650 wpa_auth |= WPA_AUTH_NONE;
3651 break;
3652 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003653 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003654 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3655 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3656 break;
3657 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003658 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003659 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3660 (wpa_auth |= WPA_AUTH_PSK);
3661 break;
3662 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003663 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003664 }
3665 offset++;
3666 }
3667
3668 if (is_rsn_ie) {
3669 wme_bss_disable = 1;
3670 if ((offset + RSN_CAP_LEN) <= len) {
3671 rsn_cap = data[offset] + (data[offset + 1] << 8);
3672 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3673 wme_bss_disable = 0;
3674 }
3675 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003676 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003677 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003678 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003679 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003680 goto exit;
3681 }
3682 }
3683 /* FOR WPS , set SES_OW_ENABLED */
3684 wsec = (pval | gval | SES_OW_ENABLED);
3685
3686 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003687 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003688 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003689 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003690 goto exit;
3691 }
3692 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003693 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003694 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003695 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003696 goto exit;
3697 }
3698 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003699 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003700 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003701 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003702 goto exit;
3703 }
3704
3705exit:
3706 return err;
3707}
3708
3709static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003710brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003711 struct parsed_vndr_ies *vndr_ies)
3712{
Hante Meuleman1a873342012-09-27 14:17:54 +02003713 struct brcmf_vs_tlv *vndrie;
3714 struct brcmf_tlv *ie;
3715 struct parsed_vndr_ie_info *parsed_info;
3716 s32 remaining_len;
3717
3718 remaining_len = (s32)vndr_ie_len;
3719 memset(vndr_ies, 0, sizeof(*vndr_ies));
3720
3721 ie = (struct brcmf_tlv *)vndr_ie_buf;
3722 while (ie) {
3723 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
3724 goto next;
3725 vndrie = (struct brcmf_vs_tlv *)ie;
3726 /* len should be bigger than OUI length + one */
3727 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003728 brcmf_err("invalid vndr ie. length is too small %d\n",
3729 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003730 goto next;
3731 }
3732 /* if wpa or wme ie, do not add ie */
3733 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
3734 ((vndrie->oui_type == WPA_OUI_TYPE) ||
3735 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003736 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003737 goto next;
3738 }
3739
3740 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
3741
3742 /* save vndr ie information */
3743 parsed_info->ie_ptr = (char *)vndrie;
3744 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
3745 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
3746
3747 vndr_ies->count++;
3748
Arend van Sprield96b8012012-12-05 15:26:02 +01003749 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
3750 parsed_info->vndrie.oui[0],
3751 parsed_info->vndrie.oui[1],
3752 parsed_info->vndrie.oui[2],
3753 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02003754
Arend van Spriel9f440b72013-02-08 15:53:36 +01003755 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02003756 break;
3757next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003758 remaining_len -= (ie->len + TLV_HDR_LEN);
3759 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02003760 ie = NULL;
3761 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003762 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
3763 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02003764 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003765 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02003766}
3767
3768static u32
3769brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
3770{
3771
Hante Meuleman1a873342012-09-27 14:17:54 +02003772 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
3773 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
3774
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303775 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003776
Vaishali Thakkar362126c2015-01-16 21:36:14 +05303777 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003778
3779 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
3780
3781 return ie_len + VNDR_IE_HDR_SIZE;
3782}
3783
Arend van Spriel1332e262012-11-05 16:22:18 -08003784s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
3785 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02003786{
Arend van Spriel1332e262012-11-05 16:22:18 -08003787 struct brcmf_if *ifp;
3788 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003789 s32 err = 0;
3790 u8 *iovar_ie_buf;
3791 u8 *curr_ie_buf;
3792 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003793 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07003794 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003795 u32 del_add_ie_buf_len = 0;
3796 u32 total_ie_buf_len = 0;
3797 u32 parsed_ie_buf_len = 0;
3798 struct parsed_vndr_ies old_vndr_ies;
3799 struct parsed_vndr_ies new_vndr_ies;
3800 struct parsed_vndr_ie_info *vndrie_info;
3801 s32 i;
3802 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07003803 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003804
Arend van Spriel1332e262012-11-05 16:22:18 -08003805 if (!vif)
3806 return -ENODEV;
3807 ifp = vif->ifp;
3808 saved_ie = &vif->saved_ie;
3809
Arend van Sprield96b8012012-12-05 15:26:02 +01003810 brcmf_dbg(TRACE, "bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02003811 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
3812 if (!iovar_ie_buf)
3813 return -ENOMEM;
3814 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01003815 switch (pktflag) {
3816 case BRCMF_VNDR_IE_PRBREQ_FLAG:
3817 mgmt_ie_buf = saved_ie->probe_req_ie;
3818 mgmt_ie_len = &saved_ie->probe_req_ie_len;
3819 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
3820 break;
3821 case BRCMF_VNDR_IE_PRBRSP_FLAG:
3822 mgmt_ie_buf = saved_ie->probe_res_ie;
3823 mgmt_ie_len = &saved_ie->probe_res_ie_len;
3824 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
3825 break;
3826 case BRCMF_VNDR_IE_BEACON_FLAG:
3827 mgmt_ie_buf = saved_ie->beacon_ie;
3828 mgmt_ie_len = &saved_ie->beacon_ie_len;
3829 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
3830 break;
3831 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
3832 mgmt_ie_buf = saved_ie->assoc_req_ie;
3833 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
3834 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
3835 break;
3836 default:
3837 err = -EPERM;
3838 brcmf_err("not suitable type\n");
3839 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02003840 }
3841
3842 if (vndr_ie_len > mgmt_ie_buf_len) {
3843 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003844 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003845 goto exit;
3846 }
3847
3848 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
3849 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
3850 ptr = curr_ie_buf;
3851 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
3852 for (i = 0; i < new_vndr_ies.count; i++) {
3853 vndrie_info = &new_vndr_ies.ie_info[i];
3854 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
3855 vndrie_info->ie_len);
3856 parsed_ie_buf_len += vndrie_info->ie_len;
3857 }
3858 }
3859
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003860 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02003861 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
3862 (memcmp(mgmt_ie_buf, curr_ie_buf,
3863 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01003864 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003865 goto exit;
3866 }
3867
3868 /* parse old vndr_ie */
3869 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
3870
3871 /* make a command to delete old ie */
3872 for (i = 0; i < old_vndr_ies.count; i++) {
3873 vndrie_info = &old_vndr_ies.ie_info[i];
3874
Arend van Sprield96b8012012-12-05 15:26:02 +01003875 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
3876 vndrie_info->vndrie.id,
3877 vndrie_info->vndrie.len,
3878 vndrie_info->vndrie.oui[0],
3879 vndrie_info->vndrie.oui[1],
3880 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003881
3882 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3883 vndrie_info->ie_ptr,
3884 vndrie_info->ie_len,
3885 "del");
3886 curr_ie_buf += del_add_ie_buf_len;
3887 total_ie_buf_len += del_add_ie_buf_len;
3888 }
3889 }
3890
3891 *mgmt_ie_len = 0;
3892 /* Add if there is any extra IE */
3893 if (mgmt_ie_buf && parsed_ie_buf_len) {
3894 ptr = mgmt_ie_buf;
3895
3896 remained_buf_len = mgmt_ie_buf_len;
3897
3898 /* make a command to add new ie */
3899 for (i = 0; i < new_vndr_ies.count; i++) {
3900 vndrie_info = &new_vndr_ies.ie_info[i];
3901
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003902 /* verify remained buf size before copy data */
3903 if (remained_buf_len < (vndrie_info->vndrie.len +
3904 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003905 brcmf_err("no space in mgmt_ie_buf: len left %d",
3906 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01003907 break;
3908 }
3909 remained_buf_len -= (vndrie_info->ie_len +
3910 VNDR_IE_VSIE_OFFSET);
3911
Arend van Sprield96b8012012-12-05 15:26:02 +01003912 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
3913 vndrie_info->vndrie.id,
3914 vndrie_info->vndrie.len,
3915 vndrie_info->vndrie.oui[0],
3916 vndrie_info->vndrie.oui[1],
3917 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02003918
3919 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
3920 vndrie_info->ie_ptr,
3921 vndrie_info->ie_len,
3922 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02003923
3924 /* save the parsed IE in wl struct */
3925 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
3926 vndrie_info->ie_len);
3927 *mgmt_ie_len += vndrie_info->ie_len;
3928
3929 curr_ie_buf += del_add_ie_buf_len;
3930 total_ie_buf_len += del_add_ie_buf_len;
3931 }
3932 }
3933 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07003934 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003935 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02003936 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01003937 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003938 }
3939
3940exit:
3941 kfree(iovar_ie_buf);
3942 return err;
3943}
3944
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01003945s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
3946{
3947 s32 pktflags[] = {
3948 BRCMF_VNDR_IE_PRBREQ_FLAG,
3949 BRCMF_VNDR_IE_PRBRSP_FLAG,
3950 BRCMF_VNDR_IE_BEACON_FLAG
3951 };
3952 int i;
3953
3954 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
3955 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
3956
3957 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
3958 return 0;
3959}
3960
Hante Meuleman1a873342012-09-27 14:17:54 +02003961static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01003962brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
3963 struct cfg80211_beacon_data *beacon)
3964{
3965 s32 err;
3966
3967 /* Set Beacon IEs to FW */
3968 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
3969 beacon->tail, beacon->tail_len);
3970 if (err) {
3971 brcmf_err("Set Beacon IE Failed\n");
3972 return err;
3973 }
3974 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
3975
3976 /* Set Probe Response IEs to FW */
3977 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
3978 beacon->proberesp_ies,
3979 beacon->proberesp_ies_len);
3980 if (err)
3981 brcmf_err("Set Probe Resp IE Failed\n");
3982 else
3983 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
3984
3985 return err;
3986}
3987
3988static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02003989brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
3990 struct cfg80211_ap_settings *settings)
3991{
3992 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02003993 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07003994 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01003995 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01003996 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02003997 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02003998 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01003999 const struct brcmf_tlv *rsn_ie;
4000 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004001 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004002 enum nl80211_iftype dev_role;
4003 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004004 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004005 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004006 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004007
Arend van Spriel06c01582014-05-12 10:47:37 +02004008 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4009 settings->chandef.chan->hw_value,
4010 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004011 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004012 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4013 settings->ssid, settings->ssid_len, settings->auth_type,
4014 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004015 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004016 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004017
Arend van Spriel98027762014-12-21 12:43:53 +01004018 /* store current 11d setting */
4019 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4020 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4021 settings->beacon.tail_len,
4022 WLAN_EID_COUNTRY);
4023 is_11d = country_ie ? 1 : 0;
4024
Hante Meuleman1a873342012-09-27 14:17:54 +02004025 memset(&ssid_le, 0, sizeof(ssid_le));
4026 if (settings->ssid == NULL || settings->ssid_len == 0) {
4027 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4028 ssid_ie = brcmf_parse_tlvs(
4029 (u8 *)&settings->beacon.head[ie_offset],
4030 settings->beacon.head_len - ie_offset,
4031 WLAN_EID_SSID);
4032 if (!ssid_ie)
4033 return -EINVAL;
4034
4035 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4036 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004037 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004038 } else {
4039 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4040 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4041 }
4042
Hante Meulemana44aa402014-12-03 21:05:33 +01004043 if (!mbss) {
4044 brcmf_set_mpc(ifp, 0);
4045 brcmf_configure_arp_offload(ifp, false);
4046 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004047
4048 /* find the RSN_IE */
4049 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4050 settings->beacon.tail_len, WLAN_EID_RSN);
4051
4052 /* find the WPA_IE */
4053 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4054 settings->beacon.tail_len);
4055
Hante Meuleman1a873342012-09-27 14:17:54 +02004056 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004057 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004058 if (wpa_ie != NULL) {
4059 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004060 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004061 if (err < 0)
4062 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004063 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004064 struct brcmf_vs_tlv *tmp_ie;
4065
4066 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4067
Hante Meuleman1a873342012-09-27 14:17:54 +02004068 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004069 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004070 if (err < 0)
4071 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004072 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004073 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004074 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004075 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004076 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004077
Hante Meulemana0f07952013-02-08 15:53:47 +01004078 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004079
Hante Meulemana44aa402014-12-03 21:05:33 +01004080 if (!mbss) {
4081 chanspec = chandef_to_chanspec(&cfg->d11inf,
4082 &settings->chandef);
4083 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004084 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004085 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4086 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004087 goto exit;
4088 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004089
Arend van Spriel98027762014-12-21 12:43:53 +01004090 if (is_11d != ifp->vif->is_11d) {
4091 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4092 is_11d);
4093 if (err < 0) {
4094 brcmf_err("Regulatory Set Error, %d\n", err);
4095 goto exit;
4096 }
4097 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004098 if (settings->beacon_interval) {
4099 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4100 settings->beacon_interval);
4101 if (err < 0) {
4102 brcmf_err("Beacon Interval Set Error, %d\n",
4103 err);
4104 goto exit;
4105 }
4106 }
4107 if (settings->dtim_period) {
4108 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4109 settings->dtim_period);
4110 if (err < 0) {
4111 brcmf_err("DTIM Interval Set Error, %d\n", err);
4112 goto exit;
4113 }
4114 }
4115
4116 if (dev_role == NL80211_IFTYPE_AP) {
4117 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4118 if (err < 0) {
4119 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4120 goto exit;
4121 }
4122 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4123 }
4124
4125 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004126 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004127 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004128 goto exit;
4129 }
Arend van Spriel98027762014-12-21 12:43:53 +01004130 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4131 /* Multiple-BSS should use same 11d configuration */
4132 err = -EINVAL;
4133 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004134 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004135 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004136 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4137 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4138
Hante Meulemana0f07952013-02-08 15:53:47 +01004139 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4140 if (err < 0) {
4141 brcmf_err("setting AP mode failed %d\n", err);
4142 goto exit;
4143 }
4144 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4145 if (err < 0) {
4146 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4147 goto exit;
4148 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004149 /* On DOWN the firmware removes the WEP keys, reconfigure
4150 * them if they were set.
4151 */
4152 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004153
4154 memset(&join_params, 0, sizeof(join_params));
4155 /* join parameters starts with ssid */
4156 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4157 /* create softap */
4158 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4159 &join_params, sizeof(join_params));
4160 if (err < 0) {
4161 brcmf_err("SET SSID error (%d)\n", err);
4162 goto exit;
4163 }
4164 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4165 } else {
4166 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4167 sizeof(ssid_le));
4168 if (err < 0) {
4169 brcmf_err("setting ssid failed %d\n", err);
4170 goto exit;
4171 }
4172 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4173 bss_enable.enable = cpu_to_le32(1);
4174 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4175 sizeof(bss_enable));
4176 if (err < 0) {
4177 brcmf_err("bss_enable config failed %d\n", err);
4178 goto exit;
4179 }
4180
4181 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4182 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004183 clear_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4184 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman1a873342012-09-27 14:17:54 +02004185
4186exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004187 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004188 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004189 brcmf_configure_arp_offload(ifp, true);
4190 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004191 return err;
4192}
4193
4194static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4195{
Arend van Sprielc1179032012-10-22 13:55:33 -07004196 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004197 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004198 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004199 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004200
Arend van Sprield96b8012012-12-05 15:26:02 +01004201 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004202
Hante Meuleman426d0a52013-02-08 15:53:53 +01004203 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004204 /* Due to most likely deauths outstanding we sleep */
4205 /* first to make sure they get processed by fw. */
4206 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004207
Hante Meulemana44aa402014-12-03 21:05:33 +01004208 if (ifp->vif->mbss) {
4209 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4210 return err;
4211 }
4212
Hante Meuleman5c33a942013-04-02 21:06:18 +02004213 memset(&join_params, 0, sizeof(join_params));
4214 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4215 &join_params, sizeof(join_params));
4216 if (err < 0)
4217 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004218 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004219 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004220 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004221 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4222 if (err < 0)
4223 brcmf_err("setting AP mode failed %d\n", err);
4224 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4225 if (err < 0)
4226 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004227 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4228 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004229 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4230 ifp->vif->is_11d);
4231 if (err < 0)
4232 brcmf_err("restoring REGULATORY setting failed %d\n",
4233 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004234 /* Bring device back up so it can be used again */
4235 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4236 if (err < 0)
4237 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004238 } else {
4239 bss_enable.bsscfg_idx = cpu_to_le32(ifp->bssidx);
4240 bss_enable.enable = cpu_to_le32(0);
4241 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4242 sizeof(bss_enable));
4243 if (err < 0)
4244 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004245 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004246 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004247 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004248 set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state);
4249 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
4250
Hante Meuleman1a873342012-09-27 14:17:54 +02004251 return err;
4252}
4253
Hante Meulemana0f07952013-02-08 15:53:47 +01004254static s32
4255brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4256 struct cfg80211_beacon_data *info)
4257{
Hante Meulemana0f07952013-02-08 15:53:47 +01004258 struct brcmf_if *ifp = netdev_priv(ndev);
4259 s32 err;
4260
4261 brcmf_dbg(TRACE, "Enter\n");
4262
Hante Meulemana0f07952013-02-08 15:53:47 +01004263 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4264
4265 return err;
4266}
4267
Hante Meuleman1a873342012-09-27 14:17:54 +02004268static int
4269brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004270 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004271{
Hante Meulemana0f07952013-02-08 15:53:47 +01004272 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004273 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004274 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004275 s32 err;
4276
Jouni Malinen89c771e2014-10-10 20:52:40 +03004277 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004278 return -EFAULT;
4279
Jouni Malinen89c771e2014-10-10 20:52:40 +03004280 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004281
Hante Meulemana0f07952013-02-08 15:53:47 +01004282 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4283 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004284 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004285 return -EIO;
4286
Jouni Malinen89c771e2014-10-10 20:52:40 +03004287 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004288 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004289 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004290 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004291 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004292 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004293
Arend van Sprield96b8012012-12-05 15:26:02 +01004294 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004295 return err;
4296}
4297
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004298static int
4299brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4300 const u8 *mac, struct station_parameters *params)
4301{
4302 struct brcmf_if *ifp = netdev_priv(ndev);
4303 s32 err;
4304
4305 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4306 params->sta_flags_mask, params->sta_flags_set);
4307
4308 /* Ignore all 00 MAC */
4309 if (is_zero_ether_addr(mac))
4310 return 0;
4311
4312 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4313 return 0;
4314
4315 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4316 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4317 (void *)mac, ETH_ALEN);
4318 else
4319 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4320 (void *)mac, ETH_ALEN);
4321 if (err < 0)
4322 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4323
4324 return err;
4325}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004326
4327static void
4328brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4329 struct wireless_dev *wdev,
4330 u16 frame_type, bool reg)
4331{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004332 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004333 u16 mgmt_type;
4334
4335 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4336
4337 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004338 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004339 if (reg)
4340 vif->mgmt_rx_reg |= BIT(mgmt_type);
4341 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004342 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004343}
4344
4345
4346static int
4347brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004348 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004349{
4350 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004351 struct ieee80211_channel *chan = params->chan;
4352 const u8 *buf = params->buf;
4353 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004354 const struct ieee80211_mgmt *mgmt;
4355 struct brcmf_cfg80211_vif *vif;
4356 s32 err = 0;
4357 s32 ie_offset;
4358 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004359 struct brcmf_fil_action_frame_le *action_frame;
4360 struct brcmf_fil_af_params_le *af_params;
4361 bool ack;
4362 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004363 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004364
4365 brcmf_dbg(TRACE, "Enter\n");
4366
4367 *cookie = 0;
4368
4369 mgmt = (const struct ieee80211_mgmt *)buf;
4370
Hante Meulemana0f07952013-02-08 15:53:47 +01004371 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4372 brcmf_err("Driver only allows MGMT packet type\n");
4373 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004374 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004375
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004376 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4377
Hante Meulemana0f07952013-02-08 15:53:47 +01004378 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4379 /* Right now the only reason to get a probe response */
4380 /* is for p2p listen response or for p2p GO from */
4381 /* wpa_supplicant. Unfortunately the probe is send */
4382 /* on primary ndev, while dongle wants it on the p2p */
4383 /* vif. Since this is only reason for a probe */
4384 /* response to be sent, the vif is taken from cfg. */
4385 /* If ever desired to send proberesp for non p2p */
4386 /* response then data should be checked for */
4387 /* "DIRECT-". Note in future supplicant will take */
4388 /* dedicated p2p wdev to do this and then this 'hack'*/
4389 /* is not needed anymore. */
4390 ie_offset = DOT11_MGMT_HDR_LEN +
4391 DOT11_BCN_PRB_FIXED_LEN;
4392 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004393 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4394 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4395 err = brcmf_vif_set_mgmt_ie(vif,
4396 BRCMF_VNDR_IE_PRBRSP_FLAG,
4397 &buf[ie_offset],
4398 ie_len);
4399 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4400 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004401 } else if (ieee80211_is_action(mgmt->frame_control)) {
4402 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4403 if (af_params == NULL) {
4404 brcmf_err("unable to allocate frame\n");
4405 err = -ENOMEM;
4406 goto exit;
4407 }
4408 action_frame = &af_params->action_frame;
4409 /* Add the packet Id */
4410 action_frame->packet_id = cpu_to_le32(*cookie);
4411 /* Add BSSID */
4412 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4413 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4414 /* Add the length exepted for 802.11 header */
4415 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004416 /* Add the channel. Use the one specified as parameter if any or
4417 * the current one (got from the firmware) otherwise
4418 */
4419 if (chan)
4420 freq = chan->center_freq;
4421 else
4422 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4423 &freq);
4424 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004425 af_params->channel = cpu_to_le32(chan_nr);
4426
4427 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4428 le16_to_cpu(action_frame->len));
4429
4430 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004431 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004432
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004433 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004434 af_params);
4435
4436 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4437 GFP_KERNEL);
4438 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004439 } else {
4440 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4441 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4442 }
4443
Hante Meuleman18e2f612013-02-08 15:53:49 +01004444exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004445 return err;
4446}
4447
4448
4449static int
4450brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4451 struct wireless_dev *wdev,
4452 u64 cookie)
4453{
4454 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4455 struct brcmf_cfg80211_vif *vif;
4456 int err = 0;
4457
4458 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4459
4460 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4461 if (vif == NULL) {
4462 brcmf_err("No p2p device available for probe response\n");
4463 err = -ENODEV;
4464 goto exit;
4465 }
4466 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4467exit:
4468 return err;
4469}
4470
Piotr Haber61730d42013-04-23 12:53:12 +02004471static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4472 struct wireless_dev *wdev,
4473 enum nl80211_crit_proto_id proto,
4474 u16 duration)
4475{
4476 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4477 struct brcmf_cfg80211_vif *vif;
4478
4479 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4480
4481 /* only DHCP support for now */
4482 if (proto != NL80211_CRIT_PROTO_DHCP)
4483 return -EINVAL;
4484
4485 /* suppress and abort scanning */
4486 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4487 brcmf_abort_scanning(cfg);
4488
4489 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4490}
4491
4492static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4493 struct wireless_dev *wdev)
4494{
4495 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4496 struct brcmf_cfg80211_vif *vif;
4497
4498 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4499
4500 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4501 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4502}
4503
Hante Meuleman70b7d942014-07-30 13:20:07 +02004504static s32
4505brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4506 const struct brcmf_event_msg *e, void *data)
4507{
4508 switch (e->reason) {
4509 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4510 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4511 break;
4512 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4513 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4514 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4515 break;
4516 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4517 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4518 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4519 break;
4520 }
4521
4522 return 0;
4523}
4524
Arend van Spriel89c2f382013-08-10 12:27:25 +02004525static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4526{
4527 int ret;
4528
4529 switch (oper) {
4530 case NL80211_TDLS_DISCOVERY_REQ:
4531 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4532 break;
4533 case NL80211_TDLS_SETUP:
4534 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4535 break;
4536 case NL80211_TDLS_TEARDOWN:
4537 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4538 break;
4539 default:
4540 brcmf_err("unsupported operation: %d\n", oper);
4541 ret = -EOPNOTSUPP;
4542 }
4543 return ret;
4544}
4545
4546static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004547 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004548 enum nl80211_tdls_operation oper)
4549{
4550 struct brcmf_if *ifp;
4551 struct brcmf_tdls_iovar_le info;
4552 int ret = 0;
4553
4554 ret = brcmf_convert_nl80211_tdls_oper(oper);
4555 if (ret < 0)
4556 return ret;
4557
4558 ifp = netdev_priv(ndev);
4559 memset(&info, 0, sizeof(info));
4560 info.mode = (u8)ret;
4561 if (peer)
4562 memcpy(info.ea, peer, ETH_ALEN);
4563
4564 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4565 &info, sizeof(info));
4566 if (ret < 0)
4567 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4568
4569 return ret;
4570}
4571
Arend van Spriel5b435de2011-10-05 13:19:03 +02004572static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004573 .add_virtual_intf = brcmf_cfg80211_add_iface,
4574 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004575 .change_virtual_intf = brcmf_cfg80211_change_iface,
4576 .scan = brcmf_cfg80211_scan,
4577 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4578 .join_ibss = brcmf_cfg80211_join_ibss,
4579 .leave_ibss = brcmf_cfg80211_leave_ibss,
4580 .get_station = brcmf_cfg80211_get_station,
4581 .set_tx_power = brcmf_cfg80211_set_tx_power,
4582 .get_tx_power = brcmf_cfg80211_get_tx_power,
4583 .add_key = brcmf_cfg80211_add_key,
4584 .del_key = brcmf_cfg80211_del_key,
4585 .get_key = brcmf_cfg80211_get_key,
4586 .set_default_key = brcmf_cfg80211_config_default_key,
4587 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4588 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004589 .connect = brcmf_cfg80211_connect,
4590 .disconnect = brcmf_cfg80211_disconnect,
4591 .suspend = brcmf_cfg80211_suspend,
4592 .resume = brcmf_cfg80211_resume,
4593 .set_pmksa = brcmf_cfg80211_set_pmksa,
4594 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004595 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004596 .start_ap = brcmf_cfg80211_start_ap,
4597 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004598 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004599 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004600 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004601 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4602 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004603 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4604 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4605 .remain_on_channel = brcmf_p2p_remain_on_channel,
4606 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004607 .start_p2p_device = brcmf_p2p_start_device,
4608 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004609 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4610 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004611 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004612};
4613
Arend van Spriel3eacf862012-10-22 13:55:30 -07004614struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004615 enum nl80211_iftype type,
4616 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004617{
Hante Meulemana44aa402014-12-03 21:05:33 +01004618 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004619 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004620 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004621
Arend van Spriel33a6b152013-02-08 15:53:39 +01004622 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004623 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004624 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4625 if (!vif)
4626 return ERR_PTR(-ENOMEM);
4627
4628 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004629 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004630
Arend van Spriel3eacf862012-10-22 13:55:30 -07004631 vif->pm_block = pm_block;
4632 vif->roam_off = -1;
4633
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004634 brcmf_init_prof(&vif->profile);
4635
Hante Meulemana44aa402014-12-03 21:05:33 +01004636 if (type == NL80211_IFTYPE_AP) {
4637 mbss = false;
4638 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4639 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4640 mbss = true;
4641 break;
4642 }
4643 }
4644 vif->mbss = mbss;
4645 }
4646
Arend van Spriel3eacf862012-10-22 13:55:30 -07004647 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004648 return vif;
4649}
4650
Arend van Spriel427dec52014-01-06 12:40:47 +01004651void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004652{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004653 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004654 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004655}
4656
Arend van Spriel9df4d542014-01-06 12:40:49 +01004657void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4658{
4659 struct brcmf_cfg80211_vif *vif;
4660 struct brcmf_if *ifp;
4661
4662 ifp = netdev_priv(ndev);
4663 vif = ifp->vif;
4664
Arend van Spriel95ef1232015-08-26 22:15:04 +02004665 if (vif)
4666 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01004667 free_netdev(ndev);
4668}
4669
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004670static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004671{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004672 u32 event = e->event_code;
4673 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004674
4675 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004676 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004677 return true;
4678 }
4679
4680 return false;
4681}
4682
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004683static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004684{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004685 u32 event = e->event_code;
4686 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004687
Hante Meuleman68ca3952014-02-25 20:30:26 +01004688 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4689 (event == BRCMF_E_DISASSOC_IND) ||
4690 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004691 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004692 return true;
4693 }
4694 return false;
4695}
4696
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004697static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004698 const struct brcmf_event_msg *e)
4699{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004700 u32 event = e->event_code;
4701 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004702
4703 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004704 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4705 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004706 return true;
4707 }
4708
4709 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004710 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004711 return true;
4712 }
4713
4714 return false;
4715}
4716
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004717static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004718{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004719 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004720
4721 kfree(conn_info->req_ie);
4722 conn_info->req_ie = NULL;
4723 conn_info->req_ie_len = 0;
4724 kfree(conn_info->resp_ie);
4725 conn_info->resp_ie = NULL;
4726 conn_info->resp_ie_len = 0;
4727}
4728
Hante Meuleman89286dc2013-02-08 15:53:46 +01004729static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
4730 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004731{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004732 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004733 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004734 u32 req_len;
4735 u32 resp_len;
4736 s32 err = 0;
4737
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004738 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004739
Arend van Sprielac24be62012-10-22 10:36:23 -07004740 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
4741 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004742 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004743 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004744 return err;
4745 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004746 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004747 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02004748 req_len = le32_to_cpu(assoc_info->req_len);
4749 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004750 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004751 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004752 cfg->extra_buf,
4753 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004754 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004755 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004756 return err;
4757 }
4758 conn_info->req_ie_len = req_len;
4759 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004760 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004761 GFP_KERNEL);
4762 } else {
4763 conn_info->req_ie_len = 0;
4764 conn_info->req_ie = NULL;
4765 }
4766 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07004767 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004768 cfg->extra_buf,
4769 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004770 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004771 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004772 return err;
4773 }
4774 conn_info->resp_ie_len = resp_len;
4775 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004776 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004777 GFP_KERNEL);
4778 } else {
4779 conn_info->resp_ie_len = 0;
4780 conn_info->resp_ie = NULL;
4781 }
Arend van Spriel16886732012-12-05 15:26:04 +01004782 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
4783 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004784
4785 return err;
4786}
4787
4788static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004789brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004790 struct net_device *ndev,
4791 const struct brcmf_event_msg *e)
4792{
Arend van Sprielc1179032012-10-22 13:55:33 -07004793 struct brcmf_if *ifp = netdev_priv(ndev);
4794 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004795 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
4796 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07004797 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004798 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07004799 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02004800 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004801 u32 freq;
4802 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07004803 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004804
Arend van Sprield96b8012012-12-05 15:26:02 +01004805 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004806
Hante Meuleman89286dc2013-02-08 15:53:46 +01004807 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004808 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004809 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004810
Franky Lina180b832012-10-10 11:13:09 -07004811 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
4812 if (buf == NULL) {
4813 err = -ENOMEM;
4814 goto done;
4815 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02004816
Franky Lina180b832012-10-10 11:13:09 -07004817 /* data sent to dongle has to be little endian */
4818 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07004819 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07004820 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07004821
4822 if (err)
4823 goto done;
4824
4825 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02004826 ch.chspec = le16_to_cpu(bi->chanspec);
4827 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004828
Franky Lin83cf17a2013-04-11 13:28:50 +02004829 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004830 band = wiphy->bands[IEEE80211_BAND_2GHZ];
4831 else
4832 band = wiphy->bands[IEEE80211_BAND_5GHZ];
4833
Franky Lin83cf17a2013-04-11 13:28:50 +02004834 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004835 notify_channel = ieee80211_get_channel(wiphy, freq);
4836
Franky Lina180b832012-10-10 11:13:09 -07004837done:
4838 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02004839 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004840 conn_info->req_ie, conn_info->req_ie_len,
4841 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004842 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004843
Arend van Sprielc1179032012-10-22 13:55:33 -07004844 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01004845 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004846 return err;
4847}
4848
4849static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004850brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004851 struct net_device *ndev, const struct brcmf_event_msg *e,
4852 bool completed)
4853{
Arend van Sprielc1179032012-10-22 13:55:33 -07004854 struct brcmf_if *ifp = netdev_priv(ndev);
4855 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004856 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004857
Arend van Sprield96b8012012-12-05 15:26:02 +01004858 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004859
Arend van Sprielc1179032012-10-22 13:55:33 -07004860 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4861 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02004862 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01004863 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004864 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01004865 brcmf_update_bss_info(cfg, ifp);
4866 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4867 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004868 }
4869 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02004870 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004871 conn_info->req_ie,
4872 conn_info->req_ie_len,
4873 conn_info->resp_ie,
4874 conn_info->resp_ie_len,
4875 completed ? WLAN_STATUS_SUCCESS :
4876 WLAN_STATUS_AUTH_TIMEOUT,
4877 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01004878 brcmf_dbg(CONN, "Report connect result - connection %s\n",
4879 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004880 }
Arend van Sprield96b8012012-12-05 15:26:02 +01004881 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004882 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004883}
4884
4885static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004886brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02004887 struct net_device *ndev,
4888 const struct brcmf_event_msg *e, void *data)
4889{
Hante Meulemana44aa402014-12-03 21:05:33 +01004890 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01004891 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004892 u32 event = e->event_code;
4893 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02004894 struct station_info sinfo;
4895
Arend van Spriel16886732012-12-05 15:26:04 +01004896 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004897 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
4898 ndev != cfg_to_ndev(cfg)) {
4899 brcmf_dbg(CONN, "AP mode link down\n");
4900 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01004901 if (ifp->vif->mbss)
Arend van Sprielee6e3a32015-08-26 22:14:55 +02004902 brcmf_remove_interface(ifp);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004903 return 0;
4904 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004905
Hante Meuleman1a873342012-09-27 14:17:54 +02004906 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01004907 (reason == BRCMF_E_STATUS_SUCCESS)) {
4908 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02004909 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004910 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02004911 return -EINVAL;
4912 }
4913 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004914 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02004915 generation++;
4916 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01004917 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004918 } else if ((event == BRCMF_E_DISASSOC_IND) ||
4919 (event == BRCMF_E_DEAUTH_IND) ||
4920 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01004921 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02004922 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01004923 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004924}
4925
4926static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004927brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004928 const struct brcmf_event_msg *e, void *data)
4929{
Arend van Spriel19937322012-11-05 16:22:32 -08004930 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
4931 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07004932 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004933 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004934 s32 err = 0;
4935
Hante Meuleman8851cce2014-07-30 13:20:02 +02004936 if ((e->event_code == BRCMF_E_DEAUTH) ||
4937 (e->event_code == BRCMF_E_DEAUTH_IND) ||
4938 (e->event_code == BRCMF_E_DISASSOC_IND) ||
4939 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
4940 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4941 }
4942
Arend van Spriel967fe2c2014-03-15 17:18:21 +01004943 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004944 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004945 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004946 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004947 if (brcmf_is_ibssmode(ifp->vif)) {
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004948 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02004949 memcpy(profile->bssid, e->addr, ETH_ALEN);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004950 wl_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01004951 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07004952 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4953 &ifp->vif->sme_state);
4954 set_bit(BRCMF_VIF_STATUS_CONNECTED,
4955 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004956 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004957 brcmf_bss_connect_done(cfg, ndev, e, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004958 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01004959 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004960 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004961 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004962 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01004963 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004964 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004965 if (ndev != cfg_to_ndev(cfg))
4966 complete(&cfg->vif_disabled);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004967 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01004968 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07004969 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
4970 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004971 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004972 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004973 }
4974
4975 return err;
4976}
4977
4978static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004979brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004980 const struct brcmf_event_msg *e, void *data)
4981{
Arend van Spriel19937322012-11-05 16:22:32 -08004982 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08004983 u32 event = e->event_code;
4984 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004985
4986 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004987 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08004988 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004989 else
Arend van Spriel19937322012-11-05 16:22:32 -08004990 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004991 }
4992
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004993 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004994}
4995
4996static s32
Arend van Spriel19937322012-11-05 16:22:32 -08004997brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004998 const struct brcmf_event_msg *e, void *data)
4999{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005000 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005001 enum nl80211_key_type key_type;
5002
5003 if (flags & BRCMF_EVENT_MSG_GROUP)
5004 key_type = NL80211_KEYTYPE_GROUP;
5005 else
5006 key_type = NL80211_KEYTYPE_PAIRWISE;
5007
Arend van Spriel19937322012-11-05 16:22:32 -08005008 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005009 NULL, GFP_KERNEL);
5010
5011 return 0;
5012}
5013
Arend van Sprield3c0b632013-02-08 15:53:37 +01005014static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5015 const struct brcmf_event_msg *e, void *data)
5016{
5017 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5018 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5019 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5020 struct brcmf_cfg80211_vif *vif;
5021
5022 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfg %u\n",
5023 ifevent->action, ifevent->flags, ifevent->ifidx,
5024 ifevent->bssidx);
5025
Arend van Sprield3c0b632013-02-08 15:53:37 +01005026 mutex_lock(&event->vif_event_lock);
5027 event->action = ifevent->action;
5028 vif = event->vif;
5029
5030 switch (ifevent->action) {
5031 case BRCMF_E_IF_ADD:
5032 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005033 if (!cfg->vif_event.vif) {
5034 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005035 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005036 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005037
5038 ifp->vif = vif;
5039 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005040 if (ifp->ndev) {
5041 vif->wdev.netdev = ifp->ndev;
5042 ifp->ndev->ieee80211_ptr = &vif->wdev;
5043 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5044 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005045 mutex_unlock(&event->vif_event_lock);
5046 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005047 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005048
5049 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005050 mutex_unlock(&event->vif_event_lock);
5051 /* event may not be upon user request */
5052 if (brcmf_cfg80211_vif_event_armed(cfg))
5053 wake_up(&event->vif_wq);
5054 return 0;
5055
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005056 case BRCMF_E_IF_CHANGE:
5057 mutex_unlock(&event->vif_event_lock);
5058 wake_up(&event->vif_wq);
5059 return 0;
5060
Arend van Sprield3c0b632013-02-08 15:53:37 +01005061 default:
5062 mutex_unlock(&event->vif_event_lock);
5063 break;
5064 }
5065 return -EINVAL;
5066}
5067
Arend van Spriel5b435de2011-10-05 13:19:03 +02005068static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5069{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005070 conf->frag_threshold = (u32)-1;
5071 conf->rts_threshold = (u32)-1;
5072 conf->retry_short = (u32)-1;
5073 conf->retry_long = (u32)-1;
5074 conf->tx_power = -1;
5075}
5076
Arend van Spriel5c36b992012-11-14 18:46:05 -08005077static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005078{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005079 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5080 brcmf_notify_connect_status);
5081 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5082 brcmf_notify_connect_status);
5083 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5084 brcmf_notify_connect_status);
5085 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5086 brcmf_notify_connect_status);
5087 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5088 brcmf_notify_connect_status);
5089 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5090 brcmf_notify_connect_status);
5091 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5092 brcmf_notify_roaming_status);
5093 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5094 brcmf_notify_mic_status);
5095 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5096 brcmf_notify_connect_status);
5097 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5098 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005099 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5100 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005101 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005102 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005103 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5104 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005105 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5106 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005107 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5108 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005109 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5110 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005111}
5112
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005113static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005114{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005115 kfree(cfg->conf);
5116 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005117 kfree(cfg->escan_ioctl_buf);
5118 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005119 kfree(cfg->extra_buf);
5120 cfg->extra_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005121 kfree(cfg->pmk_list);
5122 cfg->pmk_list = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005123}
5124
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005125static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005126{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005127 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5128 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005129 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005130 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5131 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005132 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005133 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5134 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005135 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005136 cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL);
5137 if (!cfg->pmk_list)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005138 goto init_priv_mem_out;
5139
5140 return 0;
5141
5142init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005143 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005144
5145 return -ENOMEM;
5146}
5147
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005148static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005149{
5150 s32 err = 0;
5151
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005152 cfg->scan_request = NULL;
5153 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005154 cfg->active_scan = true; /* we do active scan per default */
5155 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005156 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005157 if (err)
5158 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005159 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005160 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005161 brcmf_init_escan(cfg);
5162 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005163 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005164 return err;
5165}
5166
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005167static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005168{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005169 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005170 brcmf_abort_scanning(cfg);
5171 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005172}
5173
Arend van Sprield3c0b632013-02-08 15:53:37 +01005174static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5175{
5176 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005177 mutex_init(&event->vif_event_lock);
5178}
5179
Arend van Spriel5b435de2011-10-05 13:19:03 +02005180static s32
Hante Meuleman68ca3952014-02-25 20:30:26 +01005181brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005182{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005183 s32 err = 0;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005184 __le32 roamtrigger[2];
5185 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005186
5187 /*
5188 * Setup timeout if Beacons are lost and roam is
5189 * off to report link down
5190 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005191 if (brcmf_roamoff) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005192 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005193 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005194 brcmf_err("bcn_timeout error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005195 goto dongle_rom_out;
5196 }
5197 }
5198
5199 /*
5200 * Enable/Disable built-in roaming to allow supplicant
5201 * to take care of roaming
5202 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005203 brcmf_dbg(INFO, "Internal Roaming = %s\n",
5204 brcmf_roamoff ? "Off" : "On");
5205 err = brcmf_fil_iovar_int_set(ifp, "roam_off", !!(brcmf_roamoff));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005206 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005207 brcmf_err("roam_off error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005208 goto dongle_rom_out;
5209 }
5210
Arend van Sprielf588bc02011-10-12 20:51:22 +02005211 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5212 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005213 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005214 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005215 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005216 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005217 goto dongle_rom_out;
5218 }
5219
Arend van Sprielf588bc02011-10-12 20:51:22 +02005220 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5221 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005222 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005223 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005224 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005225 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005226 goto dongle_rom_out;
5227 }
5228
5229dongle_rom_out:
5230 return err;
5231}
5232
5233static s32
Hante Meuleman40a23292013-01-02 15:22:51 +01005234brcmf_dongle_scantime(struct brcmf_if *ifp, s32 scan_assoc_time,
Arend van Sprielc68cdc02011-10-12 20:51:23 +02005235 s32 scan_unassoc_time, s32 scan_passive_time)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005236{
5237 s32 err = 0;
5238
Arend van Sprielac24be62012-10-22 10:36:23 -07005239 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005240 scan_assoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005241 if (err) {
5242 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005243 brcmf_dbg(INFO, "Scan assoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005244 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005245 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005246 goto dongle_scantime_out;
5247 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005248 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005249 scan_unassoc_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005250 if (err) {
5251 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005252 brcmf_dbg(INFO, "Scan unassoc time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005253 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005254 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005255 goto dongle_scantime_out;
5256 }
5257
Arend van Sprielac24be62012-10-22 10:36:23 -07005258 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005259 scan_passive_time);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005260 if (err) {
5261 if (err == -EOPNOTSUPP)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005262 brcmf_dbg(INFO, "Scan passive time is not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005263 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01005264 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005265 goto dongle_scantime_out;
5266 }
5267
5268dongle_scantime_out:
5269 return err;
5270}
5271
Arend van Sprielb48d8912014-07-12 08:49:41 +02005272static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5273 struct brcmu_chan *ch)
5274{
5275 u32 ht40_flag;
5276
5277 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5278 if (ch->sb == BRCMU_CHAN_SB_U) {
5279 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5280 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5281 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5282 } else {
5283 /* It should be one of
5284 * IEEE80211_CHAN_NO_HT40 or
5285 * IEEE80211_CHAN_NO_HT40PLUS
5286 */
5287 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5288 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5289 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5290 }
5291}
5292
5293static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5294 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005295{
5296 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005297 struct ieee80211_supported_band *band;
5298 struct ieee80211_channel *channel;
5299 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005300 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005301 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005302 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005303 u8 *pbuf;
5304 u32 i, j;
5305 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005306 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005307 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005308
5309 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5310
5311 if (pbuf == NULL)
5312 return -ENOMEM;
5313
5314 list = (struct brcmf_chanspec_list *)pbuf;
5315
5316 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5317 BRCMF_DCMD_MEDLEN);
5318 if (err) {
5319 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005320 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005321 }
5322
Arend van Sprielb48d8912014-07-12 08:49:41 +02005323 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005324 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5325 if (band)
5326 for (i = 0; i < band->n_channels; i++)
5327 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5328 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5329 if (band)
5330 for (i = 0; i < band->n_channels; i++)
5331 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005332
5333 total = le32_to_cpu(list->count);
5334 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005335 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5336 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005337
Franky Lin83cf17a2013-04-11 13:28:50 +02005338 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005339 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005340 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005341 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005342 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005343 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005344 continue;
5345 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005346 if (!band)
5347 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005348 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005349 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005350 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005351 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005352 ch.bw == BRCMU_CHAN_BW_80)
5353 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005354
5355 channel = band->channels;
5356 index = band->n_channels;
5357 for (j = 0; j < band->n_channels; j++) {
5358 if (channel[j].hw_value == ch.chnum) {
5359 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005360 break;
5361 }
5362 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005363 channel[index].center_freq =
5364 ieee80211_channel_to_frequency(ch.chnum, band->band);
5365 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005366
Arend van Sprielb48d8912014-07-12 08:49:41 +02005367 /* assuming the chanspecs order is HT20,
5368 * HT40 upper, HT40 lower, and VHT80.
5369 */
5370 if (ch.bw == BRCMU_CHAN_BW_80) {
5371 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5372 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5373 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5374 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005375 /* enable the channel and disable other bandwidths
5376 * for now as mentioned order assure they are enabled
5377 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005378 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005379 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5380 IEEE80211_CHAN_NO_80MHZ;
5381 ch.bw = BRCMU_CHAN_BW_20;
5382 cfg->d11inf.encchspec(&ch);
5383 chaninfo = ch.chspec;
5384 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5385 &chaninfo);
5386 if (!err) {
5387 if (chaninfo & WL_CHAN_RADAR)
5388 channel[index].flags |=
5389 (IEEE80211_CHAN_RADAR |
5390 IEEE80211_CHAN_NO_IR);
5391 if (chaninfo & WL_CHAN_PASSIVE)
5392 channel[index].flags |=
5393 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005394 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005395 }
5396 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005397
Arend van Sprielb48d8912014-07-12 08:49:41 +02005398fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005399 kfree(pbuf);
5400 return err;
5401}
5402
Arend van Sprielb48d8912014-07-12 08:49:41 +02005403static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005404{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005405 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5406 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005407 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005408 struct brcmf_chanspec_list *list;
5409 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005410 u32 val;
5411 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005412 struct brcmu_chan ch;
5413 u32 num_chan;
5414 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005415
5416 /* verify support for bw_cap command */
5417 val = WLC_BAND_5G;
5418 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5419
5420 if (!err) {
5421 /* only set 2G bandwidth using bw_cap command */
5422 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5423 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5424 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5425 sizeof(band_bwcap));
5426 } else {
5427 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5428 val = WLC_N_BW_40ALL;
5429 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5430 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005431
5432 if (!err) {
5433 /* update channel info in 2G band */
5434 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5435
5436 if (pbuf == NULL)
5437 return -ENOMEM;
5438
5439 ch.band = BRCMU_CHAN_BAND_2G;
5440 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005441 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005442 ch.chnum = 0;
5443 cfg->d11inf.encchspec(&ch);
5444
5445 /* pass encoded chanspec in query */
5446 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5447
5448 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5449 BRCMF_DCMD_MEDLEN);
5450 if (err) {
5451 brcmf_err("get chanspecs error (%d)\n", err);
5452 kfree(pbuf);
5453 return err;
5454 }
5455
5456 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5457 list = (struct brcmf_chanspec_list *)pbuf;
5458 num_chan = le32_to_cpu(list->count);
5459 for (i = 0; i < num_chan; i++) {
5460 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5461 cfg->d11inf.decchspec(&ch);
5462 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5463 continue;
5464 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5465 continue;
5466 for (j = 0; j < band->n_channels; j++) {
5467 if (band->channels[j].hw_value == ch.chnum)
5468 break;
5469 }
5470 if (WARN_ON(j == band->n_channels))
5471 continue;
5472
5473 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5474 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005475 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005476 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005477 return err;
5478}
5479
Arend van Spriel2375d972014-01-06 12:40:41 +01005480static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5481{
5482 u32 band, mimo_bwcap;
5483 int err;
5484
5485 band = WLC_BAND_2G;
5486 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5487 if (!err) {
5488 bw_cap[IEEE80211_BAND_2GHZ] = band;
5489 band = WLC_BAND_5G;
5490 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5491 if (!err) {
5492 bw_cap[IEEE80211_BAND_5GHZ] = band;
5493 return;
5494 }
5495 WARN_ON(1);
5496 return;
5497 }
5498 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5499 mimo_bwcap = 0;
5500 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5501 if (err)
5502 /* assume 20MHz if firmware does not give a clue */
5503 mimo_bwcap = WLC_N_BW_20ALL;
5504
5505 switch (mimo_bwcap) {
5506 case WLC_N_BW_40ALL:
5507 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5508 /* fall-thru */
5509 case WLC_N_BW_20IN2G_40IN5G:
5510 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5511 /* fall-thru */
5512 case WLC_N_BW_20ALL:
5513 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5514 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5515 break;
5516 default:
5517 brcmf_err("invalid mimo_bw_cap value\n");
5518 }
5519}
Hante Meulemand48200b2013-04-03 12:40:29 +02005520
Arend van Spriel18d6c532014-05-12 10:47:35 +02005521static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5522 u32 bw_cap[2], u32 nchain)
5523{
5524 band->ht_cap.ht_supported = true;
5525 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5526 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5527 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5528 }
5529 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5530 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5531 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5532 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5533 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5534 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5535}
5536
5537static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5538{
5539 u16 mcs_map;
5540 int i;
5541
5542 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5543 mcs_map = (mcs_map << 2) | supp;
5544
5545 return cpu_to_le16(mcs_map);
5546}
5547
5548static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
5549 u32 bw_cap[2], u32 nchain)
5550{
5551 __le16 mcs_map;
5552
5553 /* not allowed in 2.4G band */
5554 if (band->band == IEEE80211_BAND_2GHZ)
5555 return;
5556
5557 band->vht_cap.vht_supported = true;
5558 /* 80MHz is mandatory */
5559 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5560 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5561 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5562 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5563 }
5564 /* all support 256-QAM */
5565 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5566 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5567 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
5568}
5569
Arend van Sprielb48d8912014-07-12 08:49:41 +02005570static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005571{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005572 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005573 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005574 u32 nmode = 0;
5575 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005576 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005577 u32 rxchain;
5578 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005579 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005580 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005581 struct ieee80211_supported_band *band;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005582
Arend van Spriel18d6c532014-05-12 10:47:35 +02005583 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005584 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5585 if (err) {
5586 brcmf_err("nmode error (%d)\n", err);
5587 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005588 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005589 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005590 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5591 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5592 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005593
Daniel Kim4aca7a12014-02-25 20:30:36 +01005594 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5595 if (err) {
5596 brcmf_err("rxchain error (%d)\n", err);
5597 nchain = 1;
5598 } else {
5599 for (nchain = 0; rxchain; nchain++)
5600 rxchain = rxchain & (rxchain - 1);
5601 }
5602 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5603
Arend van Sprielb48d8912014-07-12 08:49:41 +02005604 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005605 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005606 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005607 return err;
5608 }
5609
Arend van Sprielb48d8912014-07-12 08:49:41 +02005610 wiphy = cfg_to_wiphy(cfg);
5611 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5612 band = wiphy->bands[i];
5613 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005614 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005615
Arend van Spriel18d6c532014-05-12 10:47:35 +02005616 if (nmode)
5617 brcmf_update_ht_cap(band, bw_cap, nchain);
5618 if (vhtmode)
5619 brcmf_update_vht_cap(band, bw_cap, nchain);
Hante Meulemand48200b2013-04-03 12:40:29 +02005620 }
5621
Arend van Sprielb48d8912014-07-12 08:49:41 +02005622 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005623}
5624
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005625static const struct ieee80211_txrx_stypes
5626brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5627 [NL80211_IFTYPE_STATION] = {
5628 .tx = 0xffff,
5629 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5630 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5631 },
5632 [NL80211_IFTYPE_P2P_CLIENT] = {
5633 .tx = 0xffff,
5634 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5635 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5636 },
5637 [NL80211_IFTYPE_P2P_GO] = {
5638 .tx = 0xffff,
5639 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5640 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5641 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5642 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5643 BIT(IEEE80211_STYPE_AUTH >> 4) |
5644 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5645 BIT(IEEE80211_STYPE_ACTION >> 4)
5646 },
5647 [NL80211_IFTYPE_P2P_DEVICE] = {
5648 .tx = 0xffff,
5649 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5650 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5651 }
5652};
5653
Arend van Spriel0882dda2015-08-20 22:06:03 +02005654/**
5655 * brcmf_setup_ifmodes() - determine interface modes and combinations.
5656 *
5657 * @wiphy: wiphy object.
5658 * @ifp: interface object needed for feat module api.
5659 *
5660 * The interface modes and combinations are determined dynamically here
5661 * based on firmware functionality.
5662 *
5663 * no p2p and no mbss:
5664 *
5665 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5666 *
5667 * no p2p and mbss:
5668 *
5669 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5670 * #AP <= 4, matching BI, channels = 1, 4 total
5671 *
5672 * p2p, no mchan, and mbss:
5673 *
5674 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
5675 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5676 * #AP <= 4, matching BI, channels = 1, 4 total
5677 *
5678 * p2p, mchan, and mbss:
5679 *
5680 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
5681 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5682 * #AP <= 4, matching BI, channels = 1, 4 total
5683 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005684static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
5685{
5686 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02005687 struct ieee80211_iface_limit *c0_limits = NULL;
5688 struct ieee80211_iface_limit *p2p_limits = NULL;
5689 struct ieee80211_iface_limit *mbss_limits = NULL;
5690 bool mbss, p2p;
5691 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005692
Arend van Spriel0882dda2015-08-20 22:06:03 +02005693 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
5694 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
5695
5696 n_combos = 1 + !!p2p + !!mbss;
5697 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005698 if (!combo)
5699 goto err;
5700
Arend van Spriel0882dda2015-08-20 22:06:03 +02005701 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
5702 if (!c0_limits)
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005703 goto err;
5704
Arend van Spriel0882dda2015-08-20 22:06:03 +02005705 if (p2p) {
5706 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
5707 if (!p2p_limits)
5708 goto err;
5709 }
5710
5711 if (mbss) {
5712 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
5713 if (!mbss_limits)
5714 goto err;
5715 }
5716
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005717 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
5718 BIT(NL80211_IFTYPE_ADHOC) |
5719 BIT(NL80211_IFTYPE_AP);
5720
Arend van Spriel0882dda2015-08-20 22:06:03 +02005721 c = 0;
5722 i = 0;
5723 combo[c].num_different_channels = 1;
5724 c0_limits[i].max = 1;
5725 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5726 if (p2p) {
5727 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
5728 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005729 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
5730 BIT(NL80211_IFTYPE_P2P_GO) |
5731 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02005732 c0_limits[i].max = 1;
5733 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5734 c0_limits[i].max = 1;
5735 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
5736 BIT(NL80211_IFTYPE_P2P_GO);
5737 } else {
5738 c0_limits[i].max = 1;
5739 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005740 }
Arend van Spriel0882dda2015-08-20 22:06:03 +02005741 combo[c].max_interfaces = i;
5742 combo[c].n_limits = i;
5743 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005744
Arend van Spriel0882dda2015-08-20 22:06:03 +02005745 if (p2p) {
5746 c++;
5747 i = 0;
5748 combo[c].num_different_channels = 1;
5749 p2p_limits[i].max = 1;
5750 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
5751 p2p_limits[i].max = 1;
5752 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
5753 p2p_limits[i].max = 1;
5754 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
5755 p2p_limits[i].max = 1;
5756 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
5757 combo[c].max_interfaces = i;
5758 combo[c].n_limits = i;
5759 combo[c].limits = p2p_limits;
5760 }
5761
5762 if (mbss) {
5763 c++;
5764 combo[c].beacon_int_infra_match = true;
5765 combo[c].num_different_channels = 1;
5766 mbss_limits[0].max = 4;
5767 mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
5768 combo[c].max_interfaces = 4;
5769 combo[c].n_limits = 1;
5770 combo[c].limits = mbss_limits;
5771 }
5772 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005773 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005774 return 0;
5775
5776err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02005777 kfree(c0_limits);
5778 kfree(p2p_limits);
5779 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005780 kfree(combo);
5781 return -ENOMEM;
5782}
5783
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005784static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
5785{
5786 /* scheduled scan settings */
5787 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
5788 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
5789 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5790 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
5791}
5792
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005793#ifdef CONFIG_PM
5794static const struct wiphy_wowlan_support brcmf_wowlan_support = {
5795 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01005796 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
5797 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
5798 .pattern_min_len = 1,
5799 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005800};
5801#endif
5802
5803static void brcmf_wiphy_wowl_params(struct wiphy *wiphy)
5804{
5805#ifdef CONFIG_PM
5806 /* wowl settings */
5807 wiphy->wowlan = &brcmf_wowlan_support;
5808#endif
5809}
5810
Arend van Sprielb48d8912014-07-12 08:49:41 +02005811static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005812{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005813 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005814 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005815 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005816 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02005817 __le32 bandlist[3];
5818 u32 n_bands;
5819 int err, i;
5820
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005821 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
5822 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
5823 wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02005824
5825 err = brcmf_setup_ifmodes(wiphy, ifp);
5826 if (err)
5827 return err;
5828
Rafał Miłecki50f32e22015-08-20 00:16:42 +02005829 for (i = 0, combo = wiphy->iface_combinations;
5830 i < wiphy->n_iface_combinations; i++, combo++) {
5831 max_interfaces = max(max_interfaces, combo->max_interfaces);
5832 }
5833
5834 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
5835 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02005836 u8 *addr = drvr->addresses[i].addr;
5837
5838 memcpy(addr, drvr->mac, ETH_ALEN);
5839 if (i) {
5840 addr[0] |= BIT(1);
5841 addr[ETH_ALEN - 1] ^= i;
5842 }
5843 }
5844 wiphy->addresses = drvr->addresses;
5845 wiphy->n_addresses = i;
5846
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005847 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
5848 wiphy->cipher_suites = __wl_cipher_suites;
5849 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
5850 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
5851 WIPHY_FLAG_OFFCHAN_TX |
5852 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
5853 WIPHY_FLAG_SUPPORTS_TDLS;
5854 if (!brcmf_roamoff)
5855 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
5856 wiphy->mgmt_stypes = brcmf_txrx_stypes;
5857 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02005858 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
5859 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005860
5861 /* vendor commands/events support */
5862 wiphy->vendor_commands = brcmf_vendor_cmds;
5863 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
5864
Hante Meuleman4eb3af72014-09-30 10:23:18 +02005865 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
5866 brcmf_wiphy_wowl_params(wiphy);
5867
Arend van Spriel58de92d2015-04-14 20:10:24 +02005868 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
5869 sizeof(bandlist));
5870 if (err) {
5871 brcmf_err("could not obtain band info: err=%d\n", err);
5872 return err;
5873 }
5874 /* first entry in bandlist is number of bands */
5875 n_bands = le32_to_cpu(bandlist[0]);
5876 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
5877 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
5878 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
5879 GFP_KERNEL);
5880 if (!band)
5881 return -ENOMEM;
5882
5883 band->channels = kmemdup(&__wl_2ghz_channels,
5884 sizeof(__wl_2ghz_channels),
5885 GFP_KERNEL);
5886 if (!band->channels) {
5887 kfree(band);
5888 return -ENOMEM;
5889 }
5890
5891 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
5892 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
5893 }
5894 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
5895 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
5896 GFP_KERNEL);
5897 if (!band)
5898 return -ENOMEM;
5899
5900 band->channels = kmemdup(&__wl_5ghz_channels,
5901 sizeof(__wl_5ghz_channels),
5902 GFP_KERNEL);
5903 if (!band->channels) {
5904 kfree(band);
5905 return -ENOMEM;
5906 }
5907
5908 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
5909 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
5910 }
5911 }
5912 err = brcmf_setup_wiphybands(wiphy);
5913 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005914}
5915
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005916static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005917{
5918 struct net_device *ndev;
5919 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01005920 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005921 s32 power_mode;
5922 s32 err = 0;
5923
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005924 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005925 return err;
5926
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005927 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005928 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01005929 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005930
Hante Meuleman40a23292013-01-02 15:22:51 +01005931 /* make sure RF is ready for work */
5932 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
5933
5934 brcmf_dongle_scantime(ifp, WL_SCAN_CHANNEL_TIME,
5935 WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005936
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005937 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01005938 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005939 if (err)
5940 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01005941 brcmf_dbg(INFO, "power save set to %s\n",
5942 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005943
Hante Meuleman68ca3952014-02-25 20:30:26 +01005944 err = brcmf_dongle_roam(ifp, WL_BEACON_TIMEOUT);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005945 if (err)
5946 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07005947 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
5948 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01005949 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005950 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005951
Hante Meulemanb3657452013-05-27 21:09:53 +02005952 brcmf_configure_arp_offload(ifp, true);
5953
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005954 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01005955default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005956
5957 return err;
5958
5959}
5960
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005961static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005962{
Arend van Sprielc1179032012-10-22 13:55:33 -07005963 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005964
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005965 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005966}
5967
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005968static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005969{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005970 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07005971
Arend van Spriel5b435de2011-10-05 13:19:03 +02005972 /*
5973 * While going down, if associated with AP disassociate
5974 * from AP to save power
5975 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005976 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005977 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005978
5979 /* Make sure WPA_Supplicant receives all the event
5980 generated due to DISASSOC call to the fw to keep
5981 the state fw and WPA_Supplicant state consistent
5982 */
5983 brcmf_delay(500);
5984 }
5985
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005986 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07005987 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005988
Arend van Spriel5b435de2011-10-05 13:19:03 +02005989 return 0;
5990}
5991
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005992s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005993{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005994 struct brcmf_if *ifp = netdev_priv(ndev);
5995 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005996 s32 err = 0;
5997
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005998 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08005999 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006000 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006001
6002 return err;
6003}
6004
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006005s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006006{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006007 struct brcmf_if *ifp = netdev_priv(ndev);
6008 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006009 s32 err = 0;
6010
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006011 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006012 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006013 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006014
6015 return err;
6016}
6017
Arend van Spriela7965fb2013-04-11 17:08:37 +02006018enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6019{
6020 struct wireless_dev *wdev = &ifp->vif->wdev;
6021
6022 return wdev->iftype;
6023}
6024
Hante Meulemanbfe81972014-10-28 14:56:16 +01006025bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6026 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006027{
6028 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006029
6030 list_for_each_entry(vif, &cfg->vif_list, list) {
6031 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006032 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006033 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006034 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006035}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006036
6037static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6038 u8 action)
6039{
6040 u8 evt_action;
6041
6042 mutex_lock(&event->vif_event_lock);
6043 evt_action = event->action;
6044 mutex_unlock(&event->vif_event_lock);
6045 return evt_action == action;
6046}
6047
6048void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6049 struct brcmf_cfg80211_vif *vif)
6050{
6051 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6052
6053 mutex_lock(&event->vif_event_lock);
6054 event->vif = vif;
6055 event->action = 0;
6056 mutex_unlock(&event->vif_event_lock);
6057}
6058
6059bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6060{
6061 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6062 bool armed;
6063
6064 mutex_lock(&event->vif_event_lock);
6065 armed = event->vif != NULL;
6066 mutex_unlock(&event->vif_event_lock);
6067
6068 return armed;
6069}
6070int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6071 u8 action, ulong timeout)
6072{
6073 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6074
6075 return wait_event_timeout(event->vif_wq,
6076 vif_event_equals(event, action), timeout);
6077}
6078
Arend van Spriel63db1a42014-12-21 12:43:51 +01006079static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6080 struct regulatory_request *req)
6081{
6082 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6083 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6084 struct brcmf_fil_country_le ccreq;
6085 int i;
6086
6087 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6088 req->alpha2[0], req->alpha2[1]);
6089
6090 /* ignore non-ISO3166 country codes */
6091 for (i = 0; i < sizeof(req->alpha2); i++)
6092 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6093 brcmf_err("not a ISO3166 code\n");
6094 return;
6095 }
6096 memset(&ccreq, 0, sizeof(ccreq));
6097 ccreq.rev = cpu_to_le32(-1);
6098 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006099 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6100 brcmf_err("firmware rejected country setting\n");
6101 return;
6102 }
6103 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006104}
6105
Arend van Sprielb48d8912014-07-12 08:49:41 +02006106static void brcmf_free_wiphy(struct wiphy *wiphy)
6107{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006108 int i;
6109
Arend van Spriel58de92d2015-04-14 20:10:24 +02006110 if (!wiphy)
6111 return;
6112
Arend van Spriel0882dda2015-08-20 22:06:03 +02006113 if (wiphy->iface_combinations) {
6114 for (i = 0; i < wiphy->n_iface_combinations; i++)
6115 kfree(wiphy->iface_combinations[i].limits);
6116 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006117 kfree(wiphy->iface_combinations);
6118 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6119 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6120 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6121 }
6122 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6123 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6124 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6125 }
6126 wiphy_free(wiphy);
6127}
6128
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006129struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
6130 struct device *busdev)
6131{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006132 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006133 struct brcmf_cfg80211_info *cfg;
6134 struct wiphy *wiphy;
6135 struct brcmf_cfg80211_vif *vif;
6136 struct brcmf_if *ifp;
6137 s32 err = 0;
6138 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006139 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006140
6141 if (!ndev) {
6142 brcmf_err("ndev is invalid\n");
6143 return NULL;
6144 }
6145
6146 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006147 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6148 if (!wiphy) {
6149 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006150 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006151 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006152 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006153 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006154
6155 cfg = wiphy_priv(wiphy);
6156 cfg->wiphy = wiphy;
6157 cfg->pub = drvr;
6158 init_vif_event(&cfg->vif_event);
6159 INIT_LIST_HEAD(&cfg->vif_list);
6160
6161 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006162 if (IS_ERR(vif))
6163 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006164
6165 vif->ifp = ifp;
6166 vif->wdev.netdev = ndev;
6167 ndev->ieee80211_ptr = &vif->wdev;
6168 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6169
6170 err = wl_init_priv(cfg);
6171 if (err) {
6172 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006173 brcmf_free_vif(vif);
6174 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006175 }
6176 ifp->vif = vif;
6177
Arend van Sprielb48d8912014-07-12 08:49:41 +02006178 /* determine d11 io type before wiphy setup */
6179 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006180 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006181 brcmf_err("Failed to get D11 version (%d)\n", err);
6182 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006183 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006184 cfg->d11inf.io_type = (u8)io_type;
6185 brcmu_d11_attach(&cfg->d11inf);
6186
6187 err = brcmf_setup_wiphy(wiphy, ifp);
6188 if (err < 0)
6189 goto priv_out;
6190
6191 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006192 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006193 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6194 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6195
6196 /* firmware defaults to 40MHz disabled in 2G band. We signal
6197 * cfg80211 here that we do and have it decide we can enable
6198 * it. But first check if device does support 2G operation.
6199 */
6200 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6201 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6202 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6203 }
6204 err = wiphy_register(wiphy);
6205 if (err < 0) {
6206 brcmf_err("Could not register wiphy device (%d)\n", err);
6207 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006208 }
6209
6210 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6211 * setup 40MHz in 2GHz band and enable OBSS scanning.
6212 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006213 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6214 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006215 if (!err)
6216 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6217 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006218 else
6219 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006220 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006221
6222 err = brcmf_p2p_attach(cfg);
6223 if (err) {
6224 brcmf_err("P2P initilisation failed (%d)\n", err);
6225 goto wiphy_unreg_out;
6226 }
6227 err = brcmf_btcoex_attach(cfg);
6228 if (err) {
6229 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6230 brcmf_p2p_detach(&cfg->p2p);
6231 goto wiphy_unreg_out;
6232 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006233
6234 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6235 if (err) {
6236 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6237 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman70b7d942014-07-30 13:20:07 +02006238 } else {
6239 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6240 brcmf_notify_tdls_peer_event);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006241 }
6242
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006243 return cfg;
6244
Arend van Sprielb48d8912014-07-12 08:49:41 +02006245wiphy_unreg_out:
6246 wiphy_unregister(cfg->wiphy);
6247priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006248 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006249 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006250 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006251wiphy_out:
6252 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006253 return NULL;
6254}
6255
6256void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6257{
6258 if (!cfg)
6259 return;
6260
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006261 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006262 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006263 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006264 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006265}