rtl819x_TSProc.c revision 0ee9f67c4028500a4348e8bc87ee7ec1139b8259
1#include "ieee80211.h"
2#include <linux/etherdevice.h>
3#include "rtl819x_TS.h"
4
5void TsSetupTimeOut(unsigned long data)
6{
7	// Not implement yet
8	// This is used for WMMSA and ACM , that would send ADDTSReq frame.
9}
10
11void TsInactTimeout(unsigned long data)
12{
13	// Not implement yet
14	// This is used for WMMSA and ACM.
15	// This function would be call when TS is no Tx/Rx for some period of time.
16}
17
18/********************************************************************************************************************
19 *function:  I still not understand this function, so wait for further implementation
20 *   input:  unsigned long	 data		//acturally we send TX_TS_RECORD or RX_TS_RECORD to these timer
21 *  return:  NULL
22 *  notice:
23********************************************************************************************************************/
24void RxPktPendingTimeout(unsigned long data)
25{
26	PRX_TS_RECORD	pRxTs = (PRX_TS_RECORD)data;
27	struct ieee80211_device *ieee = container_of(pRxTs, struct ieee80211_device, RxTsRecord[pRxTs->num]);
28
29	PRX_REORDER_ENTRY 	pReorderEntry = NULL;
30
31	//u32 flags = 0;
32	unsigned long flags = 0;
33	struct ieee80211_rxb *stats_IndicateArray[REORDER_WIN_SIZE];
34	u8 index = 0;
35	bool bPktInBuf = false;
36
37
38	spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
39	//PlatformAcquireSpinLock(Adapter, RT_RX_SPINLOCK);
40	IEEE80211_DEBUG(IEEE80211_DL_REORDER,"==================>%s()\n",__FUNCTION__);
41	if(pRxTs->RxTimeoutIndicateSeq != 0xffff)
42	{
43		// Indicate the pending packets sequentially according to SeqNum until meet the gap.
44		while(!list_empty(&pRxTs->RxPendingPktList))
45		{
46			pReorderEntry = (PRX_REORDER_ENTRY)list_entry(pRxTs->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
47			if(index == 0)
48				pRxTs->RxIndicateSeq = pReorderEntry->SeqNum;
49
50			if( SN_LESS(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq) ||
51				SN_EQUAL(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq)	)
52			{
53				list_del_init(&pReorderEntry->List);
54
55				if(SN_EQUAL(pReorderEntry->SeqNum, pRxTs->RxIndicateSeq))
56					pRxTs->RxIndicateSeq = (pRxTs->RxIndicateSeq + 1) % 4096;
57
58				IEEE80211_DEBUG(IEEE80211_DL_REORDER,"RxPktPendingTimeout(): IndicateSeq: %d\n", pReorderEntry->SeqNum);
59				stats_IndicateArray[index] = pReorderEntry->prxb;
60				index++;
61
62				list_add_tail(&pReorderEntry->List, &ieee->RxReorder_Unused_List);
63			}
64			else
65			{
66				bPktInBuf = true;
67				break;
68			}
69		}
70	}
71
72	if(index>0)
73	{
74		// Set RxTimeoutIndicateSeq to 0xffff to indicate no pending packets in buffer now.
75		pRxTs->RxTimeoutIndicateSeq = 0xffff;
76
77		// Indicate packets
78		if(index > REORDER_WIN_SIZE){
79			IEEE80211_DEBUG(IEEE80211_DL_ERR, "RxReorderIndicatePacket(): Rx Reorer buffer full!! \n");
80			spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
81			return;
82		}
83		ieee80211_indicate_packets(ieee, stats_IndicateArray, index);
84	}
85
86	if(bPktInBuf && (pRxTs->RxTimeoutIndicateSeq==0xffff))
87	{
88		pRxTs->RxTimeoutIndicateSeq = pRxTs->RxIndicateSeq;
89		if(timer_pending(&pRxTs->RxPktPendingTimer))
90			del_timer_sync(&pRxTs->RxPktPendingTimer);
91		pRxTs->RxPktPendingTimer.expires = jiffies + ieee->pHTInfo->RxReorderPendingTime;
92		add_timer(&pRxTs->RxPktPendingTimer);
93	}
94	spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
95	//PlatformReleaseSpinLock(Adapter, RT_RX_SPINLOCK);
96}
97
98/********************************************************************************************************************
99 *function:  Add BA timer function
100 *   input:  unsigned long	 data		//acturally we send TX_TS_RECORD or RX_TS_RECORD to these timer
101 *  return:  NULL
102 *  notice:
103********************************************************************************************************************/
104void TsAddBaProcess(unsigned long data)
105{
106	PTX_TS_RECORD	pTxTs = (PTX_TS_RECORD)data;
107	u8 num = pTxTs->num;
108	struct ieee80211_device *ieee = container_of(pTxTs, struct ieee80211_device, TxTsRecord[num]);
109
110	TsInitAddBA(ieee, pTxTs, BA_POLICY_IMMEDIATE, false);
111	IEEE80211_DEBUG(IEEE80211_DL_BA, "TsAddBaProcess(): ADDBA Req is started!! \n");
112}
113
114
115void ResetTsCommonInfo(PTS_COMMON_INFO	pTsCommonInfo)
116{
117	memset(pTsCommonInfo->Addr, 0, 6);
118	memset(&pTsCommonInfo->TSpec, 0, sizeof(TSPEC_BODY));
119	memset(&pTsCommonInfo->TClass, 0, sizeof(QOS_TCLAS)*TCLAS_NUM);
120	pTsCommonInfo->TClasProc = 0;
121	pTsCommonInfo->TClasNum = 0;
122}
123
124void ResetTxTsEntry(PTX_TS_RECORD pTS)
125{
126	ResetTsCommonInfo(&pTS->TsCommonInfo);
127	pTS->TxCurSeq = 0;
128	pTS->bAddBaReqInProgress = false;
129	pTS->bAddBaReqDelayed = false;
130	pTS->bUsingBa = false;
131	ResetBaEntry(&pTS->TxAdmittedBARecord); //For BA Originator
132	ResetBaEntry(&pTS->TxPendingBARecord);
133}
134
135void ResetRxTsEntry(PRX_TS_RECORD pTS)
136{
137	ResetTsCommonInfo(&pTS->TsCommonInfo);
138	pTS->RxIndicateSeq = 0xffff; // This indicate the RxIndicateSeq is not used now!!
139	pTS->RxTimeoutIndicateSeq = 0xffff; // This indicate the RxTimeoutIndicateSeq is not used now!!
140	ResetBaEntry(&pTS->RxAdmittedBARecord);	  // For BA Recepient
141}
142
143void TSInitialize(struct ieee80211_device *ieee)
144{
145	PTX_TS_RECORD		pTxTS  = ieee->TxTsRecord;
146	PRX_TS_RECORD		pRxTS  = ieee->RxTsRecord;
147	PRX_REORDER_ENTRY	pRxReorderEntry = ieee->RxReorderEntry;
148	u8				count = 0;
149	IEEE80211_DEBUG(IEEE80211_DL_TS, "==========>%s()\n", __FUNCTION__);
150	// Initialize Tx TS related info.
151	INIT_LIST_HEAD(&ieee->Tx_TS_Admit_List);
152	INIT_LIST_HEAD(&ieee->Tx_TS_Pending_List);
153	INIT_LIST_HEAD(&ieee->Tx_TS_Unused_List);
154
155	for(count = 0; count < TOTAL_TS_NUM; count++)
156	{
157		//
158		pTxTS->num = count;
159		// The timers for the operation of Traffic Stream and Block Ack.
160		// DLS related timer will be add here in the future!!
161		init_timer(&pTxTS->TsCommonInfo.SetupTimer);
162		pTxTS->TsCommonInfo.SetupTimer.data = (unsigned long)pTxTS;
163		pTxTS->TsCommonInfo.SetupTimer.function = TsSetupTimeOut;
164
165		init_timer(&pTxTS->TsCommonInfo.InactTimer);
166		pTxTS->TsCommonInfo.InactTimer.data = (unsigned long)pTxTS;
167		pTxTS->TsCommonInfo.InactTimer.function = TsInactTimeout;
168
169		init_timer(&pTxTS->TsAddBaTimer);
170		pTxTS->TsAddBaTimer.data = (unsigned long)pTxTS;
171		pTxTS->TsAddBaTimer.function = TsAddBaProcess;
172
173		init_timer(&pTxTS->TxPendingBARecord.Timer);
174		pTxTS->TxPendingBARecord.Timer.data = (unsigned long)pTxTS;
175		pTxTS->TxPendingBARecord.Timer.function = BaSetupTimeOut;
176
177		init_timer(&pTxTS->TxAdmittedBARecord.Timer);
178		pTxTS->TxAdmittedBARecord.Timer.data = (unsigned long)pTxTS;
179		pTxTS->TxAdmittedBARecord.Timer.function = TxBaInactTimeout;
180
181		ResetTxTsEntry(pTxTS);
182		list_add_tail(&pTxTS->TsCommonInfo.List, &ieee->Tx_TS_Unused_List);
183		pTxTS++;
184	}
185
186	// Initialize Rx TS related info.
187	INIT_LIST_HEAD(&ieee->Rx_TS_Admit_List);
188	INIT_LIST_HEAD(&ieee->Rx_TS_Pending_List);
189	INIT_LIST_HEAD(&ieee->Rx_TS_Unused_List);
190	for(count = 0; count < TOTAL_TS_NUM; count++)
191	{
192		pRxTS->num = count;
193		INIT_LIST_HEAD(&pRxTS->RxPendingPktList);
194
195		init_timer(&pRxTS->TsCommonInfo.SetupTimer);
196		pRxTS->TsCommonInfo.SetupTimer.data = (unsigned long)pRxTS;
197		pRxTS->TsCommonInfo.SetupTimer.function = TsSetupTimeOut;
198
199		init_timer(&pRxTS->TsCommonInfo.InactTimer);
200		pRxTS->TsCommonInfo.InactTimer.data = (unsigned long)pRxTS;
201		pRxTS->TsCommonInfo.InactTimer.function = TsInactTimeout;
202
203		init_timer(&pRxTS->RxAdmittedBARecord.Timer);
204		pRxTS->RxAdmittedBARecord.Timer.data = (unsigned long)pRxTS;
205		pRxTS->RxAdmittedBARecord.Timer.function = RxBaInactTimeout;
206
207		init_timer(&pRxTS->RxPktPendingTimer);
208		pRxTS->RxPktPendingTimer.data = (unsigned long)pRxTS;
209		pRxTS->RxPktPendingTimer.function = RxPktPendingTimeout;
210
211		ResetRxTsEntry(pRxTS);
212		list_add_tail(&pRxTS->TsCommonInfo.List, &ieee->Rx_TS_Unused_List);
213		pRxTS++;
214	}
215	// Initialize unused Rx Reorder List.
216	INIT_LIST_HEAD(&ieee->RxReorder_Unused_List);
217//#ifdef TO_DO_LIST
218	for(count = 0; count < REORDER_ENTRY_NUM; count++)
219	{
220		list_add_tail( &pRxReorderEntry->List,&ieee->RxReorder_Unused_List);
221		if(count == (REORDER_ENTRY_NUM-1))
222			break;
223		pRxReorderEntry = &ieee->RxReorderEntry[count+1];
224	}
225//#endif
226
227}
228
229void AdmitTS(struct ieee80211_device *ieee, PTS_COMMON_INFO pTsCommonInfo, u32 InactTime)
230{
231	del_timer_sync(&pTsCommonInfo->SetupTimer);
232	del_timer_sync(&pTsCommonInfo->InactTimer);
233
234	if(InactTime!=0)
235		mod_timer(&pTsCommonInfo->InactTimer, jiffies + MSECS(InactTime));
236}
237
238
239PTS_COMMON_INFO SearchAdmitTRStream(struct ieee80211_device *ieee, u8*	Addr, u8 TID, TR_SELECT	TxRxSelect)
240{
241	//DIRECTION_VALUE 	dir;
242	u8 	dir;
243	bool				search_dir[4] = {0, 0, 0, 0};
244	struct list_head*		psearch_list; //FIXME
245	PTS_COMMON_INFO	pRet = NULL;
246	if(ieee->iw_mode == IW_MODE_MASTER) //ap mode
247	{
248		if(TxRxSelect == TX_DIR)
249		{
250			search_dir[DIR_DOWN] = true;
251			search_dir[DIR_BI_DIR]= true;
252		}
253		else
254		{
255			search_dir[DIR_UP] 	= true;
256			search_dir[DIR_BI_DIR]= true;
257		}
258	}
259	else if(ieee->iw_mode == IW_MODE_ADHOC)
260	{
261		if(TxRxSelect == TX_DIR)
262			search_dir[DIR_UP] 	= true;
263		else
264			search_dir[DIR_DOWN] = true;
265	}
266	else
267	{
268		if(TxRxSelect == TX_DIR)
269		{
270			search_dir[DIR_UP] 	= true;
271			search_dir[DIR_BI_DIR]= true;
272			search_dir[DIR_DIRECT]= true;
273		}
274		else
275		{
276			search_dir[DIR_DOWN] = true;
277			search_dir[DIR_BI_DIR]= true;
278			search_dir[DIR_DIRECT]= true;
279		}
280	}
281
282	if(TxRxSelect == TX_DIR)
283		psearch_list = &ieee->Tx_TS_Admit_List;
284	else
285		psearch_list = &ieee->Rx_TS_Admit_List;
286
287	//for(dir = DIR_UP; dir <= DIR_BI_DIR; dir++)
288	for(dir = 0; dir <= DIR_BI_DIR; dir++)
289	{
290		if(search_dir[dir] ==false )
291			continue;
292		list_for_each_entry(pRet, psearch_list, List){
293	//		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);
294			if (memcmp(pRet->Addr, Addr, 6) == 0)
295				if (pRet->TSpec.f.TSInfo.field.ucTSID == TID)
296					if(pRet->TSpec.f.TSInfo.field.ucDirection == dir)
297					{
298	//					printk("Bingo! got it\n");
299						break;
300					}
301
302		}
303		if(&pRet->List  != psearch_list)
304			break;
305	}
306
307	if(&pRet->List  != psearch_list){
308		return pRet ;
309	}
310	else
311		return NULL;
312}
313
314void MakeTSEntry(
315		PTS_COMMON_INFO	pTsCommonInfo,
316		u8*		Addr,
317		PTSPEC_BODY	pTSPEC,
318		PQOS_TCLAS	pTCLAS,
319		u8		TCLAS_Num,
320		u8		TCLAS_Proc
321	)
322{
323	u8	count;
324
325	if(pTsCommonInfo == NULL)
326		return;
327
328	memcpy(pTsCommonInfo->Addr, Addr, 6);
329
330	if(pTSPEC != NULL)
331		memcpy((u8*)(&(pTsCommonInfo->TSpec)), (u8*)pTSPEC, sizeof(TSPEC_BODY));
332
333	for(count = 0; count < TCLAS_Num; count++)
334		memcpy((u8*)(&(pTsCommonInfo->TClass[count])), (u8*)pTCLAS, sizeof(QOS_TCLAS));
335
336	pTsCommonInfo->TClasProc = TCLAS_Proc;
337	pTsCommonInfo->TClasNum = TCLAS_Num;
338}
339
340
341bool GetTs(
342	struct ieee80211_device*	ieee,
343	PTS_COMMON_INFO			*ppTS,
344	u8*				Addr,
345	u8				TID,
346	TR_SELECT			TxRxSelect,  //Rx:1, Tx:0
347	bool				bAddNewTs
348	)
349{
350	u8	UP = 0;
351	//
352	// We do not build any TS for Broadcast or Multicast stream.
353	// So reject these kinds of search here.
354	//
355	if(is_broadcast_ether_addr(Addr) || is_multicast_ether_addr(Addr))
356	{
357		IEEE80211_DEBUG(IEEE80211_DL_ERR, "get TS for Broadcast or Multicast\n");
358		return false;
359	}
360	if (ieee->current_network.qos_data.supported == 0)
361		UP = 0;
362	else
363	{
364		// In WMM case: we use 4 TID only
365		if (!IsACValid(TID))
366		{
367			IEEE80211_DEBUG(IEEE80211_DL_ERR, " in %s(), TID(%d) is not valid\n", __FUNCTION__, TID);
368			return false;
369		}
370
371		switch(TID)
372		{
373		case 0:
374		case 3:
375			UP = 0;
376			break;
377
378		case 1:
379		case 2:
380			UP = 2;
381			break;
382
383		case 4:
384		case 5:
385			UP = 5;
386			break;
387
388		case 6:
389		case 7:
390			UP = 7;
391			break;
392		}
393	}
394
395	*ppTS = SearchAdmitTRStream(
396			ieee,
397			Addr,
398			UP,
399			TxRxSelect);
400	if(*ppTS != NULL)
401	{
402		return true;
403	}
404	else
405	{
406		if(bAddNewTs == false)
407		{
408			IEEE80211_DEBUG(IEEE80211_DL_TS, "add new TS failed(tid:%d)\n", UP);
409			return false;
410		}
411		else
412		{
413			//
414			// Create a new Traffic stream for current Tx/Rx
415			// This is for EDCA and WMM to add a new TS.
416			// For HCCA or WMMSA, TS cannot be addmit without negotiation.
417			//
418			TSPEC_BODY	TSpec;
419			PQOS_TSINFO		pTSInfo = &TSpec.f.TSInfo;
420			struct list_head*	pUnusedList =
421								(TxRxSelect == TX_DIR)?
422								(&ieee->Tx_TS_Unused_List):
423								(&ieee->Rx_TS_Unused_List);
424
425			struct list_head*	pAddmitList =
426								(TxRxSelect == TX_DIR)?
427								(&ieee->Tx_TS_Admit_List):
428								(&ieee->Rx_TS_Admit_List);
429
430			DIRECTION_VALUE		Dir =		(ieee->iw_mode == IW_MODE_MASTER)?
431								((TxRxSelect==TX_DIR)?DIR_DOWN:DIR_UP):
432								((TxRxSelect==TX_DIR)?DIR_UP:DIR_DOWN);
433			IEEE80211_DEBUG(IEEE80211_DL_TS, "to add Ts\n");
434			if(!list_empty(pUnusedList))
435			{
436				(*ppTS) = list_entry(pUnusedList->next, TS_COMMON_INFO, List);
437				list_del_init(&(*ppTS)->List);
438				if(TxRxSelect==TX_DIR)
439				{
440					PTX_TS_RECORD tmp = container_of(*ppTS, TX_TS_RECORD, TsCommonInfo);
441					ResetTxTsEntry(tmp);
442				}
443				else{
444					PRX_TS_RECORD tmp = container_of(*ppTS, RX_TS_RECORD, TsCommonInfo);
445					ResetRxTsEntry(tmp);
446				}
447
448				IEEE80211_DEBUG(IEEE80211_DL_TS, "to init current TS, UP:%d, Dir:%d, addr:%pM\n", UP, Dir, Addr);
449				// Prepare TS Info releated field
450				pTSInfo->field.ucTrafficType = 0;			// Traffic type: WMM is reserved in this field
451				pTSInfo->field.ucTSID = UP;			// TSID
452				pTSInfo->field.ucDirection = Dir;			// Direction: if there is DirectLink, this need additional consideration.
453				pTSInfo->field.ucAccessPolicy = 1;		// Access policy
454				pTSInfo->field.ucAggregation = 0; 		// Aggregation
455				pTSInfo->field.ucPSB = 0; 				// Aggregation
456				pTSInfo->field.ucUP = UP;				// User priority
457				pTSInfo->field.ucTSInfoAckPolicy = 0;		// Ack policy
458				pTSInfo->field.ucSchedule = 0;			// Schedule
459
460				MakeTSEntry(*ppTS, Addr, &TSpec, NULL, 0, 0);
461				AdmitTS(ieee, *ppTS, 0);
462				list_add_tail(&((*ppTS)->List), pAddmitList);
463				// if there is DirectLink, we need to do additional operation here!!
464
465				return true;
466			}
467			else
468			{
469				IEEE80211_DEBUG(IEEE80211_DL_ERR, "in function %s() There is not enough TS record to be used!!", __FUNCTION__);
470				return false;
471			}
472		}
473	}
474}
475
476void RemoveTsEntry(
477	struct ieee80211_device*	ieee,
478	PTS_COMMON_INFO			pTs,
479	TR_SELECT			TxRxSelect
480	)
481{
482	//u32 flags = 0;
483	unsigned long flags = 0;
484	del_timer_sync(&pTs->SetupTimer);
485	del_timer_sync(&pTs->InactTimer);
486	TsInitDelBA(ieee, pTs, TxRxSelect);
487
488	if(TxRxSelect == RX_DIR)
489	{
490//#ifdef TO_DO_LIST
491		PRX_REORDER_ENTRY	pRxReorderEntry;
492		PRX_TS_RECORD 		pRxTS = (PRX_TS_RECORD)pTs;
493		if(timer_pending(&pRxTS->RxPktPendingTimer))
494			del_timer_sync(&pRxTS->RxPktPendingTimer);
495
496		while(!list_empty(&pRxTS->RxPendingPktList))
497		{
498		//      PlatformAcquireSpinLock(Adapter, RT_RX_SPINLOCK);
499			spin_lock_irqsave(&(ieee->reorder_spinlock), flags);
500			//pRxReorderEntry = list_entry(&pRxTS->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
501			pRxReorderEntry = (PRX_REORDER_ENTRY)list_entry(pRxTS->RxPendingPktList.prev,RX_REORDER_ENTRY,List);
502			list_del_init(&pRxReorderEntry->List);
503			{
504				int i = 0;
505				struct ieee80211_rxb * prxb = pRxReorderEntry->prxb;
506				if (unlikely(!prxb))
507				{
508					spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
509					return;
510				}
511				for(i =0; i < prxb->nr_subframes; i++) {
512					dev_kfree_skb(prxb->subframes[i]);
513				}
514				kfree(prxb);
515				prxb = NULL;
516			}
517			list_add_tail(&pRxReorderEntry->List,&ieee->RxReorder_Unused_List);
518			//PlatformReleaseSpinLock(Adapter, RT_RX_SPINLOCK);
519			spin_unlock_irqrestore(&(ieee->reorder_spinlock), flags);
520		}
521
522//#endif
523	}
524	else
525	{
526		PTX_TS_RECORD pTxTS = (PTX_TS_RECORD)pTs;
527		del_timer_sync(&pTxTS->TsAddBaTimer);
528	}
529}
530
531void RemovePeerTS(struct ieee80211_device* ieee, u8* Addr)
532{
533	PTS_COMMON_INFO	pTS, pTmpTS;
534	printk("===========>RemovePeerTS,%pM\n", Addr);
535	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List)
536	{
537		if (memcmp(pTS->Addr, Addr, 6) == 0)
538		{
539			RemoveTsEntry(ieee, pTS, TX_DIR);
540			list_del_init(&pTS->List);
541			list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
542		}
543	}
544
545	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List)
546	{
547		if (memcmp(pTS->Addr, Addr, 6) == 0)
548		{
549			printk("====>remove Tx_TS_admin_list\n");
550			RemoveTsEntry(ieee, pTS, TX_DIR);
551			list_del_init(&pTS->List);
552			list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
553		}
554	}
555
556	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List)
557	{
558		if (memcmp(pTS->Addr, Addr, 6) == 0)
559		{
560			RemoveTsEntry(ieee, pTS, RX_DIR);
561			list_del_init(&pTS->List);
562			list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
563		}
564	}
565
566	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List)
567	{
568		if (memcmp(pTS->Addr, Addr, 6) == 0)
569		{
570			RemoveTsEntry(ieee, pTS, RX_DIR);
571			list_del_init(&pTS->List);
572			list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
573		}
574	}
575}
576
577void RemoveAllTS(struct ieee80211_device* ieee)
578{
579	PTS_COMMON_INFO pTS, pTmpTS;
580	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Pending_List, List)
581	{
582		RemoveTsEntry(ieee, pTS, TX_DIR);
583		list_del_init(&pTS->List);
584		list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
585	}
586
587	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Tx_TS_Admit_List, List)
588	{
589		RemoveTsEntry(ieee, pTS, TX_DIR);
590		list_del_init(&pTS->List);
591		list_add_tail(&pTS->List, &ieee->Tx_TS_Unused_List);
592	}
593
594	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Pending_List, List)
595	{
596		RemoveTsEntry(ieee, pTS, RX_DIR);
597		list_del_init(&pTS->List);
598		list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
599	}
600
601	list_for_each_entry_safe(pTS, pTmpTS, &ieee->Rx_TS_Admit_List, List)
602	{
603		RemoveTsEntry(ieee, pTS, RX_DIR);
604		list_del_init(&pTS->List);
605		list_add_tail(&pTS->List, &ieee->Rx_TS_Unused_List);
606	}
607}
608
609void TsStartAddBaProcess(struct ieee80211_device* ieee, PTX_TS_RECORD	pTxTS)
610{
611	if(pTxTS->bAddBaReqInProgress == false)
612	{
613		pTxTS->bAddBaReqInProgress = true;
614		if(pTxTS->bAddBaReqDelayed)
615		{
616			IEEE80211_DEBUG(IEEE80211_DL_BA, "TsStartAddBaProcess(): Delayed Start ADDBA after 60 sec!!\n");
617			mod_timer(&pTxTS->TsAddBaTimer, jiffies + MSECS(TS_ADDBA_DELAY));
618		}
619		else
620		{
621			IEEE80211_DEBUG(IEEE80211_DL_BA,"TsStartAddBaProcess(): Immediately Start ADDBA now!!\n");
622			mod_timer(&pTxTS->TsAddBaTimer, jiffies+10); //set 10 ticks
623		}
624	}
625	else
626		IEEE80211_DEBUG(IEEE80211_DL_ERR, "%s()==>BA timer is already added\n", __FUNCTION__);
627}
628EXPORT_SYMBOL(RemovePeerTS);
629