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 | |
16fa5e65 | 27 | #define MWIFIEX_TDLS_WMM_INFO_SIZE 7 |
b23bce29 | 28 | |
3b3a0162 JB |
29 | static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, |
30 | const u8 *mac, u8 status) | |
56bd24a1 AP |
31 | { |
32 | struct mwifiex_ra_list_tbl *ra_list; | |
33 | struct list_head *tid_list; | |
34 | struct sk_buff *skb, *tmp; | |
35 | struct mwifiex_txinfo *tx_info; | |
36 | unsigned long flags; | |
37 | u32 tid; | |
38 | u8 tid_down; | |
39 | ||
acebe8c1 | 40 | mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); |
56bd24a1 AP |
41 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); |
42 | ||
43 | skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { | |
44 | if (!ether_addr_equal(mac, skb->data)) | |
45 | continue; | |
46 | ||
47 | __skb_unlink(skb, &priv->tdls_txq); | |
48 | tx_info = MWIFIEX_SKB_TXCB(skb); | |
49 | tid = skb->priority; | |
50 | tid_down = mwifiex_wmm_downgrade_tid(priv, tid); | |
51 | ||
52 | if (status == TDLS_SETUP_COMPLETE) { | |
53 | ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); | |
daeb5bb4 | 54 | ra_list->tdls_link = true; |
56bd24a1 AP |
55 | tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; |
56 | } else { | |
57 | tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; | |
58 | if (!list_empty(tid_list)) | |
59 | ra_list = list_first_entry(tid_list, | |
60 | struct mwifiex_ra_list_tbl, list); | |
61 | else | |
62 | ra_list = NULL; | |
63 | tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; | |
64 | } | |
65 | ||
66 | if (!ra_list) { | |
67 | mwifiex_write_data_complete(priv->adapter, skb, 0, -1); | |
68 | continue; | |
69 | } | |
70 | ||
71 | skb_queue_tail(&ra_list->skb_head, skb); | |
72 | ||
73 | ra_list->ba_pkt_count++; | |
74 | ra_list->total_pkt_count++; | |
75 | ||
76 | if (atomic_read(&priv->wmm.highest_queued_prio) < | |
77 | tos_to_tid_inv[tid_down]) | |
78 | atomic_set(&priv->wmm.highest_queued_prio, | |
79 | tos_to_tid_inv[tid_down]); | |
80 | ||
81 | atomic_inc(&priv->wmm.tx_pkts_queued); | |
82 | } | |
83 | ||
84 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | |
85 | return; | |
86 | } | |
87 | ||
3b3a0162 JB |
88 | static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, |
89 | const u8 *mac) | |
56bd24a1 AP |
90 | { |
91 | struct mwifiex_ra_list_tbl *ra_list; | |
92 | struct list_head *ra_list_head; | |
93 | struct sk_buff *skb, *tmp; | |
94 | unsigned long flags; | |
95 | int i; | |
96 | ||
acebe8c1 | 97 | mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); |
56bd24a1 AP |
98 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); |
99 | ||
100 | for (i = 0; i < MAX_NUM_TID; i++) { | |
101 | if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { | |
102 | ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; | |
103 | list_for_each_entry(ra_list, ra_list_head, list) { | |
104 | skb_queue_walk_safe(&ra_list->skb_head, skb, | |
105 | tmp) { | |
106 | if (!ether_addr_equal(mac, skb->data)) | |
107 | continue; | |
108 | __skb_unlink(skb, &ra_list->skb_head); | |
109 | atomic_dec(&priv->wmm.tx_pkts_queued); | |
110 | ra_list->total_pkt_count--; | |
111 | skb_queue_tail(&priv->tdls_txq, skb); | |
112 | } | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); | |
118 | return; | |
119 | } | |
120 | ||
b23bce29 AP |
121 | /* This function appends rate TLV to scan config command. */ |
122 | static int | |
123 | mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, | |
124 | struct sk_buff *skb) | |
125 | { | |
126 | u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; | |
127 | u16 rates_size, supp_rates_size, ext_rates_size; | |
128 | ||
129 | memset(rates, 0, sizeof(rates)); | |
130 | rates_size = mwifiex_get_supported_rates(priv, rates); | |
131 | ||
132 | supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); | |
133 | ||
134 | if (skb_tailroom(skb) < rates_size + 4) { | |
acebe8c1 ZL |
135 | mwifiex_dbg(priv->adapter, ERROR, |
136 | "Insuffient space while adding rates\n"); | |
b23bce29 AP |
137 | return -ENOMEM; |
138 | } | |
139 | ||
140 | pos = skb_put(skb, supp_rates_size + 2); | |
141 | *pos++ = WLAN_EID_SUPP_RATES; | |
142 | *pos++ = supp_rates_size; | |
143 | memcpy(pos, rates, supp_rates_size); | |
144 | ||
145 | if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { | |
146 | ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; | |
147 | pos = skb_put(skb, ext_rates_size + 2); | |
148 | *pos++ = WLAN_EID_EXT_SUPP_RATES; | |
149 | *pos++ = ext_rates_size; | |
150 | memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, | |
151 | ext_rates_size); | |
152 | } | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
5f6d5983 AP |
157 | static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, |
158 | struct sk_buff *skb) | |
159 | { | |
160 | struct ieee_types_assoc_rsp *assoc_rsp; | |
161 | u8 *pos; | |
162 | ||
163 | assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; | |
164 | pos = (void *)skb_put(skb, 4); | |
165 | *pos++ = WLAN_EID_AID; | |
166 | *pos++ = 2; | |
167 | *pos++ = le16_to_cpu(assoc_rsp->a_id); | |
168 | ||
169 | return; | |
170 | } | |
171 | ||
172 | static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, | |
173 | struct sk_buff *skb) | |
174 | { | |
175 | struct ieee80211_vht_cap vht_cap; | |
176 | u8 *pos; | |
177 | ||
178 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); | |
179 | *pos++ = WLAN_EID_VHT_CAPABILITY; | |
180 | *pos++ = sizeof(struct ieee80211_vht_cap); | |
181 | ||
182 | memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); | |
183 | ||
184 | mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); | |
c42c65c1 | 185 | memcpy(pos, &vht_cap, sizeof(vht_cap)); |
5f6d5983 AP |
186 | |
187 | return 0; | |
188 | } | |
189 | ||
396939f9 | 190 | static int |
ef1b075c | 191 | mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, |
396939f9 AP |
192 | u8 vht_enabled, struct sk_buff *skb) |
193 | { | |
194 | struct ieee80211_ht_operation *ht_oper; | |
195 | struct mwifiex_sta_node *sta_ptr; | |
196 | struct mwifiex_bssdescriptor *bss_desc = | |
197 | &priv->curr_bss_params.bss_descriptor; | |
198 | u8 *pos; | |
199 | ||
200 | sta_ptr = mwifiex_get_sta_entry(priv, mac); | |
201 | if (unlikely(!sta_ptr)) { | |
acebe8c1 ZL |
202 | mwifiex_dbg(priv->adapter, ERROR, |
203 | "TDLS peer station not found in list\n"); | |
396939f9 AP |
204 | return -1; |
205 | } | |
206 | ||
207 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); | |
208 | *pos++ = WLAN_EID_HT_OPERATION; | |
209 | *pos++ = sizeof(struct ieee80211_ht_operation); | |
210 | ht_oper = (void *)pos; | |
211 | ||
212 | ht_oper->primary_chan = bss_desc->channel; | |
213 | ||
214 | /* follow AP's channel bandwidth */ | |
215 | if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && | |
216 | bss_desc->bcn_ht_cap && | |
217 | ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) | |
218 | ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; | |
219 | ||
220 | if (vht_enabled) { | |
221 | ht_oper->ht_param = | |
222 | mwifiex_get_sec_chan_offset(bss_desc->channel); | |
223 | ht_oper->ht_param |= BIT(2); | |
224 | } | |
225 | ||
226 | memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper, | |
227 | sizeof(struct ieee80211_ht_operation)); | |
228 | ||
229 | return 0; | |
230 | } | |
231 | ||
5f6d5983 | 232 | static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, |
3b3a0162 | 233 | const u8 *mac, struct sk_buff *skb) |
5f6d5983 AP |
234 | { |
235 | struct mwifiex_bssdescriptor *bss_desc; | |
236 | struct ieee80211_vht_operation *vht_oper; | |
237 | struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; | |
238 | struct mwifiex_sta_node *sta_ptr; | |
239 | struct mwifiex_adapter *adapter = priv->adapter; | |
240 | u8 supp_chwd_set, peer_supp_chwd_set; | |
241 | u8 *pos, ap_supp_chwd_set, chan_bw; | |
242 | u16 mcs_map_user, mcs_map_resp, mcs_map_result; | |
243 | u16 mcs_user, mcs_resp, nss; | |
244 | u32 usr_vht_cap_info; | |
245 | ||
246 | bss_desc = &priv->curr_bss_params.bss_descriptor; | |
247 | ||
248 | sta_ptr = mwifiex_get_sta_entry(priv, mac); | |
249 | if (unlikely(!sta_ptr)) { | |
acebe8c1 ZL |
250 | mwifiex_dbg(adapter, ERROR, |
251 | "TDLS peer station not found in list\n"); | |
5f6d5983 AP |
252 | return -1; |
253 | } | |
254 | ||
255 | if (!mwifiex_is_bss_in_11ac_mode(priv)) { | |
256 | if (sta_ptr->tdls_cap.extcap.ext_capab[7] & | |
257 | WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { | |
acebe8c1 ZL |
258 | mwifiex_dbg(adapter, WARN, |
259 | "TDLS peer doesn't support wider bandwidth\n"); | |
5f6d5983 AP |
260 | return 0; |
261 | } | |
262 | } else { | |
263 | ap_vht_cap = bss_desc->bcn_vht_cap; | |
264 | } | |
265 | ||
266 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); | |
267 | *pos++ = WLAN_EID_VHT_OPERATION; | |
268 | *pos++ = sizeof(struct ieee80211_vht_operation); | |
269 | vht_oper = (struct ieee80211_vht_operation *)pos; | |
270 | ||
271 | if (bss_desc->bss_band & BAND_A) | |
272 | usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; | |
273 | else | |
274 | usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; | |
275 | ||
276 | /* find the minmum bandwith between AP/TDLS peers */ | |
277 | vht_cap = &sta_ptr->tdls_cap.vhtcap; | |
278 | supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); | |
279 | peer_supp_chwd_set = | |
280 | GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); | |
281 | supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); | |
282 | ||
283 | /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ | |
284 | ||
285 | if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & | |
286 | WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { | |
287 | ap_supp_chwd_set = | |
288 | GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); | |
289 | supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); | |
290 | } | |
291 | ||
292 | switch (supp_chwd_set) { | |
293 | case IEEE80211_VHT_CHANWIDTH_80MHZ: | |
294 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; | |
295 | break; | |
296 | case IEEE80211_VHT_CHANWIDTH_160MHZ: | |
297 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; | |
298 | break; | |
299 | case IEEE80211_VHT_CHANWIDTH_80P80MHZ: | |
300 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; | |
301 | break; | |
302 | default: | |
303 | vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; | |
304 | break; | |
305 | } | |
306 | ||
307 | mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); | |
308 | mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_mcs_map); | |
309 | mcs_map_result = 0; | |
310 | ||
311 | for (nss = 1; nss <= 8; nss++) { | |
312 | mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); | |
313 | mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); | |
314 | ||
315 | if ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || | |
316 | (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) | |
317 | SET_VHTNSSMCS(mcs_map_result, nss, | |
318 | IEEE80211_VHT_MCS_NOT_SUPPORTED); | |
319 | else | |
320 | SET_VHTNSSMCS(mcs_map_result, nss, | |
321 | min_t(u16, mcs_user, mcs_resp)); | |
322 | } | |
323 | ||
324 | vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); | |
325 | ||
326 | switch (vht_oper->chan_width) { | |
327 | case IEEE80211_VHT_CHANWIDTH_80MHZ: | |
328 | chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; | |
329 | break; | |
330 | case IEEE80211_VHT_CHANWIDTH_160MHZ: | |
331 | chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; | |
332 | break; | |
333 | case IEEE80211_VHT_CHANWIDTH_80P80MHZ: | |
334 | chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; | |
335 | break; | |
336 | default: | |
337 | chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; | |
338 | break; | |
339 | } | |
340 | vht_oper->center_freq_seg1_idx = | |
341 | mwifiex_get_center_freq_index(priv, BAND_AAC, | |
342 | bss_desc->channel, | |
343 | chan_bw); | |
344 | ||
345 | return 0; | |
346 | } | |
347 | ||
348 | static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, | |
349 | struct sk_buff *skb) | |
b23bce29 AP |
350 | { |
351 | struct ieee_types_extcap *extcap; | |
352 | ||
353 | extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); | |
354 | extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; | |
355 | extcap->ieee_hdr.len = 8; | |
356 | memset(extcap->ext_capab, 0, 8); | |
357 | extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; | |
5f6d5983 AP |
358 | |
359 | if (priv->adapter->is_hw_11ac_capable) | |
360 | extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; | |
b23bce29 AP |
361 | } |
362 | ||
363 | static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) | |
364 | { | |
365 | u8 *pos = (void *)skb_put(skb, 3); | |
366 | ||
367 | *pos++ = WLAN_EID_QOS_CAPA; | |
368 | *pos++ = 1; | |
369 | *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; | |
370 | } | |
371 | ||
16fa5e65 AP |
372 | static void |
373 | mwifiex_tdls_add_wmm_param_ie(struct mwifiex_private *priv, struct sk_buff *skb) | |
374 | { | |
375 | struct ieee80211_wmm_param_ie *wmm; | |
376 | u8 ac_vi[] = {0x42, 0x43, 0x5e, 0x00}; | |
377 | u8 ac_vo[] = {0x62, 0x32, 0x2f, 0x00}; | |
378 | u8 ac_be[] = {0x03, 0xa4, 0x00, 0x00}; | |
379 | u8 ac_bk[] = {0x27, 0xa4, 0x00, 0x00}; | |
380 | ||
381 | wmm = (void *)skb_put(skb, sizeof(*wmm)); | |
382 | memset(wmm, 0, sizeof(*wmm)); | |
383 | ||
384 | wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; | |
385 | wmm->len = sizeof(*wmm) - 2; | |
386 | wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ | |
387 | wmm->oui[1] = 0x50; | |
388 | wmm->oui[2] = 0xf2; | |
389 | wmm->oui_type = 2; /* WME */ | |
390 | wmm->oui_subtype = 1; /* WME param */ | |
391 | wmm->version = 1; /* WME ver */ | |
392 | wmm->qos_info = 0; /* U-APSD not in use */ | |
393 | ||
394 | /* use default WMM AC parameters for TDLS link*/ | |
395 | memcpy(&wmm->ac[0], ac_be, sizeof(ac_be)); | |
396 | memcpy(&wmm->ac[1], ac_bk, sizeof(ac_bk)); | |
397 | memcpy(&wmm->ac[2], ac_vi, sizeof(ac_vi)); | |
398 | memcpy(&wmm->ac[3], ac_vo, sizeof(ac_vo)); | |
399 | } | |
400 | ||
401 | static void | |
402 | mwifiex_add_wmm_info_ie(struct mwifiex_private *priv, struct sk_buff *skb, | |
403 | u8 qosinfo) | |
404 | { | |
405 | u8 *buf; | |
406 | ||
407 | buf = (void *)skb_put(skb, MWIFIEX_TDLS_WMM_INFO_SIZE + | |
408 | sizeof(struct ieee_types_header)); | |
409 | ||
410 | *buf++ = WLAN_EID_VENDOR_SPECIFIC; | |
411 | *buf++ = 7; /* len */ | |
412 | *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ | |
413 | *buf++ = 0x50; | |
414 | *buf++ = 0xf2; | |
415 | *buf++ = 2; /* WME */ | |
416 | *buf++ = 0; /* WME info */ | |
417 | *buf++ = 1; /* WME ver */ | |
418 | *buf++ = qosinfo; /* U-APSD no in use */ | |
419 | } | |
420 | ||
b23bce29 | 421 | static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, |
3b3a0162 JB |
422 | const u8 *peer, u8 action_code, |
423 | u8 dialog_token, | |
424 | u16 status_code, struct sk_buff *skb) | |
b23bce29 AP |
425 | { |
426 | struct ieee80211_tdls_data *tf; | |
427 | int ret; | |
428 | u16 capab; | |
429 | struct ieee80211_ht_cap *ht_cap; | |
430 | u8 radio, *pos; | |
431 | ||
432 | capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; | |
433 | ||
434 | tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); | |
435 | memcpy(tf->da, peer, ETH_ALEN); | |
436 | memcpy(tf->sa, priv->curr_addr, ETH_ALEN); | |
437 | tf->ether_type = cpu_to_be16(ETH_P_TDLS); | |
438 | tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; | |
439 | ||
440 | switch (action_code) { | |
441 | case WLAN_TDLS_SETUP_REQUEST: | |
442 | tf->category = WLAN_CATEGORY_TDLS; | |
443 | tf->action_code = WLAN_TDLS_SETUP_REQUEST; | |
444 | skb_put(skb, sizeof(tf->u.setup_req)); | |
445 | tf->u.setup_req.dialog_token = dialog_token; | |
446 | tf->u.setup_req.capability = cpu_to_le16(capab); | |
447 | ret = mwifiex_tdls_append_rates_ie(priv, skb); | |
448 | if (ret) { | |
449 | dev_kfree_skb_any(skb); | |
450 | return ret; | |
451 | } | |
452 | ||
453 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | |
454 | *pos++ = WLAN_EID_HT_CAPABILITY; | |
455 | *pos++ = sizeof(struct ieee80211_ht_cap); | |
456 | ht_cap = (void *)pos; | |
457 | radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | |
458 | ret = mwifiex_fill_cap_info(priv, radio, ht_cap); | |
459 | if (ret) { | |
460 | dev_kfree_skb_any(skb); | |
461 | return ret; | |
462 | } | |
463 | ||
5f6d5983 AP |
464 | if (priv->adapter->is_hw_11ac_capable) { |
465 | ret = mwifiex_tdls_add_vht_capab(priv, skb); | |
466 | if (ret) { | |
467 | dev_kfree_skb_any(skb); | |
468 | return ret; | |
469 | } | |
470 | mwifiex_tdls_add_aid(priv, skb); | |
471 | } | |
472 | ||
473 | mwifiex_tdls_add_ext_capab(priv, skb); | |
b23bce29 | 474 | mwifiex_tdls_add_qos_capab(skb); |
16fa5e65 | 475 | mwifiex_add_wmm_info_ie(priv, skb, 0); |
b23bce29 AP |
476 | break; |
477 | ||
478 | case WLAN_TDLS_SETUP_RESPONSE: | |
479 | tf->category = WLAN_CATEGORY_TDLS; | |
480 | tf->action_code = WLAN_TDLS_SETUP_RESPONSE; | |
481 | skb_put(skb, sizeof(tf->u.setup_resp)); | |
482 | tf->u.setup_resp.status_code = cpu_to_le16(status_code); | |
483 | tf->u.setup_resp.dialog_token = dialog_token; | |
484 | tf->u.setup_resp.capability = cpu_to_le16(capab); | |
485 | ret = mwifiex_tdls_append_rates_ie(priv, skb); | |
486 | if (ret) { | |
487 | dev_kfree_skb_any(skb); | |
488 | return ret; | |
489 | } | |
490 | ||
491 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | |
492 | *pos++ = WLAN_EID_HT_CAPABILITY; | |
493 | *pos++ = sizeof(struct ieee80211_ht_cap); | |
494 | ht_cap = (void *)pos; | |
495 | radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | |
496 | ret = mwifiex_fill_cap_info(priv, radio, ht_cap); | |
497 | if (ret) { | |
498 | dev_kfree_skb_any(skb); | |
499 | return ret; | |
500 | } | |
501 | ||
5f6d5983 AP |
502 | if (priv->adapter->is_hw_11ac_capable) { |
503 | ret = mwifiex_tdls_add_vht_capab(priv, skb); | |
504 | if (ret) { | |
505 | dev_kfree_skb_any(skb); | |
506 | return ret; | |
507 | } | |
508 | mwifiex_tdls_add_aid(priv, skb); | |
509 | } | |
510 | ||
511 | mwifiex_tdls_add_ext_capab(priv, skb); | |
b23bce29 | 512 | mwifiex_tdls_add_qos_capab(skb); |
16fa5e65 | 513 | mwifiex_add_wmm_info_ie(priv, skb, 0); |
b23bce29 AP |
514 | break; |
515 | ||
516 | case WLAN_TDLS_SETUP_CONFIRM: | |
517 | tf->category = WLAN_CATEGORY_TDLS; | |
518 | tf->action_code = WLAN_TDLS_SETUP_CONFIRM; | |
519 | skb_put(skb, sizeof(tf->u.setup_cfm)); | |
520 | tf->u.setup_cfm.status_code = cpu_to_le16(status_code); | |
521 | tf->u.setup_cfm.dialog_token = dialog_token; | |
16fa5e65 AP |
522 | |
523 | mwifiex_tdls_add_wmm_param_ie(priv, skb); | |
5f6d5983 AP |
524 | if (priv->adapter->is_hw_11ac_capable) { |
525 | ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); | |
526 | if (ret) { | |
527 | dev_kfree_skb_any(skb); | |
528 | return ret; | |
529 | } | |
396939f9 AP |
530 | ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb); |
531 | if (ret) { | |
532 | dev_kfree_skb_any(skb); | |
533 | return ret; | |
534 | } | |
535 | } else { | |
536 | ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb); | |
537 | if (ret) { | |
538 | dev_kfree_skb_any(skb); | |
539 | return ret; | |
540 | } | |
5f6d5983 | 541 | } |
b23bce29 AP |
542 | break; |
543 | ||
544 | case WLAN_TDLS_TEARDOWN: | |
545 | tf->category = WLAN_CATEGORY_TDLS; | |
546 | tf->action_code = WLAN_TDLS_TEARDOWN; | |
547 | skb_put(skb, sizeof(tf->u.teardown)); | |
548 | tf->u.teardown.reason_code = cpu_to_le16(status_code); | |
549 | break; | |
550 | ||
551 | case WLAN_TDLS_DISCOVERY_REQUEST: | |
552 | tf->category = WLAN_CATEGORY_TDLS; | |
553 | tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; | |
554 | skb_put(skb, sizeof(tf->u.discover_req)); | |
555 | tf->u.discover_req.dialog_token = dialog_token; | |
556 | break; | |
557 | default: | |
acebe8c1 | 558 | mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); |
b23bce29 AP |
559 | return -EINVAL; |
560 | } | |
561 | ||
562 | return 0; | |
563 | } | |
564 | ||
565 | static void | |
3b3a0162 JB |
566 | mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, |
567 | const u8 *peer, const u8 *bssid) | |
b23bce29 AP |
568 | { |
569 | struct ieee80211_tdls_lnkie *lnkid; | |
570 | ||
571 | lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); | |
572 | lnkid->ie_type = WLAN_EID_LINK_ID; | |
573 | lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - | |
574 | sizeof(struct ieee_types_header); | |
575 | ||
576 | memcpy(lnkid->bssid, bssid, ETH_ALEN); | |
577 | memcpy(lnkid->init_sta, src_addr, ETH_ALEN); | |
578 | memcpy(lnkid->resp_sta, peer, ETH_ALEN); | |
579 | } | |
580 | ||
3b3a0162 JB |
581 | int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, |
582 | u8 action_code, u8 dialog_token, | |
b23bce29 AP |
583 | u16 status_code, const u8 *extra_ies, |
584 | size_t extra_ies_len) | |
585 | { | |
586 | struct sk_buff *skb; | |
587 | struct mwifiex_txinfo *tx_info; | |
b23bce29 AP |
588 | int ret; |
589 | u16 skb_len; | |
590 | ||
591 | skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + | |
592 | max(sizeof(struct ieee80211_mgmt), | |
593 | sizeof(struct ieee80211_tdls_data)) + | |
594 | MWIFIEX_MGMT_FRAME_HEADER_SIZE + | |
595 | MWIFIEX_SUPPORTED_RATES + | |
596 | 3 + /* Qos Info */ | |
597 | sizeof(struct ieee_types_extcap) + | |
598 | sizeof(struct ieee80211_ht_cap) + | |
599 | sizeof(struct ieee_types_bss_co_2040) + | |
600 | sizeof(struct ieee80211_ht_operation) + | |
601 | sizeof(struct ieee80211_tdls_lnkie) + | |
16fa5e65 | 602 | sizeof(struct ieee80211_wmm_param_ie) + |
b23bce29 AP |
603 | extra_ies_len; |
604 | ||
5f6d5983 AP |
605 | if (priv->adapter->is_hw_11ac_capable) |
606 | skb_len += sizeof(struct ieee_types_vht_cap) + | |
607 | sizeof(struct ieee_types_vht_oper) + | |
608 | sizeof(struct ieee_types_aid); | |
609 | ||
b23bce29 AP |
610 | skb = dev_alloc_skb(skb_len); |
611 | if (!skb) { | |
acebe8c1 ZL |
612 | mwifiex_dbg(priv->adapter, ERROR, |
613 | "allocate skb failed for management frame\n"); | |
b23bce29 AP |
614 | return -ENOMEM; |
615 | } | |
616 | skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); | |
617 | ||
618 | switch (action_code) { | |
619 | case WLAN_TDLS_SETUP_REQUEST: | |
620 | case WLAN_TDLS_SETUP_CONFIRM: | |
621 | case WLAN_TDLS_TEARDOWN: | |
622 | case WLAN_TDLS_DISCOVERY_REQUEST: | |
623 | ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, | |
624 | dialog_token, status_code, | |
625 | skb); | |
626 | if (ret) { | |
627 | dev_kfree_skb_any(skb); | |
628 | return ret; | |
629 | } | |
630 | if (extra_ies_len) | |
631 | memcpy(skb_put(skb, extra_ies_len), extra_ies, | |
632 | extra_ies_len); | |
633 | mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, | |
634 | priv->cfg_bssid); | |
635 | break; | |
636 | case WLAN_TDLS_SETUP_RESPONSE: | |
637 | ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, | |
638 | dialog_token, status_code, | |
639 | skb); | |
640 | if (ret) { | |
641 | dev_kfree_skb_any(skb); | |
642 | return ret; | |
643 | } | |
644 | if (extra_ies_len) | |
645 | memcpy(skb_put(skb, extra_ies_len), extra_ies, | |
646 | extra_ies_len); | |
647 | mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, | |
648 | priv->cfg_bssid); | |
649 | break; | |
650 | } | |
651 | ||
652 | switch (action_code) { | |
653 | case WLAN_TDLS_SETUP_REQUEST: | |
654 | case WLAN_TDLS_SETUP_RESPONSE: | |
655 | skb->priority = MWIFIEX_PRIO_BK; | |
656 | break; | |
657 | default: | |
658 | skb->priority = MWIFIEX_PRIO_VI; | |
659 | break; | |
660 | } | |
661 | ||
662 | tx_info = MWIFIEX_SKB_TXCB(skb); | |
701a9e61 | 663 | memset(tx_info, 0, sizeof(*tx_info)); |
b23bce29 AP |
664 | tx_info->bss_num = priv->bss_num; |
665 | tx_info->bss_type = priv->bss_type; | |
666 | ||
c64800e7 | 667 | __net_timestamp(skb); |
b23bce29 AP |
668 | mwifiex_queue_tx_pkt(priv, skb); |
669 | ||
670 | return 0; | |
671 | } | |
672 | ||
673 | static int | |
3b3a0162 JB |
674 | mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, |
675 | const u8 *peer, | |
b23bce29 AP |
676 | u8 action_code, u8 dialog_token, |
677 | u16 status_code, struct sk_buff *skb) | |
678 | { | |
679 | struct ieee80211_mgmt *mgmt; | |
680 | u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | |
681 | int ret; | |
682 | u16 capab; | |
683 | struct ieee80211_ht_cap *ht_cap; | |
684 | u8 radio, *pos; | |
685 | ||
686 | capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; | |
687 | ||
688 | mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); | |
689 | ||
690 | memset(mgmt, 0, 24); | |
691 | memcpy(mgmt->da, peer, ETH_ALEN); | |
692 | memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); | |
693 | memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); | |
694 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | |
695 | IEEE80211_STYPE_ACTION); | |
696 | ||
697 | /* add address 4 */ | |
698 | pos = skb_put(skb, ETH_ALEN); | |
699 | ||
700 | switch (action_code) { | |
701 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | |
702 | skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); | |
703 | mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; | |
704 | mgmt->u.action.u.tdls_discover_resp.action_code = | |
705 | WLAN_PUB_ACTION_TDLS_DISCOVER_RES; | |
706 | mgmt->u.action.u.tdls_discover_resp.dialog_token = | |
707 | dialog_token; | |
708 | mgmt->u.action.u.tdls_discover_resp.capability = | |
709 | cpu_to_le16(capab); | |
710 | /* move back for addr4 */ | |
711 | memmove(pos + ETH_ALEN, &mgmt->u.action.category, | |
712 | sizeof(mgmt->u.action.u.tdls_discover_resp)); | |
713 | /* init address 4 */ | |
714 | memcpy(pos, bc_addr, ETH_ALEN); | |
715 | ||
716 | ret = mwifiex_tdls_append_rates_ie(priv, skb); | |
717 | if (ret) { | |
718 | dev_kfree_skb_any(skb); | |
719 | return ret; | |
720 | } | |
721 | ||
722 | pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); | |
723 | *pos++ = WLAN_EID_HT_CAPABILITY; | |
724 | *pos++ = sizeof(struct ieee80211_ht_cap); | |
725 | ht_cap = (void *)pos; | |
726 | radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); | |
727 | ret = mwifiex_fill_cap_info(priv, radio, ht_cap); | |
728 | if (ret) { | |
729 | dev_kfree_skb_any(skb); | |
730 | return ret; | |
731 | } | |
732 | ||
5f6d5983 AP |
733 | if (priv->adapter->is_hw_11ac_capable) { |
734 | ret = mwifiex_tdls_add_vht_capab(priv, skb); | |
735 | if (ret) { | |
736 | dev_kfree_skb_any(skb); | |
737 | return ret; | |
738 | } | |
739 | mwifiex_tdls_add_aid(priv, skb); | |
740 | } | |
741 | ||
742 | mwifiex_tdls_add_ext_capab(priv, skb); | |
b23bce29 AP |
743 | mwifiex_tdls_add_qos_capab(skb); |
744 | break; | |
745 | default: | |
acebe8c1 | 746 | mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS action frame type\n"); |
b23bce29 AP |
747 | return -EINVAL; |
748 | } | |
749 | ||
750 | return 0; | |
751 | } | |
752 | ||
3b3a0162 JB |
753 | int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, |
754 | u8 action_code, u8 dialog_token, | |
755 | u16 status_code, const u8 *extra_ies, | |
756 | size_t extra_ies_len) | |
b23bce29 AP |
757 | { |
758 | struct sk_buff *skb; | |
759 | struct mwifiex_txinfo *tx_info; | |
b23bce29 AP |
760 | u8 *pos; |
761 | u32 pkt_type, tx_control; | |
762 | u16 pkt_len, skb_len; | |
763 | ||
764 | skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + | |
765 | max(sizeof(struct ieee80211_mgmt), | |
766 | sizeof(struct ieee80211_tdls_data)) + | |
767 | MWIFIEX_MGMT_FRAME_HEADER_SIZE + | |
768 | MWIFIEX_SUPPORTED_RATES + | |
769 | sizeof(struct ieee_types_extcap) + | |
770 | sizeof(struct ieee80211_ht_cap) + | |
771 | sizeof(struct ieee_types_bss_co_2040) + | |
772 | sizeof(struct ieee80211_ht_operation) + | |
773 | sizeof(struct ieee80211_tdls_lnkie) + | |
774 | extra_ies_len + | |
775 | 3 + /* Qos Info */ | |
776 | ETH_ALEN; /* Address4 */ | |
777 | ||
5f6d5983 AP |
778 | if (priv->adapter->is_hw_11ac_capable) |
779 | skb_len += sizeof(struct ieee_types_vht_cap) + | |
780 | sizeof(struct ieee_types_vht_oper) + | |
781 | sizeof(struct ieee_types_aid); | |
782 | ||
b23bce29 AP |
783 | skb = dev_alloc_skb(skb_len); |
784 | if (!skb) { | |
acebe8c1 ZL |
785 | mwifiex_dbg(priv->adapter, ERROR, |
786 | "allocate skb failed for management frame\n"); | |
b23bce29 AP |
787 | return -ENOMEM; |
788 | } | |
789 | ||
790 | skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); | |
791 | ||
792 | pkt_type = PKT_TYPE_MGMT; | |
793 | tx_control = 0; | |
794 | pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); | |
795 | memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); | |
796 | memcpy(pos, &pkt_type, sizeof(pkt_type)); | |
797 | memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); | |
798 | ||
799 | if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, | |
800 | dialog_token, status_code, | |
801 | skb)) { | |
802 | dev_kfree_skb_any(skb); | |
803 | return -EINVAL; | |
804 | } | |
805 | ||
806 | if (extra_ies_len) | |
807 | memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); | |
808 | ||
809 | /* the TDLS link IE is always added last we are the responder */ | |
810 | ||
811 | mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, | |
812 | priv->cfg_bssid); | |
813 | ||
814 | skb->priority = MWIFIEX_PRIO_VI; | |
815 | ||
816 | tx_info = MWIFIEX_SKB_TXCB(skb); | |
701a9e61 | 817 | memset(tx_info, 0, sizeof(*tx_info)); |
b23bce29 AP |
818 | tx_info->bss_num = priv->bss_num; |
819 | tx_info->bss_type = priv->bss_type; | |
820 | tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; | |
821 | ||
822 | pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); | |
823 | memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, | |
824 | sizeof(pkt_len)); | |
c64800e7 | 825 | __net_timestamp(skb); |
b23bce29 AP |
826 | mwifiex_queue_tx_pkt(priv, skb); |
827 | ||
828 | return 0; | |
829 | } | |
5f2caaf3 AP |
830 | |
831 | /* This function process tdls action frame from peer. | |
832 | * Peer capabilities are stored into station node structure. | |
833 | */ | |
834 | void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, | |
835 | u8 *buf, int len) | |
836 | { | |
837 | struct mwifiex_sta_node *sta_ptr; | |
838 | u8 *peer, *pos, *end; | |
839 | u8 i, action, basic; | |
f95f59fe | 840 | __le16 cap = 0; |
5f2caaf3 AP |
841 | int ie_len = 0; |
842 | ||
843 | if (len < (sizeof(struct ethhdr) + 3)) | |
844 | return; | |
45d18c56 | 845 | if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) |
5f2caaf3 | 846 | return; |
45d18c56 | 847 | if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) |
5f2caaf3 AP |
848 | return; |
849 | ||
850 | peer = buf + ETH_ALEN; | |
45d18c56 | 851 | action = *(buf + sizeof(struct ethhdr) + 2); |
acebe8c1 ZL |
852 | mwifiex_dbg(priv->adapter, DATA, |
853 | "rx:tdls action: peer=%pM, action=%d\n", peer, action); | |
5f2caaf3 | 854 | |
5f2caaf3 AP |
855 | switch (action) { |
856 | case WLAN_TDLS_SETUP_REQUEST: | |
857 | if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) | |
858 | return; | |
859 | ||
860 | pos = buf + sizeof(struct ethhdr) + 4; | |
861 | /* payload 1+ category 1 + action 1 + dialog 1 */ | |
f95f59fe | 862 | cap = cpu_to_le16(*(u16 *)pos); |
5f2caaf3 AP |
863 | ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; |
864 | pos += 2; | |
865 | break; | |
866 | ||
867 | case WLAN_TDLS_SETUP_RESPONSE: | |
868 | if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) | |
869 | return; | |
870 | /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ | |
871 | pos = buf + sizeof(struct ethhdr) + 6; | |
f95f59fe | 872 | cap = cpu_to_le16(*(u16 *)pos); |
5f2caaf3 AP |
873 | ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; |
874 | pos += 2; | |
875 | break; | |
876 | ||
877 | case WLAN_TDLS_SETUP_CONFIRM: | |
878 | if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) | |
879 | return; | |
880 | pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; | |
881 | ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; | |
882 | break; | |
883 | default: | |
acebe8c1 | 884 | mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); |
5f2caaf3 AP |
885 | return; |
886 | } | |
887 | ||
f95f59fe BZ |
888 | sta_ptr = mwifiex_add_sta_entry(priv, peer); |
889 | if (!sta_ptr) | |
890 | return; | |
891 | ||
892 | sta_ptr->tdls_cap.capab = cap; | |
893 | ||
5f2caaf3 AP |
894 | for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { |
895 | if (pos + 2 + pos[1] > end) | |
896 | break; | |
897 | ||
898 | switch (*pos) { | |
899 | case WLAN_EID_SUPP_RATES: | |
900 | sta_ptr->tdls_cap.rates_len = pos[1]; | |
901 | for (i = 0; i < pos[1]; i++) | |
902 | sta_ptr->tdls_cap.rates[i] = pos[i + 2]; | |
903 | break; | |
904 | ||
905 | case WLAN_EID_EXT_SUPP_RATES: | |
906 | basic = sta_ptr->tdls_cap.rates_len; | |
907 | for (i = 0; i < pos[1]; i++) | |
908 | sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; | |
909 | sta_ptr->tdls_cap.rates_len += pos[1]; | |
910 | break; | |
911 | case WLAN_EID_HT_CAPABILITY: | |
912 | memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, | |
913 | sizeof(struct ieee80211_ht_cap)); | |
914 | sta_ptr->is_11n_enabled = 1; | |
915 | break; | |
916 | case WLAN_EID_HT_OPERATION: | |
917 | memcpy(&sta_ptr->tdls_cap.ht_oper, pos, | |
918 | sizeof(struct ieee80211_ht_operation)); | |
919 | break; | |
920 | case WLAN_EID_BSS_COEX_2040: | |
921 | sta_ptr->tdls_cap.coex_2040 = pos[2]; | |
922 | break; | |
923 | case WLAN_EID_EXT_CAPABILITY: | |
924 | memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, | |
925 | sizeof(struct ieee_types_header) + | |
926 | min_t(u8, pos[1], 8)); | |
927 | break; | |
928 | case WLAN_EID_RSN: | |
929 | memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, | |
3c99832d AP |
930 | sizeof(struct ieee_types_header) + |
931 | min_t(u8, pos[1], IEEE_MAX_IE_SIZE - | |
932 | sizeof(struct ieee_types_header))); | |
5f2caaf3 AP |
933 | break; |
934 | case WLAN_EID_QOS_CAPA: | |
935 | sta_ptr->tdls_cap.qos_info = pos[2]; | |
936 | break; | |
5f6d5983 AP |
937 | case WLAN_EID_VHT_OPERATION: |
938 | if (priv->adapter->is_hw_11ac_capable) | |
939 | memcpy(&sta_ptr->tdls_cap.vhtoper, pos, | |
940 | sizeof(struct ieee80211_vht_operation)); | |
941 | break; | |
942 | case WLAN_EID_VHT_CAPABILITY: | |
943 | if (priv->adapter->is_hw_11ac_capable) { | |
944 | memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, | |
945 | sizeof(struct ieee80211_vht_cap)); | |
946 | sta_ptr->is_11ac_enabled = 1; | |
947 | } | |
948 | break; | |
949 | case WLAN_EID_AID: | |
950 | if (priv->adapter->is_hw_11ac_capable) | |
951 | sta_ptr->tdls_cap.aid = | |
952 | le16_to_cpu(*(__le16 *)(pos + 2)); | |
5f2caaf3 AP |
953 | default: |
954 | break; | |
955 | } | |
956 | } | |
957 | ||
958 | return; | |
959 | } | |
429d90d2 | 960 | |
1f4dfd8a | 961 | static int |
3b3a0162 | 962 | mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) |
1f4dfd8a AP |
963 | { |
964 | struct mwifiex_sta_node *sta_ptr; | |
965 | struct mwifiex_ds_tdls_oper tdls_oper; | |
966 | ||
967 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | |
968 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | |
969 | ||
970 | if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { | |
acebe8c1 ZL |
971 | mwifiex_dbg(priv->adapter, ERROR, |
972 | "link absent for peer %pM; cannot config\n", peer); | |
1f4dfd8a AP |
973 | return -EINVAL; |
974 | } | |
975 | ||
976 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); | |
977 | tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; | |
fa0ecbb9 BZ |
978 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
979 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | |
1f4dfd8a AP |
980 | } |
981 | ||
e48e0de0 | 982 | static int |
3b3a0162 | 983 | mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) |
e48e0de0 AP |
984 | { |
985 | struct mwifiex_sta_node *sta_ptr; | |
986 | struct mwifiex_ds_tdls_oper tdls_oper; | |
987 | ||
988 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | |
989 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | |
990 | ||
991 | if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { | |
acebe8c1 ZL |
992 | mwifiex_dbg(priv->adapter, WARN, |
993 | "Setup already in progress for peer %pM\n", peer); | |
e48e0de0 AP |
994 | return 0; |
995 | } | |
996 | ||
997 | sta_ptr = mwifiex_add_sta_entry(priv, peer); | |
998 | if (!sta_ptr) | |
999 | return -ENOMEM; | |
1000 | ||
1001 | sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; | |
56bd24a1 | 1002 | mwifiex_hold_tdls_packets(priv, peer); |
e48e0de0 AP |
1003 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); |
1004 | tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; | |
fa0ecbb9 BZ |
1005 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
1006 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | |
e48e0de0 AP |
1007 | } |
1008 | ||
429d90d2 | 1009 | static int |
3b3a0162 | 1010 | mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) |
429d90d2 AP |
1011 | { |
1012 | struct mwifiex_sta_node *sta_ptr; | |
1013 | struct mwifiex_ds_tdls_oper tdls_oper; | |
1014 | unsigned long flags; | |
1015 | ||
1016 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | |
1017 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | |
1018 | ||
1019 | if (sta_ptr) { | |
1020 | if (sta_ptr->is_11n_enabled) { | |
1021 | mwifiex_11n_cleanup_reorder_tbl(priv); | |
1022 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, | |
1023 | flags); | |
1024 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | |
1025 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | |
1026 | flags); | |
1027 | } | |
1028 | mwifiex_del_sta_entry(priv, peer); | |
1029 | } | |
1030 | ||
56bd24a1 | 1031 | mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); |
9927baa3 | 1032 | mwifiex_auto_tdls_update_peer_status(priv, peer, TDLS_NOT_SETUP); |
429d90d2 AP |
1033 | memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); |
1034 | tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; | |
fa0ecbb9 BZ |
1035 | return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
1036 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); | |
429d90d2 AP |
1037 | } |
1038 | ||
1039 | static int | |
3b3a0162 | 1040 | mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) |
429d90d2 AP |
1041 | { |
1042 | struct mwifiex_sta_node *sta_ptr; | |
1043 | struct ieee80211_mcs_info mcs; | |
1044 | unsigned long flags; | |
1045 | int i; | |
1046 | ||
1047 | sta_ptr = mwifiex_get_sta_entry(priv, peer); | |
1048 | ||
1049 | if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { | |
acebe8c1 ZL |
1050 | mwifiex_dbg(priv->adapter, MSG, |
1051 | "tdls: enable link %pM success\n", peer); | |
429d90d2 AP |
1052 | |
1053 | sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; | |
1054 | ||
1055 | mcs = sta_ptr->tdls_cap.ht_capb.mcs; | |
1056 | if (mcs.rx_mask[0] != 0xff) | |
1057 | sta_ptr->is_11n_enabled = true; | |
1058 | if (sta_ptr->is_11n_enabled) { | |
1059 | if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & | |
1060 | IEEE80211_HT_CAP_MAX_AMSDU) | |
1061 | sta_ptr->max_amsdu = | |
1062 | MWIFIEX_TX_DATA_BUF_SIZE_8K; | |
1063 | else | |
1064 | sta_ptr->max_amsdu = | |
1065 | MWIFIEX_TX_DATA_BUF_SIZE_4K; | |
1066 | ||
1067 | for (i = 0; i < MAX_NUM_TID; i++) | |
1068 | sta_ptr->ampdu_sta[i] = | |
1069 | priv->aggr_prio_tbl[i].ampdu_user; | |
1070 | } else { | |
1071 | for (i = 0; i < MAX_NUM_TID; i++) | |
1072 | sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; | |
1073 | } | |
1074 | ||
1075 | memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); | |
56bd24a1 | 1076 | mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); |
9927baa3 AP |
1077 | mwifiex_auto_tdls_update_peer_status(priv, peer, |
1078 | TDLS_SETUP_COMPLETE); | |
429d90d2 | 1079 | } else { |
acebe8c1 ZL |
1080 | mwifiex_dbg(priv->adapter, ERROR, |
1081 | "tdls: enable link %pM failed\n", peer); | |
429d90d2 AP |
1082 | if (sta_ptr) { |
1083 | mwifiex_11n_cleanup_reorder_tbl(priv); | |
1084 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, | |
1085 | flags); | |
1086 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | |
1087 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | |
1088 | flags); | |
1089 | mwifiex_del_sta_entry(priv, peer); | |
1090 | } | |
56bd24a1 | 1091 | mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); |
9927baa3 AP |
1092 | mwifiex_auto_tdls_update_peer_status(priv, peer, |
1093 | TDLS_NOT_SETUP); | |
429d90d2 AP |
1094 | |
1095 | return -1; | |
1096 | } | |
1097 | ||
1098 | return 0; | |
1099 | } | |
1100 | ||
3b3a0162 | 1101 | int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) |
429d90d2 AP |
1102 | { |
1103 | switch (action) { | |
1104 | case MWIFIEX_TDLS_ENABLE_LINK: | |
1105 | return mwifiex_tdls_process_enable_link(priv, peer); | |
1106 | case MWIFIEX_TDLS_DISABLE_LINK: | |
1107 | return mwifiex_tdls_process_disable_link(priv, peer); | |
e48e0de0 AP |
1108 | case MWIFIEX_TDLS_CREATE_LINK: |
1109 | return mwifiex_tdls_process_create_link(priv, peer); | |
1f4dfd8a AP |
1110 | case MWIFIEX_TDLS_CONFIG_LINK: |
1111 | return mwifiex_tdls_process_config_link(priv, peer); | |
429d90d2 AP |
1112 | } |
1113 | return 0; | |
1114 | } | |
d63bf5e5 | 1115 | |
3b3a0162 | 1116 | int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) |
d63bf5e5 AP |
1117 | { |
1118 | struct mwifiex_sta_node *sta_ptr; | |
1119 | ||
1120 | sta_ptr = mwifiex_get_sta_entry(priv, mac); | |
1121 | if (sta_ptr) | |
1122 | return sta_ptr->tdls_status; | |
1123 | ||
1124 | return TDLS_NOT_SETUP; | |
1125 | } | |
be104b91 | 1126 | |
72df6310 XH |
1127 | int mwifiex_get_tdls_list(struct mwifiex_private *priv, |
1128 | struct tdls_peer_info *buf) | |
1129 | { | |
1130 | struct mwifiex_sta_node *sta_ptr; | |
1131 | struct tdls_peer_info *peer = buf; | |
1132 | int count = 0; | |
1133 | unsigned long flags; | |
1134 | ||
1135 | if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) | |
1136 | return 0; | |
1137 | ||
1138 | /* make sure we are in station mode and connected */ | |
1139 | if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) | |
1140 | return 0; | |
1141 | ||
1142 | spin_lock_irqsave(&priv->sta_list_spinlock, flags); | |
1143 | list_for_each_entry(sta_ptr, &priv->sta_list, list) { | |
1144 | if (sta_ptr->tdls_status == TDLS_SETUP_COMPLETE) { | |
1145 | ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); | |
1146 | peer++; | |
1147 | count++; | |
1148 | if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED) | |
1149 | break; | |
1150 | } | |
1151 | } | |
1152 | spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); | |
1153 | ||
1154 | return count; | |
1155 | } | |
1156 | ||
be104b91 AP |
1157 | void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) |
1158 | { | |
1159 | struct mwifiex_sta_node *sta_ptr; | |
1160 | struct mwifiex_ds_tdls_oper tdls_oper; | |
1161 | unsigned long flags; | |
1162 | ||
1163 | if (list_empty(&priv->sta_list)) | |
1164 | return; | |
1165 | ||
1166 | list_for_each_entry(sta_ptr, &priv->sta_list, list) { | |
1167 | memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); | |
1168 | ||
1169 | if (sta_ptr->is_11n_enabled) { | |
1170 | mwifiex_11n_cleanup_reorder_tbl(priv); | |
1171 | spin_lock_irqsave(&priv->wmm.ra_list_spinlock, | |
1172 | flags); | |
1173 | mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); | |
1174 | spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, | |
1175 | flags); | |
1176 | } | |
1177 | ||
1178 | mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, | |
1179 | TDLS_LINK_TEARDOWN); | |
1180 | memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); | |
1181 | tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; | |
fa0ecbb9 BZ |
1182 | if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, |
1183 | HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) | |
acebe8c1 ZL |
1184 | mwifiex_dbg(priv->adapter, ERROR, |
1185 | "Disable link failed for TDLS peer %pM", | |
1186 | sta_ptr->mac_addr); | |
be104b91 AP |
1187 | } |
1188 | ||
1189 | mwifiex_del_all_sta_list(priv); | |
1190 | } | |
9927baa3 AP |
1191 | |
1192 | int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) | |
1193 | { | |
1194 | struct mwifiex_auto_tdls_peer *peer; | |
1195 | unsigned long flags; | |
1196 | u8 mac[ETH_ALEN]; | |
1197 | ||
1198 | ether_addr_copy(mac, skb->data); | |
1199 | ||
1200 | spin_lock_irqsave(&priv->auto_tdls_lock, flags); | |
1201 | list_for_each_entry(peer, &priv->auto_tdls_list, list) { | |
1202 | if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) { | |
1203 | if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && | |
1204 | peer->tdls_status == TDLS_NOT_SETUP && | |
1205 | (peer->failure_count < | |
1206 | MWIFIEX_TDLS_MAX_FAIL_COUNT)) { | |
1207 | peer->tdls_status = TDLS_SETUP_INPROGRESS; | |
acebe8c1 ZL |
1208 | mwifiex_dbg(priv->adapter, INFO, |
1209 | "setup TDLS link, peer=%pM rssi=%d\n", | |
1210 | peer->mac_addr, peer->rssi); | |
9927baa3 AP |
1211 | |
1212 | cfg80211_tdls_oper_request(priv->netdev, | |
1213 | peer->mac_addr, | |
1214 | NL80211_TDLS_SETUP, | |
1215 | 0, GFP_ATOMIC); | |
1216 | peer->do_setup = false; | |
1217 | priv->check_tdls_tx = false; | |
1218 | } else if (peer->failure_count < | |
1219 | MWIFIEX_TDLS_MAX_FAIL_COUNT && | |
1220 | peer->do_discover) { | |
1221 | mwifiex_send_tdls_data_frame(priv, | |
1222 | peer->mac_addr, | |
1223 | WLAN_TDLS_DISCOVERY_REQUEST, | |
1224 | 1, 0, NULL, 0); | |
1225 | peer->do_discover = false; | |
1226 | } | |
1227 | } | |
1228 | } | |
1229 | spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); | |
1230 | ||
1231 | return 0; | |
1232 | } | |
1233 | ||
1234 | void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv) | |
1235 | { | |
1236 | struct mwifiex_auto_tdls_peer *peer, *tmp_node; | |
1237 | unsigned long flags; | |
1238 | ||
1239 | spin_lock_irqsave(&priv->auto_tdls_lock, flags); | |
1240 | list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) { | |
1241 | list_del(&peer->list); | |
1242 | kfree(peer); | |
1243 | } | |
1244 | ||
1245 | INIT_LIST_HEAD(&priv->auto_tdls_list); | |
1246 | spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); | |
1247 | priv->check_tdls_tx = false; | |
1248 | } | |
1249 | ||
1250 | void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac) | |
1251 | { | |
1252 | struct mwifiex_auto_tdls_peer *tdls_peer; | |
1253 | unsigned long flags; | |
1254 | ||
1255 | if (!priv->adapter->auto_tdls) | |
1256 | return; | |
1257 | ||
1258 | spin_lock_irqsave(&priv->auto_tdls_lock, flags); | |
1259 | list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { | |
1260 | if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) { | |
1261 | tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; | |
1262 | tdls_peer->rssi_jiffies = jiffies; | |
1263 | spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); | |
1264 | return; | |
1265 | } | |
1266 | } | |
1267 | ||
1268 | /* create new TDLS peer */ | |
1269 | tdls_peer = kzalloc(sizeof(*tdls_peer), GFP_ATOMIC); | |
1270 | if (tdls_peer) { | |
1271 | ether_addr_copy(tdls_peer->mac_addr, mac); | |
1272 | tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; | |
1273 | tdls_peer->rssi_jiffies = jiffies; | |
1274 | INIT_LIST_HEAD(&tdls_peer->list); | |
1275 | list_add_tail(&tdls_peer->list, &priv->auto_tdls_list); | |
acebe8c1 ZL |
1276 | mwifiex_dbg(priv->adapter, INFO, |
1277 | "Add auto TDLS peer= %pM to list\n", mac); | |
9927baa3 AP |
1278 | } |
1279 | ||
1280 | spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); | |
1281 | } | |
1282 | ||
1283 | void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, | |
1284 | const u8 *mac, u8 link_status) | |
1285 | { | |
1286 | struct mwifiex_auto_tdls_peer *peer; | |
1287 | unsigned long flags; | |
1288 | ||
1289 | if (!priv->adapter->auto_tdls) | |
1290 | return; | |
1291 | ||
1292 | spin_lock_irqsave(&priv->auto_tdls_lock, flags); | |
1293 | list_for_each_entry(peer, &priv->auto_tdls_list, list) { | |
1294 | if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { | |
1295 | if ((link_status == TDLS_NOT_SETUP) && | |
1296 | (peer->tdls_status == TDLS_SETUP_INPROGRESS)) | |
1297 | peer->failure_count++; | |
1298 | else if (link_status == TDLS_SETUP_COMPLETE) | |
1299 | peer->failure_count = 0; | |
1300 | ||
1301 | peer->tdls_status = link_status; | |
1302 | break; | |
1303 | } | |
1304 | } | |
1305 | spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); | |
1306 | } | |
1307 | ||
1308 | void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, | |
1309 | u8 *mac, s8 snr, s8 nflr) | |
1310 | { | |
1311 | struct mwifiex_auto_tdls_peer *peer; | |
1312 | unsigned long flags; | |
1313 | ||
1314 | if (!priv->adapter->auto_tdls) | |
1315 | return; | |
1316 | ||
1317 | spin_lock_irqsave(&priv->auto_tdls_lock, flags); | |
1318 | list_for_each_entry(peer, &priv->auto_tdls_list, list) { | |
1319 | if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { | |
1320 | peer->rssi = nflr - snr; | |
1321 | peer->rssi_jiffies = jiffies; | |
1322 | break; | |
1323 | } | |
1324 | } | |
1325 | spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); | |
1326 | } | |
1327 | ||
1328 | void mwifiex_check_auto_tdls(unsigned long context) | |
1329 | { | |
1330 | struct mwifiex_private *priv = (struct mwifiex_private *)context; | |
1331 | struct mwifiex_auto_tdls_peer *tdls_peer; | |
1332 | unsigned long flags; | |
1333 | u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; | |
1334 | ||
1335 | if (WARN_ON_ONCE(!priv || !priv->adapter)) { | |
1336 | pr_err("mwifiex: %s: adapter or private structure is NULL\n", | |
1337 | __func__); | |
1338 | return; | |
1339 | } | |
1340 | ||
1341 | if (unlikely(!priv->adapter->auto_tdls)) | |
1342 | return; | |
1343 | ||
1344 | if (!priv->auto_tdls_timer_active) { | |
acebe8c1 ZL |
1345 | mwifiex_dbg(priv->adapter, INFO, |
1346 | "auto TDLS timer inactive; return"); | |
9927baa3 AP |
1347 | return; |
1348 | } | |
1349 | ||
1350 | priv->check_tdls_tx = false; | |
1351 | ||
1352 | if (list_empty(&priv->auto_tdls_list)) { | |
1353 | mod_timer(&priv->auto_tdls_timer, | |
1354 | jiffies + | |
1355 | msecs_to_jiffies(MWIFIEX_TIMER_10S)); | |
1356 | return; | |
1357 | } | |
1358 | ||
1359 | spin_lock_irqsave(&priv->auto_tdls_lock, flags); | |
1360 | list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { | |
1361 | if ((jiffies - tdls_peer->rssi_jiffies) > | |
1362 | (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) { | |
1363 | tdls_peer->rssi = 0; | |
1364 | tdls_peer->do_discover = true; | |
1365 | priv->check_tdls_tx = true; | |
1366 | } | |
1367 | ||
1368 | if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) || | |
1369 | !tdls_peer->rssi) && | |
1370 | tdls_peer->tdls_status == TDLS_SETUP_COMPLETE) { | |
1371 | tdls_peer->tdls_status = TDLS_LINK_TEARDOWN; | |
acebe8c1 ZL |
1372 | mwifiex_dbg(priv->adapter, MSG, |
1373 | "teardown TDLS link,peer=%pM rssi=%d\n", | |
1374 | tdls_peer->mac_addr, -tdls_peer->rssi); | |
9927baa3 AP |
1375 | tdls_peer->do_discover = true; |
1376 | priv->check_tdls_tx = true; | |
1377 | cfg80211_tdls_oper_request(priv->netdev, | |
1378 | tdls_peer->mac_addr, | |
1379 | NL80211_TDLS_TEARDOWN, | |
1380 | reason, GFP_ATOMIC); | |
1381 | } else if (tdls_peer->rssi && | |
1382 | tdls_peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && | |
1383 | tdls_peer->tdls_status == TDLS_NOT_SETUP && | |
1384 | tdls_peer->failure_count < | |
1385 | MWIFIEX_TDLS_MAX_FAIL_COUNT) { | |
1386 | priv->check_tdls_tx = true; | |
1387 | tdls_peer->do_setup = true; | |
acebe8c1 ZL |
1388 | mwifiex_dbg(priv->adapter, INFO, |
1389 | "check TDLS with peer=%pM\t" | |
1390 | "rssi=%d\n", tdls_peer->mac_addr, | |
1391 | tdls_peer->rssi); | |
9927baa3 AP |
1392 | } |
1393 | } | |
1394 | spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); | |
1395 | ||
1396 | mod_timer(&priv->auto_tdls_timer, | |
1397 | jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); | |
1398 | } | |
1399 | ||
1400 | void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv) | |
1401 | { | |
6383539b JL |
1402 | setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls, |
1403 | (unsigned long)priv); | |
9927baa3 AP |
1404 | priv->auto_tdls_timer_active = true; |
1405 | mod_timer(&priv->auto_tdls_timer, | |
1406 | jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); | |
1407 | } | |
1408 | ||
1409 | void mwifiex_clean_auto_tdls(struct mwifiex_private *priv) | |
1410 | { | |
1411 | if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && | |
1412 | priv->adapter->auto_tdls && | |
1413 | priv->bss_type == MWIFIEX_BSS_TYPE_STA) { | |
1414 | priv->auto_tdls_timer_active = false; | |
1415 | del_timer(&priv->auto_tdls_timer); | |
1416 | mwifiex_flush_auto_tdls_list(priv); | |
1417 | } | |
1418 | } |