1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright(c) 2008 - 2010 Realtek Corporation. All rights reserved. |
4 | * |
5 | * Contact Information: wlanfae <wlanfae@realtek.com> |
6 | */ |
7 | #include "rtllib.h" |
8 | #include <linux/etherdevice.h> |
9 | #include "rtl819x_TS.h" |
10 | |
11 | static void RxPktPendingTimeout(struct timer_list *t) |
12 | { |
13 | struct rx_ts_record *ts = from_timer(ts, t, rx_pkt_pending_timer); |
14 | struct rtllib_device *ieee = container_of(ts, struct rtllib_device, |
15 | rx_ts_records[ts->num]); |
16 | |
17 | struct rx_reorder_entry *pReorderEntry = NULL; |
18 | |
19 | unsigned long flags = 0; |
20 | u8 index = 0; |
21 | bool bPktInBuf = false; |
22 | |
23 | spin_lock_irqsave(&(ieee->reorder_spinlock), flags); |
24 | if (ts->rx_timeout_indicate_seq != 0xffff) { |
25 | while (!list_empty(head: &ts->rx_pending_pkt_list)) { |
26 | pReorderEntry = (struct rx_reorder_entry *) |
27 | list_entry(ts->rx_pending_pkt_list.prev, |
28 | struct rx_reorder_entry, list); |
29 | if (index == 0) |
30 | ts->rx_indicate_seq = pReorderEntry->SeqNum; |
31 | |
32 | if (SN_LESS(pReorderEntry->SeqNum, |
33 | ts->rx_indicate_seq) || |
34 | SN_EQUAL(pReorderEntry->SeqNum, |
35 | ts->rx_indicate_seq)) { |
36 | list_del_init(entry: &pReorderEntry->list); |
37 | |
38 | if (SN_EQUAL(pReorderEntry->SeqNum, |
39 | ts->rx_indicate_seq)) |
40 | ts->rx_indicate_seq = |
41 | (ts->rx_indicate_seq + 1) % 4096; |
42 | |
43 | netdev_dbg(ieee->dev, |
44 | "%s(): Indicate SeqNum: %d\n" , |
45 | __func__, pReorderEntry->SeqNum); |
46 | ieee->stats_IndicateArray[index] = |
47 | pReorderEntry->prxb; |
48 | index++; |
49 | |
50 | list_add_tail(new: &pReorderEntry->list, |
51 | head: &ieee->RxReorder_Unused_List); |
52 | } else { |
53 | bPktInBuf = true; |
54 | break; |
55 | } |
56 | } |
57 | } |
58 | |
59 | if (index > 0) { |
60 | ts->rx_timeout_indicate_seq = 0xffff; |
61 | |
62 | if (index > REORDER_WIN_SIZE) { |
63 | netdev_warn(dev: ieee->dev, |
64 | format: "%s(): Rx Reorder struct buffer full\n" , |
65 | __func__); |
66 | spin_unlock_irqrestore(lock: &(ieee->reorder_spinlock), |
67 | flags); |
68 | return; |
69 | } |
70 | rtllib_indicate_packets(ieee, prxbIndicateArray: ieee->stats_IndicateArray, index); |
71 | bPktInBuf = false; |
72 | } |
73 | |
74 | if (bPktInBuf && (ts->rx_timeout_indicate_seq == 0xffff)) { |
75 | ts->rx_timeout_indicate_seq = ts->rx_indicate_seq; |
76 | mod_timer(timer: &ts->rx_pkt_pending_timer, expires: jiffies + |
77 | msecs_to_jiffies(m: ieee->ht_info->rx_reorder_pending_time) |
78 | ); |
79 | } |
80 | spin_unlock_irqrestore(lock: &(ieee->reorder_spinlock), flags); |
81 | } |
82 | |
83 | static void TsAddBaProcess(struct timer_list *t) |
84 | { |
85 | struct tx_ts_record *ts = from_timer(ts, t, ts_add_ba_timer); |
86 | u8 num = ts->num; |
87 | struct rtllib_device *ieee = container_of(ts, struct rtllib_device, |
88 | tx_ts_records[num]); |
89 | |
90 | rtllib_ts_init_add_ba(ieee, ts, BA_POLICY_IMMEDIATE, overwrite_pending: false); |
91 | netdev_dbg(ieee->dev, "%s(): ADDBA Req is started\n" , __func__); |
92 | } |
93 | |
94 | static void ResetTsCommonInfo(struct ts_common_info *ts_common_info) |
95 | { |
96 | eth_zero_addr(addr: ts_common_info->addr); |
97 | memset(&ts_common_info->tspec, 0, sizeof(struct qos_tsinfo)); |
98 | } |
99 | |
100 | static void ResetTxTsEntry(struct tx_ts_record *ts) |
101 | { |
102 | ResetTsCommonInfo(ts_common_info: &ts->ts_common_info); |
103 | ts->tx_cur_seq = 0; |
104 | ts->add_ba_req_in_progress = false; |
105 | ts->add_ba_req_delayed = false; |
106 | ts->using_ba = false; |
107 | ts->disable_add_ba = false; |
108 | rtllib_reset_ba_entry(ba: &ts->tx_admitted_ba_record); |
109 | rtllib_reset_ba_entry(ba: &ts->tx_pending_ba_record); |
110 | } |
111 | |
112 | static void ResetRxTsEntry(struct rx_ts_record *ts) |
113 | { |
114 | ResetTsCommonInfo(ts_common_info: &ts->ts_common_info); |
115 | ts->rx_indicate_seq = 0xffff; |
116 | ts->rx_timeout_indicate_seq = 0xffff; |
117 | rtllib_reset_ba_entry(ba: &ts->rx_admitted_ba_record); |
118 | } |
119 | |
120 | void rtllib_ts_init(struct rtllib_device *ieee) |
121 | { |
122 | struct tx_ts_record *pTxTS = ieee->tx_ts_records; |
123 | struct rx_ts_record *rxts = ieee->rx_ts_records; |
124 | struct rx_reorder_entry *pRxReorderEntry = ieee->RxReorderEntry; |
125 | u8 count = 0; |
126 | |
127 | INIT_LIST_HEAD(list: &ieee->Tx_TS_Admit_List); |
128 | INIT_LIST_HEAD(list: &ieee->Tx_TS_Pending_List); |
129 | INIT_LIST_HEAD(list: &ieee->Tx_TS_Unused_List); |
130 | |
131 | for (count = 0; count < TOTAL_TS_NUM; count++) { |
132 | pTxTS->num = count; |
133 | timer_setup(&pTxTS->ts_add_ba_timer, TsAddBaProcess, 0); |
134 | |
135 | timer_setup(&pTxTS->tx_pending_ba_record.timer, rtllib_ba_setup_timeout, |
136 | 0); |
137 | timer_setup(&pTxTS->tx_admitted_ba_record.timer, |
138 | rtllib_tx_ba_inact_timeout, 0); |
139 | |
140 | ResetTxTsEntry(ts: pTxTS); |
141 | list_add_tail(new: &pTxTS->ts_common_info.list, |
142 | head: &ieee->Tx_TS_Unused_List); |
143 | pTxTS++; |
144 | } |
145 | |
146 | INIT_LIST_HEAD(list: &ieee->Rx_TS_Admit_List); |
147 | INIT_LIST_HEAD(list: &ieee->Rx_TS_Pending_List); |
148 | INIT_LIST_HEAD(list: &ieee->Rx_TS_Unused_List); |
149 | for (count = 0; count < TOTAL_TS_NUM; count++) { |
150 | rxts->num = count; |
151 | INIT_LIST_HEAD(list: &rxts->rx_pending_pkt_list); |
152 | timer_setup(&rxts->rx_admitted_ba_record.timer, |
153 | rtllib_rx_ba_inact_timeout, 0); |
154 | |
155 | timer_setup(&rxts->rx_pkt_pending_timer, RxPktPendingTimeout, 0); |
156 | |
157 | ResetRxTsEntry(ts: rxts); |
158 | list_add_tail(new: &rxts->ts_common_info.list, |
159 | head: &ieee->Rx_TS_Unused_List); |
160 | rxts++; |
161 | } |
162 | INIT_LIST_HEAD(list: &ieee->RxReorder_Unused_List); |
163 | for (count = 0; count < REORDER_ENTRY_NUM; count++) { |
164 | list_add_tail(new: &pRxReorderEntry->list, |
165 | head: &ieee->RxReorder_Unused_List); |
166 | if (count == (REORDER_ENTRY_NUM - 1)) |
167 | break; |
168 | pRxReorderEntry = &ieee->RxReorderEntry[count + 1]; |
169 | } |
170 | } |
171 | |
172 | static struct ts_common_info *SearchAdmitTRStream(struct rtllib_device *ieee, |
173 | u8 *addr, u8 TID, |
174 | enum tr_select tx_rx_select) |
175 | { |
176 | u8 dir; |
177 | bool search_dir[4] = {0}; |
178 | struct list_head *psearch_list; |
179 | struct ts_common_info *pRet = NULL; |
180 | |
181 | if (tx_rx_select == TX_DIR) { |
182 | search_dir[DIR_UP] = true; |
183 | search_dir[DIR_BI_DIR] = true; |
184 | search_dir[DIR_DIRECT] = true; |
185 | } else { |
186 | search_dir[DIR_DOWN] = true; |
187 | search_dir[DIR_BI_DIR] = true; |
188 | search_dir[DIR_DIRECT] = true; |
189 | } |
190 | |
191 | if (tx_rx_select == TX_DIR) |
192 | psearch_list = &ieee->Tx_TS_Admit_List; |
193 | else |
194 | psearch_list = &ieee->Rx_TS_Admit_List; |
195 | |
196 | for (dir = 0; dir <= DIR_BI_DIR; dir++) { |
197 | if (!search_dir[dir]) |
198 | continue; |
199 | list_for_each_entry(pRet, psearch_list, list) { |
200 | if (memcmp(p: pRet->addr, q: addr, size: 6) == 0 && |
201 | pRet->tspec.ts_id == TID && |
202 | pRet->tspec.ucDirection == dir) |
203 | break; |
204 | } |
205 | if (&pRet->list != psearch_list) |
206 | break; |
207 | } |
208 | |
209 | if (pRet && &pRet->list != psearch_list) |
210 | return pRet; |
211 | return NULL; |
212 | } |
213 | |
214 | static void MakeTSEntry(struct ts_common_info *ts_common_info, u8 *addr, |
215 | struct qos_tsinfo *pTSPEC) |
216 | { |
217 | if (!ts_common_info) |
218 | return; |
219 | |
220 | memcpy(ts_common_info->addr, addr, 6); |
221 | |
222 | if (pTSPEC) |
223 | memcpy((u8 *)(&(ts_common_info->tspec)), (u8 *)pTSPEC, |
224 | sizeof(struct qos_tsinfo)); |
225 | } |
226 | |
227 | bool rtllib_get_ts(struct rtllib_device *ieee, struct ts_common_info **ppTS, |
228 | u8 *addr, u8 TID, enum tr_select tx_rx_select, bool bAddNewTs) |
229 | { |
230 | u8 UP = 0; |
231 | struct qos_tsinfo tspec; |
232 | struct qos_tsinfo *ts_info = &tspec; |
233 | struct list_head *pUnusedList; |
234 | struct list_head *pAddmitList; |
235 | enum direction_value Dir; |
236 | |
237 | if (is_multicast_ether_addr(addr)) { |
238 | netdev_warn(dev: ieee->dev, format: "Get TS for Broadcast or Multicast\n" ); |
239 | return false; |
240 | } |
241 | if (ieee->current_network.qos_data.supported == 0) { |
242 | UP = 0; |
243 | } else { |
244 | switch (TID) { |
245 | case 0: |
246 | case 3: |
247 | UP = 0; |
248 | break; |
249 | case 1: |
250 | case 2: |
251 | UP = 2; |
252 | break; |
253 | case 4: |
254 | case 5: |
255 | UP = 5; |
256 | break; |
257 | case 6: |
258 | case 7: |
259 | UP = 7; |
260 | break; |
261 | default: |
262 | netdev_warn(dev: ieee->dev, format: "%s(): TID(%d) is not valid\n" , |
263 | __func__, TID); |
264 | return false; |
265 | } |
266 | } |
267 | |
268 | *ppTS = SearchAdmitTRStream(ieee, addr, TID: UP, tx_rx_select); |
269 | if (*ppTS) |
270 | return true; |
271 | |
272 | if (!bAddNewTs) { |
273 | netdev_dbg(ieee->dev, "add new TS failed(tid:%d)\n" , UP); |
274 | return false; |
275 | } |
276 | |
277 | pUnusedList = (tx_rx_select == TX_DIR) ? |
278 | (&ieee->Tx_TS_Unused_List) : |
279 | (&ieee->Rx_TS_Unused_List); |
280 | |
281 | pAddmitList = (tx_rx_select == TX_DIR) ? |
282 | (&ieee->Tx_TS_Admit_List) : |
283 | (&ieee->Rx_TS_Admit_List); |
284 | |
285 | Dir = ((tx_rx_select == TX_DIR) ? DIR_UP : DIR_DOWN); |
286 | |
287 | if (!list_empty(head: pUnusedList)) { |
288 | (*ppTS) = list_entry(pUnusedList->next, |
289 | struct ts_common_info, list); |
290 | list_del_init(entry: &(*ppTS)->list); |
291 | if (tx_rx_select == TX_DIR) { |
292 | struct tx_ts_record *tmp = |
293 | container_of(*ppTS, |
294 | struct tx_ts_record, |
295 | ts_common_info); |
296 | ResetTxTsEntry(ts: tmp); |
297 | } else { |
298 | struct rx_ts_record *ts = |
299 | container_of(*ppTS, |
300 | struct rx_ts_record, |
301 | ts_common_info); |
302 | ResetRxTsEntry(ts); |
303 | } |
304 | |
305 | netdev_dbg(ieee->dev, |
306 | "to init current TS, UP:%d, Dir:%d, addr: %pM ppTs=%p\n" , |
307 | UP, Dir, addr, *ppTS); |
308 | ts_info->ts_id = UP; |
309 | ts_info->ucDirection = Dir; |
310 | |
311 | MakeTSEntry(ts_common_info: *ppTS, addr, pTSPEC: &tspec); |
312 | list_add_tail(new: &((*ppTS)->list), head: pAddmitList); |
313 | |
314 | return true; |
315 | } |
316 | |
317 | netdev_warn(dev: ieee->dev, |
318 | format: "There is not enough dir=%d(0=up down=1) TS record to be used!" , |
319 | Dir); |
320 | return false; |
321 | } |
322 | |
323 | static void RemoveTsEntry(struct rtllib_device *ieee, |
324 | struct ts_common_info *pTs, enum tr_select tx_rx_select) |
325 | { |
326 | rtllib_ts_init_del_ba(ieee, ts_common_info: pTs, tx_rx_select); |
327 | |
328 | if (tx_rx_select == RX_DIR) { |
329 | struct rx_reorder_entry *pRxReorderEntry; |
330 | struct rx_ts_record *ts = (struct rx_ts_record *)pTs; |
331 | |
332 | if (timer_pending(timer: &ts->rx_pkt_pending_timer)) |
333 | del_timer_sync(timer: &ts->rx_pkt_pending_timer); |
334 | |
335 | while (!list_empty(head: &ts->rx_pending_pkt_list)) { |
336 | pRxReorderEntry = (struct rx_reorder_entry *) |
337 | list_entry(ts->rx_pending_pkt_list.prev, |
338 | struct rx_reorder_entry, list); |
339 | netdev_dbg(ieee->dev, "%s(): Delete SeqNum %d!\n" , |
340 | __func__, pRxReorderEntry->SeqNum); |
341 | list_del_init(entry: &pRxReorderEntry->list); |
342 | { |
343 | int i = 0; |
344 | struct rtllib_rxb *prxb = pRxReorderEntry->prxb; |
345 | |
346 | if (unlikely(!prxb)) |
347 | return; |
348 | for (i = 0; i < prxb->nr_subframes; i++) |
349 | dev_kfree_skb(prxb->subframes[i]); |
350 | kfree(objp: prxb); |
351 | prxb = NULL; |
352 | } |
353 | list_add_tail(new: &pRxReorderEntry->list, |
354 | head: &ieee->RxReorder_Unused_List); |
355 | } |
356 | } else { |
357 | struct tx_ts_record *pTxTS = (struct tx_ts_record *)pTs; |
358 | |
359 | del_timer_sync(timer: &pTxTS->ts_add_ba_timer); |
360 | } |
361 | } |
362 | |
363 | void remove_peer_ts(struct rtllib_device *ieee, u8 *addr) |
364 | { |
365 | struct ts_common_info *ts, *pTmpTS; |
366 | |
367 | netdev_info(dev: ieee->dev, format: "===========>%s, %pM\n" , __func__, addr); |
368 | |
369 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Pending_List, list) { |
370 | if (memcmp(p: ts->addr, q: addr, size: 6) == 0) { |
371 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: TX_DIR); |
372 | list_del_init(entry: &ts->list); |
373 | list_add_tail(new: &ts->list, head: &ieee->Tx_TS_Unused_List); |
374 | } |
375 | } |
376 | |
377 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Admit_List, list) { |
378 | if (memcmp(p: ts->addr, q: addr, size: 6) == 0) { |
379 | netdev_info(dev: ieee->dev, |
380 | format: "====>remove Tx_TS_admin_list\n" ); |
381 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: TX_DIR); |
382 | list_del_init(entry: &ts->list); |
383 | list_add_tail(new: &ts->list, head: &ieee->Tx_TS_Unused_List); |
384 | } |
385 | } |
386 | |
387 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Pending_List, list) { |
388 | if (memcmp(p: ts->addr, q: addr, size: 6) == 0) { |
389 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: RX_DIR); |
390 | list_del_init(entry: &ts->list); |
391 | list_add_tail(new: &ts->list, head: &ieee->Rx_TS_Unused_List); |
392 | } |
393 | } |
394 | |
395 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Admit_List, list) { |
396 | if (memcmp(p: ts->addr, q: addr, size: 6) == 0) { |
397 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: RX_DIR); |
398 | list_del_init(entry: &ts->list); |
399 | list_add_tail(new: &ts->list, head: &ieee->Rx_TS_Unused_List); |
400 | } |
401 | } |
402 | } |
403 | EXPORT_SYMBOL(remove_peer_ts); |
404 | |
405 | void remove_all_ts(struct rtllib_device *ieee) |
406 | { |
407 | struct ts_common_info *ts, *pTmpTS; |
408 | |
409 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Pending_List, list) { |
410 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: TX_DIR); |
411 | list_del_init(entry: &ts->list); |
412 | list_add_tail(new: &ts->list, head: &ieee->Tx_TS_Unused_List); |
413 | } |
414 | |
415 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Tx_TS_Admit_List, list) { |
416 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: TX_DIR); |
417 | list_del_init(entry: &ts->list); |
418 | list_add_tail(new: &ts->list, head: &ieee->Tx_TS_Unused_List); |
419 | } |
420 | |
421 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Pending_List, list) { |
422 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: RX_DIR); |
423 | list_del_init(entry: &ts->list); |
424 | list_add_tail(new: &ts->list, head: &ieee->Rx_TS_Unused_List); |
425 | } |
426 | |
427 | list_for_each_entry_safe(ts, pTmpTS, &ieee->Rx_TS_Admit_List, list) { |
428 | RemoveTsEntry(ieee, pTs: ts, tx_rx_select: RX_DIR); |
429 | list_del_init(entry: &ts->list); |
430 | list_add_tail(new: &ts->list, head: &ieee->Rx_TS_Unused_List); |
431 | } |
432 | } |
433 | |
434 | void TsStartAddBaProcess(struct rtllib_device *ieee, struct tx_ts_record *pTxTS) |
435 | { |
436 | if (pTxTS->add_ba_req_in_progress == false) { |
437 | pTxTS->add_ba_req_in_progress = true; |
438 | |
439 | if (pTxTS->add_ba_req_delayed) { |
440 | netdev_dbg(ieee->dev, "Start ADDBA after 60 sec!!\n" ); |
441 | mod_timer(timer: &pTxTS->ts_add_ba_timer, expires: jiffies + |
442 | msecs_to_jiffies(TS_ADDBA_DELAY)); |
443 | } else { |
444 | netdev_dbg(ieee->dev, "Immediately Start ADDBA\n" ); |
445 | mod_timer(timer: &pTxTS->ts_add_ba_timer, expires: jiffies + 10); |
446 | } |
447 | } else { |
448 | netdev_dbg(ieee->dev, "BA timer is already added\n" ); |
449 | } |
450 | } |
451 | |