blob: 898c7b024c159df1761c179a53cf0ddcb94d9738 [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"
Arend Van Sprielac551362016-11-23 10:25:22 +000035#include "pno.h"
Hante Meulemanbfe81972014-10-28 14:56:16 +010036#include "cfg80211.h"
Arend van Sprielc08437b2014-07-12 08:49:39 +020037#include "feature.h"
Hante Meuleman81f5dcb2012-10-22 10:36:14 -070038#include "fwil.h"
Hante Meuleman8851cce2014-07-30 13:20:02 +020039#include "proto.h"
Franky Lin1bacb042014-06-21 12:11:16 +020040#include "vendor.h"
Hante Meulemand14f78b2014-10-28 14:56:14 +010041#include "bus.h"
Hante Meuleman6b89dcb2014-12-21 12:43:52 +010042#include "common.h"
Arend van Spriel5b435de2011-10-05 13:19:03 +020043
Arend van Spriele5806072012-09-19 22:21:08 +020044#define BRCMF_SCAN_IE_LEN_MAX 2048
Arend van Spriele5806072012-09-19 22:21:08 +020045
Hante Meuleman1a873342012-09-27 14:17:54 +020046#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
47#define WPA_OUI_TYPE 1
48#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
49#define WME_OUI_TYPE 2
Hante Meuleman89286dc2013-02-08 15:53:46 +010050#define WPS_OUI_TYPE 4
Hante Meuleman1a873342012-09-27 14:17:54 +020051
52#define VS_IE_FIXED_HDR_LEN 6
53#define WPA_IE_VERSION_LEN 2
54#define WPA_IE_MIN_OUI_LEN 4
55#define WPA_IE_SUITE_COUNT_LEN 2
56
57#define WPA_CIPHER_NONE 0 /* None */
58#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
59#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
60#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
61#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
62
63#define RSN_AKM_NONE 0 /* None (IBSS) */
64#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
65#define RSN_AKM_PSK 2 /* Pre-shared Key */
Hante Meuleman240d61a2016-02-17 11:27:10 +010066#define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */
67#define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */
Hante Meuleman1a873342012-09-27 14:17:54 +020068#define RSN_CAP_LEN 2 /* Length of RSN capabilities */
Hante Meuleman240d61a2016-02-17 11:27:10 +010069#define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3))
70#define RSN_CAP_MFPR_MASK BIT(6)
71#define RSN_CAP_MFPC_MASK BIT(7)
72#define RSN_PMKID_COUNT_LEN 2
Hante Meuleman1a873342012-09-27 14:17:54 +020073
74#define VNDR_IE_CMD_LEN 4 /* length of the set command
75 * string :"add", "del" (+ NUL)
76 */
77#define VNDR_IE_COUNT_OFFSET 4
78#define VNDR_IE_PKTFLAG_OFFSET 8
79#define VNDR_IE_VSIE_OFFSET 12
80#define VNDR_IE_HDR_SIZE 12
Arend van Spriel9f440b72013-02-08 15:53:36 +010081#define VNDR_IE_PARSE_LIMIT 5
Hante Meuleman1a873342012-09-27 14:17:54 +020082
83#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
84#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
Hante Meuleman04012892012-09-27 14:17:49 +020085
Hante Meuleman89286dc2013-02-08 15:53:46 +010086#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
87#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
88#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
89
Hante Meuleman1678ba82015-12-10 13:43:00 +010090#define BRCMF_SCAN_CHANNEL_TIME 40
91#define BRCMF_SCAN_UNASSOC_TIME 40
92#define BRCMF_SCAN_PASSIVE_TIME 120
93
Hante Meuleman3021ad92016-01-05 11:05:45 +010094#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)
95
Arend van Spriel5b435de2011-10-05 13:19:03 +020096#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
97 (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
98
Arend van Sprielce81e312012-10-22 13:55:37 -070099static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200100{
Arend van Sprielc1179032012-10-22 13:55:33 -0700101 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100102 brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
103 vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200104 return false;
105 }
106 return true;
107}
108
Arend van Spriel5b435de2011-10-05 13:19:03 +0200109#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
110#define RATETAB_ENT(_rateid, _flags) \
111 { \
112 .bitrate = RATE_TO_BASE100KBPS(_rateid), \
113 .hw_value = (_rateid), \
114 .flags = (_flags), \
115 }
116
117static struct ieee80211_rate __wl_rates[] = {
118 RATETAB_ENT(BRCM_RATE_1M, 0),
119 RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
120 RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
121 RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
122 RATETAB_ENT(BRCM_RATE_6M, 0),
123 RATETAB_ENT(BRCM_RATE_9M, 0),
124 RATETAB_ENT(BRCM_RATE_12M, 0),
125 RATETAB_ENT(BRCM_RATE_18M, 0),
126 RATETAB_ENT(BRCM_RATE_24M, 0),
127 RATETAB_ENT(BRCM_RATE_36M, 0),
128 RATETAB_ENT(BRCM_RATE_48M, 0),
129 RATETAB_ENT(BRCM_RATE_54M, 0),
130};
131
Arend van Spriel5b435de2011-10-05 13:19:03 +0200132#define wl_g_rates (__wl_rates + 0)
Arend van Spriel58de92d2015-04-14 20:10:24 +0200133#define wl_g_rates_size ARRAY_SIZE(__wl_rates)
134#define wl_a_rates (__wl_rates + 4)
135#define wl_a_rates_size (wl_g_rates_size - 4)
136
137#define CHAN2G(_channel, _freq) { \
Johannes Berg57fbcce2016-04-12 15:56:15 +0200138 .band = NL80211_BAND_2GHZ, \
Arend van Spriel58de92d2015-04-14 20:10:24 +0200139 .center_freq = (_freq), \
140 .hw_value = (_channel), \
Arend van Spriel58de92d2015-04-14 20:10:24 +0200141 .max_antenna_gain = 0, \
142 .max_power = 30, \
143}
144
145#define CHAN5G(_channel) { \
Johannes Berg57fbcce2016-04-12 15:56:15 +0200146 .band = NL80211_BAND_5GHZ, \
Arend van Spriel58de92d2015-04-14 20:10:24 +0200147 .center_freq = 5000 + (5 * (_channel)), \
148 .hw_value = (_channel), \
Arend van Spriel58de92d2015-04-14 20:10:24 +0200149 .max_antenna_gain = 0, \
150 .max_power = 30, \
151}
152
153static struct ieee80211_channel __wl_2ghz_channels[] = {
154 CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
155 CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
156 CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
157 CHAN2G(13, 2472), CHAN2G(14, 2484)
158};
159
160static struct ieee80211_channel __wl_5ghz_channels[] = {
161 CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
162 CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
163 CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
164 CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
165 CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
166 CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
167};
Arend van Spriel5b435de2011-10-05 13:19:03 +0200168
Arend van Sprielb48d8912014-07-12 08:49:41 +0200169/* Band templates duplicated per wiphy. The channel info
Arend van Spriel58de92d2015-04-14 20:10:24 +0200170 * above is added to the band during setup.
Arend van Sprielb48d8912014-07-12 08:49:41 +0200171 */
172static const struct ieee80211_supported_band __wl_band_2ghz = {
Johannes Berg57fbcce2016-04-12 15:56:15 +0200173 .band = NL80211_BAND_2GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200174 .bitrates = wl_g_rates,
175 .n_bitrates = wl_g_rates_size,
176};
177
Arend van Spriel58de92d2015-04-14 20:10:24 +0200178static const struct ieee80211_supported_band __wl_band_5ghz = {
Johannes Berg57fbcce2016-04-12 15:56:15 +0200179 .band = NL80211_BAND_5GHZ,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200180 .bitrates = wl_a_rates,
181 .n_bitrates = wl_a_rates_size,
182};
183
Hante Meulemand48200b2013-04-03 12:40:29 +0200184/* This is to override regulatory domains defined in cfg80211 module (reg.c)
185 * By default world regulatory domain defined in reg.c puts the flags
Luis R. Rodriguez8fe02e12013-10-21 19:22:25 +0200186 * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
187 * With respect to these flags, wpa_supplicant doesn't * start p2p
188 * operations on 5GHz channels. All the changes in world regulatory
Hante Meulemand48200b2013-04-03 12:40:29 +0200189 * domain are to be done here.
190 */
191static const struct ieee80211_regdomain brcmf_regdom = {
192 .n_reg_rules = 4,
193 .alpha2 = "99",
194 .reg_rules = {
195 /* IEEE 802.11b/g, channels 1..11 */
196 REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
197 /* If any */
198 /* IEEE 802.11 channel 14 - Only JP enables
199 * this and for 802.11b only
200 */
201 REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
202 /* IEEE 802.11a, channel 36..64 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200203 REG_RULE(5150-10, 5350+10, 80, 6, 20, 0),
Hante Meulemand48200b2013-04-03 12:40:29 +0200204 /* IEEE 802.11a, channel 100..165 */
Arend van Sprielc555ecd2014-05-12 10:47:36 +0200205 REG_RULE(5470-10, 5850+10, 80, 6, 20, 0), }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200206};
207
Hante Meuleman240d61a2016-02-17 11:27:10 +0100208/* Note: brcmf_cipher_suites is an array of int defining which cipher suites
209 * are supported. A pointer to this array and the number of entries is passed
210 * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
211 * So the cipher suite AES_CMAC has to be the last one in the array, and when
212 * device does not support MFP then the number of suites will be decreased by 1
213 */
214static const u32 brcmf_cipher_suites[] = {
Arend van Spriel5b435de2011-10-05 13:19:03 +0200215 WLAN_CIPHER_SUITE_WEP40,
216 WLAN_CIPHER_SUITE_WEP104,
217 WLAN_CIPHER_SUITE_TKIP,
218 WLAN_CIPHER_SUITE_CCMP,
Hante Meuleman240d61a2016-02-17 11:27:10 +0100219 /* Keep as last entry: */
220 WLAN_CIPHER_SUITE_AES_CMAC
Arend van Spriel5b435de2011-10-05 13:19:03 +0200221};
222
Hante Meuleman1a873342012-09-27 14:17:54 +0200223/* Vendor specific ie. id = 221, oui and type defines exact ie */
224struct brcmf_vs_tlv {
225 u8 id;
226 u8 len;
227 u8 oui[3];
228 u8 oui_type;
229};
230
231struct parsed_vndr_ie_info {
232 u8 *ie_ptr;
233 u32 ie_len; /* total length including id & length field */
234 struct brcmf_vs_tlv vndrie;
235};
236
237struct parsed_vndr_ies {
238 u32 count;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100239 struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
Hante Meuleman1a873342012-09-27 14:17:54 +0200240};
241
Arend van Spriel7705ba62016-04-17 16:44:58 +0200242static u8 nl80211_band_to_fwil(enum nl80211_band band)
243{
244 switch (band) {
245 case NL80211_BAND_2GHZ:
246 return WLC_BAND_2G;
247 case NL80211_BAND_5GHZ:
248 return WLC_BAND_5G;
249 default:
250 WARN_ON(1);
251 break;
252 }
253 return 0;
254}
255
Arend van Spriel5a394eb2014-05-27 12:56:15 +0200256static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
257 struct cfg80211_chan_def *ch)
Arend van Spriel600a8972014-05-12 10:47:39 +0200258{
259 struct brcmu_chan ch_inf;
260 s32 primary_offset;
261
262 brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
263 ch->chan->center_freq, ch->center_freq1, ch->width);
264 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
Rafał Miłecki36e80722016-01-20 16:46:04 +0100265 primary_offset = ch->chan->center_freq - ch->center_freq1;
Arend van Spriel600a8972014-05-12 10:47:39 +0200266 switch (ch->width) {
267 case NL80211_CHAN_WIDTH_20:
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100268 case NL80211_CHAN_WIDTH_20_NOHT:
Arend van Spriel600a8972014-05-12 10:47:39 +0200269 ch_inf.bw = BRCMU_CHAN_BW_20;
270 WARN_ON(primary_offset != 0);
271 break;
272 case NL80211_CHAN_WIDTH_40:
273 ch_inf.bw = BRCMU_CHAN_BW_40;
Rafał Miłecki36e80722016-01-20 16:46:04 +0100274 if (primary_offset > 0)
Arend van Spriel600a8972014-05-12 10:47:39 +0200275 ch_inf.sb = BRCMU_CHAN_SB_U;
276 else
277 ch_inf.sb = BRCMU_CHAN_SB_L;
278 break;
279 case NL80211_CHAN_WIDTH_80:
280 ch_inf.bw = BRCMU_CHAN_BW_80;
Rafał Miłecki36e80722016-01-20 16:46:04 +0100281 if (primary_offset == -30)
282 ch_inf.sb = BRCMU_CHAN_SB_LL;
283 else if (primary_offset == -10)
284 ch_inf.sb = BRCMU_CHAN_SB_LU;
285 else if (primary_offset == 10)
286 ch_inf.sb = BRCMU_CHAN_SB_UL;
287 else
288 ch_inf.sb = BRCMU_CHAN_SB_UU;
Arend van Spriel600a8972014-05-12 10:47:39 +0200289 break;
Arend van Spriel0cd75b12014-11-11 13:58:44 +0100290 case NL80211_CHAN_WIDTH_80P80:
291 case NL80211_CHAN_WIDTH_160:
292 case NL80211_CHAN_WIDTH_5:
293 case NL80211_CHAN_WIDTH_10:
Arend van Spriel600a8972014-05-12 10:47:39 +0200294 default:
295 WARN_ON_ONCE(1);
296 }
297 switch (ch->chan->band) {
Johannes Berg57fbcce2016-04-12 15:56:15 +0200298 case NL80211_BAND_2GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200299 ch_inf.band = BRCMU_CHAN_BAND_2G;
300 break;
Johannes Berg57fbcce2016-04-12 15:56:15 +0200301 case NL80211_BAND_5GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200302 ch_inf.band = BRCMU_CHAN_BAND_5G;
303 break;
Johannes Berg57fbcce2016-04-12 15:56:15 +0200304 case NL80211_BAND_60GHZ:
Arend van Spriel600a8972014-05-12 10:47:39 +0200305 default:
306 WARN_ON_ONCE(1);
307 }
308 d11inf->encchspec(&ch_inf);
309
310 return ch_inf.chspec;
311}
312
Franky Lin83cf17a2013-04-11 13:28:50 +0200313u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
314 struct ieee80211_channel *ch)
Arend van Spriel6e186162012-10-22 10:36:22 -0700315{
Franky Lin83cf17a2013-04-11 13:28:50 +0200316 struct brcmu_chan ch_inf;
Arend van Spriel6e186162012-10-22 10:36:22 -0700317
Franky Lin83cf17a2013-04-11 13:28:50 +0200318 ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
319 ch_inf.bw = BRCMU_CHAN_BW_20;
320 d11inf->encchspec(&ch_inf);
Arend van Spriel6e186162012-10-22 10:36:22 -0700321
Franky Lin83cf17a2013-04-11 13:28:50 +0200322 return ch_inf.chspec;
Arend van Spriel6e186162012-10-22 10:36:22 -0700323}
324
Hante Meuleman89286dc2013-02-08 15:53:46 +0100325/* Traverse a string of 1-byte tag/1-byte length/variable-length value
326 * triples, returning a pointer to the substring whose first element
327 * matches tag
328 */
Rafał Miłeckic8d87072017-01-18 11:48:51 +0100329static const struct brcmf_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100330brcmf_parse_tlvs(const void *buf, int buflen, uint key)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100331{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100332 const struct brcmf_tlv *elt = buf;
333 int totlen = buflen;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100334
335 /* find tagged parameter */
336 while (totlen >= TLV_HDR_LEN) {
337 int len = elt->len;
338
339 /* validate remaining totlen */
340 if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
341 return elt;
342
343 elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
344 totlen -= (len + TLV_HDR_LEN);
345 }
346
347 return NULL;
348}
349
350/* Is any of the tlvs the expected entry? If
351 * not update the tlvs buffer pointer/length.
352 */
353static bool
Johannes Berg4b5800f2014-01-15 14:55:59 +0100354brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
355 const u8 *oui, u32 oui_len, u8 type)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100356{
357 /* If the contents match the OUI and the type */
358 if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
359 !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
360 type == ie[TLV_BODY_OFF + oui_len]) {
361 return true;
362 }
363
364 if (tlvs == NULL)
365 return false;
366 /* point to the next ie */
367 ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
368 /* calculate the length of the rest of the buffer */
369 *tlvs_len -= (int)(ie - *tlvs);
370 /* update the pointer to the start of the buffer */
371 *tlvs = ie;
372
373 return false;
374}
375
376static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100377brcmf_find_wpaie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100378{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100379 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100380
381 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
Johannes Berg4b5800f2014-01-15 14:55:59 +0100382 if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
Hante Meuleman89286dc2013-02-08 15:53:46 +0100383 WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
384 return (struct brcmf_vs_tlv *)ie;
385 }
386 return NULL;
387}
388
389static struct brcmf_vs_tlv *
Johannes Berg4b5800f2014-01-15 14:55:59 +0100390brcmf_find_wpsie(const u8 *parse, u32 len)
Hante Meuleman89286dc2013-02-08 15:53:46 +0100391{
Johannes Berg4b5800f2014-01-15 14:55:59 +0100392 const struct brcmf_tlv *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +0100393
394 while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
395 if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
396 WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
397 return (struct brcmf_vs_tlv *)ie;
398 }
399 return NULL;
400}
401
Arend van Spriel39504a22015-08-20 22:06:05 +0200402static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
403 struct brcmf_cfg80211_vif *vif,
404 enum nl80211_iftype new_type)
405{
Arend van Spriel39504a22015-08-20 22:06:05 +0200406 struct brcmf_cfg80211_vif *pos;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100407 bool check_combos = false;
408 int ret = 0;
Purushottam Kushwahae2273002016-10-12 18:25:35 +0530409 struct iface_combination_params params = {
410 .num_different_channels = 1,
411 };
Arend van Spriel39504a22015-08-20 22:06:05 +0200412
Arend van Spriel39504a22015-08-20 22:06:05 +0200413 list_for_each_entry(pos, &cfg->vif_list, list)
Arend van Spriel353c46a2015-12-10 13:43:06 +0100414 if (pos == vif) {
Purushottam Kushwahae2273002016-10-12 18:25:35 +0530415 params.iftype_num[new_type]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100416 } else {
417 /* concurrent interfaces so need check combinations */
418 check_combos = true;
Purushottam Kushwahae2273002016-10-12 18:25:35 +0530419 params.iftype_num[pos->wdev.iftype]++;
Arend van Spriel353c46a2015-12-10 13:43:06 +0100420 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200421
Arend van Spriel353c46a2015-12-10 13:43:06 +0100422 if (check_combos)
Purushottam Kushwahae2273002016-10-12 18:25:35 +0530423 ret = cfg80211_check_combinations(cfg->wiphy, &params);
Arend van Spriel353c46a2015-12-10 13:43:06 +0100424
425 return ret;
Arend van Spriel39504a22015-08-20 22:06:05 +0200426}
427
428static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
429 enum nl80211_iftype new_type)
430{
Arend van Spriel39504a22015-08-20 22:06:05 +0200431 struct brcmf_cfg80211_vif *pos;
Purushottam Kushwahae2273002016-10-12 18:25:35 +0530432 struct iface_combination_params params = {
433 .num_different_channels = 1,
434 };
Arend van Spriel39504a22015-08-20 22:06:05 +0200435
Arend van Spriel39504a22015-08-20 22:06:05 +0200436 list_for_each_entry(pos, &cfg->vif_list, list)
Purushottam Kushwahae2273002016-10-12 18:25:35 +0530437 params.iftype_num[pos->wdev.iftype]++;
Arend van Spriel39504a22015-08-20 22:06:05 +0200438
Purushottam Kushwahae2273002016-10-12 18:25:35 +0530439 params.iftype_num[new_type]++;
440 return cfg80211_check_combinations(cfg->wiphy, &params);
Arend van Spriel39504a22015-08-20 22:06:05 +0200441}
Hante Meuleman89286dc2013-02-08 15:53:46 +0100442
Arend van Spriel5b435de2011-10-05 13:19:03 +0200443static void convert_key_from_CPU(struct brcmf_wsec_key *key,
444 struct brcmf_wsec_key_le *key_le)
445{
446 key_le->index = cpu_to_le32(key->index);
447 key_le->len = cpu_to_le32(key->len);
448 key_le->algo = cpu_to_le32(key->algo);
449 key_le->flags = cpu_to_le32(key->flags);
450 key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
451 key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
452 key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
453 memcpy(key_le->data, key->data, sizeof(key->data));
454 memcpy(key_le->ea, key->ea, sizeof(key->ea));
455}
456
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200457static int
Hante Meuleman118eb302014-12-21 12:43:49 +0100458send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
Arend van Spriel5b435de2011-10-05 13:19:03 +0200459{
460 int err;
461 struct brcmf_wsec_key_le key_le;
462
463 convert_key_from_CPU(key, &key_le);
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200464
Hante Meuleman118eb302014-12-21 12:43:49 +0100465 brcmf_netdev_wait_pend8021x(ifp);
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700466
Hante Meuleman118eb302014-12-21 12:43:49 +0100467 err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -0700468 sizeof(key_le));
Hante Meulemanf09d0c02012-09-27 14:17:48 +0200469
Arend van Spriel5b435de2011-10-05 13:19:03 +0200470 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +0100471 brcmf_err("wsec_key error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200472 return err;
473}
474
Hante Meulemanb3657452013-05-27 21:09:53 +0200475static s32
Franky Lin52f22fb2016-02-17 11:26:55 +0100476brcmf_configure_arp_nd_offload(struct brcmf_if *ifp, bool enable)
Hante Meulemanb3657452013-05-27 21:09:53 +0200477{
478 s32 err;
479 u32 mode;
480
481 if (enable)
482 mode = BRCMF_ARP_OL_AGENT | BRCMF_ARP_OL_PEER_AUTO_REPLY;
483 else
484 mode = 0;
485
486 /* Try to set and enable ARP offload feature, this may fail, then it */
487 /* is simply not supported and err 0 will be returned */
488 err = brcmf_fil_iovar_int_set(ifp, "arp_ol", mode);
489 if (err) {
490 brcmf_dbg(TRACE, "failed to set ARP offload mode to 0x%x, err = %d\n",
491 mode, err);
492 err = 0;
493 } else {
494 err = brcmf_fil_iovar_int_set(ifp, "arpoe", enable);
495 if (err) {
496 brcmf_dbg(TRACE, "failed to configure (%d) ARP offload err = %d\n",
497 enable, err);
498 err = 0;
499 } else
500 brcmf_dbg(TRACE, "successfully configured (%d) ARP offload to 0x%x\n",
501 enable, mode);
502 }
503
Franky Lin52f22fb2016-02-17 11:26:55 +0100504 err = brcmf_fil_iovar_int_set(ifp, "ndoe", enable);
505 if (err) {
506 brcmf_dbg(TRACE, "failed to configure (%d) ND offload err = %d\n",
507 enable, err);
508 err = 0;
509 } else
510 brcmf_dbg(TRACE, "successfully configured (%d) ND offload to 0x%x\n",
511 enable, mode);
512
Hante Meulemanb3657452013-05-27 21:09:53 +0200513 return err;
514}
515
Hante Meuleman8851cce2014-07-30 13:20:02 +0200516static void
517brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
518{
Arend van Spriel8f2b4592014-09-11 22:51:32 +0200519 struct brcmf_cfg80211_vif *vif;
520 struct brcmf_if *ifp;
521
522 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
523 ifp = vif->ifp;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200524
525 if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
526 (wdev->iftype == NL80211_IFTYPE_AP) ||
527 (wdev->iftype == NL80211_IFTYPE_P2P_GO))
528 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
529 ADDR_DIRECT);
530 else
531 brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
532 ADDR_INDIRECT);
533}
534
Rafał Miłeckid02fb8f2016-05-26 01:44:27 +0200535static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
536{
537 int bsscfgidx;
538
539 for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {
540 /* bsscfgidx 1 is reserved for legacy P2P */
541 if (bsscfgidx == 1)
542 continue;
543 if (!drvr->iflist[bsscfgidx])
544 return bsscfgidx;
545 }
546
547 return -ENOMEM;
548}
549
Hante Meulemana44aa402014-12-03 21:05:33 +0100550static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
551{
552 struct brcmf_mbss_ssid_le mbss_ssid_le;
553 int bsscfgidx;
554 int err;
555
556 memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
Rafał Miłeckid02fb8f2016-05-26 01:44:27 +0200557 bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);
Hante Meulemana44aa402014-12-03 21:05:33 +0100558 if (bsscfgidx < 0)
559 return bsscfgidx;
560
561 mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
562 mbss_ssid_le.SSID_len = cpu_to_le32(5);
563 sprintf(mbss_ssid_le.SSID, "ssid%d" , bsscfgidx);
564
565 err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
566 sizeof(mbss_ssid_le));
567 if (err < 0)
568 brcmf_err("setting ssid failed %d\n", err);
569
570 return err;
571}
572
573/**
574 * brcmf_ap_add_vif() - create a new AP virtual interface for multiple BSS
575 *
576 * @wiphy: wiphy device of new interface.
577 * @name: name of the new interface.
Hante Meulemana44aa402014-12-03 21:05:33 +0100578 * @params: contains mac address for AP device.
579 */
580static
581struct wireless_dev *brcmf_ap_add_vif(struct wiphy *wiphy, const char *name,
Johannes Berg818a9862017-04-12 11:23:28 +0200582 struct vif_params *params)
Hante Meulemana44aa402014-12-03 21:05:33 +0100583{
584 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
585 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
586 struct brcmf_cfg80211_vif *vif;
587 int err;
588
589 if (brcmf_cfg80211_vif_event_armed(cfg))
590 return ERR_PTR(-EBUSY);
591
592 brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
593
Rafał Miłecki26072332016-06-06 23:03:55 +0200594 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_AP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100595 if (IS_ERR(vif))
596 return (struct wireless_dev *)vif;
597
598 brcmf_cfg80211_arm_vif_event(cfg, vif);
599
600 err = brcmf_cfg80211_request_ap_if(ifp);
601 if (err) {
602 brcmf_cfg80211_arm_vif_event(cfg, NULL);
603 goto fail;
604 }
605
606 /* wait for firmware event */
Arend van Spriela9eb0c42016-02-17 11:26:50 +0100607 err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
608 BRCMF_VIF_EVENT_TIMEOUT);
Hante Meulemana44aa402014-12-03 21:05:33 +0100609 brcmf_cfg80211_arm_vif_event(cfg, NULL);
610 if (!err) {
611 brcmf_err("timeout occurred\n");
612 err = -EIO;
613 goto fail;
614 }
615
616 /* interface created in firmware */
617 ifp = vif->ifp;
618 if (!ifp) {
619 brcmf_err("no if pointer provided\n");
620 err = -ENOENT;
621 goto fail;
622 }
623
624 strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
625 err = brcmf_net_attach(ifp, true);
626 if (err) {
627 brcmf_err("Registering netdevice failed\n");
Arend Van Sprieldca23072017-06-24 22:08:27 +0100628 free_netdev(ifp->ndev);
Hante Meulemana44aa402014-12-03 21:05:33 +0100629 goto fail;
630 }
631
632 return &ifp->vif->wdev;
633
634fail:
635 brcmf_free_vif(vif);
636 return ERR_PTR(err);
637}
638
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100639static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
640{
641 enum nl80211_iftype iftype;
642
643 iftype = vif->wdev.iftype;
644 return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
645}
646
647static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
648{
649 return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
650}
651
Arend van Spriel9f440b72013-02-08 15:53:36 +0100652static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
653 const char *name,
Tom Gundersen6bab2e192015-03-18 11:13:39 +0100654 unsigned char name_assign_type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100655 enum nl80211_iftype type,
Arend van Spriel9f440b72013-02-08 15:53:36 +0100656 struct vif_params *params)
657{
Hante Meuleman8851cce2014-07-30 13:20:02 +0200658 struct wireless_dev *wdev;
Arend van Spriel39504a22015-08-20 22:06:05 +0200659 int err;
Hante Meuleman8851cce2014-07-30 13:20:02 +0200660
Arend van Spriel9f440b72013-02-08 15:53:36 +0100661 brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
Arend van Spriel39504a22015-08-20 22:06:05 +0200662 err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
663 if (err) {
664 brcmf_err("iface validation failed: err=%d\n", err);
665 return ERR_PTR(err);
666 }
Arend van Spriel9f440b72013-02-08 15:53:36 +0100667 switch (type) {
668 case NL80211_IFTYPE_ADHOC:
669 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100670 case NL80211_IFTYPE_AP_VLAN:
671 case NL80211_IFTYPE_WDS:
672 case NL80211_IFTYPE_MONITOR:
673 case NL80211_IFTYPE_MESH_POINT:
674 return ERR_PTR(-EOPNOTSUPP);
Hante Meulemana44aa402014-12-03 21:05:33 +0100675 case NL80211_IFTYPE_AP:
Johannes Berg818a9862017-04-12 11:23:28 +0200676 wdev = brcmf_ap_add_vif(wiphy, name, params);
Rafał Miłecki0cd33c22016-05-27 10:54:28 +0200677 break;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100678 case NL80211_IFTYPE_P2P_CLIENT:
679 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200680 case NL80211_IFTYPE_P2P_DEVICE:
Johannes Berg818a9862017-04-12 11:23:28 +0200681 wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params);
Rafał Miłecki0cd33c22016-05-27 10:54:28 +0200682 break;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100683 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100684 default:
685 return ERR_PTR(-EINVAL);
686 }
Rafał Miłecki0cd33c22016-05-27 10:54:28 +0200687
688 if (IS_ERR(wdev))
689 brcmf_err("add iface %s type %d failed: err=%d\n",
690 name, type, (int)PTR_ERR(wdev));
691 else
692 brcmf_cfg80211_update_proto_addr_mode(wdev);
693
694 return wdev;
Arend van Spriel9f440b72013-02-08 15:53:36 +0100695}
696
Daniel Kim5e787f72014-06-21 12:11:18 +0200697static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
698{
Arend van Sprielc08437b2014-07-12 08:49:39 +0200699 if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
Daniel Kim5e787f72014-06-21 12:11:18 +0200700 brcmf_set_mpc(ifp, mpc);
701}
702
Arend van Sprielf96aa072013-04-05 10:57:48 +0200703void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100704{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100705 s32 err = 0;
706
707 if (check_vif_up(ifp->vif)) {
708 err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
709 if (err) {
710 brcmf_err("fail to set mpc\n");
711 return;
712 }
713 brcmf_dbg(INFO, "MPC : %d\n", mpc);
714 }
715}
716
Arend van Spriela0f472a2013-04-05 10:57:49 +0200717s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
718 struct brcmf_if *ifp, bool aborted,
719 bool fw_abort)
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100720{
721 struct brcmf_scan_params_le params_le;
722 struct cfg80211_scan_request *scan_request;
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +0100723 u64 reqid;
724 u32 bucket;
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100725 s32 err = 0;
726
727 brcmf_dbg(SCAN, "Enter\n");
728
729 /* clear scan request, because the FW abort can cause a second call */
730 /* to this functon and might cause a double cfg80211_scan_done */
731 scan_request = cfg->scan_request;
732 cfg->scan_request = NULL;
733
734 if (timer_pending(&cfg->escan_timeout))
735 del_timer_sync(&cfg->escan_timeout);
736
737 if (fw_abort) {
738 /* Do a scan abort to stop the driver's scan engine */
739 brcmf_dbg(SCAN, "ABORT scan in firmware\n");
740 memset(&params_le, 0, sizeof(params_le));
Joe Perches93803b32015-03-02 19:54:49 -0800741 eth_broadcast_addr(params_le.bssid);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100742 params_le.bss_type = DOT11_BSSTYPE_ANY;
743 params_le.scan_type = 0;
744 params_le.channel_num = cpu_to_le32(1);
745 params_le.nprobes = cpu_to_le32(1);
746 params_le.active_time = cpu_to_le32(-1);
747 params_le.passive_time = cpu_to_le32(-1);
748 params_le.home_time = cpu_to_le32(-1);
749 /* Scan is aborted by setting channel_list[0] to -1 */
750 params_le.channel_list[0] = cpu_to_le16(-1);
751 /* E-Scan (or anyother type) can be aborted by SCAN */
Arend van Sprielf96aa072013-04-05 10:57:48 +0200752 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100753 &params_le, sizeof(params_le));
754 if (err)
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +0100755 brcmf_err("Scan abort failed\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100756 }
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200757
Daniel Kim5e787f72014-06-21 12:11:18 +0200758 brcmf_scan_config_mpc(ifp, 1);
Arend van Spriel0f0fe992014-05-27 12:56:14 +0200759
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100760 /*
Arend Van Sprielfa85b302016-11-23 10:25:25 +0000761 * e-scan can be initiated internally
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100762 * which takes precedence.
763 */
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +0100764 if (cfg->int_escan_map) {
765 brcmf_dbg(SCAN, "scheduled scan completed (%x)\n",
766 cfg->int_escan_map);
767 while (cfg->int_escan_map) {
768 bucket = __ffs(cfg->int_escan_map);
769 cfg->int_escan_map &= ~BIT(bucket);
770 reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno,
771 bucket);
772 if (!aborted) {
773 brcmf_dbg(SCAN, "report results: reqid=%llu\n",
774 reqid);
775 cfg80211_sched_scan_results(cfg_to_wiphy(cfg),
776 reqid);
777 }
778 }
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100779 } else if (scan_request) {
Avraham Stern1d762502016-07-05 17:10:13 +0300780 struct cfg80211_scan_info info = {
781 .aborted = aborted,
782 };
783
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100784 brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
785 aborted ? "Aborted" : "Done");
Avraham Stern1d762502016-07-05 17:10:13 +0300786 cfg80211_scan_done(scan_request, &info);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100787 }
Hante Meuleman6eda4e22013-02-08 15:54:02 +0100788 if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
789 brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100790
791 return err;
792}
793
Rafał Miłeckidba8fbc2016-06-29 21:54:27 +0200794static int brcmf_cfg80211_del_ap_iface(struct wiphy *wiphy,
795 struct wireless_dev *wdev)
796{
797 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
798 struct net_device *ndev = wdev->netdev;
799 struct brcmf_if *ifp = netdev_priv(ndev);
800 int ret;
801 int err;
802
803 brcmf_cfg80211_arm_vif_event(cfg, ifp->vif);
804
805 err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0);
806 if (err) {
807 brcmf_err("interface_remove failed %d\n", err);
808 goto err_unarm;
809 }
810
811 /* wait for firmware event */
812 ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
813 BRCMF_VIF_EVENT_TIMEOUT);
814 if (!ret) {
815 brcmf_err("timeout occurred\n");
816 err = -EIO;
817 goto err_unarm;
818 }
819
820 brcmf_remove_interface(ifp, true);
821
822err_unarm:
823 brcmf_cfg80211_arm_vif_event(cfg, NULL);
824 return err;
825}
826
Arend van Spriel9f440b72013-02-08 15:53:36 +0100827static
828int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
829{
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100830 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
831 struct net_device *ndev = wdev->netdev;
832
Rafał Miłeckidba8fbc2016-06-29 21:54:27 +0200833 if (ndev && ndev == cfg_to_ndev(cfg))
834 return -ENOTSUPP;
835
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100836 /* vif event pending in firmware */
837 if (brcmf_cfg80211_vif_event_armed(cfg))
838 return -EBUSY;
839
840 if (ndev) {
841 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
Arend van Spriela0f472a2013-04-05 10:57:49 +0200842 cfg->escan_info.ifp == netdev_priv(ndev))
843 brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
844 true, true);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +0100845
846 brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
847 }
848
Arend van Spriel9f440b72013-02-08 15:53:36 +0100849 switch (wdev->iftype) {
850 case NL80211_IFTYPE_ADHOC:
851 case NL80211_IFTYPE_STATION:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100852 case NL80211_IFTYPE_AP_VLAN:
853 case NL80211_IFTYPE_WDS:
854 case NL80211_IFTYPE_MONITOR:
855 case NL80211_IFTYPE_MESH_POINT:
856 return -EOPNOTSUPP;
Rafał Miłeckidba8fbc2016-06-29 21:54:27 +0200857 case NL80211_IFTYPE_AP:
858 return brcmf_cfg80211_del_ap_iface(wiphy, wdev);
Arend van Spriel9f440b72013-02-08 15:53:36 +0100859 case NL80211_IFTYPE_P2P_CLIENT:
860 case NL80211_IFTYPE_P2P_GO:
Arend van Spriel27f10e32013-04-05 10:57:50 +0200861 case NL80211_IFTYPE_P2P_DEVICE:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100862 return brcmf_p2p_del_vif(wiphy, wdev);
863 case NL80211_IFTYPE_UNSPECIFIED:
Arend van Spriel9f440b72013-02-08 15:53:36 +0100864 default:
865 return -EINVAL;
866 }
867 return -EOPNOTSUPP;
868}
869
Arend van Spriel5b435de2011-10-05 13:19:03 +0200870static s32
871brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg818a9862017-04-12 11:23:28 +0200872 enum nl80211_iftype type,
Arend van Spriel5b435de2011-10-05 13:19:03 +0200873 struct vif_params *params)
874{
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100875 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -0700876 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100877 struct brcmf_cfg80211_vif *vif = ifp->vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200878 s32 infra = 0;
Hante Meuleman1a873342012-09-27 14:17:54 +0200879 s32 ap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200880 s32 err = 0;
881
Hante Meuleman37a869e2015-10-29 20:33:17 +0100882 brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
883 type);
Hante Meuleman178e9ef2015-09-18 22:08:11 +0200884
885 /* WAR: There are a number of p2p interface related problems which
886 * need to be handled initially (before doing the validate).
887 * wpa_supplicant tends to do iface changes on p2p device/client/go
888 * which are not always possible/allowed. However we need to return
889 * OK otherwise the wpa_supplicant wont start. The situation differs
890 * on configuration and setup (p2pon=1 module param). The first check
891 * is to see if the request is a change to station for p2p iface.
892 */
893 if ((type == NL80211_IFTYPE_STATION) &&
894 ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
895 (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
896 (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
897 brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
898 /* Now depending on whether module param p2pon=1 was used the
899 * response needs to be either 0 or EOPNOTSUPP. The reason is
900 * that if p2pon=1 is used, but a newer supplicant is used then
901 * we should return an error, as this combination wont work.
902 * In other situations 0 is returned and supplicant will start
903 * normally. It will give a trace in cfg80211, but it is the
904 * only way to get it working. Unfortunately this will result
905 * in situation where we wont support new supplicant in
906 * combination with module param p2pon=1, but that is the way
907 * it is. If the user tries this then unloading of driver might
908 * fail/lock.
909 */
910 if (cfg->p2p.p2pdev_dynamically)
911 return -EOPNOTSUPP;
912 else
913 return 0;
914 }
Arend van Spriel39504a22015-08-20 22:06:05 +0200915 err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
916 if (err) {
917 brcmf_err("iface validation failed: err=%d\n", err);
918 return err;
919 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200920 switch (type) {
921 case NL80211_IFTYPE_MONITOR:
922 case NL80211_IFTYPE_WDS:
Arend van Spriel57d6e912012-12-05 15:26:00 +0100923 brcmf_err("type (%d) : currently we do not support this type\n",
924 type);
Arend van Spriel5b435de2011-10-05 13:19:03 +0200925 return -EOPNOTSUPP;
926 case NL80211_IFTYPE_ADHOC:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200927 infra = 0;
928 break;
929 case NL80211_IFTYPE_STATION:
Arend van Spriel5b435de2011-10-05 13:19:03 +0200930 infra = 1;
931 break;
Hante Meuleman1a873342012-09-27 14:17:54 +0200932 case NL80211_IFTYPE_AP:
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100933 case NL80211_IFTYPE_P2P_GO:
Hante Meuleman1a873342012-09-27 14:17:54 +0200934 ap = 1;
935 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200936 default:
937 err = -EINVAL;
938 goto done;
939 }
940
Hante Meuleman1a873342012-09-27 14:17:54 +0200941 if (ap) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100942 if (type == NL80211_IFTYPE_P2P_GO) {
943 brcmf_dbg(INFO, "IF Type = P2P GO\n");
944 err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
945 }
946 if (!err) {
Hante Meuleman7a5c1f62013-02-08 15:53:44 +0100947 brcmf_dbg(INFO, "IF Type = AP\n");
948 }
Arend van Spriel5b435de2011-10-05 13:19:03 +0200949 } else {
Arend van Spriel128ce3b2012-11-28 21:44:12 +0100950 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
Hante Meuleman1a873342012-09-27 14:17:54 +0200951 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +0100952 brcmf_err("WLC_SET_INFRA error (%d)\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +0200953 err = -EAGAIN;
954 goto done;
955 }
Arend van Spriel967fe2c2014-03-15 17:18:21 +0100956 brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
Arend van Spriel647c9ae2012-12-05 15:26:01 +0100957 "Adhoc" : "Infra");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200958 }
Hante Meuleman1a873342012-09-27 14:17:54 +0200959 ndev->ieee80211_ptr->iftype = type;
Arend van Spriel5b435de2011-10-05 13:19:03 +0200960
Hante Meuleman8851cce2014-07-30 13:20:02 +0200961 brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
962
Arend van Spriel5b435de2011-10-05 13:19:03 +0200963done:
Arend van Sprield96b8012012-12-05 15:26:02 +0100964 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +0200965
966 return err;
967}
968
Franky Lin83cf17a2013-04-11 13:28:50 +0200969static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
970 struct brcmf_scan_params_le *params_le,
Hante Meulemane756af52012-09-11 21:18:52 +0200971 struct cfg80211_scan_request *request)
972{
973 u32 n_ssids;
974 u32 n_channels;
975 s32 i;
976 s32 offset;
Arend van Spriel029591f2012-09-19 22:21:06 +0200977 u16 chanspec;
Hante Meulemane756af52012-09-11 21:18:52 +0200978 char *ptr;
Arend van Spriel029591f2012-09-19 22:21:06 +0200979 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +0200980
Joe Perches93803b32015-03-02 19:54:49 -0800981 eth_broadcast_addr(params_le->bssid);
Hante Meulemane756af52012-09-11 21:18:52 +0200982 params_le->bss_type = DOT11_BSSTYPE_ANY;
983 params_le->scan_type = 0;
984 params_le->channel_num = 0;
985 params_le->nprobes = cpu_to_le32(-1);
986 params_le->active_time = cpu_to_le32(-1);
987 params_le->passive_time = cpu_to_le32(-1);
988 params_le->home_time = cpu_to_le32(-1);
989 memset(&params_le->ssid_le, 0, sizeof(params_le->ssid_le));
990
991 /* if request is null exit so it will be all channel broadcast scan */
992 if (!request)
993 return;
994
995 n_ssids = request->n_ssids;
996 n_channels = request->n_channels;
997 /* Copy channel array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +0100998 brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
999 n_channels);
Hante Meulemane756af52012-09-11 21:18:52 +02001000 if (n_channels > 0) {
1001 for (i = 0; i < n_channels; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02001002 chanspec = channel_to_chanspec(&cfg->d11inf,
1003 request->channels[i]);
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001004 brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
1005 request->channels[i]->hw_value, chanspec);
Arend van Spriel029591f2012-09-19 22:21:06 +02001006 params_le->channel_list[i] = cpu_to_le16(chanspec);
Hante Meulemane756af52012-09-11 21:18:52 +02001007 }
1008 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001009 brcmf_dbg(SCAN, "Scanning all channels\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001010 }
1011 /* Copy ssid array if applicable */
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001012 brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
Hante Meulemane756af52012-09-11 21:18:52 +02001013 if (n_ssids > 0) {
1014 offset = offsetof(struct brcmf_scan_params_le, channel_list) +
1015 n_channels * sizeof(u16);
1016 offset = roundup(offset, sizeof(u32));
1017 ptr = (char *)params_le + offset;
1018 for (i = 0; i < n_ssids; i++) {
Arend van Spriel029591f2012-09-19 22:21:06 +02001019 memset(&ssid_le, 0, sizeof(ssid_le));
1020 ssid_le.SSID_len =
1021 cpu_to_le32(request->ssids[i].ssid_len);
1022 memcpy(ssid_le.SSID, request->ssids[i].ssid,
1023 request->ssids[i].ssid_len);
1024 if (!ssid_le.SSID_len)
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001025 brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
Hante Meulemane756af52012-09-11 21:18:52 +02001026 else
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01001027 brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n",
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001028 i, ssid_le.SSID, ssid_le.SSID_len);
Arend van Spriel029591f2012-09-19 22:21:06 +02001029 memcpy(ptr, &ssid_le, sizeof(ssid_le));
1030 ptr += sizeof(ssid_le);
Hante Meulemane756af52012-09-11 21:18:52 +02001031 }
1032 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001033 brcmf_dbg(SCAN, "Broadcast scan %p\n", request->ssids);
Hante Meulemane756af52012-09-11 21:18:52 +02001034 if ((request->ssids) && request->ssids->ssid_len) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001035 brcmf_dbg(SCAN, "SSID %s len=%d\n",
1036 params_le->ssid_le.SSID,
1037 request->ssids->ssid_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001038 params_le->ssid_le.SSID_len =
1039 cpu_to_le32(request->ssids->ssid_len);
1040 memcpy(&params_le->ssid_le.SSID, request->ssids->ssid,
1041 request->ssids->ssid_len);
1042 }
1043 }
1044 /* Adding mask to channel numbers */
1045 params_le->channel_num =
1046 cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
1047 (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
1048}
1049
1050static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001051brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
Hante Meulemanc4958102015-11-25 11:32:41 +01001052 struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001053{
1054 s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE +
1055 offsetof(struct brcmf_escan_params_le, params_le);
1056 struct brcmf_escan_params_le *params;
1057 s32 err = 0;
1058
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001059 brcmf_dbg(SCAN, "E-SCAN START\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001060
1061 if (request != NULL) {
1062 /* Allocate space for populating ssids in struct */
1063 params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
1064
1065 /* Allocate space for populating ssids in struct */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001066 params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
Hante Meulemane756af52012-09-11 21:18:52 +02001067 }
1068
1069 params = kzalloc(params_size, GFP_KERNEL);
1070 if (!params) {
1071 err = -ENOMEM;
1072 goto exit;
1073 }
1074 BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
Franky Lin83cf17a2013-04-11 13:28:50 +02001075 brcmf_escan_prep(cfg, &params->params_le, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001076 params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
Hante Meulemanc4958102015-11-25 11:32:41 +01001077 params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
Hante Meulemane756af52012-09-11 21:18:52 +02001078 params->sync_id = cpu_to_le16(0x1234);
1079
Arend van Spriela0f472a2013-04-05 10:57:49 +02001080 err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
Hante Meulemane756af52012-09-11 21:18:52 +02001081 if (err) {
1082 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001083 brcmf_dbg(INFO, "system busy : escan canceled\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001084 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001085 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001086 }
1087
1088 kfree(params);
1089exit:
1090 return err;
1091}
1092
1093static s32
Arend Van Sprielab5981c2016-11-23 10:25:24 +00001094brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request)
Hante Meulemane756af52012-09-11 21:18:52 +02001095{
Arend Van Sprielab5981c2016-11-23 10:25:24 +00001096 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02001097 s32 err;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001098 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001099 struct brcmf_scan_results *results;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001100 struct escan_info *escan = &cfg->escan_info;
Hante Meulemane756af52012-09-11 21:18:52 +02001101
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001102 brcmf_dbg(SCAN, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001103 escan->ifp = ifp;
Arend Van Sprielab5981c2016-11-23 10:25:24 +00001104 escan->wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01001105 escan->escan_state = WL_ESCAN_STATE_SCANNING;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001106 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielf96aa072013-04-05 10:57:48 +02001107 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001108 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001109 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001110 brcmf_err("error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001111 return err;
1112 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001113 brcmf_scan_config_mpc(ifp, 0);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001114 results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Hante Meulemane756af52012-09-11 21:18:52 +02001115 results->version = 0;
1116 results->count = 0;
1117 results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
1118
Hante Meulemanc4958102015-11-25 11:32:41 +01001119 err = escan->run(cfg, ifp, request);
Hante Meulemane756af52012-09-11 21:18:52 +02001120 if (err)
Daniel Kim5e787f72014-06-21 12:11:18 +02001121 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001122 return err;
1123}
1124
1125static s32
Arend van Spriela0f472a2013-04-05 10:57:49 +02001126brcmf_cfg80211_escan(struct wiphy *wiphy, struct brcmf_cfg80211_vif *vif,
Hante Meulemane756af52012-09-11 21:18:52 +02001127 struct cfg80211_scan_request *request,
1128 struct cfg80211_ssid *this_ssid)
1129{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001130 struct brcmf_if *ifp = vif->ifp;
1131 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemane756af52012-09-11 21:18:52 +02001132 struct cfg80211_ssid *ssids;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001133 u32 passive_scan;
Hante Meulemane756af52012-09-11 21:18:52 +02001134 bool escan_req;
1135 bool spec_scan;
1136 s32 err;
Hante Meuleman675f5d82015-12-10 13:43:01 +01001137 struct brcmf_ssid_le ssid_le;
Hante Meulemane756af52012-09-11 21:18:52 +02001138 u32 SSID_len;
1139
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001140 brcmf_dbg(SCAN, "START ESCAN\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001141
Arend van Sprielc1179032012-10-22 13:55:33 -07001142 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001143 brcmf_err("Scanning already: status (%lu)\n", cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001144 return -EAGAIN;
1145 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001146 if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001147 brcmf_err("Scanning being aborted: status (%lu)\n",
1148 cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001149 return -EAGAIN;
1150 }
Arend van Spriel1687eee2013-04-23 12:53:11 +02001151 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
1152 brcmf_err("Scanning suppressed: status (%lu)\n",
1153 cfg->scan_status);
1154 return -EAGAIN;
1155 }
Arend van Sprielc1179032012-10-22 13:55:33 -07001156 if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001157 brcmf_err("Connecting: status (%lu)\n", ifp->vif->sme_state);
Hante Meulemane756af52012-09-11 21:18:52 +02001158 return -EAGAIN;
1159 }
1160
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001161 /* If scan req comes for p2p0, send it over primary I/F */
Arend van Spriela0f472a2013-04-05 10:57:49 +02001162 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
1163 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
Hante Meuleman0f8ffe12013-02-08 15:53:42 +01001164
Hante Meulemane756af52012-09-11 21:18:52 +02001165 escan_req = false;
1166 if (request) {
1167 /* scan bss */
1168 ssids = request->ssids;
1169 escan_req = true;
1170 } else {
1171 /* scan in ibss */
1172 /* we don't do escan in ibss */
1173 ssids = this_ssid;
1174 }
1175
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001176 cfg->scan_request = request;
Arend van Sprielc1179032012-10-22 13:55:33 -07001177 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Hante Meulemane756af52012-09-11 21:18:52 +02001178 if (escan_req) {
Arend van Spriel9f440b72013-02-08 15:53:36 +01001179 cfg->escan_info.run = brcmf_run_escan;
Arend van Spriela0f472a2013-04-05 10:57:49 +02001180 err = brcmf_p2p_scan_prep(wiphy, request, vif);
Arend van Spriel9f440b72013-02-08 15:53:36 +01001181 if (err)
1182 goto scan_out;
1183
Arend Van Sprielab5981c2016-11-23 10:25:24 +00001184 err = brcmf_do_escan(vif->ifp, request);
Arend van Spriel2cb941c2012-11-05 16:22:10 -08001185 if (err)
Hante Meulemane756af52012-09-11 21:18:52 +02001186 goto scan_out;
1187 } else {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001188 brcmf_dbg(SCAN, "ssid \"%s\", ssid_len (%d)\n",
1189 ssids->ssid, ssids->ssid_len);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001190 memset(&ssid_le, 0, sizeof(ssid_le));
1191 SSID_len = min_t(u8, sizeof(ssid_le.SSID), ssids->ssid_len);
1192 ssid_le.SSID_len = cpu_to_le32(0);
Hante Meulemane756af52012-09-11 21:18:52 +02001193 spec_scan = false;
1194 if (SSID_len) {
Hante Meuleman675f5d82015-12-10 13:43:01 +01001195 memcpy(ssid_le.SSID, ssids->ssid, SSID_len);
1196 ssid_le.SSID_len = cpu_to_le32(SSID_len);
Hante Meulemane756af52012-09-11 21:18:52 +02001197 spec_scan = true;
1198 } else
Arend van Spriel4e8a0082012-12-05 15:26:03 +01001199 brcmf_dbg(SCAN, "Broadcast scan\n");
Hante Meulemane756af52012-09-11 21:18:52 +02001200
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001201 passive_scan = cfg->active_scan ? 0 : 1;
Arend van Sprielc1179032012-10-22 13:55:33 -07001202 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001203 passive_scan);
Hante Meulemane756af52012-09-11 21:18:52 +02001204 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001205 brcmf_err("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001206 goto scan_out;
1207 }
Daniel Kim5e787f72014-06-21 12:11:18 +02001208 brcmf_scan_config_mpc(ifp, 0);
Hante Meuleman675f5d82015-12-10 13:43:01 +01001209 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, &ssid_le,
1210 sizeof(ssid_le));
Hante Meulemane756af52012-09-11 21:18:52 +02001211 if (err) {
1212 if (err == -EBUSY)
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001213 brcmf_dbg(INFO, "BUSY: scan for \"%s\" canceled\n",
Hante Meuleman675f5d82015-12-10 13:43:01 +01001214 ssid_le.SSID);
Hante Meulemane756af52012-09-11 21:18:52 +02001215 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01001216 brcmf_err("WLC_SCAN error (%d)\n", err);
Hante Meulemane756af52012-09-11 21:18:52 +02001217
Daniel Kim5e787f72014-06-21 12:11:18 +02001218 brcmf_scan_config_mpc(ifp, 1);
Hante Meulemane756af52012-09-11 21:18:52 +02001219 goto scan_out;
1220 }
1221 }
1222
Hante Meuleman661fa952015-02-06 18:36:47 +01001223 /* Arm scan timeout timer */
1224 mod_timer(&cfg->escan_timeout, jiffies +
Hante Meulemand5367332016-02-17 11:26:51 +01001225 BRCMF_ESCAN_TIMER_INTERVAL_MS * HZ / 1000);
Hante Meuleman661fa952015-02-06 18:36:47 +01001226
Hante Meulemane756af52012-09-11 21:18:52 +02001227 return 0;
1228
1229scan_out:
Arend van Sprielc1179032012-10-22 13:55:33 -07001230 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001231 cfg->scan_request = NULL;
Hante Meulemane756af52012-09-11 21:18:52 +02001232 return err;
1233}
1234
Arend van Spriel5b435de2011-10-05 13:19:03 +02001235static s32
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001236brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001237{
Arend van Spriela0f472a2013-04-05 10:57:49 +02001238 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001239 s32 err = 0;
1240
Arend van Sprield96b8012012-12-05 15:26:02 +01001241 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriela0f472a2013-04-05 10:57:49 +02001242 vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1243 if (!check_vif_up(vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001244 return -EIO;
1245
Arend van Spriela0f472a2013-04-05 10:57:49 +02001246 err = brcmf_cfg80211_escan(wiphy, vif, request, NULL);
Hante Meulemane756af52012-09-11 21:18:52 +02001247
Arend van Spriel5b435de2011-10-05 13:19:03 +02001248 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001249 brcmf_err("scan error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001250
Arend van Sprield96b8012012-12-05 15:26:02 +01001251 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001252 return err;
1253}
1254
1255static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
1256{
1257 s32 err = 0;
1258
Arend van Sprielac24be62012-10-22 10:36:23 -07001259 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "rtsthresh",
1260 rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001261 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001262 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001263
1264 return err;
1265}
1266
1267static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
1268{
1269 s32 err = 0;
1270
Arend van Sprielac24be62012-10-22 10:36:23 -07001271 err = brcmf_fil_iovar_int_set(netdev_priv(ndev), "fragthresh",
1272 frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001273 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001274 brcmf_err("Error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001275
1276 return err;
1277}
1278
1279static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
1280{
1281 s32 err = 0;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001282 u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001283
Arend van Sprielac24be62012-10-22 10:36:23 -07001284 err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001285 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001286 brcmf_err("cmd (%d) , error (%d)\n", cmd, err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001287 return err;
1288 }
1289 return err;
1290}
1291
1292static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
1293{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001294 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1295 struct net_device *ndev = cfg_to_ndev(cfg);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001296 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001297 s32 err = 0;
1298
Arend van Sprield96b8012012-12-05 15:26:02 +01001299 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001300 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001301 return -EIO;
1302
1303 if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001304 (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
1305 cfg->conf->rts_threshold = wiphy->rts_threshold;
1306 err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001307 if (!err)
1308 goto done;
1309 }
1310 if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001311 (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
1312 cfg->conf->frag_threshold = wiphy->frag_threshold;
1313 err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001314 if (!err)
1315 goto done;
1316 }
1317 if (changed & WIPHY_PARAM_RETRY_LONG
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001318 && (cfg->conf->retry_long != wiphy->retry_long)) {
1319 cfg->conf->retry_long = wiphy->retry_long;
1320 err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001321 if (!err)
1322 goto done;
1323 }
1324 if (changed & WIPHY_PARAM_RETRY_SHORT
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001325 && (cfg->conf->retry_short != wiphy->retry_short)) {
1326 cfg->conf->retry_short = wiphy->retry_short;
1327 err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001328 if (!err)
1329 goto done;
1330 }
1331
1332done:
Arend van Sprield96b8012012-12-05 15:26:02 +01001333 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001334 return err;
1335}
1336
Arend van Spriel5b435de2011-10-05 13:19:03 +02001337static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
1338{
1339 memset(prof, 0, sizeof(*prof));
1340}
1341
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001342static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
1343{
1344 u16 reason;
1345
1346 switch (e->event_code) {
1347 case BRCMF_E_DEAUTH:
1348 case BRCMF_E_DEAUTH_IND:
1349 case BRCMF_E_DISASSOC_IND:
1350 reason = e->reason;
1351 break;
1352 case BRCMF_E_LINK:
1353 default:
1354 reason = 0;
1355 break;
1356 }
1357 return reason;
1358}
1359
1360static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001361{
Piotr Haber61730d42013-04-23 12:53:12 +02001362 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001363 s32 err = 0;
1364
Arend van Sprield96b8012012-12-05 15:26:02 +01001365 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001366
Hante Meulemanb0a79082015-12-10 13:43:07 +01001367 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001368 brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n ");
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001369 err = brcmf_fil_cmd_data_set(vif->ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07001370 BRCMF_C_DISASSOC, NULL, 0);
Arend van Spriela538ae32013-07-25 23:01:34 +02001371 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001372 brcmf_err("WLC_DISASSOC failed (%d)\n", err);
Arend van Spriela538ae32013-07-25 23:01:34 +02001373 }
Hante Meulemanb0a79082015-12-10 13:43:07 +01001374 if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1375 (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
1376 cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
1377 true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001378 }
Arend van Spriel903e0ee2012-11-28 21:44:11 +01001379 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
Piotr Haber61730d42013-04-23 12:53:12 +02001380 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
1381 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
Arend van Sprield96b8012012-12-05 15:26:02 +01001382 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001383}
1384
1385static s32
1386brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
1387 struct cfg80211_ibss_params *params)
1388{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001389 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001390 struct brcmf_if *ifp = netdev_priv(ndev);
1391 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001392 struct brcmf_join_params join_params;
1393 size_t join_params_size = 0;
1394 s32 err = 0;
1395 s32 wsec = 0;
1396 s32 bcnprd;
Hante Meuleman17012612013-02-06 18:40:44 +01001397 u16 chanspec;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001398 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001399
Arend van Sprield96b8012012-12-05 15:26:02 +01001400 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001401 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001402 return -EIO;
1403
1404 if (params->ssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001405 brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001406 else {
Arend van Spriel16886732012-12-05 15:26:04 +01001407 brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001408 return -EOPNOTSUPP;
1409 }
1410
Arend van Sprielc1179032012-10-22 13:55:33 -07001411 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001412
1413 if (params->bssid)
Arend van Spriel16886732012-12-05 15:26:04 +01001414 brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001415 else
Arend van Spriel16886732012-12-05 15:26:04 +01001416 brcmf_dbg(CONN, "No BSSID specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001417
Johannes Berg683b6d32012-11-08 21:25:48 +01001418 if (params->chandef.chan)
Arend van Spriel16886732012-12-05 15:26:04 +01001419 brcmf_dbg(CONN, "channel: %d\n",
1420 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001421 else
Arend van Spriel16886732012-12-05 15:26:04 +01001422 brcmf_dbg(CONN, "no channel specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001423
1424 if (params->channel_fixed)
Arend van Spriel16886732012-12-05 15:26:04 +01001425 brcmf_dbg(CONN, "fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001426 else
Arend van Spriel16886732012-12-05 15:26:04 +01001427 brcmf_dbg(CONN, "no fixed channel required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001428
1429 if (params->ie && params->ie_len)
Arend van Spriel16886732012-12-05 15:26:04 +01001430 brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001431 else
Arend van Spriel16886732012-12-05 15:26:04 +01001432 brcmf_dbg(CONN, "no ie specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001433
1434 if (params->beacon_interval)
Arend van Spriel16886732012-12-05 15:26:04 +01001435 brcmf_dbg(CONN, "beacon interval: %d\n",
1436 params->beacon_interval);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001437 else
Arend van Spriel16886732012-12-05 15:26:04 +01001438 brcmf_dbg(CONN, "no beacon interval specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001439
1440 if (params->basic_rates)
Arend van Spriel16886732012-12-05 15:26:04 +01001441 brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001442 else
Arend van Spriel16886732012-12-05 15:26:04 +01001443 brcmf_dbg(CONN, "no basic rates specified\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001444
1445 if (params->privacy)
Arend van Spriel16886732012-12-05 15:26:04 +01001446 brcmf_dbg(CONN, "privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001447 else
Arend van Spriel16886732012-12-05 15:26:04 +01001448 brcmf_dbg(CONN, "no privacy required\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001449
1450 /* Configure Privacy for starter */
1451 if (params->privacy)
1452 wsec |= WEP_ENABLED;
1453
Arend van Sprielc1179032012-10-22 13:55:33 -07001454 err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001455 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001456 brcmf_err("wsec failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001457 goto done;
1458 }
1459
1460 /* Configure Beacon Interval for starter */
1461 if (params->beacon_interval)
1462 bcnprd = params->beacon_interval;
1463 else
1464 bcnprd = 100;
1465
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001466 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001467 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001468 brcmf_err("WLC_SET_BCNPRD failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001469 goto done;
1470 }
1471
1472 /* Configure required join parameter */
1473 memset(&join_params, 0, sizeof(struct brcmf_join_params));
1474
1475 /* SSID */
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001476 ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1477 memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1478 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001479 join_params_size = sizeof(join_params.ssid_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001480
1481 /* BSSID */
1482 if (params->bssid) {
1483 memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001484 join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02001485 memcpy(profile->bssid, params->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001486 } else {
Joe Perches93803b32015-03-02 19:54:49 -08001487 eth_broadcast_addr(join_params.params_le.bssid);
1488 eth_zero_addr(profile->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001489 }
1490
Arend van Spriel5b435de2011-10-05 13:19:03 +02001491 /* Channel */
Johannes Berg683b6d32012-11-08 21:25:48 +01001492 if (params->chandef.chan) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02001493 u32 target_channel;
1494
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001495 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001496 ieee80211_frequency_to_channel(
Johannes Berg683b6d32012-11-08 21:25:48 +01001497 params->chandef.chan->center_freq);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001498 if (params->channel_fixed) {
1499 /* adding chanspec */
Arend van Spriel600a8972014-05-12 10:47:39 +02001500 chanspec = chandef_to_chanspec(&cfg->d11inf,
1501 &params->chandef);
Hante Meuleman17012612013-02-06 18:40:44 +01001502 join_params.params_le.chanspec_list[0] =
1503 cpu_to_le16(chanspec);
1504 join_params.params_le.chanspec_num = cpu_to_le32(1);
1505 join_params_size += sizeof(join_params.params_le);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001506 }
1507
1508 /* set channel for starter */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001509 target_channel = cfg->channel;
Hante Meulemanb87e2c42012-11-14 18:46:23 -08001510 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001511 target_channel);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001512 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001513 brcmf_err("WLC_SET_CHANNEL failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001514 goto done;
1515 }
1516 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001517 cfg->channel = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001518
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001519 cfg->ibss_starter = false;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001520
1521
Arend van Sprielc1179032012-10-22 13:55:33 -07001522 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07001523 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001524 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001525 brcmf_err("WLC_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001526 goto done;
1527 }
1528
1529done:
1530 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07001531 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01001532 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001533 return err;
1534}
1535
1536static s32
1537brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
1538{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001539 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001540
Arend van Sprield96b8012012-12-05 15:26:02 +01001541 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman6a98d642016-01-02 09:41:40 +01001542 if (!check_vif_up(ifp->vif)) {
1543 /* When driver is being unloaded, it can end up here. If an
1544 * error is returned then later on a debug trace in the wireless
1545 * core module will be printed. To avoid this 0 is returned.
1546 */
1547 return 0;
1548 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001549
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01001550 brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING);
Hante Meuleman42e0ed02016-01-05 11:05:50 +01001551 brcmf_net_setcarrier(ifp, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001552
Arend van Sprield96b8012012-12-05 15:26:02 +01001553 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001554
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03001555 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001556}
1557
1558static s32 brcmf_set_wpa_version(struct net_device *ndev,
1559 struct cfg80211_connect_params *sme)
1560{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001561 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001562 struct brcmf_cfg80211_security *sec;
1563 s32 val = 0;
1564 s32 err = 0;
1565
1566 if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
1567 val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
1568 else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
1569 val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
1570 else
1571 val = WPA_AUTH_DISABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01001572 brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001573 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001574 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001575 brcmf_err("set wpa_auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001576 return err;
1577 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001578 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001579 sec->wpa_versions = sme->crypto.wpa_versions;
1580 return err;
1581}
1582
1583static s32 brcmf_set_auth_type(struct net_device *ndev,
1584 struct cfg80211_connect_params *sme)
1585{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001586 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001587 struct brcmf_cfg80211_security *sec;
1588 s32 val = 0;
1589 s32 err = 0;
1590
1591 switch (sme->auth_type) {
1592 case NL80211_AUTHTYPE_OPEN_SYSTEM:
1593 val = 0;
Arend van Spriel16886732012-12-05 15:26:04 +01001594 brcmf_dbg(CONN, "open system\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001595 break;
1596 case NL80211_AUTHTYPE_SHARED_KEY:
1597 val = 1;
Arend van Spriel16886732012-12-05 15:26:04 +01001598 brcmf_dbg(CONN, "shared key\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001599 break;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001600 default:
1601 val = 2;
Hante Meuleman92c31362016-09-19 12:09:59 +01001602 brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001603 break;
1604 }
1605
Hante Meuleman89286dc2013-02-08 15:53:46 +01001606 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001607 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001608 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001609 return err;
1610 }
Arend van Spriel06bb1232012-09-27 14:17:56 +02001611 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001612 sec->auth_type = sme->auth_type;
1613 return err;
1614}
1615
1616static s32
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001617brcmf_set_wsec_mode(struct net_device *ndev,
Hante Meuleman240d61a2016-02-17 11:27:10 +01001618 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001619{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001620 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001621 struct brcmf_cfg80211_security *sec;
1622 s32 pval = 0;
1623 s32 gval = 0;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001624 s32 wsec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001625 s32 err = 0;
1626
1627 if (sme->crypto.n_ciphers_pairwise) {
1628 switch (sme->crypto.ciphers_pairwise[0]) {
1629 case WLAN_CIPHER_SUITE_WEP40:
1630 case WLAN_CIPHER_SUITE_WEP104:
1631 pval = WEP_ENABLED;
1632 break;
1633 case WLAN_CIPHER_SUITE_TKIP:
1634 pval = TKIP_ENABLED;
1635 break;
1636 case WLAN_CIPHER_SUITE_CCMP:
1637 pval = AES_ENABLED;
1638 break;
1639 case WLAN_CIPHER_SUITE_AES_CMAC:
1640 pval = AES_ENABLED;
1641 break;
1642 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001643 brcmf_err("invalid cipher pairwise (%d)\n",
1644 sme->crypto.ciphers_pairwise[0]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001645 return -EINVAL;
1646 }
1647 }
1648 if (sme->crypto.cipher_group) {
1649 switch (sme->crypto.cipher_group) {
1650 case WLAN_CIPHER_SUITE_WEP40:
1651 case WLAN_CIPHER_SUITE_WEP104:
1652 gval = WEP_ENABLED;
1653 break;
1654 case WLAN_CIPHER_SUITE_TKIP:
1655 gval = TKIP_ENABLED;
1656 break;
1657 case WLAN_CIPHER_SUITE_CCMP:
1658 gval = AES_ENABLED;
1659 break;
1660 case WLAN_CIPHER_SUITE_AES_CMAC:
1661 gval = AES_ENABLED;
1662 break;
1663 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001664 brcmf_err("invalid cipher group (%d)\n",
1665 sme->crypto.cipher_group);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001666 return -EINVAL;
1667 }
1668 }
1669
Arend van Spriel16886732012-12-05 15:26:04 +01001670 brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
Hante Meuleman89286dc2013-02-08 15:53:46 +01001671 /* In case of privacy, but no security and WPS then simulate */
1672 /* setting AES. WPS-2.0 allows no security */
1673 if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
1674 sme->privacy)
1675 pval = AES_ENABLED;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001676
Hante Meuleman240d61a2016-02-17 11:27:10 +01001677 wsec = pval | gval;
Daniel Kim87b7e9e2014-03-25 21:45:09 +01001678 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001679 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001680 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001681 return err;
1682 }
1683
Arend van Spriel06bb1232012-09-27 14:17:56 +02001684 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001685 sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
1686 sec->cipher_group = sme->crypto.cipher_group;
1687
1688 return err;
1689}
1690
1691static s32
1692brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
1693{
Hante Meuleman240d61a2016-02-17 11:27:10 +01001694 struct brcmf_if *ifp = netdev_priv(ndev);
1695 s32 val;
1696 s32 err;
1697 const struct brcmf_tlv *rsn_ie;
1698 const u8 *ie;
1699 u32 ie_len;
1700 u32 offset;
1701 u16 rsn_cap;
1702 u32 mfp;
1703 u16 count;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001704
Hante Meuleman240d61a2016-02-17 11:27:10 +01001705 if (!sme->crypto.n_akm_suites)
1706 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001707
Hante Meuleman240d61a2016-02-17 11:27:10 +01001708 err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val);
1709 if (err) {
1710 brcmf_err("could not get wpa_auth (%d)\n", err);
1711 return err;
1712 }
1713 if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
1714 switch (sme->crypto.akm_suites[0]) {
1715 case WLAN_AKM_SUITE_8021X:
1716 val = WPA_AUTH_UNSPECIFIED;
1717 break;
1718 case WLAN_AKM_SUITE_PSK:
1719 val = WPA_AUTH_PSK;
1720 break;
1721 default:
1722 brcmf_err("invalid cipher group (%d)\n",
1723 sme->crypto.cipher_group);
1724 return -EINVAL;
1725 }
1726 } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
1727 switch (sme->crypto.akm_suites[0]) {
1728 case WLAN_AKM_SUITE_8021X:
1729 val = WPA2_AUTH_UNSPECIFIED;
1730 break;
1731 case WLAN_AKM_SUITE_8021X_SHA256:
1732 val = WPA2_AUTH_1X_SHA256;
1733 break;
1734 case WLAN_AKM_SUITE_PSK_SHA256:
1735 val = WPA2_AUTH_PSK_SHA256;
1736 break;
1737 case WLAN_AKM_SUITE_PSK:
1738 val = WPA2_AUTH_PSK;
1739 break;
1740 default:
1741 brcmf_err("invalid cipher group (%d)\n",
1742 sme->crypto.cipher_group);
1743 return -EINVAL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001744 }
1745 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01001746
1747 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
1748 goto skip_mfp_config;
1749 /* The MFP mode (1 or 2) needs to be determined, parse IEs. The
1750 * IE will not be verified, just a quick search for MFP config
1751 */
1752 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
1753 WLAN_EID_RSN);
1754 if (!rsn_ie)
1755 goto skip_mfp_config;
1756 ie = (const u8 *)rsn_ie;
1757 ie_len = rsn_ie->len + TLV_HDR_LEN;
1758 /* Skip unicast suite */
1759 offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
1760 if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
1761 goto skip_mfp_config;
1762 /* Skip multicast suite */
1763 count = ie[offset] + (ie[offset + 1] << 8);
1764 offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
1765 if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
1766 goto skip_mfp_config;
1767 /* Skip auth key management suite(s) */
1768 count = ie[offset] + (ie[offset + 1] << 8);
1769 offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
1770 if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
1771 goto skip_mfp_config;
1772 /* Ready to read capabilities */
1773 mfp = BRCMF_MFP_NONE;
1774 rsn_cap = ie[offset] + (ie[offset + 1] << 8);
1775 if (rsn_cap & RSN_CAP_MFPR_MASK)
1776 mfp = BRCMF_MFP_REQUIRED;
1777 else if (rsn_cap & RSN_CAP_MFPC_MASK)
1778 mfp = BRCMF_MFP_CAPABLE;
1779 brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
1780
1781skip_mfp_config:
1782 brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
1783 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
1784 if (err) {
1785 brcmf_err("could not set wpa_auth (%d)\n", err);
1786 return err;
1787 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001788
1789 return err;
1790}
1791
1792static s32
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001793brcmf_set_sharedkey(struct net_device *ndev,
1794 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001795{
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07001796 struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001797 struct brcmf_cfg80211_security *sec;
1798 struct brcmf_wsec_key key;
1799 s32 val;
1800 s32 err = 0;
1801
Arend van Spriel16886732012-12-05 15:26:04 +01001802 brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001803
Roland Vossena718e2f2011-10-12 20:51:24 +02001804 if (sme->key_len == 0)
1805 return 0;
1806
Arend van Spriel06bb1232012-09-27 14:17:56 +02001807 sec = &profile->sec;
Arend van Spriel16886732012-12-05 15:26:04 +01001808 brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
1809 sec->wpa_versions, sec->cipher_pairwise);
Roland Vossena718e2f2011-10-12 20:51:24 +02001810
1811 if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2))
1812 return 0;
1813
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001814 if (!(sec->cipher_pairwise &
1815 (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
1816 return 0;
Roland Vossena718e2f2011-10-12 20:51:24 +02001817
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001818 memset(&key, 0, sizeof(key));
1819 key.len = (u32) sme->key_len;
1820 key.index = (u32) sme->key_idx;
1821 if (key.len > sizeof(key.data)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001822 brcmf_err("Too long key length (%u)\n", key.len);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001823 return -EINVAL;
1824 }
1825 memcpy(key.data, sme->key, key.len);
1826 key.flags = BRCMF_PRIMARY_KEY;
1827 switch (sec->cipher_pairwise) {
1828 case WLAN_CIPHER_SUITE_WEP40:
1829 key.algo = CRYPTO_ALGO_WEP1;
1830 break;
1831 case WLAN_CIPHER_SUITE_WEP104:
1832 key.algo = CRYPTO_ALGO_WEP128;
1833 break;
1834 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01001835 brcmf_err("Invalid algorithm (%d)\n",
1836 sme->crypto.ciphers_pairwise[0]);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001837 return -EINVAL;
1838 }
1839 /* Set the new key/index */
Arend van Spriel16886732012-12-05 15:26:04 +01001840 brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
1841 key.len, key.index, key.algo);
1842 brcmf_dbg(CONN, "key \"%s\"\n", key.data);
Hante Meuleman118eb302014-12-21 12:43:49 +01001843 err = send_key_to_dongle(netdev_priv(ndev), &key);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001844 if (err)
1845 return err;
1846
1847 if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
Arend van Spriel16886732012-12-05 15:26:04 +01001848 brcmf_dbg(CONN, "set auth_type to shared key\n");
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001849 val = WL_AUTH_SHARED_KEY; /* shared key */
Arend van Sprielac24be62012-10-22 10:36:23 -07001850 err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "auth", val);
Hante Meulemanf09d0c02012-09-27 14:17:48 +02001851 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01001852 brcmf_err("set auth failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001853 }
1854 return err;
1855}
1856
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001857static
1858enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
1859 enum nl80211_auth_type type)
1860{
Arend van Sprielc08437b2014-07-12 08:49:39 +02001861 if (type == NL80211_AUTHTYPE_AUTOMATIC &&
1862 brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
1863 brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
1864 type = NL80211_AUTHTYPE_OPEN_SYSTEM;
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001865 }
1866 return type;
1867}
1868
Arend van Spriel7705ba62016-04-17 16:44:58 +02001869static void brcmf_set_join_pref(struct brcmf_if *ifp,
1870 struct cfg80211_bss_selection *bss_select)
1871{
1872 struct brcmf_join_pref_params join_pref_params[2];
1873 enum nl80211_band band;
1874 int err, i = 0;
1875
1876 join_pref_params[i].len = 2;
1877 join_pref_params[i].rssi_gain = 0;
1878
1879 if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)
1880 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);
1881
1882 switch (bss_select->behaviour) {
1883 case __NL80211_BSS_SELECT_ATTR_INVALID:
1884 brcmf_c_set_joinpref_default(ifp);
1885 return;
1886 case NL80211_BSS_SELECT_ATTR_BAND_PREF:
1887 join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;
1888 band = bss_select->param.band_pref;
1889 join_pref_params[i].band = nl80211_band_to_fwil(band);
1890 i++;
1891 break;
1892 case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:
1893 join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;
1894 band = bss_select->param.adjust.band;
1895 join_pref_params[i].band = nl80211_band_to_fwil(band);
1896 join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;
1897 i++;
1898 break;
1899 case NL80211_BSS_SELECT_ATTR_RSSI:
1900 default:
1901 break;
1902 }
1903 join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;
1904 join_pref_params[i].len = 2;
1905 join_pref_params[i].rssi_gain = 0;
1906 join_pref_params[i].band = 0;
1907 err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
1908 sizeof(join_pref_params));
1909 if (err)
1910 brcmf_err("Set join_pref error (%d)\n", err);
1911}
1912
Arend van Spriel5b435de2011-10-05 13:19:03 +02001913static s32
1914brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001915 struct cfg80211_connect_params *sme)
Arend van Spriel5b435de2011-10-05 13:19:03 +02001916{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001917 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07001918 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001919 struct ieee80211_channel *chan = sme->channel;
1920 struct brcmf_join_params join_params;
1921 size_t join_params_size;
Johannes Berg4b5800f2014-01-15 14:55:59 +01001922 const struct brcmf_tlv *rsn_ie;
1923 const struct brcmf_vs_tlv *wpa_ie;
1924 const void *ie;
Hante Meuleman89286dc2013-02-08 15:53:46 +01001925 u32 ie_len;
1926 struct brcmf_ext_join_params_le *ext_join_params;
Hante Meuleman17012612013-02-06 18:40:44 +01001927 u16 chanspec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001928 s32 err = 0;
Hante Meulemane9a6ca82015-11-25 11:32:37 +01001929 u32 ssid_len;
Arend van Spriel5b435de2011-10-05 13:19:03 +02001930
Arend van Sprield96b8012012-12-05 15:26:02 +01001931 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07001932 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02001933 return -EIO;
1934
1935 if (!sme->ssid) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001936 brcmf_err("Invalid ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02001937 return -EOPNOTSUPP;
1938 }
1939
Hante Meuleman89286dc2013-02-08 15:53:46 +01001940 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
1941 /* A normal (non P2P) connection request setup. */
1942 ie = NULL;
1943 ie_len = 0;
1944 /* find the WPA_IE */
1945 wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
1946 if (wpa_ie) {
1947 ie = wpa_ie;
1948 ie_len = wpa_ie->len + TLV_HDR_LEN;
1949 } else {
1950 /* find the RSN_IE */
Johannes Berg4b5800f2014-01-15 14:55:59 +01001951 rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
1952 sme->ie_len,
Hante Meuleman89286dc2013-02-08 15:53:46 +01001953 WLAN_EID_RSN);
1954 if (rsn_ie) {
1955 ie = rsn_ie;
1956 ie_len = rsn_ie->len + TLV_HDR_LEN;
1957 }
1958 }
1959 brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
1960 }
1961
1962 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
1963 sme->ie, sme->ie_len);
1964 if (err)
1965 brcmf_err("Set Assoc REQ IE Failed\n");
1966 else
1967 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
1968
Arend van Sprielc1179032012-10-22 13:55:33 -07001969 set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001970
1971 if (chan) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001972 cfg->channel =
Arend van Spriel5b435de2011-10-05 13:19:03 +02001973 ieee80211_frequency_to_channel(chan->center_freq);
Franky Lin83cf17a2013-04-11 13:28:50 +02001974 chanspec = channel_to_chanspec(&cfg->d11inf, chan);
Hante Meuleman17012612013-02-06 18:40:44 +01001975 brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
1976 cfg->channel, chan->center_freq, chanspec);
1977 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02001978 cfg->channel = 0;
Hante Meuleman17012612013-02-06 18:40:44 +01001979 chanspec = 0;
1980 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02001981
Arend van Spriel647c9ae2012-12-05 15:26:01 +01001982 brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001983
1984 err = brcmf_set_wpa_version(ndev, sme);
1985 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001986 brcmf_err("wl_set_wpa_version failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001987 goto done;
1988 }
1989
Arend van Sprielcbb1ec92013-02-06 18:40:47 +01001990 sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001991 err = brcmf_set_auth_type(ndev, sme);
1992 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001993 brcmf_err("wl_set_auth_type failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001994 goto done;
1995 }
1996
Hante Meuleman240d61a2016-02-17 11:27:10 +01001997 err = brcmf_set_wsec_mode(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02001998 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01001999 brcmf_err("wl_set_set_cipher failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002000 goto done;
2001 }
2002
2003 err = brcmf_set_key_mgmt(ndev, sme);
2004 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002005 brcmf_err("wl_set_key_mgmt failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002006 goto done;
2007 }
2008
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002009 err = brcmf_set_sharedkey(ndev, sme);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002010 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002011 brcmf_err("brcmf_set_sharedkey failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002012 goto done;
2013 }
2014
Hante Meuleman89286dc2013-02-08 15:53:46 +01002015 /* Join with specific BSSID and cached SSID
2016 * If SSID is zero join based on BSSID only
2017 */
2018 join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
2019 offsetof(struct brcmf_assoc_params_le, chanspec_list);
2020 if (cfg->channel)
2021 join_params_size += sizeof(u16);
2022 ext_join_params = kzalloc(join_params_size, GFP_KERNEL);
2023 if (ext_join_params == NULL) {
2024 err = -ENOMEM;
2025 goto done;
2026 }
Hante Meulemane9a6ca82015-11-25 11:32:37 +01002027 ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
2028 ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
2029 memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
2030 if (ssid_len < IEEE80211_MAX_SSID_LEN)
2031 brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
2032 ext_join_params->ssid_le.SSID, ssid_len);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01002033
Hante Meuleman89286dc2013-02-08 15:53:46 +01002034 /* Set up join scan parameters */
2035 ext_join_params->scan_le.scan_type = -1;
Hante Meuleman89286dc2013-02-08 15:53:46 +01002036 ext_join_params->scan_le.home_time = cpu_to_le32(-1);
2037
2038 if (sme->bssid)
2039 memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
2040 else
Joe Perches93803b32015-03-02 19:54:49 -08002041 eth_broadcast_addr(ext_join_params->assoc_le.bssid);
Hante Meuleman89286dc2013-02-08 15:53:46 +01002042
2043 if (cfg->channel) {
2044 ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
2045
2046 ext_join_params->assoc_le.chanspec_list[0] =
2047 cpu_to_le16(chanspec);
Hante Meuleman63dd99e2014-03-15 17:18:19 +01002048 /* Increase dwell time to receive probe response or detect
2049 * beacon from target AP at a noisy air only during connect
2050 * command.
2051 */
2052 ext_join_params->scan_le.active_time =
2053 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
2054 ext_join_params->scan_le.passive_time =
2055 cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
2056 /* To sync with presence period of VSDB GO send probe request
2057 * more frequently. Probe request will be stopped when it gets
2058 * probe response from target AP/GO.
2059 */
2060 ext_join_params->scan_le.nprobes =
2061 cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
2062 BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
2063 } else {
2064 ext_join_params->scan_le.active_time = cpu_to_le32(-1);
2065 ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
2066 ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
Hante Meuleman89286dc2013-02-08 15:53:46 +01002067 }
2068
Arend van Spriel7705ba62016-04-17 16:44:58 +02002069 brcmf_set_join_pref(ifp, &sme->bss_select);
2070
Hante Meuleman89286dc2013-02-08 15:53:46 +01002071 err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
2072 join_params_size);
2073 kfree(ext_join_params);
2074 if (!err)
2075 /* This is it. join command worked, we are done */
2076 goto done;
2077
2078 /* join command failed, fallback to set ssid */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002079 memset(&join_params, 0, sizeof(join_params));
2080 join_params_size = sizeof(join_params.ssid_le);
2081
Hante Meulemane9a6ca82015-11-25 11:32:37 +01002082 memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
2083 join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002084
Hante Meuleman89286dc2013-02-08 15:53:46 +01002085 if (sme->bssid)
2086 memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
2087 else
Joe Perches93803b32015-03-02 19:54:49 -08002088 eth_broadcast_addr(join_params.params_le.bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002089
Hante Meuleman17012612013-02-06 18:40:44 +01002090 if (cfg->channel) {
2091 join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
2092 join_params.params_le.chanspec_num = cpu_to_le32(1);
2093 join_params_size += sizeof(join_params.params_le);
2094 }
Arend van Sprielc1179032012-10-22 13:55:33 -07002095 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002096 &join_params, join_params_size);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002097 if (err)
Hante Meuleman89286dc2013-02-08 15:53:46 +01002098 brcmf_err("BRCMF_C_SET_SSID failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002099
2100done:
2101 if (err)
Arend van Sprielc1179032012-10-22 13:55:33 -07002102 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01002103 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002104 return err;
2105}
2106
2107static s32
2108brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
2109 u16 reason_code)
2110{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002111 struct brcmf_if *ifp = netdev_priv(ndev);
2112 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002113 struct brcmf_scb_val_le scbval;
2114 s32 err = 0;
2115
Arend van Sprield96b8012012-12-05 15:26:02 +01002116 brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
Arend van Sprielce81e312012-10-22 13:55:37 -07002117 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002118 return -EIO;
2119
Arend van Sprielc1179032012-10-22 13:55:33 -07002120 clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Spriel4f3fff12014-11-20 22:27:02 +01002121 clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
Johannes Berg80279fb2015-05-22 16:22:20 +02002122 cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002123
Arend van Spriel06bb1232012-09-27 14:17:56 +02002124 memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002125 scbval.val = cpu_to_le32(reason_code);
Arend van Sprielc1179032012-10-22 13:55:33 -07002126 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
Arend van Sprielac24be62012-10-22 10:36:23 -07002127 &scbval, sizeof(scbval));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002128 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002129 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002130
Arend van Sprield96b8012012-12-05 15:26:02 +01002131 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002132 return err;
2133}
2134
2135static s32
Johannes Bergc8442112012-10-24 10:17:18 +02002136brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
Luis R. Rodriguezd3f31132011-11-28 16:38:48 -05002137 enum nl80211_tx_power_setting type, s32 mbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002138{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002139 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002140 struct net_device *ndev = cfg_to_ndev(cfg);
2141 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002142 s32 err;
2143 s32 disable;
2144 u32 qdbm = 127;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002145
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002146 brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
Arend van Sprielce81e312012-10-22 13:55:37 -07002147 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002148 return -EIO;
2149
2150 switch (type) {
2151 case NL80211_TX_POWER_AUTOMATIC:
2152 break;
2153 case NL80211_TX_POWER_LIMITED:
Arend van Spriel5b435de2011-10-05 13:19:03 +02002154 case NL80211_TX_POWER_FIXED:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002155 if (mbm < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002156 brcmf_err("TX_POWER_FIXED - dbm is negative\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002157 err = -EINVAL;
2158 goto done;
2159 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002160 qdbm = MBM_TO_DBM(4 * mbm);
2161 if (qdbm > 127)
2162 qdbm = 127;
2163 qdbm |= WL_TXPWR_OVERRIDE;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002164 break;
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002165 default:
2166 brcmf_err("Unsupported type %d\n", type);
2167 err = -EINVAL;
2168 goto done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002169 }
2170 /* Make sure radio is off or on as far as software is concerned */
2171 disable = WL_RADIO_SW_DISABLE << 16;
Arend van Sprielac24be62012-10-22 10:36:23 -07002172 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002173 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002174 brcmf_err("WLC_SET_RADIO error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002175
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002176 err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002177 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002178 brcmf_err("qtxpower error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002179
2180done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002181 brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002182 return err;
2183}
2184
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002185static s32
2186brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
2187 s32 *dbm)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002188{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002189 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002190 struct net_device *ndev = cfg_to_ndev(cfg);
2191 struct brcmf_if *ifp = netdev_priv(ndev);
2192 s32 qdbm = 0;
2193 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002194
Arend van Sprield96b8012012-12-05 15:26:02 +01002195 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07002196 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002197 return -EIO;
2198
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002199 err = brcmf_fil_iovar_int_get(ifp, "qtxpower", &qdbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002200 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002201 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002202 goto done;
2203 }
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002204 *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002205
2206done:
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002207 brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002208 return err;
2209}
2210
2211static s32
2212brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman60dc35e2015-09-18 22:08:06 +02002213 u8 key_idx, bool unicast, bool multicast)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002214{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002215 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002216 u32 index;
2217 u32 wsec;
2218 s32 err = 0;
2219
Arend van Sprield96b8012012-12-05 15:26:02 +01002220 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002221 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002222 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002223 return -EIO;
2224
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002225 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002226 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002227 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002228 goto done;
2229 }
2230
2231 if (wsec & WEP_ENABLED) {
2232 /* Just select a new current key */
2233 index = key_idx;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002234 err = brcmf_fil_cmd_int_set(ifp,
Arend van Sprielac24be62012-10-22 10:36:23 -07002235 BRCMF_C_SET_KEY_PRIMARY, index);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002236 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002237 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002238 }
2239done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002240 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002241 return err;
2242}
2243
2244static s32
Hante Meuleman219e0f72016-02-17 11:27:09 +01002245brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2246 u8 key_idx, bool pairwise, const u8 *mac_addr)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002247{
Hante Meuleman992f6062013-04-02 21:06:17 +02002248 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman240d61a2016-02-17 11:27:10 +01002249 struct brcmf_wsec_key *key;
2250 s32 err;
Hante Meuleman219e0f72016-02-17 11:27:09 +01002251
2252 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman240d61a2016-02-17 11:27:10 +01002253 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
2254
Hante Meuleman219e0f72016-02-17 11:27:09 +01002255 if (!check_vif_up(ifp->vif))
2256 return -EIO;
2257
2258 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2259 /* we ignore this key index in this case */
2260 return -EINVAL;
2261 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002262
Hante Meuleman240d61a2016-02-17 11:27:10 +01002263 key = &ifp->vif->profile.key[key_idx];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002264
Hante Meuleman240d61a2016-02-17 11:27:10 +01002265 if (key->algo == CRYPTO_ALGO_OFF) {
2266 brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
2267 return -EINVAL;
2268 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002269
Hante Meuleman240d61a2016-02-17 11:27:10 +01002270 memset(key, 0, sizeof(*key));
2271 key->index = (u32)key_idx;
2272 key->flags = BRCMF_PRIMARY_KEY;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002273
Hante Meuleman240d61a2016-02-17 11:27:10 +01002274 /* Clear the key/index */
2275 err = send_key_to_dongle(ifp, key);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002276
Hante Meuleman219e0f72016-02-17 11:27:09 +01002277 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002278 return err;
2279}
2280
2281static s32
2282brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman240d61a2016-02-17 11:27:10 +01002283 u8 key_idx, bool pairwise, const u8 *mac_addr,
2284 struct key_params *params)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002285{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002286 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman118eb302014-12-21 12:43:49 +01002287 struct brcmf_wsec_key *key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002288 s32 val;
2289 s32 wsec;
Hante Meuleman219e0f72016-02-17 11:27:09 +01002290 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002291 u8 keybuf[8];
Hante Meuleman219e0f72016-02-17 11:27:09 +01002292 bool ext_key;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002293
Arend van Sprield96b8012012-12-05 15:26:02 +01002294 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002295 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002296 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002297 return -EIO;
2298
Hante Meuleman118eb302014-12-21 12:43:49 +01002299 if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2300 /* we ignore this key index in this case */
2301 brcmf_err("invalid key index (%d)\n", key_idx);
2302 return -EINVAL;
2303 }
2304
Hante Meuleman219e0f72016-02-17 11:27:09 +01002305 if (params->key_len == 0)
2306 return brcmf_cfg80211_del_key(wiphy, ndev, key_idx, pairwise,
2307 mac_addr);
2308
2309 if (params->key_len > sizeof(key->data)) {
2310 brcmf_err("Too long key length (%u)\n", params->key_len);
2311 return -EINVAL;
2312 }
2313
2314 ext_key = false;
2315 if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
2316 (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
2317 brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
2318 ext_key = true;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002319 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002320
Hante Meuleman118eb302014-12-21 12:43:49 +01002321 key = &ifp->vif->profile.key[key_idx];
2322 memset(key, 0, sizeof(*key));
Hante Meuleman219e0f72016-02-17 11:27:09 +01002323 if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
2324 memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
Hante Meuleman118eb302014-12-21 12:43:49 +01002325 key->len = params->key_len;
2326 key->index = key_idx;
Hante Meuleman118eb302014-12-21 12:43:49 +01002327 memcpy(key->data, params->key, key->len);
Hante Meuleman219e0f72016-02-17 11:27:09 +01002328 if (!ext_key)
2329 key->flags = BRCMF_PRIMARY_KEY;
Hante Meuleman118eb302014-12-21 12:43:49 +01002330
Arend van Spriel5b435de2011-10-05 13:19:03 +02002331 switch (params->cipher) {
2332 case WLAN_CIPHER_SUITE_WEP40:
Hante Meuleman118eb302014-12-21 12:43:49 +01002333 key->algo = CRYPTO_ALGO_WEP1;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002334 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002335 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002336 break;
2337 case WLAN_CIPHER_SUITE_WEP104:
Hante Meuleman118eb302014-12-21 12:43:49 +01002338 key->algo = CRYPTO_ALGO_WEP128;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002339 val = WEP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002340 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002341 break;
2342 case WLAN_CIPHER_SUITE_TKIP:
Arend van Spriel967fe2c2014-03-15 17:18:21 +01002343 if (!brcmf_is_apmode(ifp->vif)) {
Hante Meuleman992f6062013-04-02 21:06:17 +02002344 brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
Hante Meuleman118eb302014-12-21 12:43:49 +01002345 memcpy(keybuf, &key->data[24], sizeof(keybuf));
2346 memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
2347 memcpy(&key->data[16], keybuf, sizeof(keybuf));
Hante Meuleman1a873342012-09-27 14:17:54 +02002348 }
Hante Meuleman118eb302014-12-21 12:43:49 +01002349 key->algo = CRYPTO_ALGO_TKIP;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002350 val = TKIP_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002351 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002352 break;
2353 case WLAN_CIPHER_SUITE_AES_CMAC:
Hante Meuleman118eb302014-12-21 12:43:49 +01002354 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002355 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002356 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002357 break;
2358 case WLAN_CIPHER_SUITE_CCMP:
Hante Meuleman118eb302014-12-21 12:43:49 +01002359 key->algo = CRYPTO_ALGO_AES_CCM;
Hante Meulemanf09d0c02012-09-27 14:17:48 +02002360 val = AES_ENABLED;
Arend van Spriel16886732012-12-05 15:26:04 +01002361 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002362 break;
2363 default:
Arend van Spriel57d6e912012-12-05 15:26:00 +01002364 brcmf_err("Invalid cipher (0x%x)\n", params->cipher);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002365 err = -EINVAL;
2366 goto done;
2367 }
2368
Hante Meuleman118eb302014-12-21 12:43:49 +01002369 err = send_key_to_dongle(ifp, key);
Hante Meuleman219e0f72016-02-17 11:27:09 +01002370 if (ext_key || err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002371 goto done;
2372
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002373 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002374 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002375 brcmf_err("get wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002376 goto done;
2377 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002378 wsec |= val;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002379 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002380 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002381 brcmf_err("set wsec error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002382 goto done;
2383 }
2384
Arend van Spriel5b435de2011-10-05 13:19:03 +02002385done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002386 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002387 return err;
2388}
2389
2390static s32
Hante Meuleman240d61a2016-02-17 11:27:10 +01002391brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, u8 key_idx,
2392 bool pairwise, const u8 *mac_addr, void *cookie,
2393 void (*callback)(void *cookie,
2394 struct key_params *params))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002395{
2396 struct key_params params;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002397 struct brcmf_if *ifp = netdev_priv(ndev);
2398 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002399 struct brcmf_cfg80211_security *sec;
2400 s32 wsec;
2401 s32 err = 0;
2402
Arend van Sprield96b8012012-12-05 15:26:02 +01002403 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel16886732012-12-05 15:26:04 +01002404 brcmf_dbg(CONN, "key index (%d)\n", key_idx);
Arend van Sprielce81e312012-10-22 13:55:37 -07002405 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002406 return -EIO;
2407
2408 memset(&params, 0, sizeof(params));
2409
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002410 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002411 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002412 brcmf_err("WLC_GET_WSEC error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002413 /* Ignore this error, may happen during DISASSOC */
2414 err = -EAGAIN;
2415 goto done;
2416 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002417 if (wsec & WEP_ENABLED) {
Arend van Spriel06bb1232012-09-27 14:17:56 +02002418 sec = &profile->sec;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002419 if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
2420 params.cipher = WLAN_CIPHER_SUITE_WEP40;
Arend van Spriel16886732012-12-05 15:26:04 +01002421 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002422 } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
2423 params.cipher = WLAN_CIPHER_SUITE_WEP104;
Arend van Spriel16886732012-12-05 15:26:04 +01002424 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002425 }
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002426 } else if (wsec & TKIP_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002427 params.cipher = WLAN_CIPHER_SUITE_TKIP;
Arend van Spriel16886732012-12-05 15:26:04 +01002428 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002429 } else if (wsec & AES_ENABLED) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002430 params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
Arend van Spriel16886732012-12-05 15:26:04 +01002431 brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
Hante Meulemanc5bf53a2013-04-02 21:06:19 +02002432 } else {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002433 brcmf_err("Invalid algo (0x%x)\n", wsec);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002434 err = -EINVAL;
2435 goto done;
2436 }
2437 callback(cookie, &params);
2438
2439done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002440 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002441 return err;
2442}
2443
2444static s32
2445brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
Hante Meuleman240d61a2016-02-17 11:27:10 +01002446 struct net_device *ndev, u8 key_idx)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002447{
Hante Meuleman240d61a2016-02-17 11:27:10 +01002448 struct brcmf_if *ifp = netdev_priv(ndev);
2449
2450 brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
2451
2452 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
2453 return 0;
2454
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002455 brcmf_dbg(INFO, "Not supported\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002456
2457 return -EOPNOTSUPP;
2458}
2459
Hante Meuleman118eb302014-12-21 12:43:49 +01002460static void
2461brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
2462{
2463 s32 err;
2464 u8 key_idx;
2465 struct brcmf_wsec_key *key;
2466 s32 wsec;
2467
2468 for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
2469 key = &ifp->vif->profile.key[key_idx];
2470 if ((key->algo == CRYPTO_ALGO_WEP1) ||
2471 (key->algo == CRYPTO_ALGO_WEP128))
2472 break;
2473 }
2474 if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
2475 return;
2476
2477 err = send_key_to_dongle(ifp, key);
2478 if (err) {
2479 brcmf_err("Setting WEP key failed (%d)\n", err);
2480 return;
2481 }
2482 err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
2483 if (err) {
2484 brcmf_err("get wsec error (%d)\n", err);
2485 return;
2486 }
2487 wsec |= WEP_ENABLED;
2488 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
2489 if (err)
2490 brcmf_err("set wsec error (%d)\n", err);
2491}
2492
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002493static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
2494{
2495 struct nl80211_sta_flag_update *sfu;
2496
2497 brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
2498 si->filled |= BIT(NL80211_STA_INFO_STA_FLAGS);
2499 sfu = &si->sta_flags;
2500 sfu->mask = BIT(NL80211_STA_FLAG_WME) |
2501 BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2502 BIT(NL80211_STA_FLAG_ASSOCIATED) |
2503 BIT(NL80211_STA_FLAG_AUTHORIZED);
2504 if (fw_sta_flags & BRCMF_STA_WME)
2505 sfu->set |= BIT(NL80211_STA_FLAG_WME);
2506 if (fw_sta_flags & BRCMF_STA_AUTHE)
2507 sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
2508 if (fw_sta_flags & BRCMF_STA_ASSOC)
2509 sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
2510 if (fw_sta_flags & BRCMF_STA_AUTHO)
2511 sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
2512}
2513
2514static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
2515{
2516 struct {
2517 __le32 len;
2518 struct brcmf_bss_info_le bss_le;
2519 } *buf;
2520 u16 capability;
2521 int err;
2522
2523 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2524 if (!buf)
2525 return;
2526
2527 buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
2528 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
2529 WL_BSS_INFO_MAX);
2530 if (err) {
2531 brcmf_err("Failed to get bss info (%d)\n", err);
Rafał Miłecki23e9c122016-09-21 08:23:24 +02002532 goto out_kfree;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002533 }
2534 si->filled |= BIT(NL80211_STA_INFO_BSS_PARAM);
2535 si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
2536 si->bss_param.dtim_period = buf->bss_le.dtim_period;
2537 capability = le16_to_cpu(buf->bss_le.capability);
2538 if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
2539 si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
2540 if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
2541 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
2542 if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
2543 si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
Rafał Miłecki23e9c122016-09-21 08:23:24 +02002544
2545out_kfree:
2546 kfree(buf);
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002547}
2548
Arend van Spriel5b435de2011-10-05 13:19:03 +02002549static s32
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002550brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
2551 struct station_info *sinfo)
2552{
2553 struct brcmf_scb_val_le scbval;
2554 struct brcmf_pktcnt_le pktcnt;
2555 s32 err;
2556 u32 rate;
2557 u32 rssi;
2558
2559 /* Get the current tx rate */
2560 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
2561 if (err < 0) {
2562 brcmf_err("BRCMF_C_GET_RATE error (%d)\n", err);
2563 return err;
2564 }
2565 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
2566 sinfo->txrate.legacy = rate * 5;
2567
2568 memset(&scbval, 0, sizeof(scbval));
2569 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
2570 sizeof(scbval));
2571 if (err) {
2572 brcmf_err("BRCMF_C_GET_RSSI error (%d)\n", err);
2573 return err;
2574 }
2575 rssi = le32_to_cpu(scbval.val);
2576 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2577 sinfo->signal = rssi;
2578
2579 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
2580 sizeof(pktcnt));
2581 if (err) {
2582 brcmf_err("BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
2583 return err;
2584 }
2585 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS) |
2586 BIT(NL80211_STA_INFO_RX_DROP_MISC) |
2587 BIT(NL80211_STA_INFO_TX_PACKETS) |
2588 BIT(NL80211_STA_INFO_TX_FAILED);
2589 sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
2590 sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
2591 sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
2592 sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
2593
2594 return 0;
2595}
2596
2597static s32
Arend van Spriel5b435de2011-10-05 13:19:03 +02002598brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +02002599 const u8 *mac, struct station_info *sinfo)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002600{
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002601 struct brcmf_if *ifp = netdev_priv(ndev);
Jaap Jan Meijer94abd772016-05-12 18:25:08 +02002602 struct brcmf_scb_val_le scb_val;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002603 s32 err = 0;
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002604 struct brcmf_sta_info_le sta_info_le;
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002605 u32 sta_flags;
2606 u32 is_tdls_peer;
Hante Meulemancae355d2015-10-08 20:33:17 +02002607 s32 total_rssi;
2608 s32 count_rssi;
Jaap Jan Meijer94abd772016-05-12 18:25:08 +02002609 int rssi;
Hante Meulemancae355d2015-10-08 20:33:17 +02002610 u32 i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002611
Arend van Sprield96b8012012-12-05 15:26:02 +01002612 brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
Arend van Sprielce81e312012-10-22 13:55:37 -07002613 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002614 return -EIO;
2615
Hante Meuleman3f5893d2016-01-02 09:41:37 +01002616 if (brcmf_is_ibssmode(ifp->vif))
2617 return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
2618
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002619 memset(&sta_info_le, 0, sizeof(sta_info_le));
2620 memcpy(&sta_info_le, mac, ETH_ALEN);
2621 err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
2622 &sta_info_le,
2623 sizeof(sta_info_le));
2624 is_tdls_peer = !err;
2625 if (err) {
Arend van Spriel0abb5f212012-10-22 13:55:32 -07002626 err = brcmf_fil_iovar_data_get(ifp, "sta_info",
Arend van Sprielac24be62012-10-22 10:36:23 -07002627 &sta_info_le,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002628 sizeof(sta_info_le));
Hante Meuleman1a873342012-09-27 14:17:54 +02002629 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002630 brcmf_err("GET STA INFO failed, %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02002631 goto done;
Hante Meuleman7f6c5622012-08-30 10:05:37 +02002632 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002633 }
2634 brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
2635 sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME);
2636 sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
2637 sta_flags = le32_to_cpu(sta_info_le.flags);
2638 brcmf_convert_sta_flags(sta_flags, sinfo);
2639 sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2640 if (is_tdls_peer)
2641 sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
2642 else
2643 sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2644 if (sta_flags & BRCMF_STA_ASSOC) {
2645 sinfo->filled |= BIT(NL80211_STA_INFO_CONNECTED_TIME);
2646 sinfo->connected_time = le32_to_cpu(sta_info_le.in);
2647 brcmf_fill_bss_param(ifp, sinfo);
2648 }
2649 if (sta_flags & BRCMF_STA_SCBSTATS) {
2650 sinfo->filled |= BIT(NL80211_STA_INFO_TX_FAILED);
2651 sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
2652 sinfo->filled |= BIT(NL80211_STA_INFO_TX_PACKETS);
2653 sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
2654 sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
2655 sinfo->filled |= BIT(NL80211_STA_INFO_RX_PACKETS);
2656 sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
2657 sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
2658 if (sinfo->tx_packets) {
Johannes Berg319090b2014-11-17 14:08:11 +01002659 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002660 sinfo->txrate.legacy =
2661 le32_to_cpu(sta_info_le.tx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002662 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002663 if (sinfo->rx_packets) {
2664 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BITRATE);
Hante Meuleman124d5172015-10-08 20:33:16 +02002665 sinfo->rxrate.legacy =
2666 le32_to_cpu(sta_info_le.rx_rate) / 100;
Hante Meuleman1a873342012-09-27 14:17:54 +02002667 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002668 if (le16_to_cpu(sta_info_le.ver) >= 4) {
2669 sinfo->filled |= BIT(NL80211_STA_INFO_TX_BYTES);
2670 sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
2671 sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES);
2672 sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
2673 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002674 total_rssi = 0;
2675 count_rssi = 0;
2676 for (i = 0; i < BRCMF_ANT_MAX; i++) {
2677 if (sta_info_le.rssi[i]) {
2678 sinfo->chain_signal_avg[count_rssi] =
2679 sta_info_le.rssi[i];
2680 sinfo->chain_signal[count_rssi] =
2681 sta_info_le.rssi[i];
2682 total_rssi += sta_info_le.rssi[i];
2683 count_rssi++;
2684 }
2685 }
2686 if (count_rssi) {
2687 sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL);
2688 sinfo->chains = count_rssi;
2689
2690 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2691 total_rssi /= count_rssi;
2692 sinfo->signal = total_rssi;
Jaap Jan Meijer94abd772016-05-12 18:25:08 +02002693 } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
2694 &ifp->vif->sme_state)) {
2695 memset(&scb_val, 0, sizeof(scb_val));
2696 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
2697 &scb_val, sizeof(scb_val));
2698 if (err) {
2699 brcmf_err("Could not get rssi (%d)\n", err);
2700 goto done;
2701 } else {
2702 rssi = le32_to_cpu(scb_val.val);
2703 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
2704 sinfo->signal = rssi;
2705 brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
2706 }
Hante Meulemancae355d2015-10-08 20:33:17 +02002707 }
Arend van Spriel1f0dc592015-06-11 00:12:19 +02002708 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02002709done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002710 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002711 return err;
2712}
2713
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02002714static int
2715brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
2716 int idx, u8 *mac, struct station_info *sinfo)
2717{
2718 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2719 struct brcmf_if *ifp = netdev_priv(ndev);
2720 s32 err;
2721
2722 brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
2723
2724 if (idx == 0) {
2725 cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
2726 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
2727 &cfg->assoclist,
2728 sizeof(cfg->assoclist));
2729 if (err) {
2730 brcmf_err("BRCMF_C_GET_ASSOCLIST unsupported, err=%d\n",
2731 err);
2732 cfg->assoclist.count = 0;
2733 return -EOPNOTSUPP;
2734 }
2735 }
2736 if (idx < le32_to_cpu(cfg->assoclist.count)) {
2737 memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
2738 return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
2739 }
2740 return -ENOENT;
2741}
2742
Arend van Spriel5b435de2011-10-05 13:19:03 +02002743static s32
2744brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
2745 bool enabled, s32 timeout)
2746{
2747 s32 pm;
2748 s32 err = 0;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002749 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielc1179032012-10-22 13:55:33 -07002750 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002751
Arend van Sprield96b8012012-12-05 15:26:02 +01002752 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002753
2754 /*
2755 * Powersave enable/disable request is coming from the
2756 * cfg80211 even before the interface is up. In that
2757 * scenario, driver will be storing the power save
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002758 * preference in cfg struct to apply this to
Arend van Spriel5b435de2011-10-05 13:19:03 +02002759 * FW later while initializing the dongle
2760 */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002761 cfg->pwr_save = enabled;
Arend van Sprielce81e312012-10-22 13:55:37 -07002762 if (!check_vif_up(ifp->vif)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02002763
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002764 brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002765 goto done;
2766 }
2767
2768 pm = enabled ? PM_FAST : PM_OFF;
Hante Meuleman102fd0d2013-05-27 21:09:59 +02002769 /* Do not enable the power save after assoc if it is a p2p interface */
2770 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
2771 brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
2772 pm = PM_OFF;
2773 }
Arend van Spriel647c9ae2012-12-05 15:26:01 +01002774 brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02002775
Arend van Sprielc1179032012-10-22 13:55:33 -07002776 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002777 if (err) {
2778 if (err == -ENODEV)
Arend van Spriel57d6e912012-12-05 15:26:00 +01002779 brcmf_err("net_device is not ready yet\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002780 else
Arend van Spriel57d6e912012-12-05 15:26:00 +01002781 brcmf_err("error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002782 }
2783done:
Arend van Sprield96b8012012-12-05 15:26:02 +01002784 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002785 return err;
2786}
2787
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002788static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
Roland Vossend34bf642011-10-18 14:03:01 +02002789 struct brcmf_bss_info_le *bi)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002790{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002791 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002792 struct ieee80211_channel *notify_channel;
2793 struct cfg80211_bss *bss;
2794 struct ieee80211_supported_band *band;
Franky Lin83cf17a2013-04-11 13:28:50 +02002795 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002796 u16 channel;
2797 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002798 u16 notify_capability;
2799 u16 notify_interval;
2800 u8 *notify_ie;
2801 size_t notify_ielen;
2802 s32 notify_signal;
2803
2804 if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002805 brcmf_err("Bss info is larger than buffer. Discarding\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002806 return 0;
2807 }
2808
Franky Lin83cf17a2013-04-11 13:28:50 +02002809 if (!bi->ctl_ch) {
2810 ch.chspec = le16_to_cpu(bi->chanspec);
2811 cfg->d11inf.decchspec(&ch);
Rafał Miłecki4712d882016-05-20 13:38:57 +02002812 bi->ctl_ch = ch.control_ch_num;
Franky Lin83cf17a2013-04-11 13:28:50 +02002813 }
2814 channel = bi->ctl_ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002815
2816 if (channel <= CH_MAX_2G_CHANNEL)
Johannes Berg57fbcce2016-04-12 15:56:15 +02002817 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002818 else
Johannes Berg57fbcce2016-04-12 15:56:15 +02002819 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002820
2821 freq = ieee80211_channel_to_frequency(channel, band->band);
2822 notify_channel = ieee80211_get_channel(wiphy, freq);
2823
Arend van Spriel5b435de2011-10-05 13:19:03 +02002824 notify_capability = le16_to_cpu(bi->capability);
2825 notify_interval = le16_to_cpu(bi->beacon_period);
2826 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2827 notify_ielen = le32_to_cpu(bi->ie_length);
2828 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2829
Arend van Spriel16886732012-12-05 15:26:04 +01002830 brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
2831 brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
2832 brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
2833 brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
2834 brcmf_dbg(CONN, "Signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002835
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002836 bss = cfg80211_inform_bss(wiphy, notify_channel,
2837 CFG80211_BSS_FTYPE_UNKNOWN,
2838 (const u8 *)bi->BSSID,
2839 0, notify_capability,
2840 notify_interval, notify_ie,
2841 notify_ielen, notify_signal,
2842 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002843
Franky Line78946e2011-11-10 20:30:34 +01002844 if (!bss)
2845 return -ENOMEM;
2846
Johannes Berg5b112d32013-02-01 01:49:58 +01002847 cfg80211_put_bss(wiphy, bss);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002848
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03002849 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002850}
2851
Roland Vossen6f09be02011-10-18 14:03:02 +02002852static struct brcmf_bss_info_le *
2853next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
2854{
2855 if (bss == NULL)
2856 return list->bss_info_le;
2857 return (struct brcmf_bss_info_le *)((unsigned long)bss +
2858 le32_to_cpu(bss->length));
2859}
2860
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002861static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002862{
2863 struct brcmf_scan_results *bss_list;
Roland Vossend34bf642011-10-18 14:03:01 +02002864 struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
Arend van Spriel5b435de2011-10-05 13:19:03 +02002865 s32 err = 0;
2866 int i;
2867
Hante Meulemanef8596e2014-09-30 10:23:13 +02002868 bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
Arend van Spriel0ecd8162012-11-05 16:22:11 -08002869 if (bss_list->count != 0 &&
2870 bss_list->version != BRCMF_BSS_INFO_VERSION) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002871 brcmf_err("Version %d != WL_BSS_INFO_VERSION\n",
2872 bss_list->version);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002873 return -EOPNOTSUPP;
2874 }
Arend van Spriel4e8a0082012-12-05 15:26:03 +01002875 brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
Hante Meulemanf07998952012-11-05 16:22:13 -08002876 for (i = 0; i < bss_list->count; i++) {
Roland Vossen6f09be02011-10-18 14:03:02 +02002877 bi = next_bss_le(bss_list, bi);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002878 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002879 if (err)
2880 break;
2881 }
2882 return err;
2883}
2884
Hante Meulemanb0a79082015-12-10 13:43:07 +01002885static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
2886 struct net_device *ndev, const u8 *bssid)
Arend van Spriel5b435de2011-10-05 13:19:03 +02002887{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002888 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002889 struct ieee80211_channel *notify_channel;
Roland Vossend34bf642011-10-18 14:03:01 +02002890 struct brcmf_bss_info_le *bi = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002891 struct ieee80211_supported_band *band;
Franky Line78946e2011-11-10 20:30:34 +01002892 struct cfg80211_bss *bss;
Franky Lin83cf17a2013-04-11 13:28:50 +02002893 struct brcmu_chan ch;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002894 u8 *buf = NULL;
2895 s32 err = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002896 u32 freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002897 u16 notify_capability;
2898 u16 notify_interval;
2899 u8 *notify_ie;
2900 size_t notify_ielen;
2901 s32 notify_signal;
2902
Arend van Sprield96b8012012-12-05 15:26:02 +01002903 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002904
2905 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
2906 if (buf == NULL) {
2907 err = -ENOMEM;
2908 goto CleanUp;
2909 }
2910
2911 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
2912
Arend van Sprielac24be62012-10-22 10:36:23 -07002913 err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
2914 buf, WL_BSS_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002915 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002916 brcmf_err("WLC_GET_BSS_INFO failed: %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002917 goto CleanUp;
2918 }
2919
Roland Vossend34bf642011-10-18 14:03:01 +02002920 bi = (struct brcmf_bss_info_le *)(buf + 4);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002921
Franky Lin83cf17a2013-04-11 13:28:50 +02002922 ch.chspec = le16_to_cpu(bi->chanspec);
2923 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002924
Franky Lin83cf17a2013-04-11 13:28:50 +02002925 if (ch.band == BRCMU_CHAN_BAND_2G)
Johannes Berg57fbcce2016-04-12 15:56:15 +02002926 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002927 else
Johannes Berg57fbcce2016-04-12 15:56:15 +02002928 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02002929
Rafał Miłecki4712d882016-05-20 13:38:57 +02002930 freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
Hante Meulemanb0a79082015-12-10 13:43:07 +01002931 cfg->channel = freq;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002932 notify_channel = ieee80211_get_channel(wiphy, freq);
2933
Arend van Spriel5b435de2011-10-05 13:19:03 +02002934 notify_capability = le16_to_cpu(bi->capability);
2935 notify_interval = le16_to_cpu(bi->beacon_period);
2936 notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
2937 notify_ielen = le32_to_cpu(bi->ie_length);
2938 notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
2939
Rafał Miłecki4712d882016-05-20 13:38:57 +02002940 brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq);
Arend van Spriel16886732012-12-05 15:26:04 +01002941 brcmf_dbg(CONN, "capability: %X\n", notify_capability);
2942 brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
2943 brcmf_dbg(CONN, "signal: %d\n", notify_signal);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002944
Johannes Berg5bc8c1f2014-08-12 21:01:28 +02002945 bss = cfg80211_inform_bss(wiphy, notify_channel,
2946 CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
2947 notify_capability, notify_interval,
2948 notify_ie, notify_ielen, notify_signal,
2949 GFP_KERNEL);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002950
Franky Line78946e2011-11-10 20:30:34 +01002951 if (!bss) {
2952 err = -ENOMEM;
2953 goto CleanUp;
2954 }
2955
Johannes Berg5b112d32013-02-01 01:49:58 +01002956 cfg80211_put_bss(wiphy, bss);
Franky Line78946e2011-11-10 20:30:34 +01002957
Arend van Spriel5b435de2011-10-05 13:19:03 +02002958CleanUp:
2959
2960 kfree(buf);
2961
Arend van Sprield96b8012012-12-05 15:26:02 +01002962 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02002963
2964 return err;
2965}
2966
Hante Meuleman89286dc2013-02-08 15:53:46 +01002967static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
2968 struct brcmf_if *ifp)
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002969{
Roland Vossend34bf642011-10-18 14:03:01 +02002970 struct brcmf_bss_info_le *bi;
Johannes Berg4b5800f2014-01-15 14:55:59 +01002971 const struct brcmf_tlv *tim;
Arend van Spriel5b435de2011-10-05 13:19:03 +02002972 u16 beacon_interval;
2973 u8 dtim_period;
2974 size_t ie_len;
2975 u8 *ie;
2976 s32 err = 0;
2977
Arend van Sprield96b8012012-12-05 15:26:02 +01002978 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01002979 if (brcmf_is_ibssmode(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02002980 return err;
2981
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002982 *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
Arend van Sprielac24be62012-10-22 10:36:23 -07002983 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07002984 cfg->extra_buf, WL_EXTRA_BUF_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002985 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01002986 brcmf_err("Could not get bss info %d\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002987 goto update_bss_info_out;
2988 }
2989
Arend van Spriel27a68fe2012-09-27 14:17:55 +02002990 bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
2991 err = brcmf_inform_single_bss(cfg, bi);
Arend van Spriel5b435de2011-10-05 13:19:03 +02002992 if (err)
2993 goto update_bss_info_out;
2994
2995 ie = ((u8 *)bi) + le16_to_cpu(bi->ie_offset);
2996 ie_len = le32_to_cpu(bi->ie_length);
2997 beacon_interval = le16_to_cpu(bi->beacon_period);
2998
Alwin Beukersf8e4b412011-10-12 20:51:28 +02002999 tim = brcmf_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003000 if (tim)
3001 dtim_period = tim->data[1];
3002 else {
3003 /*
3004 * active scan was done so we could not get dtim
3005 * information out of probe response.
3006 * so we speficially query dtim information to dongle.
3007 */
3008 u32 var;
Arend van Sprielac24be62012-10-22 10:36:23 -07003009 err = brcmf_fil_iovar_int_get(ifp, "dtim_assoc", &var);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003010 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003011 brcmf_err("wl dtim_assoc failed (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003012 goto update_bss_info_out;
3013 }
3014 dtim_period = (u8)var;
3015 }
3016
Arend van Spriel5b435de2011-10-05 13:19:03 +02003017update_bss_info_out:
Arend van Sprield96b8012012-12-05 15:26:02 +01003018 brcmf_dbg(TRACE, "Exit");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003019 return err;
3020}
3021
Hante Meuleman18e2f612013-02-08 15:53:49 +01003022void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003023{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003024 struct escan_info *escan = &cfg->escan_info;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003025
Arend van Sprielc1179032012-10-22 13:55:33 -07003026 set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003027 if (cfg->int_escan_map || cfg->scan_request) {
Arend van Spriel108a4be2012-09-19 22:21:07 +02003028 escan->escan_state = WL_ESCAN_STATE_IDLE;
Arend van Spriela0f472a2013-04-05 10:57:49 +02003029 brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
Arend van Spriel108a4be2012-09-19 22:21:07 +02003030 }
Arend van Sprielc1179032012-10-22 13:55:33 -07003031 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3032 clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003033}
3034
Hante Meulemane756af52012-09-11 21:18:52 +02003035static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
3036{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003037 struct brcmf_cfg80211_info *cfg =
3038 container_of(work, struct brcmf_cfg80211_info,
Hante Meulemane756af52012-09-11 21:18:52 +02003039 escan_timeout_work);
3040
Hante Meulemanef8596e2014-09-30 10:23:13 +02003041 brcmf_inform_bss(cfg);
Arend van Spriela0f472a2013-04-05 10:57:49 +02003042 brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
Hante Meulemane756af52012-09-11 21:18:52 +02003043}
3044
3045static void brcmf_escan_timeout(unsigned long data)
3046{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003047 struct brcmf_cfg80211_info *cfg =
3048 (struct brcmf_cfg80211_info *)data;
Hante Meulemane756af52012-09-11 21:18:52 +02003049
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003050 if (cfg->int_escan_map || cfg->scan_request) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003051 brcmf_err("timer expired\n");
Hante Meulemanf07998952012-11-05 16:22:13 -08003052 schedule_work(&cfg->escan_timeout_work);
Hante Meulemane756af52012-09-11 21:18:52 +02003053 }
3054}
3055
3056static s32
Franky Lin83cf17a2013-04-11 13:28:50 +02003057brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
3058 struct brcmf_bss_info_le *bss,
Hante Meulemane756af52012-09-11 21:18:52 +02003059 struct brcmf_bss_info_le *bss_info_le)
3060{
Franky Lin83cf17a2013-04-11 13:28:50 +02003061 struct brcmu_chan ch_bss, ch_bss_info_le;
3062
3063 ch_bss.chspec = le16_to_cpu(bss->chanspec);
3064 cfg->d11inf.decchspec(&ch_bss);
3065 ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
3066 cfg->d11inf.decchspec(&ch_bss_info_le);
3067
Hante Meulemane756af52012-09-11 21:18:52 +02003068 if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
Franky Lin83cf17a2013-04-11 13:28:50 +02003069 ch_bss.band == ch_bss_info_le.band &&
Hante Meulemane756af52012-09-11 21:18:52 +02003070 bss_info_le->SSID_len == bss->SSID_len &&
3071 !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
Arend van Spriel6f5838a2013-11-29 12:25:19 +01003072 if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
3073 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
Arend van Spriel029591f2012-09-19 22:21:06 +02003074 s16 bss_rssi = le16_to_cpu(bss->RSSI);
3075 s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
3076
Hante Meulemane756af52012-09-11 21:18:52 +02003077 /* preserve max RSSI if the measurements are
3078 * both on-channel or both off-channel
3079 */
Arend van Spriel029591f2012-09-19 22:21:06 +02003080 if (bss_info_rssi > bss_rssi)
Hante Meulemane756af52012-09-11 21:18:52 +02003081 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01003082 } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
3083 (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
Hante Meulemane756af52012-09-11 21:18:52 +02003084 /* preserve the on-channel rssi measurement
3085 * if the new measurement is off channel
3086 */
3087 bss->RSSI = bss_info_le->RSSI;
Arend van Spriel6f5838a2013-11-29 12:25:19 +01003088 bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
Hante Meulemane756af52012-09-11 21:18:52 +02003089 }
3090 return 1;
3091 }
3092 return 0;
3093}
3094
3095static s32
Arend van Spriel19937322012-11-05 16:22:32 -08003096brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
Hante Meulemane756af52012-09-11 21:18:52 +02003097 const struct brcmf_event_msg *e, void *data)
3098{
Arend van Spriel19937322012-11-05 16:22:32 -08003099 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Hante Meulemane756af52012-09-11 21:18:52 +02003100 s32 status;
Hante Meulemane756af52012-09-11 21:18:52 +02003101 struct brcmf_escan_result_le *escan_result_le;
3102 struct brcmf_bss_info_le *bss_info_le;
3103 struct brcmf_bss_info_le *bss = NULL;
3104 u32 bi_length;
3105 struct brcmf_scan_results *list;
3106 u32 i;
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003107 bool aborted;
Hante Meulemane756af52012-09-11 21:18:52 +02003108
Arend van Spriel5c36b992012-11-14 18:46:05 -08003109 status = e->status;
Hante Meulemane756af52012-09-11 21:18:52 +02003110
Hans de Goedeb9472a22017-03-08 14:50:17 +01003111 if (status == BRCMF_E_STATUS_ABORT)
3112 goto exit;
3113
Arend van Spriela0f472a2013-04-05 10:57:49 +02003114 if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Hante Meuleman37a869e2015-10-29 20:33:17 +01003115 brcmf_err("scan not ready, bsscfgidx=%d\n", ifp->bsscfgidx);
Hante Meulemane756af52012-09-11 21:18:52 +02003116 return -EPERM;
3117 }
3118
3119 if (status == BRCMF_E_STATUS_PARTIAL) {
Arend van Spriel4e8a0082012-12-05 15:26:03 +01003120 brcmf_dbg(SCAN, "ESCAN Partial result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003121 escan_result_le = (struct brcmf_escan_result_le *) data;
3122 if (!escan_result_le) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003123 brcmf_err("Invalid escan result (NULL pointer)\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003124 goto exit;
3125 }
Hante Meulemane756af52012-09-11 21:18:52 +02003126 if (le16_to_cpu(escan_result_le->bss_count) != 1) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003127 brcmf_err("Invalid bss_count %d: ignoring\n",
3128 escan_result_le->bss_count);
Hante Meulemane756af52012-09-11 21:18:52 +02003129 goto exit;
3130 }
3131 bss_info_le = &escan_result_le->bss_info_le;
3132
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003133 if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
3134 goto exit;
3135
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003136 if (!cfg->int_escan_map && !cfg->scan_request) {
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003137 brcmf_dbg(SCAN, "result without cfg80211 request\n");
3138 goto exit;
3139 }
3140
Hante Meulemane756af52012-09-11 21:18:52 +02003141 bi_length = le32_to_cpu(bss_info_le->length);
3142 if (bi_length != (le32_to_cpu(escan_result_le->buflen) -
3143 WL_ESCAN_RESULTS_FIXED_SIZE)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003144 brcmf_err("Invalid bss_info length %d: ignoring\n",
3145 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02003146 goto exit;
3147 }
3148
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003149 if (!(cfg_to_wiphy(cfg)->interface_modes &
Hante Meulemane756af52012-09-11 21:18:52 +02003150 BIT(NL80211_IFTYPE_ADHOC))) {
3151 if (le16_to_cpu(bss_info_le->capability) &
3152 WLAN_CAPABILITY_IBSS) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003153 brcmf_err("Ignoring IBSS result\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003154 goto exit;
3155 }
3156 }
3157
3158 list = (struct brcmf_scan_results *)
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003159 cfg->escan_info.escan_buf;
Hante Meulemand5367332016-02-17 11:26:51 +01003160 if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003161 brcmf_err("Buffer is too small: ignoring\n");
Hante Meulemane756af52012-09-11 21:18:52 +02003162 goto exit;
3163 }
3164
3165 for (i = 0; i < list->count; i++) {
3166 bss = bss ? (struct brcmf_bss_info_le *)
3167 ((unsigned char *)bss +
3168 le32_to_cpu(bss->length)) : list->bss_info_le;
Franky Lin83cf17a2013-04-11 13:28:50 +02003169 if (brcmf_compare_update_same_bss(cfg, bss,
3170 bss_info_le))
Hante Meulemane756af52012-09-11 21:18:52 +02003171 goto exit;
3172 }
Hante Meulemand5367332016-02-17 11:26:51 +01003173 memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
3174 bi_length);
Hante Meulemane756af52012-09-11 21:18:52 +02003175 list->version = le32_to_cpu(bss_info_le->version);
3176 list->buflen += bi_length;
3177 list->count++;
3178 } else {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003179 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003180 if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
3181 goto exit;
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003182 if (cfg->int_escan_map || cfg->scan_request) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003183 brcmf_inform_bss(cfg);
Arend van Spriel97ed15c2012-09-13 21:12:06 +02003184 aborted = status != BRCMF_E_STATUS_SUCCESS;
Hante Meulemanef8596e2014-09-30 10:23:13 +02003185 brcmf_notify_escan_complete(cfg, ifp, aborted, false);
Hante Meulemane756af52012-09-11 21:18:52 +02003186 } else
Hante Meuleman6eda4e22013-02-08 15:54:02 +01003187 brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
3188 status);
Hante Meulemane756af52012-09-11 21:18:52 +02003189 }
3190exit:
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03003191 return 0;
Hante Meulemane756af52012-09-11 21:18:52 +02003192}
3193
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003194static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
Hante Meulemane756af52012-09-11 21:18:52 +02003195{
Arend van Spriel5c36b992012-11-14 18:46:05 -08003196 brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
3197 brcmf_cfg80211_escan_handler);
Hante Meulemanf07998952012-11-05 16:22:13 -08003198 cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
3199 /* Init scan_timeout timer */
3200 init_timer(&cfg->escan_timeout);
3201 cfg->escan_timeout.data = (unsigned long) cfg;
3202 cfg->escan_timeout.function = brcmf_escan_timeout;
3203 INIT_WORK(&cfg->escan_timeout_work,
3204 brcmf_cfg80211_escan_timeout_worker);
Hante Meulemane756af52012-09-11 21:18:52 +02003205}
3206
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003207static struct cfg80211_scan_request *
3208brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {
3209 struct cfg80211_scan_request *req;
3210 size_t req_size;
3211
3212 req_size = sizeof(*req) +
3213 n_netinfo * sizeof(req->channels[0]) +
3214 n_netinfo * sizeof(*req->ssids);
3215
3216 req = kzalloc(req_size, GFP_KERNEL);
3217 if (req) {
3218 req->wiphy = wiphy;
3219 req->ssids = (void *)(&req->channels[0]) +
3220 n_netinfo * sizeof(req->channels[0]);
3221 }
3222 return req;
3223}
3224
3225static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
3226 u8 *ssid, u8 ssid_len, u8 channel)
3227{
3228 struct ieee80211_channel *chan;
3229 enum nl80211_band band;
Arend Van Spriel6ea51fc2017-04-06 13:14:42 +01003230 int freq, i;
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003231
3232 if (channel <= CH_MAX_2G_CHANNEL)
3233 band = NL80211_BAND_2GHZ;
3234 else
3235 band = NL80211_BAND_5GHZ;
3236
3237 freq = ieee80211_channel_to_frequency(channel, band);
3238 if (!freq)
3239 return -EINVAL;
3240
3241 chan = ieee80211_get_channel(req->wiphy, freq);
3242 if (!chan)
3243 return -EINVAL;
3244
Arend Van Spriel6ea51fc2017-04-06 13:14:42 +01003245 for (i = 0; i < req->n_channels; i++) {
3246 if (req->channels[i] == chan)
3247 break;
3248 }
3249 if (i == req->n_channels)
3250 req->channels[req->n_channels++] = chan;
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003251
Arend Van Spriel6ea51fc2017-04-06 13:14:42 +01003252 for (i = 0; i < req->n_ssids; i++) {
3253 if (req->ssids[i].ssid_len == ssid_len &&
3254 !memcmp(req->ssids[i].ssid, ssid, ssid_len))
3255 break;
3256 }
3257 if (i == req->n_ssids) {
3258 memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
3259 req->ssids[req->n_ssids++].ssid_len = ssid_len;
3260 }
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003261 return 0;
3262}
3263
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003264static int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap,
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003265 struct cfg80211_scan_request *request)
3266{
3267 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3268 int err;
3269
3270 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003271 if (cfg->int_escan_map)
3272 brcmf_dbg(SCAN, "aborting internal scan: map=%u\n",
3273 cfg->int_escan_map);
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003274 /* Abort any on-going scan */
3275 brcmf_abort_scanning(cfg);
3276 }
3277
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003278 brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap);
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003279 set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3280 cfg->escan_info.run = brcmf_run_escan;
3281 err = brcmf_do_escan(ifp, request);
3282 if (err) {
3283 clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3284 return err;
3285 }
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003286 cfg->int_escan_map = fwmap;
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003287 return 0;
3288}
3289
Arend Van Spriel53e3a802016-11-23 10:25:31 +00003290static struct brcmf_pno_net_info_le *
3291brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)
3292{
3293 struct brcmf_pno_scanresults_v2_le *pfn_v2;
3294 struct brcmf_pno_net_info_le *netinfo;
3295
3296 switch (pfn_v1->version) {
3297 default:
3298 WARN_ON(1);
3299 /* fall-thru */
3300 case cpu_to_le32(1):
3301 netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);
3302 break;
3303 case cpu_to_le32(2):
3304 pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;
3305 netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);
3306 break;
3307 }
3308
3309 return netinfo;
3310}
3311
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003312/* PFN result doesn't have all the info which are required by the supplicant
3313 * (For e.g IEs) Do a target Escan so that sched scan results are reported
3314 * via wl_inform_single_bss in the required format. Escan does require the
3315 * scan request in the form of cfg80211_scan_request. For timebeing, create
3316 * cfg80211_scan_request one out of the received PNO event.
3317 */
3318static s32
3319brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
3320 const struct brcmf_event_msg *e, void *data)
3321{
3322 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3323 struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
3324 struct cfg80211_scan_request *request = NULL;
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003325 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003326 int i, err = 0;
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003327 struct brcmf_pno_scanresults_le *pfn_result;
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003328 u32 bucket_map;
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003329 u32 result_count;
3330 u32 status;
Arend Van Spriel4835f372017-04-06 13:14:40 +01003331 u32 datalen;
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003332
3333 brcmf_dbg(SCAN, "Enter\n");
3334
Hante Meuleman0aedbca2016-02-17 11:26:54 +01003335 if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
3336 brcmf_dbg(SCAN, "Event data to small. Ignore\n");
3337 return 0;
3338 }
3339
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003340 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
3341 brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
3342 return 0;
3343 }
3344
3345 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3346 result_count = le32_to_cpu(pfn_result->count);
3347 status = le32_to_cpu(pfn_result->status);
3348
3349 /* PFN event is limited to fit 512 bytes so we may get
3350 * multiple NET_FOUND events. For now place a warning here.
3351 */
3352 WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
3353 brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003354 if (!result_count) {
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003355 brcmf_err("FALSE PNO Event. (pfn_count == 0)\n");
3356 goto out_err;
3357 }
Arend Van Spriel4835f372017-04-06 13:14:40 +01003358
3359 netinfo_start = brcmf_get_netinfo_array(pfn_result);
3360 datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
3361 if (datalen < result_count * sizeof(*netinfo)) {
3362 brcmf_err("insufficient event data\n");
3363 goto out_err;
3364 }
3365
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003366 request = brcmf_alloc_internal_escan_request(wiphy,
3367 result_count);
3368 if (!request) {
3369 err = -ENOMEM;
3370 goto out_err;
3371 }
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003372
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003373 bucket_map = 0;
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003374 for (i = 0; i < result_count; i++) {
3375 netinfo = &netinfo_start[i];
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003376
Arend Van Spriel4835f372017-04-06 13:14:40 +01003377 if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
3378 netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003379 brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
3380 netinfo->SSID, netinfo->channel);
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003381 bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo);
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003382 err = brcmf_internal_escan_add_info(request,
3383 netinfo->SSID,
3384 netinfo->SSID_len,
3385 netinfo->channel);
3386 if (err)
3387 goto out_err;
3388 }
3389
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003390 if (!bucket_map)
3391 goto free_req;
3392
3393 err = brcmf_start_internal_escan(ifp, bucket_map, request);
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003394 if (!err)
3395 goto free_req;
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003396
3397out_err:
Arend Van Sprielb34939b2017-04-28 13:40:28 +01003398 cfg80211_sched_scan_stopped(wiphy, 0);
Arend Van Sprielfa85b302016-11-23 10:25:25 +00003399free_req:
3400 kfree(request);
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003401 return err;
3402}
3403
3404static int
3405brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
3406 struct net_device *ndev,
Arend Van Spriel3e2e86a2016-11-23 10:25:23 +00003407 struct cfg80211_sched_scan_request *req)
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003408{
3409 struct brcmf_if *ifp = netdev_priv(ndev);
3410 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003411
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003412 brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n",
Arend Van Spriel3e2e86a2016-11-23 10:25:23 +00003413 req->n_match_sets, req->n_ssids);
Arend Van Sprieldfe5b0d2016-11-23 10:25:29 +00003414
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003415 if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003416 brcmf_err("Scanning suppressed: status=%lu\n",
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003417 cfg->scan_status);
3418 return -EAGAIN;
3419 }
3420
Arend Van Spriel3e2e86a2016-11-23 10:25:23 +00003421 if (req->n_match_sets <= 0) {
3422 brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n",
3423 req->n_match_sets);
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003424 return -EINVAL;
3425 }
3426
Arend Van Spriel3e486112016-11-23 10:25:27 +00003427 return brcmf_pno_start_sched_scan(ifp, req);
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003428}
3429
3430static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
Arend Van Spriel3a3ecf12017-04-21 13:05:02 +01003431 struct net_device *ndev, u64 reqid)
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003432{
3433 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend Van Sprielac551362016-11-23 10:25:22 +00003434 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003435
3436 brcmf_dbg(SCAN, "enter\n");
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01003437 brcmf_pno_stop_sched_scan(ifp, reqid);
3438 if (cfg->int_escan_map)
Arend Van Sprielac551362016-11-23 10:25:22 +00003439 brcmf_notify_escan_complete(cfg, ifp, true, true);
Hante Meuleman5419f7f2016-01-05 11:05:46 +01003440 return 0;
3441}
3442
Alexandre Oliva5addc0d2012-01-16 14:00:12 -05003443static __always_inline void brcmf_delay(u32 ms)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003444{
3445 if (ms < 1000 / HZ) {
3446 cond_resched();
3447 mdelay(ms);
3448 } else {
3449 msleep(ms);
3450 }
3451}
3452
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003453static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
3454 u8 *pattern, u32 patternsize, u8 *mask,
3455 u32 packet_offset)
3456{
3457 struct brcmf_fil_wowl_pattern_le *filter;
3458 u32 masksize;
3459 u32 patternoffset;
3460 u8 *buf;
3461 u32 bufsize;
3462 s32 ret;
3463
3464 masksize = (patternsize + 7) / 8;
3465 patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
3466
3467 bufsize = sizeof(*filter) + patternsize + masksize;
3468 buf = kzalloc(bufsize, GFP_KERNEL);
3469 if (!buf)
3470 return -ENOMEM;
3471 filter = (struct brcmf_fil_wowl_pattern_le *)buf;
3472
3473 memcpy(filter->cmd, cmd, 4);
3474 filter->masksize = cpu_to_le32(masksize);
3475 filter->offset = cpu_to_le32(packet_offset);
3476 filter->patternoffset = cpu_to_le32(patternoffset);
3477 filter->patternsize = cpu_to_le32(patternsize);
3478 filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
3479
3480 if ((mask) && (masksize))
3481 memcpy(buf + sizeof(*filter), mask, masksize);
3482 if ((pattern) && (patternsize))
3483 memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
3484
3485 ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
3486
3487 kfree(buf);
3488 return ret;
3489}
3490
Hante Meuleman3021ad92016-01-05 11:05:45 +01003491static s32
3492brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
3493 void *data)
3494{
3495 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3496 struct brcmf_pno_scanresults_le *pfn_result;
3497 struct brcmf_pno_net_info_le *netinfo;
3498
3499 brcmf_dbg(SCAN, "Enter\n");
3500
Hante Meuleman0aedbca2016-02-17 11:26:54 +01003501 if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
3502 brcmf_dbg(SCAN, "Event data to small. Ignore\n");
3503 return 0;
3504 }
3505
Hante Meuleman3021ad92016-01-05 11:05:45 +01003506 pfn_result = (struct brcmf_pno_scanresults_le *)data;
3507
3508 if (e->event_code == BRCMF_E_PFN_NET_LOST) {
3509 brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
3510 return 0;
3511 }
3512
3513 if (le32_to_cpu(pfn_result->count) < 1) {
3514 brcmf_err("Invalid result count, expected 1 (%d)\n",
3515 le32_to_cpu(pfn_result->count));
3516 return -EINVAL;
3517 }
3518
Arend Van Sprield29afe92017-01-27 12:27:46 +00003519 netinfo = brcmf_get_netinfo_array(pfn_result);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003520 memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
3521 cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
3522 cfg->wowl.nd->n_channels = 1;
3523 cfg->wowl.nd->channels[0] =
3524 ieee80211_channel_to_frequency(netinfo->channel,
3525 netinfo->channel <= CH_MAX_2G_CHANNEL ?
3526 NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
3527 cfg->wowl.nd_info->n_matches = 1;
3528 cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
3529
3530 /* Inform (the resume task) that the net detect information was recvd */
3531 cfg->wowl.nd_data_completed = true;
3532 wake_up(&cfg->wowl.nd_data_wait);
3533
3534 return 0;
3535}
3536
Hante Meulemanaeb64222015-10-29 20:33:19 +01003537#ifdef CONFIG_PM
3538
3539static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3540{
Hante Meuleman3021ad92016-01-05 11:05:45 +01003541 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meulemanaeb64222015-10-29 20:33:19 +01003542 struct brcmf_wowl_wakeind_le wake_ind_le;
3543 struct cfg80211_wowlan_wakeup wakeup_data;
3544 struct cfg80211_wowlan_wakeup *wakeup;
3545 u32 wakeind;
3546 s32 err;
Hante Meuleman3021ad92016-01-05 11:05:45 +01003547 int timeout;
Hante Meulemanaeb64222015-10-29 20:33:19 +01003548
3549 err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
3550 sizeof(wake_ind_le));
Hante Meuleman3021ad92016-01-05 11:05:45 +01003551 if (err) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003552 brcmf_err("Get wowl_wakeind failed, err = %d\n", err);
3553 return;
3554 }
3555
3556 wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
3557 if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
Hante Meuleman3021ad92016-01-05 11:05:45 +01003558 BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
3559 BRCMF_WOWL_PFN_FOUND)) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003560 wakeup = &wakeup_data;
3561 memset(&wakeup_data, 0, sizeof(wakeup_data));
3562 wakeup_data.pattern_idx = -1;
3563
3564 if (wakeind & BRCMF_WOWL_MAGIC) {
3565 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
3566 wakeup_data.magic_pkt = true;
3567 }
3568 if (wakeind & BRCMF_WOWL_DIS) {
3569 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
3570 wakeup_data.disconnect = true;
3571 }
3572 if (wakeind & BRCMF_WOWL_BCN) {
3573 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
3574 wakeup_data.disconnect = true;
3575 }
3576 if (wakeind & BRCMF_WOWL_RETR) {
3577 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
3578 wakeup_data.disconnect = true;
3579 }
3580 if (wakeind & BRCMF_WOWL_NET) {
3581 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
3582 /* For now always map to pattern 0, no API to get
3583 * correct information available at the moment.
3584 */
3585 wakeup_data.pattern_idx = 0;
3586 }
Hante Meuleman3021ad92016-01-05 11:05:45 +01003587 if (wakeind & BRCMF_WOWL_PFN_FOUND) {
3588 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
3589 timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
3590 cfg->wowl.nd_data_completed,
3591 BRCMF_ND_INFO_TIMEOUT);
3592 if (!timeout)
3593 brcmf_err("No result for wowl net detect\n");
3594 else
3595 wakeup_data.net_detect = cfg->wowl.nd_info;
3596 }
Hante Meuleman5c22fb82016-02-17 11:27:03 +01003597 if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
3598 brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
3599 wakeup_data.gtk_rekey_failure = true;
3600 }
Hante Meulemanaeb64222015-10-29 20:33:19 +01003601 } else {
3602 wakeup = NULL;
3603 }
3604 cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
3605}
3606
3607#else
3608
3609static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
3610{
3611}
3612
3613#endif /* CONFIG_PM */
3614
Arend van Spriel5b435de2011-10-05 13:19:03 +02003615static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
3616{
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003617 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3618 struct net_device *ndev = cfg_to_ndev(cfg);
3619 struct brcmf_if *ifp = netdev_priv(ndev);
3620
Arend van Sprield96b8012012-12-05 15:26:02 +01003621 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003622
Hante Meuleman3021ad92016-01-05 11:05:45 +01003623 if (cfg->wowl.active) {
Hante Meulemanaeb64222015-10-29 20:33:19 +01003624 brcmf_report_wowl_wakeind(wiphy, ifp);
3625 brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
3626 brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
Hante Meuleman73ef9e62016-02-17 11:27:05 +01003627 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
3628 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003629 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
Hante Meuleman3021ad92016-01-05 11:05:45 +01003630 cfg->wowl.pre_pmmode);
3631 cfg->wowl.active = false;
3632 if (cfg->wowl.nd_enabled) {
Arend Van Spriel3a3ecf12017-04-21 13:05:02 +01003633 brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003634 brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
3635 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
3636 brcmf_notify_sched_scan_results);
3637 cfg->wowl.nd_enabled = false;
3638 }
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003639 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003640 return 0;
3641}
3642
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003643static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
3644 struct brcmf_if *ifp,
3645 struct cfg80211_wowlan *wowl)
3646{
3647 u32 wowl_config;
Hante Meulemana7ed7822016-09-19 12:09:58 +01003648 struct brcmf_wowl_wakeind_le wowl_wakeind;
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003649 u32 i;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003650
3651 brcmf_dbg(TRACE, "Suspend, wowl config.\n");
3652
Hante Meuleman73ef9e62016-02-17 11:27:05 +01003653 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
3654 brcmf_configure_arp_nd_offload(ifp, false);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003655 brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003656 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
3657
3658 wowl_config = 0;
3659 if (wowl->disconnect)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003660 wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003661 if (wowl->magic_pkt)
Hante Meulemanb9a82f82014-10-28 14:56:06 +01003662 wowl_config |= BRCMF_WOWL_MAGIC;
3663 if ((wowl->patterns) && (wowl->n_patterns)) {
3664 wowl_config |= BRCMF_WOWL_NET;
3665 for (i = 0; i < wowl->n_patterns; i++) {
3666 brcmf_config_wowl_pattern(ifp, "add",
3667 (u8 *)wowl->patterns[i].pattern,
3668 wowl->patterns[i].pattern_len,
3669 (u8 *)wowl->patterns[i].mask,
3670 wowl->patterns[i].pkt_offset);
3671 }
3672 }
Hante Meuleman3021ad92016-01-05 11:05:45 +01003673 if (wowl->nd_config) {
3674 brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
3675 wowl->nd_config);
3676 wowl_config |= BRCMF_WOWL_PFN_FOUND;
3677
3678 cfg->wowl.nd_data_completed = false;
3679 cfg->wowl.nd_enabled = true;
3680 /* Now reroute the event for PFN to the wowl function. */
3681 brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
3682 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
3683 brcmf_wowl_nd_results);
3684 }
Hante Meuleman5c22fb82016-02-17 11:27:03 +01003685 if (wowl->gtk_rekey_failure)
3686 wowl_config |= BRCMF_WOWL_GTK_FAILURE;
Hante Meuleman3021ad92016-01-05 11:05:45 +01003687 if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
3688 wowl_config |= BRCMF_WOWL_UNASSOC;
3689
Hante Meulemana7ed7822016-09-19 12:09:58 +01003690 memcpy(&wowl_wakeind, "clear", 6);
3691 brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind,
3692 sizeof(wowl_wakeind));
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003693 brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
3694 brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
3695 brcmf_bus_wowl_config(cfg->pub->bus_if, true);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003696 cfg->wowl.active = true;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003697}
3698
Arend van Spriel5b435de2011-10-05 13:19:03 +02003699static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003700 struct cfg80211_wowlan *wowl)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003701{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003702 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3703 struct net_device *ndev = cfg_to_ndev(cfg);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003704 struct brcmf_if *ifp = netdev_priv(ndev);
Arend van Spriel7d641072012-10-22 13:55:39 -07003705 struct brcmf_cfg80211_vif *vif;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003706
Arend van Sprield96b8012012-12-05 15:26:02 +01003707 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003708
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003709 /* if the primary net_device is not READY there is nothing
Arend van Spriel7d641072012-10-22 13:55:39 -07003710 * we can do but pray resume goes smoothly.
Arend van Spriel5b435de2011-10-05 13:19:03 +02003711 */
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003712 if (!check_vif_up(ifp->vif))
Arend van Spriel7d641072012-10-22 13:55:39 -07003713 goto exit;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003714
Hante Meuleman3021ad92016-01-05 11:05:45 +01003715 /* Stop scheduled scan */
3716 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
Arend Van Spriel3a3ecf12017-04-21 13:05:02 +01003717 brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0);
Hante Meuleman3021ad92016-01-05 11:05:45 +01003718
Arend van Spriel7d641072012-10-22 13:55:39 -07003719 /* end any scanning */
3720 if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003721 brcmf_abort_scanning(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003722
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003723 if (wowl == NULL) {
3724 brcmf_bus_wowl_config(cfg->pub->bus_if, false);
3725 list_for_each_entry(vif, &cfg->vif_list, list) {
3726 if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
3727 continue;
3728 /* While going to suspend if associated with AP
3729 * disassociate from AP to save power while system is
3730 * in suspended state
3731 */
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01003732 brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED);
Hante Meuleman4eb3af72014-09-30 10:23:18 +02003733 /* Make sure WPA_Supplicant receives all the event
3734 * generated due to DISASSOC call to the fw to keep
3735 * the state fw and WPA_Supplicant state consistent
3736 */
3737 brcmf_delay(500);
3738 }
3739 /* Configure MPC */
3740 brcmf_set_mpc(ifp, 1);
3741
3742 } else {
3743 /* Configure WOWL paramaters */
3744 brcmf_configure_wowl(cfg, ifp, wowl);
3745 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003746
Arend van Spriel7d641072012-10-22 13:55:39 -07003747exit:
Arend van Sprield96b8012012-12-05 15:26:02 +01003748 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel7d641072012-10-22 13:55:39 -07003749 /* clear any scanning activity */
3750 cfg->scan_status = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003751 return 0;
3752}
3753
3754static __used s32
Hante Meuleman6c404f32015-12-10 13:43:03 +01003755brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003756{
Hante Meuleman6c404f32015-12-10 13:43:03 +01003757 struct brcmf_pmk_list_le *pmk_list;
3758 int i;
3759 u32 npmk;
3760 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003761
Hante Meuleman6c404f32015-12-10 13:43:03 +01003762 pmk_list = &cfg->pmk_list;
3763 npmk = le32_to_cpu(pmk_list->npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003764
Hante Meuleman6c404f32015-12-10 13:43:03 +01003765 brcmf_dbg(CONN, "No of elements %d\n", npmk);
3766 for (i = 0; i < npmk; i++)
3767 brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003768
Hante Meuleman6c404f32015-12-10 13:43:03 +01003769 err = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
3770 sizeof(*pmk_list));
Arend van Spriel5b435de2011-10-05 13:19:03 +02003771
3772 return err;
3773}
3774
3775static s32
3776brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
3777 struct cfg80211_pmksa *pmksa)
3778{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003779 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003780 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003781 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3782 s32 err;
3783 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003784
Arend van Sprield96b8012012-12-05 15:26:02 +01003785 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003786 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003787 return -EIO;
3788
Hante Meuleman6c404f32015-12-10 13:43:03 +01003789 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3790 for (i = 0; i < npmk; i++)
3791 if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003792 break;
Hante Meuleman6c404f32015-12-10 13:43:03 +01003793 if (i < BRCMF_MAXPMKID) {
3794 memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
3795 memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
3796 if (i == npmk) {
3797 npmk++;
3798 cfg->pmk_list.npmk = cpu_to_le32(npmk);
Arend van Spriel40c8e952011-10-12 20:51:20 +02003799 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003800 } else {
3801 brcmf_err("Too many PMKSA entries cached %d\n", npmk);
3802 return -EINVAL;
3803 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003804
Hante Meuleman6c404f32015-12-10 13:43:03 +01003805 brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmk[npmk].bssid);
3806 for (i = 0; i < WLAN_PMKID_LEN; i += 4)
3807 brcmf_dbg(CONN, "%02x %02x %02x %02x\n", pmk[npmk].pmkid[i],
3808 pmk[npmk].pmkid[i + 1], pmk[npmk].pmkid[i + 2],
3809 pmk[npmk].pmkid[i + 3]);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003810
Hante Meuleman6c404f32015-12-10 13:43:03 +01003811 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003812
Arend van Sprield96b8012012-12-05 15:26:02 +01003813 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003814 return err;
3815}
3816
3817static s32
3818brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
Hante Meuleman6c404f32015-12-10 13:43:03 +01003819 struct cfg80211_pmksa *pmksa)
Arend van Spriel5b435de2011-10-05 13:19:03 +02003820{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003821 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003822 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003823 struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
3824 s32 err;
3825 u32 npmk, i;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003826
Arend van Sprield96b8012012-12-05 15:26:02 +01003827 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003828 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003829 return -EIO;
3830
Nicolas Iooss7703773ef2016-08-23 11:37:17 +02003831 brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003832
Hante Meuleman6c404f32015-12-10 13:43:03 +01003833 npmk = le32_to_cpu(cfg->pmk_list.npmk);
3834 for (i = 0; i < npmk; i++)
Nicolas Iooss7703773ef2016-08-23 11:37:17 +02003835 if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003836 break;
3837
Hante Meuleman6c404f32015-12-10 13:43:03 +01003838 if ((npmk > 0) && (i < npmk)) {
3839 for (; i < (npmk - 1); i++) {
3840 memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
3841 memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02003842 WLAN_PMKID_LEN);
3843 }
Hante Meuleman6c404f32015-12-10 13:43:03 +01003844 memset(&pmk[i], 0, sizeof(*pmk));
3845 cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
3846 } else {
3847 brcmf_err("Cache entry not found\n");
3848 return -EINVAL;
3849 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02003850
Hante Meuleman6c404f32015-12-10 13:43:03 +01003851 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003852
Arend van Sprield96b8012012-12-05 15:26:02 +01003853 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003854 return err;
3855
3856}
3857
3858static s32
3859brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
3860{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02003861 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07003862 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman6c404f32015-12-10 13:43:03 +01003863 s32 err;
Arend van Spriel5b435de2011-10-05 13:19:03 +02003864
Arend van Sprield96b8012012-12-05 15:26:02 +01003865 brcmf_dbg(TRACE, "Enter\n");
Arend van Sprielce81e312012-10-22 13:55:37 -07003866 if (!check_vif_up(ifp->vif))
Arend van Spriel5b435de2011-10-05 13:19:03 +02003867 return -EIO;
3868
Hante Meuleman6c404f32015-12-10 13:43:03 +01003869 memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
3870 err = brcmf_update_pmklist(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02003871
Arend van Sprield96b8012012-12-05 15:26:02 +01003872 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02003873 return err;
3874
3875}
3876
Hante Meuleman1f170112013-02-06 18:40:38 +01003877static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
Hante Meuleman1a873342012-09-27 14:17:54 +02003878{
3879 s32 err;
3880
3881 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003882 err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003883 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003884 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003885 return err;
3886 }
3887 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07003888 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
Hante Meuleman1a873342012-09-27 14:17:54 +02003889 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003890 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003891 return err;
3892 }
3893 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07003894 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", WPA_AUTH_NONE);
Hante Meuleman1a873342012-09-27 14:17:54 +02003895 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01003896 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02003897 return err;
3898 }
3899
3900 return 0;
3901}
3902
3903static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
3904{
3905 if (is_rsn_ie)
3906 return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
3907
3908 return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
3909}
3910
3911static s32
Hante Meulemana44aa402014-12-03 21:05:33 +01003912brcmf_configure_wpaie(struct brcmf_if *ifp,
Johannes Berg4b5800f2014-01-15 14:55:59 +01003913 const struct brcmf_vs_tlv *wpa_ie,
3914 bool is_rsn_ie)
Hante Meuleman1a873342012-09-27 14:17:54 +02003915{
3916 u32 auth = 0; /* d11 open authentication */
3917 u16 count;
3918 s32 err = 0;
Hante Meuleman240d61a2016-02-17 11:27:10 +01003919 s32 len;
Hante Meuleman1a873342012-09-27 14:17:54 +02003920 u32 i;
3921 u32 wsec;
3922 u32 pval = 0;
3923 u32 gval = 0;
3924 u32 wpa_auth = 0;
3925 u32 offset;
3926 u8 *data;
3927 u16 rsn_cap;
3928 u32 wme_bss_disable;
Hante Meuleman240d61a2016-02-17 11:27:10 +01003929 u32 mfp;
Hante Meuleman1a873342012-09-27 14:17:54 +02003930
Arend van Sprield96b8012012-12-05 15:26:02 +01003931 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003932 if (wpa_ie == NULL)
3933 goto exit;
3934
3935 len = wpa_ie->len + TLV_HDR_LEN;
3936 data = (u8 *)wpa_ie;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003937 offset = TLV_HDR_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003938 if (!is_rsn_ie)
3939 offset += VS_IE_FIXED_HDR_LEN;
Hante Meuleman619c5a92013-01-02 15:12:39 +01003940 else
3941 offset += WPA_IE_VERSION_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02003942
3943 /* check for multicast cipher suite */
3944 if (offset + WPA_IE_MIN_OUI_LEN > len) {
3945 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003946 brcmf_err("no multicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003947 goto exit;
3948 }
3949
3950 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3951 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003952 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003953 goto exit;
3954 }
3955 offset += TLV_OUI_LEN;
3956
3957 /* pick up multicast cipher */
3958 switch (data[offset]) {
3959 case WPA_CIPHER_NONE:
3960 gval = 0;
3961 break;
3962 case WPA_CIPHER_WEP_40:
3963 case WPA_CIPHER_WEP_104:
3964 gval = WEP_ENABLED;
3965 break;
3966 case WPA_CIPHER_TKIP:
3967 gval = TKIP_ENABLED;
3968 break;
3969 case WPA_CIPHER_AES_CCM:
3970 gval = AES_ENABLED;
3971 break;
3972 default:
3973 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003974 brcmf_err("Invalid multi cast cipher info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003975 goto exit;
3976 }
3977
3978 offset++;
3979 /* walk thru unicast cipher list and pick up what we recognize */
3980 count = data[offset] + (data[offset + 1] << 8);
3981 offset += WPA_IE_SUITE_COUNT_LEN;
3982 /* Check for unicast suite(s) */
3983 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
3984 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003985 brcmf_err("no unicast cipher suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003986 goto exit;
3987 }
3988 for (i = 0; i < count; i++) {
3989 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
3990 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01003991 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02003992 goto exit;
3993 }
3994 offset += TLV_OUI_LEN;
3995 switch (data[offset]) {
3996 case WPA_CIPHER_NONE:
3997 break;
3998 case WPA_CIPHER_WEP_40:
3999 case WPA_CIPHER_WEP_104:
4000 pval |= WEP_ENABLED;
4001 break;
4002 case WPA_CIPHER_TKIP:
4003 pval |= TKIP_ENABLED;
4004 break;
4005 case WPA_CIPHER_AES_CCM:
4006 pval |= AES_ENABLED;
4007 break;
4008 default:
Colin Ian Kingad334bb2016-12-23 00:43:22 +00004009 brcmf_err("Invalid unicast security info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004010 }
4011 offset++;
4012 }
4013 /* walk thru auth management suite list and pick up what we recognize */
4014 count = data[offset] + (data[offset + 1] << 8);
4015 offset += WPA_IE_SUITE_COUNT_LEN;
4016 /* Check for auth key management suite(s) */
4017 if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
4018 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004019 brcmf_err("no auth key mgmt suite\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004020 goto exit;
4021 }
4022 for (i = 0; i < count; i++) {
4023 if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
4024 err = -EINVAL;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004025 brcmf_err("ivalid OUI\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004026 goto exit;
4027 }
4028 offset += TLV_OUI_LEN;
4029 switch (data[offset]) {
4030 case RSN_AKM_NONE:
Arend van Sprield96b8012012-12-05 15:26:02 +01004031 brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004032 wpa_auth |= WPA_AUTH_NONE;
4033 break;
4034 case RSN_AKM_UNSPECIFIED:
Arend van Sprield96b8012012-12-05 15:26:02 +01004035 brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004036 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
4037 (wpa_auth |= WPA_AUTH_UNSPECIFIED);
4038 break;
4039 case RSN_AKM_PSK:
Arend van Sprield96b8012012-12-05 15:26:02 +01004040 brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004041 is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
4042 (wpa_auth |= WPA_AUTH_PSK);
4043 break;
Hante Meuleman240d61a2016-02-17 11:27:10 +01004044 case RSN_AKM_SHA256_PSK:
4045 brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
4046 wpa_auth |= WPA2_AUTH_PSK_SHA256;
4047 break;
4048 case RSN_AKM_SHA256_1X:
4049 brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
4050 wpa_auth |= WPA2_AUTH_1X_SHA256;
4051 break;
Hante Meuleman1a873342012-09-27 14:17:54 +02004052 default:
Colin Ian Kingad334bb2016-12-23 00:43:22 +00004053 brcmf_err("Invalid key mgmt info\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004054 }
4055 offset++;
4056 }
4057
Hante Meuleman240d61a2016-02-17 11:27:10 +01004058 mfp = BRCMF_MFP_NONE;
Hante Meuleman1a873342012-09-27 14:17:54 +02004059 if (is_rsn_ie) {
4060 wme_bss_disable = 1;
4061 if ((offset + RSN_CAP_LEN) <= len) {
4062 rsn_cap = data[offset] + (data[offset + 1] << 8);
4063 if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
4064 wme_bss_disable = 0;
Hante Meuleman240d61a2016-02-17 11:27:10 +01004065 if (rsn_cap & RSN_CAP_MFPR_MASK) {
4066 brcmf_dbg(TRACE, "MFP Required\n");
4067 mfp = BRCMF_MFP_REQUIRED;
4068 /* Firmware only supports mfp required in
4069 * combination with WPA2_AUTH_PSK_SHA256 or
4070 * WPA2_AUTH_1X_SHA256.
4071 */
4072 if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
4073 WPA2_AUTH_1X_SHA256))) {
4074 err = -EINVAL;
4075 goto exit;
4076 }
4077 /* Firmware has requirement that WPA2_AUTH_PSK/
4078 * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
4079 * is to be included in the rsn ie.
4080 */
4081 if (wpa_auth & WPA2_AUTH_PSK_SHA256)
4082 wpa_auth |= WPA2_AUTH_PSK;
4083 else if (wpa_auth & WPA2_AUTH_1X_SHA256)
4084 wpa_auth |= WPA2_AUTH_UNSPECIFIED;
4085 } else if (rsn_cap & RSN_CAP_MFPC_MASK) {
4086 brcmf_dbg(TRACE, "MFP Capable\n");
4087 mfp = BRCMF_MFP_CAPABLE;
4088 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004089 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01004090 offset += RSN_CAP_LEN;
Hante Meuleman1a873342012-09-27 14:17:54 +02004091 /* set wme_bss_disable to sync RSN Capabilities */
Arend van Sprielac24be62012-10-22 10:36:23 -07004092 err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004093 wme_bss_disable);
Hante Meuleman1a873342012-09-27 14:17:54 +02004094 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004095 brcmf_err("wme_bss_disable error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004096 goto exit;
4097 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01004098
4099 /* Skip PMKID cnt as it is know to be 0 for AP. */
4100 offset += RSN_PMKID_COUNT_LEN;
4101
4102 /* See if there is BIP wpa suite left for MFP */
4103 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
4104 ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
4105 err = brcmf_fil_bsscfg_data_set(ifp, "bip",
4106 &data[offset],
4107 WPA_IE_MIN_OUI_LEN);
4108 if (err < 0) {
4109 brcmf_err("bip error %d\n", err);
4110 goto exit;
4111 }
4112 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004113 }
4114 /* FOR WPS , set SES_OW_ENABLED */
4115 wsec = (pval | gval | SES_OW_ENABLED);
4116
4117 /* set auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07004118 err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02004119 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004120 brcmf_err("auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004121 goto exit;
4122 }
4123 /* set wsec */
Arend van Sprielac24be62012-10-22 10:36:23 -07004124 err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
Hante Meuleman1a873342012-09-27 14:17:54 +02004125 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004126 brcmf_err("wsec error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004127 goto exit;
4128 }
Hante Meuleman240d61a2016-02-17 11:27:10 +01004129 /* Configure MFP, this needs to go after wsec otherwise the wsec command
4130 * will overwrite the values set by MFP
4131 */
4132 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
4133 err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
4134 if (err < 0) {
4135 brcmf_err("mfp error %d\n", err);
4136 goto exit;
4137 }
4138 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004139 /* set upper-layer auth */
Arend van Sprielac24be62012-10-22 10:36:23 -07004140 err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
Hante Meuleman1a873342012-09-27 14:17:54 +02004141 if (err < 0) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004142 brcmf_err("wpa_auth error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004143 goto exit;
4144 }
4145
4146exit:
4147 return err;
4148}
4149
4150static s32
Arend van Spriel3082b9b2012-11-05 16:22:12 -08004151brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
Hante Meuleman1a873342012-09-27 14:17:54 +02004152 struct parsed_vndr_ies *vndr_ies)
4153{
Hante Meuleman1a873342012-09-27 14:17:54 +02004154 struct brcmf_vs_tlv *vndrie;
4155 struct brcmf_tlv *ie;
4156 struct parsed_vndr_ie_info *parsed_info;
4157 s32 remaining_len;
4158
4159 remaining_len = (s32)vndr_ie_len;
4160 memset(vndr_ies, 0, sizeof(*vndr_ies));
4161
4162 ie = (struct brcmf_tlv *)vndr_ie_buf;
4163 while (ie) {
4164 if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
4165 goto next;
4166 vndrie = (struct brcmf_vs_tlv *)ie;
4167 /* len should be bigger than OUI length + one */
4168 if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004169 brcmf_err("invalid vndr ie. length is too small %d\n",
4170 vndrie->len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004171 goto next;
4172 }
4173 /* if wpa or wme ie, do not add ie */
4174 if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
4175 ((vndrie->oui_type == WPA_OUI_TYPE) ||
4176 (vndrie->oui_type == WME_OUI_TYPE))) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004177 brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004178 goto next;
4179 }
4180
4181 parsed_info = &vndr_ies->ie_info[vndr_ies->count];
4182
4183 /* save vndr ie information */
4184 parsed_info->ie_ptr = (char *)vndrie;
4185 parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
4186 memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
4187
4188 vndr_ies->count++;
4189
Arend van Sprield96b8012012-12-05 15:26:02 +01004190 brcmf_dbg(TRACE, "** OUI %02x %02x %02x, type 0x%02x\n",
4191 parsed_info->vndrie.oui[0],
4192 parsed_info->vndrie.oui[1],
4193 parsed_info->vndrie.oui[2],
4194 parsed_info->vndrie.oui_type);
Hante Meuleman1a873342012-09-27 14:17:54 +02004195
Arend van Spriel9f440b72013-02-08 15:53:36 +01004196 if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
Hante Meuleman1a873342012-09-27 14:17:54 +02004197 break;
4198next:
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004199 remaining_len -= (ie->len + TLV_HDR_LEN);
4200 if (remaining_len <= TLV_HDR_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02004201 ie = NULL;
4202 else
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004203 ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
4204 TLV_HDR_LEN);
Hante Meuleman1a873342012-09-27 14:17:54 +02004205 }
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03004206 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02004207}
4208
4209static u32
4210brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
4211{
4212
Hante Meuleman1a873342012-09-27 14:17:54 +02004213 strncpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN - 1);
4214 iebuf[VNDR_IE_CMD_LEN - 1] = '\0';
4215
Vaishali Thakkar362126c2015-01-16 21:36:14 +05304216 put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004217
Vaishali Thakkar362126c2015-01-16 21:36:14 +05304218 put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004219
4220 memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
4221
4222 return ie_len + VNDR_IE_HDR_SIZE;
4223}
4224
Arend van Spriel1332e262012-11-05 16:22:18 -08004225s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
4226 const u8 *vndr_ie_buf, u32 vndr_ie_len)
Hante Meuleman1a873342012-09-27 14:17:54 +02004227{
Arend van Spriel1332e262012-11-05 16:22:18 -08004228 struct brcmf_if *ifp;
4229 struct vif_saved_ie *saved_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004230 s32 err = 0;
4231 u8 *iovar_ie_buf;
4232 u8 *curr_ie_buf;
4233 u8 *mgmt_ie_buf = NULL;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004234 int mgmt_ie_buf_len;
Dan Carpenter81118d12012-10-10 11:13:07 -07004235 u32 *mgmt_ie_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004236 u32 del_add_ie_buf_len = 0;
4237 u32 total_ie_buf_len = 0;
4238 u32 parsed_ie_buf_len = 0;
4239 struct parsed_vndr_ies old_vndr_ies;
4240 struct parsed_vndr_ies new_vndr_ies;
4241 struct parsed_vndr_ie_info *vndrie_info;
4242 s32 i;
4243 u8 *ptr;
Dan Carpenter3e4f3192012-10-10 11:13:12 -07004244 int remained_buf_len;
Hante Meuleman1a873342012-09-27 14:17:54 +02004245
Arend van Spriel1332e262012-11-05 16:22:18 -08004246 if (!vif)
4247 return -ENODEV;
4248 ifp = vif->ifp;
4249 saved_ie = &vif->saved_ie;
4250
Hante Meuleman37a869e2015-10-29 20:33:17 +01004251 brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
4252 pktflag);
Hante Meuleman1a873342012-09-27 14:17:54 +02004253 iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
4254 if (!iovar_ie_buf)
4255 return -ENOMEM;
4256 curr_ie_buf = iovar_ie_buf;
Hante Meuleman89286dc2013-02-08 15:53:46 +01004257 switch (pktflag) {
4258 case BRCMF_VNDR_IE_PRBREQ_FLAG:
4259 mgmt_ie_buf = saved_ie->probe_req_ie;
4260 mgmt_ie_len = &saved_ie->probe_req_ie_len;
4261 mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
4262 break;
4263 case BRCMF_VNDR_IE_PRBRSP_FLAG:
4264 mgmt_ie_buf = saved_ie->probe_res_ie;
4265 mgmt_ie_len = &saved_ie->probe_res_ie_len;
4266 mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
4267 break;
4268 case BRCMF_VNDR_IE_BEACON_FLAG:
4269 mgmt_ie_buf = saved_ie->beacon_ie;
4270 mgmt_ie_len = &saved_ie->beacon_ie_len;
4271 mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
4272 break;
4273 case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
4274 mgmt_ie_buf = saved_ie->assoc_req_ie;
4275 mgmt_ie_len = &saved_ie->assoc_req_ie_len;
4276 mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
4277 break;
4278 default:
4279 err = -EPERM;
4280 brcmf_err("not suitable type\n");
4281 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004282 }
4283
4284 if (vndr_ie_len > mgmt_ie_buf_len) {
4285 err = -ENOMEM;
Arend van Spriel57d6e912012-12-05 15:26:00 +01004286 brcmf_err("extra IE size too big\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004287 goto exit;
4288 }
4289
4290 /* parse and save new vndr_ie in curr_ie_buff before comparing it */
4291 if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
4292 ptr = curr_ie_buf;
4293 brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
4294 for (i = 0; i < new_vndr_ies.count; i++) {
4295 vndrie_info = &new_vndr_ies.ie_info[i];
4296 memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
4297 vndrie_info->ie_len);
4298 parsed_ie_buf_len += vndrie_info->ie_len;
4299 }
4300 }
4301
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004302 if (mgmt_ie_buf && *mgmt_ie_len) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004303 if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
4304 (memcmp(mgmt_ie_buf, curr_ie_buf,
4305 parsed_ie_buf_len) == 0)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004306 brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004307 goto exit;
4308 }
4309
4310 /* parse old vndr_ie */
4311 brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
4312
4313 /* make a command to delete old ie */
4314 for (i = 0; i < old_vndr_ies.count; i++) {
4315 vndrie_info = &old_vndr_ies.ie_info[i];
4316
Arend van Sprield96b8012012-12-05 15:26:02 +01004317 brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%02x:%02x:%02x\n",
4318 vndrie_info->vndrie.id,
4319 vndrie_info->vndrie.len,
4320 vndrie_info->vndrie.oui[0],
4321 vndrie_info->vndrie.oui[1],
4322 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004323
4324 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4325 vndrie_info->ie_ptr,
4326 vndrie_info->ie_len,
4327 "del");
4328 curr_ie_buf += del_add_ie_buf_len;
4329 total_ie_buf_len += del_add_ie_buf_len;
4330 }
4331 }
4332
4333 *mgmt_ie_len = 0;
4334 /* Add if there is any extra IE */
4335 if (mgmt_ie_buf && parsed_ie_buf_len) {
4336 ptr = mgmt_ie_buf;
4337
4338 remained_buf_len = mgmt_ie_buf_len;
4339
4340 /* make a command to add new ie */
4341 for (i = 0; i < new_vndr_ies.count; i++) {
4342 vndrie_info = &new_vndr_ies.ie_info[i];
4343
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004344 /* verify remained buf size before copy data */
4345 if (remained_buf_len < (vndrie_info->vndrie.len +
4346 VNDR_IE_VSIE_OFFSET)) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01004347 brcmf_err("no space in mgmt_ie_buf: len left %d",
4348 remained_buf_len);
Hante Meulemanb41fc3d2012-11-28 21:44:13 +01004349 break;
4350 }
4351 remained_buf_len -= (vndrie_info->ie_len +
4352 VNDR_IE_VSIE_OFFSET);
4353
Arend van Sprield96b8012012-12-05 15:26:02 +01004354 brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%02x:%02x:%02x\n",
4355 vndrie_info->vndrie.id,
4356 vndrie_info->vndrie.len,
4357 vndrie_info->vndrie.oui[0],
4358 vndrie_info->vndrie.oui[1],
4359 vndrie_info->vndrie.oui[2]);
Hante Meuleman1a873342012-09-27 14:17:54 +02004360
4361 del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
4362 vndrie_info->ie_ptr,
4363 vndrie_info->ie_len,
4364 "add");
Hante Meuleman1a873342012-09-27 14:17:54 +02004365
4366 /* save the parsed IE in wl struct */
4367 memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
4368 vndrie_info->ie_len);
4369 *mgmt_ie_len += vndrie_info->ie_len;
4370
4371 curr_ie_buf += del_add_ie_buf_len;
4372 total_ie_buf_len += del_add_ie_buf_len;
4373 }
4374 }
4375 if (total_ie_buf_len) {
Arend van Sprielc1179032012-10-22 13:55:33 -07004376 err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004377 total_ie_buf_len);
Hante Meuleman1a873342012-09-27 14:17:54 +02004378 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004379 brcmf_err("vndr ie set error : %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004380 }
4381
4382exit:
4383 kfree(iovar_ie_buf);
4384 return err;
4385}
4386
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01004387s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
4388{
4389 s32 pktflags[] = {
4390 BRCMF_VNDR_IE_PRBREQ_FLAG,
4391 BRCMF_VNDR_IE_PRBRSP_FLAG,
4392 BRCMF_VNDR_IE_BEACON_FLAG
4393 };
4394 int i;
4395
4396 for (i = 0; i < ARRAY_SIZE(pktflags); i++)
4397 brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
4398
4399 memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
4400 return 0;
4401}
4402
Hante Meuleman1a873342012-09-27 14:17:54 +02004403static s32
Hante Meulemana0f07952013-02-08 15:53:47 +01004404brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
4405 struct cfg80211_beacon_data *beacon)
4406{
4407 s32 err;
4408
4409 /* Set Beacon IEs to FW */
4410 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
4411 beacon->tail, beacon->tail_len);
4412 if (err) {
4413 brcmf_err("Set Beacon IE Failed\n");
4414 return err;
4415 }
4416 brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
4417
4418 /* Set Probe Response IEs to FW */
4419 err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
4420 beacon->proberesp_ies,
4421 beacon->proberesp_ies_len);
4422 if (err)
4423 brcmf_err("Set Probe Resp IE Failed\n");
4424 else
4425 brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
4426
4427 return err;
4428}
4429
4430static s32
Hante Meuleman1a873342012-09-27 14:17:54 +02004431brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
4432 struct cfg80211_ap_settings *settings)
4433{
4434 s32 ie_offset;
Hante Meuleman1c9d30c2013-05-27 21:09:58 +02004435 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07004436 struct brcmf_if *ifp = netdev_priv(ndev);
Johannes Berg4b5800f2014-01-15 14:55:59 +01004437 const struct brcmf_tlv *ssid_ie;
Arend van Spriel98027762014-12-21 12:43:53 +01004438 const struct brcmf_tlv *country_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004439 struct brcmf_ssid_le ssid_le;
Hante Meuleman1a873342012-09-27 14:17:54 +02004440 s32 err = -EPERM;
Johannes Berg4b5800f2014-01-15 14:55:59 +01004441 const struct brcmf_tlv *rsn_ie;
4442 const struct brcmf_vs_tlv *wpa_ie;
Hante Meuleman1a873342012-09-27 14:17:54 +02004443 struct brcmf_join_params join_params;
Hante Meulemana0f07952013-02-08 15:53:47 +01004444 enum nl80211_iftype dev_role;
4445 struct brcmf_fil_bss_enable_le bss_enable;
Rafał Miłecki8707e082016-05-27 21:07:19 +02004446 u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
Hante Meulemana44aa402014-12-03 21:05:33 +01004447 bool mbss;
Arend van Spriel98027762014-12-21 12:43:53 +01004448 int is_11d;
Hante Meulemanb3589df2016-09-19 12:09:51 +01004449 bool supports_11d;
Hante Meuleman1a873342012-09-27 14:17:54 +02004450
Arend van Spriel06c01582014-05-12 10:47:37 +02004451 brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
4452 settings->chandef.chan->hw_value,
4453 settings->chandef.center_freq1, settings->chandef.width,
Arend van Spriela9a56872014-05-12 10:47:33 +02004454 settings->beacon_interval, settings->dtim_period);
Arend van Sprield96b8012012-12-05 15:26:02 +01004455 brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
4456 settings->ssid, settings->ssid_len, settings->auth_type,
4457 settings->inactivity_timeout);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004458 dev_role = ifp->vif->wdev.iftype;
Hante Meulemana44aa402014-12-03 21:05:33 +01004459 mbss = ifp->vif->mbss;
Hante Meuleman1a873342012-09-27 14:17:54 +02004460
Arend van Spriel98027762014-12-21 12:43:53 +01004461 /* store current 11d setting */
Hante Meulemanb3589df2016-09-19 12:09:51 +01004462 if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY,
4463 &ifp->vif->is_11d)) {
Arnd Bergmannd3532ea2016-10-18 00:13:40 +02004464 is_11d = supports_11d = false;
Hante Meulemanb3589df2016-09-19 12:09:51 +01004465 } else {
4466 country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4467 settings->beacon.tail_len,
4468 WLAN_EID_COUNTRY);
4469 is_11d = country_ie ? 1 : 0;
4470 supports_11d = true;
4471 }
Arend van Spriel98027762014-12-21 12:43:53 +01004472
Hante Meuleman1a873342012-09-27 14:17:54 +02004473 memset(&ssid_le, 0, sizeof(ssid_le));
4474 if (settings->ssid == NULL || settings->ssid_len == 0) {
4475 ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4476 ssid_ie = brcmf_parse_tlvs(
4477 (u8 *)&settings->beacon.head[ie_offset],
4478 settings->beacon.head_len - ie_offset,
4479 WLAN_EID_SSID);
Arend Van Sprielded89912016-09-05 10:45:47 +01004480 if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
Hante Meuleman1a873342012-09-27 14:17:54 +02004481 return -EINVAL;
4482
4483 memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
4484 ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
Arend van Sprield96b8012012-12-05 15:26:02 +01004485 brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
Hante Meuleman1a873342012-09-27 14:17:54 +02004486 } else {
4487 memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
4488 ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
4489 }
4490
Hante Meulemana44aa402014-12-03 21:05:33 +01004491 if (!mbss) {
4492 brcmf_set_mpc(ifp, 0);
Franky Lin52f22fb2016-02-17 11:26:55 +01004493 brcmf_configure_arp_nd_offload(ifp, false);
Hante Meulemana44aa402014-12-03 21:05:33 +01004494 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004495
4496 /* find the RSN_IE */
4497 rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
4498 settings->beacon.tail_len, WLAN_EID_RSN);
4499
4500 /* find the WPA_IE */
4501 wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
4502 settings->beacon.tail_len);
4503
Hante Meuleman1a873342012-09-27 14:17:54 +02004504 if ((wpa_ie != NULL || rsn_ie != NULL)) {
Arend van Sprield96b8012012-12-05 15:26:02 +01004505 brcmf_dbg(TRACE, "WPA(2) IE is found\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004506 if (wpa_ie != NULL) {
4507 /* WPA IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004508 err = brcmf_configure_wpaie(ifp, wpa_ie, false);
Hante Meuleman1a873342012-09-27 14:17:54 +02004509 if (err < 0)
4510 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004511 } else {
Hante Meulemana44aa402014-12-03 21:05:33 +01004512 struct brcmf_vs_tlv *tmp_ie;
4513
4514 tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
4515
Hante Meuleman1a873342012-09-27 14:17:54 +02004516 /* RSN IE */
Hante Meulemana44aa402014-12-03 21:05:33 +01004517 err = brcmf_configure_wpaie(ifp, tmp_ie, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004518 if (err < 0)
4519 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004520 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004521 } else {
Arend van Sprield96b8012012-12-05 15:26:02 +01004522 brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
Hante Meuleman1f170112013-02-06 18:40:38 +01004523 brcmf_configure_opensecurity(ifp);
Hante Meuleman1a873342012-09-27 14:17:54 +02004524 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004525
Rafał Miłecki8707e082016-05-27 21:07:19 +02004526 /* Parameters shared by all radio interfaces */
Hante Meulemana44aa402014-12-03 21:05:33 +01004527 if (!mbss) {
Hante Meulemanb3589df2016-09-19 12:09:51 +01004528 if ((supports_11d) && (is_11d != ifp->vif->is_11d)) {
Arend van Spriel98027762014-12-21 12:43:53 +01004529 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4530 is_11d);
4531 if (err < 0) {
4532 brcmf_err("Regulatory Set Error, %d\n", err);
4533 goto exit;
4534 }
4535 }
Hante Meulemana44aa402014-12-03 21:05:33 +01004536 if (settings->beacon_interval) {
4537 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
4538 settings->beacon_interval);
4539 if (err < 0) {
4540 brcmf_err("Beacon Interval Set Error, %d\n",
4541 err);
4542 goto exit;
4543 }
4544 }
4545 if (settings->dtim_period) {
4546 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
4547 settings->dtim_period);
4548 if (err < 0) {
4549 brcmf_err("DTIM Interval Set Error, %d\n", err);
4550 goto exit;
4551 }
4552 }
4553
Hante Meuleman8abffd82015-10-29 20:33:16 +01004554 if ((dev_role == NL80211_IFTYPE_AP) &&
4555 ((ifp->ifidx == 0) ||
4556 !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB))) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004557 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4558 if (err < 0) {
4559 brcmf_err("BRCMF_C_DOWN error %d\n", err);
4560 goto exit;
4561 }
4562 brcmf_fil_iovar_int_set(ifp, "apsta", 0);
4563 }
4564
4565 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
Hante Meuleman1a873342012-09-27 14:17:54 +02004566 if (err < 0) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004567 brcmf_err("SET INFRA error %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004568 goto exit;
4569 }
Hante Meulemanb3589df2016-09-19 12:09:51 +01004570 } else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) {
Arend van Spriel98027762014-12-21 12:43:53 +01004571 /* Multiple-BSS should use same 11d configuration */
4572 err = -EINVAL;
4573 goto exit;
Hante Meuleman1a873342012-09-27 14:17:54 +02004574 }
Rafał Miłecki8707e082016-05-27 21:07:19 +02004575
4576 /* Interface specific setup */
Hante Meulemana0f07952013-02-08 15:53:47 +01004577 if (dev_role == NL80211_IFTYPE_AP) {
Hante Meulemana44aa402014-12-03 21:05:33 +01004578 if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
4579 brcmf_fil_iovar_int_set(ifp, "mbss", 1);
4580
Hante Meulemana0f07952013-02-08 15:53:47 +01004581 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
4582 if (err < 0) {
4583 brcmf_err("setting AP mode failed %d\n", err);
4584 goto exit;
4585 }
Rafał Miłecki8707e082016-05-27 21:07:19 +02004586 if (!mbss) {
4587 /* Firmware 10.x requires setting channel after enabling
4588 * AP and before bringing interface up.
4589 */
4590 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
4591 if (err < 0) {
4592 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4593 chanspec, err);
4594 goto exit;
4595 }
4596 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004597 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4598 if (err < 0) {
4599 brcmf_err("BRCMF_C_UP error (%d)\n", err);
4600 goto exit;
4601 }
Hante Meuleman118eb302014-12-21 12:43:49 +01004602 /* On DOWN the firmware removes the WEP keys, reconfigure
4603 * them if they were set.
4604 */
4605 brcmf_cfg80211_reconfigure_wep(ifp);
Hante Meulemana0f07952013-02-08 15:53:47 +01004606
4607 memset(&join_params, 0, sizeof(join_params));
4608 /* join parameters starts with ssid */
4609 memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
4610 /* create softap */
4611 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4612 &join_params, sizeof(join_params));
4613 if (err < 0) {
4614 brcmf_err("SET SSID error (%d)\n", err);
4615 goto exit;
4616 }
Rafał Miłeckic940de12016-07-06 12:22:54 +02004617
4618 if (settings->hidden_ssid) {
4619 err = brcmf_fil_iovar_int_set(ifp, "closednet", 1);
4620 if (err) {
4621 brcmf_err("closednet error (%d)\n", err);
4622 goto exit;
4623 }
4624 }
4625
Hante Meulemana0f07952013-02-08 15:53:47 +01004626 brcmf_dbg(TRACE, "AP mode configuration complete\n");
Rafał Miłecki8707e082016-05-27 21:07:19 +02004627 } else if (dev_role == NL80211_IFTYPE_P2P_GO) {
4628 err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
4629 if (err < 0) {
4630 brcmf_err("Set Channel failed: chspec=%d, %d\n",
4631 chanspec, err);
4632 goto exit;
4633 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004634 err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
4635 sizeof(ssid_le));
4636 if (err < 0) {
4637 brcmf_err("setting ssid failed %d\n", err);
4638 goto exit;
4639 }
Hante Meuleman37a869e2015-10-29 20:33:17 +01004640 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meulemana0f07952013-02-08 15:53:47 +01004641 bss_enable.enable = cpu_to_le32(1);
4642 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4643 sizeof(bss_enable));
4644 if (err < 0) {
4645 brcmf_err("bss_enable config failed %d\n", err);
4646 goto exit;
4647 }
4648
4649 brcmf_dbg(TRACE, "GO mode configuration complete\n");
Rafał Miłecki8707e082016-05-27 21:07:19 +02004650 } else {
4651 WARN_ON(1);
Hante Meulemana0f07952013-02-08 15:53:47 +01004652 }
Rafał Miłecki8707e082016-05-27 21:07:19 +02004653
Wright Fengf25ba692016-11-18 09:59:52 +08004654 brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
Arend van Sprielc1179032012-10-22 13:55:33 -07004655 set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004656 brcmf_net_setcarrier(ifp, true);
Hante Meuleman1a873342012-09-27 14:17:54 +02004657
4658exit:
Hante Meulemana44aa402014-12-03 21:05:33 +01004659 if ((err) && (!mbss)) {
Arend van Sprielf96aa072013-04-05 10:57:48 +02004660 brcmf_set_mpc(ifp, 1);
Franky Lin52f22fb2016-02-17 11:26:55 +01004661 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meulemanb3657452013-05-27 21:09:53 +02004662 }
Hante Meuleman1a873342012-09-27 14:17:54 +02004663 return err;
4664}
4665
4666static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev)
4667{
Arend van Sprielc1179032012-10-22 13:55:33 -07004668 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004669 s32 err;
Hante Meuleman426d0a52013-02-08 15:53:53 +01004670 struct brcmf_fil_bss_enable_le bss_enable;
Hante Meuleman5c33a942013-04-02 21:06:18 +02004671 struct brcmf_join_params join_params;
Hante Meuleman1a873342012-09-27 14:17:54 +02004672
Arend van Sprield96b8012012-12-05 15:26:02 +01004673 brcmf_dbg(TRACE, "Enter\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004674
Hante Meuleman426d0a52013-02-08 15:53:53 +01004675 if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
Hante Meuleman1a873342012-09-27 14:17:54 +02004676 /* Due to most likely deauths outstanding we sleep */
4677 /* first to make sure they get processed by fw. */
4678 msleep(400);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004679
Hante Meulemana44aa402014-12-03 21:05:33 +01004680 if (ifp->vif->mbss) {
4681 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
4682 return err;
4683 }
4684
Rafał Miłeckic940de12016-07-06 12:22:54 +02004685 /* First BSS doesn't get a full reset */
4686 if (ifp->bsscfgidx == 0)
4687 brcmf_fil_iovar_int_set(ifp, "closednet", 0);
4688
Hante Meuleman5c33a942013-04-02 21:06:18 +02004689 memset(&join_params, 0, sizeof(join_params));
4690 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
4691 &join_params, sizeof(join_params));
4692 if (err < 0)
4693 brcmf_err("SET SSID error (%d)\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004694 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004695 if (err < 0)
Hante Meulemana44aa402014-12-03 21:05:33 +01004696 brcmf_err("BRCMF_C_DOWN error %d\n", err);
Hante Meuleman5c33a942013-04-02 21:06:18 +02004697 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
4698 if (err < 0)
4699 brcmf_err("setting AP mode failed %d\n", err);
Hante Meulemana44aa402014-12-03 21:05:33 +01004700 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
4701 brcmf_fil_iovar_int_set(ifp, "mbss", 0);
Hante Meulemanb3589df2016-09-19 12:09:51 +01004702 brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
4703 ifp->vif->is_11d);
Hante Meulemana44aa402014-12-03 21:05:33 +01004704 /* Bring device back up so it can be used again */
4705 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
4706 if (err < 0)
4707 brcmf_err("BRCMF_C_UP error %d\n", err);
Wright Fengf25ba692016-11-18 09:59:52 +08004708
4709 brcmf_vif_clear_mgmt_ies(ifp->vif);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004710 } else {
Hante Meuleman37a869e2015-10-29 20:33:17 +01004711 bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004712 bss_enable.enable = cpu_to_le32(0);
4713 err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
4714 sizeof(bss_enable));
4715 if (err < 0)
4716 brcmf_err("bss_enable config failed %d\n", err);
Hante Meuleman1a873342012-09-27 14:17:54 +02004717 }
Arend van Sprielf96aa072013-04-05 10:57:48 +02004718 brcmf_set_mpc(ifp, 1);
Franky Lin52f22fb2016-02-17 11:26:55 +01004719 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004720 clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
Hante Meuleman92121e62015-10-08 20:33:21 +02004721 brcmf_net_setcarrier(ifp, false);
Hante Meuleman426d0a52013-02-08 15:53:53 +01004722
Hante Meuleman1a873342012-09-27 14:17:54 +02004723 return err;
4724}
4725
Hante Meulemana0f07952013-02-08 15:53:47 +01004726static s32
4727brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
4728 struct cfg80211_beacon_data *info)
4729{
Hante Meulemana0f07952013-02-08 15:53:47 +01004730 struct brcmf_if *ifp = netdev_priv(ndev);
4731 s32 err;
4732
4733 brcmf_dbg(TRACE, "Enter\n");
4734
Hante Meulemana0f07952013-02-08 15:53:47 +01004735 err = brcmf_config_ap_mgmt_ie(ifp->vif, info);
4736
4737 return err;
4738}
4739
Hante Meuleman1a873342012-09-27 14:17:54 +02004740static int
4741brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
Jouni Malinen89c771e2014-10-10 20:52:40 +03004742 struct station_del_parameters *params)
Hante Meuleman1a873342012-09-27 14:17:54 +02004743{
Hante Meulemana0f07952013-02-08 15:53:47 +01004744 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Hante Meuleman1a873342012-09-27 14:17:54 +02004745 struct brcmf_scb_val_le scbval;
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004746 struct brcmf_if *ifp = netdev_priv(ndev);
Hante Meuleman1a873342012-09-27 14:17:54 +02004747 s32 err;
4748
Jouni Malinen89c771e2014-10-10 20:52:40 +03004749 if (!params->mac)
Hante Meuleman1a873342012-09-27 14:17:54 +02004750 return -EFAULT;
4751
Jouni Malinen89c771e2014-10-10 20:52:40 +03004752 brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
Hante Meuleman1a873342012-09-27 14:17:54 +02004753
Hante Meulemana0f07952013-02-08 15:53:47 +01004754 if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
4755 ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
Arend van Sprielce81e312012-10-22 13:55:37 -07004756 if (!check_vif_up(ifp->vif))
Hante Meuleman1a873342012-09-27 14:17:54 +02004757 return -EIO;
4758
Jouni Malinen89c771e2014-10-10 20:52:40 +03004759 memcpy(&scbval.ea, params->mac, ETH_ALEN);
Rafał Miłeckiba8b6ae2015-02-08 11:51:47 +01004760 scbval.val = cpu_to_le32(params->reason_code);
Arend van Spriel0abb5f212012-10-22 13:55:32 -07004761 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07004762 &scbval, sizeof(scbval));
Hante Meuleman1a873342012-09-27 14:17:54 +02004763 if (err)
Arend van Spriel57d6e912012-12-05 15:26:00 +01004764 brcmf_err("SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", err);
Hante Meuleman7ab6acd2013-02-08 15:53:58 +01004765
Arend van Sprield96b8012012-12-05 15:26:02 +01004766 brcmf_dbg(TRACE, "Exit\n");
Hante Meuleman1a873342012-09-27 14:17:54 +02004767 return err;
4768}
4769
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01004770static int
4771brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
4772 const u8 *mac, struct station_parameters *params)
4773{
4774 struct brcmf_if *ifp = netdev_priv(ndev);
4775 s32 err;
4776
4777 brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
4778 params->sta_flags_mask, params->sta_flags_set);
4779
4780 /* Ignore all 00 MAC */
4781 if (is_zero_ether_addr(mac))
4782 return 0;
4783
4784 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
4785 return 0;
4786
4787 if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
4788 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
4789 (void *)mac, ETH_ALEN);
4790 else
4791 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
4792 (void *)mac, ETH_ALEN);
4793 if (err < 0)
4794 brcmf_err("Setting SCB (de-)authorize failed, %d\n", err);
4795
4796 return err;
4797}
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004798
4799static void
4800brcmf_cfg80211_mgmt_frame_register(struct wiphy *wiphy,
4801 struct wireless_dev *wdev,
4802 u16 frame_type, bool reg)
4803{
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004804 struct brcmf_cfg80211_vif *vif;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004805 u16 mgmt_type;
4806
4807 brcmf_dbg(TRACE, "Enter, frame_type %04x, reg=%d\n", frame_type, reg);
4808
4809 mgmt_type = (frame_type & IEEE80211_FCTL_STYPE) >> 4;
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004810 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004811 if (reg)
4812 vif->mgmt_rx_reg |= BIT(mgmt_type);
4813 else
Hante Meuleman318a64c2013-02-08 15:53:45 +01004814 vif->mgmt_rx_reg &= ~BIT(mgmt_type);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004815}
4816
4817
4818static int
4819brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004820 struct cfg80211_mgmt_tx_params *params, u64 *cookie)
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004821{
4822 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Andrei Otcheretianskib176e622013-11-18 19:06:49 +02004823 struct ieee80211_channel *chan = params->chan;
4824 const u8 *buf = params->buf;
4825 size_t len = params->len;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004826 const struct ieee80211_mgmt *mgmt;
4827 struct brcmf_cfg80211_vif *vif;
4828 s32 err = 0;
4829 s32 ie_offset;
4830 s32 ie_len;
Hante Meuleman18e2f612013-02-08 15:53:49 +01004831 struct brcmf_fil_action_frame_le *action_frame;
4832 struct brcmf_fil_af_params_le *af_params;
4833 bool ack;
4834 s32 chan_nr;
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004835 u32 freq;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004836
4837 brcmf_dbg(TRACE, "Enter\n");
4838
4839 *cookie = 0;
4840
4841 mgmt = (const struct ieee80211_mgmt *)buf;
4842
Hante Meulemana0f07952013-02-08 15:53:47 +01004843 if (!ieee80211_is_mgmt(mgmt->frame_control)) {
4844 brcmf_err("Driver only allows MGMT packet type\n");
4845 return -EPERM;
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004846 }
Hante Meulemana0f07952013-02-08 15:53:47 +01004847
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004848 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
4849
Hante Meulemana0f07952013-02-08 15:53:47 +01004850 if (ieee80211_is_probe_resp(mgmt->frame_control)) {
4851 /* Right now the only reason to get a probe response */
4852 /* is for p2p listen response or for p2p GO from */
4853 /* wpa_supplicant. Unfortunately the probe is send */
4854 /* on primary ndev, while dongle wants it on the p2p */
4855 /* vif. Since this is only reason for a probe */
4856 /* response to be sent, the vif is taken from cfg. */
4857 /* If ever desired to send proberesp for non p2p */
4858 /* response then data should be checked for */
4859 /* "DIRECT-". Note in future supplicant will take */
4860 /* dedicated p2p wdev to do this and then this 'hack'*/
4861 /* is not needed anymore. */
4862 ie_offset = DOT11_MGMT_HDR_LEN +
4863 DOT11_BCN_PRB_FIXED_LEN;
4864 ie_len = len - ie_offset;
Hante Meulemana0f07952013-02-08 15:53:47 +01004865 if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
4866 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4867 err = brcmf_vif_set_mgmt_ie(vif,
4868 BRCMF_VNDR_IE_PRBRSP_FLAG,
4869 &buf[ie_offset],
4870 ie_len);
4871 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
4872 GFP_KERNEL);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004873 } else if (ieee80211_is_action(mgmt->frame_control)) {
4874 af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
4875 if (af_params == NULL) {
4876 brcmf_err("unable to allocate frame\n");
4877 err = -ENOMEM;
4878 goto exit;
4879 }
4880 action_frame = &af_params->action_frame;
4881 /* Add the packet Id */
4882 action_frame->packet_id = cpu_to_le32(*cookie);
4883 /* Add BSSID */
4884 memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
4885 memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
4886 /* Add the length exepted for 802.11 header */
4887 action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
Antonio Quartullic2ff8ca2013-06-11 14:20:01 +02004888 /* Add the channel. Use the one specified as parameter if any or
4889 * the current one (got from the firmware) otherwise
4890 */
4891 if (chan)
4892 freq = chan->center_freq;
4893 else
4894 brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
4895 &freq);
4896 chan_nr = ieee80211_frequency_to_channel(freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004897 af_params->channel = cpu_to_le32(chan_nr);
4898
4899 memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
4900 le16_to_cpu(action_frame->len));
4901
4902 brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
Antonio Quartulli86a9c4a2013-06-19 13:35:31 +02004903 *cookie, le16_to_cpu(action_frame->len), freq);
Hante Meuleman18e2f612013-02-08 15:53:49 +01004904
Arend van Spriel7fa2e352013-04-05 10:57:47 +02004905 ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
Hante Meuleman18e2f612013-02-08 15:53:49 +01004906 af_params);
4907
4908 cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
4909 GFP_KERNEL);
4910 kfree(af_params);
Hante Meulemana0f07952013-02-08 15:53:47 +01004911 } else {
4912 brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
Alexey Dobriyan5b5e0922017-02-27 14:30:02 -08004913 brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len);
Hante Meulemana0f07952013-02-08 15:53:47 +01004914 }
4915
Hante Meuleman18e2f612013-02-08 15:53:49 +01004916exit:
Hante Meuleman0de8aac2013-02-08 15:53:38 +01004917 return err;
4918}
4919
4920
4921static int
4922brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
4923 struct wireless_dev *wdev,
4924 u64 cookie)
4925{
4926 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4927 struct brcmf_cfg80211_vif *vif;
4928 int err = 0;
4929
4930 brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
4931
4932 vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
4933 if (vif == NULL) {
4934 brcmf_err("No p2p device available for probe response\n");
4935 err = -ENODEV;
4936 goto exit;
4937 }
4938 brcmf_p2p_cancel_remain_on_channel(vif->ifp);
4939exit:
4940 return err;
4941}
4942
Rafał Miłeckiee6e7aa2016-05-20 13:38:58 +02004943static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
4944 struct wireless_dev *wdev,
4945 struct cfg80211_chan_def *chandef)
4946{
4947 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
4948 struct net_device *ndev = wdev->netdev;
4949 struct brcmf_if *ifp;
4950 struct brcmu_chan ch;
4951 enum nl80211_band band = 0;
4952 enum nl80211_chan_width width = 0;
4953 u32 chanspec;
4954 int freq, err;
4955
4956 if (!ndev)
4957 return -ENODEV;
4958 ifp = netdev_priv(ndev);
4959
4960 err = brcmf_fil_iovar_int_get(ifp, "chanspec", &chanspec);
4961 if (err) {
4962 brcmf_err("chanspec failed (%d)\n", err);
4963 return err;
4964 }
4965
4966 ch.chspec = chanspec;
4967 cfg->d11inf.decchspec(&ch);
4968
4969 switch (ch.band) {
4970 case BRCMU_CHAN_BAND_2G:
4971 band = NL80211_BAND_2GHZ;
4972 break;
4973 case BRCMU_CHAN_BAND_5G:
4974 band = NL80211_BAND_5GHZ;
4975 break;
4976 }
4977
4978 switch (ch.bw) {
4979 case BRCMU_CHAN_BW_80:
4980 width = NL80211_CHAN_WIDTH_80;
4981 break;
4982 case BRCMU_CHAN_BW_40:
4983 width = NL80211_CHAN_WIDTH_40;
4984 break;
4985 case BRCMU_CHAN_BW_20:
4986 width = NL80211_CHAN_WIDTH_20;
4987 break;
4988 case BRCMU_CHAN_BW_80P80:
4989 width = NL80211_CHAN_WIDTH_80P80;
4990 break;
4991 case BRCMU_CHAN_BW_160:
4992 width = NL80211_CHAN_WIDTH_160;
4993 break;
4994 }
4995
4996 freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
4997 chandef->chan = ieee80211_get_channel(wiphy, freq);
4998 chandef->width = width;
4999 chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);
5000 chandef->center_freq2 = 0;
5001
5002 return 0;
5003}
5004
Piotr Haber61730d42013-04-23 12:53:12 +02005005static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
5006 struct wireless_dev *wdev,
5007 enum nl80211_crit_proto_id proto,
5008 u16 duration)
5009{
5010 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5011 struct brcmf_cfg80211_vif *vif;
5012
5013 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5014
5015 /* only DHCP support for now */
5016 if (proto != NL80211_CRIT_PROTO_DHCP)
5017 return -EINVAL;
5018
5019 /* suppress and abort scanning */
5020 set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
5021 brcmf_abort_scanning(cfg);
5022
5023 return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
5024}
5025
5026static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
5027 struct wireless_dev *wdev)
5028{
5029 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5030 struct brcmf_cfg80211_vif *vif;
5031
5032 vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
5033
5034 brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
5035 clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
5036}
5037
Hante Meuleman70b7d942014-07-30 13:20:07 +02005038static s32
5039brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
5040 const struct brcmf_event_msg *e, void *data)
5041{
5042 switch (e->reason) {
5043 case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
5044 brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
5045 break;
5046 case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
5047 brcmf_dbg(TRACE, "TDLS Peer Connected\n");
5048 brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5049 break;
5050 case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
5051 brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
5052 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5053 break;
5054 }
5055
5056 return 0;
5057}
5058
Arend van Spriel89c2f382013-08-10 12:27:25 +02005059static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
5060{
5061 int ret;
5062
5063 switch (oper) {
5064 case NL80211_TDLS_DISCOVERY_REQ:
5065 ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
5066 break;
5067 case NL80211_TDLS_SETUP:
5068 ret = BRCMF_TDLS_MANUAL_EP_CREATE;
5069 break;
5070 case NL80211_TDLS_TEARDOWN:
5071 ret = BRCMF_TDLS_MANUAL_EP_DELETE;
5072 break;
5073 default:
5074 brcmf_err("unsupported operation: %d\n", oper);
5075 ret = -EOPNOTSUPP;
5076 }
5077 return ret;
5078}
5079
5080static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
Johannes Berg3b3a0162014-05-19 17:19:31 +02005081 struct net_device *ndev, const u8 *peer,
Arend van Spriel89c2f382013-08-10 12:27:25 +02005082 enum nl80211_tdls_operation oper)
5083{
5084 struct brcmf_if *ifp;
5085 struct brcmf_tdls_iovar_le info;
5086 int ret = 0;
5087
5088 ret = brcmf_convert_nl80211_tdls_oper(oper);
5089 if (ret < 0)
5090 return ret;
5091
5092 ifp = netdev_priv(ndev);
5093 memset(&info, 0, sizeof(info));
5094 info.mode = (u8)ret;
5095 if (peer)
5096 memcpy(info.ea, peer, ETH_ALEN);
5097
5098 ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
5099 &info, sizeof(info));
5100 if (ret < 0)
5101 brcmf_err("tdls_endpoint iovar failed: ret=%d\n", ret);
5102
5103 return ret;
5104}
5105
Arend Van Spriel2a2a5d12017-01-27 12:27:48 +00005106static int
5107brcmf_cfg80211_update_conn_params(struct wiphy *wiphy,
5108 struct net_device *ndev,
5109 struct cfg80211_connect_params *sme,
5110 u32 changed)
5111{
5112 struct brcmf_if *ifp;
5113 int err;
5114
5115 if (!(changed & UPDATE_ASSOC_IES))
5116 return 0;
5117
5118 ifp = netdev_priv(ndev);
5119 err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
5120 sme->ie, sme->ie_len);
5121 if (err)
5122 brcmf_err("Set Assoc REQ IE Failed\n");
5123 else
5124 brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
5125
5126 return err;
5127}
5128
Hante Meuleman5c22fb82016-02-17 11:27:03 +01005129#ifdef CONFIG_PM
5130static int
5131brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
5132 struct cfg80211_gtk_rekey_data *gtk)
5133{
5134 struct brcmf_if *ifp = netdev_priv(ndev);
5135 struct brcmf_gtk_keyinfo_le gtk_le;
5136 int ret;
5137
5138 brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
5139
5140 memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
5141 memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
5142 memcpy(gtk_le.replay_counter, gtk->replay_ctr,
5143 sizeof(gtk_le.replay_counter));
5144
5145 ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", &gtk_le,
5146 sizeof(gtk_le));
5147 if (ret < 0)
5148 brcmf_err("gtk_key_info iovar failed: ret=%d\n", ret);
5149
5150 return ret;
5151}
5152#endif
5153
5154static struct cfg80211_ops brcmf_cfg80211_ops = {
Arend van Spriel9f440b72013-02-08 15:53:36 +01005155 .add_virtual_intf = brcmf_cfg80211_add_iface,
5156 .del_virtual_intf = brcmf_cfg80211_del_iface,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005157 .change_virtual_intf = brcmf_cfg80211_change_iface,
5158 .scan = brcmf_cfg80211_scan,
5159 .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
5160 .join_ibss = brcmf_cfg80211_join_ibss,
5161 .leave_ibss = brcmf_cfg80211_leave_ibss,
5162 .get_station = brcmf_cfg80211_get_station,
Hante Meulemanbf2a7e02015-10-08 20:33:18 +02005163 .dump_station = brcmf_cfg80211_dump_station,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005164 .set_tx_power = brcmf_cfg80211_set_tx_power,
5165 .get_tx_power = brcmf_cfg80211_get_tx_power,
5166 .add_key = brcmf_cfg80211_add_key,
5167 .del_key = brcmf_cfg80211_del_key,
5168 .get_key = brcmf_cfg80211_get_key,
5169 .set_default_key = brcmf_cfg80211_config_default_key,
5170 .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
5171 .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005172 .connect = brcmf_cfg80211_connect,
5173 .disconnect = brcmf_cfg80211_disconnect,
5174 .suspend = brcmf_cfg80211_suspend,
5175 .resume = brcmf_cfg80211_resume,
5176 .set_pmksa = brcmf_cfg80211_set_pmksa,
5177 .del_pmksa = brcmf_cfg80211_del_pmksa,
Arend van Sprielcbaa1772012-08-30 19:43:02 +02005178 .flush_pmksa = brcmf_cfg80211_flush_pmksa,
Hante Meuleman1a873342012-09-27 14:17:54 +02005179 .start_ap = brcmf_cfg80211_start_ap,
5180 .stop_ap = brcmf_cfg80211_stop_ap,
Hante Meulemana0f07952013-02-08 15:53:47 +01005181 .change_beacon = brcmf_cfg80211_change_beacon,
Hante Meuleman1a873342012-09-27 14:17:54 +02005182 .del_station = brcmf_cfg80211_del_station,
Hante Meuleman6b89dcb2014-12-21 12:43:52 +01005183 .change_station = brcmf_cfg80211_change_station,
Arend van Spriele5806072012-09-19 22:21:08 +02005184 .sched_scan_start = brcmf_cfg80211_sched_scan_start,
5185 .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005186 .mgmt_frame_register = brcmf_cfg80211_mgmt_frame_register,
5187 .mgmt_tx = brcmf_cfg80211_mgmt_tx,
5188 .remain_on_channel = brcmf_p2p_remain_on_channel,
5189 .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
Rafał Miłeckiee6e7aa2016-05-20 13:38:58 +02005190 .get_channel = brcmf_cfg80211_get_channel,
Arend van Spriel27f10e32013-04-05 10:57:50 +02005191 .start_p2p_device = brcmf_p2p_start_device,
5192 .stop_p2p_device = brcmf_p2p_stop_device,
Piotr Haber61730d42013-04-23 12:53:12 +02005193 .crit_proto_start = brcmf_cfg80211_crit_proto_start,
5194 .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
Arend van Spriel89c2f382013-08-10 12:27:25 +02005195 .tdls_oper = brcmf_cfg80211_tdls_oper,
Arend Van Spriel2a2a5d12017-01-27 12:27:48 +00005196 .update_connect_params = brcmf_cfg80211_update_conn_params,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005197};
5198
Arend van Spriel3eacf862012-10-22 13:55:30 -07005199struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
Rafał Miłecki26072332016-06-06 23:03:55 +02005200 enum nl80211_iftype type)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005201{
Hante Meulemana44aa402014-12-03 21:05:33 +01005202 struct brcmf_cfg80211_vif *vif_walk;
Arend van Spriel3eacf862012-10-22 13:55:30 -07005203 struct brcmf_cfg80211_vif *vif;
Hante Meulemana44aa402014-12-03 21:05:33 +01005204 bool mbss;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005205
Arend van Spriel33a6b152013-02-08 15:53:39 +01005206 brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
Arend van Spriel9f440b72013-02-08 15:53:36 +01005207 sizeof(*vif));
Arend van Spriel3eacf862012-10-22 13:55:30 -07005208 vif = kzalloc(sizeof(*vif), GFP_KERNEL);
5209 if (!vif)
5210 return ERR_PTR(-ENOMEM);
5211
5212 vif->wdev.wiphy = cfg->wiphy;
Arend van Spriel9f440b72013-02-08 15:53:36 +01005213 vif->wdev.iftype = type;
Arend van Spriel3eacf862012-10-22 13:55:30 -07005214
Arend van Spriel6ac4f4e2012-10-22 13:55:31 -07005215 brcmf_init_prof(&vif->profile);
5216
Hante Meulemana44aa402014-12-03 21:05:33 +01005217 if (type == NL80211_IFTYPE_AP) {
5218 mbss = false;
5219 list_for_each_entry(vif_walk, &cfg->vif_list, list) {
5220 if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
5221 mbss = true;
5222 break;
5223 }
5224 }
5225 vif->mbss = mbss;
5226 }
5227
Arend van Spriel3eacf862012-10-22 13:55:30 -07005228 list_add_tail(&vif->list, &cfg->vif_list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07005229 return vif;
5230}
5231
Arend van Spriel427dec52014-01-06 12:40:47 +01005232void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
Arend van Spriel3eacf862012-10-22 13:55:30 -07005233{
Arend van Spriel3eacf862012-10-22 13:55:30 -07005234 list_del(&vif->list);
Arend van Spriel3eacf862012-10-22 13:55:30 -07005235 kfree(vif);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005236}
5237
Arend van Spriel9df4d542014-01-06 12:40:49 +01005238void brcmf_cfg80211_free_netdev(struct net_device *ndev)
5239{
5240 struct brcmf_cfg80211_vif *vif;
5241 struct brcmf_if *ifp;
5242
5243 ifp = netdev_priv(ndev);
5244 vif = ifp->vif;
5245
Arend van Spriel95ef1232015-08-26 22:15:04 +02005246 if (vif)
5247 brcmf_free_vif(vif);
Arend van Spriel9df4d542014-01-06 12:40:49 +01005248}
5249
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005250static bool brcmf_is_linkup(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005251{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005252 u32 event = e->event_code;
5253 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005254
5255 if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01005256 brcmf_dbg(CONN, "Processing set ssid\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005257 return true;
5258 }
5259
5260 return false;
5261}
5262
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005263static bool brcmf_is_linkdown(const struct brcmf_event_msg *e)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005264{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005265 u32 event = e->event_code;
5266 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005267
Hante Meuleman68ca3952014-02-25 20:30:26 +01005268 if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
5269 (event == BRCMF_E_DISASSOC_IND) ||
5270 ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
Arend van Spriel16886732012-12-05 15:26:04 +01005271 brcmf_dbg(CONN, "Processing link down\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005272 return true;
5273 }
5274 return false;
5275}
5276
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005277static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005278 const struct brcmf_event_msg *e)
5279{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005280 u32 event = e->event_code;
5281 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005282
5283 if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
Arend van Spriel16886732012-12-05 15:26:04 +01005284 brcmf_dbg(CONN, "Processing Link %s & no network found\n",
5285 e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005286 return true;
5287 }
5288
5289 if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
Arend van Spriel16886732012-12-05 15:26:04 +01005290 brcmf_dbg(CONN, "Processing connecting & no network found\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005291 return true;
5292 }
5293
5294 return false;
5295}
5296
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005297static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005298{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005299 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005300
5301 kfree(conn_info->req_ie);
5302 conn_info->req_ie = NULL;
5303 conn_info->req_ie_len = 0;
5304 kfree(conn_info->resp_ie);
5305 conn_info->resp_ie = NULL;
5306 conn_info->resp_ie_len = 0;
5307}
5308
Hante Meuleman89286dc2013-02-08 15:53:46 +01005309static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
5310 struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005311{
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005312 struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005313 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005314 u32 req_len;
5315 u32 resp_len;
5316 s32 err = 0;
5317
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005318 brcmf_clear_assoc_ies(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005319
Arend van Sprielac24be62012-10-22 10:36:23 -07005320 err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
5321 cfg->extra_buf, WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005322 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005323 brcmf_err("could not get assoc info (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005324 return err;
5325 }
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005326 assoc_info =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005327 (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
Arend van Sprielc4e382d2011-10-12 20:51:21 +02005328 req_len = le32_to_cpu(assoc_info->req_len);
5329 resp_len = le32_to_cpu(assoc_info->resp_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005330 if (req_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005331 err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005332 cfg->extra_buf,
5333 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005334 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005335 brcmf_err("could not get assoc req (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005336 return err;
5337 }
5338 conn_info->req_ie_len = req_len;
5339 conn_info->req_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005340 kmemdup(cfg->extra_buf, conn_info->req_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005341 GFP_KERNEL);
5342 } else {
5343 conn_info->req_ie_len = 0;
5344 conn_info->req_ie = NULL;
5345 }
5346 if (resp_len) {
Arend van Sprielac24be62012-10-22 10:36:23 -07005347 err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005348 cfg->extra_buf,
5349 WL_ASSOC_INFO_MAX);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005350 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005351 brcmf_err("could not get assoc resp (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005352 return err;
5353 }
5354 conn_info->resp_ie_len = resp_len;
5355 conn_info->resp_ie =
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005356 kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005357 GFP_KERNEL);
5358 } else {
5359 conn_info->resp_ie_len = 0;
5360 conn_info->resp_ie = NULL;
5361 }
Arend van Spriel16886732012-12-05 15:26:04 +01005362 brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
5363 conn_info->req_ie_len, conn_info->resp_ie_len);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005364
5365 return err;
5366}
5367
5368static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005369brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005370 struct net_device *ndev,
5371 const struct brcmf_event_msg *e)
5372{
Arend van Sprielc1179032012-10-22 13:55:33 -07005373 struct brcmf_if *ifp = netdev_priv(ndev);
5374 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005375 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
5376 struct wiphy *wiphy = cfg_to_wiphy(cfg);
Franky Lina180b832012-10-10 11:13:09 -07005377 struct ieee80211_channel *notify_channel = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005378 struct ieee80211_supported_band *band;
Franky Lina180b832012-10-10 11:13:09 -07005379 struct brcmf_bss_info_le *bi;
Franky Lin83cf17a2013-04-11 13:28:50 +02005380 struct brcmu_chan ch;
Avraham Stern29ce6ec2017-04-26 10:58:49 +03005381 struct cfg80211_roam_info roam_info = {};
Arend van Spriel5b435de2011-10-05 13:19:03 +02005382 u32 freq;
5383 s32 err = 0;
Franky Lina180b832012-10-10 11:13:09 -07005384 u8 *buf;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005385
Arend van Sprield96b8012012-12-05 15:26:02 +01005386 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005387
Hante Meuleman89286dc2013-02-08 15:53:46 +01005388 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005389 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005390 brcmf_update_bss_info(cfg, ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005391
Franky Lina180b832012-10-10 11:13:09 -07005392 buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
5393 if (buf == NULL) {
5394 err = -ENOMEM;
5395 goto done;
5396 }
Arend van Spriel5b435de2011-10-05 13:19:03 +02005397
Franky Lina180b832012-10-10 11:13:09 -07005398 /* data sent to dongle has to be little endian */
5399 *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
Arend van Sprielc1179032012-10-22 13:55:33 -07005400 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
Arend van Sprielac24be62012-10-22 10:36:23 -07005401 buf, WL_BSS_INFO_MAX);
Franky Lina180b832012-10-10 11:13:09 -07005402
5403 if (err)
5404 goto done;
5405
5406 bi = (struct brcmf_bss_info_le *)(buf + 4);
Franky Lin83cf17a2013-04-11 13:28:50 +02005407 ch.chspec = le16_to_cpu(bi->chanspec);
5408 cfg->d11inf.decchspec(&ch);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005409
Franky Lin83cf17a2013-04-11 13:28:50 +02005410 if (ch.band == BRCMU_CHAN_BAND_2G)
Johannes Berg57fbcce2016-04-12 15:56:15 +02005411 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005412 else
Johannes Berg57fbcce2016-04-12 15:56:15 +02005413 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005414
Rafał Miłecki4712d882016-05-20 13:38:57 +02005415 freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005416 notify_channel = ieee80211_get_channel(wiphy, freq);
5417
Franky Lina180b832012-10-10 11:13:09 -07005418done:
5419 kfree(buf);
Avraham Stern29ce6ec2017-04-26 10:58:49 +03005420
5421 roam_info.channel = notify_channel;
5422 roam_info.bssid = profile->bssid;
5423 roam_info.req_ie = conn_info->req_ie;
5424 roam_info.req_ie_len = conn_info->req_ie_len;
5425 roam_info.resp_ie = conn_info->resp_ie;
5426 roam_info.resp_ie_len = conn_info->resp_ie_len;
5427
5428 cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005429 brcmf_dbg(CONN, "Report roaming result\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005430
Arend van Sprielc1179032012-10-22 13:55:33 -07005431 set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
Arend van Sprield96b8012012-12-05 15:26:02 +01005432 brcmf_dbg(TRACE, "Exit\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005433 return err;
5434}
5435
5436static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005437brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005438 struct net_device *ndev, const struct brcmf_event_msg *e,
5439 bool completed)
5440{
Arend van Sprielc1179032012-10-22 13:55:33 -07005441 struct brcmf_if *ifp = netdev_priv(ndev);
5442 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005443 struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005444
Arend van Sprield96b8012012-12-05 15:26:02 +01005445 brcmf_dbg(TRACE, "Enter\n");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005446
Arend van Sprielc1179032012-10-22 13:55:33 -07005447 if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5448 &ifp->vif->sme_state)) {
Arend van Spriel5b435de2011-10-05 13:19:03 +02005449 if (completed) {
Hante Meuleman89286dc2013-02-08 15:53:46 +01005450 brcmf_get_assoc_ies(cfg, ifp);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005451 memcpy(profile->bssid, e->addr, ETH_ALEN);
Hante Meuleman89286dc2013-02-08 15:53:46 +01005452 brcmf_update_bss_info(cfg, ifp);
5453 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5454 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005455 }
5456 cfg80211_connect_result(ndev,
Arend van Spriel06bb1232012-09-27 14:17:56 +02005457 (u8 *)profile->bssid,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005458 conn_info->req_ie,
5459 conn_info->req_ie_len,
5460 conn_info->resp_ie,
5461 conn_info->resp_ie_len,
5462 completed ? WLAN_STATUS_SUCCESS :
5463 WLAN_STATUS_AUTH_TIMEOUT,
5464 GFP_KERNEL);
Arend van Spriel16886732012-12-05 15:26:04 +01005465 brcmf_dbg(CONN, "Report connect result - connection %s\n",
5466 completed ? "succeeded" : "failed");
Arend van Spriel5b435de2011-10-05 13:19:03 +02005467 }
Arend van Sprield96b8012012-12-05 15:26:02 +01005468 brcmf_dbg(TRACE, "Exit\n");
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005469 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005470}
5471
5472static s32
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005473brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
Hante Meuleman1a873342012-09-27 14:17:54 +02005474 struct net_device *ndev,
5475 const struct brcmf_event_msg *e, void *data)
5476{
Hante Meuleman7ee29602013-02-06 18:40:43 +01005477 static int generation;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005478 u32 event = e->event_code;
5479 u32 reason = e->reason;
Hante Meuleman1a873342012-09-27 14:17:54 +02005480 struct station_info sinfo;
5481
Rafał Miłeckie1c122d2016-10-14 09:45:59 +02005482 brcmf_dbg(CONN, "event %s (%u), reason %d\n",
5483 brcmf_fweh_event_name(event), event, reason);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005484 if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
5485 ndev != cfg_to_ndev(cfg)) {
5486 brcmf_dbg(CONN, "AP mode link down\n");
5487 complete(&cfg->vif_disabled);
5488 return 0;
5489 }
Hante Meuleman1a873342012-09-27 14:17:54 +02005490
Hante Meuleman1a873342012-09-27 14:17:54 +02005491 if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
Hante Meuleman7ee29602013-02-06 18:40:43 +01005492 (reason == BRCMF_E_STATUS_SUCCESS)) {
5493 memset(&sinfo, 0, sizeof(sinfo));
Hante Meuleman1a873342012-09-27 14:17:54 +02005494 if (!data) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005495 brcmf_err("No IEs present in ASSOC/REASSOC_IND");
Hante Meuleman1a873342012-09-27 14:17:54 +02005496 return -EINVAL;
5497 }
5498 sinfo.assoc_req_ies = data;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005499 sinfo.assoc_req_ies_len = e->datalen;
Hante Meuleman1a873342012-09-27 14:17:54 +02005500 generation++;
5501 sinfo.generation = generation;
Hante Meuleman7ee29602013-02-06 18:40:43 +01005502 cfg80211_new_sta(ndev, e->addr, &sinfo, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005503 } else if ((event == BRCMF_E_DISASSOC_IND) ||
5504 (event == BRCMF_E_DEAUTH_IND) ||
5505 (event == BRCMF_E_DEAUTH)) {
Hante Meuleman7ee29602013-02-06 18:40:43 +01005506 cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
Hante Meuleman1a873342012-09-27 14:17:54 +02005507 }
Hante Meuleman7ee29602013-02-06 18:40:43 +01005508 return 0;
Hante Meuleman1a873342012-09-27 14:17:54 +02005509}
5510
5511static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005512brcmf_notify_connect_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005513 const struct brcmf_event_msg *e, void *data)
5514{
Arend van Spriel19937322012-11-05 16:22:32 -08005515 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5516 struct net_device *ndev = ifp->ndev;
Arend van Sprielc1179032012-10-22 13:55:33 -07005517 struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005518 struct ieee80211_channel *chan;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005519 s32 err = 0;
5520
Hante Meuleman8851cce2014-07-30 13:20:02 +02005521 if ((e->event_code == BRCMF_E_DEAUTH) ||
5522 (e->event_code == BRCMF_E_DEAUTH_IND) ||
5523 (e->event_code == BRCMF_E_DISASSOC_IND) ||
5524 ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
5525 brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
5526 }
5527
Arend van Spriel967fe2c2014-03-15 17:18:21 +01005528 if (brcmf_is_apmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005529 err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005530 } else if (brcmf_is_linkup(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005531 brcmf_dbg(CONN, "Linkup\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005532 if (brcmf_is_ibssmode(ifp->vif)) {
Hante Meulemanb0a79082015-12-10 13:43:07 +01005533 brcmf_inform_ibss(cfg, ndev, e->addr);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005534 chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
Arend van Spriel6c8c4f72012-09-27 14:17:57 +02005535 memcpy(profile->bssid, e->addr, ETH_ALEN);
Antonio Quartullife94f3a2014-01-29 17:53:43 +01005536 cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
Arend van Sprielc1179032012-10-22 13:55:33 -07005537 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5538 &ifp->vif->sme_state);
5539 set_bit(BRCMF_VIF_STATUS_CONNECTED,
5540 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005541 } else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005542 brcmf_bss_connect_done(cfg, ndev, e, true);
Hante Meuleman92121e62015-10-08 20:33:21 +02005543 brcmf_net_setcarrier(ifp, true);
Arend van Spriel903e0ee2012-11-28 21:44:11 +01005544 } else if (brcmf_is_linkdown(e)) {
Arend van Spriel16886732012-12-05 15:26:04 +01005545 brcmf_dbg(CONN, "Linkdown\n");
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005546 if (!brcmf_is_ibssmode(ifp->vif)) {
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005547 brcmf_bss_connect_done(cfg, ndev, e, false);
Hante Meuleman42e0ed02016-01-05 11:05:50 +01005548 brcmf_link_down(ifp->vif,
5549 brcmf_map_fw_linkdown_reason(e));
5550 brcmf_init_prof(ndev_to_prof(ndev));
5551 if (ndev != cfg_to_ndev(cfg))
5552 complete(&cfg->vif_disabled);
5553 brcmf_net_setcarrier(ifp, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005554 }
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005555 } else if (brcmf_is_nonetwork(cfg, e)) {
Arend van Spriel128ce3b2012-11-28 21:44:12 +01005556 if (brcmf_is_ibssmode(ifp->vif))
Arend van Sprielc1179032012-10-22 13:55:33 -07005557 clear_bit(BRCMF_VIF_STATUS_CONNECTING,
5558 &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005559 else
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005560 brcmf_bss_connect_done(cfg, ndev, e, false);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005561 }
5562
5563 return err;
5564}
5565
5566static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005567brcmf_notify_roaming_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005568 const struct brcmf_event_msg *e, void *data)
5569{
Arend van Spriel19937322012-11-05 16:22:32 -08005570 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005571 u32 event = e->event_code;
5572 u32 status = e->status;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005573
5574 if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
Arend van Sprielc1179032012-10-22 13:55:33 -07005575 if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
Arend van Spriel19937322012-11-05 16:22:32 -08005576 brcmf_bss_roaming_done(cfg, ifp->ndev, e);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005577 else
Arend van Spriel19937322012-11-05 16:22:32 -08005578 brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005579 }
5580
Peter Senna Tschudin12f32372014-05-31 10:14:06 -03005581 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005582}
5583
5584static s32
Arend van Spriel19937322012-11-05 16:22:32 -08005585brcmf_notify_mic_status(struct brcmf_if *ifp,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005586 const struct brcmf_event_msg *e, void *data)
5587{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005588 u16 flags = e->flags;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005589 enum nl80211_key_type key_type;
5590
5591 if (flags & BRCMF_EVENT_MSG_GROUP)
5592 key_type = NL80211_KEYTYPE_GROUP;
5593 else
5594 key_type = NL80211_KEYTYPE_PAIRWISE;
5595
Arend van Spriel19937322012-11-05 16:22:32 -08005596 cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
Arend van Spriel5b435de2011-10-05 13:19:03 +02005597 NULL, GFP_KERNEL);
5598
5599 return 0;
5600}
5601
Arend van Sprield3c0b632013-02-08 15:53:37 +01005602static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
5603 const struct brcmf_event_msg *e, void *data)
5604{
5605 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
5606 struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
5607 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
5608 struct brcmf_cfg80211_vif *vif;
5609
Hante Meuleman37a869e2015-10-29 20:33:17 +01005610 brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
Arend van Sprield3c0b632013-02-08 15:53:37 +01005611 ifevent->action, ifevent->flags, ifevent->ifidx,
Hante Meuleman37a869e2015-10-29 20:33:17 +01005612 ifevent->bsscfgidx);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005613
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09005614 spin_lock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005615 event->action = ifevent->action;
5616 vif = event->vif;
5617
5618 switch (ifevent->action) {
5619 case BRCMF_E_IF_ADD:
5620 /* waiting process may have timed out */
Wei Yongjundc4a7872013-02-22 21:32:20 +08005621 if (!cfg->vif_event.vif) {
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09005622 spin_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005623 return -EBADF;
Wei Yongjundc4a7872013-02-22 21:32:20 +08005624 }
Arend van Sprield3c0b632013-02-08 15:53:37 +01005625
5626 ifp->vif = vif;
5627 vif->ifp = ifp;
Arend van Spriel01b8e7d2013-04-05 10:57:51 +02005628 if (ifp->ndev) {
5629 vif->wdev.netdev = ifp->ndev;
5630 ifp->ndev->ieee80211_ptr = &vif->wdev;
5631 SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
5632 }
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09005633 spin_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005634 wake_up(&event->vif_wq);
Hante Meuleman4b3a89d2013-02-08 15:54:01 +01005635 return 0;
Arend van Sprield3c0b632013-02-08 15:53:37 +01005636
5637 case BRCMF_E_IF_DEL:
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09005638 spin_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005639 /* event may not be upon user request */
5640 if (brcmf_cfg80211_vif_event_armed(cfg))
5641 wake_up(&event->vif_wq);
5642 return 0;
5643
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005644 case BRCMF_E_IF_CHANGE:
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09005645 spin_unlock(&event->vif_event_lock);
Hante Meuleman7a5c1f62013-02-08 15:53:44 +01005646 wake_up(&event->vif_wq);
5647 return 0;
5648
Arend van Sprield3c0b632013-02-08 15:53:37 +01005649 default:
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09005650 spin_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005651 break;
5652 }
5653 return -EINVAL;
5654}
5655
Arend van Spriel5b435de2011-10-05 13:19:03 +02005656static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
5657{
Arend van Spriel5b435de2011-10-05 13:19:03 +02005658 conf->frag_threshold = (u32)-1;
5659 conf->rts_threshold = (u32)-1;
5660 conf->retry_short = (u32)-1;
5661 conf->retry_long = (u32)-1;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005662}
5663
Arend van Spriel5c36b992012-11-14 18:46:05 -08005664static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005665{
Arend van Spriel5c36b992012-11-14 18:46:05 -08005666 brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
5667 brcmf_notify_connect_status);
5668 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
5669 brcmf_notify_connect_status);
5670 brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
5671 brcmf_notify_connect_status);
5672 brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
5673 brcmf_notify_connect_status);
5674 brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
5675 brcmf_notify_connect_status);
5676 brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
5677 brcmf_notify_connect_status);
5678 brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
5679 brcmf_notify_roaming_status);
5680 brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
5681 brcmf_notify_mic_status);
5682 brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
5683 brcmf_notify_connect_status);
5684 brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
5685 brcmf_notify_sched_scan_results);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005686 brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
5687 brcmf_notify_vif_event);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005688 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005689 brcmf_p2p_notify_rx_mgmt_p2p_probereq);
Hante Meuleman0de8aac2013-02-08 15:53:38 +01005690 brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
5691 brcmf_p2p_notify_listen_complete);
Hante Meulemane6da3402013-02-08 15:53:48 +01005692 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
5693 brcmf_p2p_notify_action_frame_rx);
Hante Meuleman18e2f612013-02-08 15:53:49 +01005694 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
5695 brcmf_p2p_notify_action_tx_complete);
Hante Meuleman6eda4e22013-02-08 15:54:02 +01005696 brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
5697 brcmf_p2p_notify_action_tx_complete);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005698}
5699
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005700static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005701{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005702 kfree(cfg->conf);
5703 cfg->conf = NULL;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005704 kfree(cfg->extra_buf);
5705 cfg->extra_buf = NULL;
Hante Meuleman3021ad92016-01-05 11:05:45 +01005706 kfree(cfg->wowl.nd);
5707 cfg->wowl.nd = NULL;
5708 kfree(cfg->wowl.nd_info);
5709 cfg->wowl.nd_info = NULL;
Hante Meulemand5367332016-02-17 11:26:51 +01005710 kfree(cfg->escan_info.escan_buf);
5711 cfg->escan_info.escan_buf = NULL;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005712}
5713
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005714static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005715{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005716 cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
5717 if (!cfg->conf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005718 goto init_priv_mem_out;
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005719 cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
5720 if (!cfg->extra_buf)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005721 goto init_priv_mem_out;
Hante Meuleman3021ad92016-01-05 11:05:45 +01005722 cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
5723 if (!cfg->wowl.nd)
5724 goto init_priv_mem_out;
5725 cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
5726 sizeof(struct cfg80211_wowlan_nd_match *),
5727 GFP_KERNEL);
5728 if (!cfg->wowl.nd_info)
5729 goto init_priv_mem_out;
Hante Meulemand5367332016-02-17 11:26:51 +01005730 cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
5731 if (!cfg->escan_info.escan_buf)
5732 goto init_priv_mem_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005733
5734 return 0;
5735
5736init_priv_mem_out:
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005737 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005738
5739 return -ENOMEM;
5740}
5741
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005742static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005743{
5744 s32 err = 0;
5745
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005746 cfg->scan_request = NULL;
5747 cfg->pwr_save = true;
Hante Meuleman68ca3952014-02-25 20:30:26 +01005748 cfg->active_scan = true; /* we do active scan per default */
5749 cfg->dongle_up = false; /* dongle is not up yet */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005750 err = brcmf_init_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005751 if (err)
5752 return err;
Arend van Spriel5c36b992012-11-14 18:46:05 -08005753 brcmf_register_event_handlers(cfg);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005754 mutex_init(&cfg->usr_sync);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005755 brcmf_init_escan(cfg);
5756 brcmf_init_conf(cfg->conf);
Arend van Spriel5f4f9f12013-02-08 15:53:41 +01005757 init_completion(&cfg->vif_disabled);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005758 return err;
5759}
5760
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005761static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005762{
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005763 cfg->dongle_up = false; /* dongle down */
Arend van Spriel27a68fe2012-09-27 14:17:55 +02005764 brcmf_abort_scanning(cfg);
5765 brcmf_deinit_priv_mem(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005766}
5767
Arend van Sprield3c0b632013-02-08 15:53:37 +01005768static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
5769{
5770 init_waitqueue_head(&event->vif_wq);
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09005771 spin_lock_init(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01005772}
5773
Hante Meuleman1119e232015-11-25 11:32:42 +01005774static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005775{
Hante Meuleman1119e232015-11-25 11:32:42 +01005776 s32 err;
5777 u32 bcn_timeout;
Arend van Sprielf588bc02011-10-12 20:51:22 +02005778 __le32 roamtrigger[2];
5779 __le32 roam_delta[2];
Arend van Spriel5b435de2011-10-05 13:19:03 +02005780
Hante Meuleman1119e232015-11-25 11:32:42 +01005781 /* Configure beacon timeout value based upon roaming setting */
Hante Meuleman7d34b052016-01-02 09:41:41 +01005782 if (ifp->drvr->settings->roamoff)
Hante Meuleman1119e232015-11-25 11:32:42 +01005783 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
5784 else
5785 bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
5786 err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
5787 if (err) {
5788 brcmf_err("bcn_timeout error (%d)\n", err);
5789 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005790 }
5791
Hante Meuleman1119e232015-11-25 11:32:42 +01005792 /* Enable/Disable built-in roaming to allow supplicant to take care of
5793 * roaming.
Arend van Spriel5b435de2011-10-05 13:19:03 +02005794 */
Hante Meuleman68ca3952014-02-25 20:30:26 +01005795 brcmf_dbg(INFO, "Internal Roaming = %s\n",
Hante Meuleman7d34b052016-01-02 09:41:41 +01005796 ifp->drvr->settings->roamoff ? "Off" : "On");
5797 err = brcmf_fil_iovar_int_set(ifp, "roam_off",
5798 ifp->drvr->settings->roamoff);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005799 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005800 brcmf_err("roam_off error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005801 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005802 }
5803
Arend van Sprielf588bc02011-10-12 20:51:22 +02005804 roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
5805 roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005806 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005807 (void *)roamtrigger, sizeof(roamtrigger));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005808 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005809 brcmf_err("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005810 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005811 }
5812
Arend van Sprielf588bc02011-10-12 20:51:22 +02005813 roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
5814 roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
Arend van Sprielac24be62012-10-22 10:36:23 -07005815 err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
Hante Meuleman81f5dcb2012-10-22 10:36:14 -07005816 (void *)roam_delta, sizeof(roam_delta));
Arend van Spriel5b435de2011-10-05 13:19:03 +02005817 if (err) {
Arend van Spriel57d6e912012-12-05 15:26:00 +01005818 brcmf_err("WLC_SET_ROAM_DELTA error (%d)\n", err);
Hante Meuleman1119e232015-11-25 11:32:42 +01005819 goto roam_setup_done;
Arend van Spriel5b435de2011-10-05 13:19:03 +02005820 }
5821
Hante Meuleman1119e232015-11-25 11:32:42 +01005822roam_setup_done:
Arend van Spriel5b435de2011-10-05 13:19:03 +02005823 return err;
5824}
5825
5826static s32
Hante Meuleman1678ba82015-12-10 13:43:00 +01005827brcmf_dongle_scantime(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02005828{
5829 s32 err = 0;
5830
Arend van Sprielac24be62012-10-22 10:36:23 -07005831 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005832 BRCMF_SCAN_CHANNEL_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005833 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005834 brcmf_err("Scan assoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005835 goto dongle_scantime_out;
5836 }
Arend van Sprielac24be62012-10-22 10:36:23 -07005837 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005838 BRCMF_SCAN_UNASSOC_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005839 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005840 brcmf_err("Scan unassoc time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005841 goto dongle_scantime_out;
5842 }
5843
Arend van Sprielac24be62012-10-22 10:36:23 -07005844 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
Hante Meuleman1678ba82015-12-10 13:43:00 +01005845 BRCMF_SCAN_PASSIVE_TIME);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005846 if (err) {
Hante Meuleman1678ba82015-12-10 13:43:00 +01005847 brcmf_err("Scan passive time error (%d)\n", err);
Arend van Spriel5b435de2011-10-05 13:19:03 +02005848 goto dongle_scantime_out;
5849 }
5850
5851dongle_scantime_out:
5852 return err;
5853}
5854
Arend van Sprielb48d8912014-07-12 08:49:41 +02005855static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
5856 struct brcmu_chan *ch)
5857{
5858 u32 ht40_flag;
5859
5860 ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
5861 if (ch->sb == BRCMU_CHAN_SB_U) {
5862 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5863 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5864 channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
5865 } else {
5866 /* It should be one of
5867 * IEEE80211_CHAN_NO_HT40 or
5868 * IEEE80211_CHAN_NO_HT40PLUS
5869 */
5870 channel->flags &= ~IEEE80211_CHAN_NO_HT40;
5871 if (ht40_flag == IEEE80211_CHAN_NO_HT40)
5872 channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
5873 }
5874}
5875
5876static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
5877 u32 bw_cap[])
Hante Meulemand48200b2013-04-03 12:40:29 +02005878{
5879 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Sprielb48d8912014-07-12 08:49:41 +02005880 struct ieee80211_supported_band *band;
5881 struct ieee80211_channel *channel;
5882 struct wiphy *wiphy;
Hante Meulemand48200b2013-04-03 12:40:29 +02005883 struct brcmf_chanspec_list *list;
Franky Lin83cf17a2013-04-11 13:28:50 +02005884 struct brcmu_chan ch;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005885 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02005886 u8 *pbuf;
5887 u32 i, j;
5888 u32 total;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005889 u32 chaninfo;
Hante Meulemand48200b2013-04-03 12:40:29 +02005890
5891 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
5892
5893 if (pbuf == NULL)
5894 return -ENOMEM;
5895
5896 list = (struct brcmf_chanspec_list *)pbuf;
5897
5898 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
5899 BRCMF_DCMD_MEDLEN);
5900 if (err) {
5901 brcmf_err("get chanspecs error (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005902 goto fail_pbuf;
Hante Meulemand48200b2013-04-03 12:40:29 +02005903 }
5904
Arend van Sprielb48d8912014-07-12 08:49:41 +02005905 wiphy = cfg_to_wiphy(cfg);
Johannes Berg57fbcce2016-04-12 15:56:15 +02005906 band = wiphy->bands[NL80211_BAND_2GHZ];
Arend van Spriel58de92d2015-04-14 20:10:24 +02005907 if (band)
5908 for (i = 0; i < band->n_channels; i++)
5909 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Johannes Berg57fbcce2016-04-12 15:56:15 +02005910 band = wiphy->bands[NL80211_BAND_5GHZ];
Arend van Spriel58de92d2015-04-14 20:10:24 +02005911 if (band)
5912 for (i = 0; i < band->n_channels; i++)
5913 band->channels[i].flags = IEEE80211_CHAN_DISABLED;
Hante Meulemand48200b2013-04-03 12:40:29 +02005914
5915 total = le32_to_cpu(list->count);
5916 for (i = 0; i < total; i++) {
Franky Lin83cf17a2013-04-11 13:28:50 +02005917 ch.chspec = (u16)le32_to_cpu(list->element[i]);
5918 cfg->d11inf.decchspec(&ch);
Hante Meulemand48200b2013-04-03 12:40:29 +02005919
Franky Lin83cf17a2013-04-11 13:28:50 +02005920 if (ch.band == BRCMU_CHAN_BAND_2G) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02005921 band = wiphy->bands[NL80211_BAND_2GHZ];
Franky Lin83cf17a2013-04-11 13:28:50 +02005922 } else if (ch.band == BRCMU_CHAN_BAND_5G) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02005923 band = wiphy->bands[NL80211_BAND_5GHZ];
Hante Meulemand48200b2013-04-03 12:40:29 +02005924 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01005925 brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec);
Hante Meulemand48200b2013-04-03 12:40:29 +02005926 continue;
5927 }
Arend van Spriel58de92d2015-04-14 20:10:24 +02005928 if (!band)
5929 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005930 if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
Arend van Spriel2375d972014-01-06 12:40:41 +01005931 ch.bw == BRCMU_CHAN_BW_40)
Hante Meulemand48200b2013-04-03 12:40:29 +02005932 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005933 if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
Arend van Sprielee942ec2014-05-12 10:47:38 +02005934 ch.bw == BRCMU_CHAN_BW_80)
5935 continue;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005936
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005937 channel = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005938 for (j = 0; j < band->n_channels; j++) {
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005939 if (band->channels[j].hw_value == ch.control_ch_num) {
5940 channel = &band->channels[j];
Hante Meulemand48200b2013-04-03 12:40:29 +02005941 break;
5942 }
5943 }
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005944 if (!channel) {
5945 /* It seems firmware supports some channel we never
5946 * considered. Something new in IEEE standard?
5947 */
5948 brcmf_err("Ignoring unexpected firmware channel %d\n",
5949 ch.control_ch_num);
5950 continue;
5951 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005952
Rafał Miłecki0f83ff62017-01-17 23:35:50 +01005953 if (channel->orig_flags & IEEE80211_CHAN_DISABLED)
5954 continue;
5955
Arend van Sprielb48d8912014-07-12 08:49:41 +02005956 /* assuming the chanspecs order is HT20,
5957 * HT40 upper, HT40 lower, and VHT80.
5958 */
5959 if (ch.bw == BRCMU_CHAN_BW_80) {
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005960 channel->flags &= ~IEEE80211_CHAN_NO_80MHZ;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005961 } else if (ch.bw == BRCMU_CHAN_BW_40) {
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005962 brcmf_update_bw40_channel_flag(channel, &ch);
Arend van Sprielb48d8912014-07-12 08:49:41 +02005963 } else {
Arend van Spriel58de92d2015-04-14 20:10:24 +02005964 /* enable the channel and disable other bandwidths
5965 * for now as mentioned order assure they are enabled
5966 * for subsequent chanspecs.
Arend van Sprielee942ec2014-05-12 10:47:38 +02005967 */
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005968 channel->flags = IEEE80211_CHAN_NO_HT40 |
5969 IEEE80211_CHAN_NO_80MHZ;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005970 ch.bw = BRCMU_CHAN_BW_20;
5971 cfg->d11inf.encchspec(&ch);
5972 chaninfo = ch.chspec;
5973 err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info",
5974 &chaninfo);
5975 if (!err) {
5976 if (chaninfo & WL_CHAN_RADAR)
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005977 channel->flags |=
Arend van Sprielb48d8912014-07-12 08:49:41 +02005978 (IEEE80211_CHAN_RADAR |
5979 IEEE80211_CHAN_NO_IR);
5980 if (chaninfo & WL_CHAN_PASSIVE)
Rafał Miłecki77c0d0c2017-01-04 12:09:41 +01005981 channel->flags |=
Arend van Sprielb48d8912014-07-12 08:49:41 +02005982 IEEE80211_CHAN_NO_IR;
Hante Meulemand48200b2013-04-03 12:40:29 +02005983 }
Hante Meulemand48200b2013-04-03 12:40:29 +02005984 }
5985 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02005986
Arend van Sprielb48d8912014-07-12 08:49:41 +02005987fail_pbuf:
Hante Meulemand48200b2013-04-03 12:40:29 +02005988 kfree(pbuf);
5989 return err;
5990}
5991
Arend van Sprielb48d8912014-07-12 08:49:41 +02005992static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005993{
Arend van Sprielb48d8912014-07-12 08:49:41 +02005994 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
5995 struct ieee80211_supported_band *band;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005996 struct brcmf_fil_bwcap_le band_bwcap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02005997 struct brcmf_chanspec_list *list;
5998 u8 *pbuf;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02005999 u32 val;
6000 int err;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006001 struct brcmu_chan ch;
6002 u32 num_chan;
6003 int i, j;
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006004
6005 /* verify support for bw_cap command */
6006 val = WLC_BAND_5G;
6007 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val);
6008
6009 if (!err) {
6010 /* only set 2G bandwidth using bw_cap command */
6011 band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
6012 band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
6013 err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
6014 sizeof(band_bwcap));
6015 } else {
6016 brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
6017 val = WLC_N_BW_40ALL;
6018 err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
6019 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006020
6021 if (!err) {
6022 /* update channel info in 2G band */
6023 pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
6024
6025 if (pbuf == NULL)
6026 return -ENOMEM;
6027
6028 ch.band = BRCMU_CHAN_BAND_2G;
6029 ch.bw = BRCMU_CHAN_BW_40;
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02006030 ch.sb = BRCMU_CHAN_SB_NONE;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006031 ch.chnum = 0;
6032 cfg->d11inf.encchspec(&ch);
6033
6034 /* pass encoded chanspec in query */
6035 *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
6036
6037 err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
6038 BRCMF_DCMD_MEDLEN);
6039 if (err) {
6040 brcmf_err("get chanspecs error (%d)\n", err);
6041 kfree(pbuf);
6042 return err;
6043 }
6044
Johannes Berg57fbcce2016-04-12 15:56:15 +02006045 band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
Arend van Sprielb48d8912014-07-12 08:49:41 +02006046 list = (struct brcmf_chanspec_list *)pbuf;
6047 num_chan = le32_to_cpu(list->count);
6048 for (i = 0; i < num_chan; i++) {
6049 ch.chspec = (u16)le32_to_cpu(list->element[i]);
6050 cfg->d11inf.decchspec(&ch);
6051 if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
6052 continue;
6053 if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
6054 continue;
6055 for (j = 0; j < band->n_channels; j++) {
Rafał Miłecki4712d882016-05-20 13:38:57 +02006056 if (band->channels[j].hw_value == ch.control_ch_num)
Arend van Sprielb48d8912014-07-12 08:49:41 +02006057 break;
6058 }
6059 if (WARN_ON(j == band->n_channels))
6060 continue;
6061
6062 brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
6063 }
Hante Meulemanfac7d2a2014-09-11 22:51:30 +02006064 kfree(pbuf);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006065 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006066 return err;
6067}
6068
Arend van Spriel2375d972014-01-06 12:40:41 +01006069static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
6070{
6071 u32 band, mimo_bwcap;
6072 int err;
6073
6074 band = WLC_BAND_2G;
6075 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
6076 if (!err) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02006077 bw_cap[NL80211_BAND_2GHZ] = band;
Arend van Spriel2375d972014-01-06 12:40:41 +01006078 band = WLC_BAND_5G;
6079 err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band);
6080 if (!err) {
Johannes Berg57fbcce2016-04-12 15:56:15 +02006081 bw_cap[NL80211_BAND_5GHZ] = band;
Arend van Spriel2375d972014-01-06 12:40:41 +01006082 return;
6083 }
6084 WARN_ON(1);
6085 return;
6086 }
6087 brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
6088 mimo_bwcap = 0;
6089 err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
6090 if (err)
6091 /* assume 20MHz if firmware does not give a clue */
6092 mimo_bwcap = WLC_N_BW_20ALL;
6093
6094 switch (mimo_bwcap) {
6095 case WLC_N_BW_40ALL:
Johannes Berg57fbcce2016-04-12 15:56:15 +02006096 bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
Arend van Spriel2375d972014-01-06 12:40:41 +01006097 /* fall-thru */
6098 case WLC_N_BW_20IN2G_40IN5G:
Johannes Berg57fbcce2016-04-12 15:56:15 +02006099 bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
Arend van Spriel2375d972014-01-06 12:40:41 +01006100 /* fall-thru */
6101 case WLC_N_BW_20ALL:
Johannes Berg57fbcce2016-04-12 15:56:15 +02006102 bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
6103 bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
Arend van Spriel2375d972014-01-06 12:40:41 +01006104 break;
6105 default:
6106 brcmf_err("invalid mimo_bw_cap value\n");
6107 }
6108}
Hante Meulemand48200b2013-04-03 12:40:29 +02006109
Arend van Spriel18d6c532014-05-12 10:47:35 +02006110static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
6111 u32 bw_cap[2], u32 nchain)
6112{
6113 band->ht_cap.ht_supported = true;
6114 if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
6115 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
6116 band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6117 }
6118 band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
6119 band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
6120 band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
6121 band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
6122 memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
6123 band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
6124}
6125
6126static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
6127{
6128 u16 mcs_map;
6129 int i;
6130
6131 for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
6132 mcs_map = (mcs_map << 2) | supp;
6133
6134 return cpu_to_le16(mcs_map);
6135}
6136
6137static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006138 u32 bw_cap[2], u32 nchain, u32 txstreams,
6139 u32 txbf_bfe_cap, u32 txbf_bfr_cap)
Arend van Spriel18d6c532014-05-12 10:47:35 +02006140{
6141 __le16 mcs_map;
6142
6143 /* not allowed in 2.4G band */
Johannes Berg57fbcce2016-04-12 15:56:15 +02006144 if (band->band == NL80211_BAND_2GHZ)
Arend van Spriel18d6c532014-05-12 10:47:35 +02006145 return;
6146
6147 band->vht_cap.vht_supported = true;
6148 /* 80MHz is mandatory */
6149 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
6150 if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
6151 band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
6152 band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
6153 }
6154 /* all support 256-QAM */
6155 mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
6156 band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
6157 band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006158
6159 /* Beamforming support information */
6160 if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
6161 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
6162 if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
6163 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
6164 if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
6165 band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
6166 if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
6167 band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
6168
6169 if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
6170 band->vht_cap.cap |=
6171 (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
6172 band->vht_cap.cap |= ((txstreams - 1) <<
6173 IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
6174 band->vht_cap.cap |=
6175 IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
6176 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02006177}
6178
Arend van Sprielb48d8912014-07-12 08:49:41 +02006179static int brcmf_setup_wiphybands(struct wiphy *wiphy)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006180{
Arend van Sprielb48d8912014-07-12 08:49:41 +02006181 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
Arend van Sprielac24be62012-10-22 10:36:23 -07006182 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
Arend van Spriel18d6c532014-05-12 10:47:35 +02006183 u32 nmode = 0;
6184 u32 vhtmode = 0;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006185 u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
Daniel Kim4aca7a12014-02-25 20:30:36 +01006186 u32 rxchain;
6187 u32 nchain;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006188 int err;
Hante Meulemand48200b2013-04-03 12:40:29 +02006189 s32 i;
Arend van Spriel2375d972014-01-06 12:40:41 +01006190 struct ieee80211_supported_band *band;
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006191 u32 txstreams = 0;
6192 u32 txbf_bfe_cap = 0;
6193 u32 txbf_bfr_cap = 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006194
Arend van Spriel18d6c532014-05-12 10:47:35 +02006195 (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
Hante Meulemand48200b2013-04-03 12:40:29 +02006196 err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
6197 if (err) {
6198 brcmf_err("nmode error (%d)\n", err);
6199 } else {
Arend van Spriel2375d972014-01-06 12:40:41 +01006200 brcmf_get_bwcap(ifp, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02006201 }
Arend van Spriel18d6c532014-05-12 10:47:35 +02006202 brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
Johannes Berg57fbcce2016-04-12 15:56:15 +02006203 nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
6204 bw_cap[NL80211_BAND_5GHZ]);
Hante Meulemand48200b2013-04-03 12:40:29 +02006205
Daniel Kim4aca7a12014-02-25 20:30:36 +01006206 err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
6207 if (err) {
6208 brcmf_err("rxchain error (%d)\n", err);
6209 nchain = 1;
6210 } else {
6211 for (nchain = 0; rxchain; nchain++)
6212 rxchain = rxchain & (rxchain - 1);
6213 }
6214 brcmf_dbg(INFO, "nchain=%d\n", nchain);
6215
Arend van Sprielb48d8912014-07-12 08:49:41 +02006216 err = brcmf_construct_chaninfo(cfg, bw_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02006217 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006218 brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err);
Hante Meulemand48200b2013-04-03 12:40:29 +02006219 return err;
6220 }
6221
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006222 if (vhtmode) {
6223 (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
6224 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
6225 &txbf_bfe_cap);
6226 (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
6227 &txbf_bfr_cap);
6228 }
6229
Arend van Sprielb48d8912014-07-12 08:49:41 +02006230 wiphy = cfg_to_wiphy(cfg);
6231 for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
6232 band = wiphy->bands[i];
6233 if (band == NULL)
Arend van Spriel2375d972014-01-06 12:40:41 +01006234 continue;
Hante Meulemand48200b2013-04-03 12:40:29 +02006235
Arend van Spriel18d6c532014-05-12 10:47:35 +02006236 if (nmode)
6237 brcmf_update_ht_cap(band, bw_cap, nchain);
6238 if (vhtmode)
Hante Meuleman7bf65aa2015-11-25 11:32:43 +01006239 brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
6240 txbf_bfe_cap, txbf_bfr_cap);
Hante Meulemand48200b2013-04-03 12:40:29 +02006241 }
6242
Arend van Sprielb48d8912014-07-12 08:49:41 +02006243 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006244}
6245
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006246static const struct ieee80211_txrx_stypes
6247brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
6248 [NL80211_IFTYPE_STATION] = {
6249 .tx = 0xffff,
6250 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
6251 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
6252 },
6253 [NL80211_IFTYPE_P2P_CLIENT] = {
6254 .tx = 0xffff,
6255 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
6256 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
6257 },
6258 [NL80211_IFTYPE_P2P_GO] = {
6259 .tx = 0xffff,
6260 .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
6261 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
6262 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
6263 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
6264 BIT(IEEE80211_STYPE_AUTH >> 4) |
6265 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
6266 BIT(IEEE80211_STYPE_ACTION >> 4)
6267 },
6268 [NL80211_IFTYPE_P2P_DEVICE] = {
6269 .tx = 0xffff,
6270 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
6271 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
6272 }
6273};
6274
Arend van Spriel0882dda2015-08-20 22:06:03 +02006275/**
6276 * brcmf_setup_ifmodes() - determine interface modes and combinations.
6277 *
6278 * @wiphy: wiphy object.
6279 * @ifp: interface object needed for feat module api.
6280 *
6281 * The interface modes and combinations are determined dynamically here
6282 * based on firmware functionality.
6283 *
6284 * no p2p and no mbss:
6285 *
6286 * #STA <= 1, #AP <= 1, channels = 1, 2 total
6287 *
6288 * no p2p and mbss:
6289 *
6290 * #STA <= 1, #AP <= 1, channels = 1, 2 total
6291 * #AP <= 4, matching BI, channels = 1, 4 total
6292 *
6293 * p2p, no mchan, and mbss:
6294 *
6295 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
6296 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
6297 * #AP <= 4, matching BI, channels = 1, 4 total
6298 *
6299 * p2p, mchan, and mbss:
6300 *
6301 * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
6302 * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
6303 * #AP <= 4, matching BI, channels = 1, 4 total
6304 */
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006305static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
6306{
6307 struct ieee80211_iface_combination *combo = NULL;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006308 struct ieee80211_iface_limit *c0_limits = NULL;
6309 struct ieee80211_iface_limit *p2p_limits = NULL;
6310 struct ieee80211_iface_limit *mbss_limits = NULL;
6311 bool mbss, p2p;
6312 int i, c, n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006313
Arend van Spriel0882dda2015-08-20 22:06:03 +02006314 mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
6315 p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
6316
6317 n_combos = 1 + !!p2p + !!mbss;
6318 combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006319 if (!combo)
6320 goto err;
6321
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006322 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
6323 BIT(NL80211_IFTYPE_ADHOC) |
6324 BIT(NL80211_IFTYPE_AP);
6325
Arend van Spriel0882dda2015-08-20 22:06:03 +02006326 c = 0;
6327 i = 0;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006328 c0_limits = kcalloc(p2p ? 3 : 2, sizeof(*c0_limits), GFP_KERNEL);
6329 if (!c0_limits)
6330 goto err;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006331 c0_limits[i].max = 1;
6332 c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
6333 if (p2p) {
6334 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN))
6335 combo[c].num_different_channels = 2;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006336 wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
6337 BIT(NL80211_IFTYPE_P2P_GO) |
6338 BIT(NL80211_IFTYPE_P2P_DEVICE);
Arend van Spriel0882dda2015-08-20 22:06:03 +02006339 c0_limits[i].max = 1;
6340 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
6341 c0_limits[i].max = 1;
6342 c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
6343 BIT(NL80211_IFTYPE_P2P_GO);
6344 } else {
6345 c0_limits[i].max = 1;
6346 c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006347 }
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006348 combo[c].num_different_channels = 1;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006349 combo[c].max_interfaces = i;
6350 combo[c].n_limits = i;
6351 combo[c].limits = c0_limits;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006352
Arend van Spriel0882dda2015-08-20 22:06:03 +02006353 if (p2p) {
6354 c++;
6355 i = 0;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006356 p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
6357 if (!p2p_limits)
6358 goto err;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006359 p2p_limits[i].max = 1;
6360 p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
6361 p2p_limits[i].max = 1;
6362 p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
6363 p2p_limits[i].max = 1;
6364 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
6365 p2p_limits[i].max = 1;
6366 p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006367 combo[c].num_different_channels = 1;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006368 combo[c].max_interfaces = i;
6369 combo[c].n_limits = i;
6370 combo[c].limits = p2p_limits;
6371 }
6372
6373 if (mbss) {
6374 c++;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006375 i = 0;
6376 mbss_limits = kcalloc(1, sizeof(*mbss_limits), GFP_KERNEL);
6377 if (!mbss_limits)
6378 goto err;
6379 mbss_limits[i].max = 4;
6380 mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);
Arend van Spriel0882dda2015-08-20 22:06:03 +02006381 combo[c].beacon_int_infra_match = true;
6382 combo[c].num_different_channels = 1;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006383 combo[c].max_interfaces = 4;
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006384 combo[c].n_limits = i;
Arend van Spriel0882dda2015-08-20 22:06:03 +02006385 combo[c].limits = mbss_limits;
6386 }
Rafał Miłeckif568ada2016-06-07 21:10:18 +02006387
Arend van Spriel0882dda2015-08-20 22:06:03 +02006388 wiphy->n_iface_combinations = n_combos;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006389 wiphy->iface_combinations = combo;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006390 return 0;
6391
6392err:
Arend van Spriel0882dda2015-08-20 22:06:03 +02006393 kfree(c0_limits);
6394 kfree(p2p_limits);
6395 kfree(mbss_limits);
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006396 kfree(combo);
6397 return -ENOMEM;
6398}
6399
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006400#ifdef CONFIG_PM
Arend Van Spriel0b570102017-01-27 12:27:47 +00006401static const struct wiphy_wowlan_support brcmf_wowlan_support = {
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006402 .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
Hante Meulemanb9a82f82014-10-28 14:56:06 +01006403 .n_patterns = BRCMF_WOWL_MAXPATTERNS,
6404 .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
6405 .pattern_min_len = 1,
6406 .max_pkt_offset = 1500,
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006407};
6408#endif
6409
Hante Meuleman3021ad92016-01-05 11:05:45 +01006410static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006411{
6412#ifdef CONFIG_PM
Hante Meuleman3021ad92016-01-05 11:05:45 +01006413 struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
Arend Van Spriel0b570102017-01-27 12:27:47 +00006414 struct wiphy_wowlan_support *wowl;
6415
6416 wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
6417 GFP_KERNEL);
6418 if (!wowl) {
6419 brcmf_err("only support basic wowlan features\n");
6420 wiphy->wowlan = &brcmf_wowlan_support;
6421 return;
6422 }
Hante Meuleman3021ad92016-01-05 11:05:45 +01006423
6424 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006425 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
Arend Van Spriel0b570102017-01-27 12:27:47 +00006426 wowl->flags |= WIPHY_WOWLAN_NET_DETECT;
6427 wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006428 init_waitqueue_head(&cfg->wowl.nd_data_wait);
Hante Meuleman3021ad92016-01-05 11:05:45 +01006429 }
6430 }
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006431 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
Arend Van Spriel0b570102017-01-27 12:27:47 +00006432 wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
6433 wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006434 }
6435
Arend Van Spriel0b570102017-01-27 12:27:47 +00006436 wiphy->wowlan = wowl;
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006437#endif
6438}
6439
Arend van Sprielb48d8912014-07-12 08:49:41 +02006440static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006441{
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006442 struct brcmf_pub *drvr = ifp->drvr;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006443 const struct ieee80211_iface_combination *combo;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006444 struct ieee80211_supported_band *band;
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006445 u16 max_interfaces = 0;
Arend Van Spriel94ed6ff2017-04-21 13:05:05 +01006446 bool gscan;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006447 __le32 bandlist[3];
6448 u32 n_bands;
6449 int err, i;
6450
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006451 wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
6452 wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
Hante Meuleman6c404f32015-12-10 13:43:03 +01006453 wiphy->max_num_pmkids = BRCMF_MAXPMKID;
Pontus Fuchs2e5f66f2015-06-11 00:12:18 +02006454
6455 err = brcmf_setup_ifmodes(wiphy, ifp);
6456 if (err)
6457 return err;
6458
Rafał Miłecki50f32e22015-08-20 00:16:42 +02006459 for (i = 0, combo = wiphy->iface_combinations;
6460 i < wiphy->n_iface_combinations; i++, combo++) {
6461 max_interfaces = max(max_interfaces, combo->max_interfaces);
6462 }
6463
6464 for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
6465 i++) {
Rafa? Mi?eckie3faa862015-07-09 17:07:08 +02006466 u8 *addr = drvr->addresses[i].addr;
6467
6468 memcpy(addr, drvr->mac, ETH_ALEN);
6469 if (i) {
6470 addr[0] |= BIT(1);
6471 addr[ETH_ALEN - 1] ^= i;
6472 }
6473 }
6474 wiphy->addresses = drvr->addresses;
6475 wiphy->n_addresses = i;
6476
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006477 wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
Hante Meuleman240d61a2016-02-17 11:27:10 +01006478 wiphy->cipher_suites = brcmf_cipher_suites;
6479 wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
6480 if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
6481 wiphy->n_cipher_suites--;
Arend van Spriel7705ba62016-04-17 16:44:58 +02006482 wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |
6483 BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
6484 BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
6485
Arend Van Spriel0cc02362017-03-28 11:43:26 +01006486 wiphy->flags |= WIPHY_FLAG_NETNS_OK |
6487 WIPHY_FLAG_PS_ON_BY_DEFAULT |
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006488 WIPHY_FLAG_OFFCHAN_TX |
Hante Meulemana7b82d42015-12-10 13:43:04 +01006489 WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
6490 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
6491 wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
Hante Meuleman7d34b052016-01-02 09:41:41 +01006492 if (!ifp->drvr->settings->roamoff)
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006493 wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
6494 wiphy->mgmt_stypes = brcmf_txrx_stypes;
6495 wiphy->max_remain_on_channel_duration = 5000;
Arend Van Spriel94ed6ff2017-04-21 13:05:05 +01006496 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
6497 gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN);
6498 brcmf_pno_wiphy_params(wiphy, gscan);
6499 }
Arend van Sprielaa70b4f2014-07-12 08:49:40 +02006500 /* vendor commands/events support */
6501 wiphy->vendor_commands = brcmf_vendor_cmds;
6502 wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
6503
Hante Meuleman4eb3af72014-09-30 10:23:18 +02006504 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
Hante Meuleman3021ad92016-01-05 11:05:45 +01006505 brcmf_wiphy_wowl_params(wiphy, ifp);
Arend van Spriel58de92d2015-04-14 20:10:24 +02006506 err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
6507 sizeof(bandlist));
6508 if (err) {
6509 brcmf_err("could not obtain band info: err=%d\n", err);
6510 return err;
6511 }
6512 /* first entry in bandlist is number of bands */
6513 n_bands = le32_to_cpu(bandlist[0]);
6514 for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
6515 if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
6516 band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
6517 GFP_KERNEL);
6518 if (!band)
6519 return -ENOMEM;
6520
6521 band->channels = kmemdup(&__wl_2ghz_channels,
6522 sizeof(__wl_2ghz_channels),
6523 GFP_KERNEL);
6524 if (!band->channels) {
6525 kfree(band);
6526 return -ENOMEM;
6527 }
6528
6529 band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
Johannes Berg57fbcce2016-04-12 15:56:15 +02006530 wiphy->bands[NL80211_BAND_2GHZ] = band;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006531 }
6532 if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
6533 band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
6534 GFP_KERNEL);
6535 if (!band)
6536 return -ENOMEM;
6537
6538 band->channels = kmemdup(&__wl_5ghz_channels,
6539 sizeof(__wl_5ghz_channels),
6540 GFP_KERNEL);
6541 if (!band->channels) {
6542 kfree(band);
6543 return -ENOMEM;
6544 }
6545
6546 band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
Johannes Berg57fbcce2016-04-12 15:56:15 +02006547 wiphy->bands[NL80211_BAND_5GHZ] = band;
Arend van Spriel58de92d2015-04-14 20:10:24 +02006548 }
6549 }
Rafał Miłecki0f83ff62017-01-17 23:35:50 +01006550
6551 wiphy_read_of_freq_limits(wiphy);
6552
Rafał Miłeckiab990632017-01-07 21:36:05 +01006553 return 0;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006554}
6555
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006556static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006557{
6558 struct net_device *ndev;
6559 struct wireless_dev *wdev;
Hante Meuleman40a23292013-01-02 15:22:51 +01006560 struct brcmf_if *ifp;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006561 s32 power_mode;
6562 s32 err = 0;
6563
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006564 if (cfg->dongle_up)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006565 return err;
6566
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006567 ndev = cfg_to_ndev(cfg);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006568 wdev = ndev->ieee80211_ptr;
Hante Meuleman40a23292013-01-02 15:22:51 +01006569 ifp = netdev_priv(ndev);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006570
Hante Meuleman40a23292013-01-02 15:22:51 +01006571 /* make sure RF is ready for work */
6572 brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
6573
Hante Meuleman1678ba82015-12-10 13:43:00 +01006574 brcmf_dongle_scantime(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006575
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006576 power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
Hante Meuleman40a23292013-01-02 15:22:51 +01006577 err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006578 if (err)
6579 goto default_conf_out;
Arend van Spriel647c9ae2012-12-05 15:26:01 +01006580 brcmf_dbg(INFO, "power save set to %s\n",
6581 (power_mode ? "enabled" : "disabled"));
Arend van Spriel5b435de2011-10-05 13:19:03 +02006582
Hante Meuleman1119e232015-11-25 11:32:42 +01006583 err = brcmf_dongle_roam(ifp);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006584 if (err)
6585 goto default_conf_out;
Franky Lin5dd161f2012-10-10 11:13:10 -07006586 err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
Johannes Berg818a9862017-04-12 11:23:28 +02006587 NULL);
Hante Meuleman40a23292013-01-02 15:22:51 +01006588 if (err)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006589 goto default_conf_out;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006590
Franky Lin52f22fb2016-02-17 11:26:55 +01006591 brcmf_configure_arp_nd_offload(ifp, true);
Hante Meulemanb3657452013-05-27 21:09:53 +02006592
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006593 cfg->dongle_up = true;
Hante Meuleman40a23292013-01-02 15:22:51 +01006594default_conf_out:
Arend van Spriel5b435de2011-10-05 13:19:03 +02006595
6596 return err;
6597
6598}
6599
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006600static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006601{
Arend van Sprielc1179032012-10-22 13:55:33 -07006602 set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006603
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006604 return brcmf_config_dongle(ifp->drvr->config);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006605}
6606
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006607static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006608{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006609 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Sprielc1179032012-10-22 13:55:33 -07006610
Arend van Spriel5b435de2011-10-05 13:19:03 +02006611 /*
6612 * While going down, if associated with AP disassociate
6613 * from AP to save power
6614 */
Arend van Spriel903e0ee2012-11-28 21:44:11 +01006615 if (check_vif_up(ifp->vif)) {
Arend van Spriel9b7a0dd2015-01-25 20:31:33 +01006616 brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006617
6618 /* Make sure WPA_Supplicant receives all the event
6619 generated due to DISASSOC call to the fw to keep
6620 the state fw and WPA_Supplicant state consistent
6621 */
6622 brcmf_delay(500);
6623 }
6624
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006625 brcmf_abort_scanning(cfg);
Arend van Sprielc1179032012-10-22 13:55:33 -07006626 clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006627
Arend van Spriel5b435de2011-10-05 13:19:03 +02006628 return 0;
6629}
6630
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006631s32 brcmf_cfg80211_up(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006632{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006633 struct brcmf_if *ifp = netdev_priv(ndev);
6634 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006635 s32 err = 0;
6636
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006637 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006638 err = __brcmf_cfg80211_up(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006639 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006640
6641 return err;
6642}
6643
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006644s32 brcmf_cfg80211_down(struct net_device *ndev)
Arend van Spriel5b435de2011-10-05 13:19:03 +02006645{
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006646 struct brcmf_if *ifp = netdev_priv(ndev);
6647 struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
Arend van Spriel5b435de2011-10-05 13:19:03 +02006648 s32 err = 0;
6649
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006650 mutex_lock(&cfg->usr_sync);
Arend van Sprielbdf5ff52012-11-14 18:46:09 -08006651 err = __brcmf_cfg80211_down(ifp);
Arend van Spriel27a68fe2012-09-27 14:17:55 +02006652 mutex_unlock(&cfg->usr_sync);
Arend van Spriel5b435de2011-10-05 13:19:03 +02006653
6654 return err;
6655}
6656
Arend van Spriela7965fb2013-04-11 17:08:37 +02006657enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
6658{
6659 struct wireless_dev *wdev = &ifp->vif->wdev;
6660
6661 return wdev->iftype;
6662}
6663
Hante Meulemanbfe81972014-10-28 14:56:16 +01006664bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
6665 unsigned long state)
Arend van Spriel9f440b72013-02-08 15:53:36 +01006666{
6667 struct brcmf_cfg80211_vif *vif;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006668
6669 list_for_each_entry(vif, &cfg->vif_list, list) {
6670 if (test_bit(state, &vif->sme_state))
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006671 return true;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006672 }
Rasmus Villemoese843bb12014-06-22 20:50:40 +02006673 return false;
Arend van Spriel9f440b72013-02-08 15:53:36 +01006674}
Arend van Sprield3c0b632013-02-08 15:53:37 +01006675
6676static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
6677 u8 action)
6678{
6679 u8 evt_action;
6680
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09006681 spin_lock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01006682 evt_action = event->action;
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09006683 spin_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01006684 return evt_action == action;
6685}
6686
6687void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
6688 struct brcmf_cfg80211_vif *vif)
6689{
6690 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6691
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09006692 spin_lock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01006693 event->vif = vif;
6694 event->action = 0;
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09006695 spin_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01006696}
6697
6698bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
6699{
6700 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6701 bool armed;
6702
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09006703 spin_lock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01006704 armed = event->vif != NULL;
mhiramat@kernel.orgb64abcb2016-08-15 18:41:12 +09006705 spin_unlock(&event->vif_event_lock);
Arend van Sprield3c0b632013-02-08 15:53:37 +01006706
6707 return armed;
6708}
Arend van Spriela9eb0c42016-02-17 11:26:50 +01006709
6710int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
6711 u8 action, ulong timeout)
Arend van Sprield3c0b632013-02-08 15:53:37 +01006712{
6713 struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
6714
6715 return wait_event_timeout(event->vif_wq,
6716 vif_event_equals(event, action), timeout);
6717}
6718
Hante Meuleman73345fd2016-02-17 11:26:53 +01006719static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
6720 struct brcmf_fil_country_le *ccreq)
6721{
Hante Meuleman4d792892016-02-17 11:27:07 +01006722 struct brcmfmac_pd_cc *country_codes;
6723 struct brcmfmac_pd_cc_entry *cc;
Hante Meuleman73345fd2016-02-17 11:26:53 +01006724 s32 found_index;
6725 int i;
6726
6727 country_codes = drvr->settings->country_codes;
6728 if (!country_codes) {
6729 brcmf_dbg(TRACE, "No country codes configured for device\n");
6730 return -EINVAL;
6731 }
6732
6733 if ((alpha2[0] == ccreq->country_abbrev[0]) &&
6734 (alpha2[1] == ccreq->country_abbrev[1])) {
6735 brcmf_dbg(TRACE, "Country code already set\n");
6736 return -EAGAIN;
6737 }
6738
6739 found_index = -1;
6740 for (i = 0; i < country_codes->table_size; i++) {
6741 cc = &country_codes->table[i];
6742 if ((cc->iso3166[0] == '\0') && (found_index == -1))
6743 found_index = i;
6744 if ((cc->iso3166[0] == alpha2[0]) &&
6745 (cc->iso3166[1] == alpha2[1])) {
6746 found_index = i;
6747 break;
6748 }
6749 }
6750 if (found_index == -1) {
6751 brcmf_dbg(TRACE, "No country code match found\n");
6752 return -EINVAL;
6753 }
6754 memset(ccreq, 0, sizeof(*ccreq));
6755 ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
6756 memcpy(ccreq->ccode, country_codes->table[found_index].cc,
6757 BRCMF_COUNTRY_BUF_SZ);
6758 ccreq->country_abbrev[0] = alpha2[0];
6759 ccreq->country_abbrev[1] = alpha2[1];
6760 ccreq->country_abbrev[2] = 0;
6761
6762 return 0;
6763}
6764
Arend van Spriel63db1a42014-12-21 12:43:51 +01006765static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
6766 struct regulatory_request *req)
6767{
6768 struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy);
6769 struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
6770 struct brcmf_fil_country_le ccreq;
Hante Meuleman73345fd2016-02-17 11:26:53 +01006771 s32 err;
Arend van Spriel63db1a42014-12-21 12:43:51 +01006772 int i;
6773
Hans de Goede26e537882017-03-08 14:50:16 +01006774 /* The country code gets set to "00" by default at boot, ignore */
6775 if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
6776 return;
6777
Arend van Spriel63db1a42014-12-21 12:43:51 +01006778 /* ignore non-ISO3166 country codes */
6779 for (i = 0; i < sizeof(req->alpha2); i++)
6780 if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
Martin Michlmayra9507d52017-06-10 11:40:04 +02006781 brcmf_err("not an ISO3166 code (0x%02x 0x%02x)\n",
Hante Meuleman73345fd2016-02-17 11:26:53 +01006782 req->alpha2[0], req->alpha2[1]);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006783 return;
6784 }
Hante Meuleman73345fd2016-02-17 11:26:53 +01006785
6786 brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
6787 req->alpha2[0], req->alpha2[1]);
6788
6789 err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
6790 if (err) {
6791 brcmf_err("Country code iovar returned err = %d\n", err);
6792 return;
6793 }
6794
6795 err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
6796 if (err)
6797 return;
6798
6799 err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
6800 if (err) {
6801 brcmf_err("Firmware rejected country setting\n");
Arend van Spriel8afe0ec2015-04-14 20:10:25 +02006802 return;
6803 }
6804 brcmf_setup_wiphybands(wiphy);
Arend van Spriel63db1a42014-12-21 12:43:51 +01006805}
6806
Arend van Sprielb48d8912014-07-12 08:49:41 +02006807static void brcmf_free_wiphy(struct wiphy *wiphy)
6808{
Arend van Spriel0882dda2015-08-20 22:06:03 +02006809 int i;
6810
Arend van Spriel58de92d2015-04-14 20:10:24 +02006811 if (!wiphy)
6812 return;
6813
Arend van Spriel0882dda2015-08-20 22:06:03 +02006814 if (wiphy->iface_combinations) {
6815 for (i = 0; i < wiphy->n_iface_combinations; i++)
6816 kfree(wiphy->iface_combinations[i].limits);
6817 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006818 kfree(wiphy->iface_combinations);
Johannes Berg57fbcce2016-04-12 15:56:15 +02006819 if (wiphy->bands[NL80211_BAND_2GHZ]) {
6820 kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
6821 kfree(wiphy->bands[NL80211_BAND_2GHZ]);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006822 }
Johannes Berg57fbcce2016-04-12 15:56:15 +02006823 if (wiphy->bands[NL80211_BAND_5GHZ]) {
6824 kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
6825 kfree(wiphy->bands[NL80211_BAND_5GHZ]);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006826 }
Arend Van Spriel0b570102017-01-27 12:27:47 +00006827#if IS_ENABLED(CONFIG_PM)
6828 if (wiphy->wowlan != &brcmf_wowlan_support)
6829 kfree(wiphy->wowlan);
6830#endif
Arend van Sprielb48d8912014-07-12 08:49:41 +02006831 wiphy_free(wiphy);
6832}
6833
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006834struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006835 struct device *busdev,
6836 bool p2pdev_forced)
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006837{
Arend van Spriel46f3b6e2015-08-26 22:14:58 +02006838 struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006839 struct brcmf_cfg80211_info *cfg;
6840 struct wiphy *wiphy;
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006841 struct cfg80211_ops *ops;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006842 struct brcmf_cfg80211_vif *vif;
6843 struct brcmf_if *ifp;
6844 s32 err = 0;
6845 s32 io_type;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006846 u16 *cap = NULL;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006847
6848 if (!ndev) {
6849 brcmf_err("ndev is invalid\n");
6850 return NULL;
6851 }
6852
Muhammad Falak R Wanid464fd82016-05-19 19:29:03 +05306853 ops = kmemdup(&brcmf_cfg80211_ops, sizeof(*ops), GFP_KERNEL);
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006854 if (!ops)
6855 return NULL;
6856
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006857 ifp = netdev_priv(ndev);
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006858#ifdef CONFIG_PM
6859 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
6860 ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
6861#endif
6862 wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info));
Arend van Sprielb48d8912014-07-12 08:49:41 +02006863 if (!wiphy) {
6864 brcmf_err("Could not allocate wiphy device\n");
Christophe Jaillet57c00f22017-06-21 07:45:53 +02006865 goto ops_out;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006866 }
Rafał Miłecki6896f4f2015-05-31 02:52:26 +02006867 memcpy(wiphy->perm_addr, drvr->mac, ETH_ALEN);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006868 set_wiphy_dev(wiphy, busdev);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006869
6870 cfg = wiphy_priv(wiphy);
6871 cfg->wiphy = wiphy;
Hante Meuleman5c22fb82016-02-17 11:27:03 +01006872 cfg->ops = ops;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006873 cfg->pub = drvr;
6874 init_vif_event(&cfg->vif_event);
6875 INIT_LIST_HEAD(&cfg->vif_list);
6876
Rafał Miłecki26072332016-06-06 23:03:55 +02006877 vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006878 if (IS_ERR(vif))
6879 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006880
6881 vif->ifp = ifp;
6882 vif->wdev.netdev = ndev;
6883 ndev->ieee80211_ptr = &vif->wdev;
6884 SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
6885
6886 err = wl_init_priv(cfg);
6887 if (err) {
6888 brcmf_err("Failed to init iwm_priv (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006889 brcmf_free_vif(vif);
6890 goto wiphy_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006891 }
6892 ifp->vif = vif;
6893
Arend van Sprielb48d8912014-07-12 08:49:41 +02006894 /* determine d11 io type before wiphy setup */
6895 err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006896 if (err) {
Arend van Sprielb48d8912014-07-12 08:49:41 +02006897 brcmf_err("Failed to get D11 version (%d)\n", err);
6898 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006899 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006900 cfg->d11inf.io_type = (u8)io_type;
6901 brcmu_d11_attach(&cfg->d11inf);
6902
6903 err = brcmf_setup_wiphy(wiphy, ifp);
6904 if (err < 0)
6905 goto priv_out;
6906
6907 brcmf_dbg(INFO, "Registering custom regulatory\n");
Arend van Spriel63db1a42014-12-21 12:43:51 +01006908 wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006909 wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
6910 wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
6911
6912 /* firmware defaults to 40MHz disabled in 2G band. We signal
6913 * cfg80211 here that we do and have it decide we can enable
6914 * it. But first check if device does support 2G operation.
6915 */
Johannes Berg57fbcce2016-04-12 15:56:15 +02006916 if (wiphy->bands[NL80211_BAND_2GHZ]) {
6917 cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
Arend van Sprielb48d8912014-07-12 08:49:41 +02006918 *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
6919 }
6920 err = wiphy_register(wiphy);
6921 if (err < 0) {
6922 brcmf_err("Could not register wiphy device (%d)\n", err);
6923 goto priv_out;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006924 }
6925
Rafał Miłeckiab990632017-01-07 21:36:05 +01006926 err = brcmf_setup_wiphybands(wiphy);
6927 if (err) {
6928 brcmf_err("Setting wiphy bands failed (%d)\n", err);
6929 goto wiphy_unreg_out;
6930 }
6931
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006932 /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
6933 * setup 40MHz in 2GHz band and enable OBSS scanning.
6934 */
Arend van Sprielb48d8912014-07-12 08:49:41 +02006935 if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
6936 err = brcmf_enable_bw40_2g(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006937 if (!err)
6938 err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
6939 BRCMF_OBSS_COEX_AUTO);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006940 else
6941 *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006942 }
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006943 /* p2p might require that "if-events" get processed by fweh. So
6944 * activate the already registered event handlers now and activate
6945 * the rest when initialization has completed. drvr->config needs to
6946 * be assigned before activating events.
6947 */
6948 drvr->config = cfg;
6949 err = brcmf_fweh_activate_events(ifp);
6950 if (err) {
6951 brcmf_err("FWEH activation failed (%d)\n", err);
6952 goto wiphy_unreg_out;
6953 }
Arend van Sprielb48d8912014-07-12 08:49:41 +02006954
Hante Meulemanae7c03f2015-09-18 22:08:08 +02006955 err = brcmf_p2p_attach(cfg, p2pdev_forced);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006956 if (err) {
Arend Van Sprielcb853da2016-12-09 11:34:13 +00006957 brcmf_err("P2P initialisation failed (%d)\n", err);
Arend van Sprielb48d8912014-07-12 08:49:41 +02006958 goto wiphy_unreg_out;
6959 }
6960 err = brcmf_btcoex_attach(cfg);
6961 if (err) {
6962 brcmf_err("BT-coex initialisation failed (%d)\n", err);
6963 brcmf_p2p_detach(&cfg->p2p);
6964 goto wiphy_unreg_out;
6965 }
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01006966 err = brcmf_pno_attach(cfg);
6967 if (err) {
6968 brcmf_err("PNO initialisation failed (%d)\n", err);
6969 brcmf_btcoex_detach(cfg);
6970 brcmf_p2p_detach(&cfg->p2p);
6971 goto wiphy_unreg_out;
6972 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006973
Hante Meulemana7b82d42015-12-10 13:43:04 +01006974 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
6975 err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
6976 if (err) {
6977 brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
6978 wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
6979 } else {
6980 brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
6981 brcmf_notify_tdls_peer_event);
6982 }
Arend van Sprielccfd1e82014-07-12 08:49:38 +02006983 }
6984
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006985 /* (re-) activate FWEH event handling */
6986 err = brcmf_fweh_activate_events(ifp);
6987 if (err) {
6988 brcmf_err("FWEH activation failed (%d)\n", err);
Arend Van Sprielcb853da2016-12-09 11:34:13 +00006989 goto detach;
Hante Meuleman2b76acd2015-10-08 20:33:15 +02006990 }
6991
Hante Meuleman48ed16e2016-01-02 09:41:38 +01006992 /* Fill in some of the advertised nl80211 supported features */
6993 if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
6994 wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
6995#ifdef CONFIG_PM
Franky Lin6ea09152016-02-17 11:26:52 +01006996 if (wiphy->wowlan &&
6997 wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
Hante Meuleman48ed16e2016-01-02 09:41:38 +01006998 wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
6999#endif
7000 }
7001
Arend van Sprielccfd1e82014-07-12 08:49:38 +02007002 return cfg;
7003
Arend Van Sprielcb853da2016-12-09 11:34:13 +00007004detach:
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01007005 brcmf_pno_detach(cfg);
Arend Van Sprielcb853da2016-12-09 11:34:13 +00007006 brcmf_btcoex_detach(cfg);
7007 brcmf_p2p_detach(&cfg->p2p);
Arend van Sprielb48d8912014-07-12 08:49:41 +02007008wiphy_unreg_out:
7009 wiphy_unregister(cfg->wiphy);
7010priv_out:
Arend van Sprielccfd1e82014-07-12 08:49:38 +02007011 wl_deinit_priv(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02007012 brcmf_free_vif(vif);
Hante Meuleman2b5d3482015-09-18 22:08:04 +02007013 ifp->vif = NULL;
Arend van Sprielb48d8912014-07-12 08:49:41 +02007014wiphy_out:
7015 brcmf_free_wiphy(wiphy);
Christophe Jaillet57c00f22017-06-21 07:45:53 +02007016ops_out:
Hante Meuleman5c22fb82016-02-17 11:27:03 +01007017 kfree(ops);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02007018 return NULL;
7019}
7020
7021void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
7022{
7023 if (!cfg)
7024 return;
7025
Arend Van Sprielefc2c1f2017-06-09 12:19:20 +01007026 brcmf_pno_detach(cfg);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02007027 brcmf_btcoex_detach(cfg);
Arend van Sprielf7a40872015-06-11 00:12:23 +02007028 wiphy_unregister(cfg->wiphy);
Hante Meuleman5c22fb82016-02-17 11:27:03 +01007029 kfree(cfg->ops);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02007030 wl_deinit_priv(cfg);
Arend van Sprielb48d8912014-07-12 08:49:41 +02007031 brcmf_free_wiphy(cfg->wiphy);
Arend van Sprielccfd1e82014-07-12 08:49:38 +02007032}