blob: c9657be3d4c30ee40545cd97b7748098c062c38b [file] [log] [blame]
Arend van Spriel5b435de2011-10-05 13:19:03 +02001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
18
19#include <linux/kernel.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020020#include <linux/etherdevice.h>
Hante Meuleman68ca3952014-02-25 20:30:26 +010021#include <linux/module.h>
Franky Lin1bacb042014-06-21 12:11:16 +020022#include <linux/vmalloc.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020023#include <net/cfg80211.h>
Arend van Sprielcbaa1772012-08-30 19:43:02 +020024#include <net/netlink.h>
Arend van Spriel5b435de2011-10-05 13:19:03 +020025
26#include <brcmu_utils.h>
27#include <defs.h>
28#include <brcmu_wifi.h>
Hante Meuleman122d3d02014-10-28 14:56:18 +010029#include "core.h"
Hante Meulemana8e8ed32014-10-28 14:56:13 +010030#include "debug.h"
Arend van Spriel40c1c242013-04-05 10:57:44 +020031#include "tracepoint.h"
Hante Meuleman7a5c1f62013-02-08 15:53:44 +010032#include "fwil_types.h"
Arend van Spriel9f440b72013-02-08 15:53:36 +010033#include "p2p.h"
Piotr Haber61730d42013-04-23 12:53:12 +020034#include "btcoex.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010035#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020036#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070037#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020038#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020039#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010040#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010041#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020042
Arend van Spriele5806072012-09-19 22:21:08 +020043#define BRCMF_SCAN_IE_LEN_MAX 2048
44#define BRCMF_PNO_VERSION 2
45#define BRCMF_PNO_TIME 30
46#define BRCMF_PNO_REPEAT 4
47#define BRCMF_PNO_FREQ_EXPO_MAX 3
48#define BRCMF_PNO_MAX_PFN_COUNT 16
49#define BRCMF_PNO_ENABLE_ADAPTSCAN_BIT 6
50#define BRCMF_PNO_HIDDEN_BIT 2
51#define BRCMF_PNO_WPA_AUTH_ANY 0xFFFFFFFF
52#define BRCMF_PNO_SCAN_COMPLETE 1
53#define BRCMF_PNO_SCAN_INCOMPLETE 0
54
Hante Meuleman1a873342012-09-27 14:17:54 +020055#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
56#define WPA_OUI_TYPE 1
57#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
58#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010059#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020060
61#define VS_IE_FIXED_HDR_LEN 6
62#define WPA_IE_VERSION_LEN 2
63#define WPA_IE_MIN_OUI_LEN 4
64#define WPA_IE_SUITE_COUNT_LEN 2
65
66#define WPA_CIPHER_NONE 0 /* None */
67#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
68#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
69#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
70#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
71
72#define RSN_AKM_NONE 0 /* None (IBSS) */
73#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
74#define RSN_AKM_PSK 2 /* Pre-shared Key */
75#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
76#define RSN_CAP_PTK_REPLAY_CNTR_MASK 0x000C
77
78#define VNDR_IE_CMD_LEN 4 /* length of the set command
79 * string :"add", "del" (+ NUL)
80 */
81#define VNDR_IE_COUNT_OFFSET 4
82#define VNDR_IE_PKTFLAG_OFFSET 8
83#define VNDR_IE_VSIE_OFFSET 12
84#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010085#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020086
87#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
88#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020089
Hante Meuleman89286dc2013-02-08 15:53:46 +010090#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
91#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
92#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
93
Hante Meuleman1678ba82015-12-10 13:43:00 +010094#define BRCMF_SCAN_CHANNEL_TIME 40
95#define BRCMF_SCAN_UNASSOC_TIME 40
96#define BRCMF_SCAN_PASSIVE_TIME 120
97
Hante Meuleman3021ad92016-01-05 11:05:45 +010098#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)
99
Arend van Spriel5b435de2011-10-05 13:19:03 +0200100#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
101 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
102
Arend van Sprielce81e312012-10-22 13:55:37 -0700103static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200104{
Arend van Sprielc1179032012-10-22 13:55:33 -0700105 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100106 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
107 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200108 return false;
109 }
110 return true;
111}
112
Arend van Spriel5b435de2011-10-05 13:19:03 +0200113#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
114#define RATETAB_ENT(_rateid, _flags) \
115 { \
116 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
117 .hw_value = (_rateid), \
118 .flags = (_flags), \
119 }
120
121static struct ieee80211_rate __wl_rates[] = {
122 RATETAB_ENT(BRCM_RATE_1M, 0),
123 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
124 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
125 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
126 RATETAB_ENT(BRCM_RATE_6M, 0),
127 RATETAB_ENT(BRCM_RATE_9M, 0),
128 RATETAB_ENT(BRCM_RATE_12M, 0),
129 RATETAB_ENT(BRCM_RATE_18M, 0),
130 RATETAB_ENT(BRCM_RATE_24M, 0),
131 RATETAB_ENT(BRCM_RATE_36M, 0),
132 RATETAB_ENT(BRCM_RATE_48M, 0),
133 RATETAB_ENT(BRCM_RATE_54M, 0),
134};
135
Arend van Spriel5b435de2011-10-05 13:19:03 +0200136#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200137#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
138#define wl_a_rates (__wl_rates + 4)
139#define wl_a_rates_size (wl_g_rates_size - 4)
140
141#define CHAN2G(_channel, _freq) { \
142 .band = IEEE80211_BAND_2GHZ, \
143 .center_freq = (_freq), \
144 .hw_value = (_channel), \
145 .flags = IEEE80211_CHAN_DISABLED, \
146 .max_antenna_gain = 0, \
147 .max_power = 30, \
148}
149
150#define CHAN5G(_channel) { \
151 .band = IEEE80211_BAND_5GHZ, \
152 .center_freq = 5000 + (5 * (_channel)), \
153 .hw_value = (_channel), \
154 .flags = IEEE80211_CHAN_DISABLED, \
155 .max_antenna_gain = 0, \
156 .max_power = 30, \
157}
158
159static struct ieee80211_channel __wl_2ghz_channels[] = {
160 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
161 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
162 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
163 CHAN2G(13, 2472), CHAN2G(14, 2484)
164};
165
166static struct ieee80211_channel __wl_5ghz_channels[] = {
167 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
168 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
169 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
170 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
171 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
172 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
173};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200174
Arend van Sprielb48d8912014-07-12 08:49:41 +0200175/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200176 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200177 */
178static const struct ieee80211_supported_band __wl_band_2ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200179 .band = IEEE80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200180 .bitrates = wl_g_rates,
181 .n_bitrates = wl_g_rates_size,
182};
183
Arend van Spriel58de92d2015-04-14 20:10:24 +0200184static const struct ieee80211_supported_band __wl_band_5ghz = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200185 .band = IEEE80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200186 .bitrates = wl_a_rates,
187 .n_bitrates = wl_a_rates_size,
188};
189
Hante Meulemand48200b2013-04-03 12:40:29 +0200190/* This is to override regulatory domains defined in cfg80211 module (reg.c)
191 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200192 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
193 * With respect to these flags, wpa_supplicant doesn't * start p2p
194 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200195 * domain are to be done here.
196 */
197static const struct ieee80211_regdomain brcmf_regdom = {
198 .n_reg_rules = 4,
199 .alpha2 = "99",
200 .reg_rules = {
201 /* IEEE 802.11b/g, channels 1..11 */
202 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
203 /* If any */
204 /* IEEE 802.11 channel 14 - Only JP enables
205 * this and for 802.11b only
206 */
207 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
208 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200209 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200210 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200211 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200212};
213
214static const u32 __wl_cipher_suites[] = {
215 WLAN_CIPHER_SUITE_WEP40,
216 WLAN_CIPHER_SUITE_WEP104,
217 WLAN_CIPHER_SUITE_TKIP,
218 WLAN_CIPHER_SUITE_CCMP,
219 WLAN_CIPHER_SUITE_AES_CMAC,
220};
221
Hante Meuleman1a873342012-09-27 14:17:54 +0200222/* Vendor specific ie. id = 221, oui and type defines exact ie */
223struct brcmf_vs_tlv {
224 u8 id;
225 u8 len;
226 u8 oui[3];
227 u8 oui_type;
228};
229
230struct parsed_vndr_ie_info {
231 u8 *ie_ptr;
232 u32 ie_len; /* total length including id & length field */
233 struct brcmf_vs_tlv vndrie;
234};
235
236struct parsed_vndr_ies {
237 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100238 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200239};
240
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200241static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
242 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200243{
244 struct brcmu_chan ch_inf;
245 s32 primary_offset;
246
247 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
248 ch->chan->center_freq, ch->center_freq1, ch->width);
249 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
250 primary_offset = ch->center_freq1 - ch->chan->center_freq;
251 switch (ch->width) {
252 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100253 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200254 ch_inf.bw = BRCMU_CHAN_BW_20;
255 WARN_ON(primary_offset != 0);
256 break;
257 case NL80211_CHAN_WIDTH_40:
258 ch_inf.bw = BRCMU_CHAN_BW_40;
259 if (primary_offset < 0)
260 ch_inf.sb = BRCMU_CHAN_SB_U;
261 else
262 ch_inf.sb = BRCMU_CHAN_SB_L;
263 break;
264 case NL80211_CHAN_WIDTH_80:
265 ch_inf.bw = BRCMU_CHAN_BW_80;
266 if (primary_offset < 0) {
267 if (primary_offset < -CH_10MHZ_APART)
268 ch_inf.sb = BRCMU_CHAN_SB_UU;
269 else
270 ch_inf.sb = BRCMU_CHAN_SB_UL;
271 } else {
272 if (primary_offset > CH_10MHZ_APART)
273 ch_inf.sb = BRCMU_CHAN_SB_LL;
274 else
275 ch_inf.sb = BRCMU_CHAN_SB_LU;
276 }
277 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100278 case NL80211_CHAN_WIDTH_80P80:
279 case NL80211_CHAN_WIDTH_160:
280 case NL80211_CHAN_WIDTH_5:
281 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200282 default:
283 WARN_ON_ONCE(1);
284 }
285 switch (ch->chan->band) {
286 case IEEE80211_BAND_2GHZ:
287 ch_inf.band = BRCMU_CHAN_BAND_2G;
288 break;
289 case IEEE80211_BAND_5GHZ:
290 ch_inf.band = BRCMU_CHAN_BAND_5G;
291 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100292 case IEEE80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200293 default:
294 WARN_ON_ONCE(1);
295 }
296 d11inf->encchspec(&ch_inf);
297
298 return ch_inf.chspec;
299}
300
Franky Lin83cf17a2013-04-11 13:28:50 +0200301u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
302 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700303{
Franky Lin83cf17a2013-04-11 13:28:50 +0200304 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700305
Franky Lin83cf17a2013-04-11 13:28:50 +0200306 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
307 ch_inf.bw = BRCMU_CHAN_BW_20;
308 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700309
Franky Lin83cf17a2013-04-11 13:28:50 +0200310 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700311}
312
Hante Meuleman89286dc2013-02-08 15:53:46 +0100313/* Traverse a string of 1-byte tag/1-byte length/variable-length value
314 * triples, returning a pointer to the substring whose first element
315 * matches tag
316 */
Johannes Berg4b5800f2014-01-15 14:55:59 +0100317const struct brcmf_tlv *
318brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100319{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100320 const struct brcmf_tlv *elt = buf;
321 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100322
323 /* find tagged parameter */
324 while (totlen >= TLV_HDR_LEN) {
325 int len = elt->len;
326
327 /* validate remaining totlen */
328 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
329 return elt;
330
331 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
332 totlen -= (len + TLV_HDR_LEN);
333 }
334
335 return NULL;
336}
337
338/* Is any of the tlvs the expected entry? If
339 * not update the tlvs buffer pointer/length.
340 */
341static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100342brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
343 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100344{
345 /* If the contents match the OUI and the type */
346 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
347 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
348 type == ie[TLV_BODY_OFF + oui_len]) {
349 return true;
350 }
351
352 if (tlvs == NULL)
353 return false;
354 /* point to the next ie */
355 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
356 /* calculate the length of the rest of the buffer */
357 *tlvs_len -= (int)(ie - *tlvs);
358 /* update the pointer to the start of the buffer */
359 *tlvs = ie;
360
361 return false;
362}
363
364static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100365brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100366{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100367 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100368
369 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100370 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100371 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
372 return (struct brcmf_vs_tlv *)ie;
373 }
374 return NULL;
375}
376
377static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100378brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100379{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100380 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100381
382 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
383 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
384 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
385 return (struct brcmf_vs_tlv *)ie;
386 }
387 return NULL;
388}
389
Arend van Spriel39504a22015-08-20 22:06:05 +0200390static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
391 struct brcmf_cfg80211_vif *vif,
392 enum nl80211_iftype new_type)
393{
394 int iftype_num[NUM_NL80211_IFTYPES];
395 struct brcmf_cfg80211_vif *pos;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100396 bool check_combos = false;
397 int ret = 0;
Arend van Spriel39504a22015-08-20 22:06:05 +0200398
399 memset(&iftype_num[0], 0, sizeof(iftype_num));
400 list_for_each_entry(pos, &cfg->vif_list, list)
Arend van Spriel353c46a2015-12-10 13:43:06 +0100401 if (pos == vif) {
Arend van Spriel39504a22015-08-20 22:06:05 +0200402 iftype_num[new_type]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100403 } else {
404 /* concurrent interfaces so need check combinations */
405 check_combos = true;
Arend van Spriel39504a22015-08-20 22:06:05 +0200406 iftype_num[pos->wdev.iftype]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100407 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200408
Arend van Spriel353c46a2015-12-10 13:43:06 +0100409 if (check_combos)
410 ret = cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
411
412 return ret;
Arend van Spriel39504a22015-08-20 22:06:05 +0200413}
414
415static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
416 enum nl80211_iftype new_type)
417{
418 int iftype_num[NUM_NL80211_IFTYPES];
419 struct brcmf_cfg80211_vif *pos;
420
421 memset(&iftype_num[0], 0, sizeof(iftype_num));
422 list_for_each_entry(pos, &cfg->vif_list, list)
423 iftype_num[pos->wdev.iftype]++;
424
425 iftype_num[new_type]++;
426 return cfg80211_check_combinations(cfg->wiphy, 1, 0, iftype_num);
427}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100428
Arend van Spriel5b435de2011-10-05 13:19:03 +0200429static void convert_key_from_CPU(struct brcmf_wsec_key *key,
430 struct brcmf_wsec_key_le *key_le)
431{
432 key_le->index = cpu_to_le32(key->index);
433 key_le->len = cpu_to_le32(key->len);
434 key_le->algo = cpu_to_le32(key->algo);
435 key_le->flags = cpu_to_le32(key->flags);
436 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
437 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
438 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
439 memcpy(key_le->data, key->data, sizeof(key->data));
440 memcpy(key_le->ea, key->ea, sizeof(key->ea));
441}
442
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200443static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100444send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200445{
446 int err;
447 struct brcmf_wsec_key_le key_le;
448
449 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200450
Hante Meuleman118eb302014-12-21 12:43:49 +0100451 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700452
Hante Meuleman118eb302014-12-21 12:43:49 +0100453 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700454 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200455
Arend van Spriel5b435de2011-10-05 13:19:03 +0200456 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100457 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200458 return err;
459}
460
Hante Meulemanb3657452013-05-27 21:09:53 +0200461static s32
462brcmf_configure_arp_offload(struct brcmf_if *ifp, bool enable)
463{
464 s32 err;
465 u32 mode;
466
467 if (enable)
468 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
469 else
470 mode = 0;
471
472 /* Try to set and enable ARP offload feature, this may fail, then it */
473 /* is simply not supported and err 0 will be returned */
474 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
475 if (err) {
476 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
477 mode, err);
478 err = 0;
479 } else {
480 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
481 if (err) {
482 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
483 enable, err);
484 err = 0;
485 } else
486 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
487 enable, mode);
488 }
489
490 return err;
491}
492
Hante Meuleman8851cce2014-07-30 13:20:02 +0200493static void
494brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
495{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200496 struct brcmf_cfg80211_vif *vif;
497 struct brcmf_if *ifp;
498
499 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
500 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200501
502 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
503 (wdev->iftype == NL80211_IFTYPE_AP) ||
504 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
505 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
506 ADDR_DIRECT);
507 else
508 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
509 ADDR_INDIRECT);
510}
511
Hante Meulemana44aa402014-12-03 21:05:33 +0100512static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
513{
514 struct brcmf_mbss_ssid_le mbss_ssid_le;
515 int bsscfgidx;
516 int err;
517
518 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
519 bsscfgidx = brcmf_get_next_free_bsscfgidx(ifp->drvr);
520 if (bsscfgidx < 0)
521 return bsscfgidx;
522
523 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
524 mbss_ssid_le.SSID_len = cpu_to_le32(5);
525 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
526
527 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
528 sizeof(mbss_ssid_le));
529 if (err < 0)
530 brcmf_err("setting ssid failed %d\n", err);
531
532 return err;
533}
534
535/**
536 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
537 *
538 * @wiphy: wiphy device of new interface.
539 * @name: name of the new interface.
540 * @flags: not used.
541 * @params: contains mac address for AP device.
542 */
543static
544struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
545 u32 *flags, struct vif_params *params)
546{
547 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
548 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
549 struct brcmf_cfg80211_vif *vif;
550 int err;
551
552 if (brcmf_cfg80211_vif_event_armed(cfg))
553 return ERR_PTR(-EBUSY);
554
555 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
556
557 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP, false);
558 if (IS_ERR(vif))
559 return (struct wireless_dev *)vif;
560
561 brcmf_cfg80211_arm_vif_event(cfg, vif);
562
563 err = brcmf_cfg80211_request_ap_if(ifp);
564 if (err) {
565 brcmf_cfg80211_arm_vif_event(cfg, NULL);
566 goto fail;
567 }
568
569 /* wait for firmware event */
570 err = brcmf_cfg80211_wait_vif_event_timeout(cfg, BRCMF_E_IF_ADD,
571 msecs_to_jiffies(1500));
572 brcmf_cfg80211_arm_vif_event(cfg, NULL);
573 if (!err) {
574 brcmf_err("timeout occurred\n");
575 err = -EIO;
576 goto fail;
577 }
578
579 /* interface created in firmware */
580 ifp = vif->ifp;
581 if (!ifp) {
582 brcmf_err("no if pointer provided\n");
583 err = -ENOENT;
584 goto fail;
585 }
586
587 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
588 err = brcmf_net_attach(ifp, true);
589 if (err) {
590 brcmf_err("Registering netdevice failed\n");
591 goto fail;
592 }
593
594 return &ifp->vif->wdev;
595
596fail:
597 brcmf_free_vif(vif);
598 return ERR_PTR(err);
599}
600
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100601static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
602{
603 enum nl80211_iftype iftype;
604
605 iftype = vif->wdev.iftype;
606 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
607}
608
609static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
610{
611 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
612}
613
Arend van Spriel9f440b72013-02-08 15:53:36 +0100614static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
615 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100616 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100617 enum nl80211_iftype type,
618 u32 *flags,
619 struct vif_params *params)
620{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200621 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200622 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200623
Arend van Spriel9f440b72013-02-08 15:53:36 +0100624 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200625 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
626 if (err) {
627 brcmf_err("iface validation failed: err=%d\n", err);
628 return ERR_PTR(err);
629 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100630 switch (type) {
631 case NL80211_IFTYPE_ADHOC:
632 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100633 case NL80211_IFTYPE_AP_VLAN:
634 case NL80211_IFTYPE_WDS:
635 case NL80211_IFTYPE_MONITOR:
636 case NL80211_IFTYPE_MESH_POINT:
637 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100638 case NL80211_IFTYPE_AP:
639 wdev = brcmf_ap_add_vif(wiphy, name, flags, params);
640 if (!IS_ERR(wdev))
641 brcmf_cfg80211_update_proto_addr_mode(wdev);
642 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100643 case NL80211_IFTYPE_P2P_CLIENT:
644 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200645 case NL80211_IFTYPE_P2P_DEVICE:
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100646 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, flags, params);
Hante Meuleman8851cce2014-07-30 13:20:02 +0200647 if (!IS_ERR(wdev))
648 brcmf_cfg80211_update_proto_addr_mode(wdev);
649 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100650 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100651 default:
652 return ERR_PTR(-EINVAL);
653 }
654}
655
Daniel Kim5e787f72014-06-21 12:11:18 +0200656static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
657{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200658 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200659 brcmf_set_mpc(ifp, mpc);
660}
661
Arend van Sprielf96aa072013-04-05 10:57:48 +0200662void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100663{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100664 s32 err = 0;
665
666 if (check_vif_up(ifp->vif)) {
667 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
668 if (err) {
669 brcmf_err("fail to set mpc\n");
670 return;
671 }
672 brcmf_dbg(INFO, "MPC : %d\n", mpc);
673 }
674}
675
Arend van Spriela0f472a2013-04-05 10:57:49 +0200676s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
677 struct brcmf_if *ifp, bool aborted,
678 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100679{
680 struct brcmf_scan_params_le params_le;
681 struct cfg80211_scan_request *scan_request;
682 s32 err = 0;
683
684 brcmf_dbg(SCAN, "Enter\n");
685
686 /* clear scan request, because the FW abort can cause a second call */
687 /* to this functon and might cause a double cfg80211_scan_done */
688 scan_request = cfg->scan_request;
689 cfg->scan_request = NULL;
690
691 if (timer_pending(&cfg->escan_timeout))
692 del_timer_sync(&cfg->escan_timeout);
693
694 if (fw_abort) {
695 /* Do a scan abort to stop the driver's scan engine */
696 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
697 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800698 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100699 params_le.bss_type = DOT11_BSSTYPE_ANY;
700 params_le.scan_type = 0;
701 params_le.channel_num = cpu_to_le32(1);
702 params_le.nprobes = cpu_to_le32(1);
703 params_le.active_time = cpu_to_le32(-1);
704 params_le.passive_time = cpu_to_le32(-1);
705 params_le.home_time = cpu_to_le32(-1);
706 /* Scan is aborted by setting channel_list[0] to -1 */
707 params_le.channel_list[0] = cpu_to_le16(-1);
708 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200709 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100710 &params_le, sizeof(params_le));
711 if (err)
712 brcmf_err("Scan abort failed\n");
713 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200714
Daniel Kim5e787f72014-06-21 12:11:18 +0200715 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200716
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100717 /*
718 * e-scan can be initiated by scheduled scan
719 * which takes precedence.
720 */
721 if (cfg->sched_escan) {
722 brcmf_dbg(SCAN, "scheduled scan completed\n");
723 cfg->sched_escan = false;
724 if (!aborted)
725 cfg80211_sched_scan_results(cfg_to_wiphy(cfg));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100726 } else if (scan_request) {
727 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
728 aborted ? "Aborted" : "Done");
729 cfg80211_scan_done(scan_request, aborted);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100730 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100731 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
732 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100733
734 return err;
735}
736
Arend van Spriel9f440b72013-02-08 15:53:36 +0100737static
738int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
739{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100740 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
741 struct net_device *ndev = wdev->netdev;
742
743 /* vif event pending in firmware */
744 if (brcmf_cfg80211_vif_event_armed(cfg))
745 return -EBUSY;
746
747 if (ndev) {
748 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200749 cfg->escan_info.ifp == netdev_priv(ndev))
750 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
751 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100752
753 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
754 }
755
Arend van Spriel9f440b72013-02-08 15:53:36 +0100756 switch (wdev->iftype) {
757 case NL80211_IFTYPE_ADHOC:
758 case NL80211_IFTYPE_STATION:
759 case NL80211_IFTYPE_AP:
760 case NL80211_IFTYPE_AP_VLAN:
761 case NL80211_IFTYPE_WDS:
762 case NL80211_IFTYPE_MONITOR:
763 case NL80211_IFTYPE_MESH_POINT:
764 return -EOPNOTSUPP;
765 case NL80211_IFTYPE_P2P_CLIENT:
766 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200767 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100768 return brcmf_p2p_del_vif(wiphy, wdev);
769 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100770 default:
771 return -EINVAL;
772 }
773 return -EOPNOTSUPP;
774}
775
Arend van Spriel5b435de2011-10-05 13:19:03 +0200776static s32
777brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
778 enum nl80211_iftype type, u32 *flags,
779 struct vif_params *params)
780{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100781 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700782 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100783 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200784 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200785 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200786 s32 err = 0;
787
Hante Meuleman37a869e2015-10-29 20:33:17 +0100788 brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
789 type);
Hante Meuleman178e9ef2015-09-18 22:08:11 +0200790
791 /* WAR: There are a number of p2p interface related problems which
792 * need to be handled initially (before doing the validate).
793 * wpa_supplicant tends to do iface changes on p2p device/client/go
794 * which are not always possible/allowed. However we need to return
795 * OK otherwise the wpa_supplicant wont start. The situation differs
796 * on configuration and setup (p2pon=1 module param). The first check
797 * is to see if the request is a change to station for p2p iface.
798 */
799 if ((type == NL80211_IFTYPE_STATION) &&
800 ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
801 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
802 (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
803 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
804 /* Now depending on whether module param p2pon=1 was used the
805 * response needs to be either 0 or EOPNOTSUPP. The reason is
806 * that if p2pon=1 is used, but a newer supplicant is used then
807 * we should return an error, as this combination wont work.
808 * In other situations 0 is returned and supplicant will start
809 * normally. It will give a trace in cfg80211, but it is the
810 * only way to get it working. Unfortunately this will result
811 * in situation where we wont support new supplicant in
812 * combination with module param p2pon=1, but that is the way
813 * it is. If the user tries this then unloading of driver might
814 * fail/lock.
815 */
816 if (cfg->p2p.p2pdev_dynamically)
817 return -EOPNOTSUPP;
818 else
819 return 0;
820 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200821 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
822 if (err) {
823 brcmf_err("iface validation failed: err=%d\n", err);
824 return err;
825 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200826 switch (type) {
827 case NL80211_IFTYPE_MONITOR:
828 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100829 brcmf_err("type (%d) : currently we do not support this type\n",
830 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200831 return -EOPNOTSUPP;
832 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200833 infra = 0;
834 break;
835 case NL80211_IFTYPE_STATION:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200836 infra = 1;
837 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200838 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100839 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200840 ap = 1;
841 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200842 default:
843 err = -EINVAL;
844 goto done;
845 }
846
Hante Meuleman1a873342012-09-27 14:17:54 +0200847 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100848 if (type == NL80211_IFTYPE_P2P_GO) {
849 brcmf_dbg(INFO, "IF Type = P2P GO\n");
850 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
851 }
852 if (!err) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100853 brcmf_dbg(INFO, "IF Type = AP\n");
854 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200855 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100856 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200857 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100858 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200859 err = -EAGAIN;
860 goto done;
861 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100862 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100863 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200864 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200865 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200866
Hante Meuleman8851cce2014-07-30 13:20:02 +0200867 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
868
Arend van Spriel5b435de2011-10-05 13:19:03 +0200869done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100870 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200871
872 return err;
873}
874
Franky Lin83cf17a2013-04-11 13:28:50 +0200875static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
876 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200877 struct cfg80211_scan_request *request)
878{
879 u32 n_ssids;
880 u32 n_channels;
881 s32 i;
882 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200883 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200884 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200885 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200886
Joe Perches93803b32015-03-02 19:54:49 -0800887 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200888 params_le->bss_type = DOT11_BSSTYPE_ANY;
889 params_le->scan_type = 0;
890 params_le->channel_num = 0;
891 params_le->nprobes = cpu_to_le32(-1);
892 params_le->active_time = cpu_to_le32(-1);
893 params_le->passive_time = cpu_to_le32(-1);
894 params_le->home_time = cpu_to_le32(-1);
895 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
896
897 /* if request is null exit so it will be all channel broadcast scan */
898 if (!request)
899 return;
900
901 n_ssids = request->n_ssids;
902 n_channels = request->n_channels;
903 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100904 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
905 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +0200906 if (n_channels > 0) {
907 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +0200908 chanspec = channel_to_chanspec(&cfg->d11inf,
909 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100910 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
911 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +0200912 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +0200913 }
914 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100915 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200916 }
917 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100918 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200919 if (n_ssids > 0) {
920 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
921 n_channels * sizeof(u16);
922 offset = roundup(offset, sizeof(u32));
923 ptr = (char *)params_le + offset;
924 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +0200925 memset(&ssid_le, 0, sizeof(ssid_le));
926 ssid_le.SSID_len =
927 cpu_to_le32(request->ssids[i].ssid_len);
928 memcpy(ssid_le.SSID, request->ssids[i].ssid,
929 request->ssids[i].ssid_len);
930 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100931 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +0200932 else
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100933 brcmf_dbg(SCAN, "%d: scan for %s size =%d\n",
934 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +0200935 memcpy(ptr, &ssid_le, sizeof(ssid_le));
936 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +0200937 }
938 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100939 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +0200940 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100941 brcmf_dbg(SCAN, "SSID %s len=%d\n",
942 params_le->ssid_le.SSID,
943 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +0200944 params_le->ssid_le.SSID_len =
945 cpu_to_le32(request->ssids->ssid_len);
946 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
947 request->ssids->ssid_len);
948 }
949 }
950 /* Adding mask to channel numbers */
951 params_le->channel_num =
952 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
953 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
954}
955
956static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +0200957brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemanc4958102015-11-25 11:32:41 +0100958 struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +0200959{
960 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
961 offsetof(struct brcmf_escan_params_le, params_le);
962 struct brcmf_escan_params_le *params;
963 s32 err = 0;
964
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100965 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200966
967 if (request != NULL) {
968 /* Allocate space for populating ssids in struct */
969 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
970
971 /* Allocate space for populating ssids in struct */
Hante Meulemane9a6ca82015-11-25 11:32:37 +0100972 params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
Hante Meulemane756af52012-09-11 21:18:52 +0200973 }
974
975 params = kzalloc(params_size, GFP_KERNEL);
976 if (!params) {
977 err = -ENOMEM;
978 goto exit;
979 }
980 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +0200981 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +0200982 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
Hante Meulemanc4958102015-11-25 11:32:41 +0100983 params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +0200984 params->sync_id = cpu_to_le16(0x1234);
985
Arend van Spriela0f472a2013-04-05 10:57:49 +0200986 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +0200987 if (err) {
988 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100989 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +0200990 else
Arend van Spriel57d6e912012-12-05 15:26:00 +0100991 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +0200992 }
993
994 kfree(params);
995exit:
996 return err;
997}
998
999static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001000brcmf_do_escan(struct brcmf_cfg80211_info *cfg, struct wiphy *wiphy,
Arend van Spriela0f472a2013-04-05 10:57:49 +02001001 struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001002{
1003 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001004 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001005 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001006 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001007
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001008 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001009 escan->ifp = ifp;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001010 escan->wiphy = wiphy;
1011 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001012 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001013 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001014 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001015 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001016 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001017 return err;
1018 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001019 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001020 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001021 results->version = 0;
1022 results->count = 0;
1023 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1024
Hante Meulemanc4958102015-11-25 11:32:41 +01001025 err = escan->run(cfg, ifp, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001026 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001027 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001028 return err;
1029}
1030
1031static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001032brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001033 struct cfg80211_scan_request *request,
1034 struct cfg80211_ssid *this_ssid)
1035{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001036 struct brcmf_if *ifp = vif->ifp;
1037 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001038 struct cfg80211_ssid *ssids;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001039 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001040 bool escan_req;
1041 bool spec_scan;
1042 s32 err;
Hante Meuleman675f5d82015-12-10 13:43:01 +01001043 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +02001044 u32 SSID_len;
1045
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001046 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001047
Arend van Sprielc1179032012-10-22 13:55:33 -07001048 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001049 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001050 return -EAGAIN;
1051 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001052 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001053 brcmf_err("Scanning being aborted: status (%lu)\n",
1054 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001055 return -EAGAIN;
1056 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001057 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1058 brcmf_err("Scanning suppressed: status (%lu)\n",
1059 cfg->scan_status);
1060 return -EAGAIN;
1061 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001062 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001063 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001064 return -EAGAIN;
1065 }
1066
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001067 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001068 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1069 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001070
Hante Meulemane756af52012-09-11 21:18:52 +02001071 escan_req = false;
1072 if (request) {
1073 /* scan bss */
1074 ssids = request->ssids;
1075 escan_req = true;
1076 } else {
1077 /* scan in ibss */
1078 /* we don't do escan in ibss */
1079 ssids = this_ssid;
1080 }
1081
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001082 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001083 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001084 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001085 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001086 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001087 if (err)
1088 goto scan_out;
1089
Arend van Spriela0f472a2013-04-05 10:57:49 +02001090 err = brcmf_do_escan(cfg, wiphy, vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001091 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001092 goto scan_out;
1093 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001094 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1095 ssids->ssid, ssids->ssid_len);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001096 memset(&ssid_le, 0, sizeof(ssid_le));
1097 SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
1098 ssid_le.SSID_len = cpu_to_le32(0);
Hante Meulemane756af52012-09-11 21:18:52 +02001099 spec_scan = false;
1100 if (SSID_len) {
Hante Meuleman675f5d82015-12-10 13:43:01 +01001101 memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
1102 ssid_le.SSID_len = cpu_to_le32(SSID_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001103 spec_scan = true;
1104 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001105 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001106
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001107 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001108 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001109 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001110 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001111 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001112 goto scan_out;
1113 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001114 brcmf_scan_config_mpc(ifp, 0);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001115 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
1116 sizeof(ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001117 if (err) {
1118 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001119 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
Hante Meuleman675f5d82015-12-10 13:43:01 +01001120 ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001121 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001122 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001123
Daniel Kim5e787f72014-06-21 12:11:18 +02001124 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001125 goto scan_out;
1126 }
1127 }
1128
Hante Meuleman661fa952015-02-06 18:36:47 +01001129 /* Arm scan timeout timer */
1130 mod_timer(&cfg->escan_timeout, jiffies +
1131 WL_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
1132
Hante Meulemane756af52012-09-11 21:18:52 +02001133 return 0;
1134
1135scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001136 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001137 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001138 return err;
1139}
1140
Arend van Spriel5b435de2011-10-05 13:19:03 +02001141static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001142brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001143{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001144 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001145 s32 err = 0;
1146
Arend van Sprield96b8012012-12-05 15:26:02 +01001147 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001148 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1149 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001150 return -EIO;
1151
Arend van Spriela0f472a2013-04-05 10:57:49 +02001152 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001153
Arend van Spriel5b435de2011-10-05 13:19:03 +02001154 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001155 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001156
Arend van Sprield96b8012012-12-05 15:26:02 +01001157 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001158 return err;
1159}
1160
1161static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1162{
1163 s32 err = 0;
1164
Arend van Sprielac24be62012-10-22 10:36:23 -07001165 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1166 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001167 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001168 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001169
1170 return err;
1171}
1172
1173static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1174{
1175 s32 err = 0;
1176
Arend van Sprielac24be62012-10-22 10:36:23 -07001177 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1178 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001179 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001180 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001181
1182 return err;
1183}
1184
1185static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1186{
1187 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001188 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001189
Arend van Sprielac24be62012-10-22 10:36:23 -07001190 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001191 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001192 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001193 return err;
1194 }
1195 return err;
1196}
1197
1198static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1199{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001200 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1201 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001202 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001203 s32 err = 0;
1204
Arend van Sprield96b8012012-12-05 15:26:02 +01001205 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001206 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001207 return -EIO;
1208
1209 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001210 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1211 cfg->conf->rts_threshold = wiphy->rts_threshold;
1212 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001213 if (!err)
1214 goto done;
1215 }
1216 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001217 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1218 cfg->conf->frag_threshold = wiphy->frag_threshold;
1219 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001220 if (!err)
1221 goto done;
1222 }
1223 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001224 && (cfg->conf->retry_long != wiphy->retry_long)) {
1225 cfg->conf->retry_long = wiphy->retry_long;
1226 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001227 if (!err)
1228 goto done;
1229 }
1230 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001231 && (cfg->conf->retry_short != wiphy->retry_short)) {
1232 cfg->conf->retry_short = wiphy->retry_short;
1233 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001234 if (!err)
1235 goto done;
1236 }
1237
1238done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001239 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001240 return err;
1241}
1242
Arend van Spriel5b435de2011-10-05 13:19:03 +02001243static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1244{
1245 memset(prof, 0, sizeof(*prof));
1246}
1247
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001248static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1249{
1250 u16 reason;
1251
1252 switch (e->event_code) {
1253 case BRCMF_E_DEAUTH:
1254 case BRCMF_E_DEAUTH_IND:
1255 case BRCMF_E_DISASSOC_IND:
1256 reason = e->reason;
1257 break;
1258 case BRCMF_E_LINK:
1259 default:
1260 reason = 0;
1261 break;
1262 }
1263 return reason;
1264}
1265
1266static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001267{
Piotr Haber61730d42013-04-23 12:53:12 +02001268 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001269 s32 err = 0;
1270
Arend van Sprield96b8012012-12-05 15:26:02 +01001271 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001272
Hante Meulemanb0a79082015-12-10 13:43:07 +01001273 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001274 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001275 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001276 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001277 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001278 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001279 }
Hante Meulemanb0a79082015-12-10 13:43:07 +01001280 if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1281 (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
1282 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
1283 true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001284 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001285 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001286 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1287 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001288 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001289}
1290
1291static s32
1292brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1293 struct cfg80211_ibss_params *params)
1294{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001295 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001296 struct brcmf_if *ifp = netdev_priv(ndev);
1297 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001298 struct brcmf_join_params join_params;
1299 size_t join_params_size = 0;
1300 s32 err = 0;
1301 s32 wsec = 0;
1302 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001303 u16 chanspec;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001304 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001305
Arend van Sprield96b8012012-12-05 15:26:02 +01001306 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001307 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001308 return -EIO;
1309
1310 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001311 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001312 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001313 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001314 return -EOPNOTSUPP;
1315 }
1316
Arend van Sprielc1179032012-10-22 13:55:33 -07001317 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001318
1319 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001320 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321 else
Arend van Spriel16886732012-12-05 15:26:04 +01001322 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001323
Johannes Berg683b6d32012-11-08 21:25:48 +01001324 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001325 brcmf_dbg(CONN, "channel: %d\n",
1326 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001327 else
Arend van Spriel16886732012-12-05 15:26:04 +01001328 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001329
1330 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001331 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001332 else
Arend van Spriel16886732012-12-05 15:26:04 +01001333 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334
1335 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001336 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337 else
Arend van Spriel16886732012-12-05 15:26:04 +01001338 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001339
1340 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001341 brcmf_dbg(CONN, "beacon interval: %d\n",
1342 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001343 else
Arend van Spriel16886732012-12-05 15:26:04 +01001344 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001345
1346 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001347 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001348 else
Arend van Spriel16886732012-12-05 15:26:04 +01001349 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001350
1351 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001352 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001353 else
Arend van Spriel16886732012-12-05 15:26:04 +01001354 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001355
1356 /* Configure Privacy for starter */
1357 if (params->privacy)
1358 wsec |= WEP_ENABLED;
1359
Arend van Sprielc1179032012-10-22 13:55:33 -07001360 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001361 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001362 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001363 goto done;
1364 }
1365
1366 /* Configure Beacon Interval for starter */
1367 if (params->beacon_interval)
1368 bcnprd = params->beacon_interval;
1369 else
1370 bcnprd = 100;
1371
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001372 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001373 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001374 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001375 goto done;
1376 }
1377
1378 /* Configure required join parameter */
1379 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1380
1381 /* SSID */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001382 ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1383 memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1384 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001385 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001386
1387 /* BSSID */
1388 if (params->bssid) {
1389 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001390 join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001391 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001392 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001393 eth_broadcast_addr(join_params.params_le.bssid);
1394 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001395 }
1396
Arend van Spriel5b435de2011-10-05 13:19:03 +02001397 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001398 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001399 u32 target_channel;
1400
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001401 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001402 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001403 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001404 if (params->channel_fixed) {
1405 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001406 chanspec = chandef_to_chanspec(&cfg->d11inf,
1407 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001408 join_params.params_le.chanspec_list[0] =
1409 cpu_to_le16(chanspec);
1410 join_params.params_le.chanspec_num = cpu_to_le32(1);
1411 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001412 }
1413
1414 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001415 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001416 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001417 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001418 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001419 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001420 goto done;
1421 }
1422 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001423 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001424
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001425 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001426
1427
Arend van Sprielc1179032012-10-22 13:55:33 -07001428 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001429 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001430 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001431 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001432 goto done;
1433 }
1434
1435done:
1436 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001437 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001438 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001439 return err;
1440}
1441
1442static s32
1443brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1444{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001445 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001446
Arend van Sprield96b8012012-12-05 15:26:02 +01001447 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman6a98d642016-01-02 09:41:40 +01001448 if (!check_vif_up(ifp->vif)) {
1449 /* When driver is being unloaded, it can end up here. If an
1450 * error is returned then later on a debug trace in the wireless
1451 * core module will be printed. To avoid this 0 is returned.
1452 */
1453 return 0;
1454 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001455
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001456 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001457
Arend van Sprield96b8012012-12-05 15:26:02 +01001458 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001459
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001460 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001461}
1462
1463static s32 brcmf_set_wpa_version(struct net_device *ndev,
1464 struct cfg80211_connect_params *sme)
1465{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001466 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001467 struct brcmf_cfg80211_security *sec;
1468 s32 val = 0;
1469 s32 err = 0;
1470
1471 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1472 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1473 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1474 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1475 else
1476 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001477 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001478 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001479 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001480 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001481 return err;
1482 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001483 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001484 sec->wpa_versions = sme->crypto.wpa_versions;
1485 return err;
1486}
1487
1488static s32 brcmf_set_auth_type(struct net_device *ndev,
1489 struct cfg80211_connect_params *sme)
1490{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001491 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001492 struct brcmf_cfg80211_security *sec;
1493 s32 val = 0;
1494 s32 err = 0;
1495
1496 switch (sme->auth_type) {
1497 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1498 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001499 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001500 break;
1501 case NL80211_AUTHTYPE_SHARED_KEY:
1502 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001503 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001504 break;
1505 case NL80211_AUTHTYPE_AUTOMATIC:
1506 val = 2;
Arend van Spriel16886732012-12-05 15:26:04 +01001507 brcmf_dbg(CONN, "automatic\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001508 break;
1509 case NL80211_AUTHTYPE_NETWORK_EAP:
Arend van Spriel16886732012-12-05 15:26:04 +01001510 brcmf_dbg(CONN, "network eap\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001511 default:
1512 val = 2;
Arend van Spriel57d6e912012-12-05 15:26:00 +01001513 brcmf_err("invalid auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001514 break;
1515 }
1516
Hante Meuleman89286dc2013-02-08 15:53:46 +01001517 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001518 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001519 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001520 return err;
1521 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001522 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001523 sec->auth_type = sme->auth_type;
1524 return err;
1525}
1526
1527static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001528brcmf_set_wsec_mode(struct net_device *ndev,
1529 struct cfg80211_connect_params *sme, bool mfp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001530{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001531 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001532 struct brcmf_cfg80211_security *sec;
1533 s32 pval = 0;
1534 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001535 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001536 s32 err = 0;
1537
1538 if (sme->crypto.n_ciphers_pairwise) {
1539 switch (sme->crypto.ciphers_pairwise[0]) {
1540 case WLAN_CIPHER_SUITE_WEP40:
1541 case WLAN_CIPHER_SUITE_WEP104:
1542 pval = WEP_ENABLED;
1543 break;
1544 case WLAN_CIPHER_SUITE_TKIP:
1545 pval = TKIP_ENABLED;
1546 break;
1547 case WLAN_CIPHER_SUITE_CCMP:
1548 pval = AES_ENABLED;
1549 break;
1550 case WLAN_CIPHER_SUITE_AES_CMAC:
1551 pval = AES_ENABLED;
1552 break;
1553 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001554 brcmf_err("invalid cipher pairwise (%d)\n",
1555 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001556 return -EINVAL;
1557 }
1558 }
1559 if (sme->crypto.cipher_group) {
1560 switch (sme->crypto.cipher_group) {
1561 case WLAN_CIPHER_SUITE_WEP40:
1562 case WLAN_CIPHER_SUITE_WEP104:
1563 gval = WEP_ENABLED;
1564 break;
1565 case WLAN_CIPHER_SUITE_TKIP:
1566 gval = TKIP_ENABLED;
1567 break;
1568 case WLAN_CIPHER_SUITE_CCMP:
1569 gval = AES_ENABLED;
1570 break;
1571 case WLAN_CIPHER_SUITE_AES_CMAC:
1572 gval = AES_ENABLED;
1573 break;
1574 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001575 brcmf_err("invalid cipher group (%d)\n",
1576 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001577 return -EINVAL;
1578 }
1579 }
1580
Arend van Spriel16886732012-12-05 15:26:04 +01001581 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001582 /* In case of privacy, but no security and WPS then simulate */
1583 /* setting AES. WPS-2.0 allows no security */
1584 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1585 sme->privacy)
1586 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001587
1588 if (mfp)
1589 wsec = pval | gval | MFP_CAPABLE;
1590 else
1591 wsec = pval | gval;
1592 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001593 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001594 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001595 return err;
1596 }
1597
Arend van Spriel06bb1232012-09-27 14:17:56 +02001598 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001599 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1600 sec->cipher_group = sme->crypto.cipher_group;
1601
1602 return err;
1603}
1604
1605static s32
1606brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1607{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001608 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001609 struct brcmf_cfg80211_security *sec;
1610 s32 val = 0;
1611 s32 err = 0;
1612
1613 if (sme->crypto.n_akm_suites) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01001614 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
1615 "wpa_auth", &val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001616 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001617 brcmf_err("could not get wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001618 return err;
1619 }
1620 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1621 switch (sme->crypto.akm_suites[0]) {
1622 case WLAN_AKM_SUITE_8021X:
1623 val = WPA_AUTH_UNSPECIFIED;
1624 break;
1625 case WLAN_AKM_SUITE_PSK:
1626 val = WPA_AUTH_PSK;
1627 break;
1628 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001629 brcmf_err("invalid cipher group (%d)\n",
1630 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001631 return -EINVAL;
1632 }
1633 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1634 switch (sme->crypto.akm_suites[0]) {
1635 case WLAN_AKM_SUITE_8021X:
1636 val = WPA2_AUTH_UNSPECIFIED;
1637 break;
1638 case WLAN_AKM_SUITE_PSK:
1639 val = WPA2_AUTH_PSK;
1640 break;
1641 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001642 brcmf_err("invalid cipher group (%d)\n",
1643 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001644 return -EINVAL;
1645 }
1646 }
1647
Arend van Spriel16886732012-12-05 15:26:04 +01001648 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001649 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev),
1650 "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001651 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001652 brcmf_err("could not set wpa_auth (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001653 return err;
1654 }
1655 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001656 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001657 sec->wpa_auth = sme->crypto.akm_suites[0];
1658
1659 return err;
1660}
1661
1662static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001663brcmf_set_sharedkey(struct net_device *ndev,
1664 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001665{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001666 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001667 struct brcmf_cfg80211_security *sec;
1668 struct brcmf_wsec_key key;
1669 s32 val;
1670 s32 err = 0;
1671
Arend van Spriel16886732012-12-05 15:26:04 +01001672 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001673
Roland Vossena718e2f2011-10-12 20:51:24 +02001674 if (sme->key_len == 0)
1675 return 0;
1676
Arend van Spriel06bb1232012-09-27 14:17:56 +02001677 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001678 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1679 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001680
1681 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1682 return 0;
1683
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001684 if (!(sec->cipher_pairwise &
1685 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1686 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001687
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001688 memset(&key, 0, sizeof(key));
1689 key.len = (u32) sme->key_len;
1690 key.index = (u32) sme->key_idx;
1691 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001692 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001693 return -EINVAL;
1694 }
1695 memcpy(key.data, sme->key, key.len);
1696 key.flags = BRCMF_PRIMARY_KEY;
1697 switch (sec->cipher_pairwise) {
1698 case WLAN_CIPHER_SUITE_WEP40:
1699 key.algo = CRYPTO_ALGO_WEP1;
1700 break;
1701 case WLAN_CIPHER_SUITE_WEP104:
1702 key.algo = CRYPTO_ALGO_WEP128;
1703 break;
1704 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001705 brcmf_err("Invalid algorithm (%d)\n",
1706 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001707 return -EINVAL;
1708 }
1709 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001710 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1711 key.len, key.index, key.algo);
1712 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001713 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001714 if (err)
1715 return err;
1716
1717 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001718 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001719 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001720 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001721 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001722 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001723 }
1724 return err;
1725}
1726
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001727static
1728enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1729 enum nl80211_auth_type type)
1730{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001731 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1732 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1733 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1734 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001735 }
1736 return type;
1737}
1738
Arend van Spriel5b435de2011-10-05 13:19:03 +02001739static s32
1740brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001741 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001742{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001743 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001744 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001745 struct ieee80211_channel *chan = sme->channel;
1746 struct brcmf_join_params join_params;
1747 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001748 const struct brcmf_tlv *rsn_ie;
1749 const struct brcmf_vs_tlv *wpa_ie;
1750 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001751 u32 ie_len;
1752 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001753 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001754 s32 err = 0;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001755 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001756
Arend van Sprield96b8012012-12-05 15:26:02 +01001757 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001758 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001759 return -EIO;
1760
1761 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001762 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001763 return -EOPNOTSUPP;
1764 }
1765
Hante Meuleman89286dc2013-02-08 15:53:46 +01001766 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1767 /* A normal (non P2P) connection request setup. */
1768 ie = NULL;
1769 ie_len = 0;
1770 /* find the WPA_IE */
1771 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1772 if (wpa_ie) {
1773 ie = wpa_ie;
1774 ie_len = wpa_ie->len + TLV_HDR_LEN;
1775 } else {
1776 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001777 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1778 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001779 WLAN_EID_RSN);
1780 if (rsn_ie) {
1781 ie = rsn_ie;
1782 ie_len = rsn_ie->len + TLV_HDR_LEN;
1783 }
1784 }
1785 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1786 }
1787
1788 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1789 sme->ie, sme->ie_len);
1790 if (err)
1791 brcmf_err("Set Assoc REQ IE Failed\n");
1792 else
1793 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1794
Arend van Sprielc1179032012-10-22 13:55:33 -07001795 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001796
1797 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001798 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001799 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001800 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001801 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1802 cfg->channel, chan->center_freq, chanspec);
1803 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001804 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001805 chanspec = 0;
1806 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001807
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001808 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001809
1810 err = brcmf_set_wpa_version(ndev, sme);
1811 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001812 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001813 goto done;
1814 }
1815
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001816 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001817 err = brcmf_set_auth_type(ndev, sme);
1818 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001819 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001820 goto done;
1821 }
1822
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001823 err = brcmf_set_wsec_mode(ndev, sme, sme->mfp == NL80211_MFP_REQUIRED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001824 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001825 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001826 goto done;
1827 }
1828
1829 err = brcmf_set_key_mgmt(ndev, sme);
1830 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001831 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001832 goto done;
1833 }
1834
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001835 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001836 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001837 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001838 goto done;
1839 }
1840
Hante Meuleman89286dc2013-02-08 15:53:46 +01001841 /* Join with specific BSSID and cached SSID
1842 * If SSID is zero join based on BSSID only
1843 */
1844 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
1845 offsetof(struct brcmf_assoc_params_le, chanspec_list);
1846 if (cfg->channel)
1847 join_params_size += sizeof(u16);
1848 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
1849 if (ext_join_params == NULL) {
1850 err = -ENOMEM;
1851 goto done;
1852 }
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001853 ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
1854 ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
1855 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
1856 if (ssid_len < IEEE80211_MAX_SSID_LEN)
1857 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
1858 ext_join_params->ssid_le.SSID, ssid_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001859
Hante Meuleman89286dc2013-02-08 15:53:46 +01001860 /* Set up join scan parameters */
1861 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001862 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
1863
1864 if (sme->bssid)
1865 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
1866 else
Joe Perches93803b32015-03-02 19:54:49 -08001867 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001868
1869 if (cfg->channel) {
1870 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
1871
1872 ext_join_params->assoc_le.chanspec_list[0] =
1873 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01001874 /* Increase dwell time to receive probe response or detect
1875 * beacon from target AP at a noisy air only during connect
1876 * command.
1877 */
1878 ext_join_params->scan_le.active_time =
1879 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
1880 ext_join_params->scan_le.passive_time =
1881 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
1882 /* To sync with presence period of VSDB GO send probe request
1883 * more frequently. Probe request will be stopped when it gets
1884 * probe response from target AP/GO.
1885 */
1886 ext_join_params->scan_le.nprobes =
1887 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
1888 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
1889 } else {
1890 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
1891 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
1892 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001893 }
1894
1895 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
1896 join_params_size);
1897 kfree(ext_join_params);
1898 if (!err)
1899 /* This is it. join command worked, we are done */
1900 goto done;
1901
1902 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02001903 memset(&join_params, 0, sizeof(join_params));
1904 join_params_size = sizeof(join_params.ssid_le);
1905
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001906 memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
1907 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001908
Hante Meuleman89286dc2013-02-08 15:53:46 +01001909 if (sme->bssid)
1910 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
1911 else
Joe Perches93803b32015-03-02 19:54:49 -08001912 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001913
Hante Meuleman17012612013-02-06 18:40:44 +01001914 if (cfg->channel) {
1915 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
1916 join_params.params_le.chanspec_num = cpu_to_le32(1);
1917 join_params_size += sizeof(join_params.params_le);
1918 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001919 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001920 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001921 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01001922 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001923
1924done:
1925 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001926 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001927 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001928 return err;
1929}
1930
1931static s32
1932brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
1933 u16 reason_code)
1934{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001935 struct brcmf_if *ifp = netdev_priv(ndev);
1936 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001937 struct brcmf_scb_val_le scbval;
1938 s32 err = 0;
1939
Arend van Sprield96b8012012-12-05 15:26:02 +01001940 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07001941 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001942 return -EIO;
1943
Arend van Sprielc1179032012-10-22 13:55:33 -07001944 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01001945 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02001946 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001947
Arend van Spriel06bb1232012-09-27 14:17:56 +02001948 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001949 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07001950 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07001951 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02001952 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001953 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001954
Arend van Sprield96b8012012-12-05 15:26:02 +01001955 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001956 return err;
1957}
1958
1959static s32
Johannes Bergc8442112012-10-24 10:17:18 +02001960brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05001961 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001962{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001963 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001964 struct net_device *ndev = cfg_to_ndev(cfg);
1965 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001966 s32 err;
1967 s32 disable;
1968 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001969
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001970 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07001971 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001972 return -EIO;
1973
1974 switch (type) {
1975 case NL80211_TX_POWER_AUTOMATIC:
1976 break;
1977 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02001978 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001979 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001980 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001981 err = -EINVAL;
1982 goto done;
1983 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001984 qdbm = MBM_TO_DBM(4 * mbm);
1985 if (qdbm > 127)
1986 qdbm = 127;
1987 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001988 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02001989 default:
1990 brcmf_err("Unsupported type %d\n", type);
1991 err = -EINVAL;
1992 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001993 }
1994 /* Make sure radio is off or on as far as software is concerned */
1995 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07001996 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001997 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001998 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001999
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002000 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002001 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002002 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002003
2004done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002005 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002006 return err;
2007}
2008
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002009static s32
2010brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2011 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002012{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002013 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002014 struct net_device *ndev = cfg_to_ndev(cfg);
2015 struct brcmf_if *ifp = netdev_priv(ndev);
2016 s32 qdbm = 0;
2017 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002018
Arend van Sprield96b8012012-12-05 15:26:02 +01002019 brcmf_dbg(TRACE, "Enter\n");
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
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002023 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002024 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002025 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002026 goto done;
2027 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002028 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002029
2030done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002031 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002032 return err;
2033}
2034
2035static s32
2036brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002037 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002038{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002039 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002040 u32 index;
2041 u32 wsec;
2042 s32 err = 0;
2043
Arend van Sprield96b8012012-12-05 15:26:02 +01002044 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002045 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002046 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002047 return -EIO;
2048
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002049 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002050 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002051 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002052 goto done;
2053 }
2054
2055 if (wsec & WEP_ENABLED) {
2056 /* Just select a new current key */
2057 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002058 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002059 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002060 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002061 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002062 }
2063done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002064 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002065 return err;
2066}
2067
2068static s32
2069brcmf_add_keyext(struct wiphy *wiphy, struct net_device *ndev,
2070 u8 key_idx, const u8 *mac_addr, struct key_params *params)
2071{
Hante Meuleman992f6062013-04-02 21:06:17 +02002072 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002073 struct brcmf_wsec_key key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002074 s32 err = 0;
Hante Meuleman992f6062013-04-02 21:06:17 +02002075 u8 keybuf[8];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002076
2077 memset(&key, 0, sizeof(key));
2078 key.index = (u32) key_idx;
2079 /* Instead of bcast for ea address for default wep keys,
2080 driver needs it to be Null */
2081 if (!is_multicast_ether_addr(mac_addr))
2082 memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
2083 key.len = (u32) params->key_len;
2084 /* check for key index change */
2085 if (key.len == 0) {
2086 /* key delete */
Hante Meuleman118eb302014-12-21 12:43:49 +01002087 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002088 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002089 brcmf_err("key delete error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002090 } else {
2091 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002092 brcmf_err("Invalid key length (%d)\n", key.len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002093 return -EINVAL;
2094 }
2095
Arend van Spriel16886732012-12-05 15:26:04 +01002096 brcmf_dbg(CONN, "Setting the key index %d\n", key.index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002097 memcpy(key.data, params->key, key.len);
2098
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002099 if (!brcmf_is_apmode(ifp->vif) &&
Hante Meuleman992f6062013-04-02 21:06:17 +02002100 (params->cipher == WLAN_CIPHER_SUITE_TKIP)) {
2101 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002102 memcpy(keybuf, &key.data[24], sizeof(keybuf));
2103 memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
2104 memcpy(&key.data[16], keybuf, sizeof(keybuf));
2105 }
2106
2107 /* if IW_ENCODE_EXT_RX_SEQ_VALID set */
2108 if (params->seq && params->seq_len == 6) {
2109 /* rx iv */
2110 u8 *ivptr;
2111 ivptr = (u8 *) params->seq;
2112 key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
2113 (ivptr[3] << 8) | ivptr[2];
2114 key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
2115 key.iv_initialized = true;
2116 }
2117
2118 switch (params->cipher) {
2119 case WLAN_CIPHER_SUITE_WEP40:
2120 key.algo = CRYPTO_ALGO_WEP1;
Arend van Spriel16886732012-12-05 15:26:04 +01002121 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002122 break;
2123 case WLAN_CIPHER_SUITE_WEP104:
2124 key.algo = CRYPTO_ALGO_WEP128;
Arend van Spriel16886732012-12-05 15:26:04 +01002125 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002126 break;
2127 case WLAN_CIPHER_SUITE_TKIP:
2128 key.algo = CRYPTO_ALGO_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002129 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002130 break;
2131 case WLAN_CIPHER_SUITE_AES_CMAC:
2132 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002133 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002134 break;
2135 case WLAN_CIPHER_SUITE_CCMP:
2136 key.algo = CRYPTO_ALGO_AES_CCM;
Arend van Spriel16886732012-12-05 15:26:04 +01002137 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002138 break;
2139 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002140 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002141 return -EINVAL;
2142 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002143 err = send_key_to_dongle(ifp, &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002144 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002145 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002146 }
2147 return err;
2148}
2149
2150static s32
2151brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2152 u8 key_idx, bool pairwise, const u8 *mac_addr,
2153 struct key_params *params)
2154{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002155 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002156 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002157 s32 val;
2158 s32 wsec;
2159 s32 err = 0;
2160 u8 keybuf[8];
2161
Arend van Sprield96b8012012-12-05 15:26:02 +01002162 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002163 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002164 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002165 return -EIO;
2166
Hante Meuleman118eb302014-12-21 12:43:49 +01002167 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2168 /* we ignore this key index in this case */
2169 brcmf_err("invalid key index (%d)\n", key_idx);
2170 return -EINVAL;
2171 }
2172
Daniel Kim787eb032014-01-29 15:32:23 +01002173 if (mac_addr &&
2174 (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2175 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01002176 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002177 return brcmf_add_keyext(wiphy, ndev, key_idx, mac_addr, params);
2178 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002179
Hante Meuleman118eb302014-12-21 12:43:49 +01002180 key = &ifp->vif->profile.key[key_idx];
2181 memset(key, 0, sizeof(*key));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002182
Hante Meuleman118eb302014-12-21 12:43:49 +01002183 if (params->key_len > sizeof(key->data)) {
2184 brcmf_err("Too long key length (%u)\n", params->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002185 err = -EINVAL;
2186 goto done;
2187 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002188 key->len = params->key_len;
2189 key->index = key_idx;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002190
Hante Meuleman118eb302014-12-21 12:43:49 +01002191 memcpy(key->data, params->key, key->len);
2192
2193 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002194 switch (params->cipher) {
2195 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002196 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002197 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002198 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002199 break;
2200 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002201 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002202 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002203 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002204 break;
2205 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002206 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002207 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002208 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2209 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2210 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002211 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002212 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002213 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002214 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002215 break;
2216 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002217 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002218 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002219 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002220 break;
2221 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002222 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002223 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002224 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002225 break;
2226 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002227 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002228 err = -EINVAL;
2229 goto done;
2230 }
2231
Hante Meuleman118eb302014-12-21 12:43:49 +01002232 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002233 if (err)
2234 goto done;
2235
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002236 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002237 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002238 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002239 goto done;
2240 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002241 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002242 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002243 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002244 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002245 goto done;
2246 }
2247
Arend van Spriel5b435de2011-10-05 13:19:03 +02002248done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002249 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002250 return err;
2251}
2252
2253static s32
2254brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2255 u8 key_idx, bool pairwise, const u8 *mac_addr)
2256{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002257 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002258 struct brcmf_wsec_key key;
2259 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002260
Arend van Sprield96b8012012-12-05 15:26:02 +01002261 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002262 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002263 return -EIO;
2264
Hante Meuleman118eb302014-12-21 12:43:49 +01002265 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
Hante Meuleman256c3742012-11-05 16:22:28 -08002266 /* we ignore this key index in this case */
Hante Meuleman256c3742012-11-05 16:22:28 -08002267 return -EINVAL;
2268 }
2269
Arend van Spriel5b435de2011-10-05 13:19:03 +02002270 memset(&key, 0, sizeof(key));
2271
2272 key.index = (u32) key_idx;
2273 key.flags = BRCMF_PRIMARY_KEY;
2274 key.algo = CRYPTO_ALGO_OFF;
2275
Arend van Spriel16886732012-12-05 15:26:04 +01002276 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002277
2278 /* Set the new key/index */
Hante Meuleman118eb302014-12-21 12:43:49 +01002279 err = send_key_to_dongle(ifp, &key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002280
Arend van Sprield96b8012012-12-05 15:26:02 +01002281 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002282 return err;
2283}
2284
2285static s32
2286brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2287 u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
2288 void (*callback) (void *cookie, struct key_params * params))
2289{
2290 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002291 struct brcmf_if *ifp = netdev_priv(ndev);
2292 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002293 struct brcmf_cfg80211_security *sec;
2294 s32 wsec;
2295 s32 err = 0;
2296
Arend van Sprield96b8012012-12-05 15:26:02 +01002297 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002298 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002299 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002300 return -EIO;
2301
2302 memset(&params, 0, sizeof(params));
2303
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002304 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002305 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002306 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002307 /* Ignore this error, may happen during DISASSOC */
2308 err = -EAGAIN;
2309 goto done;
2310 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002311 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002312 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002313 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2314 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002315 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002316 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2317 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002318 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002319 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002320 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002321 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002322 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002323 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002324 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002325 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002326 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002327 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002328 err = -EINVAL;
2329 goto done;
2330 }
2331 callback(cookie, &params);
2332
2333done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002334 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002335 return err;
2336}
2337
2338static s32
2339brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2340 struct net_device *ndev, u8 key_idx)
2341{
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002342 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002343
2344 return -EOPNOTSUPP;
2345}
2346
Hante Meuleman118eb302014-12-21 12:43:49 +01002347static void
2348brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2349{
2350 s32 err;
2351 u8 key_idx;
2352 struct brcmf_wsec_key *key;
2353 s32 wsec;
2354
2355 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2356 key = &ifp->vif->profile.key[key_idx];
2357 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2358 (key->algo == CRYPTO_ALGO_WEP128))
2359 break;
2360 }
2361 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2362 return;
2363
2364 err = send_key_to_dongle(ifp, key);
2365 if (err) {
2366 brcmf_err("Setting WEP key failed (%d)\n", err);
2367 return;
2368 }
2369 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2370 if (err) {
2371 brcmf_err("get wsec error (%d)\n", err);
2372 return;
2373 }
2374 wsec |= WEP_ENABLED;
2375 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2376 if (err)
2377 brcmf_err("set wsec error (%d)\n", err);
2378}
2379
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002380static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2381{
2382 struct nl80211_sta_flag_update *sfu;
2383
2384 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2385 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2386 sfu = &si->sta_flags;
2387 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2388 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2389 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2390 BIT(NL80211_STA_FLAG_AUTHORIZED);
2391 if (fw_sta_flags & BRCMF_STA_WME)
2392 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2393 if (fw_sta_flags & BRCMF_STA_AUTHE)
2394 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2395 if (fw_sta_flags & BRCMF_STA_ASSOC)
2396 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2397 if (fw_sta_flags & BRCMF_STA_AUTHO)
2398 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2399}
2400
2401static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2402{
2403 struct {
2404 __le32 len;
2405 struct brcmf_bss_info_le bss_le;
2406 } *buf;
2407 u16 capability;
2408 int err;
2409
2410 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2411 if (!buf)
2412 return;
2413
2414 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2415 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2416 WL_BSS_INFO_MAX);
2417 if (err) {
2418 brcmf_err("Failed to get bss info (%d)\n", err);
2419 return;
2420 }
2421 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2422 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2423 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2424 capability = le16_to_cpu(buf->bss_le.capability);
2425 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2426 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2427 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2428 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2429 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2430 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
2431}
2432
Arend van Spriel5b435de2011-10-05 13:19:03 +02002433static s32
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002434brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
2435 struct station_info *sinfo)
2436{
2437 struct brcmf_scb_val_le scbval;
2438 struct brcmf_pktcnt_le pktcnt;
2439 s32 err;
2440 u32 rate;
2441 u32 rssi;
2442
2443 /* Get the current tx rate */
2444 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
2445 if (err < 0) {
2446 brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
2447 return err;
2448 }
2449 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
2450 sinfo->txrate.legacy = rate * 5;
2451
2452 memset(&scbval, 0, sizeof(scbval));
2453 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
2454 sizeof(scbval));
2455 if (err) {
2456 brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
2457 return err;
2458 }
2459 rssi = le32_to_cpu(scbval.val);
2460 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2461 sinfo->signal = rssi;
2462
2463 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
2464 sizeof(pktcnt));
2465 if (err) {
2466 brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
2467 return err;
2468 }
2469 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
2470 BIT(NL80211_STA_INFO_RX_DROP_MISC) |
2471 BIT(NL80211_STA_INFO_TX_PACKETS) |
2472 BIT(NL80211_STA_INFO_TX_FAILED);
2473 sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
2474 sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
2475 sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
2476 sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
2477
2478 return 0;
2479}
2480
2481static s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002482brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002483 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002484{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002485 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002486 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002487 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002488 u32 sta_flags;
2489 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002490 s32 total_rssi;
2491 s32 count_rssi;
2492 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002493
Arend van Sprield96b8012012-12-05 15:26:02 +01002494 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002495 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002496 return -EIO;
2497
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002498 if (brcmf_is_ibssmode(ifp->vif))
2499 return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
2500
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002501 memset(&sta_info_le, 0, sizeof(sta_info_le));
2502 memcpy(&sta_info_le, mac, ETH_ALEN);
2503 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2504 &sta_info_le,
2505 sizeof(sta_info_le));
2506 is_tdls_peer = !err;
2507 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002508 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002509 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002510 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002511 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002512 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002513 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002514 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002515 }
2516 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2517 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2518 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2519 sta_flags = le32_to_cpu(sta_info_le.flags);
2520 brcmf_convert_sta_flags(sta_flags, sinfo);
2521 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2522 if (is_tdls_peer)
2523 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2524 else
2525 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2526 if (sta_flags & BRCMF_STA_ASSOC) {
2527 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2528 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2529 brcmf_fill_bss_param(ifp, sinfo);
2530 }
2531 if (sta_flags & BRCMF_STA_SCBSTATS) {
2532 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2533 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2534 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2535 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2536 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2537 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2538 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2539 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2540 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002541 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002542 sinfo->txrate.legacy =
2543 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002544 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002545 if (sinfo->rx_packets) {
2546 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002547 sinfo->rxrate.legacy =
2548 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002549 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002550 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2551 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2552 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2553 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2554 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2555 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002556 total_rssi = 0;
2557 count_rssi = 0;
2558 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2559 if (sta_info_le.rssi[i]) {
2560 sinfo->chain_signal_avg[count_rssi] =
2561 sta_info_le.rssi[i];
2562 sinfo->chain_signal[count_rssi] =
2563 sta_info_le.rssi[i];
2564 total_rssi += sta_info_le.rssi[i];
2565 count_rssi++;
2566 }
2567 }
2568 if (count_rssi) {
2569 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2570 sinfo->chains = count_rssi;
2571
2572 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2573 total_rssi /= count_rssi;
2574 sinfo->signal = total_rssi;
2575 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002576 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002577done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002578 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002579 return err;
2580}
2581
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002582static int
2583brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2584 int idx, u8 *mac, struct station_info *sinfo)
2585{
2586 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2587 struct brcmf_if *ifp = netdev_priv(ndev);
2588 s32 err;
2589
2590 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2591
2592 if (idx == 0) {
2593 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2594 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2595 &cfg->assoclist,
2596 sizeof(cfg->assoclist));
2597 if (err) {
2598 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2599 err);
2600 cfg->assoclist.count = 0;
2601 return -EOPNOTSUPP;
2602 }
2603 }
2604 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2605 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2606 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2607 }
2608 return -ENOENT;
2609}
2610
Arend van Spriel5b435de2011-10-05 13:19:03 +02002611static s32
2612brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2613 bool enabled, s32 timeout)
2614{
2615 s32 pm;
2616 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002617 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002618 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002619
Arend van Sprield96b8012012-12-05 15:26:02 +01002620 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002621
2622 /*
2623 * Powersave enable/disable request is coming from the
2624 * cfg80211 even before the interface is up. In that
2625 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002626 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002627 * FW later while initializing the dongle
2628 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002629 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002630 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002631
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002632 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002633 goto done;
2634 }
2635
2636 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002637 /* Do not enable the power save after assoc if it is a p2p interface */
2638 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2639 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2640 pm = PM_OFF;
2641 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002642 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002643
Arend van Sprielc1179032012-10-22 13:55:33 -07002644 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002645 if (err) {
2646 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002647 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002648 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002649 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002650 }
2651done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002652 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002653 return err;
2654}
2655
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002656static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002657 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002658{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002659 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002660 struct ieee80211_channel *notify_channel;
2661 struct cfg80211_bss *bss;
2662 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002663 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002664 u16 channel;
2665 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002666 u16 notify_capability;
2667 u16 notify_interval;
2668 u8 *notify_ie;
2669 size_t notify_ielen;
2670 s32 notify_signal;
2671
2672 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002673 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002674 return 0;
2675 }
2676
Franky Lin83cf17a2013-04-11 13:28:50 +02002677 if (!bi->ctl_ch) {
2678 ch.chspec = le16_to_cpu(bi->chanspec);
2679 cfg->d11inf.decchspec(&ch);
2680 bi->ctl_ch = ch.chnum;
2681 }
2682 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002683
2684 if (channel <= CH_MAX_2G_CHANNEL)
2685 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2686 else
2687 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2688
2689 freq = ieee80211_channel_to_frequency(channel, band->band);
2690 notify_channel = ieee80211_get_channel(wiphy, freq);
2691
Arend van Spriel5b435de2011-10-05 13:19:03 +02002692 notify_capability = le16_to_cpu(bi->capability);
2693 notify_interval = le16_to_cpu(bi->beacon_period);
2694 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2695 notify_ielen = le32_to_cpu(bi->ie_length);
2696 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2697
Arend van Spriel16886732012-12-05 15:26:04 +01002698 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2699 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2700 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2701 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2702 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002703
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002704 bss = cfg80211_inform_bss(wiphy, notify_channel,
2705 CFG80211_BSS_FTYPE_UNKNOWN,
2706 (const u8 *)bi->BSSID,
2707 0, notify_capability,
2708 notify_interval, notify_ie,
2709 notify_ielen, notify_signal,
2710 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002711
Franky Line78946e2011-11-10 20:30:34 +01002712 if (!bss)
2713 return -ENOMEM;
2714
Johannes Berg5b112d32013-02-01 01:49:58 +01002715 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002716
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002717 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002718}
2719
Roland Vossen6f09be02011-10-18 14:03:02 +02002720static struct brcmf_bss_info_le *
2721next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2722{
2723 if (bss == NULL)
2724 return list->bss_info_le;
2725 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2726 le32_to_cpu(bss->length));
2727}
2728
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002729static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002730{
2731 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002732 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002733 s32 err = 0;
2734 int i;
2735
Hante Meulemanef8596e2014-09-30 10:23:13 +02002736 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002737 if (bss_list->count != 0 &&
2738 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002739 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2740 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002741 return -EOPNOTSUPP;
2742 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002743 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002744 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002745 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002746 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002747 if (err)
2748 break;
2749 }
2750 return err;
2751}
2752
Hante Meulemanb0a79082015-12-10 13:43:07 +01002753static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
2754 struct net_device *ndev, const u8 *bssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002755{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002756 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002757 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002758 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002759 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002760 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002761 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002762 u8 *buf = NULL;
2763 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002764 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002765 u16 notify_capability;
2766 u16 notify_interval;
2767 u8 *notify_ie;
2768 size_t notify_ielen;
2769 s32 notify_signal;
2770
Arend van Sprield96b8012012-12-05 15:26:02 +01002771 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002772
2773 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2774 if (buf == NULL) {
2775 err = -ENOMEM;
2776 goto CleanUp;
2777 }
2778
2779 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2780
Arend van Sprielac24be62012-10-22 10:36:23 -07002781 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2782 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002783 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002784 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002785 goto CleanUp;
2786 }
2787
Roland Vossend34bf642011-10-18 14:03:01 +02002788 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002789
Franky Lin83cf17a2013-04-11 13:28:50 +02002790 ch.chspec = le16_to_cpu(bi->chanspec);
2791 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002792
Franky Lin83cf17a2013-04-11 13:28:50 +02002793 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002794 band = wiphy->bands[IEEE80211_BAND_2GHZ];
2795 else
2796 band = wiphy->bands[IEEE80211_BAND_5GHZ];
2797
Franky Lin83cf17a2013-04-11 13:28:50 +02002798 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Hante Meulemanb0a79082015-12-10 13:43:07 +01002799 cfg->channel = freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002800 notify_channel = ieee80211_get_channel(wiphy, freq);
2801
Arend van Spriel5b435de2011-10-05 13:19:03 +02002802 notify_capability = le16_to_cpu(bi->capability);
2803 notify_interval = le16_to_cpu(bi->beacon_period);
2804 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2805 notify_ielen = le32_to_cpu(bi->ie_length);
2806 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2807
Franky Lin83cf17a2013-04-11 13:28:50 +02002808 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.chnum, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002809 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2810 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2811 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002812
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002813 bss = cfg80211_inform_bss(wiphy, notify_channel,
2814 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2815 notify_capability, notify_interval,
2816 notify_ie, notify_ielen, notify_signal,
2817 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002818
Franky Line78946e2011-11-10 20:30:34 +01002819 if (!bss) {
2820 err = -ENOMEM;
2821 goto CleanUp;
2822 }
2823
Johannes Berg5b112d32013-02-01 01:49:58 +01002824 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002825
Arend van Spriel5b435de2011-10-05 13:19:03 +02002826CleanUp:
2827
2828 kfree(buf);
2829
Arend van Sprield96b8012012-12-05 15:26:02 +01002830 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002831
2832 return err;
2833}
2834
Hante Meuleman89286dc2013-02-08 15:53:46 +01002835static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2836 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002837{
Roland Vossend34bf642011-10-18 14:03:01 +02002838 struct brcmf_bss_info_le *bi;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002839 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002840 u16 beacon_interval;
2841 u8 dtim_period;
2842 size_t ie_len;
2843 u8 *ie;
2844 s32 err = 0;
2845
Arend van Sprield96b8012012-12-05 15:26:02 +01002846 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002847 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002848 return err;
2849
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002850 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002851 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002852 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002853 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002854 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002855 goto update_bss_info_out;
2856 }
2857
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002858 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2859 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002860 if (err)
2861 goto update_bss_info_out;
2862
2863 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2864 ie_len = le32_to_cpu(bi->ie_length);
2865 beacon_interval = le16_to_cpu(bi->beacon_period);
2866
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002867 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002868 if (tim)
2869 dtim_period = tim->data[1];
2870 else {
2871 /*
2872 * active scan was done so we could not get dtim
2873 * information out of probe response.
2874 * so we speficially query dtim information to dongle.
2875 */
2876 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07002877 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002878 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002879 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002880 goto update_bss_info_out;
2881 }
2882 dtim_period = (u8)var;
2883 }
2884
Arend van Spriel5b435de2011-10-05 13:19:03 +02002885update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01002886 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002887 return err;
2888}
2889
Hante Meuleman18e2f612013-02-08 15:53:49 +01002890void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002891{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002892 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002893
Arend van Sprielc1179032012-10-22 13:55:33 -07002894 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Hante Meulemanf07998952012-11-05 16:22:13 -08002895 if (cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02002896 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02002897 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02002898 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002899 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
2900 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002901}
2902
Hante Meulemane756af52012-09-11 21:18:52 +02002903static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
2904{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002905 struct brcmf_cfg80211_info *cfg =
2906 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02002907 escan_timeout_work);
2908
Hante Meulemanef8596e2014-09-30 10:23:13 +02002909 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02002910 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02002911}
2912
2913static void brcmf_escan_timeout(unsigned long data)
2914{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002915 struct brcmf_cfg80211_info *cfg =
2916 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02002917
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002918 if (cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002919 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08002920 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02002921 }
2922}
2923
2924static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02002925brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
2926 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02002927 struct brcmf_bss_info_le *bss_info_le)
2928{
Franky Lin83cf17a2013-04-11 13:28:50 +02002929 struct brcmu_chan ch_bss, ch_bss_info_le;
2930
2931 ch_bss.chspec = le16_to_cpu(bss->chanspec);
2932 cfg->d11inf.decchspec(&ch_bss);
2933 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
2934 cfg->d11inf.decchspec(&ch_bss_info_le);
2935
Hante Meulemane756af52012-09-11 21:18:52 +02002936 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02002937 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02002938 bss_info_le->SSID_len == bss->SSID_len &&
2939 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002940 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
2941 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02002942 s16 bss_rssi = le16_to_cpu(bss->RSSI);
2943 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
2944
Hante Meulemane756af52012-09-11 21:18:52 +02002945 /* preserve max RSSI if the measurements are
2946 * both on-channel or both off-channel
2947 */
Arend van Spriel029591f2012-09-19 22:21:06 +02002948 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02002949 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002950 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
2951 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02002952 /* preserve the on-channel rssi measurement
2953 * if the new measurement is off channel
2954 */
2955 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01002956 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02002957 }
2958 return 1;
2959 }
2960 return 0;
2961}
2962
2963static s32
Arend van Spriel19937322012-11-05 16:22:32 -08002964brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02002965 const struct brcmf_event_msg *e, void *data)
2966{
Arend van Spriel19937322012-11-05 16:22:32 -08002967 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02002968 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02002969 struct brcmf_escan_result_le *escan_result_le;
2970 struct brcmf_bss_info_le *bss_info_le;
2971 struct brcmf_bss_info_le *bss = NULL;
2972 u32 bi_length;
2973 struct brcmf_scan_results *list;
2974 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02002975 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02002976
Arend van Spriel5c36b992012-11-14 18:46:05 -08002977 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02002978
Arend van Spriela0f472a2013-04-05 10:57:49 +02002979 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meuleman37a869e2015-10-29 20:33:17 +01002980 brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
Hante Meulemane756af52012-09-11 21:18:52 +02002981 return -EPERM;
2982 }
2983
2984 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002985 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002986 escan_result_le = (struct brcmf_escan_result_le *) data;
2987 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002988 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02002989 goto exit;
2990 }
Hante Meulemane756af52012-09-11 21:18:52 +02002991 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002992 brcmf_err("Invalid bss_count %d: ignoring\n",
2993 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02002994 goto exit;
2995 }
2996 bss_info_le = &escan_result_le->bss_info_le;
2997
Hante Meuleman6eda4e22013-02-08 15:54:02 +01002998 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
2999 goto exit;
3000
3001 if (!cfg->scan_request) {
3002 brcmf_dbg(SCAN, "result without cfg80211 request\n");
3003 goto exit;
3004 }
3005
Hante Meulemane756af52012-09-11 21:18:52 +02003006 bi_length = le32_to_cpu(bss_info_le->length);
3007 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
3008 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003009 brcmf_err("Invalid bss_info length %d: ignoring\n",
3010 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02003011 goto exit;
3012 }
3013
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003014 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02003015 BIT(NL80211_IFTYPE_ADHOC))) {
3016 if (le16_to_cpu(bss_info_le->capability) &
3017 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003018 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003019 goto exit;
3020 }
3021 }
3022
3023 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003024 cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02003025 if (bi_length > WL_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003026 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003027 goto exit;
3028 }
3029
3030 for (i = 0; i < list->count; i++) {
3031 bss = bss ? (struct brcmf_bss_info_le *)
3032 ((unsigned char *)bss +
3033 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02003034 if (brcmf_compare_update_same_bss(cfg, bss,
3035 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02003036 goto exit;
3037 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003038 memcpy(&(cfg->escan_info.escan_buf[list->buflen]),
Hante Meulemane756af52012-09-11 21:18:52 +02003039 bss_info_le, bi_length);
3040 list->version = le32_to_cpu(bss_info_le->version);
3041 list->buflen += bi_length;
3042 list->count++;
3043 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003044 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003045 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
3046 goto exit;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003047 if (cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003048 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003049 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02003050 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02003051 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003052 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
3053 status);
Hante Meulemane756af52012-09-11 21:18:52 +02003054 }
3055exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003056 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02003057}
3058
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003059static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003060{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003061 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3062 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003063 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3064 /* Init scan_timeout timer */
3065 init_timer(&cfg->escan_timeout);
3066 cfg->escan_timeout.data = (unsigned long) cfg;
3067 cfg->escan_timeout.function = brcmf_escan_timeout;
3068 INIT_WORK(&cfg->escan_timeout_work,
3069 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003070}
3071
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003072/* PFN result doesn't have all the info which are required by the supplicant
3073 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3074 * via wl_inform_single_bss in the required format. Escan does require the
3075 * scan request in the form of cfg80211_scan_request. For timebeing, create
3076 * cfg80211_scan_request one out of the received PNO event.
3077 */
3078static s32
3079brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
3080 const struct brcmf_event_msg *e, void *data)
3081{
3082 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3083 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3084 struct cfg80211_scan_request *request = NULL;
3085 struct cfg80211_ssid *ssid = NULL;
3086 struct ieee80211_channel *channel = NULL;
3087 struct wiphy *wiphy = cfg_to_wiphy(cfg);
3088 int err = 0;
3089 int channel_req = 0;
3090 int band = 0;
3091 struct brcmf_pno_scanresults_le *pfn_result;
3092 u32 result_count;
3093 u32 status;
3094
3095 brcmf_dbg(SCAN, "Enter\n");
3096
3097 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
3098 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
3099 return 0;
3100 }
3101
3102 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3103 result_count = le32_to_cpu(pfn_result->count);
3104 status = le32_to_cpu(pfn_result->status);
3105
3106 /* PFN event is limited to fit 512 bytes so we may get
3107 * multiple NET_FOUND events. For now place a warning here.
3108 */
3109 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3110 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
3111 if (result_count > 0) {
3112 int i;
3113
3114 request = kzalloc(sizeof(*request), GFP_KERNEL);
3115 ssid = kcalloc(result_count, sizeof(*ssid), GFP_KERNEL);
3116 channel = kcalloc(result_count, sizeof(*channel), GFP_KERNEL);
3117 if (!request || !ssid || !channel) {
3118 err = -ENOMEM;
3119 goto out_err;
3120 }
3121
3122 request->wiphy = wiphy;
3123 data += sizeof(struct brcmf_pno_scanresults_le);
3124 netinfo_start = (struct brcmf_pno_net_info_le *)data;
3125
3126 for (i = 0; i < result_count; i++) {
3127 netinfo = &netinfo_start[i];
3128 if (!netinfo) {
3129 brcmf_err("Invalid netinfo ptr. index: %d\n",
3130 i);
3131 err = -EINVAL;
3132 goto out_err;
3133 }
3134
3135 brcmf_dbg(SCAN, "SSID:%s Channel:%d\n",
3136 netinfo->SSID, netinfo->channel);
3137 memcpy(ssid[i].ssid, netinfo->SSID, netinfo->SSID_len);
3138 ssid[i].ssid_len = netinfo->SSID_len;
3139 request->n_ssids++;
3140
3141 channel_req = netinfo->channel;
3142 if (channel_req <= CH_MAX_2G_CHANNEL)
3143 band = NL80211_BAND_2GHZ;
3144 else
3145 band = NL80211_BAND_5GHZ;
3146 channel[i].center_freq =
3147 ieee80211_channel_to_frequency(channel_req,
3148 band);
3149 channel[i].band = band;
3150 channel[i].flags |= IEEE80211_CHAN_NO_HT40;
3151 request->channels[i] = &channel[i];
3152 request->n_channels++;
3153 }
3154
3155 /* assign parsed ssid array */
3156 if (request->n_ssids)
3157 request->ssids = &ssid[0];
3158
3159 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3160 /* Abort any on-going scan */
3161 brcmf_abort_scanning(cfg);
3162 }
3163
3164 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3165 cfg->escan_info.run = brcmf_run_escan;
3166 err = brcmf_do_escan(cfg, wiphy, ifp, request);
3167 if (err) {
3168 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3169 goto out_err;
3170 }
3171 cfg->sched_escan = true;
3172 cfg->scan_request = request;
3173 } else {
3174 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
3175 goto out_err;
3176 }
3177
3178 kfree(ssid);
3179 kfree(channel);
3180 kfree(request);
3181 return 0;
3182
3183out_err:
3184 kfree(ssid);
3185 kfree(channel);
3186 kfree(request);
3187 cfg80211_sched_scan_stopped(wiphy);
3188 return err;
3189}
3190
3191static int brcmf_dev_pno_clean(struct net_device *ndev)
3192{
3193 int ret;
3194
3195 /* Disable pfn */
3196 ret = brcmf_fil_iovar_int_set(netdev_priv(ndev), "pfn", 0);
3197 if (ret == 0) {
3198 /* clear pfn */
3199 ret = brcmf_fil_iovar_data_set(netdev_priv(ndev), "pfnclear",
3200 NULL, 0);
3201 }
3202 if (ret < 0)
3203 brcmf_err("failed code %d\n", ret);
3204
3205 return ret;
3206}
3207
3208static int brcmf_dev_pno_config(struct brcmf_if *ifp,
3209 struct cfg80211_sched_scan_request *request)
3210{
3211 struct brcmf_pno_param_le pfn_param;
3212 struct brcmf_pno_macaddr_le pfn_mac;
3213 s32 err;
3214 u8 *mac_mask;
3215 int i;
3216
3217 memset(&pfn_param, 0, sizeof(pfn_param));
3218 pfn_param.version = cpu_to_le32(BRCMF_PNO_VERSION);
3219
3220 /* set extra pno params */
3221 pfn_param.flags = cpu_to_le16(1 << BRCMF_PNO_ENABLE_ADAPTSCAN_BIT);
3222 pfn_param.repeat = BRCMF_PNO_REPEAT;
3223 pfn_param.exp = BRCMF_PNO_FREQ_EXPO_MAX;
3224
3225 /* set up pno scan fr */
3226 pfn_param.scan_freq = cpu_to_le32(BRCMF_PNO_TIME);
3227
3228 err = brcmf_fil_iovar_data_set(ifp, "pfn_set", &pfn_param,
3229 sizeof(pfn_param));
3230 if (err) {
3231 brcmf_err("pfn_set failed, err=%d\n", err);
3232 return err;
3233 }
3234
3235 /* Find out if mac randomization should be turned on */
3236 if (!(request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR))
3237 return 0;
3238
3239 pfn_mac.version = BRCMF_PFN_MACADDR_CFG_VER;
3240 pfn_mac.flags = BRCMF_PFN_MAC_OUI_ONLY | BRCMF_PFN_SET_MAC_UNASSOC;
3241
3242 memcpy(pfn_mac.mac, request->mac_addr, ETH_ALEN);
3243 mac_mask = request->mac_addr_mask;
3244 for (i = 0; i < ETH_ALEN; i++) {
3245 pfn_mac.mac[i] &= mac_mask[i];
3246 pfn_mac.mac[i] |= get_random_int() & ~(mac_mask[i]);
3247 }
3248 /* Clear multi bit */
3249 pfn_mac.mac[0] &= 0xFE;
3250 /* Set locally administered */
3251 pfn_mac.mac[0] |= 0x02;
3252
3253 err = brcmf_fil_iovar_data_set(ifp, "pfn_macaddr", &pfn_mac,
3254 sizeof(pfn_mac));
3255 if (err)
3256 brcmf_err("pfn_macaddr failed, err=%d\n", err);
3257
3258 return err;
3259}
3260
3261static int
3262brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3263 struct net_device *ndev,
3264 struct cfg80211_sched_scan_request *request)
3265{
3266 struct brcmf_if *ifp = netdev_priv(ndev);
3267 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
3268 struct brcmf_pno_net_param_le pfn;
3269 int i;
3270 int ret = 0;
3271
3272 brcmf_dbg(SCAN, "Enter n_match_sets:%d n_ssids:%d\n",
3273 request->n_match_sets, request->n_ssids);
3274 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3275 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
3276 return -EAGAIN;
3277 }
3278 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
3279 brcmf_err("Scanning suppressed: status (%lu)\n",
3280 cfg->scan_status);
3281 return -EAGAIN;
3282 }
3283
3284 if (!request->n_ssids || !request->n_match_sets) {
3285 brcmf_dbg(SCAN, "Invalid sched scan req!! n_ssids:%d\n",
3286 request->n_ssids);
3287 return -EINVAL;
3288 }
3289
3290 if (request->n_ssids > 0) {
3291 for (i = 0; i < request->n_ssids; i++) {
3292 /* Active scan req for ssids */
3293 brcmf_dbg(SCAN, ">>> Active scan req for ssid (%s)\n",
3294 request->ssids[i].ssid);
3295
3296 /* match_set ssids is a supert set of n_ssid list,
3297 * so we need not add these set separately.
3298 */
3299 }
3300 }
3301
3302 if (request->n_match_sets > 0) {
3303 /* clean up everything */
3304 ret = brcmf_dev_pno_clean(ndev);
3305 if (ret < 0) {
3306 brcmf_err("failed error=%d\n", ret);
3307 return ret;
3308 }
3309
3310 /* configure pno */
3311 if (brcmf_dev_pno_config(ifp, request))
3312 return -EINVAL;
3313
3314 /* configure each match set */
3315 for (i = 0; i < request->n_match_sets; i++) {
3316 struct cfg80211_ssid *ssid;
3317 u32 ssid_len;
3318
3319 ssid = &request->match_sets[i].ssid;
3320 ssid_len = ssid->ssid_len;
3321
3322 if (!ssid_len) {
3323 brcmf_err("skip broadcast ssid\n");
3324 continue;
3325 }
3326 pfn.auth = cpu_to_le32(WLAN_AUTH_OPEN);
3327 pfn.wpa_auth = cpu_to_le32(BRCMF_PNO_WPA_AUTH_ANY);
3328 pfn.wsec = cpu_to_le32(0);
3329 pfn.infra = cpu_to_le32(1);
3330 pfn.flags = cpu_to_le32(1 << BRCMF_PNO_HIDDEN_BIT);
3331 pfn.ssid.SSID_len = cpu_to_le32(ssid_len);
3332 memcpy(pfn.ssid.SSID, ssid->ssid, ssid_len);
3333 ret = brcmf_fil_iovar_data_set(ifp, "pfn_add", &pfn,
3334 sizeof(pfn));
3335 brcmf_dbg(SCAN, ">>> PNO filter %s for ssid (%s)\n",
3336 ret == 0 ? "set" : "failed", ssid->ssid);
3337 }
3338 /* Enable the PNO */
3339 if (brcmf_fil_iovar_int_set(ifp, "pfn", 1) < 0) {
3340 brcmf_err("PNO enable failed!! ret=%d\n", ret);
3341 return -EINVAL;
3342 }
3343 } else {
3344 return -EINVAL;
3345 }
3346
3347 return 0;
3348}
3349
3350static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
3351 struct net_device *ndev)
3352{
3353 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3354
3355 brcmf_dbg(SCAN, "enter\n");
3356 brcmf_dev_pno_clean(ndev);
3357 if (cfg->sched_escan)
3358 brcmf_notify_escan_complete(cfg, netdev_priv(ndev), true, true);
3359 return 0;
3360}
3361
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003362static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003363{
3364 if (ms < 1000 / HZ) {
3365 cond_resched();
3366 mdelay(ms);
3367 } else {
3368 msleep(ms);
3369 }
3370}
3371
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003372static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3373 u8 *pattern, u32 patternsize, u8 *mask,
3374 u32 packet_offset)
3375{
3376 struct brcmf_fil_wowl_pattern_le *filter;
3377 u32 masksize;
3378 u32 patternoffset;
3379 u8 *buf;
3380 u32 bufsize;
3381 s32 ret;
3382
3383 masksize = (patternsize + 7) / 8;
3384 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3385
3386 bufsize = sizeof(*filter) + patternsize + masksize;
3387 buf = kzalloc(bufsize, GFP_KERNEL);
3388 if (!buf)
3389 return -ENOMEM;
3390 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3391
3392 memcpy(filter->cmd, cmd, 4);
3393 filter->masksize = cpu_to_le32(masksize);
3394 filter->offset = cpu_to_le32(packet_offset);
3395 filter->patternoffset = cpu_to_le32(patternoffset);
3396 filter->patternsize = cpu_to_le32(patternsize);
3397 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3398
3399 if ((mask) && (masksize))
3400 memcpy(buf + sizeof(*filter), mask, masksize);
3401 if ((pattern) && (patternsize))
3402 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3403
3404 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3405
3406 kfree(buf);
3407 return ret;
3408}
3409
Hante Meuleman3021ad92016-01-05 11:05:45 +01003410static s32
3411brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
3412 void *data)
3413{
3414 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3415 struct brcmf_pno_scanresults_le *pfn_result;
3416 struct brcmf_pno_net_info_le *netinfo;
3417
3418 brcmf_dbg(SCAN, "Enter\n");
3419
3420 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3421
3422 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
3423 brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
3424 return 0;
3425 }
3426
3427 if (le32_to_cpu(pfn_result->count) < 1) {
3428 brcmf_err("Invalid result count, expected 1 (%d)\n",
3429 le32_to_cpu(pfn_result->count));
3430 return -EINVAL;
3431 }
3432
3433 data += sizeof(struct brcmf_pno_scanresults_le);
3434 netinfo = (struct brcmf_pno_net_info_le *)data;
3435 memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
3436 cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
3437 cfg->wowl.nd->n_channels = 1;
3438 cfg->wowl.nd->channels[0] =
3439 ieee80211_channel_to_frequency(netinfo->channel,
3440 netinfo->channel <= CH_MAX_2G_CHANNEL ?
3441 NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
3442 cfg->wowl.nd_info->n_matches = 1;
3443 cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
3444
3445 /* Inform (the resume task) that the net detect information was recvd */
3446 cfg->wowl.nd_data_completed = true;
3447 wake_up(&cfg->wowl.nd_data_wait);
3448
3449 return 0;
3450}
3451
Hante Meulemanaeb64222015-10-29 20:33:19 +01003452#ifdef CONFIG_PM
3453
3454static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3455{
Hante Meuleman3021ad92016-01-05 11:05:45 +01003456 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemanaeb64222015-10-29 20:33:19 +01003457 struct brcmf_wowl_wakeind_le wake_ind_le;
3458 struct cfg80211_wowlan_wakeup wakeup_data;
3459 struct cfg80211_wowlan_wakeup *wakeup;
3460 u32 wakeind;
3461 s32 err;
Hante Meuleman3021ad92016-01-05 11:05:45 +01003462 int timeout;
Hante Meulemanaeb64222015-10-29 20:33:19 +01003463
3464 err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
3465 sizeof(wake_ind_le));
Hante Meuleman3021ad92016-01-05 11:05:45 +01003466 if (err) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003467 brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
3468 return;
3469 }
3470
3471 wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
3472 if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
Hante Meuleman3021ad92016-01-05 11:05:45 +01003473 BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
3474 BRCMF_WOWL_PFN_FOUND)) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003475 wakeup = &wakeup_data;
3476 memset(&wakeup_data, 0, sizeof(wakeup_data));
3477 wakeup_data.pattern_idx = -1;
3478
3479 if (wakeind & BRCMF_WOWL_MAGIC) {
3480 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
3481 wakeup_data.magic_pkt = true;
3482 }
3483 if (wakeind & BRCMF_WOWL_DIS) {
3484 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
3485 wakeup_data.disconnect = true;
3486 }
3487 if (wakeind & BRCMF_WOWL_BCN) {
3488 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
3489 wakeup_data.disconnect = true;
3490 }
3491 if (wakeind & BRCMF_WOWL_RETR) {
3492 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
3493 wakeup_data.disconnect = true;
3494 }
3495 if (wakeind & BRCMF_WOWL_NET) {
3496 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
3497 /* For now always map to pattern 0, no API to get
3498 * correct information available at the moment.
3499 */
3500 wakeup_data.pattern_idx = 0;
3501 }
Hante Meuleman3021ad92016-01-05 11:05:45 +01003502 if (wakeind & BRCMF_WOWL_PFN_FOUND) {
3503 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
3504 timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
3505 cfg->wowl.nd_data_completed,
3506 BRCMF_ND_INFO_TIMEOUT);
3507 if (!timeout)
3508 brcmf_err("No result for wowl net detect\n");
3509 else
3510 wakeup_data.net_detect = cfg->wowl.nd_info;
3511 }
Hante Meulemanaeb64222015-10-29 20:33:19 +01003512 } else {
3513 wakeup = NULL;
3514 }
3515 cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
3516}
3517
3518#else
3519
3520static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3521{
3522}
3523
3524#endif /* CONFIG_PM */
3525
Arend van Spriel5b435de2011-10-05 13:19:03 +02003526static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3527{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003528 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3529 struct net_device *ndev = cfg_to_ndev(cfg);
3530 struct brcmf_if *ifp = netdev_priv(ndev);
3531
Arend van Sprield96b8012012-12-05 15:26:02 +01003532 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003533
Hante Meuleman3021ad92016-01-05 11:05:45 +01003534 if (cfg->wowl.active) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003535 brcmf_report_wowl_wakeind(wiphy, ifp);
3536 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
3537 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003538 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003539 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
Hante Meuleman3021ad92016-01-05 11:05:45 +01003540 cfg->wowl.pre_pmmode);
3541 cfg->wowl.active = false;
3542 if (cfg->wowl.nd_enabled) {
3543 brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev);
3544 brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
3545 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
3546 brcmf_notify_sched_scan_results);
3547 cfg->wowl.nd_enabled = false;
3548 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003549 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003550 return 0;
3551}
3552
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003553static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3554 struct brcmf_if *ifp,
3555 struct cfg80211_wowlan *wowl)
3556{
3557 u32 wowl_config;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003558 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003559
3560 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3561
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003562 brcmf_configure_arp_offload(ifp, false);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003563 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003564 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3565
3566 wowl_config = 0;
3567 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003568 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003569 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003570 wowl_config |= BRCMF_WOWL_MAGIC;
3571 if ((wowl->patterns) && (wowl->n_patterns)) {
3572 wowl_config |= BRCMF_WOWL_NET;
3573 for (i = 0; i < wowl->n_patterns; i++) {
3574 brcmf_config_wowl_pattern(ifp, "add",
3575 (u8 *)wowl->patterns[i].pattern,
3576 wowl->patterns[i].pattern_len,
3577 (u8 *)wowl->patterns[i].mask,
3578 wowl->patterns[i].pkt_offset);
3579 }
3580 }
Hante Meuleman3021ad92016-01-05 11:05:45 +01003581 if (wowl->nd_config) {
3582 brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
3583 wowl->nd_config);
3584 wowl_config |= BRCMF_WOWL_PFN_FOUND;
3585
3586 cfg->wowl.nd_data_completed = false;
3587 cfg->wowl.nd_enabled = true;
3588 /* Now reroute the event for PFN to the wowl function. */
3589 brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
3590 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
3591 brcmf_wowl_nd_results);
3592 }
3593 if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
3594 wowl_config |= BRCMF_WOWL_UNASSOC;
3595
Hante Meulemanaeb64222015-10-29 20:33:19 +01003596 brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", "clear", strlen("clear"));
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003597 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3598 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3599 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003600 cfg->wowl.active = true;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003601}
3602
Arend van Spriel5b435de2011-10-05 13:19:03 +02003603static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003604 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003605{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003606 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3607 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003608 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003609 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003610
Arend van Sprield96b8012012-12-05 15:26:02 +01003611 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003612
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003613 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003614 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003615 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003616 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003617 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003618
Hante Meuleman3021ad92016-01-05 11:05:45 +01003619 /* Stop scheduled scan */
3620 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
3621 brcmf_cfg80211_sched_scan_stop(wiphy, ndev);
3622
Arend van Spriel7d641072012-10-22 13:55:39 -07003623 /* end any scanning */
3624 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003625 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003626
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003627 if (wowl == NULL) {
3628 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3629 list_for_each_entry(vif, &cfg->vif_list, list) {
3630 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3631 continue;
3632 /* While going to suspend if associated with AP
3633 * disassociate from AP to save power while system is
3634 * in suspended state
3635 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003636 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003637 /* Make sure WPA_Supplicant receives all the event
3638 * generated due to DISASSOC call to the fw to keep
3639 * the state fw and WPA_Supplicant state consistent
3640 */
3641 brcmf_delay(500);
3642 }
3643 /* Configure MPC */
3644 brcmf_set_mpc(ifp, 1);
3645
3646 } else {
3647 /* Configure WOWL paramaters */
3648 brcmf_configure_wowl(cfg, ifp, wowl);
3649 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003650
Arend van Spriel7d641072012-10-22 13:55:39 -07003651exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003652 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003653 /* clear any scanning activity */
3654 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003655 return 0;
3656}
3657
3658static __used s32
Hante Meuleman6c404f32015-12-10 13:43:03 +01003659brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003660{
Hante Meuleman6c404f32015-12-10 13:43:03 +01003661 struct brcmf_pmk_list_le *pmk_list;
3662 int i;
3663 u32 npmk;
3664 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003665
Hante Meuleman6c404f32015-12-10 13:43:03 +01003666 pmk_list = &cfg->pmk_list;
3667 npmk = le32_to_cpu(pmk_list->npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003668
Hante Meuleman6c404f32015-12-10 13:43:03 +01003669 brcmf_dbg(CONN, "No of elements %d\n", npmk);
3670 for (i = 0; i < npmk; i++)
3671 brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003672
Hante Meuleman6c404f32015-12-10 13:43:03 +01003673 err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
3674 sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003675
3676 return err;
3677}
3678
3679static s32
3680brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3681 struct cfg80211_pmksa *pmksa)
3682{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003683 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003684 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003685 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3686 s32 err;
3687 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003688
Arend van Sprield96b8012012-12-05 15:26:02 +01003689 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003690 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003691 return -EIO;
3692
Hante Meuleman6c404f32015-12-10 13:43:03 +01003693 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3694 for (i = 0; i < npmk; i++)
3695 if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003696 break;
Hante Meuleman6c404f32015-12-10 13:43:03 +01003697 if (i < BRCMF_MAXPMKID) {
3698 memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
3699 memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
3700 if (i == npmk) {
3701 npmk++;
3702 cfg->pmk_list.npmk = cpu_to_le32(npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003703 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003704 } else {
3705 brcmf_err("Too many PMKSA entries cached %d\n", npmk);
3706 return -EINVAL;
3707 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003708
Hante Meuleman6c404f32015-12-10 13:43:03 +01003709 brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
3710 for (i = 0; i < WLAN_PMKID_LEN; i += 4)
3711 brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
3712 pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
3713 pmk[npmk].pmkid[i + 3]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003714
Hante Meuleman6c404f32015-12-10 13:43:03 +01003715 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003716
Arend van Sprield96b8012012-12-05 15:26:02 +01003717 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003718 return err;
3719}
3720
3721static s32
3722brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman6c404f32015-12-10 13:43:03 +01003723 struct cfg80211_pmksa *pmksa)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003724{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003725 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003726 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003727 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3728 s32 err;
3729 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003730
Arend van Sprield96b8012012-12-05 15:26:02 +01003731 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003732 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003733 return -EIO;
3734
Hante Meuleman6c404f32015-12-10 13:43:03 +01003735 brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", &pmksa->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003736
Hante Meuleman6c404f32015-12-10 13:43:03 +01003737 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3738 for (i = 0; i < npmk; i++)
3739 if (!memcmp(&pmksa->bssid, &pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003740 break;
3741
Hante Meuleman6c404f32015-12-10 13:43:03 +01003742 if ((npmk > 0) && (i < npmk)) {
3743 for (; i < (npmk - 1); i++) {
3744 memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
3745 memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003746 WLAN_PMKID_LEN);
3747 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003748 memset(&pmk[i], 0, sizeof(*pmk));
3749 cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
3750 } else {
3751 brcmf_err("Cache entry not found\n");
3752 return -EINVAL;
3753 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003754
Hante Meuleman6c404f32015-12-10 13:43:03 +01003755 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003756
Arend van Sprield96b8012012-12-05 15:26:02 +01003757 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003758 return err;
3759
3760}
3761
3762static s32
3763brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3764{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003765 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003766 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003767 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003768
Arend van Sprield96b8012012-12-05 15:26:02 +01003769 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003770 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003771 return -EIO;
3772
Hante Meuleman6c404f32015-12-10 13:43:03 +01003773 memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
3774 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003775
Arend van Sprield96b8012012-12-05 15:26:02 +01003776 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003777 return err;
3778
3779}
3780
Hante Meuleman1f170112013-02-06 18:40:38 +01003781static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003782{
3783 s32 err;
3784
3785 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003786 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003787 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003788 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003789 return err;
3790 }
3791 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003792 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003793 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003794 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003795 return err;
3796 }
3797 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003798 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003799 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003800 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003801 return err;
3802 }
3803
3804 return 0;
3805}
3806
3807static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3808{
3809 if (is_rsn_ie)
3810 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3811
3812 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3813}
3814
3815static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003816brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003817 const struct brcmf_vs_tlv *wpa_ie,
3818 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003819{
3820 u32 auth = 0; /* d11 open authentication */
3821 u16 count;
3822 s32 err = 0;
3823 s32 len = 0;
3824 u32 i;
3825 u32 wsec;
3826 u32 pval = 0;
3827 u32 gval = 0;
3828 u32 wpa_auth = 0;
3829 u32 offset;
3830 u8 *data;
3831 u16 rsn_cap;
3832 u32 wme_bss_disable;
3833
Arend van Sprield96b8012012-12-05 15:26:02 +01003834 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003835 if (wpa_ie == NULL)
3836 goto exit;
3837
3838 len = wpa_ie->len + TLV_HDR_LEN;
3839 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003840 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003841 if (!is_rsn_ie)
3842 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003843 else
3844 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003845
3846 /* check for multicast cipher suite */
3847 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3848 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003849 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003850 goto exit;
3851 }
3852
3853 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3854 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003855 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003856 goto exit;
3857 }
3858 offset += TLV_OUI_LEN;
3859
3860 /* pick up multicast cipher */
3861 switch (data[offset]) {
3862 case WPA_CIPHER_NONE:
3863 gval = 0;
3864 break;
3865 case WPA_CIPHER_WEP_40:
3866 case WPA_CIPHER_WEP_104:
3867 gval = WEP_ENABLED;
3868 break;
3869 case WPA_CIPHER_TKIP:
3870 gval = TKIP_ENABLED;
3871 break;
3872 case WPA_CIPHER_AES_CCM:
3873 gval = AES_ENABLED;
3874 break;
3875 default:
3876 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003877 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003878 goto exit;
3879 }
3880
3881 offset++;
3882 /* walk thru unicast cipher list and pick up what we recognize */
3883 count = data[offset] + (data[offset + 1] << 8);
3884 offset += WPA_IE_SUITE_COUNT_LEN;
3885 /* Check for unicast suite(s) */
3886 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3887 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003888 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003889 goto exit;
3890 }
3891 for (i = 0; i < count; i++) {
3892 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3893 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003894 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003895 goto exit;
3896 }
3897 offset += TLV_OUI_LEN;
3898 switch (data[offset]) {
3899 case WPA_CIPHER_NONE:
3900 break;
3901 case WPA_CIPHER_WEP_40:
3902 case WPA_CIPHER_WEP_104:
3903 pval |= WEP_ENABLED;
3904 break;
3905 case WPA_CIPHER_TKIP:
3906 pval |= TKIP_ENABLED;
3907 break;
3908 case WPA_CIPHER_AES_CCM:
3909 pval |= AES_ENABLED;
3910 break;
3911 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003912 brcmf_err("Ivalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003913 }
3914 offset++;
3915 }
3916 /* walk thru auth management suite list and pick up what we recognize */
3917 count = data[offset] + (data[offset + 1] << 8);
3918 offset += WPA_IE_SUITE_COUNT_LEN;
3919 /* Check for auth key management suite(s) */
3920 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3921 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003922 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003923 goto exit;
3924 }
3925 for (i = 0; i < count; i++) {
3926 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3927 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003928 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003929 goto exit;
3930 }
3931 offset += TLV_OUI_LEN;
3932 switch (data[offset]) {
3933 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01003934 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003935 wpa_auth |= WPA_AUTH_NONE;
3936 break;
3937 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01003938 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003939 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
3940 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
3941 break;
3942 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01003943 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003944 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
3945 (wpa_auth |= WPA_AUTH_PSK);
3946 break;
3947 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01003948 brcmf_err("Ivalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003949 }
3950 offset++;
3951 }
3952
3953 if (is_rsn_ie) {
3954 wme_bss_disable = 1;
3955 if ((offset + RSN_CAP_LEN) <= len) {
3956 rsn_cap = data[offset] + (data[offset + 1] << 8);
3957 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
3958 wme_bss_disable = 0;
3959 }
3960 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07003961 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07003962 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02003963 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003964 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003965 goto exit;
3966 }
3967 }
3968 /* FOR WPS , set SES_OW_ENABLED */
3969 wsec = (pval | gval | SES_OW_ENABLED);
3970
3971 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003972 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003973 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003974 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003975 goto exit;
3976 }
3977 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003978 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02003979 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003980 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003981 goto exit;
3982 }
3983 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003984 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02003985 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003986 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003987 goto exit;
3988 }
3989
3990exit:
3991 return err;
3992}
3993
3994static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08003995brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02003996 struct parsed_vndr_ies *vndr_ies)
3997{
Hante Meuleman1a873342012-09-27 14:17:54 +02003998 struct brcmf_vs_tlv *vndrie;
3999 struct brcmf_tlv *ie;
4000 struct parsed_vndr_ie_info *parsed_info;
4001 s32 remaining_len;
4002
4003 remaining_len = (s32)vndr_ie_len;
4004 memset(vndr_ies, 0, sizeof(*vndr_ies));
4005
4006 ie = (struct brcmf_tlv *)vndr_ie_buf;
4007 while (ie) {
4008 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
4009 goto next;
4010 vndrie = (struct brcmf_vs_tlv *)ie;
4011 /* len should be bigger than OUI length + one */
4012 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004013 brcmf_err("invalid vndr ie. length is too small %d\n",
4014 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004015 goto next;
4016 }
4017 /* if wpa or wme ie, do not add ie */
4018 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
4019 ((vndrie->oui_type == WPA_OUI_TYPE) ||
4020 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004021 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004022 goto next;
4023 }
4024
4025 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
4026
4027 /* save vndr ie information */
4028 parsed_info->ie_ptr = (char *)vndrie;
4029 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
4030 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
4031
4032 vndr_ies->count++;
4033
Arend van Sprield96b8012012-12-05 15:26:02 +01004034 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
4035 parsed_info->vndrie.oui[0],
4036 parsed_info->vndrie.oui[1],
4037 parsed_info->vndrie.oui[2],
4038 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02004039
Arend van Spriel9f440b72013-02-08 15:53:36 +01004040 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02004041 break;
4042next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004043 remaining_len -= (ie->len + TLV_HDR_LEN);
4044 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02004045 ie = NULL;
4046 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004047 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
4048 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02004049 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004050 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004051}
4052
4053static u32
4054brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
4055{
4056
Hante Meuleman1a873342012-09-27 14:17:54 +02004057 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
4058 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
4059
Vaishali Thakkar362126c2015-01-16 21:36:14 +05304060 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004061
Vaishali Thakkar362126c2015-01-16 21:36:14 +05304062 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004063
4064 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
4065
4066 return ie_len + VNDR_IE_HDR_SIZE;
4067}
4068
Arend van Spriel1332e262012-11-05 16:22:18 -08004069s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
4070 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02004071{
Arend van Spriel1332e262012-11-05 16:22:18 -08004072 struct brcmf_if *ifp;
4073 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004074 s32 err = 0;
4075 u8 *iovar_ie_buf;
4076 u8 *curr_ie_buf;
4077 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004078 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07004079 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004080 u32 del_add_ie_buf_len = 0;
4081 u32 total_ie_buf_len = 0;
4082 u32 parsed_ie_buf_len = 0;
4083 struct parsed_vndr_ies old_vndr_ies;
4084 struct parsed_vndr_ies new_vndr_ies;
4085 struct parsed_vndr_ie_info *vndrie_info;
4086 s32 i;
4087 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004088 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004089
Arend van Spriel1332e262012-11-05 16:22:18 -08004090 if (!vif)
4091 return -ENODEV;
4092 ifp = vif->ifp;
4093 saved_ie = &vif->saved_ie;
4094
Hante Meuleman37a869e2015-10-29 20:33:17 +01004095 brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
4096 pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02004097 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4098 if (!iovar_ie_buf)
4099 return -ENOMEM;
4100 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01004101 switch (pktflag) {
4102 case BRCMF_VNDR_IE_PRBREQ_FLAG:
4103 mgmt_ie_buf = saved_ie->probe_req_ie;
4104 mgmt_ie_len = &saved_ie->probe_req_ie_len;
4105 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
4106 break;
4107 case BRCMF_VNDR_IE_PRBRSP_FLAG:
4108 mgmt_ie_buf = saved_ie->probe_res_ie;
4109 mgmt_ie_len = &saved_ie->probe_res_ie_len;
4110 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
4111 break;
4112 case BRCMF_VNDR_IE_BEACON_FLAG:
4113 mgmt_ie_buf = saved_ie->beacon_ie;
4114 mgmt_ie_len = &saved_ie->beacon_ie_len;
4115 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
4116 break;
4117 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
4118 mgmt_ie_buf = saved_ie->assoc_req_ie;
4119 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
4120 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
4121 break;
4122 default:
4123 err = -EPERM;
4124 brcmf_err("not suitable type\n");
4125 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004126 }
4127
4128 if (vndr_ie_len > mgmt_ie_buf_len) {
4129 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004130 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004131 goto exit;
4132 }
4133
4134 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
4135 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
4136 ptr = curr_ie_buf;
4137 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
4138 for (i = 0; i < new_vndr_ies.count; i++) {
4139 vndrie_info = &new_vndr_ies.ie_info[i];
4140 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
4141 vndrie_info->ie_len);
4142 parsed_ie_buf_len += vndrie_info->ie_len;
4143 }
4144 }
4145
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004146 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004147 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
4148 (memcmp(mgmt_ie_buf, curr_ie_buf,
4149 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004150 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004151 goto exit;
4152 }
4153
4154 /* parse old vndr_ie */
4155 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
4156
4157 /* make a command to delete old ie */
4158 for (i = 0; i < old_vndr_ies.count; i++) {
4159 vndrie_info = &old_vndr_ies.ie_info[i];
4160
Arend van Sprield96b8012012-12-05 15:26:02 +01004161 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4162 vndrie_info->vndrie.id,
4163 vndrie_info->vndrie.len,
4164 vndrie_info->vndrie.oui[0],
4165 vndrie_info->vndrie.oui[1],
4166 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004167
4168 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4169 vndrie_info->ie_ptr,
4170 vndrie_info->ie_len,
4171 "del");
4172 curr_ie_buf += del_add_ie_buf_len;
4173 total_ie_buf_len += del_add_ie_buf_len;
4174 }
4175 }
4176
4177 *mgmt_ie_len = 0;
4178 /* Add if there is any extra IE */
4179 if (mgmt_ie_buf && parsed_ie_buf_len) {
4180 ptr = mgmt_ie_buf;
4181
4182 remained_buf_len = mgmt_ie_buf_len;
4183
4184 /* make a command to add new ie */
4185 for (i = 0; i < new_vndr_ies.count; i++) {
4186 vndrie_info = &new_vndr_ies.ie_info[i];
4187
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004188 /* verify remained buf size before copy data */
4189 if (remained_buf_len < (vndrie_info->vndrie.len +
4190 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004191 brcmf_err("no space in mgmt_ie_buf: len left %d",
4192 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004193 break;
4194 }
4195 remained_buf_len -= (vndrie_info->ie_len +
4196 VNDR_IE_VSIE_OFFSET);
4197
Arend van Sprield96b8012012-12-05 15:26:02 +01004198 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4199 vndrie_info->vndrie.id,
4200 vndrie_info->vndrie.len,
4201 vndrie_info->vndrie.oui[0],
4202 vndrie_info->vndrie.oui[1],
4203 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004204
4205 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4206 vndrie_info->ie_ptr,
4207 vndrie_info->ie_len,
4208 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02004209
4210 /* save the parsed IE in wl struct */
4211 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4212 vndrie_info->ie_len);
4213 *mgmt_ie_len += vndrie_info->ie_len;
4214
4215 curr_ie_buf += del_add_ie_buf_len;
4216 total_ie_buf_len += del_add_ie_buf_len;
4217 }
4218 }
4219 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004220 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004221 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004222 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004223 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004224 }
4225
4226exit:
4227 kfree(iovar_ie_buf);
4228 return err;
4229}
4230
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004231s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4232{
4233 s32 pktflags[] = {
4234 BRCMF_VNDR_IE_PRBREQ_FLAG,
4235 BRCMF_VNDR_IE_PRBRSP_FLAG,
4236 BRCMF_VNDR_IE_BEACON_FLAG
4237 };
4238 int i;
4239
4240 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4241 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4242
4243 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4244 return 0;
4245}
4246
Hante Meuleman1a873342012-09-27 14:17:54 +02004247static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004248brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4249 struct cfg80211_beacon_data *beacon)
4250{
4251 s32 err;
4252
4253 /* Set Beacon IEs to FW */
4254 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4255 beacon->tail, beacon->tail_len);
4256 if (err) {
4257 brcmf_err("Set Beacon IE Failed\n");
4258 return err;
4259 }
4260 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4261
4262 /* Set Probe Response IEs to FW */
4263 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4264 beacon->proberesp_ies,
4265 beacon->proberesp_ies_len);
4266 if (err)
4267 brcmf_err("Set Probe Resp IE Failed\n");
4268 else
4269 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4270
4271 return err;
4272}
4273
4274static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004275brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4276 struct cfg80211_ap_settings *settings)
4277{
4278 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004279 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004280 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004281 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004282 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004283 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004284 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004285 const struct brcmf_tlv *rsn_ie;
4286 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004287 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004288 enum nl80211_iftype dev_role;
4289 struct brcmf_fil_bss_enable_le bss_enable;
Arend van Spriel06c01582014-05-12 10:47:37 +02004290 u16 chanspec;
Hante Meulemana44aa402014-12-03 21:05:33 +01004291 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004292 int is_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004293
Arend van Spriel06c01582014-05-12 10:47:37 +02004294 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4295 settings->chandef.chan->hw_value,
4296 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004297 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004298 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4299 settings->ssid, settings->ssid_len, settings->auth_type,
4300 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004301 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004302 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004303
Arend van Spriel98027762014-12-21 12:43:53 +01004304 /* store current 11d setting */
4305 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, &ifp->vif->is_11d);
4306 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4307 settings->beacon.tail_len,
4308 WLAN_EID_COUNTRY);
4309 is_11d = country_ie ? 1 : 0;
4310
Hante Meuleman1a873342012-09-27 14:17:54 +02004311 memset(&ssid_le, 0, sizeof(ssid_le));
4312 if (settings->ssid == NULL || settings->ssid_len == 0) {
4313 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4314 ssid_ie = brcmf_parse_tlvs(
4315 (u8 *)&settings->beacon.head[ie_offset],
4316 settings->beacon.head_len - ie_offset,
4317 WLAN_EID_SSID);
4318 if (!ssid_ie)
4319 return -EINVAL;
4320
4321 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4322 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004323 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004324 } else {
4325 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4326 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4327 }
4328
Hante Meulemana44aa402014-12-03 21:05:33 +01004329 if (!mbss) {
4330 brcmf_set_mpc(ifp, 0);
4331 brcmf_configure_arp_offload(ifp, false);
4332 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004333
4334 /* find the RSN_IE */
4335 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4336 settings->beacon.tail_len, WLAN_EID_RSN);
4337
4338 /* find the WPA_IE */
4339 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4340 settings->beacon.tail_len);
4341
Hante Meuleman1a873342012-09-27 14:17:54 +02004342 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004343 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004344 if (wpa_ie != NULL) {
4345 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004346 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004347 if (err < 0)
4348 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004349 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004350 struct brcmf_vs_tlv *tmp_ie;
4351
4352 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4353
Hante Meuleman1a873342012-09-27 14:17:54 +02004354 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004355 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004356 if (err < 0)
4357 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004358 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004359 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004360 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004361 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004362 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004363
Hante Meulemana0f07952013-02-08 15:53:47 +01004364 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Hante Meuleman1a873342012-09-27 14:17:54 +02004365
Hante Meulemana44aa402014-12-03 21:05:33 +01004366 if (!mbss) {
4367 chanspec = chandef_to_chanspec(&cfg->d11inf,
4368 &settings->chandef);
4369 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004370 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004371 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4372 chanspec, err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004373 goto exit;
4374 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004375
Arend van Spriel98027762014-12-21 12:43:53 +01004376 if (is_11d != ifp->vif->is_11d) {
4377 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4378 is_11d);
4379 if (err < 0) {
4380 brcmf_err("Regulatory Set Error, %d\n", err);
4381 goto exit;
4382 }
4383 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004384 if (settings->beacon_interval) {
4385 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4386 settings->beacon_interval);
4387 if (err < 0) {
4388 brcmf_err("Beacon Interval Set Error, %d\n",
4389 err);
4390 goto exit;
4391 }
4392 }
4393 if (settings->dtim_period) {
4394 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4395 settings->dtim_period);
4396 if (err < 0) {
4397 brcmf_err("DTIM Interval Set Error, %d\n", err);
4398 goto exit;
4399 }
4400 }
4401
Hante Meuleman8abffd82015-10-29 20:33:16 +01004402 if ((dev_role == NL80211_IFTYPE_AP) &&
4403 ((ifp->ifidx == 0) ||
4404 !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004405 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4406 if (err < 0) {
4407 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4408 goto exit;
4409 }
4410 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4411 }
4412
4413 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004414 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004415 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004416 goto exit;
4417 }
Arend van Spriel98027762014-12-21 12:43:53 +01004418 } else if (WARN_ON(is_11d != ifp->vif->is_11d)) {
4419 /* Multiple-BSS should use same 11d configuration */
4420 err = -EINVAL;
4421 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004422 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004423 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004424 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4425 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4426
Hante Meulemana0f07952013-02-08 15:53:47 +01004427 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4428 if (err < 0) {
4429 brcmf_err("setting AP mode failed %d\n", err);
4430 goto exit;
4431 }
4432 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4433 if (err < 0) {
4434 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4435 goto exit;
4436 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004437 /* On DOWN the firmware removes the WEP keys, reconfigure
4438 * them if they were set.
4439 */
4440 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004441
4442 memset(&join_params, 0, sizeof(join_params));
4443 /* join parameters starts with ssid */
4444 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4445 /* create softap */
4446 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4447 &join_params, sizeof(join_params));
4448 if (err < 0) {
4449 brcmf_err("SET SSID error (%d)\n", err);
4450 goto exit;
4451 }
4452 brcmf_dbg(TRACE, "AP mode configuration complete\n");
4453 } else {
4454 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4455 sizeof(ssid_le));
4456 if (err < 0) {
4457 brcmf_err("setting ssid failed %d\n", err);
4458 goto exit;
4459 }
Hante Meuleman37a869e2015-10-29 20:33:17 +01004460 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meulemana0f07952013-02-08 15:53:47 +01004461 bss_enable.enable = cpu_to_le32(1);
4462 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4463 sizeof(bss_enable));
4464 if (err < 0) {
4465 brcmf_err("bss_enable config failed %d\n", err);
4466 goto exit;
4467 }
4468
4469 brcmf_dbg(TRACE, "GO mode configuration complete\n");
4470 }
Arend van Sprielc1179032012-10-22 13:55:33 -07004471 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004472 brcmf_net_setcarrier(ifp, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004473
4474exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004475 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004476 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004477 brcmf_configure_arp_offload(ifp, true);
4478 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004479 return err;
4480}
4481
4482static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4483{
Arend van Sprielc1179032012-10-22 13:55:33 -07004484 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004485 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004486 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004487 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004488
Arend van Sprield96b8012012-12-05 15:26:02 +01004489 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004490
Hante Meuleman426d0a52013-02-08 15:53:53 +01004491 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004492 /* Due to most likely deauths outstanding we sleep */
4493 /* first to make sure they get processed by fw. */
4494 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004495
Hante Meulemana44aa402014-12-03 21:05:33 +01004496 if (ifp->vif->mbss) {
4497 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4498 return err;
4499 }
4500
Hante Meuleman5c33a942013-04-02 21:06:18 +02004501 memset(&join_params, 0, sizeof(join_params));
4502 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4503 &join_params, sizeof(join_params));
4504 if (err < 0)
4505 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004506 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004507 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004508 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004509 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4510 if (err < 0)
4511 brcmf_err("setting AP mode failed %d\n", err);
4512 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 0);
4513 if (err < 0)
4514 brcmf_err("setting INFRA mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004515 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4516 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Arend van Spriel98027762014-12-21 12:43:53 +01004517 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4518 ifp->vif->is_11d);
4519 if (err < 0)
4520 brcmf_err("restoring REGULATORY setting failed %d\n",
4521 err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004522 /* Bring device back up so it can be used again */
4523 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4524 if (err < 0)
4525 brcmf_err("BRCMF_C_UP error %d\n", err);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004526 } else {
Hante Meuleman37a869e2015-10-29 20:33:17 +01004527 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004528 bss_enable.enable = cpu_to_le32(0);
4529 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4530 sizeof(bss_enable));
4531 if (err < 0)
4532 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004533 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004534 brcmf_set_mpc(ifp, 1);
Hante Meulemanb3657452013-05-27 21:09:53 +02004535 brcmf_configure_arp_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004536 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004537 brcmf_net_setcarrier(ifp, false);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004538
Hante Meuleman1a873342012-09-27 14:17:54 +02004539 return err;
4540}
4541
Hante Meulemana0f07952013-02-08 15:53:47 +01004542static s32
4543brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4544 struct cfg80211_beacon_data *info)
4545{
Hante Meulemana0f07952013-02-08 15:53:47 +01004546 struct brcmf_if *ifp = netdev_priv(ndev);
4547 s32 err;
4548
4549 brcmf_dbg(TRACE, "Enter\n");
4550
Hante Meulemana0f07952013-02-08 15:53:47 +01004551 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4552
4553 return err;
4554}
4555
Hante Meuleman1a873342012-09-27 14:17:54 +02004556static int
4557brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004558 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004559{
Hante Meulemana0f07952013-02-08 15:53:47 +01004560 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004561 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004562 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004563 s32 err;
4564
Jouni Malinen89c771e2014-10-10 20:52:40 +03004565 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004566 return -EFAULT;
4567
Jouni Malinen89c771e2014-10-10 20:52:40 +03004568 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004569
Hante Meulemana0f07952013-02-08 15:53:47 +01004570 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4571 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004572 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004573 return -EIO;
4574
Jouni Malinen89c771e2014-10-10 20:52:40 +03004575 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004576 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004577 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004578 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004579 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004580 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004581
Arend van Sprield96b8012012-12-05 15:26:02 +01004582 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004583 return err;
4584}
4585
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004586static int
4587brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4588 const u8 *mac, struct station_parameters *params)
4589{
4590 struct brcmf_if *ifp = netdev_priv(ndev);
4591 s32 err;
4592
4593 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4594 params->sta_flags_mask, params->sta_flags_set);
4595
4596 /* Ignore all 00 MAC */
4597 if (is_zero_ether_addr(mac))
4598 return 0;
4599
4600 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4601 return 0;
4602
4603 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4604 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4605 (void *)mac, ETH_ALEN);
4606 else
4607 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4608 (void *)mac, ETH_ALEN);
4609 if (err < 0)
4610 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4611
4612 return err;
4613}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004614
4615static void
4616brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4617 struct wireless_dev *wdev,
4618 u16 frame_type, bool reg)
4619{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004620 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004621 u16 mgmt_type;
4622
4623 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4624
4625 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004626 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004627 if (reg)
4628 vif->mgmt_rx_reg |= BIT(mgmt_type);
4629 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004630 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004631}
4632
4633
4634static int
4635brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004636 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004637{
4638 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004639 struct ieee80211_channel *chan = params->chan;
4640 const u8 *buf = params->buf;
4641 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004642 const struct ieee80211_mgmt *mgmt;
4643 struct brcmf_cfg80211_vif *vif;
4644 s32 err = 0;
4645 s32 ie_offset;
4646 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004647 struct brcmf_fil_action_frame_le *action_frame;
4648 struct brcmf_fil_af_params_le *af_params;
4649 bool ack;
4650 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004651 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004652
4653 brcmf_dbg(TRACE, "Enter\n");
4654
4655 *cookie = 0;
4656
4657 mgmt = (const struct ieee80211_mgmt *)buf;
4658
Hante Meulemana0f07952013-02-08 15:53:47 +01004659 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4660 brcmf_err("Driver only allows MGMT packet type\n");
4661 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004662 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004663
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004664 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4665
Hante Meulemana0f07952013-02-08 15:53:47 +01004666 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4667 /* Right now the only reason to get a probe response */
4668 /* is for p2p listen response or for p2p GO from */
4669 /* wpa_supplicant. Unfortunately the probe is send */
4670 /* on primary ndev, while dongle wants it on the p2p */
4671 /* vif. Since this is only reason for a probe */
4672 /* response to be sent, the vif is taken from cfg. */
4673 /* If ever desired to send proberesp for non p2p */
4674 /* response then data should be checked for */
4675 /* "DIRECT-". Note in future supplicant will take */
4676 /* dedicated p2p wdev to do this and then this 'hack'*/
4677 /* is not needed anymore. */
4678 ie_offset = DOT11_MGMT_HDR_LEN +
4679 DOT11_BCN_PRB_FIXED_LEN;
4680 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004681 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4682 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4683 err = brcmf_vif_set_mgmt_ie(vif,
4684 BRCMF_VNDR_IE_PRBRSP_FLAG,
4685 &buf[ie_offset],
4686 ie_len);
4687 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4688 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004689 } else if (ieee80211_is_action(mgmt->frame_control)) {
4690 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4691 if (af_params == NULL) {
4692 brcmf_err("unable to allocate frame\n");
4693 err = -ENOMEM;
4694 goto exit;
4695 }
4696 action_frame = &af_params->action_frame;
4697 /* Add the packet Id */
4698 action_frame->packet_id = cpu_to_le32(*cookie);
4699 /* Add BSSID */
4700 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4701 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4702 /* Add the length exepted for 802.11 header */
4703 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004704 /* Add the channel. Use the one specified as parameter if any or
4705 * the current one (got from the firmware) otherwise
4706 */
4707 if (chan)
4708 freq = chan->center_freq;
4709 else
4710 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4711 &freq);
4712 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004713 af_params->channel = cpu_to_le32(chan_nr);
4714
4715 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4716 le16_to_cpu(action_frame->len));
4717
4718 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004719 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004720
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004721 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004722 af_params);
4723
4724 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4725 GFP_KERNEL);
4726 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004727 } else {
4728 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
4729 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%Zu\n", len);
4730 }
4731
Hante Meuleman18e2f612013-02-08 15:53:49 +01004732exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004733 return err;
4734}
4735
4736
4737static int
4738brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4739 struct wireless_dev *wdev,
4740 u64 cookie)
4741{
4742 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4743 struct brcmf_cfg80211_vif *vif;
4744 int err = 0;
4745
4746 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4747
4748 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4749 if (vif == NULL) {
4750 brcmf_err("No p2p device available for probe response\n");
4751 err = -ENODEV;
4752 goto exit;
4753 }
4754 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4755exit:
4756 return err;
4757}
4758
Piotr Haber61730d42013-04-23 12:53:12 +02004759static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
4760 struct wireless_dev *wdev,
4761 enum nl80211_crit_proto_id proto,
4762 u16 duration)
4763{
4764 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4765 struct brcmf_cfg80211_vif *vif;
4766
4767 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4768
4769 /* only DHCP support for now */
4770 if (proto != NL80211_CRIT_PROTO_DHCP)
4771 return -EINVAL;
4772
4773 /* suppress and abort scanning */
4774 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4775 brcmf_abort_scanning(cfg);
4776
4777 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
4778}
4779
4780static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
4781 struct wireless_dev *wdev)
4782{
4783 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4784 struct brcmf_cfg80211_vif *vif;
4785
4786 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4787
4788 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
4789 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
4790}
4791
Hante Meuleman70b7d942014-07-30 13:20:07 +02004792static s32
4793brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
4794 const struct brcmf_event_msg *e, void *data)
4795{
4796 switch (e->reason) {
4797 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
4798 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
4799 break;
4800 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
4801 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
4802 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4803 break;
4804 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
4805 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
4806 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
4807 break;
4808 }
4809
4810 return 0;
4811}
4812
Arend van Spriel89c2f382013-08-10 12:27:25 +02004813static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
4814{
4815 int ret;
4816
4817 switch (oper) {
4818 case NL80211_TDLS_DISCOVERY_REQ:
4819 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
4820 break;
4821 case NL80211_TDLS_SETUP:
4822 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
4823 break;
4824 case NL80211_TDLS_TEARDOWN:
4825 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
4826 break;
4827 default:
4828 brcmf_err("unsupported operation: %d\n", oper);
4829 ret = -EOPNOTSUPP;
4830 }
4831 return ret;
4832}
4833
4834static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02004835 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004836 enum nl80211_tdls_operation oper)
4837{
4838 struct brcmf_if *ifp;
4839 struct brcmf_tdls_iovar_le info;
4840 int ret = 0;
4841
4842 ret = brcmf_convert_nl80211_tdls_oper(oper);
4843 if (ret < 0)
4844 return ret;
4845
4846 ifp = netdev_priv(ndev);
4847 memset(&info, 0, sizeof(info));
4848 info.mode = (u8)ret;
4849 if (peer)
4850 memcpy(info.ea, peer, ETH_ALEN);
4851
4852 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
4853 &info, sizeof(info));
4854 if (ret < 0)
4855 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
4856
4857 return ret;
4858}
4859
Arend van Spriel5b435de2011-10-05 13:19:03 +02004860static struct cfg80211_ops wl_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01004861 .add_virtual_intf = brcmf_cfg80211_add_iface,
4862 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004863 .change_virtual_intf = brcmf_cfg80211_change_iface,
4864 .scan = brcmf_cfg80211_scan,
4865 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
4866 .join_ibss = brcmf_cfg80211_join_ibss,
4867 .leave_ibss = brcmf_cfg80211_leave_ibss,
4868 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02004869 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004870 .set_tx_power = brcmf_cfg80211_set_tx_power,
4871 .get_tx_power = brcmf_cfg80211_get_tx_power,
4872 .add_key = brcmf_cfg80211_add_key,
4873 .del_key = brcmf_cfg80211_del_key,
4874 .get_key = brcmf_cfg80211_get_key,
4875 .set_default_key = brcmf_cfg80211_config_default_key,
4876 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
4877 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004878 .connect = brcmf_cfg80211_connect,
4879 .disconnect = brcmf_cfg80211_disconnect,
4880 .suspend = brcmf_cfg80211_suspend,
4881 .resume = brcmf_cfg80211_resume,
4882 .set_pmksa = brcmf_cfg80211_set_pmksa,
4883 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02004884 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02004885 .start_ap = brcmf_cfg80211_start_ap,
4886 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01004887 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02004888 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004889 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02004890 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
4891 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004892 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
4893 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
4894 .remain_on_channel = brcmf_p2p_remain_on_channel,
4895 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02004896 .start_p2p_device = brcmf_p2p_start_device,
4897 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02004898 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
4899 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02004900 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004901};
4902
Arend van Spriel3eacf862012-10-22 13:55:30 -07004903struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Arend van Spriel9f440b72013-02-08 15:53:36 +01004904 enum nl80211_iftype type,
4905 bool pm_block)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004906{
Hante Meulemana44aa402014-12-03 21:05:33 +01004907 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004908 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01004909 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004910
Arend van Spriel33a6b152013-02-08 15:53:39 +01004911 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01004912 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07004913 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
4914 if (!vif)
4915 return ERR_PTR(-ENOMEM);
4916
4917 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01004918 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004919
Arend van Spriel3eacf862012-10-22 13:55:30 -07004920 vif->pm_block = pm_block;
Arend van Spriel3eacf862012-10-22 13:55:30 -07004921
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07004922 brcmf_init_prof(&vif->profile);
4923
Hante Meulemana44aa402014-12-03 21:05:33 +01004924 if (type == NL80211_IFTYPE_AP) {
4925 mbss = false;
4926 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
4927 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
4928 mbss = true;
4929 break;
4930 }
4931 }
4932 vif->mbss = mbss;
4933 }
4934
Arend van Spriel3eacf862012-10-22 13:55:30 -07004935 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004936 return vif;
4937}
4938
Arend van Spriel427dec52014-01-06 12:40:47 +01004939void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07004940{
Arend van Spriel3eacf862012-10-22 13:55:30 -07004941 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07004942 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02004943}
4944
Arend van Spriel9df4d542014-01-06 12:40:49 +01004945void brcmf_cfg80211_free_netdev(struct net_device *ndev)
4946{
4947 struct brcmf_cfg80211_vif *vif;
4948 struct brcmf_if *ifp;
4949
4950 ifp = netdev_priv(ndev);
4951 vif = ifp->vif;
4952
Arend van Spriel95ef1232015-08-26 22:15:04 +02004953 if (vif)
4954 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01004955 free_netdev(ndev);
4956}
4957
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004958static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004959{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004960 u32 event = e->event_code;
4961 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004962
4963 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004964 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004965 return true;
4966 }
4967
4968 return false;
4969}
4970
Arend van Spriel903e0ee2012-11-28 21:44:11 +01004971static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02004972{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004973 u32 event = e->event_code;
4974 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004975
Hante Meuleman68ca3952014-02-25 20:30:26 +01004976 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
4977 (event == BRCMF_E_DISASSOC_IND) ||
4978 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01004979 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004980 return true;
4981 }
4982 return false;
4983}
4984
Arend van Spriel27a68fe2012-09-27 14:17:55 +02004985static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02004986 const struct brcmf_event_msg *e)
4987{
Arend van Spriel5c36b992012-11-14 18:46:05 -08004988 u32 event = e->event_code;
4989 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02004990
4991 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004992 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
4993 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004994 return true;
4995 }
4996
4997 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01004998 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02004999 return true;
5000 }
5001
5002 return false;
5003}
5004
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005005static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005006{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005007 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005008
5009 kfree(conn_info->req_ie);
5010 conn_info->req_ie = NULL;
5011 conn_info->req_ie_len = 0;
5012 kfree(conn_info->resp_ie);
5013 conn_info->resp_ie = NULL;
5014 conn_info->resp_ie_len = 0;
5015}
5016
Hante Meuleman89286dc2013-02-08 15:53:46 +01005017static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
5018 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005019{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005020 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005021 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005022 u32 req_len;
5023 u32 resp_len;
5024 s32 err = 0;
5025
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005026 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005027
Arend van Sprielac24be62012-10-22 10:36:23 -07005028 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
5029 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005030 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005031 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005032 return err;
5033 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005034 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005035 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005036 req_len = le32_to_cpu(assoc_info->req_len);
5037 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005038 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005039 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005040 cfg->extra_buf,
5041 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005042 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005043 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005044 return err;
5045 }
5046 conn_info->req_ie_len = req_len;
5047 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005048 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005049 GFP_KERNEL);
5050 } else {
5051 conn_info->req_ie_len = 0;
5052 conn_info->req_ie = NULL;
5053 }
5054 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005055 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005056 cfg->extra_buf,
5057 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005058 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005059 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005060 return err;
5061 }
5062 conn_info->resp_ie_len = resp_len;
5063 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005064 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005065 GFP_KERNEL);
5066 } else {
5067 conn_info->resp_ie_len = 0;
5068 conn_info->resp_ie = NULL;
5069 }
Arend van Spriel16886732012-12-05 15:26:04 +01005070 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
5071 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005072
5073 return err;
5074}
5075
5076static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005077brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005078 struct net_device *ndev,
5079 const struct brcmf_event_msg *e)
5080{
Arend van Sprielc1179032012-10-22 13:55:33 -07005081 struct brcmf_if *ifp = netdev_priv(ndev);
5082 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005083 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
5084 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07005085 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005086 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07005087 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02005088 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005089 u32 freq;
5090 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07005091 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005092
Arend van Sprield96b8012012-12-05 15:26:02 +01005093 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005094
Hante Meuleman89286dc2013-02-08 15:53:46 +01005095 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005096 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005097 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005098
Franky Lina180b832012-10-10 11:13:09 -07005099 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
5100 if (buf == NULL) {
5101 err = -ENOMEM;
5102 goto done;
5103 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005104
Franky Lina180b832012-10-10 11:13:09 -07005105 /* data sent to dongle has to be little endian */
5106 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07005107 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07005108 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07005109
5110 if (err)
5111 goto done;
5112
5113 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02005114 ch.chspec = le16_to_cpu(bi->chanspec);
5115 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005116
Franky Lin83cf17a2013-04-11 13:28:50 +02005117 if (ch.band == BRCMU_CHAN_BAND_2G)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005118 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5119 else
5120 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5121
Franky Lin83cf17a2013-04-11 13:28:50 +02005122 freq = ieee80211_channel_to_frequency(ch.chnum, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005123 notify_channel = ieee80211_get_channel(wiphy, freq);
5124
Franky Lina180b832012-10-10 11:13:09 -07005125done:
5126 kfree(buf);
Arend van Spriel06bb1232012-09-27 14:17:56 +02005127 cfg80211_roamed(ndev, notify_channel, (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005128 conn_info->req_ie, conn_info->req_ie_len,
5129 conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005130 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005131
Arend van Sprielc1179032012-10-22 13:55:33 -07005132 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01005133 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005134 return err;
5135}
5136
5137static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005138brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005139 struct net_device *ndev, const struct brcmf_event_msg *e,
5140 bool completed)
5141{
Arend van Sprielc1179032012-10-22 13:55:33 -07005142 struct brcmf_if *ifp = netdev_priv(ndev);
5143 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005144 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005145
Arend van Sprield96b8012012-12-05 15:26:02 +01005146 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005147
Arend van Sprielc1179032012-10-22 13:55:33 -07005148 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5149 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005150 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01005151 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005152 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005153 brcmf_update_bss_info(cfg, ifp);
5154 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5155 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005156 }
5157 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02005158 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005159 conn_info->req_ie,
5160 conn_info->req_ie_len,
5161 conn_info->resp_ie,
5162 conn_info->resp_ie_len,
5163 completed ? WLAN_STATUS_SUCCESS :
5164 WLAN_STATUS_AUTH_TIMEOUT,
5165 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005166 brcmf_dbg(CONN, "Report connect result - connection %s\n",
5167 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005168 }
Arend van Sprield96b8012012-12-05 15:26:02 +01005169 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005170 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005171}
5172
5173static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005174brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02005175 struct net_device *ndev,
5176 const struct brcmf_event_msg *e, void *data)
5177{
Hante Meulemana44aa402014-12-03 21:05:33 +01005178 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman7ee29602013-02-06 18:40:43 +01005179 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005180 u32 event = e->event_code;
5181 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02005182 struct station_info sinfo;
5183
Arend van Spriel16886732012-12-05 15:26:04 +01005184 brcmf_dbg(CONN, "event %d, reason %d\n", event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005185 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
5186 ndev != cfg_to_ndev(cfg)) {
5187 brcmf_dbg(CONN, "AP mode link down\n");
5188 complete(&cfg->vif_disabled);
Hante Meulemana44aa402014-12-03 21:05:33 +01005189 if (ifp->vif->mbss)
Arend van Sprielee6e3a32015-08-26 22:14:55 +02005190 brcmf_remove_interface(ifp);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005191 return 0;
5192 }
Hante Meuleman1a873342012-09-27 14:17:54 +02005193
Hante Meuleman1a873342012-09-27 14:17:54 +02005194 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01005195 (reason == BRCMF_E_STATUS_SUCCESS)) {
5196 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02005197 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005198 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02005199 return -EINVAL;
5200 }
5201 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005202 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02005203 generation++;
5204 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005205 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005206 } else if ((event == BRCMF_E_DISASSOC_IND) ||
5207 (event == BRCMF_E_DEAUTH_IND) ||
5208 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01005209 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005210 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01005211 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02005212}
5213
5214static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005215brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005216 const struct brcmf_event_msg *e, void *data)
5217{
Arend van Spriel19937322012-11-05 16:22:32 -08005218 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5219 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005220 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005221 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005222 s32 err = 0;
5223
Hante Meuleman8851cce2014-07-30 13:20:02 +02005224 if ((e->event_code == BRCMF_E_DEAUTH) ||
5225 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5226 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5227 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5228 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5229 }
5230
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005231 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005232 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005233 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005234 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005235 if (brcmf_is_ibssmode(ifp->vif)) {
Hante Meulemanb0a79082015-12-10 13:43:07 +01005236 brcmf_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005237 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005238 memcpy(profile->bssid, e->addr, ETH_ALEN);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005239 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005240 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5241 &ifp->vif->sme_state);
5242 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5243 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005244 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005245 brcmf_bss_connect_done(cfg, ndev, e, true);
Hante Meuleman92121e62015-10-08 20:33:21 +02005246 brcmf_net_setcarrier(ifp, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005247 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005248 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005249 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005250 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005251 }
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01005252 brcmf_link_down(ifp->vif, brcmf_map_fw_linkdown_reason(e));
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005253 brcmf_init_prof(ndev_to_prof(ndev));
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005254 if (ndev != cfg_to_ndev(cfg))
5255 complete(&cfg->vif_disabled);
Hante Meuleman92121e62015-10-08 20:33:21 +02005256 brcmf_net_setcarrier(ifp, false);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005257 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005258 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005259 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5260 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005261 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005262 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005263 }
5264
5265 return err;
5266}
5267
5268static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005269brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005270 const struct brcmf_event_msg *e, void *data)
5271{
Arend van Spriel19937322012-11-05 16:22:32 -08005272 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005273 u32 event = e->event_code;
5274 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005275
5276 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005277 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005278 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005279 else
Arend van Spriel19937322012-11-05 16:22:32 -08005280 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005281 }
5282
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005283 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005284}
5285
5286static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005287brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005288 const struct brcmf_event_msg *e, void *data)
5289{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005290 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005291 enum nl80211_key_type key_type;
5292
5293 if (flags & BRCMF_EVENT_MSG_GROUP)
5294 key_type = NL80211_KEYTYPE_GROUP;
5295 else
5296 key_type = NL80211_KEYTYPE_PAIRWISE;
5297
Arend van Spriel19937322012-11-05 16:22:32 -08005298 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005299 NULL, GFP_KERNEL);
5300
5301 return 0;
5302}
5303
Arend van Sprield3c0b632013-02-08 15:53:37 +01005304static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5305 const struct brcmf_event_msg *e, void *data)
5306{
5307 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5308 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5309 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5310 struct brcmf_cfg80211_vif *vif;
5311
Hante Meuleman37a869e2015-10-29 20:33:17 +01005312 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
Arend van Sprield3c0b632013-02-08 15:53:37 +01005313 ifevent->action, ifevent->flags, ifevent->ifidx,
Hante Meuleman37a869e2015-10-29 20:33:17 +01005314 ifevent->bsscfgidx);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005315
Arend van Sprield3c0b632013-02-08 15:53:37 +01005316 mutex_lock(&event->vif_event_lock);
5317 event->action = ifevent->action;
5318 vif = event->vif;
5319
5320 switch (ifevent->action) {
5321 case BRCMF_E_IF_ADD:
5322 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005323 if (!cfg->vif_event.vif) {
5324 mutex_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005325 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005326 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005327
5328 ifp->vif = vif;
5329 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005330 if (ifp->ndev) {
5331 vif->wdev.netdev = ifp->ndev;
5332 ifp->ndev->ieee80211_ptr = &vif->wdev;
5333 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5334 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005335 mutex_unlock(&event->vif_event_lock);
5336 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005337 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005338
5339 case BRCMF_E_IF_DEL:
Arend van Sprield3c0b632013-02-08 15:53:37 +01005340 mutex_unlock(&event->vif_event_lock);
5341 /* event may not be upon user request */
5342 if (brcmf_cfg80211_vif_event_armed(cfg))
5343 wake_up(&event->vif_wq);
5344 return 0;
5345
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005346 case BRCMF_E_IF_CHANGE:
5347 mutex_unlock(&event->vif_event_lock);
5348 wake_up(&event->vif_wq);
5349 return 0;
5350
Arend van Sprield3c0b632013-02-08 15:53:37 +01005351 default:
5352 mutex_unlock(&event->vif_event_lock);
5353 break;
5354 }
5355 return -EINVAL;
5356}
5357
Arend van Spriel5b435de2011-10-05 13:19:03 +02005358static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5359{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005360 conf->frag_threshold = (u32)-1;
5361 conf->rts_threshold = (u32)-1;
5362 conf->retry_short = (u32)-1;
5363 conf->retry_long = (u32)-1;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005364}
5365
Arend van Spriel5c36b992012-11-14 18:46:05 -08005366static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005367{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005368 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5369 brcmf_notify_connect_status);
5370 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5371 brcmf_notify_connect_status);
5372 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5373 brcmf_notify_connect_status);
5374 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5375 brcmf_notify_connect_status);
5376 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5377 brcmf_notify_connect_status);
5378 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5379 brcmf_notify_connect_status);
5380 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5381 brcmf_notify_roaming_status);
5382 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5383 brcmf_notify_mic_status);
5384 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5385 brcmf_notify_connect_status);
5386 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5387 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005388 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5389 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005390 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005391 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005392 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5393 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005394 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5395 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005396 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5397 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005398 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5399 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005400}
5401
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005402static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005403{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005404 kfree(cfg->conf);
5405 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005406 kfree(cfg->escan_ioctl_buf);
5407 cfg->escan_ioctl_buf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005408 kfree(cfg->extra_buf);
5409 cfg->extra_buf = NULL;
Hante Meuleman3021ad92016-01-05 11:05:45 +01005410 kfree(cfg->wowl.nd);
5411 cfg->wowl.nd = NULL;
5412 kfree(cfg->wowl.nd_info);
5413 cfg->wowl.nd_info = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005414}
5415
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005416static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005417{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005418 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5419 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005420 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005421 cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5422 if (!cfg->escan_ioctl_buf)
Hante Meulemane756af52012-09-11 21:18:52 +02005423 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005424 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5425 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005426 goto init_priv_mem_out;
Hante Meuleman3021ad92016-01-05 11:05:45 +01005427 cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
5428 if (!cfg->wowl.nd)
5429 goto init_priv_mem_out;
5430 cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
5431 sizeof(struct cfg80211_wowlan_nd_match *),
5432 GFP_KERNEL);
5433 if (!cfg->wowl.nd_info)
5434 goto init_priv_mem_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005435
5436 return 0;
5437
5438init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005439 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005440
5441 return -ENOMEM;
5442}
5443
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005444static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005445{
5446 s32 err = 0;
5447
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005448 cfg->scan_request = NULL;
5449 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005450 cfg->active_scan = true; /* we do active scan per default */
5451 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005452 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005453 if (err)
5454 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005455 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005456 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005457 brcmf_init_escan(cfg);
5458 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005459 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005460 return err;
5461}
5462
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005463static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005464{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005465 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005466 brcmf_abort_scanning(cfg);
5467 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005468}
5469
Arend van Sprield3c0b632013-02-08 15:53:37 +01005470static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5471{
5472 init_waitqueue_head(&event->vif_wq);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005473 mutex_init(&event->vif_event_lock);
5474}
5475
Hante Meuleman1119e232015-11-25 11:32:42 +01005476static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005477{
Hante Meuleman1119e232015-11-25 11:32:42 +01005478 s32 err;
5479 u32 bcn_timeout;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005480 __le32 roamtrigger[2];
5481 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005482
Hante Meuleman1119e232015-11-25 11:32:42 +01005483 /* Configure beacon timeout value based upon roaming setting */
Hante Meuleman7d34b052016-01-02 09:41:41 +01005484 if (ifp->drvr->settings->roamoff)
Hante Meuleman1119e232015-11-25 11:32:42 +01005485 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
5486 else
5487 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
5488 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
5489 if (err) {
5490 brcmf_err("bcn_timeout error (%d)\n", err);
5491 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005492 }
5493
Hante Meuleman1119e232015-11-25 11:32:42 +01005494 /* Enable/Disable built-in roaming to allow supplicant to take care of
5495 * roaming.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005496 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005497 brcmf_dbg(INFO, "Internal Roaming = %s\n",
Hante Meuleman7d34b052016-01-02 09:41:41 +01005498 ifp->drvr->settings->roamoff ? "Off" : "On");
5499 err = brcmf_fil_iovar_int_set(ifp, "roam_off",
5500 ifp->drvr->settings->roamoff);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005501 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005502 brcmf_err("roam_off error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005503 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005504 }
5505
Arend van Sprielf588bc02011-10-12 20:51:22 +02005506 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5507 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005508 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005509 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005510 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005511 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005512 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005513 }
5514
Arend van Sprielf588bc02011-10-12 20:51:22 +02005515 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5516 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005517 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005518 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005519 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005520 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005521 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005522 }
5523
Hante Meuleman1119e232015-11-25 11:32:42 +01005524roam_setup_done:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005525 return err;
5526}
5527
5528static s32
Hante Meuleman1678ba82015-12-10 13:43:00 +01005529brcmf_dongle_scantime(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005530{
5531 s32 err = 0;
5532
Arend van Sprielac24be62012-10-22 10:36:23 -07005533 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005534 BRCMF_SCAN_CHANNEL_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005535 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005536 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005537 goto dongle_scantime_out;
5538 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005539 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005540 BRCMF_SCAN_UNASSOC_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005541 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005542 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005543 goto dongle_scantime_out;
5544 }
5545
Arend van Sprielac24be62012-10-22 10:36:23 -07005546 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005547 BRCMF_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005548 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005549 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005550 goto dongle_scantime_out;
5551 }
5552
5553dongle_scantime_out:
5554 return err;
5555}
5556
Arend van Sprielb48d8912014-07-12 08:49:41 +02005557static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5558 struct brcmu_chan *ch)
5559{
5560 u32 ht40_flag;
5561
5562 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5563 if (ch->sb == BRCMU_CHAN_SB_U) {
5564 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5565 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5566 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5567 } else {
5568 /* It should be one of
5569 * IEEE80211_CHAN_NO_HT40 or
5570 * IEEE80211_CHAN_NO_HT40PLUS
5571 */
5572 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5573 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5574 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5575 }
5576}
5577
5578static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5579 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005580{
5581 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005582 struct ieee80211_supported_band *band;
5583 struct ieee80211_channel *channel;
5584 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005585 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005586 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005587 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005588 u8 *pbuf;
5589 u32 i, j;
5590 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005591 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005592 u32 index;
Hante Meulemand48200b2013-04-03 12:40:29 +02005593
5594 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5595
5596 if (pbuf == NULL)
5597 return -ENOMEM;
5598
5599 list = (struct brcmf_chanspec_list *)pbuf;
5600
5601 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5602 BRCMF_DCMD_MEDLEN);
5603 if (err) {
5604 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005605 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005606 }
5607
Arend van Sprielb48d8912014-07-12 08:49:41 +02005608 wiphy = cfg_to_wiphy(cfg);
Arend van Spriel58de92d2015-04-14 20:10:24 +02005609 band = wiphy->bands[IEEE80211_BAND_2GHZ];
5610 if (band)
5611 for (i = 0; i < band->n_channels; i++)
5612 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
5613 band = wiphy->bands[IEEE80211_BAND_5GHZ];
5614 if (band)
5615 for (i = 0; i < band->n_channels; i++)
5616 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005617
5618 total = le32_to_cpu(list->count);
5619 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005620 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5621 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005622
Franky Lin83cf17a2013-04-11 13:28:50 +02005623 if (ch.band == BRCMU_CHAN_BAND_2G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005624 band = wiphy->bands[IEEE80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005625 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005626 band = wiphy->bands[IEEE80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005627 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005628 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005629 continue;
5630 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005631 if (!band)
5632 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005633 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005634 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005635 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005636 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005637 ch.bw == BRCMU_CHAN_BW_80)
5638 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005639
5640 channel = band->channels;
5641 index = band->n_channels;
5642 for (j = 0; j < band->n_channels; j++) {
5643 if (channel[j].hw_value == ch.chnum) {
5644 index = j;
Hante Meulemand48200b2013-04-03 12:40:29 +02005645 break;
5646 }
5647 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005648 channel[index].center_freq =
5649 ieee80211_channel_to_frequency(ch.chnum, band->band);
5650 channel[index].hw_value = ch.chnum;
Hante Meulemand48200b2013-04-03 12:40:29 +02005651
Arend van Sprielb48d8912014-07-12 08:49:41 +02005652 /* assuming the chanspecs order is HT20,
5653 * HT40 upper, HT40 lower, and VHT80.
5654 */
5655 if (ch.bw == BRCMU_CHAN_BW_80) {
5656 channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ;
5657 } else if (ch.bw == BRCMU_CHAN_BW_40) {
5658 brcmf_update_bw40_channel_flag(&channel[index], &ch);
5659 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005660 /* enable the channel and disable other bandwidths
5661 * for now as mentioned order assure they are enabled
5662 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005663 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02005664 channel[index].flags = IEEE80211_CHAN_NO_HT40 |
5665 IEEE80211_CHAN_NO_80MHZ;
5666 ch.bw = BRCMU_CHAN_BW_20;
5667 cfg->d11inf.encchspec(&ch);
5668 chaninfo = ch.chspec;
5669 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5670 &chaninfo);
5671 if (!err) {
5672 if (chaninfo & WL_CHAN_RADAR)
5673 channel[index].flags |=
5674 (IEEE80211_CHAN_RADAR |
5675 IEEE80211_CHAN_NO_IR);
5676 if (chaninfo & WL_CHAN_PASSIVE)
5677 channel[index].flags |=
5678 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005679 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005680 }
5681 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005682
Arend van Sprielb48d8912014-07-12 08:49:41 +02005683fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005684 kfree(pbuf);
5685 return err;
5686}
5687
Arend van Sprielb48d8912014-07-12 08:49:41 +02005688static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005689{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005690 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5691 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005692 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005693 struct brcmf_chanspec_list *list;
5694 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005695 u32 val;
5696 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005697 struct brcmu_chan ch;
5698 u32 num_chan;
5699 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005700
5701 /* verify support for bw_cap command */
5702 val = WLC_BAND_5G;
5703 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
5704
5705 if (!err) {
5706 /* only set 2G bandwidth using bw_cap command */
5707 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
5708 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
5709 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
5710 sizeof(band_bwcap));
5711 } else {
5712 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
5713 val = WLC_N_BW_40ALL;
5714 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
5715 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005716
5717 if (!err) {
5718 /* update channel info in 2G band */
5719 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5720
5721 if (pbuf == NULL)
5722 return -ENOMEM;
5723
5724 ch.band = BRCMU_CHAN_BAND_2G;
5725 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005726 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005727 ch.chnum = 0;
5728 cfg->d11inf.encchspec(&ch);
5729
5730 /* pass encoded chanspec in query */
5731 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
5732
5733 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5734 BRCMF_DCMD_MEDLEN);
5735 if (err) {
5736 brcmf_err("get chanspecs error (%d)\n", err);
5737 kfree(pbuf);
5738 return err;
5739 }
5740
5741 band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ];
5742 list = (struct brcmf_chanspec_list *)pbuf;
5743 num_chan = le32_to_cpu(list->count);
5744 for (i = 0; i < num_chan; i++) {
5745 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5746 cfg->d11inf.decchspec(&ch);
5747 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
5748 continue;
5749 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
5750 continue;
5751 for (j = 0; j < band->n_channels; j++) {
5752 if (band->channels[j].hw_value == ch.chnum)
5753 break;
5754 }
5755 if (WARN_ON(j == band->n_channels))
5756 continue;
5757
5758 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
5759 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02005760 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005761 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005762 return err;
5763}
5764
Arend van Spriel2375d972014-01-06 12:40:41 +01005765static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
5766{
5767 u32 band, mimo_bwcap;
5768 int err;
5769
5770 band = WLC_BAND_2G;
5771 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5772 if (!err) {
5773 bw_cap[IEEE80211_BAND_2GHZ] = band;
5774 band = WLC_BAND_5G;
5775 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
5776 if (!err) {
5777 bw_cap[IEEE80211_BAND_5GHZ] = band;
5778 return;
5779 }
5780 WARN_ON(1);
5781 return;
5782 }
5783 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
5784 mimo_bwcap = 0;
5785 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
5786 if (err)
5787 /* assume 20MHz if firmware does not give a clue */
5788 mimo_bwcap = WLC_N_BW_20ALL;
5789
5790 switch (mimo_bwcap) {
5791 case WLC_N_BW_40ALL:
5792 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
5793 /* fall-thru */
5794 case WLC_N_BW_20IN2G_40IN5G:
5795 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
5796 /* fall-thru */
5797 case WLC_N_BW_20ALL:
5798 bw_cap[IEEE80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
5799 bw_cap[IEEE80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
5800 break;
5801 default:
5802 brcmf_err("invalid mimo_bw_cap value\n");
5803 }
5804}
Hante Meulemand48200b2013-04-03 12:40:29 +02005805
Arend van Spriel18d6c532014-05-12 10:47:35 +02005806static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
5807 u32 bw_cap[2], u32 nchain)
5808{
5809 band->ht_cap.ht_supported = true;
5810 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
5811 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
5812 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
5813 }
5814 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
5815 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
5816 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
5817 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
5818 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
5819 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
5820}
5821
5822static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
5823{
5824 u16 mcs_map;
5825 int i;
5826
5827 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
5828 mcs_map = (mcs_map << 2) | supp;
5829
5830 return cpu_to_le16(mcs_map);
5831}
5832
5833static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005834 u32 bw_cap[2], u32 nchain, u32 txstreams,
5835 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
Arend van Spriel18d6c532014-05-12 10:47:35 +02005836{
5837 __le16 mcs_map;
5838
5839 /* not allowed in 2.4G band */
5840 if (band->band == IEEE80211_BAND_2GHZ)
5841 return;
5842
5843 band->vht_cap.vht_supported = true;
5844 /* 80MHz is mandatory */
5845 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
5846 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
5847 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
5848 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
5849 }
5850 /* all support 256-QAM */
5851 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
5852 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
5853 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005854
5855 /* Beamforming support information */
5856 if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
5857 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
5858 if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
5859 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
5860 if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
5861 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
5862 if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
5863 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
5864
5865 if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
5866 band->vht_cap.cap |=
5867 (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
5868 band->vht_cap.cap |= ((txstreams - 1) <<
5869 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
5870 band->vht_cap.cap |=
5871 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
5872 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005873}
5874
Arend van Sprielb48d8912014-07-12 08:49:41 +02005875static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005876{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005877 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07005878 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02005879 u32 nmode = 0;
5880 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005881 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01005882 u32 rxchain;
5883 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005884 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005885 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01005886 struct ieee80211_supported_band *band;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005887 u32 txstreams = 0;
5888 u32 txbf_bfe_cap = 0;
5889 u32 txbf_bfr_cap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005890
Arend van Spriel18d6c532014-05-12 10:47:35 +02005891 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02005892 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
5893 if (err) {
5894 brcmf_err("nmode error (%d)\n", err);
5895 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005896 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005897 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02005898 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
5899 nmode, vhtmode, bw_cap[IEEE80211_BAND_2GHZ],
5900 bw_cap[IEEE80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02005901
Daniel Kim4aca7a12014-02-25 20:30:36 +01005902 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
5903 if (err) {
5904 brcmf_err("rxchain error (%d)\n", err);
5905 nchain = 1;
5906 } else {
5907 for (nchain = 0; rxchain; nchain++)
5908 rxchain = rxchain & (rxchain - 1);
5909 }
5910 brcmf_dbg(INFO, "nchain=%d\n", nchain);
5911
Arend van Sprielb48d8912014-07-12 08:49:41 +02005912 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005913 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02005914 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02005915 return err;
5916 }
5917
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005918 if (vhtmode) {
5919 (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
5920 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
5921 &txbf_bfe_cap);
5922 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
5923 &txbf_bfr_cap);
5924 }
5925
Arend van Sprielb48d8912014-07-12 08:49:41 +02005926 wiphy = cfg_to_wiphy(cfg);
5927 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
5928 band = wiphy->bands[i];
5929 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01005930 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02005931
Arend van Spriel18d6c532014-05-12 10:47:35 +02005932 if (nmode)
5933 brcmf_update_ht_cap(band, bw_cap, nchain);
5934 if (vhtmode)
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01005935 brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
5936 txbf_bfe_cap, txbf_bfr_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02005937 }
5938
Arend van Sprielb48d8912014-07-12 08:49:41 +02005939 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005940}
5941
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005942static const struct ieee80211_txrx_stypes
5943brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
5944 [NL80211_IFTYPE_STATION] = {
5945 .tx = 0xffff,
5946 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5947 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5948 },
5949 [NL80211_IFTYPE_P2P_CLIENT] = {
5950 .tx = 0xffff,
5951 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5952 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5953 },
5954 [NL80211_IFTYPE_P2P_GO] = {
5955 .tx = 0xffff,
5956 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
5957 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
5958 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
5959 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
5960 BIT(IEEE80211_STYPE_AUTH >> 4) |
5961 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
5962 BIT(IEEE80211_STYPE_ACTION >> 4)
5963 },
5964 [NL80211_IFTYPE_P2P_DEVICE] = {
5965 .tx = 0xffff,
5966 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
5967 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
5968 }
5969};
5970
Arend van Spriel0882dda2015-08-20 22:06:03 +02005971/**
5972 * brcmf_setup_ifmodes() - determine interface modes and combinations.
5973 *
5974 * @wiphy: wiphy object.
5975 * @ifp: interface object needed for feat module api.
5976 *
5977 * The interface modes and combinations are determined dynamically here
5978 * based on firmware functionality.
5979 *
5980 * no p2p and no mbss:
5981 *
5982 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5983 *
5984 * no p2p and mbss:
5985 *
5986 * #STA <= 1, #AP <= 1, channels = 1, 2 total
5987 * #AP <= 4, matching BI, channels = 1, 4 total
5988 *
5989 * p2p, no mchan, and mbss:
5990 *
5991 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
5992 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5993 * #AP <= 4, matching BI, channels = 1, 4 total
5994 *
5995 * p2p, mchan, and mbss:
5996 *
5997 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
5998 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
5999 * #AP <= 4, matching BI, channels = 1, 4 total
6000 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006001static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
6002{
6003 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006004 struct ieee80211_iface_limit *c0_limits = NULL;
6005 struct ieee80211_iface_limit *p2p_limits = NULL;
6006 struct ieee80211_iface_limit *mbss_limits = NULL;
6007 bool mbss, p2p;
6008 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006009
Arend van Spriel0882dda2015-08-20 22:06:03 +02006010 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
6011 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
6012
6013 n_combos = 1 + !!p2p + !!mbss;
6014 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006015 if (!combo)
6016 goto err;
6017
Arend van Spriel0882dda2015-08-20 22:06:03 +02006018 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
6019 if (!c0_limits)
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006020 goto err;
6021
Arend van Spriel0882dda2015-08-20 22:06:03 +02006022 if (p2p) {
6023 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
6024 if (!p2p_limits)
6025 goto err;
6026 }
6027
6028 if (mbss) {
6029 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
6030 if (!mbss_limits)
6031 goto err;
6032 }
6033
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006034 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
6035 BIT(NL80211_IFTYPE_ADHOC) |
6036 BIT(NL80211_IFTYPE_AP);
6037
Arend van Spriel0882dda2015-08-20 22:06:03 +02006038 c = 0;
6039 i = 0;
6040 combo[c].num_different_channels = 1;
6041 c0_limits[i].max = 1;
6042 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
6043 if (p2p) {
6044 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
6045 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006046 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
6047 BIT(NL80211_IFTYPE_P2P_GO) |
6048 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02006049 c0_limits[i].max = 1;
6050 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
6051 c0_limits[i].max = 1;
6052 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
6053 BIT(NL80211_IFTYPE_P2P_GO);
6054 } else {
6055 c0_limits[i].max = 1;
6056 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006057 }
Arend van Spriel0882dda2015-08-20 22:06:03 +02006058 combo[c].max_interfaces = i;
6059 combo[c].n_limits = i;
6060 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006061
Arend van Spriel0882dda2015-08-20 22:06:03 +02006062 if (p2p) {
6063 c++;
6064 i = 0;
6065 combo[c].num_different_channels = 1;
6066 p2p_limits[i].max = 1;
6067 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
6068 p2p_limits[i].max = 1;
6069 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
6070 p2p_limits[i].max = 1;
6071 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
6072 p2p_limits[i].max = 1;
6073 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
6074 combo[c].max_interfaces = i;
6075 combo[c].n_limits = i;
6076 combo[c].limits = p2p_limits;
6077 }
6078
6079 if (mbss) {
6080 c++;
6081 combo[c].beacon_int_infra_match = true;
6082 combo[c].num_different_channels = 1;
6083 mbss_limits[0].max = 4;
6084 mbss_limits[0].types = BIT(NL80211_IFTYPE_AP);
6085 combo[c].max_interfaces = 4;
6086 combo[c].n_limits = 1;
6087 combo[c].limits = mbss_limits;
6088 }
6089 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006090 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006091 return 0;
6092
6093err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02006094 kfree(c0_limits);
6095 kfree(p2p_limits);
6096 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006097 kfree(combo);
6098 return -ENOMEM;
6099}
6100
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006101static void brcmf_wiphy_pno_params(struct wiphy *wiphy)
6102{
6103 /* scheduled scan settings */
6104 wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT;
6105 wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
6106 wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
6107 wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
6108}
6109
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006110#ifdef CONFIG_PM
Hante Meuleman3021ad92016-01-05 11:05:45 +01006111static struct wiphy_wowlan_support brcmf_wowlan_support = {
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006112 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01006113 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
6114 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
6115 .pattern_min_len = 1,
6116 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006117};
6118#endif
6119
Hante Meuleman3021ad92016-01-05 11:05:45 +01006120static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006121{
6122#ifdef CONFIG_PM
Hante Meuleman3021ad92016-01-05 11:05:45 +01006123 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
6124 s32 err;
6125 u32 wowl_cap;
6126
6127 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
6128 err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
6129 if (!err) {
6130 if (wowl_cap & BRCMF_WOWL_PFN_FOUND) {
6131 brcmf_wowlan_support.flags |=
6132 WIPHY_WOWLAN_NET_DETECT;
6133 init_waitqueue_head(&cfg->wowl.nd_data_wait);
6134 }
6135 }
6136 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006137 wiphy->wowlan = &brcmf_wowlan_support;
6138#endif
6139}
6140
Arend van Sprielb48d8912014-07-12 08:49:41 +02006141static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006142{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006143 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006144 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006145 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006146 u16 max_interfaces = 0;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006147 __le32 bandlist[3];
6148 u32 n_bands;
6149 int err, i;
6150
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006151 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
6152 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
Hante Meuleman6c404f32015-12-10 13:43:03 +01006153 wiphy->max_num_pmkids = BRCMF_MAXPMKID;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006154
6155 err = brcmf_setup_ifmodes(wiphy, ifp);
6156 if (err)
6157 return err;
6158
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006159 for (i = 0, combo = wiphy->iface_combinations;
6160 i < wiphy->n_iface_combinations; i++, combo++) {
6161 max_interfaces = max(max_interfaces, combo->max_interfaces);
6162 }
6163
6164 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
6165 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006166 u8 *addr = drvr->addresses[i].addr;
6167
6168 memcpy(addr, drvr->mac, ETH_ALEN);
6169 if (i) {
6170 addr[0] |= BIT(1);
6171 addr[ETH_ALEN - 1] ^= i;
6172 }
6173 }
6174 wiphy->addresses = drvr->addresses;
6175 wiphy->n_addresses = i;
6176
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006177 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
6178 wiphy->cipher_suites = __wl_cipher_suites;
6179 wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
6180 wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT |
6181 WIPHY_FLAG_OFFCHAN_TX |
Hante Meulemana7b82d42015-12-10 13:43:04 +01006182 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
6183 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
6184 wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman7d34b052016-01-02 09:41:41 +01006185 if (!ifp->drvr->settings->roamoff)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006186 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
6187 wiphy->mgmt_stypes = brcmf_txrx_stypes;
6188 wiphy->max_remain_on_channel_duration = 5000;
Arend van Spriel7a7a87d2015-04-14 20:10:27 +02006189 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
6190 brcmf_wiphy_pno_params(wiphy);
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006191
6192 /* vendor commands/events support */
6193 wiphy->vendor_commands = brcmf_vendor_cmds;
6194 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
6195
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006196 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
Hante Meuleman3021ad92016-01-05 11:05:45 +01006197 brcmf_wiphy_wowl_params(wiphy, ifp);
Arend van Spriel58de92d2015-04-14 20:10:24 +02006198 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
6199 sizeof(bandlist));
6200 if (err) {
6201 brcmf_err("could not obtain band info: err=%d\n", err);
6202 return err;
6203 }
6204 /* first entry in bandlist is number of bands */
6205 n_bands = le32_to_cpu(bandlist[0]);
6206 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
6207 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
6208 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
6209 GFP_KERNEL);
6210 if (!band)
6211 return -ENOMEM;
6212
6213 band->channels = kmemdup(&__wl_2ghz_channels,
6214 sizeof(__wl_2ghz_channels),
6215 GFP_KERNEL);
6216 if (!band->channels) {
6217 kfree(band);
6218 return -ENOMEM;
6219 }
6220
6221 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
6222 wiphy->bands[IEEE80211_BAND_2GHZ] = band;
6223 }
6224 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
6225 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
6226 GFP_KERNEL);
6227 if (!band)
6228 return -ENOMEM;
6229
6230 band->channels = kmemdup(&__wl_5ghz_channels,
6231 sizeof(__wl_5ghz_channels),
6232 GFP_KERNEL);
6233 if (!band->channels) {
6234 kfree(band);
6235 return -ENOMEM;
6236 }
6237
6238 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
6239 wiphy->bands[IEEE80211_BAND_5GHZ] = band;
6240 }
6241 }
6242 err = brcmf_setup_wiphybands(wiphy);
6243 return err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006244}
6245
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006246static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006247{
6248 struct net_device *ndev;
6249 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01006250 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006251 s32 power_mode;
6252 s32 err = 0;
6253
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006254 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006255 return err;
6256
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006257 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006258 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006259 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006260
Hante Meuleman40a23292013-01-02 15:22:51 +01006261 /* make sure RF is ready for work */
6262 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6263
Hante Meuleman1678ba82015-12-10 13:43:00 +01006264 brcmf_dongle_scantime(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006265
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006266 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006267 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006268 if (err)
6269 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006270 brcmf_dbg(INFO, "power save set to %s\n",
6271 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006272
Hante Meuleman1119e232015-11-25 11:32:42 +01006273 err = brcmf_dongle_roam(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006274 if (err)
6275 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006276 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
6277 NULL, NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006278 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006279 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006280
Hante Meulemanb3657452013-05-27 21:09:53 +02006281 brcmf_configure_arp_offload(ifp, true);
6282
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006283 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006284default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006285
6286 return err;
6287
6288}
6289
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006290static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006291{
Arend van Sprielc1179032012-10-22 13:55:33 -07006292 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006293
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006294 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006295}
6296
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006297static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006298{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006299 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006300
Arend van Spriel5b435de2011-10-05 13:19:03 +02006301 /*
6302 * While going down, if associated with AP disassociate
6303 * from AP to save power
6304 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006305 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006306 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006307
6308 /* Make sure WPA_Supplicant receives all the event
6309 generated due to DISASSOC call to the fw to keep
6310 the state fw and WPA_Supplicant state consistent
6311 */
6312 brcmf_delay(500);
6313 }
6314
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006315 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006316 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006317
Arend van Spriel5b435de2011-10-05 13:19:03 +02006318 return 0;
6319}
6320
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006321s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006322{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006323 struct brcmf_if *ifp = netdev_priv(ndev);
6324 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006325 s32 err = 0;
6326
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006327 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006328 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006329 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006330
6331 return err;
6332}
6333
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006334s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006335{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006336 struct brcmf_if *ifp = netdev_priv(ndev);
6337 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006338 s32 err = 0;
6339
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006340 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006341 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006342 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006343
6344 return err;
6345}
6346
Arend van Spriela7965fb2013-04-11 17:08:37 +02006347enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6348{
6349 struct wireless_dev *wdev = &ifp->vif->wdev;
6350
6351 return wdev->iftype;
6352}
6353
Hante Meulemanbfe81972014-10-28 14:56:16 +01006354bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6355 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006356{
6357 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006358
6359 list_for_each_entry(vif, &cfg->vif_list, list) {
6360 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006361 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006362 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006363 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006364}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006365
6366static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6367 u8 action)
6368{
6369 u8 evt_action;
6370
6371 mutex_lock(&event->vif_event_lock);
6372 evt_action = event->action;
6373 mutex_unlock(&event->vif_event_lock);
6374 return evt_action == action;
6375}
6376
6377void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6378 struct brcmf_cfg80211_vif *vif)
6379{
6380 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6381
6382 mutex_lock(&event->vif_event_lock);
6383 event->vif = vif;
6384 event->action = 0;
6385 mutex_unlock(&event->vif_event_lock);
6386}
6387
6388bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6389{
6390 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6391 bool armed;
6392
6393 mutex_lock(&event->vif_event_lock);
6394 armed = event->vif != NULL;
6395 mutex_unlock(&event->vif_event_lock);
6396
6397 return armed;
6398}
6399int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg,
6400 u8 action, ulong timeout)
6401{
6402 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6403
6404 return wait_event_timeout(event->vif_wq,
6405 vif_event_equals(event, action), timeout);
6406}
6407
Arend van Spriel63db1a42014-12-21 12:43:51 +01006408static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6409 struct regulatory_request *req)
6410{
6411 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6412 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6413 struct brcmf_fil_country_le ccreq;
6414 int i;
6415
6416 brcmf_dbg(TRACE, "enter: initiator=%d, alpha=%c%c\n", req->initiator,
6417 req->alpha2[0], req->alpha2[1]);
6418
6419 /* ignore non-ISO3166 country codes */
6420 for (i = 0; i < sizeof(req->alpha2); i++)
6421 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
6422 brcmf_err("not a ISO3166 code\n");
6423 return;
6424 }
6425 memset(&ccreq, 0, sizeof(ccreq));
6426 ccreq.rev = cpu_to_le32(-1);
6427 memcpy(ccreq.ccode, req->alpha2, sizeof(req->alpha2));
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006428 if (brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq))) {
6429 brcmf_err("firmware rejected country setting\n");
6430 return;
6431 }
6432 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006433}
6434
Arend van Sprielb48d8912014-07-12 08:49:41 +02006435static void brcmf_free_wiphy(struct wiphy *wiphy)
6436{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006437 int i;
6438
Arend van Spriel58de92d2015-04-14 20:10:24 +02006439 if (!wiphy)
6440 return;
6441
Arend van Spriel0882dda2015-08-20 22:06:03 +02006442 if (wiphy->iface_combinations) {
6443 for (i = 0; i < wiphy->n_iface_combinations; i++)
6444 kfree(wiphy->iface_combinations[i].limits);
6445 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006446 kfree(wiphy->iface_combinations);
6447 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6448 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels);
6449 kfree(wiphy->bands[IEEE80211_BAND_2GHZ]);
6450 }
6451 if (wiphy->bands[IEEE80211_BAND_5GHZ]) {
6452 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels);
6453 kfree(wiphy->bands[IEEE80211_BAND_5GHZ]);
6454 }
6455 wiphy_free(wiphy);
6456}
6457
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006458struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006459 struct device *busdev,
6460 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006461{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006462 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006463 struct brcmf_cfg80211_info *cfg;
6464 struct wiphy *wiphy;
6465 struct brcmf_cfg80211_vif *vif;
6466 struct brcmf_if *ifp;
6467 s32 err = 0;
6468 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006469 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006470
6471 if (!ndev) {
6472 brcmf_err("ndev is invalid\n");
6473 return NULL;
6474 }
6475
6476 ifp = netdev_priv(ndev);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006477 wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
6478 if (!wiphy) {
6479 brcmf_err("Could not allocate wiphy device\n");
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006480 return NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006481 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006482 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006483 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006484
6485 cfg = wiphy_priv(wiphy);
6486 cfg->wiphy = wiphy;
6487 cfg->pub = drvr;
6488 init_vif_event(&cfg->vif_event);
6489 INIT_LIST_HEAD(&cfg->vif_list);
6490
6491 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006492 if (IS_ERR(vif))
6493 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006494
6495 vif->ifp = ifp;
6496 vif->wdev.netdev = ndev;
6497 ndev->ieee80211_ptr = &vif->wdev;
6498 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6499
6500 err = wl_init_priv(cfg);
6501 if (err) {
6502 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006503 brcmf_free_vif(vif);
6504 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006505 }
6506 ifp->vif = vif;
6507
Arend van Sprielb48d8912014-07-12 08:49:41 +02006508 /* determine d11 io type before wiphy setup */
6509 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006510 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006511 brcmf_err("Failed to get D11 version (%d)\n", err);
6512 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006513 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006514 cfg->d11inf.io_type = (u8)io_type;
6515 brcmu_d11_attach(&cfg->d11inf);
6516
6517 err = brcmf_setup_wiphy(wiphy, ifp);
6518 if (err < 0)
6519 goto priv_out;
6520
6521 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006522 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006523 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6524 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6525
6526 /* firmware defaults to 40MHz disabled in 2G band. We signal
6527 * cfg80211 here that we do and have it decide we can enable
6528 * it. But first check if device does support 2G operation.
6529 */
6530 if (wiphy->bands[IEEE80211_BAND_2GHZ]) {
6531 cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap;
6532 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6533 }
6534 err = wiphy_register(wiphy);
6535 if (err < 0) {
6536 brcmf_err("Could not register wiphy device (%d)\n", err);
6537 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006538 }
6539
6540 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6541 * setup 40MHz in 2GHz band and enable OBSS scanning.
6542 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006543 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6544 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006545 if (!err)
6546 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6547 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006548 else
6549 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006550 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006551 /* p2p might require that "if-events" get processed by fweh. So
6552 * activate the already registered event handlers now and activate
6553 * the rest when initialization has completed. drvr->config needs to
6554 * be assigned before activating events.
6555 */
6556 drvr->config = cfg;
6557 err = brcmf_fweh_activate_events(ifp);
6558 if (err) {
6559 brcmf_err("FWEH activation failed (%d)\n", err);
6560 goto wiphy_unreg_out;
6561 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006562
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006563 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006564 if (err) {
6565 brcmf_err("P2P initilisation failed (%d)\n", err);
6566 goto wiphy_unreg_out;
6567 }
6568 err = brcmf_btcoex_attach(cfg);
6569 if (err) {
6570 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6571 brcmf_p2p_detach(&cfg->p2p);
6572 goto wiphy_unreg_out;
6573 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006574
Hante Meulemana7b82d42015-12-10 13:43:04 +01006575 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
6576 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6577 if (err) {
6578 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6579 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
6580 } else {
6581 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6582 brcmf_notify_tdls_peer_event);
6583 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006584 }
6585
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006586 /* (re-) activate FWEH event handling */
6587 err = brcmf_fweh_activate_events(ifp);
6588 if (err) {
6589 brcmf_err("FWEH activation failed (%d)\n", err);
6590 goto wiphy_unreg_out;
6591 }
6592
Hante Meuleman48ed16e2016-01-02 09:41:38 +01006593 /* Fill in some of the advertised nl80211 supported features */
6594 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
6595 wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
6596#ifdef CONFIG_PM
6597 if (wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
6598 wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
6599#endif
6600 }
6601
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006602 return cfg;
6603
Arend van Sprielb48d8912014-07-12 08:49:41 +02006604wiphy_unreg_out:
6605 wiphy_unregister(cfg->wiphy);
6606priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006607 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006608 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02006609 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006610wiphy_out:
6611 brcmf_free_wiphy(wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006612 return NULL;
6613}
6614
6615void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
6616{
6617 if (!cfg)
6618 return;
6619
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006620 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02006621 wiphy_unregister(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006622 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006623 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006624}