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
11static 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
83static 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
94static 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
100static 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
112static 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
120void 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
172static 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
214static 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
227bool 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
323static 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
363void 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}
403EXPORT_SYMBOL(remove_peer_ts);
404
405void 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
434void 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

source code of linux/drivers/staging/rtl8192e/rtl819x_TSProc.c