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