?? mp_nic.c
字號:
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
mp_nic.c
Abstract:
This module contains miniport send/receive routines
Revision History:
Who When What
-------- -------- ----------------------------------------------
DChen 11-01-99 created
Notes:
--*/
#include "precomp.h"
#if DBG
#define _FILENUMBER 'CINM'
#endif
__inline VOID MP_FREE_SEND_PACKET(
IN PMP_ADAPTER Adapter,
IN PMP_TCB pMpTcb
)
/*++
Routine Description:
Recycle a MP_TCB and complete the packet if necessary
Assumption: Send spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
pMpTcb Pointer to MP_TCB
Return Value:
None
--*/
{
PNDIS_PACKET Packet;
PNDIS_BUFFER CurrBuffer;
ASSERT(MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE));
Packet = pMpTcb->Packet;
pMpTcb->Packet = NULL;
pMpTcb->Count = 0;
if (pMpTcb->MpTxBuf)
{
ASSERT(MP_TEST_FLAG(pMpTcb, fMP_TCB_USE_LOCAL_BUF));
PushEntryList(&Adapter->SendBufList, &pMpTcb->MpTxBuf->SList);
pMpTcb->MpTxBuf = NULL;
}
MP_CLEAR_FLAGS(pMpTcb);
Adapter->CurrSendHead = Adapter->CurrSendHead->Next;
Adapter->nBusySend--;
ASSERT(Adapter->nBusySend >= 0);
if (Packet)
{
NdisReleaseSpinLock(&Adapter->SendLock);
DBGPRINT(MP_TRACE, ("Calling NdisMSendComplete, Pkt= "PTR_FORMAT"\n", Packet));
NdisMSendComplete(
MP_GET_ADAPTER_HANDLE(Adapter),
Packet,
NDIS_STATUS_SUCCESS);
NdisAcquireSpinLock(&Adapter->SendLock);
}
}
NDIS_STATUS MpSendPacket(
IN PMP_ADAPTER Adapter,
IN PNDIS_PACKET Packet,
IN BOOLEAN bFromQueue
)
/*++
Routine Description:
Do the work to send a packet
Assumption: Send spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
Packet The packet
bFromQueue TRUE if it's taken from the send wait queue
Return Value:
NDIS_STATUS_SUCCESS
NDIS_STATUS_PENDING Put into the send wait queue
NDIS_STATUS_HARD_ERRORS
--*/
{
NDIS_STATUS Status = NDIS_STATUS_PENDING;
PMP_TCB pMpTcb = NULL;
PMP_TXBUF pMpTxBuf = NULL;
ULONG BytesCopied;
// Mimiced frag list if the packet is too small or too fragmented.
MP_FRAG_LIST FragList;
// Pointer to either the scatter gather or the local mimiced frag list
PMP_FRAG_LIST pFragList;
DBGPRINT(MP_TRACE, ("--> MpSendPacket, Pkt= "PTR_FORMAT"\n", Packet));
pMpTcb = Adapter->CurrSendTail;
ASSERT(!MP_TEST_FLAG(pMpTcb, fMP_TCB_IN_USE));
NdisQueryPacket(
Packet,
(PUINT)&pMpTcb->PhysBufCount,
(PUINT)&pMpTcb->BufferCount,
&pMpTcb->FirstBuffer,
(PUINT)&pMpTcb->PacketLength);
ASSERT(pMpTcb->PhysBufCount);
ASSERT(pMpTcb->FirstBuffer);
ASSERT(pMpTcb->PacketLength);
//
// Check to see if we need to coalesce
//
if (pMpTcb->PacketLength < NIC_MIN_PACKET_SIZE ||
pMpTcb->PhysBufCount > NIC_MAX_PHYS_BUF_COUNT)
{
//
// A local MP_TXBUF available (for local data copying)?
//
if (IsSListEmpty(&Adapter->SendBufList))
{
Adapter->nWaitSend++;
if (bFromQueue)
{
InsertHeadQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR(Packet));
}
else
{
InsertTailQueue(&Adapter->SendWaitQueue, MP_GET_PACKET_MR(Packet));
}
DBGPRINT(MP_TRACE, ("<-- MpSendPacket - queued, no buf\n"));
return Status;
}
pMpTxBuf = (PMP_TXBUF) PopEntryList(&Adapter->SendBufList);
ASSERT(pMpTxBuf);
//
// Copy the buffers in this packet, enough to give the first buffer as they are linked
//
BytesCopied = MpCopyPacket(pMpTcb->FirstBuffer, pMpTxBuf);
//
// MpCopyPacket may return 0 if system resources are low or exhausted
//
if (BytesCopied == 0)
{
PushEntryList(&Adapter->SendBufList, &pMpTxBuf->SList);
DBGPRINT(MP_ERROR, ("Calling NdisMSendComplete with NDIS_STATUS_RESOURCES, Pkt= "PTR_FORMAT"\n", Packet));
NdisReleaseSpinLock(&Adapter->SendLock);
NdisMSendComplete(
MP_GET_ADAPTER_HANDLE(Adapter),
Packet,
NDIS_STATUS_RESOURCES);
NdisAcquireSpinLock(&Adapter->SendLock);
return NDIS_STATUS_RESOURCES;
}
pMpTcb->MpTxBuf = pMpTxBuf;
//
// Set up the frag list, only one fragment after it's coalesced
//
pFragList = &FragList;
pFragList->NumberOfElements = 1;
pFragList->Elements[0].Address = pMpTxBuf->BufferPa;
pFragList->Elements[0].Length = (BytesCopied >= NIC_MIN_PACKET_SIZE) ?
BytesCopied : NIC_MIN_PACKET_SIZE;
MP_SET_FLAG(pMpTcb, fMP_TCB_USE_LOCAL_BUF);
//
// Even the driver uses its local buffer, it has to wait the send complete interrupt to
// complete the packet. Otherwise, the driver may run into the following situation:
// before send complete interrupt happens, its halt handler is called and the halt handler
// deregisters the interrupt, so no send complete interrupt can happen, and the send
// complete interrupt handle routine will never be called to free some resources used
// by this send.
}
else
{
ASSERT(MP_TEST_FLAG(Adapter, fMP_ADAPTER_SCATTER_GATHER));
//
// In scatter/gather case, use the frag list pointer saved
// in the packet info field
//
pFragList = (PMP_FRAG_LIST) NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
ScatterGatherListPacketInfo);
}
pMpTcb->Packet = Packet;
MP_SET_FLAG(pMpTcb, fMP_TCB_IN_USE);
//
// Call the NIC specific send handler, it only needs to deal with the frag list
//
Status = NICSendPacket(Adapter, pMpTcb, pFragList);
Adapter->nBusySend++;
ASSERT(Adapter->nBusySend <= Adapter->NumTcb);
Adapter->CurrSendTail = Adapter->CurrSendTail->Next;
DBGPRINT(MP_TRACE, ("<-- MpSendPacket\n"));
return Status;
}
ULONG MpCopyPacket(
IN PNDIS_BUFFER CurrBuffer,
IN PMP_TXBUF pMpTxBuf
)
/*++
Routine Description:
Copy the packet data to a local buffer
Either the packet is too small or it has too many fragments
Assumption: Send spinlock has been acquired
Arguments:
CurrBuffer Pointer to the first NDIS_BUFFER
pMpTxBuf Pointer to the local buffer (MP_TXBUF)
Return Value:
Bytes copied
--*/
{
UINT CurrLength;
PUCHAR pSrc;
PUCHAR pDest;
UINT BytesCopied = 0;
DBGPRINT(MP_TRACE, ("--> MpCopyPacket\n"));
pDest = pMpTxBuf->pBuffer;
while ((CurrBuffer) && (BytesCopied < pMpTxBuf->BufferSize))
{
//
// Support for the following API with NormalPagePrioirty was added for
// NDIS 5.0 and 5.1 miniports in Windows XP
//
#if !BUILD_W2K
NdisQueryBufferSafe( CurrBuffer, &pSrc, &CurrLength, NormalPagePriority );
#else
NdisQueryBuffer( CurrBuffer, &pSrc, &CurrLength);
#endif
if (pSrc == NULL)
{
return 0;
}
if (pMpTxBuf->BufferSize - BytesCopied < CurrLength)
{
CurrLength = pMpTxBuf->BufferSize - BytesCopied;
}
if (CurrLength)
{
//
// Copy the data.
//
NdisMoveMemory(pDest, pSrc, CurrLength);
BytesCopied += CurrLength;
pDest += CurrLength;
}
NdisGetNextBuffer( CurrBuffer, &CurrBuffer);
}
//
// Zero out the padding bytes
//
if (BytesCopied < NIC_MIN_PACKET_SIZE)
{
NdisZeroMemory(pDest, NIC_MIN_PACKET_SIZE - BytesCopied);
}
NdisAdjustBufferLength(pMpTxBuf->NdisBuffer, BytesCopied);
NdisFlushBuffer(pMpTxBuf->NdisBuffer, TRUE);
ASSERT(BytesCopied <= pMpTxBuf->BufferSize);
DBGPRINT(MP_TRACE, ("<-- MpCopyPacket\n"));
return BytesCopied;
}
NDIS_STATUS NICSendPacket(
IN PMP_ADAPTER Adapter,
IN PMP_TCB pMpTcb,
IN PMP_FRAG_LIST pFragList
)
/*++
Routine Description:
NIC specific send handler
Assumption: Send spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
pMpTcb Pointer to MP_TCB
pFragList The pointer to the frag list to be filled
Return Value:
NDIS_STATUS_SUCCESS
NDIS_STATUS_HARD_ERRORS
--*/
{
NDIS_STATUS Status;
ULONG index;
UCHAR TbdCount = 0;
PHW_TCB pHwTcb = pMpTcb->HwTcb;
PTBD_STRUC pHwTbd = pMpTcb->HwTbd;
DBGPRINT(MP_TRACE, ("--> NICSendPacket\n"));
for (index = 0; index < pFragList->NumberOfElements; index++)
{
if (pFragList->Elements[index].Length)
{
pHwTbd->TbdBufferAddress = NdisGetPhysicalAddressLow(pFragList->Elements[index].Address);
pHwTbd->TbdCount = pFragList->Elements[index].Length;
pHwTbd++;
TbdCount++;
}
}
pHwTcb->TxCbHeader.CbStatus = 0;
pHwTcb->TxCbHeader.CbCommand = CB_S_BIT | CB_TRANSMIT | CB_TX_SF_BIT;
pHwTcb->TxCbTbdPointer = pMpTcb->HwTbdPhys;
pHwTcb->TxCbTbdNumber = TbdCount;
pHwTcb->TxCbCount = 0;
pHwTcb->TxCbThreshold = (UCHAR) Adapter->AiThreshold;
Status = NICStartSend(Adapter, pMpTcb);
DBGPRINT(MP_TRACE, ("<-- NICSendPacket\n"));
return Status;
}
NDIS_STATUS NICStartSend(
IN PMP_ADAPTER Adapter,
IN PMP_TCB pMpTcb
)
/*++
Routine Description:
Issue a send command to the NIC
Assumption: Send spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
pMpTcb Pointer to MP_TCB
Return Value:
NDIS_STATUS_SUCCESS
NDIS_STATUS_HARD_ERRORS
--*/
{
NDIS_STATUS Status;
DBGPRINT(MP_TRACE, ("--> NICStartSend\n"));
//
// If the transmit unit is idle (very first transmit) then we must
// setup the general pointer and issue a full CU-start
//
if (Adapter->TransmitIdle)
{
DBGPRINT(MP_INFO, ("CU is idle -- First TCB added to Active List\n"));
//
// Wait for the SCB to clear before we set the general pointer
//
if (!WaitScb(Adapter))
{
Status = NDIS_STATUS_HARD_ERRORS;
MP_EXIT;
}
//
// Don't try to start the transmitter if the command unit is not
// idle ((not idle) == (Cu-Suspended or Cu-Active)).
//
if ((Adapter->CSRAddress->ScbStatus & SCB_CUS_MASK) != SCB_CUS_IDLE)
{
DBGPRINT(MP_ERROR, ("Adapter = "PTR_FORMAT", CU Not IDLE\n", Adapter));
MP_SET_HARDWARE_ERROR(Adapter);
NdisStallExecution(25);
}
Adapter->CSRAddress->ScbGeneralPointer = pMpTcb->HwTcbPhys;
Status = D100IssueScbCommand(Adapter, SCB_CUC_START, FALSE);
Adapter->TransmitIdle = FALSE;
Adapter->ResumeWait = TRUE;
}
else
{
//
// If the command unit has already been started, then append this
// TCB onto the end of the transmit chain, and issue a CU-Resume.
//
DBGPRINT(MP_LOUD, ("adding TCB to Active chain\n"));
//
// Clear the suspend bit on the previous packet.
//
pMpTcb->PrevHwTcb->TxCbHeader.CbCommand &= ~CB_S_BIT;
//
// Issue a CU-Resume command to the device. We only need to do a
// WaitScb if the last command was NOT a RESUME.
//
Status = D100IssueScbCommand(Adapter, SCB_CUC_RESUME, Adapter->ResumeWait);
}
exit:
DBGPRINT(MP_TRACE, ("<-- NICStartSend\n"));
return Status;
}
NDIS_STATUS MpHandleSendInterrupt(
IN PMP_ADAPTER Adapter
)
/*++
Routine Description:
Interrupt handler for sending processing
Re-claim the send resources, complete sends and get more to send from the send wait queue
Assumption: Send spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
Return Value:
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -