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.

1486 lines
37 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. mp_nic.c
  5. Abstract:
  6. This module contains miniport send/receive routines
  7. Revision History:
  8. Who When What
  9. -------- -------- ----------------------------------------------
  10. DChen 11-01-99 created
  11. Notes:
  12. --*/
  13. #include "precomp.h"
  14. #if DBG
  15. #define _FILENUMBER 'CINM'
  16. #endif
  17. __inline VOID MP_FREE_SEND_PACKET(
  18. IN PMP_ADAPTER Adapter,
  19. IN PMP_TCB pMpTcb
  20. )
  21. /*++
  22. Routine Description:
  23. Recycle a MP_TCB and complete the packet if necessary
  24. Assumption: Send spinlock has been acquired
  25. Arguments:
  26. Adapter Pointer to our adapter
  27. pMpTcb Pointer to MP_TCB
  28. Return Value:
  29. None
  30. --*/
  31. {
  32. PNDIS_PACKET Packet;
  33. PNDIS_BUFFER CurrBuffer;
  34. ASSERT(MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE));
  35. Packet = pMpTcb->Packet;
  36. pMpTcb->Packet = NULL;
  37. pMpTcb->Count = 0;
  38. if (pMpTcb->MpTxBuf)
  39. {
  40. ASSERT(MP_TEST_FLAG(pMpTcb, fMP_TCB_USE_LOCAL_BUF));
  41. PushEntryList(&Adapter->SendBufList, &pMpTcb->MpTxBuf->SList);
  42. pMpTcb->MpTxBuf = NULL;
  43. }
  44. else if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_MAP_REGISTER))
  45. {
  46. //
  47. // Complete physical mapping for each buffer in this packet
  48. //
  49. ASSERT(Packet);
  50. CurrBuffer = pMpTcb->FirstBuffer;
  51. while (CurrBuffer)
  52. {
  53. NdisMCompleteBufferPhysicalMapping(
  54. Adapter->AdapterHandle,
  55. CurrBuffer,
  56. Adapter->CurrMapRegHead);
  57. Adapter->CurrMapRegHead++;
  58. if (Adapter->CurrMapRegHead == (ULONG)Adapter->NumTbd)
  59. Adapter->CurrMapRegHead = 0;
  60. //
  61. // Get the next buffer
  62. //
  63. NdisGetNextBuffer(CurrBuffer, &CurrBuffer);
  64. }
  65. }
  66. MP_CLEAR_FLAGS(pMpTcb);
  67. Adapter->CurrSendHead = Adapter->CurrSendHead->Next;
  68. Adapter->nBusySend--;
  69. ASSERT(Adapter->nBusySend >= 0);
  70. if (Packet)
  71. {
  72. NdisReleaseSpinLock(&Adapter->SendLock);
  73. DBGPRINT(MP_TRACE, ("Calling NdisMSendComplete, Pkt= "PTR_FORMAT"\n", Packet));
  74. NdisMSendComplete(
  75. MP_GET_ADAPTER_HANDLE(Adapter),
  76. Packet,
  77. NDIS_STATUS_SUCCESS);
  78. NdisAcquireSpinLock(&Adapter->SendLock);
  79. }
  80. }
  81. NDIS_STATUS MpSendPacket(
  82. IN PMP_ADAPTER Adapter,
  83. IN PNDIS_PACKET Packet,
  84. IN BOOLEAN bFromQueue
  85. )
  86. /*++
  87. Routine Description:
  88. Do the work to send a packet
  89. Assumption: Send spinlock has been acquired
  90. Arguments:
  91. Adapter Pointer to our adapter
  92. Packet The packet
  93. bFromQueue TRUE if it's taken from the send wait queue
  94. Return Value:
  95. NDIS_STATUS_SUCCESS
  96. NDIS_STATUS_PENDING Put into the send wait queue
  97. NDIS_STATUS_HARD_ERRORS
  98. --*/
  99. {
  100. NDIS_STATUS Status = NDIS_STATUS_PENDING;
  101. NDIS_STATUS SendStatus;
  102. PMP_TCB pMpTcb = NULL;
  103. PMP_TXBUF pMpTxBuf = NULL;
  104. ULONG BytesCopied;
  105. BOOLEAN bCompletePacket = FALSE;
  106. // Mimiced frag list if map registers are used, on the local stack as it's not so big
  107. MP_FRAG_LIST FragList;
  108. // Pointer to either the scatter gather or the local mimiced frag list
  109. PMP_FRAG_LIST pFragList;
  110. DBGPRINT(MP_TRACE, ("--> MpSendPacket, Pkt= "PTR_FORMAT"\n", Packet));
  111. pMpTcb = Adapter->CurrSendTail;
  112. ASSERT(!MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE));
  113. NdisQueryPacket(
  114. Packet,
  115. &pMpTcb->PhysBufCount,
  116. &pMpTcb->BufferCount,
  117. &pMpTcb->FirstBuffer,
  118. &pMpTcb->PacketLength);
  119. ASSERT(pMpTcb->PhysBufCount);
  120. ASSERT(pMpTcb->FirstBuffer);
  121. ASSERT(pMpTcb->PacketLength);
  122. //
  123. // Check to see if we need to coalesce
  124. //
  125. if (pMpTcb->PacketLength < NIC_MIN_PACKET_SIZE ||
  126. pMpTcb->PhysBufCount > NIC_MAX_PHYS_BUF_COUNT)
  127. {
  128. //
  129. // A local MP_TXBUF available (for local data copying)?
  130. //
  131. if (IsSListEmpty(&Adapter->SendBufList))
  132. {
  133. Adapter->nWaitSend++;
  134. if (bFromQueue)
  135. {
  136. InsertHeadQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR(Packet));
  137. }
  138. else
  139. {
  140. InsertTailQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR(Packet));
  141. }
  142. DBGPRINT(MP_TRACE, ("<-- MpSendPacket - queued, no buf\n"));
  143. return Status;
  144. }
  145. pMpTxBuf = (PMP_TXBUF) PopEntryList(&Adapter->SendBufList);
  146. ASSERT(pMpTxBuf);
  147. //
  148. // Copy the buffers in this packet, enough to give the first buffer as they are linked
  149. //
  150. BytesCopied = MpCopyPacket(pMpTcb->FirstBuffer, pMpTxBuf);
  151. #ifdef NDIS51_MINIPORT
  152. //
  153. // MpCopyPacket may return 0 if system resources are low or exhausted
  154. //
  155. if (BytesCopied == 0)
  156. {
  157. PushEntryList(&Adapter->SendBufList, &pMpTxBuf->SList);
  158. DBGPRINT(MP_ERROR, ("Calling NdisMSendComplete with NDIS_STATUS_RESOURCES, Pkt= "PTR_FORMAT"\n", Packet));
  159. NdisReleaseSpinLock(&Adapter->SendLock);
  160. NdisMSendComplete(
  161. MP_GET_ADAPTER_HANDLE(Adapter),
  162. Packet,
  163. NDIS_STATUS_RESOURCES);
  164. NdisAcquireSpinLock(&Adapter->SendLock);
  165. return NDIS_STATUS_RESOURCES;
  166. }
  167. #endif
  168. pMpTcb->MpTxBuf = pMpTxBuf;
  169. //
  170. // Set up the frag list, only one fragment after it's coalesced
  171. //
  172. pFragList = &FragList;
  173. pFragList->NumberOfElements = 1;
  174. pFragList->Elements[0].Address = pMpTxBuf->BufferPa;
  175. pFragList->Elements[0].Length = (BytesCopied >= NIC_MIN_PACKET_SIZE) ?
  176. BytesCopied : NIC_MIN_PACKET_SIZE;
  177. //
  178. // we can complete it from this routine because it's copied
  179. //
  180. bCompletePacket = TRUE;
  181. pMpTcb->Packet = NULL;
  182. MP_SET_FLAG(pMpTcb, fMP_TCB_USE_LOCAL_BUF);
  183. }
  184. else
  185. {
  186. if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_SCATTER_GATHER))
  187. {
  188. //
  189. // In scatter/gather case, use the frag list pointer saved
  190. // in the packet info field
  191. //
  192. pFragList = (PMP_FRAG_LIST) NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
  193. ScatterGatherListPacketInfo);
  194. }
  195. else
  196. {
  197. //
  198. // In the map register case, use the local frag list structure
  199. //
  200. pFragList = &FragList;
  201. //
  202. // Do the physical mapping to get all the fragment physical addresses
  203. //
  204. MpStartPacketPhysicalMapping(
  205. Adapter,
  206. pMpTcb->FirstBuffer,
  207. pFragList);
  208. }
  209. pMpTcb->Packet = Packet;
  210. }
  211. MP_SET_FLAG(pMpTcb, fMP_TCB_IN_USE);
  212. //
  213. // Call the NIC specific send handler, it only needs to deal with the frag list
  214. //
  215. Status = NICSendPacket(Adapter, pMpTcb, pFragList);
  216. Adapter->nBusySend++;
  217. ASSERT(Adapter->nBusySend <= Adapter->NumTcb);
  218. Adapter->CurrSendTail = Adapter->CurrSendTail->Next;
  219. if (bCompletePacket)
  220. {
  221. DBGPRINT(MP_TRACE, ("Calling NdisMSendComplete, Pkt= "PTR_FORMAT"\n", Packet));
  222. NdisReleaseSpinLock(&Adapter->SendLock);
  223. NdisMSendComplete( MP_GET_ADAPTER_HANDLE(Adapter), Packet, Status);
  224. NdisAcquireSpinLock(&Adapter->SendLock);
  225. }
  226. DBGPRINT(MP_TRACE, ("<-- MpSendPacket\n"));
  227. return Status;
  228. }
  229. ULONG MpCopyPacket(
  230. IN PNDIS_BUFFER CurrBuffer,
  231. IN PMP_TXBUF pMpTxBuf
  232. )
  233. /*++
  234. Routine Description:
  235. Copy the packet data to a local buffer
  236. Either the packet is too small or it has too many fragments
  237. Assumption: Send spinlock has been acquired
  238. Arguments:
  239. CurrBuffer Pointer to the first NDIS_BUFFER
  240. pMpTxBuf Pointer to the local buffer (MP_TXBUF)
  241. Return Value:
  242. Bytes copied
  243. --*/
  244. {
  245. UINT CurrLength;
  246. PUCHAR pSrc;
  247. PUCHAR pDest;
  248. UINT BytesCopied = 0;
  249. DBGPRINT(MP_TRACE, ("--> MpCopyPacket\n"));
  250. pDest = pMpTxBuf->pBuffer;
  251. while (CurrBuffer)
  252. {
  253. #ifdef NDIS51_MINIPORT
  254. NdisQueryBufferSafe( CurrBuffer, &pSrc, &CurrLength, NormalPagePriority );
  255. if (pSrc == NULL)
  256. {
  257. return 0;
  258. }
  259. #else
  260. NdisQueryBuffer( CurrBuffer, &pSrc, &CurrLength );
  261. #endif
  262. if (CurrLength)
  263. {
  264. //
  265. // Copy the data.
  266. //
  267. NdisMoveMemory(pDest, pSrc, CurrLength);
  268. BytesCopied += CurrLength;
  269. pDest += CurrLength;
  270. }
  271. NdisGetNextBuffer( CurrBuffer, &CurrBuffer);
  272. }
  273. //
  274. // Zero out the padding bytes
  275. //
  276. if (BytesCopied < NIC_MIN_PACKET_SIZE)
  277. {
  278. NdisZeroMemory(pDest, NIC_MIN_PACKET_SIZE - BytesCopied);
  279. }
  280. NdisAdjustBufferLength(pMpTxBuf->NdisBuffer, BytesCopied);
  281. NdisFlushBuffer(pMpTxBuf->NdisBuffer, TRUE);
  282. ASSERT(BytesCopied <= pMpTxBuf->BufferSize);
  283. DBGPRINT(MP_TRACE, ("<-- MpCopyPacket\n"));
  284. return BytesCopied;
  285. }
  286. VOID MpStartPacketPhysicalMapping(
  287. IN PMP_ADAPTER Adapter,
  288. IN PNDIS_BUFFER CurrBuffer,
  289. OUT PMP_FRAG_LIST pFragList
  290. )
  291. /*++
  292. Routine Description:
  293. Call NdisMStartBufferPhysicalMapping on each NDIS buffer
  294. Get the physical address for each fragment and save them in the fragment list
  295. We use the same fragment list as the scatter gather so the driver writers only need
  296. to deal one type.
  297. Assumption: spinlock has been acquired
  298. Arguments:
  299. Adapter Pointer to our adapter
  300. CurrBuffer Pointer to the first NDIS_BUFFER
  301. pFragList The pointer to the frag list to be filled
  302. Return Value:
  303. None
  304. --*/
  305. {
  306. NDIS_PHYSICAL_ADDRESS_UNIT PhysAddrUnits[NIC_MAX_PHYS_BUF_COUNT];
  307. UINT ArraySize, i;
  308. ULONG ElementIndex = 0;
  309. DBGPRINT(MP_TRACE, ("--> MpStartPacketPhysicalMapping\n"));
  310. while (CurrBuffer)
  311. {
  312. NdisMStartBufferPhysicalMapping(
  313. Adapter->AdapterHandle,
  314. CurrBuffer,
  315. Adapter->CurrMapRegTail,
  316. TRUE,
  317. PhysAddrUnits,
  318. &ArraySize);
  319. Adapter->CurrMapRegTail++;
  320. if (Adapter->CurrMapRegTail == (ULONG)Adapter->NumTbd)
  321. {
  322. Adapter->CurrMapRegTail = 0;
  323. }
  324. for (i = 0; i < ArraySize; i++)
  325. {
  326. pFragList->Elements[ElementIndex].Address = PhysAddrUnits[i].PhysicalAddress;
  327. pFragList->Elements[ElementIndex].Length = PhysAddrUnits[i].Length;
  328. ElementIndex++;
  329. }
  330. //
  331. // Flush the current buffer because it could be cached
  332. //
  333. NdisFlushBuffer(CurrBuffer, TRUE);
  334. //
  335. // point to the next buffer
  336. //
  337. NdisGetNextBuffer(CurrBuffer, &CurrBuffer);
  338. }
  339. pFragList->NumberOfElements = ElementIndex;
  340. ASSERT(pFragList->NumberOfElements);
  341. DBGPRINT(MP_TRACE, ("<-- MpStartPacketPhysicalMapping\n"));
  342. }
  343. NDIS_STATUS NICSendPacket(
  344. IN PMP_ADAPTER Adapter,
  345. IN PMP_TCB pMpTcb,
  346. IN PMP_FRAG_LIST pFragList
  347. )
  348. /*++
  349. Routine Description:
  350. NIC specific send handler
  351. Assumption: Send spinlock has been acquired
  352. Arguments:
  353. Adapter Pointer to our adapter
  354. pMpTcb Pointer to MP_TCB
  355. pFragList The pointer to the frag list to be filled
  356. Return Value:
  357. NDIS_STATUS_SUCCESS
  358. NDIS_STATUS_HARD_ERRORS
  359. --*/
  360. {
  361. NDIS_STATUS Status;
  362. ULONG index;
  363. UCHAR TbdCount = 0;
  364. PHW_TCB pHwTcb = pMpTcb->HwTcb;
  365. PTBD_STRUC pHwTbd = pMpTcb->HwTbd;
  366. DBGPRINT(MP_TRACE, ("--> NICSendPacket\n"));
  367. for (index = 0; index < pFragList->NumberOfElements; index++)
  368. {
  369. if (pFragList->Elements[index].Length)
  370. {
  371. pHwTbd->TbdBufferAddress = NdisGetPhysicalAddressLow(pFragList->Elements[index].Address);
  372. pHwTbd->TbdCount = pFragList->Elements[index].Length;
  373. pHwTbd++;
  374. TbdCount++;
  375. }
  376. }
  377. pHwTcb->TxCbHeader.CbStatus = 0;
  378. pHwTcb->TxCbHeader.CbCommand = CB_S_BIT | CB_TRANSMIT | CB_TX_SF_BIT;
  379. pHwTcb->TxCbTbdPointer = pMpTcb->HwTbdPhys;
  380. pHwTcb->TxCbTbdNumber = TbdCount;
  381. pHwTcb->TxCbCount = 0;
  382. pHwTcb->TxCbThreshold = (UCHAR) Adapter->AiThreshold;
  383. Status = NICStartSend(Adapter, pMpTcb);
  384. DBGPRINT(MP_TRACE, ("<-- NICSendPacket\n"));
  385. return Status;
  386. }
  387. NDIS_STATUS NICStartSend(
  388. IN PMP_ADAPTER Adapter,
  389. IN PMP_TCB pMpTcb
  390. )
  391. /*++
  392. Routine Description:
  393. Issue a send command to the NIC
  394. Assumption: Send spinlock has been acquired
  395. Arguments:
  396. Adapter Pointer to our adapter
  397. pMpTcb Pointer to MP_TCB
  398. Return Value:
  399. NDIS_STATUS_SUCCESS
  400. NDIS_STATUS_HARD_ERRORS
  401. --*/
  402. {
  403. NDIS_STATUS Status;
  404. DBGPRINT(MP_TRACE, ("--> NICStartSend\n"));
  405. //
  406. // If the transmit unit is idle (very first transmit) then we must
  407. // setup the general pointer and issue a full CU-start
  408. //
  409. if (Adapter->TransmitIdle)
  410. {
  411. DBGPRINT(MP_INFO, ("CU is idle -- First TCB added to Active List\n"));
  412. //
  413. // Wait for the SCB to clear before we set the general pointer
  414. //
  415. if (!WaitScb(Adapter))
  416. {
  417. Status = NDIS_STATUS_HARD_ERRORS;
  418. MP_EXIT;
  419. }
  420. //
  421. // Don't try to start the transmitter if the command unit is not
  422. // idle ((not idle) == (Cu-Suspended or Cu-Active)).
  423. //
  424. if ((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_IDLE)
  425. {
  426. DBGPRINT(MP_ERROR, ("Adapter = "PTR_FORMAT", CU Not IDLE\n", Adapter));
  427. MP_SET_HARDWARE_ERROR(Adapter);
  428. NdisStallExecution(25);
  429. }
  430. Adapter->CSRAddress->ScbGeneralPointer = pMpTcb->HwTcbPhys;
  431. Status = D100IssueScbCommand(Adapter, SCB_CUC_START, FALSE);
  432. Adapter->TransmitIdle = FALSE;
  433. Adapter->ResumeWait = TRUE;
  434. }
  435. else
  436. {
  437. //
  438. // If the command unit has already been started, then append this
  439. // TCB onto the end of the transmit chain, and issue a CU-Resume.
  440. //
  441. DBGPRINT(MP_LOUD, ("adding TCB to Active chain\n"));
  442. //
  443. // Clear the suspend bit on the previous packet.
  444. //
  445. pMpTcb->PrevHwTcb->TxCbHeader.CbCommand &= ~CB_S_BIT;
  446. //
  447. // Issue a CU-Resume command to the device. We only need to do a
  448. // WaitScb if the last command was NOT a RESUME.
  449. //
  450. Status = D100IssueScbCommand(Adapter, SCB_CUC_RESUME, Adapter->ResumeWait);
  451. }
  452. exit:
  453. DBGPRINT(MP_TRACE, ("<-- NICStartSend\n"));
  454. return Status;
  455. }
  456. NDIS_STATUS MpHandleSendInterrupt(
  457. IN PMP_ADAPTER Adapter
  458. )
  459. /*++
  460. Routine Description:
  461. Interrupt handler for sending processing
  462. Re-claim the send resources, complete sends and get more to send from the send wait queue
  463. Assumption: Send spinlock has been acquired
  464. Arguments:
  465. Adapter Pointer to our adapter
  466. Return Value:
  467. NDIS_STATUS_SUCCESS
  468. NDIS_STATUS_HARD_ERRORS
  469. NDIS_STATUS_PENDING
  470. --*/
  471. {
  472. NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
  473. PMP_TCB pMpTcb;
  474. #if DBG
  475. LONG i;
  476. #endif
  477. DBGPRINT(MP_TRACE, ("---> MpHandleSendInterrupt\n"));
  478. //
  479. // Any packets being sent? Any packet waiting in the send queue?
  480. //
  481. if (Adapter->nBusySend == 0 &&
  482. IsQueueEmpty(&Adapter->SendWaitQueue))
  483. {
  484. ASSERT(Adapter->CurrSendHead == Adapter->CurrSendTail);
  485. DBGPRINT(MP_TRACE, ("<--- MpHandleSendInterrupt\n"));
  486. return Status;
  487. }
  488. //
  489. // Check the first TCB on the send list
  490. //
  491. while (Adapter->nBusySend > 0)
  492. {
  493. #if DBG
  494. pMpTcb = Adapter->CurrSendHead;
  495. for (i = 0; i < Adapter->nBusySend; i++)
  496. {
  497. pMpTcb = pMpTcb->Next;
  498. }
  499. if (pMpTcb != Adapter->CurrSendTail)
  500. {
  501. DBGPRINT(MP_ERROR, ("nBusySend= %d\n", Adapter->nBusySend));
  502. DBGPRINT(MP_ERROR, ("CurrSendhead= "PTR_FORMAT"\n", Adapter->CurrSendHead));
  503. DBGPRINT(MP_ERROR, ("CurrSendTail= "PTR_FORMAT"\n", Adapter->CurrSendTail));
  504. ASSERT(FALSE);
  505. }
  506. #endif
  507. pMpTcb = Adapter->CurrSendHead;
  508. //
  509. // Is this TCB completed?
  510. //
  511. if (pMpTcb->HwTcb->TxCbHeader.CbStatus & CB_STATUS_COMPLETE)
  512. {
  513. //
  514. // Check if this is a multicast hw workaround packet
  515. //
  516. if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST)
  517. {
  518. MP_FREE_SEND_PACKET_FUN(Adapter, pMpTcb);
  519. }
  520. else
  521. {
  522. MP_CLEAR_FLAGS(pMpTcb);
  523. pMpTcb->Count = 0;
  524. Adapter->CurrSendHead = Adapter->CurrSendHead->Next;
  525. Adapter->nBusySend--;
  526. #if OFFLOAD
  527. NdisInterlockedDecrement(&Adapter->SharedMemRefCount);
  528. if (Adapter->SharedMemRefCount == 0)
  529. {
  530. // DbgPrint("Clear the flag\n");
  531. MP_CLEAR_FLAG(Adapter, fMP_SHARED_MEM_IN_USE);
  532. }
  533. #endif
  534. ASSERT(Adapter->nBusySend >= 0);
  535. }
  536. }
  537. else
  538. {
  539. break;
  540. }
  541. }
  542. //
  543. // If we queued any transmits because we didn't have any TCBs earlier,
  544. // dequeue and send those packets now, as long as we have free TCBs.
  545. //
  546. if (MP_IS_READY(Adapter))
  547. {
  548. while (!IsQueueEmpty(&Adapter->SendWaitQueue) &&
  549. MP_TCB_RESOURCES_AVAIABLE(Adapter))
  550. {
  551. PNDIS_PACKET Packet;
  552. PQUEUE_ENTRY pEntry;
  553. #if OFFLOAD
  554. if (MP_TEST_FLAG(Adapter, fMP_SHARED_MEM_IN_USE))
  555. {
  556. break;
  557. }
  558. #endif
  559. pEntry = RemoveHeadQueue(&Adapter->SendWaitQueue);
  560. ASSERT(pEntry);
  561. Adapter->nWaitSend--;
  562. Packet = CONTAINING_RECORD(pEntry, NDIS_PACKET, MiniportReserved);
  563. DBGPRINT(MP_INFO, ("MpHandleSendInterrupt - send a queued packet\n"));
  564. Status = MpSendPacketFun(Adapter, Packet, TRUE);
  565. if (Status != NDIS_STATUS_SUCCESS)
  566. {
  567. break;
  568. }
  569. }
  570. }
  571. DBGPRINT(MP_TRACE, ("<--- MpHandleSendInterrupt\n"));
  572. return Status;
  573. }
  574. VOID MpHandleRecvInterrupt(
  575. IN PMP_ADAPTER Adapter
  576. )
  577. /*++
  578. Routine Description:
  579. Interrupt handler for receive processing
  580. Put the received packets into an array and call NdisMIndicateReceivePacket
  581. If we run low on RFDs, allocate another one
  582. Assumption: Rcv spinlock has been acquired
  583. Arguments:
  584. Adapter Pointer to our adapter
  585. Return Value:
  586. None
  587. --*/
  588. {
  589. PMP_RFD pMpRfd;
  590. PHW_RFD pHwRfd;
  591. PNDIS_PACKET PacketArray[NIC_DEF_RFDS];
  592. PNDIS_PACKET PacketFreeArray[NIC_DEF_RFDS];
  593. UINT PacketArrayCount;
  594. UINT PacketFreeCount;
  595. UINT Index;
  596. UINT LoopIndex = 0;
  597. UINT LoopCount = NIC_MAX_RFDS / NIC_DEF_RFDS + 1; // avoid staying here too long
  598. BOOLEAN bContinue = TRUE;
  599. BOOLEAN bAllocNewRfd = FALSE;
  600. USHORT PacketStatus;
  601. #if OFFLOAD
  602. UINT i;
  603. #endif
  604. DBGPRINT(MP_TRACE, ("---> MpHandleRecvInterrupt\n"));
  605. ASSERT(Adapter->nReadyRecv >= NIC_MIN_RFDS);
  606. while (LoopIndex++ < LoopCount && bContinue)
  607. {
  608. PacketArrayCount = 0;
  609. PacketFreeCount = 0;
  610. //
  611. // Process up to the array size RFD's
  612. //
  613. while (PacketArrayCount < NIC_DEF_RFDS)
  614. {
  615. if (IsListEmpty(&Adapter->RecvList))
  616. {
  617. ASSERT(Adapter->nReadyRecv == 0);
  618. bContinue = FALSE;
  619. break;
  620. }
  621. //
  622. // Get the next MP_RFD to process
  623. //
  624. pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
  625. //
  626. // Get the associated HW_RFD
  627. //
  628. pHwRfd = pMpRfd->HwRfd;
  629. //
  630. // Is this packet completed?
  631. //
  632. PacketStatus = NIC_RFD_GET_STATUS(pHwRfd);
  633. if (!NIC_RFD_STATUS_COMPLETED(PacketStatus))
  634. {
  635. bContinue = FALSE;
  636. break;
  637. }
  638. //
  639. // HW specific - check if actual count field has been updated
  640. //
  641. if (!NIC_RFD_VALID_ACTUALCOUNT(pHwRfd))
  642. {
  643. bContinue = FALSE;
  644. break;
  645. }
  646. //
  647. // Remove the RFD from the head of the List
  648. //
  649. RemoveEntryList((PLIST_ENTRY)pMpRfd);
  650. Adapter->nReadyRecv--;
  651. ASSERT(Adapter->nReadyRecv >= 0);
  652. ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_READY));
  653. MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_READY);
  654. //
  655. // A good packet? drop it if not.
  656. //
  657. if (!NIC_RFD_STATUS_SUCCESS(PacketStatus))
  658. {
  659. DBGPRINT(MP_WARN, ("Receive failure = %x\n", PacketStatus));
  660. NICReturnRFD(Adapter, pMpRfd);
  661. continue;
  662. }
  663. //
  664. // Do not receive any packets until a filter has been set
  665. //
  666. if (!Adapter->PacketFilter)
  667. {
  668. NICReturnRFD(Adapter, pMpRfd);
  669. continue;
  670. }
  671. //
  672. // Do not receive any packets until we are at D0
  673. //
  674. if (Adapter->CurrentPowerState != NdisDeviceStateD0)
  675. {
  676. NICReturnRFD(Adapter, pMpRfd);
  677. continue;
  678. }
  679. //
  680. // Get the packet size
  681. //
  682. pMpRfd->PacketSize = NIC_RFD_GET_PACKET_SIZE(pHwRfd);
  683. NdisAdjustBufferLength(pMpRfd->NdisBuffer, pMpRfd->PacketSize);
  684. NdisFlushBuffer(pMpRfd->NdisBuffer, FALSE);
  685. // we don't mess up the buffer chain, no need to make this call in this case
  686. // NdisRecalculatePacketCounts(pMpRfd->ReceivePacket);
  687. //
  688. // set the status on the packet, either resources or success
  689. //
  690. if (Adapter->nReadyRecv >= MIN_NUM_RFD)
  691. {
  692. // NDIS_STATUS_SUCCESS
  693. NDIS_SET_PACKET_STATUS(pMpRfd->NdisPacket, NDIS_STATUS_SUCCESS);
  694. MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_PEND);
  695. InsertTailList(&Adapter->RecvPendList, (PLIST_ENTRY)pMpRfd);
  696. MP_INC_RCV_REF(Adapter);
  697. }
  698. else
  699. {
  700. //
  701. // NDIS_STATUS_RESOURCES
  702. //
  703. NDIS_SET_PACKET_STATUS(pMpRfd->NdisPacket, NDIS_STATUS_RESOURCES);
  704. MP_SET_FLAG(pMpRfd, fMP_RFD_RESOURCES);
  705. PacketFreeArray[PacketFreeCount] = pMpRfd->NdisPacket;
  706. PacketFreeCount++;
  707. //
  708. // Reset the RFD shrink count - don't attempt to shrink RFD
  709. //
  710. Adapter->RfdShrinkCount = 0;
  711. //
  712. // Remember to allocate a new RFD later
  713. //
  714. bAllocNewRfd = TRUE;
  715. }
  716. PacketArray[PacketArrayCount] = pMpRfd->NdisPacket;
  717. PacketArrayCount++;
  718. }
  719. //
  720. // if we didn't process any receives, just return from here
  721. //
  722. if (PacketArrayCount == 0) break;
  723. //
  724. // Update the number of outstanding Recvs
  725. //
  726. Adapter->PoMgmt.OutstandingRecv += PacketArrayCount;
  727. NdisDprReleaseSpinLock(&Adapter->RcvLock);
  728. NdisMIndicateReceivePacket(
  729. Adapter->AdapterHandle,
  730. PacketArray,
  731. PacketArrayCount);
  732. NdisDprAcquireSpinLock(&Adapter->RcvLock);
  733. //
  734. // NDIS won't take ownership for the packets with NDIS_STATUS_RESOURCES.
  735. // For other packets, NDIS always takes the ownership and gives them back
  736. // by calling MPReturnPackets
  737. //
  738. for (Index = 0; Index < PacketFreeCount; Index++)
  739. {
  740. //
  741. // Get the MP_RFD saved in this packet, in NICAllocRfd
  742. //
  743. pMpRfd = MP_GET_PACKET_RFD(PacketFreeArray[Index]);
  744. ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RESOURCES));
  745. MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RESOURCES);
  746. //
  747. // Decrement the number of outstanding Recvs
  748. //
  749. Adapter->PoMgmt.OutstandingRecv --;
  750. NICReturnRFD(Adapter, pMpRfd);
  751. }
  752. }
  753. //
  754. // If we ran low on RFD's, we need to allocate a new RFD
  755. //
  756. if (bAllocNewRfd)
  757. {
  758. //
  759. // Allocate one more RFD only if no pending new RFD allocation AND
  760. // it doesn't exceed the max RFD limit
  761. //
  762. if (!Adapter->bAllocNewRfd && Adapter->CurrNumRfd < Adapter->MaxNumRfd)
  763. {
  764. PMP_RFD TempMpRfd;
  765. NDIS_STATUS TempStatus;
  766. TempMpRfd = NdisAllocateFromNPagedLookasideList(&Adapter->RecvLookaside);
  767. if (TempMpRfd)
  768. {
  769. MP_INC_REF(Adapter);
  770. Adapter->bAllocNewRfd = TRUE;
  771. MP_SET_FLAG(TempMpRfd, fMP_RFD_ALLOC_PEND);
  772. //
  773. // Allocate the shared memory for this RFD.
  774. //
  775. TempStatus = NdisMAllocateSharedMemoryAsync(
  776. Adapter->AdapterHandle,
  777. Adapter->HwRfdSize,
  778. FALSE,
  779. TempMpRfd);
  780. //
  781. // The return value will be either NDIS_STATUS_PENDING or NDIS_STATUS_FAILURE
  782. //
  783. if (TempStatus == NDIS_STATUS_FAILURE)
  784. {
  785. MP_CLEAR_FLAGS(TempMpRfd);
  786. NdisFreeToNPagedLookasideList(&Adapter->RecvLookaside, TempMpRfd);
  787. Adapter->bAllocNewRfd = FALSE;
  788. MP_DEC_REF(Adapter);
  789. }
  790. }
  791. }
  792. }
  793. ASSERT(Adapter->nReadyRecv >= NIC_MIN_RFDS);
  794. DBGPRINT(MP_TRACE, ("<--- MpHandleRecvInterrupt\n"));
  795. }
  796. VOID NICReturnRFD(
  797. IN PMP_ADAPTER Adapter,
  798. IN PMP_RFD pMpRfd
  799. )
  800. /*++
  801. Routine Description:
  802. Recycle a RFD and put it back onto the receive list
  803. Assumption: Rcv spinlock has been acquired
  804. Arguments:
  805. Adapter Pointer to our adapter
  806. pMpRfd Pointer to the RFD
  807. Return Value:
  808. None
  809. --*/
  810. {
  811. PMP_RFD pLastMpRfd;
  812. PHW_RFD pHwRfd = pMpRfd->HwRfd;
  813. ASSERT(pMpRfd->Flags == 0);
  814. MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_READY);
  815. //
  816. // HW_SPECIFIC_START
  817. //
  818. pHwRfd->RfdCbHeader.CbStatus = 0;
  819. pHwRfd->RfdActualCount = 0;
  820. pHwRfd->RfdCbHeader.CbCommand = (RFD_EL_BIT);
  821. pHwRfd->RfdCbHeader.CbLinkPointer = DRIVER_NULL;
  822. //
  823. // We don't use any of the OOB data besides status
  824. // Otherwise, we need to clean up OOB data
  825. // NdisZeroMemory(NDIS_OOB_DATA_FROM_PACKET(pMpRfd->NdisPacket),14);
  826. //
  827. // Append this RFD to the RFD chain
  828. if (!IsListEmpty(&Adapter->RecvList))
  829. {
  830. pLastMpRfd = (PMP_RFD)GetListTailEntry(&Adapter->RecvList);
  831. // Link it onto the end of the chain dynamically
  832. pHwRfd = pLastMpRfd->HwRfd;
  833. pHwRfd->RfdCbHeader.CbLinkPointer = pMpRfd->HwRfdPhys;
  834. pHwRfd->RfdCbHeader.CbCommand = 0;
  835. }
  836. //
  837. // HW_SPECIFIC_END
  838. //
  839. //
  840. // The processing on this RFD is done, so put it back on the tail of
  841. // our list
  842. //
  843. InsertTailList(&Adapter->RecvList, (PLIST_ENTRY)pMpRfd);
  844. Adapter->nReadyRecv++;
  845. ASSERT(Adapter->nReadyRecv <= Adapter->CurrNumRfd);
  846. }
  847. NDIS_STATUS NICStartRecv(
  848. IN PMP_ADAPTER Adapter
  849. )
  850. /*++
  851. Routine Description:
  852. Start the receive unit if it's not in a ready state
  853. Assumption: Rcv spinlock has been acquired
  854. Arguments:
  855. Adapter Pointer to our adapter
  856. Return Value:
  857. NDIS_STATUS_SUCCESS
  858. NDIS_STATUS_HARD_ERRROS
  859. --*/
  860. {
  861. PMP_RFD pMpRfd;
  862. NDIS_STATUS Status;
  863. DBGPRINT(MP_TRACE, ("---> NICStartRecv\n"));
  864. //
  865. // If the receiver is ready, then don't try to restart.
  866. //
  867. if (NIC_IS_RECV_READY(Adapter))
  868. {
  869. DBGPRINT(MP_LOUD, ("Receive unit already active\n"));
  870. return NDIS_STATUS_SUCCESS;
  871. }
  872. DBGPRINT(MP_LOUD, ("Re-start receive unit...\n"));
  873. ASSERT(!IsListEmpty(&Adapter->RecvList));
  874. //
  875. // Get the MP_RFD head
  876. //
  877. pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
  878. //
  879. // If more packets are received, clean up RFD chain again
  880. //
  881. if (NIC_RFD_GET_STATUS(pMpRfd->HwRfd))
  882. {
  883. MpHandleRecvInterrupt(Adapter);
  884. ASSERT(!IsListEmpty(&Adapter->RecvList));
  885. //
  886. // Get the new MP_RFD head
  887. //
  888. pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
  889. }
  890. //
  891. // Wait for the SCB to clear before we set the general pointer
  892. //
  893. if (!WaitScb(Adapter))
  894. {
  895. Status = NDIS_STATUS_HARD_ERRORS;
  896. MP_EXIT;
  897. }
  898. if (Adapter->CurrentPowerState > NdisDeviceStateD0)
  899. {
  900. Status = NDIS_STATUS_HARD_ERRORS;
  901. MP_EXIT;
  902. }
  903. //
  904. // Set the SCB General Pointer to point the current Rfd
  905. //
  906. Adapter->CSRAddress->ScbGeneralPointer = pMpRfd->HwRfdPhys;
  907. //
  908. // Issue the SCB RU start command
  909. //
  910. Status = D100IssueScbCommand(Adapter, SCB_RUC_START, FALSE);
  911. if (Status == NDIS_STATUS_SUCCESS)
  912. {
  913. // wait for the command to be accepted
  914. if (!WaitScb(Adapter))
  915. {
  916. Status = NDIS_STATUS_HARD_ERRORS;
  917. }
  918. }
  919. exit:
  920. DBGPRINT_S(Status, ("<--- NICStartRecv, Status=%x\n", Status));
  921. return Status;
  922. }
  923. VOID MpFreeQueuedSendPackets(
  924. IN PMP_ADAPTER Adapter
  925. )
  926. /*++
  927. Routine Description:
  928. Free and complete the pended sends on SendWaitQueue
  929. Assumption: spinlock has been acquired
  930. Arguments:
  931. Adapter Pointer to our adapter
  932. Return Value:
  933. None
  934. --*/
  935. {
  936. PQUEUE_ENTRY pEntry;
  937. PNDIS_PACKET Packet;
  938. NDIS_STATUS Status = MP_GET_STATUS_FROM_FLAGS(Adapter);
  939. DBGPRINT(MP_TRACE, ("--> MpFreeQueuedSendPackets\n"));
  940. while (!IsQueueEmpty(&Adapter->SendWaitQueue))
  941. {
  942. pEntry = RemoveHeadQueue(&Adapter->SendWaitQueue);
  943. Adapter->nWaitSend--;
  944. NdisReleaseSpinLock(&Adapter->SendLock);
  945. ASSERT(pEntry);
  946. Packet = CONTAINING_RECORD(pEntry, NDIS_PACKET, MiniportReserved);
  947. NdisMSendComplete(
  948. MP_GET_ADAPTER_HANDLE(Adapter),
  949. Packet,
  950. Status);
  951. NdisAcquireSpinLock(&Adapter->SendLock);
  952. }
  953. DBGPRINT(MP_TRACE, ("<-- MpFreeQueuedSendPackets\n"));
  954. }
  955. void MpFreeBusySendPackets(
  956. IN PMP_ADAPTER Adapter
  957. )
  958. /*++
  959. Routine Description:
  960. Free and complete the stopped active sends
  961. Assumption: Send spinlock has been acquired
  962. Arguments:
  963. Adapter Pointer to our adapter
  964. Return Value:
  965. None
  966. --*/
  967. {
  968. PMP_TCB pMpTcb;
  969. DBGPRINT(MP_TRACE, ("--> MpFreeBusySendPackets\n"));
  970. //
  971. // Any packets being sent? Check the first TCB on the send list
  972. //
  973. while (Adapter->nBusySend > 0)
  974. {
  975. pMpTcb = Adapter->CurrSendHead;
  976. //
  977. // Is this TCB completed?
  978. //
  979. if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST)
  980. {
  981. MP_FREE_SEND_PACKET_FUN(Adapter, pMpTcb);
  982. }
  983. else
  984. {
  985. break;
  986. }
  987. }
  988. DBGPRINT(MP_TRACE, ("<-- MpFreeBusySendPackets\n"));
  989. }
  990. VOID NICResetRecv(
  991. IN PMP_ADAPTER Adapter
  992. )
  993. /*++
  994. Routine Description:
  995. Reset the receive list
  996. Assumption: Rcv spinlock has been acquired
  997. Arguments:
  998. Adapter Pointer to our adapter
  999. Return Value:
  1000. None
  1001. --*/
  1002. {
  1003. PMP_RFD pMpRfd;
  1004. PHW_RFD pHwRfd;
  1005. LONG RfdCount;
  1006. DBGPRINT(MP_TRACE, ("--> NICResetRecv\n"));
  1007. ASSERT(!IsListEmpty(&Adapter->RecvList));
  1008. //
  1009. // Get the MP_RFD head
  1010. //
  1011. pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
  1012. for (RfdCount = 0; RfdCount < Adapter->nReadyRecv; RfdCount++)
  1013. {
  1014. pHwRfd = pMpRfd->HwRfd;
  1015. pHwRfd->RfdCbHeader.CbStatus = 0;
  1016. pMpRfd = (PMP_RFD)GetListFLink(&pMpRfd->List);
  1017. }
  1018. DBGPRINT(MP_TRACE, ("<-- NICResetRecv\n"));
  1019. }
  1020. VOID MpLinkDetectionDpc(
  1021. IN PVOID SystemSpecific1,
  1022. IN PVOID FunctionContext,
  1023. IN PVOID SystemSpecific2,
  1024. IN PVOID SystemSpecific3
  1025. )
  1026. /*++
  1027. Routine Description:
  1028. Timer function for postponed link negotiation
  1029. Arguments:
  1030. SystemSpecific1 Not used
  1031. FunctionContext Pointer to our adapter
  1032. SystemSpecific2 Not used
  1033. SystemSpecific3 Not used
  1034. Return Value:
  1035. None
  1036. --*/
  1037. {
  1038. PMP_ADAPTER Adapter = (PMP_ADAPTER)FunctionContext;
  1039. NDIS_STATUS Status;
  1040. //
  1041. // Handle the link negotiation.
  1042. //
  1043. if (Adapter->bLinkDetectionWait)
  1044. {
  1045. Status = ScanAndSetupPhy(Adapter);
  1046. }
  1047. else
  1048. {
  1049. Status = PhyDetect(Adapter);
  1050. }
  1051. if (Status == NDIS_STATUS_PENDING)
  1052. {
  1053. // Wait for 100 ms
  1054. Adapter->bLinkDetectionWait = TRUE;
  1055. NdisMSetTimer(&Adapter->LinkDetectionTimer, NIC_LINK_DETECTION_DELAY);
  1056. return;
  1057. }
  1058. //
  1059. // Reset some variables for link detection
  1060. //
  1061. Adapter->bLinkDetectionWait = FALSE;
  1062. DBGPRINT(MP_WARN, ("MpLinkDetectionDpc - negotiation done\n"));
  1063. NdisDprAcquireSpinLock(&Adapter->Lock);
  1064. MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_LINK_DETECTION);
  1065. NdisDprReleaseSpinLock(&Adapter->Lock);
  1066. //
  1067. // Any OID query request?
  1068. //
  1069. if (Adapter->bQueryPending)
  1070. {
  1071. switch(Adapter->QueryRequest.Oid)
  1072. {
  1073. case OID_GEN_LINK_SPEED:
  1074. *((PULONG) Adapter->QueryRequest.InformationBuffer) = Adapter->usLinkSpeed * 10000;
  1075. *((PULONG) Adapter->QueryRequest.BytesWritten) = sizeof(ULONG);
  1076. break;
  1077. case OID_GEN_MEDIA_CONNECT_STATUS:
  1078. default:
  1079. ASSERT(Adapter->QueryRequest.Oid == OID_GEN_MEDIA_CONNECT_STATUS);
  1080. *((PULONG) Adapter->QueryRequest.InformationBuffer) = NICGetMediaState(Adapter);
  1081. *((PULONG) Adapter->QueryRequest.BytesWritten) = sizeof(ULONG);
  1082. }
  1083. Adapter->bQueryPending = FALSE;
  1084. NdisMQueryInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS);
  1085. }
  1086. //
  1087. // Any OID set request?
  1088. //
  1089. if (Adapter->bSetPending)
  1090. {
  1091. ULONG PacketFilter;
  1092. ASSERT(Adapter->SetRequest.Oid == OID_GEN_CURRENT_PACKET_FILTER);
  1093. PacketFilter = *((PULONG)Adapter->SetRequest.InformationBuffer);
  1094. NdisDprAcquireSpinLock(&Adapter->Lock);
  1095. Status = NICSetPacketFilter(
  1096. Adapter,
  1097. PacketFilter);
  1098. NdisDprReleaseSpinLock(&Adapter->Lock);
  1099. if (Status == NDIS_STATUS_SUCCESS)
  1100. {
  1101. Adapter->PacketFilter = PacketFilter;
  1102. }
  1103. Adapter->bSetPending = FALSE;
  1104. NdisMSetInformationComplete(Adapter->AdapterHandle, Status);
  1105. }
  1106. NdisDprAcquireSpinLock(&Adapter->Lock);
  1107. //
  1108. // Any pendingf reset?
  1109. //
  1110. if (Adapter->bResetPending)
  1111. {
  1112. // The link detection may have held some requests and caused reset.
  1113. // Complete the reset with NOT_READY status
  1114. Adapter->bResetPending = FALSE;
  1115. MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_RESET_IN_PROGRESS);
  1116. NdisDprReleaseSpinLock(&Adapter->Lock);
  1117. NdisMResetComplete(
  1118. Adapter->AdapterHandle,
  1119. NDIS_STATUS_ADAPTER_NOT_READY,
  1120. FALSE);
  1121. }
  1122. else
  1123. {
  1124. NdisDprReleaseSpinLock(&Adapter->Lock);
  1125. }
  1126. NdisDprAcquireSpinLock(&Adapter->RcvLock);
  1127. //
  1128. // Start the NIC receive unit
  1129. //
  1130. Status = NICStartRecv(Adapter);
  1131. if (Status != NDIS_STATUS_SUCCESS)
  1132. {
  1133. MP_SET_HARDWARE_ERROR(Adapter);
  1134. }
  1135. NdisDprReleaseSpinLock(&Adapter->RcvLock);
  1136. NdisDprAcquireSpinLock(&Adapter->SendLock);
  1137. //
  1138. // Send packets which have been queued while link detection was going on.
  1139. //
  1140. if (MP_IS_READY(Adapter))
  1141. {
  1142. while (!IsQueueEmpty(&Adapter->SendWaitQueue) &&
  1143. Adapter->nBusySend < Adapter->NumTcb)
  1144. {
  1145. PNDIS_PACKET Packet;
  1146. PQUEUE_ENTRY pEntry = RemoveHeadQueue(&Adapter->SendWaitQueue);
  1147. ASSERT(pEntry);
  1148. Adapter->nWaitSend--;
  1149. Packet = CONTAINING_RECORD(pEntry, NDIS_PACKET, MiniportReserved);
  1150. DBGPRINT(MP_INFO, ("MpLinkDetectionDpc - send a queued packet\n"));
  1151. Status = MpSendPacket(Adapter, Packet, TRUE);
  1152. if (Status != NDIS_STATUS_SUCCESS)
  1153. {
  1154. break;
  1155. }
  1156. }
  1157. }
  1158. MP_DEC_REF(Adapter);
  1159. if (MP_GET_REF(Adapter) == 0)
  1160. {
  1161. NdisSetEvent(&Adapter->ExitEvent);
  1162. }
  1163. NdisReleaseSpinLock(&Adapter->SendLock);
  1164. }