1 // SPDX-License-Identifier: GPL-2.0
3 #include <linux/etherdevice.h>
4 #include <linux/slab.h>
5 #include "rtl819x_TS.h"
7 static void TsSetupTimeOut(unsigned long data
)
10 // This is used for WMMSA and ACM , that would send ADDTSReq frame.
13 static void TsInactTimeout(unsigned long data
)
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.
20 /********************************************************************************************************************
21 *function: I still not understand this function, so wait for further implementation
22 * input: unsigned long data //acturally we send TX_TS_RECORD or RX_TS_RECORD to these timer
25 ********************************************************************************************************************/
26 static void RxPktPendingTimeout(unsigned long data
)
28 PRX_TS_RECORD pRxTs
= (PRX_TS_RECORD
)data
;
29 struct ieee80211_device
*ieee
= container_of(pRxTs
, struct ieee80211_device
, RxTsRecord
[pRxTs
->num
]);
31 PRX_REORDER_ENTRY pReorderEntry
= NULL
;
34 unsigned long flags
= 0;
36 bool bPktInBuf
= false;
38 spin_lock_irqsave(&(ieee
->reorder_spinlock
), flags
);
39 IEEE80211_DEBUG(IEEE80211_DL_REORDER
,"==================>%s()\n",__func__
);
40 if(pRxTs
->RxTimeoutIndicateSeq
!= 0xffff) {
41 // Indicate the pending packets sequentially according to SeqNum until meet the gap.
42 while(!list_empty(&pRxTs
->RxPendingPktList
)) {
43 pReorderEntry
= (PRX_REORDER_ENTRY
)list_entry(pRxTs
->RxPendingPktList
.prev
,RX_REORDER_ENTRY
,List
);
45 pRxTs
->RxIndicateSeq
= pReorderEntry
->SeqNum
;
47 if( SN_LESS(pReorderEntry
->SeqNum
, pRxTs
->RxIndicateSeq
) ||
48 SN_EQUAL(pReorderEntry
->SeqNum
, pRxTs
->RxIndicateSeq
) ) {
49 list_del_init(&pReorderEntry
->List
);
51 if(SN_EQUAL(pReorderEntry
->SeqNum
, pRxTs
->RxIndicateSeq
))
52 pRxTs
->RxIndicateSeq
= (pRxTs
->RxIndicateSeq
+ 1) % 4096;
54 IEEE80211_DEBUG(IEEE80211_DL_REORDER
,"RxPktPendingTimeout(): IndicateSeq: %d\n", pReorderEntry
->SeqNum
);
55 ieee
->stats_IndicateArray
[index
] = pReorderEntry
->prxb
;
58 list_add_tail(&pReorderEntry
->List
, &ieee
->RxReorder_Unused_List
);
67 // Set RxTimeoutIndicateSeq to 0xffff to indicate no pending packets in buffer now.
68 pRxTs
->RxTimeoutIndicateSeq
= 0xffff;
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
);
76 ieee80211_indicate_packets(ieee
, ieee
->stats_IndicateArray
, index
);
79 if(bPktInBuf
&& (pRxTs
->RxTimeoutIndicateSeq
==0xffff)) {
80 pRxTs
->RxTimeoutIndicateSeq
= pRxTs
->RxIndicateSeq
;
81 mod_timer(&pRxTs
->RxPktPendingTimer
,
82 jiffies
+ msecs_to_jiffies(ieee
->pHTInfo
->RxReorderPendingTime
));
84 spin_unlock_irqrestore(&(ieee
->reorder_spinlock
), flags
);
87 /********************************************************************************************************************
88 *function: Add BA timer function
89 * input: unsigned long data //acturally we send TX_TS_RECORD or RX_TS_RECORD to these timer
92 ********************************************************************************************************************/
93 static void TsAddBaProcess(unsigned long data
)
95 PTX_TS_RECORD pTxTs
= (PTX_TS_RECORD
)data
;
97 struct ieee80211_device
*ieee
= container_of(pTxTs
, struct ieee80211_device
, TxTsRecord
[num
]);
99 TsInitAddBA(ieee
, pTxTs
, BA_POLICY_IMMEDIATE
, false);
100 IEEE80211_DEBUG(IEEE80211_DL_BA
, "TsAddBaProcess(): ADDBA Req is started!! \n");
104 static void ResetTsCommonInfo(PTS_COMMON_INFO pTsCommonInfo
)
106 eth_zero_addr(pTsCommonInfo
->Addr
);
107 memset(&pTsCommonInfo
->TSpec
, 0, sizeof(TSPEC_BODY
));
108 memset(&pTsCommonInfo
->TClass
, 0, sizeof(QOS_TCLAS
)*TCLAS_NUM
);
109 pTsCommonInfo
->TClasProc
= 0;
110 pTsCommonInfo
->TClasNum
= 0;
113 static void ResetTxTsEntry(PTX_TS_RECORD pTS
)
115 ResetTsCommonInfo(&pTS
->TsCommonInfo
);
117 pTS
->bAddBaReqInProgress
= false;
118 pTS
->bAddBaReqDelayed
= false;
119 pTS
->bUsingBa
= false;
120 ResetBaEntry(&pTS
->TxAdmittedBARecord
); //For BA Originator
121 ResetBaEntry(&pTS
->TxPendingBARecord
);
124 static void ResetRxTsEntry(PRX_TS_RECORD pTS
)
126 ResetTsCommonInfo(&pTS
->TsCommonInfo
);
127 pTS
->RxIndicateSeq
= 0xffff; // This indicate the RxIndicateSeq is not used now!!
128 pTS
->RxTimeoutIndicateSeq
= 0xffff; // This indicate the RxTimeoutIndicateSeq is not used now!!
129 ResetBaEntry(&pTS
->RxAdmittedBARecord
); // For BA Recipient
132 void TSInitialize(struct ieee80211_device
*ieee
)
134 PTX_TS_RECORD pTxTS
= ieee
->TxTsRecord
;
135 PRX_TS_RECORD pRxTS
= ieee
->RxTsRecord
;
136 PRX_REORDER_ENTRY pRxReorderEntry
= ieee
->RxReorderEntry
;
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
);
144 for(count
= 0; count
< TOTAL_TS_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 setup_timer(&pTxTS
->TsCommonInfo
.SetupTimer
, TsSetupTimeOut
,
150 (unsigned long)pTxTS
);
151 setup_timer(&pTxTS
->TsCommonInfo
.InactTimer
, TsInactTimeout
,
152 (unsigned long)pTxTS
);
153 setup_timer(&pTxTS
->TsAddBaTimer
, TsAddBaProcess
,
154 (unsigned long)pTxTS
);
155 setup_timer(&pTxTS
->TxPendingBARecord
.Timer
, BaSetupTimeOut
,
156 (unsigned long)pTxTS
);
157 setup_timer(&pTxTS
->TxAdmittedBARecord
.Timer
,
158 TxBaInactTimeout
, (unsigned long)pTxTS
);
159 ResetTxTsEntry(pTxTS
);
160 list_add_tail(&pTxTS
->TsCommonInfo
.List
, &ieee
->Tx_TS_Unused_List
);
164 // Initialize Rx TS related info.
165 INIT_LIST_HEAD(&ieee
->Rx_TS_Admit_List
);
166 INIT_LIST_HEAD(&ieee
->Rx_TS_Pending_List
);
167 INIT_LIST_HEAD(&ieee
->Rx_TS_Unused_List
);
168 for(count
= 0; count
< TOTAL_TS_NUM
; count
++) {
170 INIT_LIST_HEAD(&pRxTS
->RxPendingPktList
);
171 setup_timer(&pRxTS
->TsCommonInfo
.SetupTimer
, TsSetupTimeOut
,
172 (unsigned long)pRxTS
);
173 setup_timer(&pRxTS
->TsCommonInfo
.InactTimer
, TsInactTimeout
,
174 (unsigned long)pRxTS
);
175 setup_timer(&pRxTS
->RxAdmittedBARecord
.Timer
,
176 RxBaInactTimeout
, (unsigned long)pRxTS
);
177 setup_timer(&pRxTS
->RxPktPendingTimer
, RxPktPendingTimeout
,
178 (unsigned long)pRxTS
);
179 ResetRxTsEntry(pRxTS
);
180 list_add_tail(&pRxTS
->TsCommonInfo
.List
, &ieee
->Rx_TS_Unused_List
);
183 // Initialize unused Rx Reorder List.
184 INIT_LIST_HEAD(&ieee
->RxReorder_Unused_List
);
186 for(count
= 0; count
< REORDER_ENTRY_NUM
; count
++) {
187 list_add_tail( &pRxReorderEntry
->List
,&ieee
->RxReorder_Unused_List
);
188 if(count
== (REORDER_ENTRY_NUM
-1))
190 pRxReorderEntry
= &ieee
->RxReorderEntry
[count
+1];
195 static void AdmitTS(struct ieee80211_device
*ieee
,
196 PTS_COMMON_INFO pTsCommonInfo
, u32 InactTime
)
198 del_timer_sync(&pTsCommonInfo
->SetupTimer
);
199 del_timer_sync(&pTsCommonInfo
->InactTimer
);
202 mod_timer(&pTsCommonInfo
->InactTimer
,
203 jiffies
+ msecs_to_jiffies(InactTime
));
207 static PTS_COMMON_INFO
SearchAdmitTRStream(struct ieee80211_device
*ieee
,
209 TR_SELECT TxRxSelect
)
211 //DIRECTION_VALUE dir;
213 bool search_dir
[4] = {0};
214 struct list_head
*psearch_list
; //FIXME
215 PTS_COMMON_INFO pRet
= NULL
;
216 if(ieee
->iw_mode
== IW_MODE_MASTER
) { //ap mode
217 if(TxRxSelect
== TX_DIR
) {
218 search_dir
[DIR_DOWN
] = true;
219 search_dir
[DIR_BI_DIR
]= true;
221 search_dir
[DIR_UP
] = true;
222 search_dir
[DIR_BI_DIR
]= true;
224 } else if(ieee
->iw_mode
== IW_MODE_ADHOC
) {
225 if(TxRxSelect
== TX_DIR
)
226 search_dir
[DIR_UP
] = true;
228 search_dir
[DIR_DOWN
] = true;
230 if(TxRxSelect
== TX_DIR
) {
231 search_dir
[DIR_UP
] = true;
232 search_dir
[DIR_BI_DIR
]= true;
233 search_dir
[DIR_DIRECT
]= true;
235 search_dir
[DIR_DOWN
] = true;
236 search_dir
[DIR_BI_DIR
]= true;
237 search_dir
[DIR_DIRECT
]= true;
241 if(TxRxSelect
== TX_DIR
)
242 psearch_list
= &ieee
->Tx_TS_Admit_List
;
244 psearch_list
= &ieee
->Rx_TS_Admit_List
;
246 //for(dir = DIR_UP; dir <= DIR_BI_DIR; dir++)
247 for(dir
= 0; dir
<= DIR_BI_DIR
; dir
++) {
248 if (!search_dir
[dir
])
250 list_for_each_entry(pRet
, psearch_list
, List
){
251 // IEEE80211_DEBUG(IEEE80211_DL_TS, "ADD:%pM, TID:%d, dir:%d\n", pRet->Addr, pRet->TSpec.f.TSInfo.field.ucTSID, pRet->TSpec.f.TSInfo.field.ucDirection);
252 if (memcmp(pRet
->Addr
, Addr
, 6) == 0)
253 if (pRet
->TSpec
.f
.TSInfo
.field
.ucTSID
== TID
)
254 if(pRet
->TSpec
.f
.TSInfo
.field
.ucDirection
== dir
) {
255 // printk("Bingo! got it\n");
259 if(&pRet
->List
!= psearch_list
)
263 if(&pRet
->List
!= psearch_list
)
269 static void MakeTSEntry(PTS_COMMON_INFO pTsCommonInfo
, u8
*Addr
,
270 PTSPEC_BODY pTSPEC
, PQOS_TCLAS pTCLAS
, u8 TCLAS_Num
,
275 if(pTsCommonInfo
== NULL
)
278 memcpy(pTsCommonInfo
->Addr
, Addr
, 6);
281 memcpy((u8
*)(&(pTsCommonInfo
->TSpec
)), (u8
*)pTSPEC
, sizeof(TSPEC_BODY
));
283 for(count
= 0; count
< TCLAS_Num
; count
++)
284 memcpy((u8
*)(&(pTsCommonInfo
->TClass
[count
])), (u8
*)pTCLAS
, sizeof(QOS_TCLAS
));
286 pTsCommonInfo
->TClasProc
= TCLAS_Proc
;
287 pTsCommonInfo
->TClasNum
= TCLAS_Num
;
292 struct ieee80211_device
*ieee
,
293 PTS_COMMON_INFO
*ppTS
,
296 TR_SELECT TxRxSelect
, //Rx:1, Tx:0
302 // We do not build any TS for Broadcast or Multicast stream.
303 // So reject these kinds of search here.
305 if (is_multicast_ether_addr(Addr
)) {
306 IEEE80211_DEBUG(IEEE80211_DL_ERR
, "get TS for Broadcast or Multicast\n");
310 if (ieee
->current_network
.qos_data
.supported
== 0) {
313 // In WMM case: we use 4 TID only
314 if (!IsACValid(TID
)) {
315 IEEE80211_DEBUG(IEEE80211_DL_ERR
, " in %s(), TID(%d) is not valid\n", __func__
, TID
);
342 *ppTS
= SearchAdmitTRStream(
351 IEEE80211_DEBUG(IEEE80211_DL_TS
, "add new TS failed(tid:%d)\n", UP
);
355 // Create a new Traffic stream for current Tx/Rx
356 // This is for EDCA and WMM to add a new TS.
357 // For HCCA or WMMSA, TS cannot be addmit without negotiation.
360 PQOS_TSINFO pTSInfo
= &TSpec
.f
.TSInfo
;
361 struct list_head
*pUnusedList
=
362 (TxRxSelect
== TX_DIR
)?
363 (&ieee
->Tx_TS_Unused_List
):
364 (&ieee
->Rx_TS_Unused_List
);
366 struct list_head
*pAddmitList
=
367 (TxRxSelect
== TX_DIR
)?
368 (&ieee
->Tx_TS_Admit_List
):
369 (&ieee
->Rx_TS_Admit_List
);
371 DIRECTION_VALUE Dir
= (ieee
->iw_mode
== IW_MODE_MASTER
)?
372 ((TxRxSelect
==TX_DIR
)?DIR_DOWN
:DIR_UP
):
373 ((TxRxSelect
==TX_DIR
)?DIR_UP
:DIR_DOWN
);
374 IEEE80211_DEBUG(IEEE80211_DL_TS
, "to add Ts\n");
375 if(!list_empty(pUnusedList
)) {
376 (*ppTS
) = list_entry(pUnusedList
->next
, TS_COMMON_INFO
, List
);
377 list_del_init(&(*ppTS
)->List
);
378 if(TxRxSelect
==TX_DIR
) {
379 PTX_TS_RECORD tmp
= container_of(*ppTS
, TX_TS_RECORD
, TsCommonInfo
);
382 PRX_TS_RECORD tmp
= container_of(*ppTS
, RX_TS_RECORD
, TsCommonInfo
);
386 IEEE80211_DEBUG(IEEE80211_DL_TS
, "to init current TS, UP:%d, Dir:%d, addr:%pM\n", UP
, Dir
, Addr
);
387 // Prepare TS Info releated field
388 pTSInfo
->field
.ucTrafficType
= 0; // Traffic type: WMM is reserved in this field
389 pTSInfo
->field
.ucTSID
= UP
; // TSID
390 pTSInfo
->field
.ucDirection
= Dir
; // Direction: if there is DirectLink, this need additional consideration.
391 pTSInfo
->field
.ucAccessPolicy
= 1; // Access policy
392 pTSInfo
->field
.ucAggregation
= 0; // Aggregation
393 pTSInfo
->field
.ucPSB
= 0; // Aggregation
394 pTSInfo
->field
.ucUP
= UP
; // User priority
395 pTSInfo
->field
.ucTSInfoAckPolicy
= 0; // Ack policy
396 pTSInfo
->field
.ucSchedule
= 0; // Schedule
398 MakeTSEntry(*ppTS
, Addr
, &TSpec
, NULL
, 0, 0);
399 AdmitTS(ieee
, *ppTS
, 0);
400 list_add_tail(&((*ppTS
)->List
), pAddmitList
);
401 // if there is DirectLink, we need to do additional operation here!!
405 IEEE80211_DEBUG(IEEE80211_DL_ERR
, "in function %s() There is not enough TS record to be used!!", __func__
);
412 static void RemoveTsEntry(struct ieee80211_device
*ieee
, PTS_COMMON_INFO pTs
,
413 TR_SELECT TxRxSelect
)
416 unsigned long flags
= 0;
417 del_timer_sync(&pTs
->SetupTimer
);
418 del_timer_sync(&pTs
->InactTimer
);
419 TsInitDelBA(ieee
, pTs
, TxRxSelect
);
421 if(TxRxSelect
== RX_DIR
) {
423 PRX_REORDER_ENTRY pRxReorderEntry
;
424 PRX_TS_RECORD pRxTS
= (PRX_TS_RECORD
)pTs
;
425 if(timer_pending(&pRxTS
->RxPktPendingTimer
))
426 del_timer_sync(&pRxTS
->RxPktPendingTimer
);
428 while(!list_empty(&pRxTS
->RxPendingPktList
)) {
429 spin_lock_irqsave(&(ieee
->reorder_spinlock
), flags
);
430 //pRxReorderEntry = list_entry(&pRxTS->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
431 pRxReorderEntry
= (PRX_REORDER_ENTRY
)list_entry(pRxTS
->RxPendingPktList
.prev
,RX_REORDER_ENTRY
,List
);
432 list_del_init(&pRxReorderEntry
->List
);
435 struct ieee80211_rxb
*prxb
= pRxReorderEntry
->prxb
;
436 if (unlikely(!prxb
)) {
437 spin_unlock_irqrestore(&(ieee
->reorder_spinlock
), flags
);
440 for(i
=0; i
< prxb
->nr_subframes
; i
++)
441 dev_kfree_skb(prxb
->subframes
[i
]);
446 list_add_tail(&pRxReorderEntry
->List
,&ieee
->RxReorder_Unused_List
);
447 spin_unlock_irqrestore(&(ieee
->reorder_spinlock
), flags
);
452 PTX_TS_RECORD pTxTS
= (PTX_TS_RECORD
)pTs
;
453 del_timer_sync(&pTxTS
->TsAddBaTimer
);
457 void RemovePeerTS(struct ieee80211_device
*ieee
, u8
*Addr
)
459 PTS_COMMON_INFO pTS
, pTmpTS
;
461 printk("===========>RemovePeerTS,%pM\n", Addr
);
462 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Tx_TS_Pending_List
, List
) {
463 if (memcmp(pTS
->Addr
, Addr
, 6) == 0) {
464 RemoveTsEntry(ieee
, pTS
, TX_DIR
);
465 list_del_init(&pTS
->List
);
466 list_add_tail(&pTS
->List
, &ieee
->Tx_TS_Unused_List
);
470 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Tx_TS_Admit_List
, List
) {
471 if (memcmp(pTS
->Addr
, Addr
, 6) == 0) {
472 printk("====>remove Tx_TS_admin_list\n");
473 RemoveTsEntry(ieee
, pTS
, TX_DIR
);
474 list_del_init(&pTS
->List
);
475 list_add_tail(&pTS
->List
, &ieee
->Tx_TS_Unused_List
);
479 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Rx_TS_Pending_List
, List
) {
480 if (memcmp(pTS
->Addr
, Addr
, 6) == 0) {
481 RemoveTsEntry(ieee
, pTS
, RX_DIR
);
482 list_del_init(&pTS
->List
);
483 list_add_tail(&pTS
->List
, &ieee
->Rx_TS_Unused_List
);
487 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Rx_TS_Admit_List
, List
) {
488 if (memcmp(pTS
->Addr
, Addr
, 6) == 0) {
489 RemoveTsEntry(ieee
, pTS
, RX_DIR
);
490 list_del_init(&pTS
->List
);
491 list_add_tail(&pTS
->List
, &ieee
->Rx_TS_Unused_List
);
496 void RemoveAllTS(struct ieee80211_device
*ieee
)
498 PTS_COMMON_INFO pTS
, pTmpTS
;
500 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Tx_TS_Pending_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
);
506 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Tx_TS_Admit_List
, List
) {
507 RemoveTsEntry(ieee
, pTS
, TX_DIR
);
508 list_del_init(&pTS
->List
);
509 list_add_tail(&pTS
->List
, &ieee
->Tx_TS_Unused_List
);
512 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Rx_TS_Pending_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
);
518 list_for_each_entry_safe(pTS
, pTmpTS
, &ieee
->Rx_TS_Admit_List
, List
) {
519 RemoveTsEntry(ieee
, pTS
, RX_DIR
);
520 list_del_init(&pTS
->List
);
521 list_add_tail(&pTS
->List
, &ieee
->Rx_TS_Unused_List
);
525 void TsStartAddBaProcess(struct ieee80211_device
*ieee
, PTX_TS_RECORD pTxTS
)
527 if(!pTxTS
->bAddBaReqInProgress
) {
528 pTxTS
->bAddBaReqInProgress
= true;
529 if(pTxTS
->bAddBaReqDelayed
) {
530 IEEE80211_DEBUG(IEEE80211_DL_BA
, "TsStartAddBaProcess(): Delayed Start ADDBA after 60 sec!!\n");
531 mod_timer(&pTxTS
->TsAddBaTimer
,
532 jiffies
+ msecs_to_jiffies(TS_ADDBA_DELAY
));
534 IEEE80211_DEBUG(IEEE80211_DL_BA
,"TsStartAddBaProcess(): Immediately Start ADDBA now!!\n");
535 mod_timer(&pTxTS
->TsAddBaTimer
, jiffies
+10); //set 10 ticks
538 IEEE80211_DEBUG(IEEE80211_DL_ERR
, "%s()==>BA timer is already added\n", __func__
);