GNU Linux-libre 5.10.215-gnu1
[releases.git] / drivers / staging / rtl8192u / ieee80211 / rtl819x_TSProc.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include "ieee80211.h"
3 #include <linux/etherdevice.h>
4 #include <linux/slab.h>
5 #include "rtl819x_TS.h"
6
7 static void TsSetupTimeOut(struct timer_list *unused)
8 {
9         // Not implement yet
10         // This is used for WMMSA and ACM , that would send ADDTSReq frame.
11 }
12
13 static void TsInactTimeout(struct timer_list *unused)
14 {
15         // Not implement yet
16         // This is used for WMMSA and ACM.
17         // This function would be call when TS is no Tx/Rx for some period of time.
18 }
19
20 /********************************************************************************************************************
21  *function:  I still not understand this function, so wait for further implementation
22  *   input:  unsigned long       data           //acturally we send struct tx_ts_record or struct rx_ts_record to these timer
23  *  return:  NULL
24  *  notice:
25  ********************************************************************************************************************/
26 static void RxPktPendingTimeout(struct timer_list *t)
27 {
28         struct rx_ts_record     *pRxTs = from_timer(pRxTs, t, rx_pkt_pending_timer);
29         struct ieee80211_device *ieee = container_of(pRxTs, struct ieee80211_device, RxTsRecord[pRxTs->num]);
30
31         struct rx_reorder_entry *pReorderEntry = NULL;
32
33         //u32 flags = 0;
34         unsigned long flags = 0;
35         u8 index = 0;
36         bool bPktInBuf = false;
37
38         spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
39         IEEE80211_DEBUG(IEEE80211_DL_REORDER, "==================>%s()\n", __func__);
40         if (pRxTs->rx_timeout_indicate_seq != 0xffff) {
41                 // Indicate the pending packets sequentially according to SeqNum until meet the gap.
42                 while (!list_empty(&pRxTs->rx_pending_pkt_list)) {
43                         pReorderEntry = list_entry(pRxTs->rx_pending_pkt_list.prev, struct rx_reorder_entry, List);
44                         if (index == 0)
45                                 pRxTs->rx_indicate_seq = pReorderEntry->SeqNum;
46
47                         if (SN_LESS(pReorderEntry->SeqNum, pRxTs->rx_indicate_seq) ||
48                                 SN_EQUAL(pReorderEntry->SeqNum, pRxTs->rx_indicate_seq)) {
49                                 list_del_init(&pReorderEntry->List);
50
51                                 if (SN_EQUAL(pReorderEntry->SeqNum, pRxTs->rx_indicate_seq))
52                                         pRxTs->rx_indicate_seq = (pRxTs->rx_indicate_seq + 1) % 4096;
53
54                                 IEEE80211_DEBUG(IEEE80211_DL_REORDER, "%s: IndicateSeq: %d\n", __func__, pReorderEntry->SeqNum);
55                                 ieee->stats_IndicateArray[index] = pReorderEntry->prxb;
56                                 index++;
57
58                                 list_add_tail(&pReorderEntry->List, &ieee->RxReorder_Unused_List);
59                         } else {
60                                 bPktInBuf = true;
61                                 break;
62                         }
63                 }
64         }
65
66         if (index > 0) {
67                 // Set rx_timeout_indicate_seq to 0xffff to indicate no pending packets in buffer now.
68                 pRxTs->rx_timeout_indicate_seq = 0xffff;
69
70                 // Indicate packets
71                 if (index > REORDER_WIN_SIZE) {
72                         IEEE80211_DEBUG(IEEE80211_DL_ERR, "RxReorderIndicatePacket(): Rx Reorder buffer full!! \n");
73                         spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
74                         return;
75                 }
76                 ieee80211_indicate_packets(ieee, ieee->stats_IndicateArray, index);
77         }
78
79         if (bPktInBuf && (pRxTs->rx_timeout_indicate_seq == 0xffff)) {
80                 pRxTs->rx_timeout_indicate_seq = pRxTs->rx_indicate_seq;
81                 mod_timer(&pRxTs->rx_pkt_pending_timer,
82                           jiffies + msecs_to_jiffies(ieee->pHTInfo->RxReorderPendingTime));
83         }
84         spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
85 }
86
87 /********************************************************************************************************************
88  *function:  Add BA timer function
89  *   input:  unsigned long       data           //acturally we send struct tx_ts_record or struct rx_ts_record to these timer
90  *  return:  NULL
91  *  notice:
92  ********************************************************************************************************************/
93 static void TsAddBaProcess(struct timer_list *t)
94 {
95         struct tx_ts_record *pTxTs = from_timer(pTxTs, t, ts_add_ba_timer);
96         u8 num = pTxTs->num;
97         struct ieee80211_device *ieee = container_of(pTxTs, struct ieee80211_device, TxTsRecord[num]);
98
99         TsInitAddBA(ieee, pTxTs, BA_POLICY_IMMEDIATE, false);
100         IEEE80211_DEBUG(IEEE80211_DL_BA, "%s: ADDBA Req is started!! \n", __func__);
101 }
102
103
104 static void ResetTsCommonInfo(struct ts_common_info *pTsCommonInfo)
105 {
106         eth_zero_addr(pTsCommonInfo->addr);
107         memset(&pTsCommonInfo->t_spec, 0, sizeof(struct tspec_body));
108         memset(&pTsCommonInfo->t_class, 0, sizeof(union qos_tclas) * TCLAS_NUM);
109         pTsCommonInfo->t_clas_proc = 0;
110         pTsCommonInfo->t_clas_num = 0;
111 }
112
113 static void ResetTxTsEntry(struct tx_ts_record *pTS)
114 {
115         ResetTsCommonInfo(&pTS->ts_common_info);
116         pTS->tx_cur_seq = 0;
117         pTS->add_ba_req_in_progress = false;
118         pTS->add_ba_req_delayed = false;
119         pTS->using_ba = false;
120         ResetBaEntry(&pTS->tx_admitted_ba_record); //For BA Originator
121         ResetBaEntry(&pTS->tx_pending_ba_record);
122 }
123
124 static void ResetRxTsEntry(struct rx_ts_record *pTS)
125 {
126         ResetTsCommonInfo(&pTS->ts_common_info);
127         pTS->rx_indicate_seq = 0xffff; // This indicate the rx_indicate_seq is not used now!!
128         pTS->rx_timeout_indicate_seq = 0xffff; // This indicate the rx_timeout_indicate_seq is not used now!!
129         ResetBaEntry(&pTS->rx_admitted_ba_record);        // For BA Recipient
130 }
131
132 void TSInitialize(struct ieee80211_device *ieee)
133 {
134         struct tx_ts_record     *pTxTS  = ieee->TxTsRecord;
135         struct rx_ts_record     *pRxTS  = ieee->RxTsRecord;
136         struct rx_reorder_entry *pRxReorderEntry = ieee->RxReorderEntry;
137         u8                              count = 0;
138         IEEE80211_DEBUG(IEEE80211_DL_TS, "==========>%s()\n", __func__);
139         // Initialize Tx TS related info.
140         INIT_LIST_HEAD(&ieee->Tx_TS_Admit_List);
141         INIT_LIST_HEAD(&ieee->Tx_TS_Pending_List);
142         INIT_LIST_HEAD(&ieee->Tx_TS_Unused_List);
143
144         for (count = 0; count < TOTAL_TS_NUM; count++) {
145                 //
146                 pTxTS->num = count;
147                 // The timers for the operation of Traffic Stream and Block Ack.
148                 // DLS related timer will be add here in the future!!
149                 timer_setup(&pTxTS->ts_common_info.setup_timer, TsSetupTimeOut,
150                             0);
151                 timer_setup(&pTxTS->ts_common_info.inact_timer, TsInactTimeout,
152                             0);
153                 timer_setup(&pTxTS->ts_add_ba_timer, TsAddBaProcess, 0);
154                 timer_setup(&pTxTS->tx_pending_ba_record.timer, BaSetupTimeOut,
155                             0);
156                 timer_setup(&pTxTS->tx_admitted_ba_record.timer,
157                             TxBaInactTimeout, 0);
158                 ResetTxTsEntry(pTxTS);
159                 list_add_tail(&pTxTS->ts_common_info.list, &ieee->Tx_TS_Unused_List);
160                 pTxTS++;
161         }
162
163         // Initialize Rx TS related info.
164         INIT_LIST_HEAD(&ieee->Rx_TS_Admit_List);
165         INIT_LIST_HEAD(&ieee->Rx_TS_Pending_List);
166         INIT_LIST_HEAD(&ieee->Rx_TS_Unused_List);
167         for (count = 0; count < TOTAL_TS_NUM; count++) {
168                 pRxTS->num = count;
169                 INIT_LIST_HEAD(&pRxTS->rx_pending_pkt_list);
170                 timer_setup(&pRxTS->ts_common_info.setup_timer, TsSetupTimeOut,
171                             0);
172                 timer_setup(&pRxTS->ts_common_info.inact_timer, TsInactTimeout,
173                             0);
174                 timer_setup(&pRxTS->rx_admitted_ba_record.timer,
175                             RxBaInactTimeout, 0);
176                 timer_setup(&pRxTS->rx_pkt_pending_timer, RxPktPendingTimeout, 0);
177                 ResetRxTsEntry(pRxTS);
178                 list_add_tail(&pRxTS->ts_common_info.list, &ieee->Rx_TS_Unused_List);
179                 pRxTS++;
180         }
181         // Initialize unused Rx Reorder List.
182         INIT_LIST_HEAD(&ieee->RxReorder_Unused_List);
183         for (count = 0; count < REORDER_ENTRY_NUM; count++) {
184                 list_add_tail(&pRxReorderEntry->List, &ieee->RxReorder_Unused_List);
185                 if (count == (REORDER_ENTRY_NUM - 1))
186                         break;
187                 pRxReorderEntry = &ieee->RxReorderEntry[count + 1];
188         }
189 }
190
191 static void AdmitTS(struct ieee80211_device *ieee,
192                     struct ts_common_info *pTsCommonInfo, u32 InactTime)
193 {
194         del_timer_sync(&pTsCommonInfo->setup_timer);
195         del_timer_sync(&pTsCommonInfo->inact_timer);
196
197         if (InactTime != 0)
198                 mod_timer(&pTsCommonInfo->inact_timer,
199                           jiffies + msecs_to_jiffies(InactTime));
200 }
201
202
203 static struct ts_common_info *SearchAdmitTRStream(struct ieee80211_device *ieee,
204                                                   u8 *Addr, u8 TID,
205                                                   enum tr_select TxRxSelect)
206 {
207         //DIRECTION_VALUE       dir;
208         u8      dir;
209         bool                            search_dir[4] = {0};
210         struct list_head                *psearch_list; //FIXME
211         struct ts_common_info   *pRet = NULL;
212         if (ieee->iw_mode == IW_MODE_MASTER) { //ap mode
213                 if (TxRxSelect == TX_DIR) {
214                         search_dir[DIR_DOWN] = true;
215                         search_dir[DIR_BI_DIR] = true;
216                 } else {
217                         search_dir[DIR_UP]      = true;
218                         search_dir[DIR_BI_DIR] = true;
219                 }
220         } else if (ieee->iw_mode == IW_MODE_ADHOC) {
221                 if (TxRxSelect == TX_DIR)
222                         search_dir[DIR_UP]      = true;
223                 else
224                         search_dir[DIR_DOWN] = true;
225         } else {
226                 if (TxRxSelect == TX_DIR) {
227                         search_dir[DIR_UP]      = true;
228                         search_dir[DIR_BI_DIR] = true;
229                         search_dir[DIR_DIRECT] = true;
230                 } else {
231                         search_dir[DIR_DOWN] = true;
232                         search_dir[DIR_BI_DIR] = true;
233                         search_dir[DIR_DIRECT] = true;
234                 }
235         }
236
237         if (TxRxSelect == TX_DIR)
238                 psearch_list = &ieee->Tx_TS_Admit_List;
239         else
240                 psearch_list = &ieee->Rx_TS_Admit_List;
241
242         //for(dir = DIR_UP; dir <= DIR_BI_DIR; dir++)
243         for (dir = 0; dir <= DIR_BI_DIR; dir++) {
244                 if (!search_dir[dir])
245                         continue;
246                 list_for_each_entry(pRet, psearch_list, list) {
247         //              IEEE80211_DEBUG(IEEE80211_DL_TS, "ADD:%pM, TID:%d, dir:%d\n", pRet->Addr, pRet->TSpec.ts_info.ucTSID, pRet->TSpec.ts_info.ucDirection);
248                         if (memcmp(pRet->addr, Addr, 6) == 0)
249                                 if (pRet->t_spec.ts_info.uc_tsid == TID)
250                                         if (pRet->t_spec.ts_info.uc_direction == dir) {
251         //                                      printk("Bingo! got it\n");
252                                                 break;
253                                         }
254                 }
255                 if (&pRet->list  != psearch_list)
256                         break;
257         }
258
259         if (&pRet->list  != psearch_list)
260                 return pRet;
261         else
262                 return NULL;
263 }
264
265 static void MakeTSEntry(struct ts_common_info *pTsCommonInfo, u8 *Addr,
266                         struct tspec_body *pTSPEC, union qos_tclas *pTCLAS, u8 TCLAS_Num,
267                         u8 TCLAS_Proc)
268 {
269         u8      count;
270
271         if (pTsCommonInfo == NULL)
272                 return;
273
274         memcpy(pTsCommonInfo->addr, Addr, 6);
275
276         if (pTSPEC != NULL)
277                 memcpy((u8 *)(&(pTsCommonInfo->t_spec)), (u8 *)pTSPEC, sizeof(struct tspec_body));
278
279         for (count = 0; count < TCLAS_Num; count++)
280                 memcpy((u8 *)(&(pTsCommonInfo->t_class[count])), (u8 *)pTCLAS, sizeof(union qos_tclas));
281
282         pTsCommonInfo->t_clas_proc = TCLAS_Proc;
283         pTsCommonInfo->t_clas_num = TCLAS_Num;
284 }
285
286
287 bool GetTs(
288         struct ieee80211_device         *ieee,
289         struct ts_common_info           **ppTS,
290         u8                              *Addr,
291         u8                              TID,
292         enum tr_select                  TxRxSelect,  //Rx:1, Tx:0
293         bool                            bAddNewTs
294         )
295 {
296         u8      UP = 0;
297         //
298         // We do not build any TS for Broadcast or Multicast stream.
299         // So reject these kinds of search here.
300         //
301         if (is_multicast_ether_addr(Addr)) {
302                 IEEE80211_DEBUG(IEEE80211_DL_ERR, "get TS for Broadcast or Multicast\n");
303                 return false;
304         }
305
306         if (ieee->current_network.qos_data.supported == 0) {
307                 UP = 0;
308         } else {
309                 // In WMM case: we use 4 TID only
310                 if (!is_ac_valid(TID)) {
311                         IEEE80211_DEBUG(IEEE80211_DL_ERR, " in %s(), TID(%d) is not valid\n", __func__, TID);
312                         return false;
313                 }
314
315                 switch (TID) {
316                 case 0:
317                 case 3:
318                         UP = 0;
319                         break;
320
321                 case 1:
322                 case 2:
323                         UP = 2;
324                         break;
325
326                 case 4:
327                 case 5:
328                         UP = 5;
329                         break;
330
331                 case 6:
332                 case 7:
333                         UP = 7;
334                         break;
335                 }
336         }
337
338         *ppTS = SearchAdmitTRStream(
339                         ieee,
340                         Addr,
341                         UP,
342                         TxRxSelect);
343         if (*ppTS != NULL) {
344                 return true;
345         } else {
346                 if (!bAddNewTs) {
347                         IEEE80211_DEBUG(IEEE80211_DL_TS, "add new TS failed(tid:%d)\n", UP);
348                         return false;
349                 } else {
350                         //
351                         // Create a new Traffic stream for current Tx/Rx
352                         // This is for EDCA and WMM to add a new TS.
353                         // For HCCA or WMMSA, TS cannot be addmit without negotiation.
354                         //
355                         struct tspec_body       TSpec;
356                         struct qos_tsinfo       *pTSInfo = &TSpec.ts_info;
357                         struct list_head        *pUnusedList =
358                                                                 (TxRxSelect == TX_DIR) ?
359                                                                 (&ieee->Tx_TS_Unused_List) :
360                                                                 (&ieee->Rx_TS_Unused_List);
361
362                         struct list_head        *pAddmitList =
363                                                                 (TxRxSelect == TX_DIR) ?
364                                                                 (&ieee->Tx_TS_Admit_List) :
365                                                                 (&ieee->Rx_TS_Admit_List);
366
367                         enum direction_value    Dir =           (ieee->iw_mode == IW_MODE_MASTER) ?
368                                                                 ((TxRxSelect == TX_DIR) ? DIR_DOWN : DIR_UP) :
369                                                                 ((TxRxSelect == TX_DIR) ? DIR_UP : DIR_DOWN);
370                         IEEE80211_DEBUG(IEEE80211_DL_TS, "to add Ts\n");
371                         if (!list_empty(pUnusedList)) {
372                                 (*ppTS) = list_entry(pUnusedList->next, struct ts_common_info, list);
373                                 list_del_init(&(*ppTS)->list);
374                                 if (TxRxSelect == TX_DIR) {
375                                         struct tx_ts_record *tmp = container_of(*ppTS, struct tx_ts_record, ts_common_info);
376                                         ResetTxTsEntry(tmp);
377                                 } else {
378                                         struct rx_ts_record *tmp = container_of(*ppTS, struct rx_ts_record, ts_common_info);
379                                         ResetRxTsEntry(tmp);
380                                 }
381
382                                 IEEE80211_DEBUG(IEEE80211_DL_TS, "to init current TS, UP:%d, Dir:%d, addr:%pM\n", UP, Dir, Addr);
383                                 // Prepare TS Info related field
384                                 pTSInfo->uc_traffic_type = 0;           // Traffic type: WMM is reserved in this field
385                                 pTSInfo->uc_tsid = UP;                  // TSID
386                                 pTSInfo->uc_direction = Dir;            // Direction: if there is DirectLink, this need additional consideration.
387                                 pTSInfo->uc_access_policy = 1;          // Access policy
388                                 pTSInfo->uc_aggregation = 0;            // Aggregation
389                                 pTSInfo->uc_psb = 0;                    // Aggregation
390                                 pTSInfo->uc_up = UP;                    // User priority
391                                 pTSInfo->uc_ts_info_ack_policy = 0;     // Ack policy
392                                 pTSInfo->uc_schedule = 0;               // Schedule
393
394                                 MakeTSEntry(*ppTS, Addr, &TSpec, NULL, 0, 0);
395                                 AdmitTS(ieee, *ppTS, 0);
396                                 list_add_tail(&((*ppTS)->list), pAddmitList);
397                                 // if there is DirectLink, we need to do additional operation here!!
398
399                                 return true;
400                         } else {
401                                 IEEE80211_DEBUG(IEEE80211_DL_ERR, "in function %s() There is not enough TS record to be used!!", __func__);
402                                 return false;
403                         }
404                 }
405         }
406 }
407
408 static void RemoveTsEntry(struct ieee80211_device *ieee, struct ts_common_info *pTs,
409                           enum tr_select TxRxSelect)
410 {
411         //u32 flags = 0;
412         unsigned long flags = 0;
413         del_timer_sync(&pTs->setup_timer);
414         del_timer_sync(&pTs->inact_timer);
415         TsInitDelBA(ieee, pTs, TxRxSelect);
416
417         if (TxRxSelect == RX_DIR) {
418                 struct rx_reorder_entry *pRxReorderEntry;
419                 struct rx_ts_record     *pRxTS = (struct rx_ts_record *)pTs;
420                 if (timer_pending(&pRxTS->rx_pkt_pending_timer))
421                         del_timer_sync(&pRxTS->rx_pkt_pending_timer);
422
423                 while (!list_empty(&pRxTS->rx_pending_pkt_list)) {
424                         spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
425                         //pRxReorderEntry = list_entry(&pRxTS->rx_pending_pkt_list.prev,RX_REORDER_ENTRY,List);
426                         pRxReorderEntry = list_entry(pRxTS->rx_pending_pkt_list.prev, struct rx_reorder_entry, List);
427                         list_del_init(&pRxReorderEntry->List);
428                         {
429                                 int i = 0;
430                                 struct ieee80211_rxb *prxb = pRxReorderEntry->prxb;
431                                 if (unlikely(!prxb)) {
432                                         spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
433                                         return;
434                                 }
435                                 for (i =  0; i < prxb->nr_subframes; i++)
436                                         dev_kfree_skb(prxb->subframes[i]);
437
438                                 kfree(prxb);
439                                 prxb = NULL;
440                         }
441                         list_add_tail(&pRxReorderEntry->List, &ieee->RxReorder_Unused_List);
442                         spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
443                 }
444
445         } else {
446                 struct tx_ts_record *pTxTS = (struct tx_ts_record *)pTs;
447                 del_timer_sync(&pTxTS->ts_add_ba_timer);
448         }
449 }
450
451 void RemovePeerTS(struct ieee80211_device *ieee, u8 *Addr)
452 {
453         struct ts_common_info   *pTS, *pTmpTS;
454
455         printk("===========>%s,%pM\n", __func__, Addr);
456         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, list) {
457                 if (memcmp(pTS->addr, Addr, 6) == 0) {
458                         RemoveTsEntry(ieee, pTS, TX_DIR);
459                         list_del_init(&pTS->list);
460                         list_add_tail(&pTS->list, &ieee->Tx_TS_Unused_List);
461                 }
462         }
463
464         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, list) {
465                 if (memcmp(pTS->addr, Addr, 6) == 0) {
466                         printk("====>remove Tx_TS_admin_list\n");
467                         RemoveTsEntry(ieee, pTS, TX_DIR);
468                         list_del_init(&pTS->list);
469                         list_add_tail(&pTS->list, &ieee->Tx_TS_Unused_List);
470                 }
471         }
472
473         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, list) {
474                 if (memcmp(pTS->addr, Addr, 6) == 0) {
475                         RemoveTsEntry(ieee, pTS, RX_DIR);
476                         list_del_init(&pTS->list);
477                         list_add_tail(&pTS->list, &ieee->Rx_TS_Unused_List);
478                 }
479         }
480
481         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, list) {
482                 if (memcmp(pTS->addr, Addr, 6) == 0) {
483                         RemoveTsEntry(ieee, pTS, RX_DIR);
484                         list_del_init(&pTS->list);
485                         list_add_tail(&pTS->list, &ieee->Rx_TS_Unused_List);
486                 }
487         }
488 }
489
490 void RemoveAllTS(struct ieee80211_device *ieee)
491 {
492         struct ts_common_info *pTS, *pTmpTS;
493
494         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, list) {
495                 RemoveTsEntry(ieee, pTS, TX_DIR);
496                 list_del_init(&pTS->list);
497                 list_add_tail(&pTS->list, &ieee->Tx_TS_Unused_List);
498         }
499
500         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, list) {
501                 RemoveTsEntry(ieee, pTS, TX_DIR);
502                 list_del_init(&pTS->list);
503                 list_add_tail(&pTS->list, &ieee->Tx_TS_Unused_List);
504         }
505
506         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, list) {
507                 RemoveTsEntry(ieee, pTS, RX_DIR);
508                 list_del_init(&pTS->list);
509                 list_add_tail(&pTS->list, &ieee->Rx_TS_Unused_List);
510         }
511
512         list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, list) {
513                 RemoveTsEntry(ieee, pTS, RX_DIR);
514                 list_del_init(&pTS->list);
515                 list_add_tail(&pTS->list, &ieee->Rx_TS_Unused_List);
516         }
517 }
518
519 void TsStartAddBaProcess(struct ieee80211_device *ieee, struct tx_ts_record *pTxTS)
520 {
521         if (!pTxTS->add_ba_req_in_progress) {
522                 pTxTS->add_ba_req_in_progress = true;
523                 if (pTxTS->add_ba_req_delayed)  {
524                         IEEE80211_DEBUG(IEEE80211_DL_BA, "%s: Delayed Start ADDBA after 60 sec!!\n", __func__);
525                         mod_timer(&pTxTS->ts_add_ba_timer,
526                                   jiffies + msecs_to_jiffies(TS_ADDBA_DELAY));
527                 } else {
528                         IEEE80211_DEBUG(IEEE80211_DL_BA, "%s: Immediately Start ADDBA now!!\n", __func__);
529                         mod_timer(&pTxTS->ts_add_ba_timer, jiffies + 10); //set 10 ticks
530                 }
531         } else {
532                 IEEE80211_DEBUG(IEEE80211_DL_ERR, "%s()==>BA timer is already added\n", __func__);
533         }
534 }