diff options
Diffstat (limited to 'drivers/net/wireless/mwifiex/tdls.c')
-rw-r--r-- | drivers/net/wireless/mwifiex/tdls.c | 1044 |
1 files changed, 1044 insertions, 0 deletions
diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c new file mode 100644 index 000000000000..97662a1ba58c --- /dev/null +++ b/drivers/net/wireless/mwifiex/tdls.c | |||
@@ -0,0 +1,1044 @@ | |||
1 | /* Marvell Wireless LAN device driver: TDLS handling | ||
2 | * | ||
3 | * Copyright (C) 2014, Marvell International Ltd. | ||
4 | * | ||
5 | * This software file (the "File") is distributed by Marvell International | ||
6 | * Ltd. under the terms of the GNU General Public License Version 2, June 1991 | ||
7 | * (the "License"). You may use, redistribute and/or modify this File in | ||
8 | * accordance with the terms and conditions of the License, a copy of which | ||
9 | * is available on the worldwide web at | ||
10 | * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. | ||
11 | * | ||
12 | * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE | ||
13 | * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE | ||
14 | * ARE EXPRESSLY DISCLAIMED. The License provides additional details about | ||
15 | * this warranty disclaimer. | ||
16 | */ | ||
17 | |||
18 | #include "main.h" | ||
19 | #include "wmm.h" | ||
20 | #include "11n.h" | ||
21 | #include "11n_rxreorder.h" | ||
22 | #include "11ac.h" | ||
23 | |||
24 | #define TDLS_REQ_FIX_LEN 6 | ||
25 | #define TDLS_RESP_FIX_LEN 8 | ||
26 | #define TDLS_CONFIRM_FIX_LEN 6 | ||
27 | |||
28 | static void | ||
29 | mwifiex_restore_tdls_packets(struct mwifiex_private *priv, u8 *mac, u8 status) | ||
30 | { | ||
31 | struct mwifiex_ra_list_tbl *ra_list; | ||
32 | struct list_head *tid_list; | ||
33 | struct sk_buff *skb, *tmp; | ||
34 | struct mwifiex_txinfo *tx_info; | ||
35 | unsigned long flags; | ||
36 | u32 tid; | ||
37 | u8 tid_down; | ||
38 | |||
39 | dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac); | ||
40 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
41 | |||
42 | skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { | ||
43 | if (!ether_addr_equal(mac, skb->data)) | ||
44 | continue; | ||
45 | |||
46 | __skb_unlink(skb, &priv->tdls_txq); | ||
47 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
48 | tid = skb->priority; | ||
49 | tid_down = mwifiex_wmm_downgrade_tid(priv, tid); | ||
50 | |||
51 | if (status == TDLS_SETUP_COMPLETE) { | ||
52 | ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); | ||
53 | ra_list->tdls_link = true; | ||
54 | tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; | ||
55 | } else { | ||
56 | tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; | ||
57 | if (!list_empty(tid_list)) | ||
58 | ra_list = list_first_entry(tid_list, | ||
59 | struct mwifiex_ra_list_tbl, list); | ||
60 | else | ||
61 | ra_list = NULL; | ||
62 | tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; | ||
63 | } | ||
64 | |||
65 | if (!ra_list) { | ||
66 | mwifiex_write_data_complete(priv->adapter, skb, 0, -1); | ||
67 | continue; | ||
68 | } | ||
69 | |||
70 | skb_queue_tail(&ra_list->skb_head, skb); | ||
71 | |||
72 | ra_list->ba_pkt_count++; | ||
73 | ra_list->total_pkt_count++; | ||
74 | |||
75 | if (atomic_read(&priv->wmm.highest_queued_prio) < | ||
76 | tos_to_tid_inv[tid_down]) | ||
77 | atomic_set(&priv->wmm.highest_queued_prio, | ||
78 | tos_to_tid_inv[tid_down]); | ||
79 | |||
80 | atomic_inc(&priv->wmm.tx_pkts_queued); | ||
81 | } | ||
82 | |||
83 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, u8 *mac) | ||
88 | { | ||
89 | struct mwifiex_ra_list_tbl *ra_list; | ||
90 | struct list_head *ra_list_head; | ||
91 | struct sk_buff *skb, *tmp; | ||
92 | unsigned long flags; | ||
93 | int i; | ||
94 | |||
95 | dev_dbg(priv->adapter->dev, "%s: %pM\n", __func__, mac); | ||
96 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); | ||
97 | |||
98 | for (i = 0; i < MAX_NUM_TID; i++) { | ||
99 | if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { | ||
100 | ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; | ||
101 | list_for_each_entry(ra_list, ra_list_head, list) { | ||
102 | skb_queue_walk_safe(&ra_list->skb_head, skb, | ||
103 | tmp) { | ||
104 | if (!ether_addr_equal(mac, skb->data)) | ||
105 | continue; | ||
106 | __skb_unlink(skb, &ra_list->skb_head); | ||
107 | atomic_dec(&priv->wmm.tx_pkts_queued); | ||
108 | ra_list->total_pkt_count--; | ||
109 | skb_queue_tail(&priv->tdls_txq, skb); | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | ||
116 | return; | ||
117 | } | ||
118 | |||
119 | /* This function appends rate TLV to scan config command. */ | ||
120 | static int | ||
121 | mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, | ||
122 | struct sk_buff *skb) | ||
123 | { | ||
124 | u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; | ||
125 | u16 rates_size, supp_rates_size, ext_rates_size; | ||
126 | |||
127 | memset(rates, 0, sizeof(rates)); | ||
128 | rates_size = mwifiex_get_supported_rates(priv, rates); | ||
129 | |||
130 | supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); | ||
131 | |||
132 | if (skb_tailroom(skb) < rates_size + 4) { | ||
133 | dev_err(priv->adapter->dev, | ||
134 | "Insuffient space while adding rates\n"); | ||
135 | return -ENOMEM; | ||
136 | } | ||
137 | |||
138 | pos = skb_put(skb, supp_rates_size + 2); | ||
139 | *pos++ = WLAN_EID_SUPP_RATES; | ||
140 | *pos++ = supp_rates_size; | ||
141 | memcpy(pos, rates, supp_rates_size); | ||
142 | |||
143 | if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { | ||
144 | ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; | ||
145 | pos = skb_put(skb, ext_rates_size + 2); | ||
146 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | ||
147 | *pos++ = ext_rates_size; | ||
148 | memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, | ||
149 | ext_rates_size); | ||
150 | } | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, | ||
156 | struct sk_buff *skb) | ||
157 | { | ||
158 | struct ieee_types_assoc_rsp *assoc_rsp; | ||
159 | u8 *pos; | ||
160 | |||
161 | assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; | ||
162 | pos = (void *)skb_put(skb, 4); | ||
163 | *pos++ = WLAN_EID_AID; | ||
164 | *pos++ = 2; | ||
165 | *pos++ = le16_to_cpu(assoc_rsp->a_id); | ||
166 | |||
167 | return; | ||
168 | } | ||
169 | |||
170 | static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, | ||
171 | struct sk_buff *skb) | ||
172 | { | ||
173 | struct ieee80211_vht_cap vht_cap; | ||
174 | u8 *pos; | ||
175 | |||
176 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); | ||
177 | *pos++ = WLAN_EID_VHT_CAPABILITY; | ||
178 | *pos++ = sizeof(struct ieee80211_vht_cap); | ||
179 | |||
180 | memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); | ||
181 | |||
182 | mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); | ||
183 | memcpy(pos, &vht_cap, sizeof(vht_cap)); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, | ||
189 | u8 *mac, struct sk_buff *skb) | ||
190 | { | ||
191 | struct mwifiex_bssdescriptor *bss_desc; | ||
192 | struct ieee80211_vht_operation *vht_oper; | ||
193 | struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; | ||
194 | struct mwifiex_sta_node *sta_ptr; | ||
195 | struct mwifiex_adapter *adapter = priv->adapter; | ||
196 | u8 supp_chwd_set, peer_supp_chwd_set; | ||
197 | u8 *pos, ap_supp_chwd_set, chan_bw; | ||
198 | u16 mcs_map_user, mcs_map_resp, mcs_map_result; | ||
199 | u16 mcs_user, mcs_resp, nss; | ||
200 | u32 usr_vht_cap_info; | ||
201 | |||
202 | bss_desc = &priv->curr_bss_params.bss_descriptor; | ||
203 | |||
204 | sta_ptr = mwifiex_get_sta_entry(priv, mac); | ||
205 | if (unlikely(!sta_ptr)) { | ||
206 | dev_warn(adapter->dev, "TDLS peer station not found in list\n"); | ||
207 | return -1; | ||
208 | } | ||
209 | |||
210 | if (!mwifiex_is_bss_in_11ac_mode(priv)) { | ||
211 | if (sta_ptr->tdls_cap.extcap.ext_capab[7] & | ||
212 | WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { | ||
213 | dev_dbg(adapter->dev, | ||
214 | "TDLS peer doesn't support wider bandwitdh\n"); | ||
215 | return 0; | ||
216 | } | ||
217 | } else { | ||
218 | ap_vht_cap = bss_desc->bcn_vht_cap; | ||
219 | } | ||
220 | |||
221 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); | ||
222 | *pos++ = WLAN_EID_VHT_OPERATION; | ||
223 | *pos++ = sizeof(struct ieee80211_vht_operation); | ||
224 | vht_oper = (struct ieee80211_vht_operation *)pos; | ||
225 | |||
226 | if (bss_desc->bss_band & BAND_A) | ||
227 | usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; | ||
228 | else | ||
229 | usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; | ||
230 | |||
231 | /* find the minmum bandwith between AP/TDLS peers */ | ||
232 | vht_cap = &sta_ptr->tdls_cap.vhtcap; | ||
233 | supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); | ||
234 | peer_supp_chwd_set = | ||
235 | GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); | ||
236 | supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); | ||
237 | |||
238 | /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ | ||
239 | |||
240 | if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & | ||
241 | WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { | ||
242 | ap_supp_chwd_set = | ||
243 | GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); | ||
244 | supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); | ||
245 | } | ||
246 | |||
247 | switch (supp_chwd_set) { | ||
248 | case IEEE80211_VHT_CHANWIDTH_80MHZ: | ||
249 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; | ||
250 | break; | ||
251 | case IEEE80211_VHT_CHANWIDTH_160MHZ: | ||
252 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; | ||
253 | break; | ||
254 | case IEEE80211_VHT_CHANWIDTH_80P80MHZ: | ||
255 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; | ||
256 | break; | ||
257 | default: | ||
258 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; | ||
259 | break; | ||
260 | } | ||
261 | |||
262 | mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); | ||
263 | mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); | ||
264 | mcs_map_result = 0; | ||
265 | |||
266 | for (nss = 1; nss <= 8; nss++) { | ||
267 | mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); | ||
268 | mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); | ||
269 | |||
270 | if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || | ||
271 | (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) | ||
272 | SET_VHTNSSMCS(mcs_map_result, nss, | ||
273 | IEEE80211_VHT_MCS_NOT_SUPPORTED); | ||
274 | else | ||
275 | SET_VHTNSSMCS(mcs_map_result, nss, | ||
276 | min_t(u16, mcs_user, mcs_resp)); | ||
277 | } | ||
278 | |||
279 | vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); | ||
280 | |||
281 | switch (vht_oper->chan_width) { | ||
282 | case IEEE80211_VHT_CHANWIDTH_80MHZ: | ||
283 | chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; | ||
284 | break; | ||
285 | case IEEE80211_VHT_CHANWIDTH_160MHZ: | ||
286 | chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; | ||
287 | break; | ||
288 | case IEEE80211_VHT_CHANWIDTH_80P80MHZ: | ||
289 | chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; | ||
290 | break; | ||
291 | default: | ||
292 | chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; | ||
293 | break; | ||
294 | } | ||
295 | vht_oper->center_freq_seg1_idx = | ||
296 | mwifiex_get_center_freq_index(priv, BAND_AAC, | ||
297 | bss_desc->channel, | ||
298 | chan_bw); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, | ||
304 | struct sk_buff *skb) | ||
305 | { | ||
306 | struct ieee_types_extcap *extcap; | ||
307 | |||
308 | extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); | ||
309 | extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; | ||
310 | extcap->ieee_hdr.len = 8; | ||
311 | memset(extcap->ext_capab, 0, 8); | ||
312 | extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; | ||
313 | |||
314 | if (priv->adapter->is_hw_11ac_capable) | ||
315 | extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; | ||
316 | } | ||
317 | |||
318 | static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) | ||
319 | { | ||
320 | u8 *pos = (void *)skb_put(skb, 3); | ||
321 | |||
322 | *pos++ = WLAN_EID_QOS_CAPA; | ||
323 | *pos++ = 1; | ||
324 | *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; | ||
325 | } | ||
326 | |||
327 | static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, | ||
328 | u8 *peer, u8 action_code, u8 dialog_token, | ||
329 | u16 status_code, struct sk_buff *skb) | ||
330 | { | ||
331 | struct ieee80211_tdls_data *tf; | ||
332 | int ret; | ||
333 | u16 capab; | ||
334 | struct ieee80211_ht_cap *ht_cap; | ||
335 | u8 radio, *pos; | ||
336 | |||
337 | capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; | ||
338 | |||
339 | tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); | ||
340 | memcpy(tf->da, peer, ETH_ALEN); | ||
341 | memcpy(tf->sa, priv->curr_addr, ETH_ALEN); | ||
342 | tf->ether_type = cpu_to_be16(ETH_P_TDLS); | ||
343 | tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; | ||
344 | |||
345 | switch (action_code) { | ||
346 | case WLAN_TDLS_SETUP_REQUEST: | ||
347 | tf->category = WLAN_CATEGORY_TDLS; | ||
348 | tf->action_code = WLAN_TDLS_SETUP_REQUEST; | ||
349 | skb_put(skb, sizeof(tf->u.setup_req)); | ||
350 | tf->u.setup_req.dialog_token = dialog_token; | ||
351 | tf->u.setup_req.capability = cpu_to_le16(capab); | ||
352 | ret = mwifiex_tdls_append_rates_ie(priv, skb); | ||
353 | if (ret) { | ||
354 | dev_kfree_skb_any(skb); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | ||
359 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
360 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
361 | ht_cap = (void *)pos; | ||
362 | radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | ||
363 | ret = mwifiex_fill_cap_info(priv, radio, ht_cap); | ||
364 | if (ret) { | ||
365 | dev_kfree_skb_any(skb); | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | if (priv->adapter->is_hw_11ac_capable) { | ||
370 | ret = mwifiex_tdls_add_vht_capab(priv, skb); | ||
371 | if (ret) { | ||
372 | dev_kfree_skb_any(skb); | ||
373 | return ret; | ||
374 | } | ||
375 | mwifiex_tdls_add_aid(priv, skb); | ||
376 | } | ||
377 | |||
378 | mwifiex_tdls_add_ext_capab(priv, skb); | ||
379 | mwifiex_tdls_add_qos_capab(skb); | ||
380 | break; | ||
381 | |||
382 | case WLAN_TDLS_SETUP_RESPONSE: | ||
383 | tf->category = WLAN_CATEGORY_TDLS; | ||
384 | tf->action_code = WLAN_TDLS_SETUP_RESPONSE; | ||
385 | skb_put(skb, sizeof(tf->u.setup_resp)); | ||
386 | tf->u.setup_resp.status_code = cpu_to_le16(status_code); | ||
387 | tf->u.setup_resp.dialog_token = dialog_token; | ||
388 | tf->u.setup_resp.capability = cpu_to_le16(capab); | ||
389 | ret = mwifiex_tdls_append_rates_ie(priv, skb); | ||
390 | if (ret) { | ||
391 | dev_kfree_skb_any(skb); | ||
392 | return ret; | ||
393 | } | ||
394 | |||
395 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | ||
396 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
397 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
398 | ht_cap = (void *)pos; | ||
399 | radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | ||
400 | ret = mwifiex_fill_cap_info(priv, radio, ht_cap); | ||
401 | if (ret) { | ||
402 | dev_kfree_skb_any(skb); | ||
403 | return ret; | ||
404 | } | ||
405 | |||
406 | if (priv->adapter->is_hw_11ac_capable) { | ||
407 | ret = mwifiex_tdls_add_vht_capab(priv, skb); | ||
408 | if (ret) { | ||
409 | dev_kfree_skb_any(skb); | ||
410 | return ret; | ||
411 | } | ||
412 | mwifiex_tdls_add_aid(priv, skb); | ||
413 | } | ||
414 | |||
415 | mwifiex_tdls_add_ext_capab(priv, skb); | ||
416 | mwifiex_tdls_add_qos_capab(skb); | ||
417 | break; | ||
418 | |||
419 | case WLAN_TDLS_SETUP_CONFIRM: | ||
420 | tf->category = WLAN_CATEGORY_TDLS; | ||
421 | tf->action_code = WLAN_TDLS_SETUP_CONFIRM; | ||
422 | skb_put(skb, sizeof(tf->u.setup_cfm)); | ||
423 | tf->u.setup_cfm.status_code = cpu_to_le16(status_code); | ||
424 | tf->u.setup_cfm.dialog_token = dialog_token; | ||
425 | if (priv->adapter->is_hw_11ac_capable) { | ||
426 | ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); | ||
427 | if (ret) { | ||
428 | dev_kfree_skb_any(skb); | ||
429 | return ret; | ||
430 | } | ||
431 | } | ||
432 | break; | ||
433 | |||
434 | case WLAN_TDLS_TEARDOWN: | ||
435 | tf->category = WLAN_CATEGORY_TDLS; | ||
436 | tf->action_code = WLAN_TDLS_TEARDOWN; | ||
437 | skb_put(skb, sizeof(tf->u.teardown)); | ||
438 | tf->u.teardown.reason_code = cpu_to_le16(status_code); | ||
439 | break; | ||
440 | |||
441 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
442 | tf->category = WLAN_CATEGORY_TDLS; | ||
443 | tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; | ||
444 | skb_put(skb, sizeof(tf->u.discover_req)); | ||
445 | tf->u.discover_req.dialog_token = dialog_token; | ||
446 | break; | ||
447 | default: | ||
448 | dev_err(priv->adapter->dev, "Unknown TDLS frame type.\n"); | ||
449 | return -EINVAL; | ||
450 | } | ||
451 | |||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | static void | ||
456 | mwifiex_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, u8 *peer, u8 *bssid) | ||
457 | { | ||
458 | struct ieee80211_tdls_lnkie *lnkid; | ||
459 | |||
460 | lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); | ||
461 | lnkid->ie_type = WLAN_EID_LINK_ID; | ||
462 | lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - | ||
463 | sizeof(struct ieee_types_header); | ||
464 | |||
465 | memcpy(lnkid->bssid, bssid, ETH_ALEN); | ||
466 | memcpy(lnkid->init_sta, src_addr, ETH_ALEN); | ||
467 | memcpy(lnkid->resp_sta, peer, ETH_ALEN); | ||
468 | } | ||
469 | |||
470 | int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, | ||
471 | u8 *peer, u8 action_code, u8 dialog_token, | ||
472 | u16 status_code, const u8 *extra_ies, | ||
473 | size_t extra_ies_len) | ||
474 | { | ||
475 | struct sk_buff *skb; | ||
476 | struct mwifiex_txinfo *tx_info; | ||
477 | struct timeval tv; | ||
478 | int ret; | ||
479 | u16 skb_len; | ||
480 | |||
481 | skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + | ||
482 | max(sizeof(struct ieee80211_mgmt), | ||
483 | sizeof(struct ieee80211_tdls_data)) + | ||
484 | MWIFIEX_MGMT_FRAME_HEADER_SIZE + | ||
485 | MWIFIEX_SUPPORTED_RATES + | ||
486 | 3 + /* Qos Info */ | ||
487 | sizeof(struct ieee_types_extcap) + | ||
488 | sizeof(struct ieee80211_ht_cap) + | ||
489 | sizeof(struct ieee_types_bss_co_2040) + | ||
490 | sizeof(struct ieee80211_ht_operation) + | ||
491 | sizeof(struct ieee80211_tdls_lnkie) + | ||
492 | extra_ies_len; | ||
493 | |||
494 | if (priv->adapter->is_hw_11ac_capable) | ||
495 | skb_len += sizeof(struct ieee_types_vht_cap) + | ||
496 | sizeof(struct ieee_types_vht_oper) + | ||
497 | sizeof(struct ieee_types_aid); | ||
498 | |||
499 | skb = dev_alloc_skb(skb_len); | ||
500 | if (!skb) { | ||
501 | dev_err(priv->adapter->dev, | ||
502 | "allocate skb failed for management frame\n"); | ||
503 | return -ENOMEM; | ||
504 | } | ||
505 | skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); | ||
506 | |||
507 | switch (action_code) { | ||
508 | case WLAN_TDLS_SETUP_REQUEST: | ||
509 | case WLAN_TDLS_SETUP_CONFIRM: | ||
510 | case WLAN_TDLS_TEARDOWN: | ||
511 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
512 | ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, | ||
513 | dialog_token, status_code, | ||
514 | skb); | ||
515 | if (ret) { | ||
516 | dev_kfree_skb_any(skb); | ||
517 | return ret; | ||
518 | } | ||
519 | if (extra_ies_len) | ||
520 | memcpy(skb_put(skb, extra_ies_len), extra_ies, | ||
521 | extra_ies_len); | ||
522 | mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, | ||
523 | priv->cfg_bssid); | ||
524 | break; | ||
525 | case WLAN_TDLS_SETUP_RESPONSE: | ||
526 | ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, | ||
527 | dialog_token, status_code, | ||
528 | skb); | ||
529 | if (ret) { | ||
530 | dev_kfree_skb_any(skb); | ||
531 | return ret; | ||
532 | } | ||
533 | if (extra_ies_len) | ||
534 | memcpy(skb_put(skb, extra_ies_len), extra_ies, | ||
535 | extra_ies_len); | ||
536 | mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, | ||
537 | priv->cfg_bssid); | ||
538 | break; | ||
539 | } | ||
540 | |||
541 | switch (action_code) { | ||
542 | case WLAN_TDLS_SETUP_REQUEST: | ||
543 | case WLAN_TDLS_SETUP_RESPONSE: | ||
544 | skb->priority = MWIFIEX_PRIO_BK; | ||
545 | break; | ||
546 | default: | ||
547 | skb->priority = MWIFIEX_PRIO_VI; | ||
548 | break; | ||
549 | } | ||
550 | |||
551 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
552 | tx_info->bss_num = priv->bss_num; | ||
553 | tx_info->bss_type = priv->bss_type; | ||
554 | |||
555 | do_gettimeofday(&tv); | ||
556 | skb->tstamp = timeval_to_ktime(tv); | ||
557 | mwifiex_queue_tx_pkt(priv, skb); | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | static int | ||
563 | mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, u8 *peer, | ||
564 | u8 action_code, u8 dialog_token, | ||
565 | u16 status_code, struct sk_buff *skb) | ||
566 | { | ||
567 | struct ieee80211_mgmt *mgmt; | ||
568 | u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | ||
569 | int ret; | ||
570 | u16 capab; | ||
571 | struct ieee80211_ht_cap *ht_cap; | ||
572 | u8 radio, *pos; | ||
573 | |||
574 | capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; | ||
575 | |||
576 | mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); | ||
577 | |||
578 | memset(mgmt, 0, 24); | ||
579 | memcpy(mgmt->da, peer, ETH_ALEN); | ||
580 | memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); | ||
581 | memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); | ||
582 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
583 | IEEE80211_STYPE_ACTION); | ||
584 | |||
585 | /* add address 4 */ | ||
586 | pos = skb_put(skb, ETH_ALEN); | ||
587 | |||
588 | switch (action_code) { | ||
589 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | ||
590 | skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); | ||
591 | mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; | ||
592 | mgmt->u.action.u.tdls_discover_resp.action_code = | ||
593 | WLAN_PUB_ACTION_TDLS_DISCOVER_RES; | ||
594 | mgmt->u.action.u.tdls_discover_resp.dialog_token = | ||
595 | dialog_token; | ||
596 | mgmt->u.action.u.tdls_discover_resp.capability = | ||
597 | cpu_to_le16(capab); | ||
598 | /* move back for addr4 */ | ||
599 | memmove(pos + ETH_ALEN, &mgmt->u.action.category, | ||
600 | sizeof(mgmt->u.action.u.tdls_discover_resp)); | ||
601 | /* init address 4 */ | ||
602 | memcpy(pos, bc_addr, ETH_ALEN); | ||
603 | |||
604 | ret = mwifiex_tdls_append_rates_ie(priv, skb); | ||
605 | if (ret) { | ||
606 | dev_kfree_skb_any(skb); | ||
607 | return ret; | ||
608 | } | ||
609 | |||
610 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | ||
611 | *pos++ = WLAN_EID_HT_CAPABILITY; | ||
612 | *pos++ = sizeof(struct ieee80211_ht_cap); | ||
613 | ht_cap = (void *)pos; | ||
614 | radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | ||
615 | ret = mwifiex_fill_cap_info(priv, radio, ht_cap); | ||
616 | if (ret) { | ||
617 | dev_kfree_skb_any(skb); | ||
618 | return ret; | ||
619 | } | ||
620 | |||
621 | if (priv->adapter->is_hw_11ac_capable) { | ||
622 | ret = mwifiex_tdls_add_vht_capab(priv, skb); | ||
623 | if (ret) { | ||
624 | dev_kfree_skb_any(skb); | ||
625 | return ret; | ||
626 | } | ||
627 | mwifiex_tdls_add_aid(priv, skb); | ||
628 | } | ||
629 | |||
630 | mwifiex_tdls_add_ext_capab(priv, skb); | ||
631 | mwifiex_tdls_add_qos_capab(skb); | ||
632 | break; | ||
633 | default: | ||
634 | dev_err(priv->adapter->dev, "Unknown TDLS action frame type\n"); | ||
635 | return -EINVAL; | ||
636 | } | ||
637 | |||
638 | return 0; | ||
639 | } | ||
640 | |||
641 | int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, | ||
642 | u8 *peer, u8 action_code, u8 dialog_token, | ||
643 | u16 status_code, const u8 *extra_ies, | ||
644 | size_t extra_ies_len) | ||
645 | { | ||
646 | struct sk_buff *skb; | ||
647 | struct mwifiex_txinfo *tx_info; | ||
648 | struct timeval tv; | ||
649 | u8 *pos; | ||
650 | u32 pkt_type, tx_control; | ||
651 | u16 pkt_len, skb_len; | ||
652 | |||
653 | skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + | ||
654 | max(sizeof(struct ieee80211_mgmt), | ||
655 | sizeof(struct ieee80211_tdls_data)) + | ||
656 | MWIFIEX_MGMT_FRAME_HEADER_SIZE + | ||
657 | MWIFIEX_SUPPORTED_RATES + | ||
658 | sizeof(struct ieee_types_extcap) + | ||
659 | sizeof(struct ieee80211_ht_cap) + | ||
660 | sizeof(struct ieee_types_bss_co_2040) + | ||
661 | sizeof(struct ieee80211_ht_operation) + | ||
662 | sizeof(struct ieee80211_tdls_lnkie) + | ||
663 | extra_ies_len + | ||
664 | 3 + /* Qos Info */ | ||
665 | ETH_ALEN; /* Address4 */ | ||
666 | |||
667 | if (priv->adapter->is_hw_11ac_capable) | ||
668 | skb_len += sizeof(struct ieee_types_vht_cap) + | ||
669 | sizeof(struct ieee_types_vht_oper) + | ||
670 | sizeof(struct ieee_types_aid); | ||
671 | |||
672 | skb = dev_alloc_skb(skb_len); | ||
673 | if (!skb) { | ||
674 | dev_err(priv->adapter->dev, | ||
675 | "allocate skb failed for management frame\n"); | ||
676 | return -ENOMEM; | ||
677 | } | ||
678 | |||
679 | skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); | ||
680 | |||
681 | pkt_type = PKT_TYPE_MGMT; | ||
682 | tx_control = 0; | ||
683 | pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); | ||
684 | memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); | ||
685 | memcpy(pos, &pkt_type, sizeof(pkt_type)); | ||
686 | memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); | ||
687 | |||
688 | if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, | ||
689 | dialog_token, status_code, | ||
690 | skb)) { | ||
691 | dev_kfree_skb_any(skb); | ||
692 | return -EINVAL; | ||
693 | } | ||
694 | |||
695 | if (extra_ies_len) | ||
696 | memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); | ||
697 | |||
698 | /* the TDLS link IE is always added last we are the responder */ | ||
699 | |||
700 | mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, | ||
701 | priv->cfg_bssid); | ||
702 | |||
703 | skb->priority = MWIFIEX_PRIO_VI; | ||
704 | |||
705 | tx_info = MWIFIEX_SKB_TXCB(skb); | ||
706 | tx_info->bss_num = priv->bss_num; | ||
707 | tx_info->bss_type = priv->bss_type; | ||
708 | tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; | ||
709 | |||
710 | pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); | ||
711 | memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, | ||
712 | sizeof(pkt_len)); | ||
713 | do_gettimeofday(&tv); | ||
714 | skb->tstamp = timeval_to_ktime(tv); | ||
715 | mwifiex_queue_tx_pkt(priv, skb); | ||
716 | |||
717 | return 0; | ||
718 | } | ||
719 | |||
720 | /* This function process tdls action frame from peer. | ||
721 | * Peer capabilities are stored into station node structure. | ||
722 | */ | ||
723 | void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, | ||
724 | u8 *buf, int len) | ||
725 | { | ||
726 | struct mwifiex_sta_node *sta_ptr; | ||
727 | u8 *peer, *pos, *end; | ||
728 | u8 i, action, basic; | ||
729 | int ie_len = 0; | ||
730 | |||
731 | if (len < (sizeof(struct ethhdr) + 3)) | ||
732 | return; | ||
733 | if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) | ||
734 | return; | ||
735 | if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) | ||
736 | return; | ||
737 | |||
738 | peer = buf + ETH_ALEN; | ||
739 | action = *(buf + sizeof(struct ethhdr) + 2); | ||
740 | |||
741 | /* just handle TDLS setup request/response/confirm */ | ||
742 | if (action > WLAN_TDLS_SETUP_CONFIRM) | ||
743 | return; | ||
744 | |||
745 | dev_dbg(priv->adapter->dev, | ||
746 | "rx:tdls action: peer=%pM, action=%d\n", peer, action); | ||
747 | |||
748 | sta_ptr = mwifiex_add_sta_entry(priv, peer); | ||
749 | if (!sta_ptr) | ||
750 | return; | ||
751 | |||
752 | switch (action) { | ||
753 | case WLAN_TDLS_SETUP_REQUEST: | ||
754 | if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) | ||
755 | return; | ||
756 | |||
757 | pos = buf + sizeof(struct ethhdr) + 4; | ||
758 | /* payload 1+ category 1 + action 1 + dialog 1 */ | ||
759 | sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); | ||
760 | ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; | ||
761 | pos += 2; | ||
762 | break; | ||
763 | |||
764 | case WLAN_TDLS_SETUP_RESPONSE: | ||
765 | if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) | ||
766 | return; | ||
767 | /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ | ||
768 | pos = buf + sizeof(struct ethhdr) + 6; | ||
769 | sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); | ||
770 | ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; | ||
771 | pos += 2; | ||
772 | break; | ||
773 | |||
774 | case WLAN_TDLS_SETUP_CONFIRM: | ||
775 | if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) | ||
776 | return; | ||
777 | pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; | ||
778 | ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; | ||
779 | break; | ||
780 | default: | ||
781 | dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n"); | ||
782 | return; | ||
783 | } | ||
784 | |||
785 | for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { | ||
786 | if (pos + 2 + pos[1] > end) | ||
787 | break; | ||
788 | |||
789 | switch (*pos) { | ||
790 | case WLAN_EID_SUPP_RATES: | ||
791 | sta_ptr->tdls_cap.rates_len = pos[1]; | ||
792 | for (i = 0; i < pos[1]; i++) | ||
793 | sta_ptr->tdls_cap.rates[i] = pos[i + 2]; | ||
794 | break; | ||
795 | |||
796 | case WLAN_EID_EXT_SUPP_RATES: | ||
797 | basic = sta_ptr->tdls_cap.rates_len; | ||
798 | for (i = 0; i < pos[1]; i++) | ||
799 | sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; | ||
800 | sta_ptr->tdls_cap.rates_len += pos[1]; | ||
801 | break; | ||
802 | case WLAN_EID_HT_CAPABILITY: | ||
803 | memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, | ||
804 | sizeof(struct ieee80211_ht_cap)); | ||
805 | sta_ptr->is_11n_enabled = 1; | ||
806 | break; | ||
807 | case WLAN_EID_HT_OPERATION: | ||
808 | memcpy(&sta_ptr->tdls_cap.ht_oper, pos, | ||
809 | sizeof(struct ieee80211_ht_operation)); | ||
810 | break; | ||
811 | case WLAN_EID_BSS_COEX_2040: | ||
812 | sta_ptr->tdls_cap.coex_2040 = pos[2]; | ||
813 | break; | ||
814 | case WLAN_EID_EXT_CAPABILITY: | ||
815 | memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, | ||
816 | sizeof(struct ieee_types_header) + | ||
817 | min_t(u8, pos[1], 8)); | ||
818 | break; | ||
819 | case WLAN_EID_RSN: | ||
820 | memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, | ||
821 | sizeof(struct ieee_types_header) + pos[1]); | ||
822 | break; | ||
823 | case WLAN_EID_QOS_CAPA: | ||
824 | sta_ptr->tdls_cap.qos_info = pos[2]; | ||
825 | break; | ||
826 | case WLAN_EID_VHT_OPERATION: | ||
827 | if (priv->adapter->is_hw_11ac_capable) | ||
828 | memcpy(&sta_ptr->tdls_cap.vhtoper, pos, | ||
829 | sizeof(struct ieee80211_vht_operation)); | ||
830 | break; | ||
831 | case WLAN_EID_VHT_CAPABILITY: | ||
832 | if (priv->adapter->is_hw_11ac_capable) { | ||
833 | memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, | ||
834 | sizeof(struct ieee80211_vht_cap)); | ||
835 | sta_ptr->is_11ac_enabled = 1; | ||
836 | } | ||
837 | break; | ||
838 | case WLAN_EID_AID: | ||
839 | if (priv->adapter->is_hw_11ac_capable) | ||
840 | sta_ptr->tdls_cap.aid = | ||
841 | le16_to_cpu(*(__le16 *)(pos + 2)); | ||
842 | default: | ||
843 | break; | ||
844 | } | ||
845 | } | ||
846 | |||
847 | return; | ||
848 | } | ||
849 | |||
850 | static int | ||
851 | mwifiex_tdls_process_config_link(struct mwifiex_private *priv, u8 *peer) | ||
852 | { | ||
853 | struct mwifiex_sta_node *sta_ptr; | ||
854 | struct mwifiex_ds_tdls_oper tdls_oper; | ||
855 | |||
856 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | ||
857 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | ||
858 | |||
859 | if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { | ||
860 | dev_err(priv->adapter->dev, | ||
861 | "link absent for peer %pM; cannot config\n", peer); | ||
862 | return -EINVAL; | ||
863 | } | ||
864 | |||
865 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); | ||
866 | tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; | ||
867 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, | ||
868 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | ||
869 | } | ||
870 | |||
871 | static int | ||
872 | mwifiex_tdls_process_create_link(struct mwifiex_private *priv, u8 *peer) | ||
873 | { | ||
874 | struct mwifiex_sta_node *sta_ptr; | ||
875 | struct mwifiex_ds_tdls_oper tdls_oper; | ||
876 | |||
877 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | ||
878 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | ||
879 | |||
880 | if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { | ||
881 | dev_dbg(priv->adapter->dev, | ||
882 | "Setup already in progress for peer %pM\n", peer); | ||
883 | return 0; | ||
884 | } | ||
885 | |||
886 | sta_ptr = mwifiex_add_sta_entry(priv, peer); | ||
887 | if (!sta_ptr) | ||
888 | return -ENOMEM; | ||
889 | |||
890 | sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; | ||
891 | mwifiex_hold_tdls_packets(priv, peer); | ||
892 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); | ||
893 | tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; | ||
894 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, | ||
895 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | ||
896 | } | ||
897 | |||
898 | static int | ||
899 | mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, u8 *peer) | ||
900 | { | ||
901 | struct mwifiex_sta_node *sta_ptr; | ||
902 | struct mwifiex_ds_tdls_oper tdls_oper; | ||
903 | unsigned long flags; | ||
904 | |||
905 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | ||
906 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | ||
907 | |||
908 | if (sta_ptr) { | ||
909 | if (sta_ptr->is_11n_enabled) { | ||
910 | mwifiex_11n_cleanup_reorder_tbl(priv); | ||
911 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, | ||
912 | flags); | ||
913 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | ||
914 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
915 | flags); | ||
916 | } | ||
917 | mwifiex_del_sta_entry(priv, peer); | ||
918 | } | ||
919 | |||
920 | mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); | ||
921 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); | ||
922 | tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; | ||
923 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, | ||
924 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | ||
925 | } | ||
926 | |||
927 | static int | ||
928 | mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, u8 *peer) | ||
929 | { | ||
930 | struct mwifiex_sta_node *sta_ptr; | ||
931 | struct ieee80211_mcs_info mcs; | ||
932 | unsigned long flags; | ||
933 | int i; | ||
934 | |||
935 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | ||
936 | |||
937 | if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { | ||
938 | dev_dbg(priv->adapter->dev, | ||
939 | "tdls: enable link %pM success\n", peer); | ||
940 | |||
941 | sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; | ||
942 | |||
943 | mcs = sta_ptr->tdls_cap.ht_capb.mcs; | ||
944 | if (mcs.rx_mask[0] != 0xff) | ||
945 | sta_ptr->is_11n_enabled = true; | ||
946 | if (sta_ptr->is_11n_enabled) { | ||
947 | if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & | ||
948 | IEEE80211_HT_CAP_MAX_AMSDU) | ||
949 | sta_ptr->max_amsdu = | ||
950 | MWIFIEX_TX_DATA_BUF_SIZE_8K; | ||
951 | else | ||
952 | sta_ptr->max_amsdu = | ||
953 | MWIFIEX_TX_DATA_BUF_SIZE_4K; | ||
954 | |||
955 | for (i = 0; i < MAX_NUM_TID; i++) | ||
956 | sta_ptr->ampdu_sta[i] = | ||
957 | priv->aggr_prio_tbl[i].ampdu_user; | ||
958 | } else { | ||
959 | for (i = 0; i < MAX_NUM_TID; i++) | ||
960 | sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; | ||
961 | } | ||
962 | |||
963 | memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); | ||
964 | mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); | ||
965 | } else { | ||
966 | dev_dbg(priv->adapter->dev, | ||
967 | "tdls: enable link %pM failed\n", peer); | ||
968 | if (sta_ptr) { | ||
969 | mwifiex_11n_cleanup_reorder_tbl(priv); | ||
970 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, | ||
971 | flags); | ||
972 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | ||
973 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
974 | flags); | ||
975 | mwifiex_del_sta_entry(priv, peer); | ||
976 | } | ||
977 | mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); | ||
978 | |||
979 | return -1; | ||
980 | } | ||
981 | |||
982 | return 0; | ||
983 | } | ||
984 | |||
985 | int mwifiex_tdls_oper(struct mwifiex_private *priv, u8 *peer, u8 action) | ||
986 | { | ||
987 | switch (action) { | ||
988 | case MWIFIEX_TDLS_ENABLE_LINK: | ||
989 | return mwifiex_tdls_process_enable_link(priv, peer); | ||
990 | case MWIFIEX_TDLS_DISABLE_LINK: | ||
991 | return mwifiex_tdls_process_disable_link(priv, peer); | ||
992 | case MWIFIEX_TDLS_CREATE_LINK: | ||
993 | return mwifiex_tdls_process_create_link(priv, peer); | ||
994 | case MWIFIEX_TDLS_CONFIG_LINK: | ||
995 | return mwifiex_tdls_process_config_link(priv, peer); | ||
996 | } | ||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, u8 *mac) | ||
1001 | { | ||
1002 | struct mwifiex_sta_node *sta_ptr; | ||
1003 | |||
1004 | sta_ptr = mwifiex_get_sta_entry(priv, mac); | ||
1005 | if (sta_ptr) | ||
1006 | return sta_ptr->tdls_status; | ||
1007 | |||
1008 | return TDLS_NOT_SETUP; | ||
1009 | } | ||
1010 | |||
1011 | void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) | ||
1012 | { | ||
1013 | struct mwifiex_sta_node *sta_ptr; | ||
1014 | struct mwifiex_ds_tdls_oper tdls_oper; | ||
1015 | unsigned long flags; | ||
1016 | |||
1017 | if (list_empty(&priv->sta_list)) | ||
1018 | return; | ||
1019 | |||
1020 | list_for_each_entry(sta_ptr, &priv->sta_list, list) { | ||
1021 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | ||
1022 | |||
1023 | if (sta_ptr->is_11n_enabled) { | ||
1024 | mwifiex_11n_cleanup_reorder_tbl(priv); | ||
1025 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, | ||
1026 | flags); | ||
1027 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | ||
1028 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | ||
1029 | flags); | ||
1030 | } | ||
1031 | |||
1032 | mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, | ||
1033 | TDLS_LINK_TEARDOWN); | ||
1034 | memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); | ||
1035 | tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; | ||
1036 | if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, | ||
1037 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) | ||
1038 | dev_warn(priv->adapter->dev, | ||
1039 | "Disable link failed for TDLS peer %pM", | ||
1040 | sta_ptr->mac_addr); | ||
1041 | } | ||
1042 | |||
1043 | mwifiex_del_all_sta_list(priv); | ||
1044 | } | ||