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 | |
56bd24a1 AP |
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); | |
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 | ||
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 | ||
b23bce29 AP |
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 | ||
5f6d5983 AP |
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); | |
c42c65c1 | 183 | memcpy(pos, &vht_cap, sizeof(vht_cap)); |
5f6d5983 AP |
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) | |
b23bce29 AP |
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; | |
5f6d5983 AP |
313 | |
314 | if (priv->adapter->is_hw_11ac_capable) | |
315 | extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; | |
b23bce29 AP |
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 | ||
5f6d5983 AP |
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); | |
b23bce29 AP |
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 | ||
5f6d5983 AP |
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); | |
b23bce29 AP |
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; | |
5f6d5983 AP |
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 | } | |
b23bce29 AP |
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 | ||
5f6d5983 AP |
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 | ||
b23bce29 AP |
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 | ||
5f6d5983 AP |
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); | |
b23bce29 AP |
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 | ||
5f6d5983 AP |
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 | ||
b23bce29 AP |
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 | } | |
5f2caaf3 AP |
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; | |
45d18c56 | 733 | if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) |
5f2caaf3 | 734 | return; |
45d18c56 | 735 | if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) |
5f2caaf3 AP |
736 | return; |
737 | ||
738 | peer = buf + ETH_ALEN; | |
45d18c56 | 739 | action = *(buf + sizeof(struct ethhdr) + 2); |
5f2caaf3 AP |
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; | |
5f6d5983 AP |
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)); | |
5f2caaf3 AP |
842 | default: |
843 | break; | |
844 | } | |
845 | } | |
846 | ||
847 | return; | |
848 | } | |
429d90d2 | 849 | |
1f4dfd8a AP |
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; | |
fa0ecbb9 BZ |
867 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
868 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | |
1f4dfd8a AP |
869 | } |
870 | ||
e48e0de0 AP |
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; | |
56bd24a1 | 891 | mwifiex_hold_tdls_packets(priv, peer); |
e48e0de0 AP |
892 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); |
893 | tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; | |
fa0ecbb9 BZ |
894 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
895 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | |
e48e0de0 AP |
896 | } |
897 | ||
429d90d2 AP |
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 | ||
56bd24a1 | 920 | mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); |
429d90d2 AP |
921 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); |
922 | tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; | |
fa0ecbb9 BZ |
923 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
924 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | |
429d90d2 AP |
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)); | |
56bd24a1 | 964 | mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); |
429d90d2 AP |
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 | } | |
56bd24a1 | 977 | mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); |
429d90d2 AP |
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); | |
e48e0de0 AP |
992 | case MWIFIEX_TDLS_CREATE_LINK: |
993 | return mwifiex_tdls_process_create_link(priv, peer); | |
1f4dfd8a AP |
994 | case MWIFIEX_TDLS_CONFIG_LINK: |
995 | return mwifiex_tdls_process_config_link(priv, peer); | |
429d90d2 AP |
996 | } |
997 | return 0; | |
998 | } | |
d63bf5e5 AP |
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 | } | |
be104b91 AP |
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; | |
fa0ecbb9 BZ |
1036 | if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
1037 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) | |
be104b91 AP |
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 | } |