windows-nt/Source/XPSP1/NT/drivers/net/ms/e100bex/mp_nic.c
2020-09-26 16:20:57 +08:00

1487 lines
37 KiB
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;
}
else if (MP_TEST_FLAG(Adapter, fMP_ADAPTER_MAP_REGISTER))
{
//
// Complete physical mapping for each buffer in this packet
//
ASSERT(Packet);
CurrBuffer = pMpTcb->FirstBuffer;
while (CurrBuffer)
{
NdisMCompleteBufferPhysicalMapping(
Adapter->AdapterHandle,
CurrBuffer,
Adapter->CurrMapRegHead);
Adapter->CurrMapRegHead++;
if (Adapter->CurrMapRegHead == (ULONG)Adapter->NumTbd)
Adapter->CurrMapRegHead = 0;
//
// Get the next buffer
//
NdisGetNextBuffer(CurrBuffer, &CurrBuffer);
}
}
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;
NDIS_STATUS SendStatus;
PMP_TCB pMpTcb = NULL;
PMP_TXBUF pMpTxBuf = NULL;
ULONG BytesCopied;
BOOLEAN bCompletePacket = FALSE;
// Mimiced frag list if map registers are used, on the local stack as it's not so big
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,
&pMpTcb->PhysBufCount,
&pMpTcb->BufferCount,
&pMpTcb->FirstBuffer,
&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);
#ifdef NDIS51_MINIPORT
//
// 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;
}
#endif
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;
//
// we can complete it from this routine because it's copied
//
bCompletePacket = TRUE;
pMpTcb->Packet = NULL;
MP_SET_FLAG(pMpTcb, fMP_TCB_USE_LOCAL_BUF);
}
else
{
if (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);
}
else
{
//
// In the map register case, use the local frag list structure
//
pFragList = &FragList;
//
// Do the physical mapping to get all the fragment physical addresses
//
MpStartPacketPhysicalMapping(
Adapter,
pMpTcb->FirstBuffer,
pFragList);
}
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;
if (bCompletePacket)
{
DBGPRINT(MP_TRACE, ("Calling NdisMSendComplete, Pkt= "PTR_FORMAT"\n", Packet));
NdisReleaseSpinLock(&Adapter->SendLock);
NdisMSendComplete( MP_GET_ADAPTER_HANDLE(Adapter), Packet, Status);
NdisAcquireSpinLock(&Adapter->SendLock);
}
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)
{
#ifdef NDIS51_MINIPORT
NdisQueryBufferSafe( CurrBuffer, &pSrc, &CurrLength, NormalPagePriority );
if (pSrc == NULL)
{
return 0;
}
#else
NdisQueryBuffer( CurrBuffer, &pSrc, &CurrLength );
#endif
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;
}
VOID MpStartPacketPhysicalMapping(
IN PMP_ADAPTER Adapter,
IN PNDIS_BUFFER CurrBuffer,
OUT PMP_FRAG_LIST pFragList
)
/*++
Routine Description:
Call NdisMStartBufferPhysicalMapping on each NDIS buffer
Get the physical address for each fragment and save them in the fragment list
We use the same fragment list as the scatter gather so the driver writers only need
to deal one type.
Assumption: spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
CurrBuffer Pointer to the first NDIS_BUFFER
pFragList The pointer to the frag list to be filled
Return Value:
None
--*/
{
NDIS_PHYSICAL_ADDRESS_UNIT PhysAddrUnits[NIC_MAX_PHYS_BUF_COUNT];
UINT ArraySize, i;
ULONG ElementIndex = 0;
DBGPRINT(MP_TRACE, ("--> MpStartPacketPhysicalMapping\n"));
while (CurrBuffer)
{
NdisMStartBufferPhysicalMapping(
Adapter->AdapterHandle,
CurrBuffer,
Adapter->CurrMapRegTail,
TRUE,
PhysAddrUnits,
&ArraySize);
Adapter->CurrMapRegTail++;
if (Adapter->CurrMapRegTail == (ULONG)Adapter->NumTbd)
{
Adapter->CurrMapRegTail = 0;
}
for (i = 0; i < ArraySize; i++)
{
pFragList->Elements[ElementIndex].Address = PhysAddrUnits[i].PhysicalAddress;
pFragList->Elements[ElementIndex].Length = PhysAddrUnits[i].Length;
ElementIndex++;
}
//
// Flush the current buffer because it could be cached
//
NdisFlushBuffer(CurrBuffer, TRUE);
//
// point to the next buffer
//
NdisGetNextBuffer(CurrBuffer, &CurrBuffer);
}
pFragList->NumberOfElements = ElementIndex;
ASSERT(pFragList->NumberOfElements);
DBGPRINT(MP_TRACE, ("<-- MpStartPacketPhysicalMapping\n"));
}
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:
NDIS_STATUS_SUCCESS
NDIS_STATUS_HARD_ERRORS
NDIS_STATUS_PENDING
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PMP_TCB pMpTcb;
#if DBG
LONG i;
#endif
DBGPRINT(MP_TRACE, ("---> MpHandleSendInterrupt\n"));
//
// Any packets being sent? Any packet waiting in the send queue?
//
if (Adapter->nBusySend == 0 &&
IsQueueEmpty(&Adapter->SendWaitQueue))
{
ASSERT(Adapter->CurrSendHead == Adapter->CurrSendTail);
DBGPRINT(MP_TRACE, ("<--- MpHandleSendInterrupt\n"));
return Status;
}
//
// Check the first TCB on the send list
//
while (Adapter->nBusySend > 0)
{
#if DBG
pMpTcb = Adapter->CurrSendHead;
for (i = 0; i < Adapter->nBusySend; i++)
{
pMpTcb = pMpTcb->Next;
}
if (pMpTcb != Adapter->CurrSendTail)
{
DBGPRINT(MP_ERROR, ("nBusySend= %d\n", Adapter->nBusySend));
DBGPRINT(MP_ERROR, ("CurrSendhead= "PTR_FORMAT"\n", Adapter->CurrSendHead));
DBGPRINT(MP_ERROR, ("CurrSendTail= "PTR_FORMAT"\n", Adapter->CurrSendTail));
ASSERT(FALSE);
}
#endif
pMpTcb = Adapter->CurrSendHead;
//
// Is this TCB completed?
//
if (pMpTcb->HwTcb->TxCbHeader.CbStatus & CB_STATUS_COMPLETE)
{
//
// Check if this is a multicast hw workaround packet
//
if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST)
{
MP_FREE_SEND_PACKET_FUN(Adapter, pMpTcb);
}
else
{
MP_CLEAR_FLAGS(pMpTcb);
pMpTcb->Count = 0;
Adapter->CurrSendHead = Adapter->CurrSendHead->Next;
Adapter->nBusySend--;
#if OFFLOAD
NdisInterlockedDecrement(&Adapter->SharedMemRefCount);
if (Adapter->SharedMemRefCount == 0)
{
// DbgPrint("Clear the flag\n");
MP_CLEAR_FLAG(Adapter, fMP_SHARED_MEM_IN_USE);
}
#endif
ASSERT(Adapter->nBusySend >= 0);
}
}
else
{
break;
}
}
//
// If we queued any transmits because we didn't have any TCBs earlier,
// dequeue and send those packets now, as long as we have free TCBs.
//
if (MP_IS_READY(Adapter))
{
while (!IsQueueEmpty(&Adapter->SendWaitQueue) &&
MP_TCB_RESOURCES_AVAIABLE(Adapter))
{
PNDIS_PACKET Packet;
PQUEUE_ENTRY pEntry;
#if OFFLOAD
if (MP_TEST_FLAG(Adapter, fMP_SHARED_MEM_IN_USE))
{
break;
}
#endif
pEntry = RemoveHeadQueue(&Adapter->SendWaitQueue);
ASSERT(pEntry);
Adapter->nWaitSend--;
Packet = CONTAINING_RECORD(pEntry, NDIS_PACKET, MiniportReserved);
DBGPRINT(MP_INFO, ("MpHandleSendInterrupt - send a queued packet\n"));
Status = MpSendPacketFun(Adapter, Packet, TRUE);
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
}
}
DBGPRINT(MP_TRACE, ("<--- MpHandleSendInterrupt\n"));
return Status;
}
VOID MpHandleRecvInterrupt(
IN PMP_ADAPTER Adapter
)
/*++
Routine Description:
Interrupt handler for receive processing
Put the received packets into an array and call NdisMIndicateReceivePacket
If we run low on RFDs, allocate another one
Assumption: Rcv spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
Return Value:
None
--*/
{
PMP_RFD pMpRfd;
PHW_RFD pHwRfd;
PNDIS_PACKET PacketArray[NIC_DEF_RFDS];
PNDIS_PACKET PacketFreeArray[NIC_DEF_RFDS];
UINT PacketArrayCount;
UINT PacketFreeCount;
UINT Index;
UINT LoopIndex = 0;
UINT LoopCount = NIC_MAX_RFDS / NIC_DEF_RFDS + 1; // avoid staying here too long
BOOLEAN bContinue = TRUE;
BOOLEAN bAllocNewRfd = FALSE;
USHORT PacketStatus;
#if OFFLOAD
UINT i;
#endif
DBGPRINT(MP_TRACE, ("---> MpHandleRecvInterrupt\n"));
ASSERT(Adapter->nReadyRecv >= NIC_MIN_RFDS);
while (LoopIndex++ < LoopCount && bContinue)
{
PacketArrayCount = 0;
PacketFreeCount = 0;
//
// Process up to the array size RFD's
//
while (PacketArrayCount < NIC_DEF_RFDS)
{
if (IsListEmpty(&Adapter->RecvList))
{
ASSERT(Adapter->nReadyRecv == 0);
bContinue = FALSE;
break;
}
//
// Get the next MP_RFD to process
//
pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
//
// Get the associated HW_RFD
//
pHwRfd = pMpRfd->HwRfd;
//
// Is this packet completed?
//
PacketStatus = NIC_RFD_GET_STATUS(pHwRfd);
if (!NIC_RFD_STATUS_COMPLETED(PacketStatus))
{
bContinue = FALSE;
break;
}
//
// HW specific - check if actual count field has been updated
//
if (!NIC_RFD_VALID_ACTUALCOUNT(pHwRfd))
{
bContinue = FALSE;
break;
}
//
// Remove the RFD from the head of the List
//
RemoveEntryList((PLIST_ENTRY)pMpRfd);
Adapter->nReadyRecv--;
ASSERT(Adapter->nReadyRecv >= 0);
ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RECV_READY));
MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RECV_READY);
//
// A good packet? drop it if not.
//
if (!NIC_RFD_STATUS_SUCCESS(PacketStatus))
{
DBGPRINT(MP_WARN, ("Receive failure = %x\n", PacketStatus));
NICReturnRFD(Adapter, pMpRfd);
continue;
}
//
// Do not receive any packets until a filter has been set
//
if (!Adapter->PacketFilter)
{
NICReturnRFD(Adapter, pMpRfd);
continue;
}
//
// Do not receive any packets until we are at D0
//
if (Adapter->CurrentPowerState != NdisDeviceStateD0)
{
NICReturnRFD(Adapter, pMpRfd);
continue;
}
//
// Get the packet size
//
pMpRfd->PacketSize = NIC_RFD_GET_PACKET_SIZE(pHwRfd);
NdisAdjustBufferLength(pMpRfd->NdisBuffer, pMpRfd->PacketSize);
NdisFlushBuffer(pMpRfd->NdisBuffer, FALSE);
// we don't mess up the buffer chain, no need to make this call in this case
// NdisRecalculatePacketCounts(pMpRfd->ReceivePacket);
//
// set the status on the packet, either resources or success
//
if (Adapter->nReadyRecv >= MIN_NUM_RFD)
{
// NDIS_STATUS_SUCCESS
NDIS_SET_PACKET_STATUS(pMpRfd->NdisPacket, NDIS_STATUS_SUCCESS);
MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_PEND);
InsertTailList(&Adapter->RecvPendList, (PLIST_ENTRY)pMpRfd);
MP_INC_RCV_REF(Adapter);
}
else
{
//
// NDIS_STATUS_RESOURCES
//
NDIS_SET_PACKET_STATUS(pMpRfd->NdisPacket, NDIS_STATUS_RESOURCES);
MP_SET_FLAG(pMpRfd, fMP_RFD_RESOURCES);
PacketFreeArray[PacketFreeCount] = pMpRfd->NdisPacket;
PacketFreeCount++;
//
// Reset the RFD shrink count - don't attempt to shrink RFD
//
Adapter->RfdShrinkCount = 0;
//
// Remember to allocate a new RFD later
//
bAllocNewRfd = TRUE;
}
PacketArray[PacketArrayCount] = pMpRfd->NdisPacket;
PacketArrayCount++;
}
//
// if we didn't process any receives, just return from here
//
if (PacketArrayCount == 0) break;
//
// Update the number of outstanding Recvs
//
Adapter->PoMgmt.OutstandingRecv += PacketArrayCount;
NdisDprReleaseSpinLock(&Adapter->RcvLock);
NdisMIndicateReceivePacket(
Adapter->AdapterHandle,
PacketArray,
PacketArrayCount);
NdisDprAcquireSpinLock(&Adapter->RcvLock);
//
// NDIS won't take ownership for the packets with NDIS_STATUS_RESOURCES.
// For other packets, NDIS always takes the ownership and gives them back
// by calling MPReturnPackets
//
for (Index = 0; Index < PacketFreeCount; Index++)
{
//
// Get the MP_RFD saved in this packet, in NICAllocRfd
//
pMpRfd = MP_GET_PACKET_RFD(PacketFreeArray[Index]);
ASSERT(MP_TEST_FLAG(pMpRfd, fMP_RFD_RESOURCES));
MP_CLEAR_FLAG(pMpRfd, fMP_RFD_RESOURCES);
//
// Decrement the number of outstanding Recvs
//
Adapter->PoMgmt.OutstandingRecv --;
NICReturnRFD(Adapter, pMpRfd);
}
}
//
// If we ran low on RFD's, we need to allocate a new RFD
//
if (bAllocNewRfd)
{
//
// Allocate one more RFD only if no pending new RFD allocation AND
// it doesn't exceed the max RFD limit
//
if (!Adapter->bAllocNewRfd && Adapter->CurrNumRfd < Adapter->MaxNumRfd)
{
PMP_RFD TempMpRfd;
NDIS_STATUS TempStatus;
TempMpRfd = NdisAllocateFromNPagedLookasideList(&Adapter->RecvLookaside);
if (TempMpRfd)
{
MP_INC_REF(Adapter);
Adapter->bAllocNewRfd = TRUE;
MP_SET_FLAG(TempMpRfd, fMP_RFD_ALLOC_PEND);
//
// Allocate the shared memory for this RFD.
//
TempStatus = NdisMAllocateSharedMemoryAsync(
Adapter->AdapterHandle,
Adapter->HwRfdSize,
FALSE,
TempMpRfd);
//
// The return value will be either NDIS_STATUS_PENDING or NDIS_STATUS_FAILURE
//
if (TempStatus == NDIS_STATUS_FAILURE)
{
MP_CLEAR_FLAGS(TempMpRfd);
NdisFreeToNPagedLookasideList(&Adapter->RecvLookaside, TempMpRfd);
Adapter->bAllocNewRfd = FALSE;
MP_DEC_REF(Adapter);
}
}
}
}
ASSERT(Adapter->nReadyRecv >= NIC_MIN_RFDS);
DBGPRINT(MP_TRACE, ("<--- MpHandleRecvInterrupt\n"));
}
VOID NICReturnRFD(
IN PMP_ADAPTER Adapter,
IN PMP_RFD pMpRfd
)
/*++
Routine Description:
Recycle a RFD and put it back onto the receive list
Assumption: Rcv spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
pMpRfd Pointer to the RFD
Return Value:
None
--*/
{
PMP_RFD pLastMpRfd;
PHW_RFD pHwRfd = pMpRfd->HwRfd;
ASSERT(pMpRfd->Flags == 0);
MP_SET_FLAG(pMpRfd, fMP_RFD_RECV_READY);
//
// HW_SPECIFIC_START
//
pHwRfd->RfdCbHeader.CbStatus = 0;
pHwRfd->RfdActualCount = 0;
pHwRfd->RfdCbHeader.CbCommand = (RFD_EL_BIT);
pHwRfd->RfdCbHeader.CbLinkPointer = DRIVER_NULL;
//
// We don't use any of the OOB data besides status
// Otherwise, we need to clean up OOB data
// NdisZeroMemory(NDIS_OOB_DATA_FROM_PACKET(pMpRfd->NdisPacket),14);
//
// Append this RFD to the RFD chain
if (!IsListEmpty(&Adapter->RecvList))
{
pLastMpRfd = (PMP_RFD)GetListTailEntry(&Adapter->RecvList);
// Link it onto the end of the chain dynamically
pHwRfd = pLastMpRfd->HwRfd;
pHwRfd->RfdCbHeader.CbLinkPointer = pMpRfd->HwRfdPhys;
pHwRfd->RfdCbHeader.CbCommand = 0;
}
//
// HW_SPECIFIC_END
//
//
// The processing on this RFD is done, so put it back on the tail of
// our list
//
InsertTailList(&Adapter->RecvList, (PLIST_ENTRY)pMpRfd);
Adapter->nReadyRecv++;
ASSERT(Adapter->nReadyRecv <= Adapter->CurrNumRfd);
}
NDIS_STATUS NICStartRecv(
IN PMP_ADAPTER Adapter
)
/*++
Routine Description:
Start the receive unit if it's not in a ready state
Assumption: Rcv spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
Return Value:
NDIS_STATUS_SUCCESS
NDIS_STATUS_HARD_ERRROS
--*/
{
PMP_RFD pMpRfd;
NDIS_STATUS Status;
DBGPRINT(MP_TRACE, ("---> NICStartRecv\n"));
//
// If the receiver is ready, then don't try to restart.
//
if (NIC_IS_RECV_READY(Adapter))
{
DBGPRINT(MP_LOUD, ("Receive unit already active\n"));
return NDIS_STATUS_SUCCESS;
}
DBGPRINT(MP_LOUD, ("Re-start receive unit...\n"));
ASSERT(!IsListEmpty(&Adapter->RecvList));
//
// Get the MP_RFD head
//
pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
//
// If more packets are received, clean up RFD chain again
//
if (NIC_RFD_GET_STATUS(pMpRfd->HwRfd))
{
MpHandleRecvInterrupt(Adapter);
ASSERT(!IsListEmpty(&Adapter->RecvList));
//
// Get the new MP_RFD head
//
pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
}
//
// Wait for the SCB to clear before we set the general pointer
//
if (!WaitScb(Adapter))
{
Status = NDIS_STATUS_HARD_ERRORS;
MP_EXIT;
}
if (Adapter->CurrentPowerState > NdisDeviceStateD0)
{
Status = NDIS_STATUS_HARD_ERRORS;
MP_EXIT;
}
//
// Set the SCB General Pointer to point the current Rfd
//
Adapter->CSRAddress->ScbGeneralPointer = pMpRfd->HwRfdPhys;
//
// Issue the SCB RU start command
//
Status = D100IssueScbCommand(Adapter, SCB_RUC_START, FALSE);
if (Status == NDIS_STATUS_SUCCESS)
{
// wait for the command to be accepted
if (!WaitScb(Adapter))
{
Status = NDIS_STATUS_HARD_ERRORS;
}
}
exit:
DBGPRINT_S(Status, ("<--- NICStartRecv, Status=%x\n", Status));
return Status;
}
VOID MpFreeQueuedSendPackets(
IN PMP_ADAPTER Adapter
)
/*++
Routine Description:
Free and complete the pended sends on SendWaitQueue
Assumption: spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
Return Value:
None
--*/
{
PQUEUE_ENTRY pEntry;
PNDIS_PACKET Packet;
NDIS_STATUS Status = MP_GET_STATUS_FROM_FLAGS(Adapter);
DBGPRINT(MP_TRACE, ("--> MpFreeQueuedSendPackets\n"));
while (!IsQueueEmpty(&Adapter->SendWaitQueue))
{
pEntry = RemoveHeadQueue(&Adapter->SendWaitQueue);
Adapter->nWaitSend--;
NdisReleaseSpinLock(&Adapter->SendLock);
ASSERT(pEntry);
Packet = CONTAINING_RECORD(pEntry, NDIS_PACKET, MiniportReserved);
NdisMSendComplete(
MP_GET_ADAPTER_HANDLE(Adapter),
Packet,
Status);
NdisAcquireSpinLock(&Adapter->SendLock);
}
DBGPRINT(MP_TRACE, ("<-- MpFreeQueuedSendPackets\n"));
}
void MpFreeBusySendPackets(
IN PMP_ADAPTER Adapter
)
/*++
Routine Description:
Free and complete the stopped active sends
Assumption: Send spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
Return Value:
None
--*/
{
PMP_TCB pMpTcb;
DBGPRINT(MP_TRACE, ("--> MpFreeBusySendPackets\n"));
//
// Any packets being sent? Check the first TCB on the send list
//
while (Adapter->nBusySend > 0)
{
pMpTcb = Adapter->CurrSendHead;
//
// Is this TCB completed?
//
if ((pMpTcb->HwTcb->TxCbHeader.CbCommand & CB_CMD_MASK) != CB_MULTICAST)
{
MP_FREE_SEND_PACKET_FUN(Adapter, pMpTcb);
}
else
{
break;
}
}
DBGPRINT(MP_TRACE, ("<-- MpFreeBusySendPackets\n"));
}
VOID NICResetRecv(
IN PMP_ADAPTER Adapter
)
/*++
Routine Description:
Reset the receive list
Assumption: Rcv spinlock has been acquired
Arguments:
Adapter Pointer to our adapter
Return Value:
None
--*/
{
PMP_RFD pMpRfd;
PHW_RFD pHwRfd;
LONG RfdCount;
DBGPRINT(MP_TRACE, ("--> NICResetRecv\n"));
ASSERT(!IsListEmpty(&Adapter->RecvList));
//
// Get the MP_RFD head
//
pMpRfd = (PMP_RFD)GetListHeadEntry(&Adapter->RecvList);
for (RfdCount = 0; RfdCount < Adapter->nReadyRecv; RfdCount++)
{
pHwRfd = pMpRfd->HwRfd;
pHwRfd->RfdCbHeader.CbStatus = 0;
pMpRfd = (PMP_RFD)GetListFLink(&pMpRfd->List);
}
DBGPRINT(MP_TRACE, ("<-- NICResetRecv\n"));
}
VOID MpLinkDetectionDpc(
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
/*++
Routine Description:
Timer function for postponed link negotiation
Arguments:
SystemSpecific1 Not used
FunctionContext Pointer to our adapter
SystemSpecific2 Not used
SystemSpecific3 Not used
Return Value:
None
--*/
{
PMP_ADAPTER Adapter = (PMP_ADAPTER)FunctionContext;
NDIS_STATUS Status;
//
// Handle the link negotiation.
//
if (Adapter->bLinkDetectionWait)
{
Status = ScanAndSetupPhy(Adapter);
}
else
{
Status = PhyDetect(Adapter);
}
if (Status == NDIS_STATUS_PENDING)
{
// Wait for 100 ms
Adapter->bLinkDetectionWait = TRUE;
NdisMSetTimer(&Adapter->LinkDetectionTimer, NIC_LINK_DETECTION_DELAY);
return;
}
//
// Reset some variables for link detection
//
Adapter->bLinkDetectionWait = FALSE;
DBGPRINT(MP_WARN, ("MpLinkDetectionDpc - negotiation done\n"));
NdisDprAcquireSpinLock(&Adapter->Lock);
MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_LINK_DETECTION);
NdisDprReleaseSpinLock(&Adapter->Lock);
//
// Any OID query request?
//
if (Adapter->bQueryPending)
{
switch(Adapter->QueryRequest.Oid)
{
case OID_GEN_LINK_SPEED:
*((PULONG) Adapter->QueryRequest.InformationBuffer) = Adapter->usLinkSpeed * 10000;
*((PULONG) Adapter->QueryRequest.BytesWritten) = sizeof(ULONG);
break;
case OID_GEN_MEDIA_CONNECT_STATUS:
default:
ASSERT(Adapter->QueryRequest.Oid == OID_GEN_MEDIA_CONNECT_STATUS);
*((PULONG) Adapter->QueryRequest.InformationBuffer) = NICGetMediaState(Adapter);
*((PULONG) Adapter->QueryRequest.BytesWritten) = sizeof(ULONG);
}
Adapter->bQueryPending = FALSE;
NdisMQueryInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS);
}
//
// Any OID set request?
//
if (Adapter->bSetPending)
{
ULONG PacketFilter;
ASSERT(Adapter->SetRequest.Oid == OID_GEN_CURRENT_PACKET_FILTER);
PacketFilter = *((PULONG)Adapter->SetRequest.InformationBuffer);
NdisDprAcquireSpinLock(&Adapter->Lock);
Status = NICSetPacketFilter(
Adapter,
PacketFilter);
NdisDprReleaseSpinLock(&Adapter->Lock);
if (Status == NDIS_STATUS_SUCCESS)
{
Adapter->PacketFilter = PacketFilter;
}
Adapter->bSetPending = FALSE;
NdisMSetInformationComplete(Adapter->AdapterHandle, Status);
}
NdisDprAcquireSpinLock(&Adapter->Lock);
//
// Any pendingf reset?
//
if (Adapter->bResetPending)
{
// The link detection may have held some requests and caused reset.
// Complete the reset with NOT_READY status
Adapter->bResetPending = FALSE;
MP_CLEAR_FLAG(Adapter, fMP_ADAPTER_RESET_IN_PROGRESS);
NdisDprReleaseSpinLock(&Adapter->Lock);
NdisMResetComplete(
Adapter->AdapterHandle,
NDIS_STATUS_ADAPTER_NOT_READY,
FALSE);
}
else
{
NdisDprReleaseSpinLock(&Adapter->Lock);
}
NdisDprAcquireSpinLock(&Adapter->RcvLock);
//
// Start the NIC receive unit
//
Status = NICStartRecv(Adapter);
if (Status != NDIS_STATUS_SUCCESS)
{
MP_SET_HARDWARE_ERROR(Adapter);
}
NdisDprReleaseSpinLock(&Adapter->RcvLock);
NdisDprAcquireSpinLock(&Adapter->SendLock);
//
// Send packets which have been queued while link detection was going on.
//
if (MP_IS_READY(Adapter))
{
while (!IsQueueEmpty(&Adapter->SendWaitQueue) &&
Adapter->nBusySend < Adapter->NumTcb)
{
PNDIS_PACKET Packet;
PQUEUE_ENTRY pEntry = RemoveHeadQueue(&Adapter->SendWaitQueue);
ASSERT(pEntry);
Adapter->nWaitSend--;
Packet = CONTAINING_RECORD(pEntry, NDIS_PACKET, MiniportReserved);
DBGPRINT(MP_INFO, ("MpLinkDetectionDpc - send a queued packet\n"));
Status = MpSendPacket(Adapter, Packet, TRUE);
if (Status != NDIS_STATUS_SUCCESS)
{
break;
}
}
}
MP_DEC_REF(Adapter);
if (MP_GET_REF(Adapter) == 0)
{
NdisSetEvent(&Adapter->ExitEvent);
}
NdisReleaseSpinLock(&Adapter->SendLock);
}