blob: 6f3a3ad0cb7c387c43a1197ea4b578f8bc11ead5 [file] [log] [blame]
Arik Nemtsov95224fe2014-05-01 10:17:28 +03001/*
2 * mac80211 TDLS handling code
3 *
4 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
5 * Copyright 2014, Intel Corporation
6 *
7 * This file is GPLv2 as found in COPYING.
8 */
9
10#include <linux/ieee80211.h>
Arik Nemtsovc887f0d32014-06-11 17:18:25 +030011#include <net/cfg80211.h>
Arik Nemtsov95224fe2014-05-01 10:17:28 +030012#include "ieee80211_i.h"
13
Arik Nemtsov17e6a592014-06-11 17:18:20 +030014/* give usermode some time for retries in setting up the TDLS session */
15#define TDLS_PEER_SETUP_TIMEOUT (15 * HZ)
16
17void ieee80211_tdls_peer_del_work(struct work_struct *wk)
18{
19 struct ieee80211_sub_if_data *sdata;
20 struct ieee80211_local *local;
21
22 sdata = container_of(wk, struct ieee80211_sub_if_data,
23 tdls_peer_del_work.work);
24 local = sdata->local;
25
26 mutex_lock(&local->mtx);
27 if (!is_zero_ether_addr(sdata->tdls_peer)) {
28 tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer);
29 sta_info_destroy_addr(sdata, sdata->tdls_peer);
30 eth_zero_addr(sdata->tdls_peer);
31 }
32 mutex_unlock(&local->mtx);
33}
34
Arik Nemtsov95224fe2014-05-01 10:17:28 +030035static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
36{
37 u8 *pos = (void *)skb_put(skb, 7);
38
39 *pos++ = WLAN_EID_EXT_CAPABILITY;
40 *pos++ = 5; /* len */
41 *pos++ = 0x0;
42 *pos++ = 0x0;
43 *pos++ = 0x0;
44 *pos++ = 0x0;
45 *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
46}
47
48static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
49{
50 struct ieee80211_local *local = sdata->local;
51 u16 capab;
52
53 capab = 0;
54 if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ)
55 return capab;
56
57 if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
58 capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
59 if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
60 capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
61
62 return capab;
63}
64
Johannes Berg3b3a0162014-05-19 17:19:31 +020065static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr,
66 const u8 *peer, const u8 *bssid)
Arik Nemtsov95224fe2014-05-01 10:17:28 +030067{
68 struct ieee80211_tdls_lnkie *lnkid;
69
70 lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
71
72 lnkid->ie_type = WLAN_EID_LINK_ID;
73 lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
74
75 memcpy(lnkid->bssid, bssid, ETH_ALEN);
76 memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
77 memcpy(lnkid->resp_sta, peer, ETH_ALEN);
78}
79
80static int
81ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
Johannes Berg3b3a0162014-05-19 17:19:31 +020082 const u8 *peer, u8 action_code, u8 dialog_token,
Arik Nemtsov95224fe2014-05-01 10:17:28 +030083 u16 status_code, struct sk_buff *skb)
84{
85 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
86 enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
87 struct ieee80211_tdls_data *tf;
88
89 tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
90
91 memcpy(tf->da, peer, ETH_ALEN);
92 memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
93 tf->ether_type = cpu_to_be16(ETH_P_TDLS);
94 tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
95
96 switch (action_code) {
97 case WLAN_TDLS_SETUP_REQUEST:
98 tf->category = WLAN_CATEGORY_TDLS;
99 tf->action_code = WLAN_TDLS_SETUP_REQUEST;
100
101 skb_put(skb, sizeof(tf->u.setup_req));
102 tf->u.setup_req.dialog_token = dialog_token;
103 tf->u.setup_req.capability =
104 cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
105
106 ieee80211_add_srates_ie(sdata, skb, false, band);
107 ieee80211_add_ext_srates_ie(sdata, skb, false, band);
108 ieee80211_tdls_add_ext_capab(skb);
109 break;
110 case WLAN_TDLS_SETUP_RESPONSE:
111 tf->category = WLAN_CATEGORY_TDLS;
112 tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
113
114 skb_put(skb, sizeof(tf->u.setup_resp));
115 tf->u.setup_resp.status_code = cpu_to_le16(status_code);
116 tf->u.setup_resp.dialog_token = dialog_token;
117 tf->u.setup_resp.capability =
118 cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
119
120 ieee80211_add_srates_ie(sdata, skb, false, band);
121 ieee80211_add_ext_srates_ie(sdata, skb, false, band);
122 ieee80211_tdls_add_ext_capab(skb);
123 break;
124 case WLAN_TDLS_SETUP_CONFIRM:
125 tf->category = WLAN_CATEGORY_TDLS;
126 tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
127
128 skb_put(skb, sizeof(tf->u.setup_cfm));
129 tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
130 tf->u.setup_cfm.dialog_token = dialog_token;
131 break;
132 case WLAN_TDLS_TEARDOWN:
133 tf->category = WLAN_CATEGORY_TDLS;
134 tf->action_code = WLAN_TDLS_TEARDOWN;
135
136 skb_put(skb, sizeof(tf->u.teardown));
137 tf->u.teardown.reason_code = cpu_to_le16(status_code);
138 break;
139 case WLAN_TDLS_DISCOVERY_REQUEST:
140 tf->category = WLAN_CATEGORY_TDLS;
141 tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
142
143 skb_put(skb, sizeof(tf->u.discover_req));
144 tf->u.discover_req.dialog_token = dialog_token;
145 break;
146 default:
147 return -EINVAL;
148 }
149
150 return 0;
151}
152
153static int
154ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
Johannes Berg3b3a0162014-05-19 17:19:31 +0200155 const u8 *peer, u8 action_code, u8 dialog_token,
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300156 u16 status_code, struct sk_buff *skb)
157{
158 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
159 enum ieee80211_band band = ieee80211_get_sdata_band(sdata);
160 struct ieee80211_mgmt *mgmt;
161
162 mgmt = (void *)skb_put(skb, 24);
163 memset(mgmt, 0, 24);
164 memcpy(mgmt->da, peer, ETH_ALEN);
165 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
166 memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
167
168 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
169 IEEE80211_STYPE_ACTION);
170
171 switch (action_code) {
172 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
173 skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
174 mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
175 mgmt->u.action.u.tdls_discover_resp.action_code =
176 WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
177 mgmt->u.action.u.tdls_discover_resp.dialog_token =
178 dialog_token;
179 mgmt->u.action.u.tdls_discover_resp.capability =
180 cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
181
182 ieee80211_add_srates_ie(sdata, skb, false, band);
183 ieee80211_add_ext_srates_ie(sdata, skb, false, band);
184 ieee80211_tdls_add_ext_capab(skb);
185 break;
186 default:
187 return -EINVAL;
188 }
189
190 return 0;
191}
192
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300193static int
194ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
195 const u8 *peer, u8 action_code,
196 u8 dialog_token, u16 status_code,
Arik Nemtsov2fb6b9b2014-06-11 17:18:22 +0300197 u32 peer_capability, bool initiator,
198 const u8 *extra_ies, size_t extra_ies_len)
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300199{
200 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
201 struct ieee80211_local *local = sdata->local;
202 struct sk_buff *skb = NULL;
203 bool send_direct;
Arik Nemtsov2fb6b9b2014-06-11 17:18:22 +0300204 const u8 *init_addr, *rsp_addr;
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300205 int ret;
206
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300207 skb = dev_alloc_skb(local->hw.extra_tx_headroom +
208 max(sizeof(struct ieee80211_mgmt),
209 sizeof(struct ieee80211_tdls_data)) +
210 50 + /* supported rates */
211 7 + /* ext capab */
212 extra_ies_len +
213 sizeof(struct ieee80211_tdls_lnkie));
214 if (!skb)
215 return -ENOMEM;
216
217 skb_reserve(skb, local->hw.extra_tx_headroom);
218
219 switch (action_code) {
220 case WLAN_TDLS_SETUP_REQUEST:
221 case WLAN_TDLS_SETUP_RESPONSE:
222 case WLAN_TDLS_SETUP_CONFIRM:
223 case WLAN_TDLS_TEARDOWN:
224 case WLAN_TDLS_DISCOVERY_REQUEST:
225 ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
226 action_code, dialog_token,
227 status_code, skb);
228 send_direct = false;
229 break;
230 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
231 ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
232 dialog_token, status_code,
233 skb);
234 send_direct = true;
235 break;
236 default:
237 ret = -ENOTSUPP;
238 break;
239 }
240
241 if (ret < 0)
242 goto fail;
243
244 if (extra_ies_len)
245 memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
246
Arik Nemtsov2fb6b9b2014-06-11 17:18:22 +0300247 /* sanity check for initiator */
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300248 switch (action_code) {
249 case WLAN_TDLS_SETUP_REQUEST:
250 case WLAN_TDLS_SETUP_CONFIRM:
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300251 case WLAN_TDLS_DISCOVERY_REQUEST:
Arik Nemtsov2fb6b9b2014-06-11 17:18:22 +0300252 if (!initiator) {
253 ret = -EINVAL;
254 goto fail;
255 }
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300256 break;
257 case WLAN_TDLS_SETUP_RESPONSE:
258 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
Arik Nemtsov2fb6b9b2014-06-11 17:18:22 +0300259 if (initiator) {
260 ret = -EINVAL;
261 goto fail;
262 }
263 break;
264 case WLAN_TDLS_TEARDOWN:
265 /* any value is ok */
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300266 break;
267 default:
268 ret = -ENOTSUPP;
269 goto fail;
270 }
271
Arik Nemtsov2fb6b9b2014-06-11 17:18:22 +0300272 if (initiator) {
273 init_addr = sdata->vif.addr;
274 rsp_addr = peer;
275 } else {
276 init_addr = peer;
277 rsp_addr = sdata->vif.addr;
278 }
279
280 ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr,
281 sdata->u.mgd.bssid);
282
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300283 if (send_direct) {
284 ieee80211_tx_skb(sdata, skb);
285 return 0;
286 }
287
288 /*
289 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
290 * we should default to AC_VI.
291 */
292 switch (action_code) {
293 case WLAN_TDLS_SETUP_REQUEST:
294 case WLAN_TDLS_SETUP_RESPONSE:
295 skb_set_queue_mapping(skb, IEEE80211_AC_BK);
296 skb->priority = 2;
297 break;
298 default:
299 skb_set_queue_mapping(skb, IEEE80211_AC_VI);
300 skb->priority = 5;
301 break;
302 }
303
304 /* disable bottom halves when entering the Tx path */
305 local_bh_disable();
306 ret = ieee80211_subif_start_xmit(skb, dev);
307 local_bh_enable();
308
309 return ret;
310
311fail:
312 dev_kfree_skb(skb);
313 return ret;
314}
315
Arik Nemtsov191dd462014-06-11 17:18:23 +0300316static int
317ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
318 const u8 *peer, u8 action_code, u8 dialog_token,
319 u16 status_code, u32 peer_capability, bool initiator,
320 const u8 *extra_ies, size_t extra_ies_len)
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300321{
322 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
323 struct ieee80211_local *local = sdata->local;
324 int ret;
325
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300326 mutex_lock(&local->mtx);
327
328 /* we don't support concurrent TDLS peer setups */
329 if (!is_zero_ether_addr(sdata->tdls_peer) &&
Arik Nemtsov191dd462014-06-11 17:18:23 +0300330 !ether_addr_equal(sdata->tdls_peer, peer)) {
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300331 ret = -EBUSY;
332 goto exit;
333 }
334
Arik Nemtsovdb67d662014-06-11 17:18:24 +0300335 ieee80211_flush_queues(local, sdata);
336
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300337 ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
338 dialog_token, status_code,
Arik Nemtsov2fb6b9b2014-06-11 17:18:22 +0300339 peer_capability, initiator,
340 extra_ies, extra_ies_len);
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300341 if (ret < 0)
342 goto exit;
343
Arik Nemtsov191dd462014-06-11 17:18:23 +0300344 memcpy(sdata->tdls_peer, peer, ETH_ALEN);
345 ieee80211_queue_delayed_work(&sdata->local->hw,
346 &sdata->tdls_peer_del_work,
347 TDLS_PEER_SETUP_TIMEOUT);
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300348
349exit:
350 mutex_unlock(&local->mtx);
Arik Nemtsov191dd462014-06-11 17:18:23 +0300351 return ret;
352}
353
Arik Nemtsovdb67d662014-06-11 17:18:24 +0300354static int
355ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
356 const u8 *peer, u8 action_code, u8 dialog_token,
357 u16 status_code, u32 peer_capability,
358 bool initiator, const u8 *extra_ies,
359 size_t extra_ies_len)
360{
361 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
362 struct ieee80211_local *local = sdata->local;
363 struct sta_info *sta;
364 int ret;
365
366 /*
367 * No packets can be transmitted to the peer via the AP during setup -
368 * the STA is set as a TDLS peer, but is not authorized.
369 * During teardown, we prevent direct transmissions by stopping the
370 * queues and flushing all direct packets.
371 */
372 ieee80211_stop_vif_queues(local, sdata,
373 IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
374 ieee80211_flush_queues(local, sdata);
375
376 ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
377 dialog_token, status_code,
378 peer_capability, initiator,
379 extra_ies, extra_ies_len);
380 if (ret < 0)
381 sdata_err(sdata, "Failed sending TDLS teardown packet %d\n",
382 ret);
383
384 /*
385 * Remove the STA AUTH flag to force further traffic through the AP. If
386 * the STA was unreachable, it was already removed.
387 */
388 rcu_read_lock();
389 sta = sta_info_get(sdata, peer);
390 if (sta)
391 clear_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
392 rcu_read_unlock();
393
394 ieee80211_wake_vif_queues(local, sdata,
395 IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
396
397 return 0;
398}
399
Arik Nemtsov191dd462014-06-11 17:18:23 +0300400int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
401 const u8 *peer, u8 action_code, u8 dialog_token,
402 u16 status_code, u32 peer_capability,
403 bool initiator, const u8 *extra_ies,
404 size_t extra_ies_len)
405{
406 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
407 int ret;
408
409 if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
410 return -ENOTSUPP;
411
412 /* make sure we are in managed mode, and associated */
413 if (sdata->vif.type != NL80211_IFTYPE_STATION ||
414 !sdata->u.mgd.associated)
415 return -EINVAL;
416
417 switch (action_code) {
418 case WLAN_TDLS_SETUP_REQUEST:
419 case WLAN_TDLS_SETUP_RESPONSE:
420 ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code,
421 dialog_token, status_code,
422 peer_capability, initiator,
423 extra_ies, extra_ies_len);
424 break;
425 case WLAN_TDLS_TEARDOWN:
Arik Nemtsovdb67d662014-06-11 17:18:24 +0300426 ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer,
427 action_code, dialog_token,
428 status_code,
429 peer_capability, initiator,
430 extra_ies, extra_ies_len);
431 break;
Arik Nemtsov191dd462014-06-11 17:18:23 +0300432 case WLAN_TDLS_SETUP_CONFIRM:
433 case WLAN_TDLS_DISCOVERY_REQUEST:
434 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
435 /* no special handling */
436 ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
437 action_code,
438 dialog_token,
439 status_code,
440 peer_capability,
441 initiator, extra_ies,
442 extra_ies_len);
443 break;
444 default:
445 ret = -EOPNOTSUPP;
446 break;
447 }
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300448
449 tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n",
450 action_code, peer, ret);
451 return ret;
452}
453
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300454int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
Johannes Berg3b3a0162014-05-19 17:19:31 +0200455 const u8 *peer, enum nl80211_tdls_operation oper)
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300456{
457 struct sta_info *sta;
458 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300459 struct ieee80211_local *local = sdata->local;
460 int ret;
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300461
462 if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
463 return -ENOTSUPP;
464
465 if (sdata->vif.type != NL80211_IFTYPE_STATION)
466 return -EINVAL;
467
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300468 switch (oper) {
469 case NL80211_TDLS_ENABLE_LINK:
470 case NL80211_TDLS_DISABLE_LINK:
471 break;
472 case NL80211_TDLS_TEARDOWN:
473 case NL80211_TDLS_SETUP:
474 case NL80211_TDLS_DISCOVERY_REQ:
475 /* We don't support in-driver setup/teardown/discovery */
476 return -ENOTSUPP;
477 }
478
479 mutex_lock(&local->mtx);
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300480 tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer);
481
482 switch (oper) {
483 case NL80211_TDLS_ENABLE_LINK:
484 rcu_read_lock();
485 sta = sta_info_get(sdata, peer);
486 if (!sta) {
487 rcu_read_unlock();
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300488 ret = -ENOLINK;
489 break;
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300490 }
491
492 set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH);
493 rcu_read_unlock();
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300494
495 WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) ||
496 !ether_addr_equal(sdata->tdls_peer, peer));
497 ret = 0;
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300498 break;
499 case NL80211_TDLS_DISABLE_LINK:
Arik Nemtsovdb67d662014-06-11 17:18:24 +0300500 /* flush a potentially queued teardown packet */
501 ieee80211_flush_queues(local, sdata);
502
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300503 ret = sta_info_destroy_addr(sdata, peer);
504 break;
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300505 default:
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300506 ret = -ENOTSUPP;
507 break;
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300508 }
509
Arik Nemtsov17e6a592014-06-11 17:18:20 +0300510 if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) {
511 cancel_delayed_work(&sdata->tdls_peer_del_work);
512 eth_zero_addr(sdata->tdls_peer);
513 }
514
515 mutex_unlock(&local->mtx);
516 return ret;
Arik Nemtsov95224fe2014-05-01 10:17:28 +0300517}
Arik Nemtsovc887f0d32014-06-11 17:18:25 +0300518
519void ieee80211_tdls_oper_request(struct ieee80211_vif *vif, const u8 *peer,
520 enum nl80211_tdls_operation oper,
521 u16 reason_code, gfp_t gfp)
522{
523 struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
524
525 if (vif->type != NL80211_IFTYPE_STATION || !vif->bss_conf.assoc) {
526 sdata_err(sdata, "Discarding TDLS oper %d - not STA or disconnected\n",
527 oper);
528 return;
529 }
530
531 cfg80211_tdls_oper_request(sdata->dev, peer, oper, reason_code, gfp);
532}
533EXPORT_SYMBOL(ieee80211_tdls_oper_request);