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