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