Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

673 lines
18 KiB

  1. /*****************************************************************************
  2. ** **
  3. ** COPYRIGHT (C) 2000, 2001 MKNET CORPORATION **
  4. ** DEVELOPED FOR THE MK7100-BASED VFIR PCI CONTROLLER. **
  5. ** **
  6. *****************************************************************************/
  7. /**********************************************************************
  8. Module Name:
  9. INTERRUP.C
  10. Procedures:
  11. MKMiniportIsr
  12. MKMiniportHandleInterrupt
  13. ProcessRXComp
  14. ProcessTXComp
  15. ProcessRXCompIsr
  16. ProcessTXCompIsr
  17. Comments:
  18. **********************************************************************/
  19. #include "precomp.h"
  20. #pragma hdrstop
  21. #include "protot.h"
  22. //-----------------------------------------------------------------------------
  23. // Procedure: [MKMiniportIsr] (miniport)
  24. //
  25. // Description: This is the interrupt service routine running at interrupt level.
  26. // It checks to see if there is an interrupt pending. If yes, it
  27. // disables board interrupts and schedules HandleInterrupt callback.
  28. //
  29. // Arguments:
  30. // MiniportAdapterContext - The context value returned by the Miniport
  31. // when the adapter was initialized (see the call
  32. // NdisMSetAttributes). In reality, it is a pointer to MK7_ADAPTER.
  33. //
  34. // Returns:
  35. // InterruptRecognized - Returns True if the interrupt belonges to this
  36. // adapter, and false otherwise.
  37. // QueueMiniportHandleInterrupt - Returns True if we want a callback to
  38. // HandleInterrupt.
  39. //
  40. //-----------------------------------------------------------------------------
  41. VOID
  42. MKMiniportIsr( OUT PBOOLEAN InterruptRecognized,
  43. OUT PBOOLEAN QueueMiniportHandleInterrupt,
  44. IN NDIS_HANDLE MiniportAdapterContext )
  45. {
  46. MK7REG mk7reg, ReadInt;
  47. PMK7_ADAPTER Adapter = PMK7_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
  48. DBGLOG("=> INT", 0);
  49. //****************************************
  50. // Read the Interrupt Event Reg and save to context area for
  51. // DPC processing.
  52. //
  53. // IMPORTANT NOTE: The ISR runs at DIRQL level and is, thus, higher
  54. // proiority than other miniport routines. We need to be careful
  55. // about shared resources. Example: If our multi-pkt send is running
  56. // when an int occurs, the send routine can be preempted. If the ISR
  57. // and the send routine access shared resource then we have problems.
  58. //
  59. // We save the interrupt in recentInt because the interrupt event
  60. // register may be cleared upon a Read. (This has been verified.)
  61. //****************************************
  62. //MK7Reg_Read(Adapter, R_INTS, &Adapter->recentInt);
  63. MK7Reg_Read(Adapter, R_INTS, &ReadInt);
  64. if (MK7OurInterrupt(ReadInt)) {
  65. // Int enable should happen only after DPC is done.
  66. // Also disabling interrupt clears Interrupt Status.
  67. MK7DisableInterrupt(Adapter);
  68. Adapter->recentInt = ReadInt;
  69. MK7Reg_Read(Adapter, R_CFG3, &mk7reg);
  70. if ((mk7reg & 0x1000) != 0){
  71. mk7reg &= 0xEFFF;
  72. MK7Reg_Write(Adapter, R_CFG3, mk7reg);
  73. mk7reg |= 0x1000;
  74. MK7Reg_Write(Adapter, R_CFG3, mk7reg);
  75. // mk7reg = mk7reg; //For Debugging
  76. }
  77. #if DBG
  78. GDbgStat.isrCnt++;
  79. DBGLOG(" INT status", Adapter->recentInt);
  80. #endif
  81. // Don't do TX processing in ISR. I saw a condition where SetSpeed()
  82. // was called while tcbused = 1. I set the change-speed flags correctly.
  83. // But the TX processing in ISR cleared tcbused resulting in the code
  84. // not chaning speed in DPC.
  85. // ProcessTXCompIsr(Adapter);
  86. ProcessRXCompIsr(Adapter);
  87. *InterruptRecognized = TRUE;
  88. *QueueMiniportHandleInterrupt = TRUE;
  89. }
  90. else {
  91. *InterruptRecognized = FALSE;
  92. *QueueMiniportHandleInterrupt = FALSE;
  93. }
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Procedure: [MKMiniportHandleInterrupt]
  97. //
  98. // Description: This is the DPC for the ISR. It goes on to do RX & TX
  99. // completion processing.
  100. //
  101. // Arguments:
  102. // MiniportAdapterContext (miniport) - The context value returned by the
  103. // Miniport when the adapter was initialized (see the call
  104. // NdisMSetAttributes). In reality, it is a pointer to MK7_ADAPTER.
  105. //
  106. // Returns: (none)
  107. //-----------------------------------------------------------------------------
  108. VOID
  109. MKMiniportHandleInterrupt(NDIS_HANDLE MiniportAdapterContext)
  110. {
  111. MK7REG mk7reg;
  112. PMK7_ADAPTER Adapter = PMK7_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
  113. NdisAcquireSpinLock(&Adapter->Lock);
  114. DBGLOG("=> MKMiniportHandleInterrupt", Adapter->recentInt);
  115. //****************************************
  116. // DPC runs at Dispatch Level IRQL (just below ISR's DIRQL in proiority).
  117. // Note that recentInt can be modified in the ISR routine, which is
  118. // higher IRQL. But since this is DPC w/ int disabled, recentInt can
  119. // be safely queried.
  120. //****************************************
  121. ProcessTXComp(Adapter);
  122. ProcessRXComp(Adapter);
  123. Adapter->recentInt = 0; // clear the saved int
  124. NdisReleaseSpinLock(&Adapter->Lock);
  125. MK7EnableInterrupt(Adapter);
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Procedure: [ProcessRXComp]
  129. //
  130. // Description: This is the DPC for RX completions.
  131. //
  132. // Arguments:
  133. // Adapter - ptr to Adapter object instance
  134. //
  135. // Returns: (none)
  136. //-----------------------------------------------------------------------------
  137. VOID ProcessRXComp(PMK7_ADAPTER Adapter)
  138. {
  139. UINT PacketArrayCount, i;
  140. // 4.0.1 BOC
  141. UINT PacketFreeCount;
  142. // 4.0.1 EOC
  143. PNDIS_PACKET PacketArray[MAX_ARRAY_RECEIVE_PACKETS];
  144. PRCB rcb;
  145. PRRD rrd;
  146. PRPD rpd;
  147. UINT rrdstatus;
  148. BOOLEAN done=FALSE;
  149. BOOLEAN gotdata=FALSE;
  150. MK7REG intereg;
  151. UINT rcvcnt;
  152. // 4.0.1 BOC
  153. BOOLEAN LowResource;
  154. // 4.0.1 EOC
  155. // Process only if we get the corresponding int.
  156. if (!(Adapter->recentInt & B_RX_INTS)) {
  157. return;
  158. }
  159. DBGLOG("=> RX comp", 0);
  160. #if DBG
  161. GDbgStat.rxIsrCnt++;
  162. #endif
  163. // 1.0.0
  164. // If we have just started receiving a packet, indicate media-busy
  165. // to the protocol.
  166. // if (Adapter->mediaBusy && !Adapter->haveIndicatedMediaBusy) {
  167. // if (Adapter->CurrentSpeed > MAX_SIR_SPEED) {
  168. //#if DBG
  169. // DBGLOG("Error: MKMiniportHandleInterrupt is in wrong state",
  170. // Adapter->CurrentSpeed);
  171. //#endif
  172. // ASSERT(0);
  173. // }
  174. // NdisMIndicateStatus(Adapter->MK7AdapterHandle,
  175. // NDIS_STATUS_MEDIA_BUSY, NULL, 0);
  176. // NdisMIndicateStatusComplete(Adapter->MK7AdapterHandle);
  177. // RYM-5+
  178. // May need to protect this because ISR also writes to this?
  179. // Adapter->haveIndicatedMediaBusy = TRUE;
  180. // }
  181. rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
  182. rrd = rcb->rrd;
  183. rrdstatus = rrd->status; // for debug
  184. do {
  185. PacketArrayCount = 0;
  186. // 4.0.1 BOC
  187. LowResource = FALSE;
  188. // 4.0.1 EOC.
  189. // 4.0.1 BOC
  190. PacketFreeCount = 0;
  191. // 4.0.1 EOC.
  192. // inner loop
  193. while ( !HwOwnsRrd(rrd) &&
  194. (PacketArrayCount < MAX_ARRAY_RECEIVE_PACKETS) ) {
  195. // 4.0.1 BOC
  196. if (QueueEmpty(&Adapter->FreeRpdList))
  197. {
  198. break;
  199. }
  200. // 4.0.1 EOC.
  201. #if DBG
  202. // DBG_STAT
  203. if (RrdAnyError(rrd)) {
  204. GDbgStat.rxErrCnt++;
  205. GDbgStat.rxErr |= rrd->status;
  206. DBGSTATUS1(" RX err: %x \n\r", rrd->status);
  207. }
  208. if (Adapter->recentInt & B_RX_INTS)
  209. GDbgStat.rxComp++;
  210. else
  211. GDbgStat.rxCompNoInt++;
  212. #endif
  213. if (RrdError(rrd)) {
  214. // If error just give RRD back to hw and continue.
  215. // (NOTE: This may indicate errors for MIR & FIR only.
  216. // The sw does the FCS for SIR.)
  217. // (Note that hw may not detect all SIR errors.)
  218. rrd->count = 0;
  219. GrantRrdToHw(rrd);
  220. // Next receive to read from
  221. Adapter->nextRxRcbIdx++;
  222. Adapter->nextRxRcbIdx %= Adapter->NumRcb;
  223. rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
  224. rrd = rcb->rrd;
  225. rrdstatus = rrd->status; // for debug
  226. // break; // this to do 1 rx per int
  227. DBGLOG(" RX err", 0);
  228. continue; // this to do > 1 rx per int
  229. }
  230. // Additional software processing for SIR frames
  231. if (Adapter->CurrentSpeed <= MAX_SIR_SPEED) {
  232. if (!ProcRXSir(rcb->rpd->databuff, (UINT)rrd->count)) {
  233. // If error just give RRD back to hw and continue.
  234. rrd->count = 0;
  235. GrantRrdToHw(rrd);
  236. // Next receive to read from
  237. Adapter->nextRxRcbIdx++;
  238. Adapter->nextRxRcbIdx %= Adapter->NumRcb;
  239. rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
  240. rrd = rcb->rrd;
  241. rrdstatus = rrd->status; // for debug
  242. #if DBG
  243. GDbgStat.rxErrCnt++;
  244. GDbgStat.rxErrSirCrc++;
  245. #endif
  246. // break; // this to do 1 rx per int
  247. DBGLOG(" RX err", 0);
  248. continue; // this to do > 1 rx per int
  249. }
  250. }
  251. // Remove count of FCS bytes:
  252. // SIR/MIR = 2 (16 bits)
  253. // FIR/VFIR = 4 (32 bits)
  254. if (Adapter->CurrentSpeed < MIN_FIR_SPEED) {
  255. rcvcnt = (UINT) rrd->count - SIR_FCS_SIZE;
  256. DBGLOG(" RX comp (slow)", 0);
  257. }
  258. else {
  259. rcvcnt = (UINT) rrd->count - FASTIR_FCS_SIZE;
  260. DBGLOG(" RX comp (fast)", 0);
  261. }
  262. NdisAdjustBufferLength(rcb->rpd->ReceiveBuffer, rcvcnt);
  263. #if DBG
  264. if (rcvcnt > GDbgStat.rxLargestPkt) {
  265. GDbgStat.rxLargestPkt = rcvcnt;
  266. }
  267. // NdisGetCurrentSystemTime((PLARGE_INTEGER)&GDbgTARspTime[GDbgTATimeIdx]);
  268. // GDbgTATime[GDbgTATimeIdx] = GDbgTARspTime[GDbgTATimeIdx] -
  269. // GDbgTACmdTime[GDbgTATimeIdx];
  270. // GDbgTATimeIdx++;
  271. // GDbgTATimeIdx %= 1000; // wrap around
  272. #endif
  273. PacketArray[PacketArrayCount] = rcb->rpd->ReceivePacket;
  274. // 4.0.1 BOC
  275. if (((Adapter->NumRpd - Adapter->UsedRpdCount-Adapter->NumRcb) <= 4)|| LowResource==TRUE) {
  276. NDIS_SET_PACKET_STATUS(PacketArray[PacketArrayCount], NDIS_STATUS_RESOURCES);
  277. LowResource = TRUE;
  278. PacketFreeCount++;
  279. }
  280. else {
  281. // NDIS_SET_PACKET_STATUS(PacketArray[PacketArrayCount], NDIS_STATUS_SUCCESS);
  282. NDIS_SET_PACKET_STATUS(PacketArray[PacketArrayCount], NDIS_STATUS_SUCCESS);
  283. }
  284. // 4.0.1 EOC
  285. PacketArrayCount++;
  286. // 4.0.1 BOC
  287. Adapter->UsedRpdCount++;
  288. // 4.0.1 EOC
  289. // unbind the one we just indicated to upper layer
  290. rcb->rpd = (PRPD)NULL;
  291. rcb->rrd->addr = 0;
  292. // get a new one for the next rx
  293. rpd = (PRPD) QueuePopHead(&Adapter->FreeRpdList);
  294. // 4.0.1 BOC
  295. ASSERT(!(rpd == (PRPD)NULL));
  296. // if (rpd == (PRPD)NULL) {
  297. //****************************************
  298. // If there's no existing RCB that's waiting for a
  299. // RPD, set the start of waiting RCBs to this one.
  300. //****************************************
  301. // if (Adapter->rcbPendRpdCnt == 0) {
  302. // Adapter->rcbPendRpdIdx = Adapter->nextRxRcbIdx;
  303. // }
  304. // Adapter->rcbPendRpdCnt++;
  305. //
  306. //#if DBG
  307. // GDbgStat.rxNoRpd++;
  308. //#endif
  309. // }
  310. // else {
  311. // 4.0.1 EOC
  312. // bind new RDP-Packet to RCB-RRD
  313. rcb->rpd = rpd;
  314. rcb->rrd->addr = rpd->databuffphys;
  315. rcb->rrd->count = 0;
  316. GrantRrdToHw(rcb->rrd);
  317. // 4.0.1 BOC
  318. // }
  319. // 4.0.1 EOC.
  320. // Next receive to read from
  321. Adapter->nextRxRcbIdx++;
  322. Adapter->nextRxRcbIdx %= Adapter->NumRcb;
  323. rcb = Adapter->pRcbArray[Adapter->nextRxRcbIdx];
  324. rrd = rcb->rrd;
  325. rrdstatus = rrd->status; // for debug
  326. } // while
  327. if (PacketArrayCount >= MAX_ARRAY_RECEIVE_PACKETS) {
  328. DBGLOG(" RX max indicate", 0);
  329. }
  330. //****************************************
  331. // RYM-5+
  332. // NOTE: This controls whether we poll the next ring buffers
  333. // for data after serviceing the current ring buffer that
  334. // caused the original RX int. The current int scheme
  335. // is to get 1 rx buffer per int. So the following lines
  336. // are replaced with a 1 rx per int logic.
  337. // **We're done when we run into the 1st Ring entry that
  338. // **we have no ownership.
  339. //****************************************
  340. if (HwOwnsRrd(rrd))
  341. done = TRUE;
  342. // done = TRUE;
  343. // Indicate away
  344. if(PacketArrayCount) {
  345. NdisReleaseSpinLock(&Adapter->Lock);
  346. NdisMIndicateReceivePacket(Adapter->MK7AdapterHandle,
  347. PacketArray,
  348. PacketArrayCount);
  349. #if DBG
  350. GDbgStat.rxPktsInd += PacketArrayCount;
  351. #endif
  352. gotdata = TRUE;
  353. NdisAcquireSpinLock(&Adapter->Lock);
  354. //DBGLOG(" ProcessRXInterrupt: indicated Packet(s)", PacketArrayCount);
  355. }
  356. //****************************************
  357. // Check Packet status on return from Indicate. Pending means
  358. // NDIS-upper layer still holds it, else it's ours.
  359. //****************************************
  360. // Don't do this for deserialized driver.
  361. // for (i=0; i<PacketArrayCount; i++ ) {
  362. // NDIS_STATUS ReturnStatus;
  363. //
  364. // ReturnStatus = NDIS_GET_PACKET_STATUS(PacketArray[i]);
  365. //
  366. // recover the RPD
  367. // rpd = *(PRPD *)(PacketArray[i]->MiniportReserved);
  368. //
  369. // if (ReturnStatus != NDIS_STATUS_PENDING) {
  370. // ProcReturnedRpd(Adapter, rpd);
  371. // }
  372. // }
  373. // 4.0.1 BOC
  374. for (i=PacketArrayCount-PacketFreeCount; i<PacketArrayCount; i++){
  375. rpd = *(PRPD *)(PacketArray[i]->MiniportReserved);
  376. ProcReturnedRpd(Adapter, rpd);
  377. Adapter->UsedRpdCount--;
  378. }
  379. // 4.0.1 EOC.
  380. } while (!done);
  381. Adapter->nowReceiving = FALSE;
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Procedure: [ProcessTXComp]
  385. //
  386. // Description: TX complete processing in DPC. This is very similar to
  387. // ProcessTXCompIsr(), the main difference being we also process the TX
  388. // queue here and perform TXs as necessary.
  389. //
  390. // Arguements: Adapter.
  391. //
  392. // Result: (none)
  393. //-----------------------------------------------------------------------------
  394. VOID ProcessTXComp(PMK7_ADAPTER Adapter)
  395. {
  396. PTCB tcb;
  397. MK7REG mk7reg;
  398. NDIS_STATUS SendStatus;
  399. PNDIS_PACKET QueuePacket;
  400. // Process only if we get the corresponding int.
  401. if (!(Adapter->recentInt & B_TX_INTS)) {
  402. return;
  403. }
  404. DBGLOG("=> TX comp", 0);
  405. #if DBG
  406. GDbgStat.txIsrCnt++;
  407. #endif
  408. // Debug
  409. if (Adapter->CurrentSpeed > MAX_SIR_SPEED) {
  410. DBGLOG(" TX comp (fast)", 0);
  411. }
  412. else {
  413. DBGLOG(" TX comp (slow)", 0);
  414. }
  415. // Simplified change speed
  416. if (Adapter->changeSpeedPending == CHANGESPEED_ON_DONE) {
  417. // Note: We're changing speed in TX mode.
  418. MK7ChangeSpeedNow(Adapter);
  419. Adapter->changeSpeedPending = 0;
  420. }
  421. while (Adapter->tcbUsed > 0) {
  422. tcb = Adapter->pTcbArray[Adapter->nextReturnTcbIdx];
  423. if ( !HwOwnsTrd(tcb->trd) ) {
  424. #if DBG
  425. if (TrdAnyError(tcb->trd)) {
  426. GDbgStat.txErrCnt++;
  427. GDbgStat.txErr |= tcb->trd->status;
  428. DBGSTATUS1(" TX err: %x \n\r", tcb->trd->status);
  429. }
  430. if (Adapter->recentInt & B_TX_INTS)
  431. GDbgStat.txComp++;
  432. else
  433. GDbgStat.txCompNoInt++;
  434. #endif
  435. tcb->trd->count = 0;
  436. // For each completing TX there's a corresponding q'd pkt.
  437. // We release it here.
  438. QueuePacket = Adapter->FirstTxQueue;
  439. DequeuePacket(Adapter->FirstTxQueue, Adapter->LastTxQueue);
  440. Adapter->NumPacketsQueued--;
  441. NDIS_SET_PACKET_STATUS(QueuePacket, NDIS_STATUS_SUCCESS);
  442. NdisMSendComplete( Adapter->MK7AdapterHandle,
  443. QueuePacket,
  444. NDIS_STATUS_SUCCESS);
  445. Adapter->HangCheck = 0; // 1.0.0
  446. Adapter->nextReturnTcbIdx++;
  447. Adapter->nextReturnTcbIdx %= Adapter->NumTcb;
  448. Adapter->tcbUsed--;
  449. }
  450. else {
  451. DBGLOG(" Not our TCB; but tcbUsed>0", 0);
  452. break;
  453. }
  454. }
  455. // No resource even if we have more to send. Return now & let subsequent
  456. // TX completes keep the ball rolling.
  457. if (Adapter->tcbUsed >= Adapter->NumTcb) {
  458. // NdisReleaseSpinLock(&Adapter->Lock);
  459. return;
  460. }
  461. // If no TXs queued and all TXs are done, then switch to RX mode.
  462. if ( (!Adapter->FirstTxQueue) && (Adapter->tcbUsed == 0) ) {
  463. MK7SwitchToRXMode(Adapter);
  464. return;
  465. }
  466. // Send the q'd pkts until all done or until all TX ring buffers are used up.
  467. //while(Adapter->FirstTxQueue) {
  468. if (Adapter->FirstTxQueue) {
  469. #if DBG
  470. GDbgStat.txProcQ++;
  471. #endif
  472. DBGLOG(" Proc Q", 0);
  473. QueuePacket = Adapter->FirstTxQueue;
  474. SendStatus = SendPkt(Adapter, QueuePacket);
  475. }
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Procedure: [ProcessRXCompIsr]
  479. //
  480. // Description: Some RX complete processing in ISR.
  481. //
  482. // Arguements: Adapter.
  483. //
  484. // Result: (none)
  485. //-----------------------------------------------------------------------------
  486. VOID ProcessRXCompIsr(PMK7_ADAPTER Adapter)
  487. {
  488. // 4.1.0 Back for HW_VER_1 support
  489. if (Adapter->recentInt & B_RX_INTS) {
  490. Adapter->nowReceiving=TRUE;
  491. // if (!Adapter->mediaBusy) {
  492. // mediaBusy: IrLAP clears mediaBusy (via OID) to indicate
  493. // it wants to be notified when media becomes busy. Here
  494. // we detect it is cleared. We then set it and clear
  495. // haveIndicatedMediaBusy so we do notify later in DPC.
  496. // Adapter->mediaBusy = TRUE;
  497. // Adapter->haveIndicatedMediaBusy = FALSE;
  498. // Adapter->nowReceiving = TRUE;
  499. // }
  500. }
  501. }
  502. //-----------------------------------------------------------------------------
  503. // Procedure: [ProcessTXCompIsr]
  504. //
  505. // Description: TX complete processing in ISR. This is very similar to
  506. // ProcessTXComp() except we don't start any TX's here.
  507. //
  508. // Arguements: Adapter.
  509. //
  510. // Result: (none)
  511. //-----------------------------------------------------------------------------
  512. VOID ProcessTXCompIsr(PMK7_ADAPTER Adapter)
  513. {
  514. PTCB tcb;
  515. MK7REG mk7reg;
  516. //******************************
  517. // Whether or not there was a TX-completion interrupt, we do some
  518. // processing here in case ever the driver or hw missed an
  519. // interrupt previously.
  520. //
  521. // We loop until all tcb's are returned (tcbUsed == 0) or we run into
  522. // a TX ring buff that the hw still owns (HwOwnsTrd()). When we leave
  523. // here, we should have processed all current TX completions based on
  524. // the TX ownership bit. We switch to RX mode ONLY after all TX are
  525. // completed (either here in the ISR or in DPC).
  526. //******************************
  527. while (Adapter->tcbUsed > 0) {
  528. tcb = Adapter->pTcbArray[Adapter->nextReturnTcbIdx];
  529. if ( !HwOwnsTrd(tcb->trd) ) {
  530. #if DBG
  531. if (TrdAnyError(tcb->trd)) {
  532. GDbgStat.txErrCnt++;
  533. GDbgStat.txErr |= tcb->trd->status;
  534. DBGSTATUS1(" TX err: %x \n\r", tcb->trd->status);
  535. }
  536. if (Adapter->recentInt & B_TX_INTS)
  537. GDbgStat.txComp++;
  538. else
  539. GDbgStat.txCompNoInt++;
  540. #endif
  541. tcb->trd->count = 0;
  542. Adapter->nextReturnTcbIdx++;
  543. Adapter->nextReturnTcbIdx %= Adapter->NumTcb;
  544. Adapter->tcbUsed--;
  545. }
  546. else {
  547. return;
  548. }
  549. }
  550. }