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