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