blob: 17fc152e33d96a2373ca83ea8ae5437082cef584 [file] [log] [blame]
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001/*
Dedy Lansky849a5642017-01-20 13:49:44 +02002 * Copyright (c) 2012-2017 Qualcomm Atheros, Inc.
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08003 *
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
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Vladimir Kondratieva82553bb2015-03-15 16:00:21 +020017#include <linux/etherdevice.h>
Johannes Berg949c2d02017-02-07 15:33:28 +010018#include <linux/moduleparam.h>
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080019#include "wil6210.h"
20#include "wmi.h"
21
Dedy Lanskye6d68342016-03-01 19:18:12 +020022#define WIL_MAX_ROC_DURATION_MS 5000
23
Dedy Lansky849a5642017-01-20 13:49:44 +020024bool disable_ap_sme;
Maya Erez78484c42017-01-20 13:49:53 +020025module_param(disable_ap_sme, bool, 0444);
Dedy Lansky849a5642017-01-20 13:49:44 +020026MODULE_PARM_DESC(disable_ap_sme, " let user space handle AP mode SME");
27
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080028#define CHAN60G(_channel, _flags) { \
Johannes Berg57fbcce2016-04-12 15:56:15 +020029 .band = NL80211_BAND_60GHZ, \
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080030 .center_freq = 56160 + (2160 * (_channel)), \
31 .hw_value = (_channel), \
32 .flags = (_flags), \
33 .max_antenna_gain = 0, \
34 .max_power = 40, \
35}
36
37static struct ieee80211_channel wil_60ghz_channels[] = {
38 CHAN60G(1, 0),
39 CHAN60G(2, 0),
40 CHAN60G(3, 0),
41/* channel 4 not supported yet */
42};
43
44static struct ieee80211_supported_band wil_band_60ghz = {
45 .channels = wil_60ghz_channels,
46 .n_channels = ARRAY_SIZE(wil_60ghz_channels),
47 .ht_cap = {
48 .ht_supported = true,
49 .cap = 0, /* TODO */
50 .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */
51 .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */
52 .mcs = {
53 /* MCS 1..12 - SC PHY */
54 .rx_mask = {0xfe, 0x1f}, /* 1..12 */
55 .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */
56 },
57 },
58};
59
60static const struct ieee80211_txrx_stypes
61wil_mgmt_stypes[NUM_NL80211_IFTYPES] = {
62 [NL80211_IFTYPE_STATION] = {
63 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
64 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
65 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
66 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
67 },
68 [NL80211_IFTYPE_AP] = {
69 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
Dedy Lansky849a5642017-01-20 13:49:44 +020070 BIT(IEEE80211_STYPE_PROBE_RESP >> 4) |
71 BIT(IEEE80211_STYPE_ASSOC_RESP >> 4) |
72 BIT(IEEE80211_STYPE_DISASSOC >> 4),
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080073 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
Dedy Lansky849a5642017-01-20 13:49:44 +020074 BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
75 BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
76 BIT(IEEE80211_STYPE_DISASSOC >> 4) |
77 BIT(IEEE80211_STYPE_AUTH >> 4) |
78 BIT(IEEE80211_STYPE_DEAUTH >> 4) |
79 BIT(IEEE80211_STYPE_REASSOC_REQ >> 4)
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080080 },
81 [NL80211_IFTYPE_P2P_CLIENT] = {
82 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
83 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
84 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
85 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
86 },
87 [NL80211_IFTYPE_P2P_GO] = {
88 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
89 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
90 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
91 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
92 },
Lior David4332cac2016-03-01 19:18:13 +020093 [NL80211_IFTYPE_P2P_DEVICE] = {
94 .tx = BIT(IEEE80211_STYPE_ACTION >> 4) |
95 BIT(IEEE80211_STYPE_PROBE_RESP >> 4),
96 .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
97 BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
98 },
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -080099};
100
101static const u32 wil_cipher_suites[] = {
102 WLAN_CIPHER_SUITE_GCMP,
103};
104
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200105static const char * const key_usage_str[] = {
106 [WMI_KEY_USE_PAIRWISE] = "PTK",
107 [WMI_KEY_USE_RX_GROUP] = "RX_GTK",
108 [WMI_KEY_USE_TX_GROUP] = "TX_GTK",
109};
110
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800111int wil_iftype_nl2wmi(enum nl80211_iftype type)
112{
113 static const struct {
114 enum nl80211_iftype nl;
115 enum wmi_network_type wmi;
116 } __nl2wmi[] = {
117 {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC},
118 {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA},
119 {NL80211_IFTYPE_AP, WMI_NETTYPE_AP},
120 {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P},
121 {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P},
122 {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */
123 };
124 uint i;
125
126 for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) {
127 if (__nl2wmi[i].nl == type)
128 return __nl2wmi[i].wmi;
129 }
130
131 return -EOPNOTSUPP;
132}
133
Vladimir Kondratiev9eb82d42014-06-16 19:37:13 +0300134int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid,
135 struct station_info *sinfo)
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800136{
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800137 struct wmi_notify_req_cmd cmd = {
Vladimir Kondratiev3df2cd362014-02-27 16:20:43 +0200138 .cid = cid,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800139 .interval_usec = 0,
140 };
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200141 struct {
Lior Davidb874dde2016-03-01 19:18:09 +0200142 struct wmi_cmd_hdr wmi;
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200143 struct wmi_notify_req_done_event evt;
144 } __packed reply;
Vladimir Kondratievc8b78b52014-02-27 16:20:49 +0200145 struct wil_net_stats *stats = &wil->sta[cid].stats;
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200146 int rc;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800147
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800148 rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd),
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200149 WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800150 if (rc)
151 return rc;
152
Vladimir Kondratievc8b78b52014-02-27 16:20:49 +0200153 wil_dbg_wmi(wil, "Link status for CID %d: {\n"
154 " MCS %d TSF 0x%016llx\n"
Vladimir Kondratievb8b33a32014-02-27 16:20:52 +0200155 " BF status 0x%08x SNR 0x%08x SQI %d%%\n"
Vladimir Kondratievc8b78b52014-02-27 16:20:49 +0200156 " Tx Tpt %d goodput %d Rx goodput %d\n"
157 " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n",
158 cid, le16_to_cpu(reply.evt.bf_mcs),
159 le64_to_cpu(reply.evt.tsf), reply.evt.status,
160 le32_to_cpu(reply.evt.snr_val),
Vladimir Kondratievb8b33a32014-02-27 16:20:52 +0200161 reply.evt.sqi,
Vladimir Kondratievc8b78b52014-02-27 16:20:49 +0200162 le32_to_cpu(reply.evt.tx_tpt),
163 le32_to_cpu(reply.evt.tx_goodput),
164 le32_to_cpu(reply.evt.rx_goodput),
165 le16_to_cpu(reply.evt.my_rx_sector),
166 le16_to_cpu(reply.evt.my_tx_sector),
167 le16_to_cpu(reply.evt.other_rx_sector),
168 le16_to_cpu(reply.evt.other_tx_sector));
169
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800170 sinfo->generation = wil->sinfo_gen;
171
Johannes Berg319090b2014-11-17 14:08:11 +0100172 sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) |
173 BIT(NL80211_STA_INFO_TX_BYTES) |
174 BIT(NL80211_STA_INFO_RX_PACKETS) |
175 BIT(NL80211_STA_INFO_TX_PACKETS) |
176 BIT(NL80211_STA_INFO_RX_BITRATE) |
177 BIT(NL80211_STA_INFO_TX_BITRATE) |
178 BIT(NL80211_STA_INFO_RX_DROP_MISC) |
179 BIT(NL80211_STA_INFO_TX_FAILED);
Vladimir Kondratievc8b78b52014-02-27 16:20:49 +0200180
Johannes Berg0c1eca42017-02-15 15:02:08 +0100181 sinfo->txrate.flags = RATE_INFO_FLAGS_60G;
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200182 sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs);
Vladimir Kondratievc8b78b52014-02-27 16:20:49 +0200183 sinfo->rxrate.mcs = stats->last_mcs_rx;
184 sinfo->rx_bytes = stats->rx_bytes;
185 sinfo->rx_packets = stats->rx_packets;
186 sinfo->rx_dropped_misc = stats->rx_dropped;
187 sinfo->tx_bytes = stats->tx_bytes;
188 sinfo->tx_packets = stats->tx_packets;
189 sinfo->tx_failed = stats->tx_errors;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800190
Vladimir Kondratiev9419b6a2014-12-23 09:47:14 +0200191 if (test_bit(wil_status_fwconnected, wil->status)) {
Johannes Berg319090b2014-11-17 14:08:11 +0100192 sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL);
Vladimir Kondratievb8b33a32014-02-27 16:20:52 +0200193 sinfo->signal = reply.evt.sqi;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800194 }
195
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200196 return rc;
197}
198
199static int wil_cfg80211_get_station(struct wiphy *wiphy,
200 struct net_device *ndev,
Johannes Berg3b3a0162014-05-19 17:19:31 +0200201 const u8 *mac, struct station_info *sinfo)
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200202{
203 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
204 int rc;
205
206 int cid = wil_find_cid(wil, mac);
207
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200208 wil_dbg_misc(wil, "get_station: %pM CID %d\n", mac, cid);
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200209 if (cid < 0)
Vladimir Kondratievc14c5d92014-03-02 11:20:51 +0200210 return cid;
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200211
212 rc = wil_cid_fill_sinfo(wil, cid, sinfo);
213
214 return rc;
215}
216
217/*
218 * Find @idx-th active STA for station dump.
219 */
220static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx)
221{
222 int i;
223
224 for (i = 0; i < ARRAY_SIZE(wil->sta); i++) {
225 if (wil->sta[i].status == wil_sta_unused)
226 continue;
227 if (idx == 0)
228 return i;
229 idx--;
230 }
231
232 return -ENOENT;
233}
234
235static int wil_cfg80211_dump_station(struct wiphy *wiphy,
236 struct net_device *dev, int idx,
237 u8 *mac, struct station_info *sinfo)
238{
239 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
240 int rc;
241 int cid = wil_find_cid_by_idx(wil, idx);
242
243 if (cid < 0)
244 return -ENOENT;
245
Vladimir Kondratieva82553bb2015-03-15 16:00:21 +0200246 ether_addr_copy(mac, wil->sta[cid].addr);
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200247 wil_dbg_misc(wil, "dump_station: %pM CID %d\n", mac, cid);
Vladimir Kondratievef28afd2014-02-27 16:20:48 +0200248
249 rc = wil_cid_fill_sinfo(wil, cid, sinfo);
250
251 return rc;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800252}
253
Lior David4332cac2016-03-01 19:18:13 +0200254static struct wireless_dev *
255wil_cfg80211_add_iface(struct wiphy *wiphy, const char *name,
256 unsigned char name_assign_type,
257 enum nl80211_iftype type,
Johannes Berg818a9862017-04-12 11:23:28 +0200258 struct vif_params *params)
Lior David4332cac2016-03-01 19:18:13 +0200259{
260 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
261 struct net_device *ndev = wil_to_ndev(wil);
262 struct wireless_dev *p2p_wdev;
263
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200264 wil_dbg_misc(wil, "add_iface\n");
Lior David4332cac2016-03-01 19:18:13 +0200265
266 if (type != NL80211_IFTYPE_P2P_DEVICE) {
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200267 wil_err(wil, "unsupported iftype %d\n", type);
Lior David4332cac2016-03-01 19:18:13 +0200268 return ERR_PTR(-EINVAL);
269 }
270
271 if (wil->p2p_wdev) {
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200272 wil_err(wil, "P2P_DEVICE interface already created\n");
Lior David4332cac2016-03-01 19:18:13 +0200273 return ERR_PTR(-EINVAL);
274 }
275
276 p2p_wdev = kzalloc(sizeof(*p2p_wdev), GFP_KERNEL);
277 if (!p2p_wdev)
278 return ERR_PTR(-ENOMEM);
279
280 p2p_wdev->iftype = type;
281 p2p_wdev->wiphy = wiphy;
282 /* use our primary ethernet address */
283 ether_addr_copy(p2p_wdev->address, ndev->perm_addr);
284
285 wil->p2p_wdev = p2p_wdev;
286
287 return p2p_wdev;
288}
289
290static int wil_cfg80211_del_iface(struct wiphy *wiphy,
291 struct wireless_dev *wdev)
292{
293 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
294
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200295 wil_dbg_misc(wil, "del_iface\n");
Lior David4332cac2016-03-01 19:18:13 +0200296
297 if (wdev != wil->p2p_wdev) {
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200298 wil_err(wil, "delete of incorrect interface 0x%p\n", wdev);
Lior David4332cac2016-03-01 19:18:13 +0200299 return -EINVAL;
300 }
301
302 wil_p2p_wdev_free(wil);
303
304 return 0;
305}
306
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800307static int wil_cfg80211_change_iface(struct wiphy *wiphy,
308 struct net_device *ndev,
Johannes Berg818a9862017-04-12 11:23:28 +0200309 enum nl80211_iftype type,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800310 struct vif_params *params)
311{
312 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Lior David4332cac2016-03-01 19:18:13 +0200313 struct wireless_dev *wdev = wil_to_wdev(wil);
Dedy Lanskye6d68342016-03-01 19:18:12 +0200314 int rc;
315
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200316 wil_dbg_misc(wil, "change_iface: type=%d\n", type);
Dedy Lanskye6d68342016-03-01 19:18:12 +0200317
Lior David375a1732016-03-01 19:18:16 +0200318 if (netif_running(wil_to_ndev(wil)) && !wil_is_recovery_blocked(wil)) {
Dedy Lanskye6d68342016-03-01 19:18:12 +0200319 wil_dbg_misc(wil, "interface is up. resetting...\n");
320 mutex_lock(&wil->mutex);
321 __wil_down(wil);
322 rc = __wil_up(wil);
323 mutex_unlock(&wil->mutex);
324
325 if (rc)
326 return rc;
327 }
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800328
329 switch (type) {
330 case NL80211_IFTYPE_STATION:
331 case NL80211_IFTYPE_AP:
332 case NL80211_IFTYPE_P2P_CLIENT:
333 case NL80211_IFTYPE_P2P_GO:
334 break;
335 case NL80211_IFTYPE_MONITOR:
Johannes Berg818a9862017-04-12 11:23:28 +0200336 if (params->flags)
337 wil->monitor_flags = params->flags;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800338 break;
339 default:
340 return -EOPNOTSUPP;
341 }
342
343 wdev->iftype = type;
344
345 return 0;
346}
347
348static int wil_cfg80211_scan(struct wiphy *wiphy,
349 struct cfg80211_scan_request *request)
350{
351 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Lior David4332cac2016-03-01 19:18:13 +0200352 struct wireless_dev *wdev = request->wdev;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800353 struct {
354 struct wmi_start_scan_cmd cmd;
355 u16 chnl[4];
356 } __packed cmd;
357 uint i, n;
Vladimir Kondratieved6f9dc2014-03-17 15:34:19 +0200358 int rc;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800359
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200360 wil_dbg_misc(wil, "scan: wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
Dedy Lanskye6d68342016-03-01 19:18:12 +0200361
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800362 /* check we are client side */
363 switch (wdev->iftype) {
364 case NL80211_IFTYPE_STATION:
365 case NL80211_IFTYPE_P2P_CLIENT:
Lior David4332cac2016-03-01 19:18:13 +0200366 case NL80211_IFTYPE_P2P_DEVICE:
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800367 break;
368 default:
369 return -EOPNOTSUPP;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800370 }
371
372 /* FW don't support scan after connection attempt */
Vladimir Kondratiev9419b6a2014-12-23 09:47:14 +0200373 if (test_bit(wil_status_dontscan, wil->status)) {
Vladimir Kondratieve83eb2f2014-03-17 15:34:07 +0200374 wil_err(wil, "Can't scan now\n");
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800375 return -EBUSY;
376 }
377
Lior Davidbb6743f72016-11-28 13:49:00 +0200378 mutex_lock(&wil->mutex);
379
380 mutex_lock(&wil->p2p_wdev_mutex);
381 if (wil->scan_request || wil->p2p.discovery_started) {
382 wil_err(wil, "Already scanning\n");
383 mutex_unlock(&wil->p2p_wdev_mutex);
384 rc = -EAGAIN;
385 goto out;
386 }
387 mutex_unlock(&wil->p2p_wdev_mutex);
388
Lior David321a0002016-04-26 14:41:38 +0300389 /* social scan on P2P_DEVICE is handled as p2p search */
390 if (wdev->iftype == NL80211_IFTYPE_P2P_DEVICE &&
391 wil_p2p_is_social_scan(request)) {
Lior Davideb57a5b2016-06-08 20:07:48 +0300392 if (!wil->p2p.p2p_dev_started) {
393 wil_err(wil, "P2P search requested on stopped P2P device\n");
Lior Davidbb6743f72016-11-28 13:49:00 +0200394 rc = -EIO;
395 goto out;
Lior Davideb57a5b2016-06-08 20:07:48 +0300396 }
Dedy Lanskye6d68342016-03-01 19:18:12 +0200397 wil->scan_request = request;
Lior David4332cac2016-03-01 19:18:13 +0200398 wil->radio_wdev = wdev;
399 rc = wil_p2p_search(wil, request);
400 if (rc) {
401 wil->radio_wdev = wil_to_wdev(wil);
402 wil->scan_request = NULL;
403 }
Lior Davidbb6743f72016-11-28 13:49:00 +0200404 goto out;
Dedy Lanskye6d68342016-03-01 19:18:12 +0200405 }
406
Lior David280ab982016-03-01 19:18:14 +0200407 (void)wil_p2p_stop_discovery(wil);
Dedy Lanskye6d68342016-03-01 19:18:12 +0200408
Vladimir Kondratiev2a91d7d2014-06-16 19:37:11 +0300409 wil_dbg_misc(wil, "Start scan_request 0x%p\n", request);
Hamad Kadmany8e52fe32015-06-09 14:11:18 +0300410 wil_dbg_misc(wil, "SSID count: %d", request->n_ssids);
411
412 for (i = 0; i < request->n_ssids; i++) {
413 wil_dbg_misc(wil, "SSID[%d]", i);
414 print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
415 request->ssids[i].ssid,
416 request->ssids[i].ssid_len);
417 }
418
419 if (request->n_ssids)
420 rc = wmi_set_ssid(wil, request->ssids[0].ssid_len,
421 request->ssids[0].ssid);
422 else
423 rc = wmi_set_ssid(wil, 0, NULL);
424
425 if (rc) {
426 wil_err(wil, "set SSID for scan request failed: %d\n", rc);
Lior Davidbb6743f72016-11-28 13:49:00 +0200427 goto out;
Hamad Kadmany8e52fe32015-06-09 14:11:18 +0300428 }
429
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800430 wil->scan_request = request;
Vladimir Kondratiev047e5d72014-05-27 14:45:48 +0300431 mod_timer(&wil->scan_timer, jiffies + WIL6210_SCAN_TO);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800432
433 memset(&cmd, 0, sizeof(cmd));
Lior David74997a52016-03-01 19:18:08 +0200434 cmd.cmd.scan_type = WMI_ACTIVE_SCAN;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800435 cmd.cmd.num_channels = 0;
436 n = min(request->n_channels, 4U);
437 for (i = 0; i < n; i++) {
438 int ch = request->channels[i]->hw_value;
Vladimir Kondratiev8fe59622014-09-10 16:34:34 +0300439
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800440 if (ch == 0) {
441 wil_err(wil,
442 "Scan requested for unknown frequency %dMhz\n",
443 request->channels[i]->center_freq);
444 continue;
445 }
446 /* 0-based channel indexes */
447 cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1;
Vladimir Kondratiev77438822013-01-28 18:31:06 +0200448 wil_dbg_misc(wil, "Scan for ch %d : %d MHz\n", ch,
Vladimir Kondratiev2057ebb2013-01-28 18:30:58 +0200449 request->channels[i]->center_freq);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800450 }
451
Vladimir Kondratiev77c91292014-09-10 16:34:47 +0300452 if (request->ie_len)
453 print_hex_dump_bytes("Scan IE ", DUMP_PREFIX_OFFSET,
454 request->ie, request->ie_len);
455 else
456 wil_dbg_misc(wil, "Scan has no IE's\n");
457
Vladimir Kondratiev5421bf02015-07-30 13:52:00 +0300458 rc = wmi_set_ie(wil, WMI_FRAME_PROBE_REQ, request->ie_len, request->ie);
459 if (rc)
Lior Davidbb6743f72016-11-28 13:49:00 +0200460 goto out_restore;
Vladimir Kondratiev77c91292014-09-10 16:34:47 +0300461
Lior David74997a52016-03-01 19:18:08 +0200462 if (wil->discovery_mode && cmd.cmd.scan_type == WMI_ACTIVE_SCAN) {
463 cmd.cmd.discovery_mode = 1;
464 wil_dbg_misc(wil, "active scan with discovery_mode=1\n");
465 }
466
Lior David4332cac2016-03-01 19:18:13 +0200467 wil->radio_wdev = wdev;
Vladimir Kondratieved6f9dc2014-03-17 15:34:19 +0200468 rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) +
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800469 cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0]));
Vladimir Kondratieved6f9dc2014-03-17 15:34:19 +0200470
Lior Davidbb6743f72016-11-28 13:49:00 +0200471out_restore:
Vladimir Kondratieva2142082014-08-06 10:31:57 +0300472 if (rc) {
473 del_timer_sync(&wil->scan_timer);
Lior David4332cac2016-03-01 19:18:13 +0200474 wil->radio_wdev = wil_to_wdev(wil);
Vladimir Kondratieved6f9dc2014-03-17 15:34:19 +0200475 wil->scan_request = NULL;
Vladimir Kondratieva2142082014-08-06 10:31:57 +0300476 }
Lior Davidbb6743f72016-11-28 13:49:00 +0200477out:
478 mutex_unlock(&wil->mutex);
Vladimir Kondratieved6f9dc2014-03-17 15:34:19 +0200479 return rc;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800480}
481
Maya Erez035859a2016-11-23 16:06:42 +0200482static void wil_cfg80211_abort_scan(struct wiphy *wiphy,
483 struct wireless_dev *wdev)
484{
485 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
486
487 wil_dbg_misc(wil, "wdev=0x%p iftype=%d\n", wdev, wdev->iftype);
488
489 mutex_lock(&wil->mutex);
490 mutex_lock(&wil->p2p_wdev_mutex);
491
492 if (!wil->scan_request)
493 goto out;
494
495 if (wdev != wil->scan_request->wdev) {
496 wil_dbg_misc(wil, "abort scan was called on the wrong iface\n");
497 goto out;
498 }
499
500 if (wil->radio_wdev == wil->p2p_wdev)
501 wil_p2p_stop_radio_operations(wil);
502 else
503 wil_abort_scan(wil, true);
504
505out:
506 mutex_unlock(&wil->p2p_wdev_mutex);
507 mutex_unlock(&wil->mutex);
508}
509
Vladimir Kondratievfeeac222015-02-01 10:55:15 +0200510static void wil_print_crypto(struct wil6210_priv *wil,
511 struct cfg80211_crypto_settings *c)
512{
513 int i, n;
514
515 wil_dbg_misc(wil, "WPA versions: 0x%08x cipher group 0x%08x\n",
516 c->wpa_versions, c->cipher_group);
517 wil_dbg_misc(wil, "Pairwise ciphers [%d] {\n", c->n_ciphers_pairwise);
518 n = min_t(int, c->n_ciphers_pairwise, ARRAY_SIZE(c->ciphers_pairwise));
519 for (i = 0; i < n; i++)
520 wil_dbg_misc(wil, " [%d] = 0x%08x\n", i,
521 c->ciphers_pairwise[i]);
522 wil_dbg_misc(wil, "}\n");
523 wil_dbg_misc(wil, "AKM suites [%d] {\n", c->n_akm_suites);
524 n = min_t(int, c->n_akm_suites, ARRAY_SIZE(c->akm_suites));
525 for (i = 0; i < n; i++)
526 wil_dbg_misc(wil, " [%d] = 0x%08x\n", i,
527 c->akm_suites[i]);
528 wil_dbg_misc(wil, "}\n");
529 wil_dbg_misc(wil, "Control port : %d, eth_type 0x%04x no_encrypt %d\n",
530 c->control_port, be16_to_cpu(c->control_port_ethertype),
531 c->control_port_no_encrypt);
532}
533
Vladimir Kondratiev8ca26162014-09-10 16:34:32 +0300534static void wil_print_connect_params(struct wil6210_priv *wil,
535 struct cfg80211_connect_params *sme)
536{
537 wil_info(wil, "Connecting to:\n");
538 if (sme->channel) {
539 wil_info(wil, " Channel: %d freq %d\n",
540 sme->channel->hw_value, sme->channel->center_freq);
541 }
542 if (sme->bssid)
543 wil_info(wil, " BSSID: %pM\n", sme->bssid);
544 if (sme->ssid)
545 print_hex_dump(KERN_INFO, " SSID: ", DUMP_PREFIX_OFFSET,
546 16, 1, sme->ssid, sme->ssid_len, true);
547 wil_info(wil, " Privacy: %s\n", sme->privacy ? "secure" : "open");
Lior Davideabb03b2016-03-01 19:18:10 +0200548 wil_info(wil, " PBSS: %d\n", sme->pbss);
Vladimir Kondratievfeeac222015-02-01 10:55:15 +0200549 wil_print_crypto(wil, &sme->crypto);
Vladimir Kondratiev8ca26162014-09-10 16:34:32 +0300550}
551
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800552static int wil_cfg80211_connect(struct wiphy *wiphy,
553 struct net_device *ndev,
554 struct cfg80211_connect_params *sme)
555{
556 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
557 struct cfg80211_bss *bss;
558 struct wmi_connect_cmd conn;
559 const u8 *ssid_eid;
560 const u8 *rsn_eid;
561 int ch;
562 int rc = 0;
Lior Davideabb03b2016-03-01 19:18:10 +0200563 enum ieee80211_bss_type bss_type = IEEE80211_BSS_TYPE_ESS;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800564
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200565 wil_dbg_misc(wil, "connect\n");
Vladimir Kondratiev344a7022015-02-15 14:02:37 +0200566 wil_print_connect_params(wil, sme);
567
Vladimir Kondratiev9419b6a2014-12-23 09:47:14 +0200568 if (test_bit(wil_status_fwconnecting, wil->status) ||
569 test_bit(wil_status_fwconnected, wil->status))
Vladimir Kondratiev4cd9e832014-03-17 15:34:20 +0200570 return -EALREADY;
571
Vladimir Kondratiev344a7022015-02-15 14:02:37 +0200572 if (sme->ie_len > WMI_MAX_IE_LEN) {
573 wil_err(wil, "IE too large (%td bytes)\n", sme->ie_len);
574 return -ERANGE;
575 }
576
577 rsn_eid = sme->ie ?
578 cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) :
579 NULL;
Vladimir Kondratiev27aa6b72015-04-30 16:25:11 +0300580 if (sme->privacy && !rsn_eid)
581 wil_info(wil, "WSC connection\n");
Vladimir Kondratiev8ca26162014-09-10 16:34:32 +0300582
Lior Davideabb03b2016-03-01 19:18:10 +0200583 if (sme->pbss)
584 bss_type = IEEE80211_BSS_TYPE_PBSS;
Lior David34d50512016-01-28 10:58:25 +0200585
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800586 bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid,
587 sme->ssid, sme->ssid_len,
Lior Davideabb03b2016-03-01 19:18:10 +0200588 bss_type, IEEE80211_PRIVACY_ANY);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800589 if (!bss) {
590 wil_err(wil, "Unable to find BSS\n");
591 return -ENOENT;
592 }
593
594 ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID);
595 if (!ssid_eid) {
596 wil_err(wil, "No SSID\n");
597 rc = -ENOENT;
598 goto out;
599 }
Vladimir Kondratiev344a7022015-02-15 14:02:37 +0200600 wil->privacy = sme->privacy;
Lior Davida895cb82017-01-20 13:49:50 +0200601 wil->pbss = sme->pbss;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800602
Vladimir Kondratiev344a7022015-02-15 14:02:37 +0200603 if (wil->privacy) {
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300604 /* For secure assoc, remove old keys */
605 rc = wmi_del_cipher_key(wil, 0, bss->bssid,
606 WMI_KEY_USE_PAIRWISE);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800607 if (rc) {
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300608 wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(PTK) failed\n");
609 goto out;
610 }
611 rc = wmi_del_cipher_key(wil, 0, bss->bssid,
612 WMI_KEY_USE_RX_GROUP);
613 if (rc) {
614 wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD(GTK) failed\n");
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800615 goto out;
616 }
Vladimir Kondratievac4acdb2014-09-10 16:34:43 +0300617 }
618
619 /* WMI_SET_APPIE_CMD. ie may contain rsn info as well as other info
620 * elements. Send it also in case it's empty, to erase previously set
621 * ies in FW.
622 */
623 rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie);
Vladimir Kondratiev5421bf02015-07-30 13:52:00 +0300624 if (rc)
Vladimir Kondratievac4acdb2014-09-10 16:34:43 +0300625 goto out;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800626
627 /* WMI_CONNECT_CMD */
628 memset(&conn, 0, sizeof(conn));
Vladimir Kondratievacc97802013-03-13 14:12:47 +0200629 switch (bss->capability & WLAN_CAPABILITY_DMG_TYPE_MASK) {
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800630 case WLAN_CAPABILITY_DMG_TYPE_AP:
631 conn.network_type = WMI_NETTYPE_INFRA;
632 break;
633 case WLAN_CAPABILITY_DMG_TYPE_PBSS:
634 conn.network_type = WMI_NETTYPE_P2P;
635 break;
636 default:
637 wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n",
638 bss->capability);
639 goto out;
640 }
Vladimir Kondratiev344a7022015-02-15 14:02:37 +0200641 if (wil->privacy) {
Vladimir Kondratiev27aa6b72015-04-30 16:25:11 +0300642 if (rsn_eid) { /* regular secure connection */
643 conn.dot11_auth_mode = WMI_AUTH11_SHARED;
644 conn.auth_mode = WMI_AUTH_WPA2_PSK;
645 conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP;
646 conn.pairwise_crypto_len = 16;
647 conn.group_crypto_type = WMI_CRYPT_AES_GCMP;
648 conn.group_crypto_len = 16;
649 } else { /* WSC */
650 conn.dot11_auth_mode = WMI_AUTH11_WSC;
651 conn.auth_mode = WMI_AUTH_NONE;
652 }
653 } else { /* insecure connection */
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800654 conn.dot11_auth_mode = WMI_AUTH11_OPEN;
655 conn.auth_mode = WMI_AUTH_NONE;
656 }
657
658 conn.ssid_len = min_t(u8, ssid_eid[1], 32);
659 memcpy(conn.ssid, ssid_eid+2, conn.ssid_len);
660
661 ch = bss->channel->hw_value;
662 if (ch == 0) {
663 wil_err(wil, "BSS at unknown frequency %dMhz\n",
664 bss->channel->center_freq);
665 rc = -EOPNOTSUPP;
666 goto out;
667 }
668 conn.channel = ch - 1;
669
Vladimir Kondratieva82553bb2015-03-15 16:00:21 +0200670 ether_addr_copy(conn.bssid, bss->bssid);
671 ether_addr_copy(conn.dst_mac, bss->bssid);
Vladimir Kondratieve83eb2f2014-03-17 15:34:07 +0200672
Vladimir Kondratiev9419b6a2014-12-23 09:47:14 +0200673 set_bit(wil_status_fwconnecting, wil->status);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800674
675 rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn));
676 if (rc == 0) {
Dedy Lanskyc5e96c92015-01-25 10:52:43 +0200677 netif_carrier_on(ndev);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800678 /* Connect can take lots of time */
679 mod_timer(&wil->connect_timer,
680 jiffies + msecs_to_jiffies(2000));
Vladimir Kondratievb338f742013-05-28 15:17:53 +0300681 } else {
Vladimir Kondratiev9419b6a2014-12-23 09:47:14 +0200682 clear_bit(wil_status_fwconnecting, wil->status);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800683 }
684
685 out:
Johannes Berg5b112d32013-02-01 01:49:58 +0100686 cfg80211_put_bss(wiphy, bss);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800687
688 return rc;
689}
690
691static int wil_cfg80211_disconnect(struct wiphy *wiphy,
692 struct net_device *ndev,
693 u16 reason_code)
694{
695 int rc;
696 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
697
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200698 wil_dbg_misc(wil, "disconnect: reason=%d\n", reason_code);
Vladimir Kondratievde9084e2015-03-30 11:28:52 +0300699
Vladimir Kondratiev78771d72016-01-28 19:24:03 +0200700 if (!(test_bit(wil_status_fwconnecting, wil->status) ||
701 test_bit(wil_status_fwconnected, wil->status))) {
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200702 wil_err(wil, "Disconnect was called while disconnected\n");
Vladimir Kondratiev78771d72016-01-28 19:24:03 +0200703 return 0;
704 }
705
706 rc = wmi_call(wil, WMI_DISCONNECT_CMDID, NULL, 0,
707 WMI_DISCONNECT_EVENTID, NULL, 0,
708 WIL6210_DISCONNECT_TO_MS);
709 if (rc)
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200710 wil_err(wil, "disconnect error %d\n", rc);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800711
712 return rc;
713}
714
Lior David3fea18d2016-11-23 16:06:43 +0200715static int wil_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
716{
717 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
718 int rc;
719
720 /* these parameters are explicitly not supported */
721 if (changed & (WIPHY_PARAM_RETRY_LONG |
722 WIPHY_PARAM_FRAG_THRESHOLD |
723 WIPHY_PARAM_RTS_THRESHOLD))
724 return -ENOTSUPP;
725
726 if (changed & WIPHY_PARAM_RETRY_SHORT) {
727 rc = wmi_set_mgmt_retry(wil, wiphy->retry_short);
728 if (rc)
729 return rc;
730 }
731
732 return 0;
733}
734
Vladimir Kondratiev0b39aaf2014-06-16 19:36:59 +0300735int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
736 struct cfg80211_mgmt_tx_params *params,
737 u64 *cookie)
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200738{
739 const u8 *buf = params->buf;
740 size_t len = params->len;
741 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
742 int rc;
Vladimir Kondratiev304464f2014-06-16 19:37:00 +0300743 bool tx_status = false;
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200744 struct ieee80211_mgmt *mgmt_frame = (void *)buf;
745 struct wmi_sw_tx_req_cmd *cmd;
746 struct {
Lior Davidb874dde2016-03-01 19:18:09 +0200747 struct wmi_cmd_hdr wmi;
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200748 struct wmi_sw_tx_complete_event evt;
749 } __packed evt;
750
Dedy Lanskye6d68342016-03-01 19:18:12 +0200751 /* Note, currently we do not support the "wait" parameter, user-space
752 * must call remain_on_channel before mgmt_tx or listen on a channel
753 * another way (AP/PCP or connected station)
754 * in addition we need to check if specified "chan" argument is
755 * different from currently "listened" channel and fail if it is.
756 */
757
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200758 wil_dbg_misc(wil, "mgmt_tx\n");
Dedy Lanskye6d68342016-03-01 19:18:12 +0200759 print_hex_dump_bytes("mgmt tx frame ", DUMP_PREFIX_OFFSET, buf, len);
760
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200761 cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
Vladimir Kondratiev304464f2014-06-16 19:37:00 +0300762 if (!cmd) {
763 rc = -ENOMEM;
764 goto out;
765 }
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200766
767 memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
768 cmd->len = cpu_to_le16(len);
769 memcpy(cmd->payload, buf, len);
770
771 rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
772 WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
773 if (rc == 0)
Vladimir Kondratiev304464f2014-06-16 19:37:00 +0300774 tx_status = !evt.evt.status;
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200775
776 kfree(cmd);
Vladimir Kondratiev304464f2014-06-16 19:37:00 +0300777 out:
778 cfg80211_mgmt_tx_status(wdev, cookie ? *cookie : 0, buf, len,
779 tx_status, GFP_KERNEL);
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200780 return rc;
781}
782
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800783static int wil_cfg80211_set_channel(struct wiphy *wiphy,
784 struct cfg80211_chan_def *chandef)
785{
786 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Lior David4332cac2016-03-01 19:18:13 +0200787 struct wireless_dev *wdev = wil_to_wdev(wil);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800788
789 wdev->preset_chandef = *chandef;
790
791 return 0;
792}
793
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300794static enum wmi_key_usage wil_detect_key_usage(struct wil6210_priv *wil,
795 bool pairwise)
796{
Lior David4332cac2016-03-01 19:18:13 +0200797 struct wireless_dev *wdev = wil_to_wdev(wil);
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300798 enum wmi_key_usage rc;
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300799
800 if (pairwise) {
801 rc = WMI_KEY_USE_PAIRWISE;
802 } else {
803 switch (wdev->iftype) {
804 case NL80211_IFTYPE_STATION:
Dedy Lanskye6d68342016-03-01 19:18:12 +0200805 case NL80211_IFTYPE_P2P_CLIENT:
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300806 rc = WMI_KEY_USE_RX_GROUP;
807 break;
808 case NL80211_IFTYPE_AP:
Dedy Lanskye6d68342016-03-01 19:18:12 +0200809 case NL80211_IFTYPE_P2P_GO:
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300810 rc = WMI_KEY_USE_TX_GROUP;
811 break;
812 default:
813 /* TODO: Rx GTK or Tx GTK? */
814 wil_err(wil, "Can't determine GTK type\n");
815 rc = WMI_KEY_USE_RX_GROUP;
816 break;
817 }
818 }
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200819 wil_dbg_misc(wil, "detect_key_usage: -> %s\n", key_usage_str[rc]);
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300820
821 return rc;
822}
823
Maya Erez74b6ac52016-08-18 16:52:12 +0300824static struct wil_sta_info *
825wil_find_sta_by_key_usage(struct wil6210_priv *wil,
826 enum wmi_key_usage key_usage, const u8 *mac_addr)
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200827{
828 int cid = -EINVAL;
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200829
830 if (key_usage == WMI_KEY_USE_TX_GROUP)
831 return NULL; /* not needed */
832
833 /* supplicant provides Rx group key in STA mode with NULL MAC address */
834 if (mac_addr)
835 cid = wil_find_cid(wil, mac_addr);
836 else if (key_usage == WMI_KEY_USE_RX_GROUP)
837 cid = wil_find_cid_by_idx(wil, 0);
838 if (cid < 0) {
Maya Erez74b6ac52016-08-18 16:52:12 +0300839 wil_err(wil, "No CID for %pM %s\n", mac_addr,
840 key_usage_str[key_usage]);
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200841 return ERR_PTR(cid);
842 }
843
Maya Erez74b6ac52016-08-18 16:52:12 +0300844 return &wil->sta[cid];
845}
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200846
Maya Erez74b6ac52016-08-18 16:52:12 +0300847static void wil_set_crypto_rx(u8 key_index, enum wmi_key_usage key_usage,
848 struct wil_sta_info *cs,
849 struct key_params *params)
850{
851 struct wil_tid_crypto_rx_single *cc;
852 int tid;
853
854 if (!cs)
855 return;
856
857 switch (key_usage) {
858 case WMI_KEY_USE_PAIRWISE:
859 for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
860 cc = &cs->tid_crypto_rx[tid].key_id[key_index];
861 if (params->seq)
862 memcpy(cc->pn, params->seq,
863 IEEE80211_GCMP_PN_LEN);
864 else
865 memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
866 cc->key_set = true;
867 }
868 break;
869 case WMI_KEY_USE_RX_GROUP:
870 cc = &cs->group_crypto_rx.key_id[key_index];
871 if (params->seq)
872 memcpy(cc->pn, params->seq, IEEE80211_GCMP_PN_LEN);
873 else
874 memset(cc->pn, 0, IEEE80211_GCMP_PN_LEN);
875 cc->key_set = true;
876 break;
877 default:
878 break;
879 }
880}
881
882static void wil_del_rx_key(u8 key_index, enum wmi_key_usage key_usage,
883 struct wil_sta_info *cs)
884{
885 struct wil_tid_crypto_rx_single *cc;
886 int tid;
887
888 if (!cs)
889 return;
890
891 switch (key_usage) {
892 case WMI_KEY_USE_PAIRWISE:
893 for (tid = 0; tid < WIL_STA_TID_NUM; tid++) {
894 cc = &cs->tid_crypto_rx[tid].key_id[key_index];
895 cc->key_set = false;
896 }
897 break;
898 case WMI_KEY_USE_RX_GROUP:
899 cc = &cs->group_crypto_rx.key_id[key_index];
900 cc->key_set = false;
901 break;
902 default:
903 break;
904 }
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200905}
906
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800907static int wil_cfg80211_add_key(struct wiphy *wiphy,
908 struct net_device *ndev,
909 u8 key_index, bool pairwise,
910 const u8 *mac_addr,
911 struct key_params *params)
912{
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200913 int rc;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800914 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300915 enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
Maya Erez74b6ac52016-08-18 16:52:12 +0300916 struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
917 mac_addr);
918
919 if (!params) {
920 wil_err(wil, "NULL params\n");
921 return -EINVAL;
922 }
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800923
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200924 wil_dbg_misc(wil, "add_key: %pM %s[%d] PN %*phN\n",
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200925 mac_addr, key_usage_str[key_usage], key_index,
926 params->seq_len, params->seq);
Vladimir Kondratievdb8adcb2015-03-30 11:28:51 +0300927
Maya Erez74b6ac52016-08-18 16:52:12 +0300928 if (IS_ERR(cs)) {
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200929 wil_err(wil, "Not connected, %pM %s[%d] PN %*phN\n",
930 mac_addr, key_usage_str[key_usage], key_index,
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200931 params->seq_len, params->seq);
932 return -EINVAL;
933 }
934
Maya Erez74b6ac52016-08-18 16:52:12 +0300935 wil_del_rx_key(key_index, key_usage, cs);
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200936
937 if (params->seq && params->seq_len != IEEE80211_GCMP_PN_LEN) {
938 wil_err(wil,
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200939 "Wrong PN len %d, %pM %s[%d] PN %*phN\n",
940 params->seq_len, mac_addr,
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200941 key_usage_str[key_usage], key_index,
942 params->seq_len, params->seq);
943 return -EINVAL;
944 }
945
946 rc = wmi_add_cipher_key(wil, key_index, mac_addr, params->key_len,
947 params->key, key_usage);
Maya Erez74b6ac52016-08-18 16:52:12 +0300948 if (!rc)
949 wil_set_crypto_rx(key_index, key_usage, cs, params);
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200950
951 return rc;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800952}
953
954static int wil_cfg80211_del_key(struct wiphy *wiphy,
955 struct net_device *ndev,
956 u8 key_index, bool pairwise,
957 const u8 *mac_addr)
958{
959 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300960 enum wmi_key_usage key_usage = wil_detect_key_usage(wil, pairwise);
Maya Erez74b6ac52016-08-18 16:52:12 +0300961 struct wil_sta_info *cs = wil_find_sta_by_key_usage(wil, key_usage,
962 mac_addr);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800963
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200964 wil_dbg_misc(wil, "del_key: %pM %s[%d]\n", mac_addr,
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200965 key_usage_str[key_usage], key_index);
966
Maya Erez74b6ac52016-08-18 16:52:12 +0300967 if (IS_ERR(cs))
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200968 wil_info(wil, "Not connected, %pM %s[%d]\n",
Vladimir Kondratiev58527422016-03-01 19:18:07 +0200969 mac_addr, key_usage_str[key_usage], key_index);
970
Maya Erez74b6ac52016-08-18 16:52:12 +0300971 if (!IS_ERR_OR_NULL(cs))
972 wil_del_rx_key(key_index, key_usage, cs);
Vladimir Kondratievdb8adcb2015-03-30 11:28:51 +0300973
Vladimir Kondratiev230d8442015-04-30 16:25:10 +0300974 return wmi_del_cipher_key(wil, key_index, mac_addr, key_usage);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800975}
976
977/* Need to be present or wiphy_new() will WARN */
978static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
979 struct net_device *ndev,
980 u8 key_index, bool unicast,
981 bool multicast)
982{
Lior David280ab982016-03-01 19:18:14 +0200983 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
984
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200985 wil_dbg_misc(wil, "set_default_key: entered\n");
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -0800986 return 0;
987}
988
Vladimir Kondratiev1647f122014-02-27 16:20:40 +0200989static int wil_remain_on_channel(struct wiphy *wiphy,
990 struct wireless_dev *wdev,
991 struct ieee80211_channel *chan,
992 unsigned int duration,
993 u64 *cookie)
994{
995 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
996 int rc;
997
Lazar Alexeiaf3db602017-01-20 13:49:46 +0200998 wil_dbg_misc(wil,
999 "remain_on_channel: center_freq=%d, duration=%d iftype=%d\n",
1000 chan->center_freq, duration, wdev->iftype);
Vladimir Kondratiev1647f122014-02-27 16:20:40 +02001001
Lior Davidbb6743f72016-11-28 13:49:00 +02001002 rc = wil_p2p_listen(wil, wdev, duration, chan, cookie);
1003 return rc;
Vladimir Kondratiev1647f122014-02-27 16:20:40 +02001004}
1005
1006static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
1007 struct wireless_dev *wdev,
1008 u64 cookie)
1009{
1010 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Vladimir Kondratiev1647f122014-02-27 16:20:40 +02001011
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001012 wil_dbg_misc(wil, "cancel_remain_on_channel\n");
Vladimir Kondratiev1647f122014-02-27 16:20:40 +02001013
Lior David280ab982016-03-01 19:18:14 +02001014 return wil_p2p_cancel_listen(wil, cookie);
Vladimir Kondratiev1647f122014-02-27 16:20:40 +02001015}
1016
Lior Davidc100c882016-01-17 12:39:07 +02001017/**
1018 * find a specific IE in a list of IEs
1019 * return a pointer to the beginning of IE in the list
1020 * or NULL if not found
1021 */
1022static const u8 *_wil_cfg80211_find_ie(const u8 *ies, u16 ies_len, const u8 *ie,
1023 u16 ie_len)
1024{
1025 struct ieee80211_vendor_ie *vie;
1026 u32 oui;
1027
1028 /* IE tag at offset 0, length at offset 1 */
1029 if (ie_len < 2 || 2 + ie[1] > ie_len)
1030 return NULL;
1031
1032 if (ie[0] != WLAN_EID_VENDOR_SPECIFIC)
1033 return cfg80211_find_ie(ie[0], ies, ies_len);
1034
1035 /* make sure there is room for 3 bytes OUI + 1 byte OUI type */
1036 if (ie[1] < 4)
1037 return NULL;
1038 vie = (struct ieee80211_vendor_ie *)ie;
1039 oui = vie->oui[0] << 16 | vie->oui[1] << 8 | vie->oui[2];
1040 return cfg80211_find_vendor_ie(oui, vie->oui_type, ies,
1041 ies_len);
1042}
1043
1044/**
1045 * merge the IEs in two lists into a single list.
1046 * do not include IEs from the second list which exist in the first list.
1047 * add only vendor specific IEs from second list to keep
1048 * the merged list sorted (since vendor-specific IE has the
1049 * highest tag number)
1050 * caller must free the allocated memory for merged IEs
1051 */
1052static int _wil_cfg80211_merge_extra_ies(const u8 *ies1, u16 ies1_len,
1053 const u8 *ies2, u16 ies2_len,
1054 u8 **merged_ies, u16 *merged_len)
1055{
1056 u8 *buf, *dpos;
1057 const u8 *spos;
1058
1059 if (ies1_len == 0 && ies2_len == 0) {
1060 *merged_ies = NULL;
1061 *merged_len = 0;
1062 return 0;
1063 }
1064
1065 buf = kmalloc(ies1_len + ies2_len, GFP_KERNEL);
1066 if (!buf)
1067 return -ENOMEM;
1068 memcpy(buf, ies1, ies1_len);
1069 dpos = buf + ies1_len;
1070 spos = ies2;
1071 while (spos + 1 < ies2 + ies2_len) {
1072 /* IE tag at offset 0, length at offset 1 */
1073 u16 ielen = 2 + spos[1];
1074
1075 if (spos + ielen > ies2 + ies2_len)
1076 break;
1077 if (spos[0] == WLAN_EID_VENDOR_SPECIFIC &&
1078 !_wil_cfg80211_find_ie(ies1, ies1_len, spos, ielen)) {
1079 memcpy(dpos, spos, ielen);
1080 dpos += ielen;
1081 }
1082 spos += ielen;
1083 }
1084
1085 *merged_ies = buf;
1086 *merged_len = dpos - buf;
1087 return 0;
1088}
1089
Vladimir Kondratievca959772014-06-16 19:37:01 +03001090static void wil_print_bcon_data(struct cfg80211_beacon_data *b)
1091{
1092 print_hex_dump_bytes("head ", DUMP_PREFIX_OFFSET,
1093 b->head, b->head_len);
1094 print_hex_dump_bytes("tail ", DUMP_PREFIX_OFFSET,
1095 b->tail, b->tail_len);
1096 print_hex_dump_bytes("BCON IE ", DUMP_PREFIX_OFFSET,
1097 b->beacon_ies, b->beacon_ies_len);
1098 print_hex_dump_bytes("PROBE ", DUMP_PREFIX_OFFSET,
1099 b->probe_resp, b->probe_resp_len);
1100 print_hex_dump_bytes("PROBE IE ", DUMP_PREFIX_OFFSET,
1101 b->proberesp_ies, b->proberesp_ies_len);
1102 print_hex_dump_bytes("ASSOC IE ", DUMP_PREFIX_OFFSET,
1103 b->assocresp_ies, b->assocresp_ies_len);
1104}
1105
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001106/* internal functions for device reset and starting AP */
1107static int _wil_cfg80211_set_ies(struct wiphy *wiphy,
Vladimir Kondratievcab5abb2015-07-30 13:51:59 +03001108 struct cfg80211_beacon_data *bcon)
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001109{
1110 int rc;
1111 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Lior Davidc100c882016-01-17 12:39:07 +02001112 u16 len = 0, proberesp_len = 0;
1113 u8 *ies = NULL, *proberesp = NULL;
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001114
Lior Davidc100c882016-01-17 12:39:07 +02001115 if (bcon->probe_resp) {
1116 struct ieee80211_mgmt *f =
1117 (struct ieee80211_mgmt *)bcon->probe_resp;
1118 size_t hlen = offsetof(struct ieee80211_mgmt,
1119 u.probe_resp.variable);
1120 proberesp = f->u.probe_resp.variable;
1121 proberesp_len = bcon->probe_resp_len - hlen;
1122 }
1123 rc = _wil_cfg80211_merge_extra_ies(proberesp,
1124 proberesp_len,
1125 bcon->proberesp_ies,
1126 bcon->proberesp_ies_len,
1127 &ies, &len);
1128
Vladimir Kondratiev5421bf02015-07-30 13:52:00 +03001129 if (rc)
Lior Davidc100c882016-01-17 12:39:07 +02001130 goto out;
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001131
Lior Davidc100c882016-01-17 12:39:07 +02001132 rc = wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, len, ies);
1133 if (rc)
1134 goto out;
1135
1136 if (bcon->assocresp_ies)
1137 rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP,
1138 bcon->assocresp_ies_len, bcon->assocresp_ies);
1139 else
1140 rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, len, ies);
Vladimir Kondratievcab5abb2015-07-30 13:51:59 +03001141#if 0 /* to use beacon IE's, remove this #if 0 */
Vladimir Kondratiev5421bf02015-07-30 13:52:00 +03001142 if (rc)
Lior Davidc100c882016-01-17 12:39:07 +02001143 goto out;
Vladimir Kondratiev5421bf02015-07-30 13:52:00 +03001144
1145 rc = wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->tail_len, bcon->tail);
Vladimir Kondratievcab5abb2015-07-30 13:51:59 +03001146#endif
Lior Davidc100c882016-01-17 12:39:07 +02001147out:
1148 kfree(ies);
Vladimir Kondratiev5421bf02015-07-30 13:52:00 +03001149 return rc;
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001150}
1151
1152static int _wil_cfg80211_start_ap(struct wiphy *wiphy,
1153 struct net_device *ndev,
1154 const u8 *ssid, size_t ssid_len, u32 privacy,
1155 int bi, u8 chan,
Vladimir Kondratievcab5abb2015-07-30 13:51:59 +03001156 struct cfg80211_beacon_data *bcon,
Lior Davideabb03b2016-03-01 19:18:10 +02001157 u8 hidden_ssid, u32 pbss)
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001158{
1159 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1160 int rc;
1161 struct wireless_dev *wdev = ndev->ieee80211_ptr;
1162 u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype);
Lior Davidb4944f22016-03-01 19:18:17 +02001163 u8 is_go = (wdev->iftype == NL80211_IFTYPE_P2P_GO);
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001164
Lior Davideabb03b2016-03-01 19:18:10 +02001165 if (pbss)
1166 wmi_nettype = WMI_NETTYPE_P2P;
1167
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001168 wil_dbg_misc(wil, "start_ap: is_go=%d\n", is_go);
Lior Davidb4944f22016-03-01 19:18:17 +02001169 if (is_go && !pbss) {
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001170 wil_err(wil, "P2P GO must be in PBSS\n");
Lior Davidb4944f22016-03-01 19:18:17 +02001171 return -ENOTSUPP;
1172 }
1173
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001174 wil_set_recovery_state(wil, fw_recovery_idle);
1175
1176 mutex_lock(&wil->mutex);
1177
1178 __wil_down(wil);
1179 rc = __wil_up(wil);
1180 if (rc)
1181 goto out;
1182
1183 rc = wmi_set_ssid(wil, ssid_len, ssid);
1184 if (rc)
1185 goto out;
1186
Vladimir Kondratievcab5abb2015-07-30 13:51:59 +03001187 rc = _wil_cfg80211_set_ies(wiphy, bcon);
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001188 if (rc)
1189 goto out;
1190
1191 wil->privacy = privacy;
1192 wil->channel = chan;
1193 wil->hidden_ssid = hidden_ssid;
Lior Davideabb03b2016-03-01 19:18:10 +02001194 wil->pbss = pbss;
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001195
1196 netif_carrier_on(ndev);
1197
Lior Davidb4944f22016-03-01 19:18:17 +02001198 rc = wmi_pcp_start(wil, bi, wmi_nettype, chan, hidden_ssid, is_go);
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001199 if (rc)
1200 goto err_pcp_start;
1201
1202 rc = wil_bcast_init(wil);
1203 if (rc)
1204 goto err_bcast;
1205
1206 goto out; /* success */
1207
1208err_bcast:
1209 wmi_pcp_stop(wil);
1210err_pcp_start:
1211 netif_carrier_off(ndev);
1212out:
1213 mutex_unlock(&wil->mutex);
1214 return rc;
1215}
1216
Vladimir Kondratiev1bd922f2014-09-10 16:34:44 +03001217static int wil_cfg80211_change_beacon(struct wiphy *wiphy,
1218 struct net_device *ndev,
1219 struct cfg80211_beacon_data *bcon)
1220{
1221 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1222 int rc;
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001223 u32 privacy = 0;
Vladimir Kondratiev1bd922f2014-09-10 16:34:44 +03001224
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001225 wil_dbg_misc(wil, "change_beacon\n");
Vladimir Kondratiev1e7e5a02015-04-30 16:25:08 +03001226 wil_print_bcon_data(bcon);
1227
Lior Davidc5a157e2016-01-17 12:39:06 +02001228 if (bcon->tail &&
1229 cfg80211_find_ie(WLAN_EID_RSN, bcon->tail,
1230 bcon->tail_len))
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001231 privacy = 1;
1232
1233 /* in case privacy has changed, need to restart the AP */
1234 if (wil->privacy != privacy) {
1235 struct wireless_dev *wdev = ndev->ieee80211_ptr;
1236
1237 wil_dbg_misc(wil, "privacy changed %d=>%d. Restarting AP\n",
1238 wil->privacy, privacy);
1239
1240 rc = _wil_cfg80211_start_ap(wiphy, ndev, wdev->ssid,
1241 wdev->ssid_len, privacy,
1242 wdev->beacon_interval,
Vladimir Kondratievcab5abb2015-07-30 13:51:59 +03001243 wil->channel, bcon,
Lior Davideabb03b2016-03-01 19:18:10 +02001244 wil->hidden_ssid,
1245 wil->pbss);
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001246 } else {
Vladimir Kondratievcab5abb2015-07-30 13:51:59 +03001247 rc = _wil_cfg80211_set_ies(wiphy, bcon);
Vladimir Kondratiev1bd922f2014-09-10 16:34:44 +03001248 }
1249
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001250 return rc;
Vladimir Kondratiev1bd922f2014-09-10 16:34:44 +03001251}
1252
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001253static int wil_cfg80211_start_ap(struct wiphy *wiphy,
1254 struct net_device *ndev,
1255 struct cfg80211_ap_settings *info)
1256{
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001257 int rc;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001258 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001259 struct ieee80211_channel *channel = info->chandef.chan;
1260 struct cfg80211_beacon_data *bcon = &info->beacon;
Vladimir Kondratievca959772014-06-16 19:37:01 +03001261 struct cfg80211_crypto_settings *crypto = &info->crypto;
Hamad Kadmany8e52fe32015-06-09 14:11:18 +03001262 u8 hidden_ssid;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001263
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001264 wil_dbg_misc(wil, "start_ap\n");
Vladimir Kondratievca959772014-06-16 19:37:01 +03001265
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001266 if (!channel) {
1267 wil_err(wil, "AP: No channel???\n");
1268 return -EINVAL;
1269 }
1270
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001271 switch (info->hidden_ssid) {
1272 case NL80211_HIDDEN_SSID_NOT_IN_USE:
1273 hidden_ssid = WMI_HIDDEN_SSID_DISABLED;
1274 break;
1275
1276 case NL80211_HIDDEN_SSID_ZERO_LEN:
1277 hidden_ssid = WMI_HIDDEN_SSID_SEND_EMPTY;
1278 break;
1279
1280 case NL80211_HIDDEN_SSID_ZERO_CONTENTS:
1281 hidden_ssid = WMI_HIDDEN_SSID_CLEAR;
1282 break;
1283
1284 default:
1285 wil_err(wil, "AP: Invalid hidden SSID %d\n", info->hidden_ssid);
1286 return -EOPNOTSUPP;
1287 }
Vladimir Kondratiev77438822013-01-28 18:31:06 +02001288 wil_dbg_misc(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value,
Vladimir Kondratiev2057ebb2013-01-28 18:30:58 +02001289 channel->center_freq, info->privacy ? "secure" : "open");
Vladimir Kondratievca959772014-06-16 19:37:01 +03001290 wil_dbg_misc(wil, "Privacy: %d auth_type %d\n",
1291 info->privacy, info->auth_type);
Hamad Kadmany8e52fe32015-06-09 14:11:18 +03001292 wil_dbg_misc(wil, "Hidden SSID mode: %d\n",
1293 info->hidden_ssid);
Vladimir Kondratievca959772014-06-16 19:37:01 +03001294 wil_dbg_misc(wil, "BI %d DTIM %d\n", info->beacon_interval,
1295 info->dtim_period);
Lior Davideabb03b2016-03-01 19:18:10 +02001296 wil_dbg_misc(wil, "PBSS %d\n", info->pbss);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001297 print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET,
1298 info->ssid, info->ssid_len);
Vladimir Kondratievca959772014-06-16 19:37:01 +03001299 wil_print_bcon_data(bcon);
1300 wil_print_crypto(wil, crypto);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001301
Vladimir Kondratiev33190eb2015-06-15 17:42:32 +03001302 rc = _wil_cfg80211_start_ap(wiphy, ndev,
1303 info->ssid, info->ssid_len, info->privacy,
1304 info->beacon_interval, channel->hw_value,
Lior Davideabb03b2016-03-01 19:18:10 +02001305 bcon, hidden_ssid, info->pbss);
Vladimir Kondratievc33407a2014-10-01 15:05:24 +03001306
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001307 return rc;
1308}
1309
1310static int wil_cfg80211_stop_ap(struct wiphy *wiphy,
1311 struct net_device *ndev)
1312{
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001313 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001314
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001315 wil_dbg_misc(wil, "stop_ap\n");
Vladimir Kondratievca959772014-06-16 19:37:01 +03001316
Dedy Lanskyc5e96c92015-01-25 10:52:43 +02001317 netif_carrier_off(ndev);
Vladimir Kondratievc33407a2014-10-01 15:05:24 +03001318 wil_set_recovery_state(wil, fw_recovery_idle);
1319
Vladimir Kondratiev9c3bde52014-03-17 15:34:21 +02001320 mutex_lock(&wil->mutex);
1321
Dedy Lansky32a20d42015-01-25 10:52:44 +02001322 wmi_pcp_stop(wil);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001323
Vladimir Kondratiev73d839a2014-09-10 16:34:50 +03001324 __wil_down(wil);
Vladimir Kondratiev73d839a2014-09-10 16:34:50 +03001325
Vladimir Kondratiev9c3bde52014-03-17 15:34:21 +02001326 mutex_unlock(&wil->mutex);
Vladimir Kondratiev73d839a2014-09-10 16:34:50 +03001327
Dedy Lansky32a20d42015-01-25 10:52:44 +02001328 return 0;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001329}
1330
Dedy Lansky849a5642017-01-20 13:49:44 +02001331static int wil_cfg80211_add_station(struct wiphy *wiphy,
1332 struct net_device *dev,
1333 const u8 *mac,
1334 struct station_parameters *params)
1335{
1336 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1337
1338 wil_dbg_misc(wil, "add station %pM aid %d\n", mac, params->aid);
1339
1340 if (!disable_ap_sme) {
1341 wil_err(wil, "not supported with AP SME enabled\n");
1342 return -EOPNOTSUPP;
1343 }
1344
1345 if (params->aid > WIL_MAX_DMG_AID) {
1346 wil_err(wil, "invalid aid\n");
1347 return -EINVAL;
1348 }
1349
1350 return wmi_new_sta(wil, mac, params->aid);
1351}
1352
Vladimir Kondratiev4d55a0a2014-02-27 16:20:54 +02001353static int wil_cfg80211_del_station(struct wiphy *wiphy,
Jouni Malinen89c771e2014-10-10 20:52:40 +03001354 struct net_device *dev,
1355 struct station_del_parameters *params)
Vladimir Kondratiev4d55a0a2014-02-27 16:20:54 +02001356{
1357 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Vladimir Kondratiev097638a2014-03-17 15:34:25 +02001358
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001359 wil_dbg_misc(wil, "del_station: %pM, reason=%d\n", params->mac,
Vladimir Kondratievde9084e2015-03-30 11:28:52 +03001360 params->reason_code);
1361
Vladimir Kondratiev097638a2014-03-17 15:34:25 +02001362 mutex_lock(&wil->mutex);
Vladimir Kondratiev4821e6d2014-12-01 15:33:15 +02001363 wil6210_disconnect(wil, params->mac, params->reason_code, false);
Vladimir Kondratiev097638a2014-03-17 15:34:25 +02001364 mutex_unlock(&wil->mutex);
1365
Vladimir Kondratiev4d55a0a2014-02-27 16:20:54 +02001366 return 0;
1367}
1368
Dedy Lansky849a5642017-01-20 13:49:44 +02001369static int wil_cfg80211_change_station(struct wiphy *wiphy,
1370 struct net_device *dev,
1371 const u8 *mac,
1372 struct station_parameters *params)
1373{
1374 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1375 int authorize;
1376 int cid, i;
1377 struct vring_tx_data *txdata = NULL;
1378
1379 wil_dbg_misc(wil, "change station %pM mask 0x%x set 0x%x\n", mac,
1380 params->sta_flags_mask, params->sta_flags_set);
1381
1382 if (!disable_ap_sme) {
1383 wil_dbg_misc(wil, "not supported with AP SME enabled\n");
1384 return -EOPNOTSUPP;
1385 }
1386
1387 if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
1388 return 0;
1389
1390 cid = wil_find_cid(wil, mac);
1391 if (cid < 0) {
1392 wil_err(wil, "station not found\n");
1393 return -ENOLINK;
1394 }
1395
1396 for (i = 0; i < ARRAY_SIZE(wil->vring2cid_tid); i++)
1397 if (wil->vring2cid_tid[i][0] == cid) {
1398 txdata = &wil->vring_tx_data[i];
1399 break;
1400 }
1401
1402 if (!txdata) {
1403 wil_err(wil, "vring data not found\n");
1404 return -ENOLINK;
1405 }
1406
1407 authorize = params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED);
1408 txdata->dot1x_open = authorize ? 1 : 0;
1409 wil_dbg_misc(wil, "cid %d vring %d authorize %d\n", cid, i,
1410 txdata->dot1x_open);
1411
1412 return 0;
1413}
1414
Vladimir Kondratiev40822a92015-01-25 10:52:50 +02001415/* probe_client handling */
1416static void wil_probe_client_handle(struct wil6210_priv *wil,
1417 struct wil_probe_client_req *req)
1418{
1419 struct net_device *ndev = wil_to_ndev(wil);
1420 struct wil_sta_info *sta = &wil->sta[req->cid];
1421 /* assume STA is alive if it is still connected,
1422 * else FW will disconnect it
1423 */
1424 bool alive = (sta->status == wil_sta_connected);
1425
1426 cfg80211_probe_status(ndev, sta->addr, req->cookie, alive, GFP_KERNEL);
1427}
1428
1429static struct list_head *next_probe_client(struct wil6210_priv *wil)
1430{
1431 struct list_head *ret = NULL;
1432
1433 mutex_lock(&wil->probe_client_mutex);
1434
1435 if (!list_empty(&wil->probe_client_pending)) {
1436 ret = wil->probe_client_pending.next;
1437 list_del(ret);
1438 }
1439
1440 mutex_unlock(&wil->probe_client_mutex);
1441
1442 return ret;
1443}
1444
1445void wil_probe_client_worker(struct work_struct *work)
1446{
1447 struct wil6210_priv *wil = container_of(work, struct wil6210_priv,
1448 probe_client_worker);
1449 struct wil_probe_client_req *req;
1450 struct list_head *lh;
1451
1452 while ((lh = next_probe_client(wil)) != NULL) {
1453 req = list_entry(lh, struct wil_probe_client_req, list);
1454
1455 wil_probe_client_handle(wil, req);
1456 kfree(req);
1457 }
1458}
1459
1460void wil_probe_client_flush(struct wil6210_priv *wil)
1461{
1462 struct wil_probe_client_req *req, *t;
1463
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001464 wil_dbg_misc(wil, "probe_client_flush\n");
Vladimir Kondratiev40822a92015-01-25 10:52:50 +02001465
1466 mutex_lock(&wil->probe_client_mutex);
1467
1468 list_for_each_entry_safe(req, t, &wil->probe_client_pending, list) {
1469 list_del(&req->list);
1470 kfree(req);
1471 }
1472
1473 mutex_unlock(&wil->probe_client_mutex);
1474}
1475
1476static int wil_cfg80211_probe_client(struct wiphy *wiphy,
1477 struct net_device *dev,
1478 const u8 *peer, u64 *cookie)
1479{
1480 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1481 struct wil_probe_client_req *req;
1482 int cid = wil_find_cid(wil, peer);
1483
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001484 wil_dbg_misc(wil, "probe_client: %pM => CID %d\n", peer, cid);
Vladimir Kondratiev40822a92015-01-25 10:52:50 +02001485
1486 if (cid < 0)
1487 return -ENOLINK;
1488
1489 req = kzalloc(sizeof(*req), GFP_KERNEL);
1490 if (!req)
1491 return -ENOMEM;
1492
1493 req->cid = cid;
1494 req->cookie = cid;
1495
1496 mutex_lock(&wil->probe_client_mutex);
1497 list_add_tail(&req->list, &wil->probe_client_pending);
1498 mutex_unlock(&wil->probe_client_mutex);
1499
1500 *cookie = req->cookie;
1501 queue_work(wil->wq_service, &wil->probe_client_worker);
1502 return 0;
1503}
1504
Vladimir Kondratiev02beaf12015-03-08 15:42:03 +02001505static int wil_cfg80211_change_bss(struct wiphy *wiphy,
1506 struct net_device *dev,
1507 struct bss_parameters *params)
1508{
1509 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1510
1511 if (params->ap_isolate >= 0) {
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001512 wil_dbg_misc(wil, "change_bss: ap_isolate %d => %d\n",
Vladimir Kondratiev02beaf12015-03-08 15:42:03 +02001513 wil->ap_isolate, params->ap_isolate);
1514 wil->ap_isolate = params->ap_isolate;
1515 }
1516
1517 return 0;
1518}
1519
Lior David4332cac2016-03-01 19:18:13 +02001520static int wil_cfg80211_start_p2p_device(struct wiphy *wiphy,
1521 struct wireless_dev *wdev)
1522{
1523 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1524
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001525 wil_dbg_misc(wil, "start_p2p_device: entered\n");
Lior Davideb57a5b2016-06-08 20:07:48 +03001526 wil->p2p.p2p_dev_started = 1;
Lior David4332cac2016-03-01 19:18:13 +02001527 return 0;
1528}
1529
1530static void wil_cfg80211_stop_p2p_device(struct wiphy *wiphy,
1531 struct wireless_dev *wdev)
1532{
1533 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
Maya Erezd35c2b62016-08-18 16:52:14 +03001534 struct wil_p2p_info *p2p = &wil->p2p;
1535
1536 if (!p2p->p2p_dev_started)
1537 return;
Lior David4332cac2016-03-01 19:18:13 +02001538
Lazar Alexeiaf3db602017-01-20 13:49:46 +02001539 wil_dbg_misc(wil, "stop_p2p_device: entered\n");
Lior Davideb57a5b2016-06-08 20:07:48 +03001540 mutex_lock(&wil->mutex);
Maya Erez035859a2016-11-23 16:06:42 +02001541 mutex_lock(&wil->p2p_wdev_mutex);
Maya Erezd35c2b62016-08-18 16:52:14 +03001542 wil_p2p_stop_radio_operations(wil);
1543 p2p->p2p_dev_started = 0;
Maya Erez035859a2016-11-23 16:06:42 +02001544 mutex_unlock(&wil->p2p_wdev_mutex);
Lior Davideb57a5b2016-06-08 20:07:48 +03001545 mutex_unlock(&wil->mutex);
Lior David4332cac2016-03-01 19:18:13 +02001546}
1547
Maya Erez2c207eb2016-11-23 16:06:40 +02001548static int wil_cfg80211_set_power_mgmt(struct wiphy *wiphy,
1549 struct net_device *dev,
1550 bool enabled, int timeout)
1551{
1552 struct wil6210_priv *wil = wiphy_to_wil(wiphy);
1553 enum wmi_ps_profile_type ps_profile;
1554 int rc;
1555
1556 if (!test_bit(WMI_FW_CAPABILITY_PS_CONFIG, wil->fw_capabilities)) {
1557 wil_err(wil, "set_power_mgmt not supported\n");
1558 return -EOPNOTSUPP;
1559 }
1560
1561 wil_dbg_misc(wil, "enabled=%d, timeout=%d\n",
1562 enabled, timeout);
1563
1564 if (enabled)
1565 ps_profile = WMI_PS_PROFILE_TYPE_DEFAULT;
1566 else
1567 ps_profile = WMI_PS_PROFILE_TYPE_PS_DISABLED;
1568
1569 rc = wmi_ps_dev_profile_cfg(wil, ps_profile);
1570 if (rc)
1571 wil_err(wil, "wmi_ps_dev_profile_cfg failed (%d)\n", rc);
1572
1573 return rc;
1574}
1575
Bhumika Goyalb59eb962017-01-11 16:32:12 +02001576static const struct cfg80211_ops wil_cfg80211_ops = {
Lior David4332cac2016-03-01 19:18:13 +02001577 .add_virtual_intf = wil_cfg80211_add_iface,
1578 .del_virtual_intf = wil_cfg80211_del_iface,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001579 .scan = wil_cfg80211_scan,
Maya Erez035859a2016-11-23 16:06:42 +02001580 .abort_scan = wil_cfg80211_abort_scan,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001581 .connect = wil_cfg80211_connect,
1582 .disconnect = wil_cfg80211_disconnect,
Lior David3fea18d2016-11-23 16:06:43 +02001583 .set_wiphy_params = wil_cfg80211_set_wiphy_params,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001584 .change_virtual_intf = wil_cfg80211_change_iface,
1585 .get_station = wil_cfg80211_get_station,
Vladimir Kondratievef28afd2014-02-27 16:20:48 +02001586 .dump_station = wil_cfg80211_dump_station,
Vladimir Kondratiev1647f122014-02-27 16:20:40 +02001587 .remain_on_channel = wil_remain_on_channel,
1588 .cancel_remain_on_channel = wil_cancel_remain_on_channel,
1589 .mgmt_tx = wil_cfg80211_mgmt_tx,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001590 .set_monitor_channel = wil_cfg80211_set_channel,
1591 .add_key = wil_cfg80211_add_key,
1592 .del_key = wil_cfg80211_del_key,
1593 .set_default_key = wil_cfg80211_set_default_key,
1594 /* AP mode */
Vladimir Kondratiev1bd922f2014-09-10 16:34:44 +03001595 .change_beacon = wil_cfg80211_change_beacon,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001596 .start_ap = wil_cfg80211_start_ap,
1597 .stop_ap = wil_cfg80211_stop_ap,
Dedy Lansky849a5642017-01-20 13:49:44 +02001598 .add_station = wil_cfg80211_add_station,
Vladimir Kondratiev4d55a0a2014-02-27 16:20:54 +02001599 .del_station = wil_cfg80211_del_station,
Dedy Lansky849a5642017-01-20 13:49:44 +02001600 .change_station = wil_cfg80211_change_station,
Vladimir Kondratiev40822a92015-01-25 10:52:50 +02001601 .probe_client = wil_cfg80211_probe_client,
Vladimir Kondratiev02beaf12015-03-08 15:42:03 +02001602 .change_bss = wil_cfg80211_change_bss,
Lior David4332cac2016-03-01 19:18:13 +02001603 /* P2P device */
1604 .start_p2p_device = wil_cfg80211_start_p2p_device,
1605 .stop_p2p_device = wil_cfg80211_stop_p2p_device,
Maya Erez2c207eb2016-11-23 16:06:40 +02001606 .set_power_mgmt = wil_cfg80211_set_power_mgmt,
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001607};
1608
1609static void wil_wiphy_init(struct wiphy *wiphy)
1610{
Hamad Kadmany8e52fe32015-06-09 14:11:18 +03001611 wiphy->max_scan_ssids = 1;
Vladimir Kondratiev77c91292014-09-10 16:34:47 +03001612 wiphy->max_scan_ie_len = WMI_MAX_IE_LEN;
Dedy Lanskye6d68342016-03-01 19:18:12 +02001613 wiphy->max_remain_on_channel_duration = WIL_MAX_ROC_DURATION_MS;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001614 wiphy->max_num_pmkids = 0 /* TODO: */;
1615 wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
1616 BIT(NL80211_IFTYPE_AP) |
Dedy Lanskye6d68342016-03-01 19:18:12 +02001617 BIT(NL80211_IFTYPE_P2P_CLIENT) |
1618 BIT(NL80211_IFTYPE_P2P_GO) |
Lior David4332cac2016-03-01 19:18:13 +02001619 BIT(NL80211_IFTYPE_P2P_DEVICE) |
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001620 BIT(NL80211_IFTYPE_MONITOR);
Dedy Lansky849a5642017-01-20 13:49:44 +02001621 wiphy->flags |= WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL |
Maya Erez2c207eb2016-11-23 16:06:40 +02001622 WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD |
1623 WIPHY_FLAG_PS_ON_BY_DEFAULT;
Dedy Lansky849a5642017-01-20 13:49:44 +02001624 if (!disable_ap_sme)
1625 wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME;
Vladimir Kondratiev9cf10d62014-09-10 16:34:36 +03001626 dev_dbg(wiphy_dev(wiphy), "%s : flags = 0x%08x\n",
1627 __func__, wiphy->flags);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001628 wiphy->probe_resp_offload =
1629 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS |
1630 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 |
1631 NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P;
1632
Johannes Berg57fbcce2016-04-12 15:56:15 +02001633 wiphy->bands[NL80211_BAND_60GHZ] = &wil_band_60ghz;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001634
1635 /* TODO: figure this out */
Vladimir Kondratievb8b33a32014-02-27 16:20:52 +02001636 wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001637
1638 wiphy->cipher_suites = wil_cipher_suites;
1639 wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites);
1640 wiphy->mgmt_stypes = wil_mgmt_stypes;
Vladimir Kondratiev713c8a22015-01-25 10:52:49 +02001641 wiphy->features |= NL80211_FEATURE_SK_TX_STATUS;
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001642}
1643
1644struct wireless_dev *wil_cfg80211_init(struct device *dev)
1645{
1646 int rc = 0;
1647 struct wireless_dev *wdev;
1648
Vladimir Kondratiev9cf10d62014-09-10 16:34:36 +03001649 dev_dbg(dev, "%s()\n", __func__);
1650
Vladimir Kondratiev8fe59622014-09-10 16:34:34 +03001651 wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001652 if (!wdev)
1653 return ERR_PTR(-ENOMEM);
1654
1655 wdev->wiphy = wiphy_new(&wil_cfg80211_ops,
1656 sizeof(struct wil6210_priv));
1657 if (!wdev->wiphy) {
1658 rc = -ENOMEM;
1659 goto out;
1660 }
1661
1662 set_wiphy_dev(wdev->wiphy, dev);
1663 wil_wiphy_init(wdev->wiphy);
1664
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001665 return wdev;
1666
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001667out:
1668 kfree(wdev);
1669
1670 return ERR_PTR(rc);
1671}
1672
1673void wil_wdev_free(struct wil6210_priv *wil)
1674{
1675 struct wireless_dev *wdev = wil_to_wdev(wil);
1676
Vladimir Kondratiev9cf10d62014-09-10 16:34:36 +03001677 dev_dbg(wil_to_dev(wil), "%s()\n", __func__);
1678
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001679 if (!wdev)
1680 return;
1681
Vladimir Kondratiev2be7d222012-12-20 13:13:19 -08001682 wiphy_free(wdev->wiphy);
1683 kfree(wdev);
1684}
Lior David4332cac2016-03-01 19:18:13 +02001685
1686void wil_p2p_wdev_free(struct wil6210_priv *wil)
1687{
1688 struct wireless_dev *p2p_wdev;
1689
1690 mutex_lock(&wil->p2p_wdev_mutex);
1691 p2p_wdev = wil->p2p_wdev;
Maya Erezd35c2b62016-08-18 16:52:14 +03001692 wil->p2p_wdev = NULL;
1693 wil->radio_wdev = wil_to_wdev(wil);
1694 mutex_unlock(&wil->p2p_wdev_mutex);
Lior David4332cac2016-03-01 19:18:13 +02001695 if (p2p_wdev) {
Lior David4332cac2016-03-01 19:18:13 +02001696 cfg80211_unregister_wdev(p2p_wdev);
1697 kfree(p2p_wdev);
1698 }
Lior David4332cac2016-03-01 19:18:13 +02001699}