windows-nt/Source/XPSP1/NT/net/1394/nic/sys/receive.c

4123 lines
107 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
//
// Copyright (c) 1998-1999, Microsoft Corporation, all rights reserved
//
// receive.c
//
// IEEE1394 mini-port/call-manager driver
//
// Mini-port Receive routines
//
// 2/13/1998 ADube Created,
//
#include <precomp.h>
#define MAX_NUM_SLIST_ENTRY 0x10
//-----------------------------------------------------------------------------
// Local prototypes (alphabetically)
//-----------------------------------------------------------------------------
VOID
nicAllocateAddressRangeCallback(
IN PNOTIFICATION_INFO NotificationInfo
);
VOID
nicAllocateAddressRangeDebugSpew(
IN PIRB pIrb
);
NDIS_STATUS
nicGetInitializedAddressFifoElement(
IN UINT BufferLength,
IN OUT PADDRESS_FIFO *ppElement
);
NDIS_STATUS
nicGetEmptyAddressFifoElement(
IN PADDRESS_FIFO *ppElement
);
VOID
nicReceivePacketWorkItem(
PNDIS_WORK_ITEM pWorkItem,
PVOID pContext
);
VOID
nicAllocateRemainingFifoWorkItem (
PNDIS_WORK_ITEM pNdisWorkItem,
IN PVOID Context
);
VOID
nicFifoAllocationScheme (
PRECVFIFO_VCCB pRecvFIFOVc
);
ULONG ReassemblyAllocated = 0;
extern ULONG NdisBufferAllocated[NoMoreCodePaths];
extern ULONG NdisBufferFreed[NoMoreCodePaths];
//-----------------------------------------------------------------------------
// prototype implementation (alphabetically)
//-----------------------------------------------------------------------------
/*
NDIS_STATUS
nicAllocateAddressRange(
IN PREMOTE_NODE pRemoteNode
)
*/
NDIS_STATUS
nicAllocateAddressRange(
IN PADAPTERCB pAdapter,
IN PRECVFIFO_VCCB pRecvFIFOVc
)
// Function Description:
// This function will use the AllocateAddressRange Bus Api
// To do this it must initialize an S-list with structures
// Allocate and Initialize an Irb and an Irp and call the nic
// nicSubmitIrp routine
// This routine will complete synchronously
//
//
// Arguments
// pdo Cb The Pdo for the remote node on which the address range will
// be allocated. There is also a pointer to the Paramters that need to be
//
// Return Value:
//
//
{
PIRB pIrb = NULL;
PIRP pIrp = NULL;
PSLIST_HEADER pSlistHead = NULL;
UINT cnt = 0;
PDEVICE_OBJECT pPdo = NULL;
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
UINT Length;
ADDRESS_OFFSET AddressOffset;
UINT MaxNumSlistEntry = MAX_NUM_SLIST_ENTRY;
BOOLEAN bRefCall = FALSE;
STORE_CURRENT_IRQL;
ASSERT (pRecvFIFOVc == pAdapter->pRecvFIFOVc);
ASSERT (pRecvFIFOVc != NULL);
TRACE( TL_T, TM_Recv, ( "==>nicAllocateAddressRange, pAdapter 8x, pRecvFIFOVc %x", pAdapter, pRecvFIFOVc ) );
do
{
// Increment the Refcount on the VC, so we can gaurantee its presence
//
VC_ACQUIRE_LOCK (pRecvFIFOVc)
//
// Add a reference to the pdo block.
// This reference is added to guarantee its presence
// Removed in Free Address Range or at the end of the function
//
bRefCall = nicReferenceCall ((PVCCB) pRecvFIFOVc, "nicAllocateAddressRange" ) ;
VC_RELEASE_LOCK (pRecvFIFOVc);
if ( bRefCall == FALSE )
{
//
// This will only fail if the Vc is not activated
//
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
//
// Allocate an IRB
//
NdisStatus = nicGetIrb (&pIrb);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
break;
}
ASSERT (pIrb != NULL);
//
// Initalize the IrB with the correct values
// AllocateAddressRange
//
ASSERT (pRecvFIFOVc->Hdr.Nic1394MediaParams.Destination.AddressType == NIC1394AddressType_FIFO);
AddressOffset.Off_High = pRecvFIFOVc->Hdr.Nic1394MediaParams.Destination.FifoAddress.Off_High;
AddressOffset.Off_Low = pRecvFIFOVc->Hdr.Nic1394MediaParams.Destination.FifoAddress.Off_Low;
ASSERT (pRecvFIFOVc->Hdr.MTU !=0 );
Length = pRecvFIFOVc->Hdr.MTU;
nicInitAllocateAddressIrb( pIrb,
pAdapter,
0,
Length,
0,
ACCESS_FLAGS_TYPE_WRITE|ACCESS_FLAGS_TYPE_BROADCAST,
NOTIFY_FLAGS_AFTER_WRITE,
&AddressOffset,
pRecvFIFOVc);
//
// Allocate an Irp
//
NdisStatus = nicGetIrp (pAdapter->pNdisDeviceObject, &pIrp);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
break;
}
ASSERT(pIrp != NULL);
NdisStatus = nicSubmitIrp_LocalHostSynch(pAdapter,
pIrp,
pIrb );
//
// Make this a synchronous call as this is during init
//
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
TRACE( TL_A, TM_Recv, ( "nicAllocateAddressRange SUBMIT IRP FAILED NdisStatus %.8x", NdisStatus ) );
break;
}
nicAllocateAddressRangeDebugSpew(pIrb);
//
// Check to see if the IoCallDriver succeeded
//
if(pIrp->IoStatus.Status == STATUS_SUCCESS)
{
NdisStatus = nicAllocateAddressRangeSucceeded (pIrb, pRecvFIFOVc);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
break;
}
}
else
{
ASSERT (pIrp->IoStatus.Status != STATUS_MORE_PROCESSING_REQUIRED);
// else mark status as failure
//
//
//This means dereference will happen in this function
NdisStatus = NDIS_STATUS_FAILURE;
}
//
// we need to clean up the Irb and the Irp
//
} while (FALSE);
//
// Clean up -dereference the Call if things failed
// If we successfully completed the Irp then all the references made above
// will be dereferenced when the remote node goes away or the
// Call is closed
//
// Deref the references that were made above.
VC_ACQUIRE_LOCK (pRecvFIFOVc);
if (! NT_SUCCESS (NdisStatus))
{
if (bRefCall == TRUE)
{
nicDereferenceCall ( (PVCCB) pRecvFIFOVc , "nicAllocateAddressRange");
}
}
VC_RELEASE_LOCK (pRecvFIFOVc);
// We don't care about the status as we are just freeing locally allocated memory
//
if (pIrb != NULL)
{
nicFreeIrb (pIrb);
}
if (pIrp!= NULL)
{
nicFreeIrp (pIrp);
}
MATCH_IRQL
TRACE( TL_T, TM_Recv, ( "<==nicAllocateAddressRange, pVc %.8x, Status %.8x ", pRecvFIFOVc, NdisStatus ) );
return NdisStatus;
}
VOID
nicAllocateAddressRangeCallback(
IN PNOTIFICATION_INFO pNotificationInfo
)
{
PADAPTERCB pAdapter = (PADAPTERCB) pNotificationInfo->Context;
PRECVFIFO_VCCB pRecvFIFOVc = pAdapter->pRecvFIFOVc;
PNODE_ADDRESS pSenderNodeAddress = NULL;
//
// Debug spew for debugging
//
TRACE( TL_V, TM_Recv, ( " Mdl is at %.8x",pNotificationInfo->Mdl ) );
TRACE( TL_V, TM_Recv, ( " ulLength is %.8x",pNotificationInfo->nLength) );
TRACE( TL_V, TM_Recv, ( " pNotificationInfo->RequestPacket %x, ", pNotificationInfo->RequestPacket) );
TRACE( TL_V, TM_Recv, ( " tLabel %x, ", ((PASYNC_PACKET)pNotificationInfo->RequestPacket)->AP_tLabel) );
pSenderNodeAddress = & (((PASYNC_PACKET)pNotificationInfo->RequestPacket)->AP_Source_ID);
TRACE( TL_V, TM_Recv, ( " Senders' NodeAddress %x, ", pSenderNodeAddress->NA_Node_Number ) );
TRACE (TL_V, TM_Reas,("tLabel %x ", ((PASYNC_PACKET)pNotificationInfo->RequestPacket)->AP_tLabel));
NdisInterlockedIncrement (&pRecvFIFOVc->NumIndicatedFifos);
pNotificationInfo->Fifo->FifoList.Next = NULL;
pNotificationInfo->Fifo->FifoMdl->Next = NULL;
nicFifoAllocationScheme (pRecvFIFOVc);
nicSetTagInFifoWrapper(pNotificationInfo->Fifo, NIC1394_TAG_IN_CALLBACK);
nicStatsRecordNumIndicatedFifos(pRecvFIFOVc->NumIndicatedFifos);
nicReceiveCommonCallback (pNotificationInfo,
(PVCCB)pRecvFIFOVc ,
AddressRange,
pNotificationInfo->Mdl );
}
VOID
nicAllocateAddressRangeDebugSpew(
IN PIRB pIrb )
// Prints out all the parameters of the allocateaddressrange Irb
//
{
ASSERT(pIrb);
/* TRACE( TL_T, TM_Recv, ( "==>nicAllocateAddressRangeDebugSpew pIrb %.8x", pIrb ) );
//
// Spew out all the information you can
//
TRACE(TL_V, TM_Recv, ("MaxSegmentSize = 0x%x\n", pIrb->u.AllocateAddressRange.MaxSegmentSize));
TRACE(TL_V, TM_Recv, ("fulAccessType = 0x%x\n", pIrb->u.AllocateAddressRange.fulAccessType));
TRACE(TL_V, TM_Recv, ("fulNotificationOptions = 0x%x\n", pIrb->u.AllocateAddressRange.fulNotificationOptions));
TRACE(TL_V, TM_Recv, ("Callback = 0x%x\n", pIrb->u.AllocateAddressRange.Callback));
TRACE(TL_V, TM_Recv, ("Context = 0x%x\n", pIrb->u.AllocateAddressRange.Context));
TRACE(TL_V, TM_Recv, ("Required1394Offset->Off_High = 0x%x\n", pIrb->u.AllocateAddressRange.Required1394Offset.Off_High));
TRACE(TL_V, TM_Recv, ("Required1394Offset->Off_Low = 0x%x\n", pIrb->u.AllocateAddressRange.Required1394Offset.Off_Low));
TRACE(TL_V, TM_Recv, ("FifoSListHeader = 0x%x\n", pIrb->u.AllocateAddressRange.FifoSListHead));
TRACE(TL_V, TM_Recv, ("FifoSListSpinLock = 0x%x\n", pIrb->u.AllocateAddressRange.FifoSpinLock));
TRACE(TL_V, TM_Recv, ("AddressesReturned = 0x%x\n", pIrb->u.AllocateAddressRange.AddressesReturned));
TRACE(TL_V, TM_Recv, ("p1394AddressRange = 0x%x\n", pIrb->u.AllocateAddressRange.p1394AddressRange));
TRACE(TL_V, TM_Recv, ("hAddressRange = 0x%x\n", pIrb->u.AllocateAddressRange.hAddressRange));
TRACE(TL_V, TM_Recv, ("AR_Off_High 0x%.4x\n",pIrb->u.AllocateAddressRange.p1394AddressRange[0].AR_Off_High));
TRACE(TL_V, TM_Recv, ("AR_Off_Low 0x%.8x\n",pIrb->u.AllocateAddressRange.p1394AddressRange[0].AR_Off_Low));
TRACE(TL_V, TM_Recv, ("AR_Length 0x%.8x\n",pIrb->u.AllocateAddressRange.p1394AddressRange[0].AR_Length));
ASSERT(pIrb->u.AllocateAddressRange.AddressesReturned == 1);
*/
TRACE( TL_T, TM_Recv, ( "<==nicAllocateAddressRangeComplete" ) );
return;
}
NDIS_STATUS
nicAllocateAddressRangeSucceeded (
IN PIRB pIrb,
IN OUT PRECVFIFO_VCCB pRecvFIFOVc
)
// Function Description:
// This function updates all the Vc, PdoCb structures once the allocate address range Irb has succeeded
// If the Irp succeeds but the rempte node is going away then it will free the address range before
// returning
// The Irb is used to initialize the fields. If this is the first Pdo for Vc, then it will initlalize the VC.
// It will also check to see if the data returned by the Irb matches with the data in the Vc
// Arguments:
// PIRB : The Irb that was used in the Irp that just succeeded
// REMOTE_NODE : The PdoCb that was used to perform the IoCallDriver ... (remote node)
//
// Return Value:
// Success if the remote node is active
// failure if the remote node has gone away and it need to free the address range
//
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
BOOLEAN fPdoBeingRemoved = FALSE;
NIC1394_FIFO_ADDRESS *pFifoAddress = NULL;
BOOLEAN fFirstAddressRangeOnVc = FALSE;
//
// These are pointers to the locations that the newly allocated address range needs to be copied to
//
ADDRESS_RANGE *pSrcAddressRange = &pIrb->u.AllocateAddressRange.p1394AddressRange[0];
// ADDRESS_RANGE *pPdoAddressRange = &pRemoteNode->RecvFIFOData.VcAddressRange;
ADDRESS_RANGE *pVcAddressRange = &pRecvFIFOVc->VcAddressRange;
TRACE( TL_T, TM_Recv, ( "==>nicAllocateAddressRangeSucceeded pIrb %.8x, ", pIrb) );
ASSERT (pIrb->u.AllocateAddressRange.AddressesReturned == 1);
//
// we expect this to be populated or
// nicFreeAllAllocatedAddressRangesOnPdo will have problems
//
ASSERT (pRecvFIFOVc != NULL);
//
// If both high and low are zero, the bus driver is doing something wrong, return Failure
//
if (pSrcAddressRange->AR_Off_Low ==0 && pSrcAddressRange ->AR_Off_High == 0)
{
// Some fun with DeMorgan's theorem
ASSERT (pSrcAddressRange->AR_Off_Low!=0 || pSrcAddressRange ->AR_Off_High!=0);
return NDIS_STATUS_FAILURE;
}
//
// Copy the Address Ranges returned. For now just copy locally without allocating extra memory
//
pFifoAddress = &pRecvFIFOVc->Hdr.Nic1394MediaParams.Destination.FifoAddress;
VC_ACQUIRE_LOCK (pRecvFIFOVc);
do
{
//
// Now we need copy the returned values to the Pdo structure.
//
// pPdoAddressRange->AR_Off_Low = pSrcAddressRange->AR_Off_Low;
// pPdoAddressRange ->AR_Off_High = pSrcAddressRange ->AR_Off_High;
// pPdoAddressRange->AR_Length = pSrcAddressRange->AR_Length;
// pRemoteNode->RecvFIFOData.AddressesReturned = pIrb->u.AllocateAddressRange.AddressesReturned;
// pRemoteNode->RecvFIFOData.hAddressRange = pIrb->u.AllocateAddressRange.hAddressRange ;
//
// check to see if we need to update the Recv Fifo's structures. This needs to be done if the addresses are zeroes
//
if (pFifoAddress->Off_Low == 0 && pFifoAddress->Off_High == 0)
{
fFirstAddressRangeOnVc = TRUE;
pFifoAddress->Off_Low = pSrcAddressRange->AR_Off_Low;
pFifoAddress->Off_High = pSrcAddressRange->AR_Off_High;
pVcAddressRange->AR_Off_Low = pSrcAddressRange->AR_Off_Low;
pVcAddressRange->AR_Off_High = pSrcAddressRange->AR_Off_High;
pVcAddressRange->AR_Length = pSrcAddressRange->AR_Length;
}
else
{
ASSERT (pFifoAddress->Off_Low == pSrcAddressRange->AR_Off_Low);
ASSERT (pFifoAddress->Off_High == pSrcAddressRange->AR_Off_High);
}
pRecvFIFOVc->AddressesReturned = pIrb->u.AllocateAddressRange.AddressesReturned;
pRecvFIFOVc->hAddressRange = pIrb->u.AllocateAddressRange.hAddressRange;
// if (REMOTE_NODE_TEST_FLAGS (pRemoteNode, PDO_BeingRemoved))
// {
// fPdoBeingRemoved = TRUE;
//
// This flag implies that the allocate address range Irp will be failed
// i.e. nicAllocateAddressIrp will return failure, it is now our responsiblity
// to free all resources
//
// NdisStatus = NDIS_STATUS_FAILURE;
// break;
// }
//
// otherwise we have succeeded, now it is the remote node's responsibility to free us
//
// pRemoteNode->RecvFIFOData.AllocatedAddressRange = TRUE;
//
// If we reached this far, we have succeeded
//
NdisStatus = NDIS_STATUS_SUCCESS;
} while (FALSE);
VC_RELEASE_LOCK (pRecvFIFOVc);
TRACE( TL_T, TM_Recv, ( " hAddressRange %x, NumReturned %x , Low %x , Hi %x, Length %x",
pRecvFIFOVc->hAddressRange ,
pRecvFIFOVc->AddressesReturned,
pSrcAddressRange->AR_Off_Low,
pSrcAddressRange->AR_Off_High,
pSrcAddressRange->AR_Length) );
// if (fPdoBeingRemoved == TRUE)
// {
//
// We need to free the address range as the pdo is being
// removed and we have not updated any of the Pdo's data structures
//
// nicFreeRecvFifoAddressRange (pRemoteNode);
//
// Clear the pointers to the Address Range, they exist in both
// the Pdo Block and the VC Block.
// However clear the Vc blocks' copy of the address range only if this
// is the Allocate Address Range that populated it
//
// NdisZeroMemory (&pRemoteNode->RecvFIFOData, sizeof (RECV_FIFO_DATA));
// if (fFirstAddressRangeOnVc = TRUE)
// {
// pFifoAddress->Off_Low = 0;
// pFifoAddress->Off_High = 0;
// pVcAddressRange->AR_Off_Low = 0;
// pVcAddressRange->AR_Off_High = 0;
// pVcAddressRange->AR_Length = 0;
// pRecvFIFOVc->AddressesReturned = 0;
// }
// }
TRACE( TL_T, TM_Recv, ( "<==nicAllocateAddressRangeSucceeded Status %.8x", NdisStatus ) );
return NdisStatus;
}
NDIS_STATUS
nicFreeAddressFifo(
IN PADDRESS_FIFO pAddressFifo,
IN PRECVFIFO_VCCB pRecvFIFOVc
)
//
// Frees the address fifo element and also its local buffer
// IMP: The Addressfifo should belong to the pRecvFIFOVc
//
{
PVOID SystemAddress = NIC_GET_SYSTEM_ADDRESS_FOR_MDL (pAddressFifo->FifoMdl);
UINT Length = MmGetMdlByteCount(pAddressFifo->FifoMdl);
ASSERT (SystemAddress!=NULL);
ASSERT (Length != 0);
TRACE( TL_T, TM_Recv, ( "==>nicFreeAddressFifo") );
if (SystemAddress != NULL)
{
nicFreeLocalBuffer(Length, SystemAddress);
}
nicFreeMdl (pAddressFifo->FifoMdl);
nicSetTagInFifoWrapper(pAddressFifo, NIC1394_TAG_FREED );
FREE_NONPAGED((PVOID)pAddressFifo);
//
// Dereference the reference added when this AddressFifo was inserted into the list
//
nicDereferenceCall ((PVCCB)pRecvFIFOVc, "nicFreeAddressFifo");
TRACE( TL_T, TM_Recv, ( "<==nicFreeAddressFifo") );
return NDIS_STATUS_SUCCESS;
}
NDIS_STATUS
nicFreeAllocateAddressRangeSList(
IN PRECVFIFO_VCCB pRecvFIFOVc
)
//
// This function should pop entries from the Slist
// Each entry is an Adress_fifo element containing an MDl
// The function should call nicFreeAddressFifo to free the Address FIFO element
//
{
NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
PADDRESS_FIFO pAddressFifoElement = NULL;
SINGLE_LIST_ENTRY *pSingleListEntry = NULL;
UINT NumFreed = 0;
STORE_CURRENT_IRQL;
TRACE( TL_T, TM_Recv, ( "==>nicFreeAllocateAddressRangeSList, Num %.8x",
ExQueryDepthSList (&pRecvFIFOVc->FifoSListHead) ) );
while ( ExQueryDepthSList (&pRecvFIFOVc->FifoSListHead) != 0)
{
pSingleListEntry= ExInterlockedPopEntrySList ( &pRecvFIFOVc->FifoSListHead,
&pRecvFIFOVc->FifoSListSpinLock );
//
// This will dereference the call
//
pAddressFifoElement = CONTAINING_RECORD (pSingleListEntry, ADDRESS_FIFO, FifoList);
ASSERT (pAddressFifoElement != NULL);
//
// This will dereference the Vc and free the address fifo
//
NdisStatus = nicFreeAddressFifo ( pAddressFifoElement,
pRecvFIFOVc );
NumFreed ++;
}
VC_ACQUIRE_LOCK (pRecvFIFOVc);
ASSERT ( ExQueryDepthSList (&pRecvFIFOVc->FifoSListHead) == 0);
pRecvFIFOVc->FifoSListHead.Alignment = 0;
pRecvFIFOVc->NumOfFifosInSlistInCloseCall = NumFreed;
VC_RELEASE_LOCK (pRecvFIFOVc);
MATCH_IRQL;
TRACE( TL_T, TM_Recv, ( "<==nicFreeAllocateAddressRangeSList %.8x, NumFreed %x", NdisStatus, NumFreed ) );
return NdisStatus;
}
NDIS_STATUS
nicFreeRecvFifoAddressRangeOnAllRemoteNodes (
IN PADAPTERCB pAdapter
)
// Function Description:
// This will walk through all the remote nodes and remove the address range
// on each remote node
// The implicit assumption is that RecvFIFO's will be allocated on all remote nodes
// on an adapter
// Arguments
// pAdapter - the adapter on which all the remote nodes are on.
//
// Return Value:
// Success : Always
// Even if one fails this will continue to try on all remaining nodes
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
PLIST_ENTRY pPdoListEntry = NULL;
REMOTE_NODE *pRemoteNode = NULL;
TRACE( TL_T, TM_Recv, ( "==>nicFreeRecvFifoAddressRangeOnAllRemoteNodes Adapter %.8x", pAdapter) );
ADAPTER_ACQUIRE_LOCK (pAdapter);
//
// Now Walk the list and free the address range on each pdo
//
for (pPdoListEntry = ListNext (&pAdapter->PDOList);
pPdoListEntry != &pAdapter->PDOList;
pPdoListEntry = ListNext(pPdoListEntry))
{
ADDRESS_RANGE *pPdoAddressRange, *pVcAddressRange;
pRemoteNode = CONTAINING_RECORD (pPdoListEntry, REMOTE_NODE, linkPdo);
pPdoAddressRange = &pRemoteNode->RecvFIFOData.VcAddressRange;
pVcAddressRange = &pRemoteNode->pAdapter->pRecvFIFOVc->VcAddressRange;
TRACE( TL_V, TM_Recv, ( "Closing all address ranges on pRemoteNode %.8x", pRemoteNode) );
if (REMOTE_NODE_TEST_FLAG (pRemoteNode, PDO_BeingRemoved) == TRUE)
{
//
// This flag is set in the Remove Remote node code path.
// If this flag is set, we do not have to free the address range
// If Not Set, then Arp has asked to close the call and we must free
// the address range
//
continue;
}
//
// Debug checks to see if the values match up
//
ASSERT ( pPdoAddressRange->AR_Off_Low == pVcAddressRange->AR_Off_Low);
ASSERT ( pPdoAddressRange->AR_Off_High == pVcAddressRange->AR_Off_High);
ASSERT ( pPdoAddressRange->AR_Length == pVcAddressRange->AR_Length);
//
// Now free the address range
// This function expects the lock to be held
//
NdisStatus = nicFreeAllAllocatedAddressRangesOnPdo (pRemoteNode);
if (pRemoteNode != NULL)
{
NdisZeroMemory (&pRemoteNode->RecvFIFOData, sizeof (RECV_FIFO_DATA) );
}
}
ADAPTER_RELEASE_LOCK (pAdapter);
NdisStatus = NDIS_STATUS_SUCCESS;
TRACE( TL_T, TM_Recv, ( "<==nicFreeRecvFifoAddressRangeOnAllRemoteNodes Status %.8x", NdisStatus) );
return NdisStatus;
}
NDIS_STATUS
nicFreeAllAllocatedAddressRangesOnPdo (
IN PREMOTE_NODE pRemoteNode
)
// Function Description:
// Goes through the PdoCb and frees all the allocated address ranges.
// for now we have one RecvFIFOVc only so this is a small function
// This will be called from the remove remote node code path
//
// Arguments
// PdoCb * Pdo Control Block that has the address ranges that this
// function will free
//
// Return Value:
// Success: always.
//
// Called With the Lock held
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
PVCCB pVc = (PVCCB)(pRemoteNode->RecvFIFOData.pRecvFIFOVc);
STORE_CURRENT_IRQL;
TRACE( TL_T, TM_Recv, ( "==>nicFreeAllAllocatedAddressRangesOnPdo pRemoteNode %.8x", pRemoteNode) );
//
// Check if the Allocated Address Range is true for the RecvFifoData structure in the
// PdoCb. If it is, then it is this thread's responsibility to free the address range
//
if (pRemoteNode->RecvFIFOData.AllocatedAddressRange == TRUE)
{
ULONG AddressesReturned = pRemoteNode->RecvFIFOData.AddressesReturned;
ADDRESS_RANGE VcAddressRange = pRemoteNode->RecvFIFOData.VcAddressRange;
HANDLE hAddressRange = pRemoteNode->RecvFIFOData.hAddressRange ;
pRemoteNode->RecvFIFOData.AllocatedAddressRange = FALSE;
REMOTE_NODE_RELEASE_LOCK (pRemoteNode);
//
// Now we free the address range.
//
ASSERT (0);
NdisStatus = nicFreeAddressRange (pRemoteNode->pAdapter,
AddressesReturned ,
&VcAddressRange ,
&hAddressRange );
REMOTE_NODE_ACQUIRE_LOCK (pRemoteNode);
nicDereferenceCall (pVc, "nicFreeAllAllocatedAddressRangesOnPdo ");
}
else
{
NdisStatus = NDIS_STATUS_SUCCESS;
}
//
// Now if anyone tries to access this pointer they will bugcheck,..
// So we can ensure that there will be no more indications via this pointer
//
NdisZeroMemory (&pRemoteNode->RecvFIFOData, sizeof (RECV_FIFO_DATA) );
MATCH_IRQL;
TRACE( TL_T, TM_Recv, ( "<==nicFreeAllAllocatedAddressRangesOnPdo Status %.8x (always success)", NdisStatus) );
NdisStatus = NDIS_STATUS_SUCCESS;
return NdisStatus;
}
NDIS_STATUS
nicFillAllocateAddressRangeSList(
PRECVFIFO_VCCB pRecvFIFOVc,
UINT *Num )
//
// Function inits the Slist that will be sent down with the
// AllocateAddressRange Irb
// RedvFifoVc - VC to be linked with the Slist
// Num - Num of AddressFifo Elements that are inserted into the SList
//
{
PADDRESS_FIFO pRecvFifoElement = NULL;
NDIS_STATUS NdisStatus;
UINT cnt = 0;
BOOLEAN bRef = FALSE;
TRACE( TL_T, TM_Recv, ( "==>nicFillAllocateAddressRangeSList" ) );
ASSERT (pRecvFIFOVc != NULL);
ASSERT (pRecvFIFOVc->Hdr.MTU != 0);
do
{
NdisStatus = nicGetInitializedAddressFifoElement (pRecvFIFOVc->Hdr.MTU,
&pRecvFifoElement);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
break;
}
ASSERT (pRecvFifoElement != NULL);
nicSetTagInFifoWrapper(pRecvFifoElement, NIC1394_TAG_QUEUED);
ExInterlockedPushEntrySList ( &pRecvFIFOVc->FifoSListHead,
&pRecvFifoElement->FifoList,
&pRecvFIFOVc->FifoSListSpinLock);
//
// Add this once for every Address Fifo element inserted
// Will be decremented by a call to nicFreeAddressFifo
//
VC_ACQUIRE_LOCK (pRecvFIFOVc);
#if FIFO_WRAPPER
pRecvFIFOVc->pFifoTable[cnt] = pRecvFifoElement;
#endif
bRef = nicReferenceCall ((PVCCB) pRecvFIFOVc, "nicFillAllocateAddressRangeSList");
VC_RELEASE_LOCK (pRecvFIFOVc);
if (bRef == FALSE)
{
NdisStatus = NDIS_STATUS_VC_NOT_ACTIVATED;
break;
}
TRACE( TL_V, TM_Recv, ( "cnt %.8x, Num %.8x, ",cnt, *Num) );
} while (++cnt < *Num);
//
// Need to handle failure cases and also return number allocated
//
*Num = cnt;
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
TRACE( TL_V, TM_Recv, ( "nicFillAllocateAddressRangeSList Failed, num allotted %.8x, MTU %,8x ",cnt ,pRecvFIFOVc->Hdr.MTU ) );
NdisStatus = nicFreeAllocateAddressRangeSList (pRecvFIFOVc);
ASSERT (NdisStatus == NDIS_STATUS_SUCCESS);
}
TRACE( TL_T, TM_Recv, ( "==>nicFillAllocateAddressRangeSList Num %.8x, MTU %.8x",cnt,pRecvFIFOVc->Hdr.MTU ) );
return NdisStatus;
}
NDIS_STATUS
nicGetInitializedAddressFifoElement(
IN UINT BufferLength,
IN OUT PADDRESS_FIFO *ppElement )
//
// This function return a single AddressFifo element,
// with an MDL pointing to locally owned allocated memory
// The size of the memory needs to be specified at MTU of
// the VC that this belongs to and is the BufferLength.
//
// Get locally owned buffer, get address fifo , init MDL with
// local buffer. return the AddressFifo
//
{
PVOID pLocalBuffer = NULL;
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
TRACE( TL_T, TM_Recv, ( "==>nicGetAddressInitializedFifoElement" ) );
ASSERT (BufferLength != 0);
do
{
if (BufferLength == 0)
{
NdisStatus = NDIS_STATUS_FAILURE;
TRACE( TL_A, TM_Recv, ( "BufferLength is 0" ) );
break;
}
//
// Get Locally owned memory for the data
//
NdisStatus = nicGetLocalBuffer (BufferLength, &pLocalBuffer);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
(*ppElement) = NULL;
break;
}
//
// Get Empty memory for the Address Fifo element
//
NdisStatus = nicGetEmptyAddressFifoElement (ppElement);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
nicFreeLocalBuffer (BufferLength,
pLocalBuffer);
(*ppElement) = NULL;
break;
}
//
// Get an MDL and initialze the MDL with the buffer
// and initialize the fifo ,with MDL.
//
nicGetMdl ( BufferLength,
pLocalBuffer,
&((*ppElement)->FifoMdl));
} while(FALSE);
TRACE( TL_T, TM_Recv, ( "<==nicGetInitializedAddressFifoElement, Status %.8x, AddressFifo at %.8x, LocalBuffer at %.8x",
NdisStatus, *ppElement,MmGetMdlVirtualAddress((*ppElement)->FifoMdl ) ) );
return NdisStatus;
}
NDIS_STATUS
nicGetEmptyAddressFifoElement(
IN PADDRESS_FIFO *ppElement)
//
// Returns an empty Address Fifo element.
// NULL otherwise
//
// i.e just allocates memory and returns for now
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
ULONG Size = 0;
TRACE( TL_T, TM_Recv, ( "==>nicGetEmptyAddressFifoElement" ) );
#if FIFO_WRAPPER // Used to track missing Address Fifo elements
Size = sizeof (ADDRESS_FIFO_WRAPPER);
#else
Size = sizeof (ADDRESS_FIFO );
#endif
*ppElement = ALLOC_NONPAGED (Size, MTAG_FIFO);
if (*ppElement != NULL)
{
NdisZeroMemory (*ppElement, Size);
nicSetTagInFifoWrapper(*ppElement, NIC1394_TAG_ALLOCATED );
NdisStatus = NDIS_STATUS_SUCCESS;
}
TRACE( TL_T, TM_Recv, ( "<==nicGetEmptyAddressFifoElement, Status % .8x, at %.8x",NdisStatus,*ppElement ) );
return NdisStatus;
}
NDIS_STATUS
nicGetNdisBuffer(
IN UINT Length,
IN PVOID pLocalBuffer,
IN OUT PNDIS_BUFFER *ppNdisBuffer )
// This function will be used by the receive side
// This returns an NdisBuffer as opposed to an MDL
// initialized to the corect start addresses
//
{
NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
ASSERT (pLocalBuffer != NULL);
ASSERT (Length > 0);
ASSERT (ppNdisBuffer != NULL);
TRACE( TL_T, TM_Recv, ( "==>nicGetNdisBuffer Local Buffer %.8x, Length %.8x", pLocalBuffer, Length) );
if ( Length > 0 &&
pLocalBuffer != NULL &&
ppNdisBuffer != NULL)
{
NdisAllocateBuffer( &NdisStatus,
ppNdisBuffer,
NULL,
pLocalBuffer,
Length );
}
else
{
nicIncrementMallocFailure();
NdisStatus = NDIS_STATUS_FAILURE;
}
TRACE( TL_T, TM_Recv, ( "<==nicGetNdisBuffer Buffer %x, NdisStatus %.8x", *ppNdisBuffer, NdisStatus ) );
return NdisStatus;
}
NDIS_STATUS
nicInitAllocateAddressIrb(
IN PIRB pIrb,
IN PVOID pContext,
IN ULONG fulFlags,
IN ULONG nLength,
IN ULONG MaxSegmentSize,
IN ULONG fulAccessType,
IN ULONG fulNotificationOptions,
IN PADDRESS_OFFSET pOffset,
IN PRECVFIFO_VCCB pRecvFIFOVc
)
//
// Initializes the allocate adddress Irb with the
// values passed to the function
//
// And adds constants for certain preknown values (e.g. callback, context)
// Spew as much debug as possible
//
{
NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
ASSERT (pRecvFIFOVc != NULL);
TRACE( TL_T, TM_Recv, ( "==>nicInitAllocateAddressIrb" ) );
pIrb->FunctionNumber = REQUEST_ALLOCATE_ADDRESS_RANGE;
pIrb->Flags = 0;
pIrb->u.AllocateAddressRange.Mdl = NULL;
pIrb->u.AllocateAddressRange.fulFlags = 0;
pIrb->u.AllocateAddressRange.nLength = nLength;
pIrb->u.AllocateAddressRange.MaxSegmentSize = 0;
pIrb->u.AllocateAddressRange.fulAccessType = fulAccessType;
pIrb->u.AllocateAddressRange.fulNotificationOptions = NOTIFY_FLAGS_AFTER_WRITE;
pIrb->u.AllocateAddressRange.Callback = nicAllocateAddressRangeCallback;
pIrb->u.AllocateAddressRange.Context = pContext; // should be pAdapter
pIrb->u.AllocateAddressRange.Required1394Offset.Off_High = pOffset->Off_High;
pIrb->u.AllocateAddressRange.Required1394Offset.Off_Low = pOffset->Off_Low;
pIrb->u.AllocateAddressRange.FifoSListHead = &pRecvFIFOVc->FifoSListHead;
pIrb->u.AllocateAddressRange.FifoSpinLock = &pRecvFIFOVc->FifoSListSpinLock;
pIrb->u.AllocateAddressRange.AddressesReturned = 0;
pIrb->u.AllocateAddressRange.p1394AddressRange = &pRecvFIFOVc->VcAddressRange;
TRACE(TL_V, TM_Recv, ("nLength = 0x%x\n", pIrb->u.AllocateAddressRange.nLength));
TRACE(TL_V, TM_Recv, ("MaxSegmentSize = 0x%x\n", pIrb->u.AllocateAddressRange.MaxSegmentSize));
TRACE(TL_V, TM_Recv, ("fulAccessType = 0x%x\n", pIrb->u.AllocateAddressRange.fulAccessType));
TRACE(TL_V, TM_Recv, ("fulNotificationOptions = 0x%x\n", pIrb->u.AllocateAddressRange.fulNotificationOptions));
TRACE(TL_V, TM_Recv, ("Callback = 0x%x\n", pIrb->u.AllocateAddressRange.Callback));
TRACE(TL_V, TM_Recv, ("Context = 0x%x\n", pIrb->u.AllocateAddressRange.Context));
TRACE(TL_V, TM_Recv, ("Required1394Offset->Off_High = 0x%x\n", pIrb->u.AllocateAddressRange.Required1394Offset.Off_High));
TRACE(TL_V, TM_Recv, ("Required1394Offset->Off_Low = 0x%x\n", pIrb->u.AllocateAddressRange.Required1394Offset.Off_Low));
TRACE(TL_V, TM_Recv, ("FifoSListHeader = 0x%x\n", pIrb->u.AllocateAddressRange.FifoSListHead));
TRACE(TL_V, TM_Recv, ("FifoSListSpinLock = 0x%x\n", pIrb->u.AllocateAddressRange.FifoSpinLock));
TRACE(TL_V, TM_Recv, ("AddressesReturned = 0x%x\n", pIrb->u.AllocateAddressRange.AddressesReturned));
TRACE(TL_V, TM_Recv, ("p1394AddressRange = 0x%x\n", pIrb->u.AllocateAddressRange.p1394AddressRange));
TRACE( TL_T, TM_Recv, ( "<==nicInitAllocateAddressIrb" ) );
return NdisStatus;
}
VOID
nicFifoReturnPacket (
IN PVCCB pVc,
IN PNDIS_PACKET pMyPacket
)
// Function Description:
// Return Packets Handler -
// For FIFO's will reinsert the buffer (MDL) into the Fifo SList
// Check for the VC Active and then return it ot the SList . Free it otherwise
//
// Arguments
// RecvFIFOVc - Vc of the packet
// pPacket = Packet in question
//
// Return Value:
//
//
//
{
PRECVFIFO_VCCB pRecvFIFOVc = (PRECVFIFO_VCCB) pVc;
PNDIS_BUFFER pMyNdisBuffer;
PADAPTERCB pAdapter = pRecvFIFOVc->Hdr.pAF->pAdapter;
BOOLEAN fVcActive = FALSE;
PADDRESS_FIFO pAddressFifo;
PPKT_CONTEXT pPktContext = (PPKT_CONTEXT)&pMyPacket->MiniportReserved;
TRACE( TL_T, TM_Recv, ( "==>nicFifoReturnPacket pVc %x, pPacket %x, pAdapter %x, ",
pRecvFIFOVc, pMyPacket, pAdapter) );
//
// Either the reassembly structure has the indicated Fifo's or if no reassembly was done
// then the PktContext has it.
//
pAddressFifo = pPktContext->AllocateAddressRange.pIndicatedFifo;
//
// Do not push it back in the list if the VC is about to close.
// However, we push it back in, if the VC has not been activated yet
//
nicReturnFifoChain ( pAddressFifo , pRecvFIFOVc) ;
//
// Now we have to free the packet and ndis buffers that we got in the
// Calback code
//
TRACE( TL_V, TM_Recv, ( " AllocateAddress Range - Free Packet and Free Buffer" ) );
nicReturnNdisBufferChain (pMyPacket->Private.Head, pVc);
nicFreePacket(pMyPacket, &pRecvFIFOVc->PacketPool);
TRACE( TL_T, TM_Recv, ( "<==nicFifoReturnPacket " ) );
return;
}
VOID
nicReturnNdisBufferChain (
IN PNDIS_BUFFER pNdisBuffer ,
IN PVCCB pVc
)
{
PNDIS_BUFFER pNext;
BOOLEAN fIsFifo = (pVc->Hdr.VcType == NIC1394_RecvFIFO);
if (pNdisBuffer == NULL)
{
ASSERT (pNdisBuffer != NULL);
return;
}
while (pNdisBuffer != NULL)
{
pNext = pNdisBuffer->Next;
NdisFreeBuffer(pNdisBuffer);
nicDecRecvBuffer(fIsFifo);
pNdisBuffer = pNext;
}
}
VOID
nicReturnFifoChain (
IN PADDRESS_FIFO pAddressFifo,
IN PRECVFIFO_VCCB pRecvFIFOVc
)
// Function Description:
// This takes a chain of Address Fifos and returns it to the slist
// if the VC is active or frees it if the VC is not active
//
//
//
// Arguments
// pAddressFifo - chain of address fifo element
// pRecvFIFOVc - Recv VC
//
// Return Value:
//
// None
//
{
TRACE( TL_T, TM_Recv, ( "==> nicReturnFifoChain pAddressFifo %x, pRecvFifoVc %x", pAddressFifo, pRecvFIFOVc) );
VC_ACQUIRE_LOCK (pRecvFIFOVc);
//
// lets update the value again, before we insert the Address Fifo back in to the Slist
// If there are any remote nodes present and the VC is active
// , then we should insert this back into the SList
//
if ( VC_ACTIVE (pRecvFIFOVc) == TRUE )
{
//
// Return all the AddressFifo elements to the slist
// Do this with the lock held so no one can change the
// VC state from under us
//
while (pAddressFifo != NULL)
{
PADDRESS_FIFO pNextFifo = (PADDRESS_FIFO)(pAddressFifo->FifoList.Next);
nicSetTagInFifoWrapper(pAddressFifo, NIC1394_TAG_QUEUED);
ExInterlockedPushEntrySList ( &pRecvFIFOVc->FifoSListHead,
&pAddressFifo ->FifoList,
&pRecvFIFOVc->FifoSListSpinLock);
TRACE( TL_V, TM_Recv, ( " VcActive Address Fifo %x, Next Fifo %x",pAddressFifo , pNextFifo) );
pAddressFifo = pNextFifo;
NdisInterlockedDecrement (&pRecvFIFOVc->NumIndicatedFifos);
}
VC_RELEASE_LOCK (pRecvFIFOVc);
}
else //VC_ACTIVE (pRecvFIFOVc) == TRUE
{
VC_RELEASE_LOCK (pRecvFIFOVc);
//
// free all the Address Fifo after releasing the lock
//
while (pAddressFifo != NULL)
{
PADDRESS_FIFO pNextFifo = (PADDRESS_FIFO)(pAddressFifo->FifoList.Next);
//
// Free the Mdl and Address Fifo structure and decrease the refcount
// on the call. Do not touch the Vc after this
//
TRACE( TL_V, TM_Recv, ( " Vc NOT Active Address Fifo %x, Next Fifo %x",pAddressFifo , pNextFifo) );
nicFreeAddressFifo(pAddressFifo ,
pRecvFIFOVc);
NdisInterlockedDecrement (&pRecvFIFOVc->NumIndicatedFifos);
pAddressFifo = pNextFifo;
}
}
TRACE( TL_T, TM_Recv, ( "<== nicReturnFifoChain ") );
return;
}
VOID
nicInternalReturnPacket(
IN PVCCB pVc ,
IN PNDIS_PACKET pPacket
)
/*++
Routine Description:
Finds out what type of Vc is being indicated and calls the appropriate VC return packets handler
Arguments:
MiniportAdapterContext - the pAdapter structure,
pPacket - pPacket that the protocol returns
Return Value:
--*/
{
PPKT_CONTEXT pPktContext = (PPKT_CONTEXT)&pPacket->MiniportReserved;
switch (pVc->Hdr.VcType)
{
case NIC1394_SendRecvChannel:
case NIC1394_RecvChannel:
{
nicChannelReturnPacket (pVc, pPacket );
break;
}
case NIC1394_RecvFIFO:
{
nicFifoReturnPacket ( pVc, pPacket);
break;
}
case NIC1394_Ethernet:
{
ASSERT (pVc->Hdr.VcType != NIC1394_Ethernet);
break;
}
case NIC1394_MultiChannel:
{
ASSERT (pVc->Hdr.VcType != NIC1394_MultiChannel);
break;
}
case NIC1394_SendChannel:
{
ASSERT (pVc->Hdr.VcType != NIC1394_SendChannel);
break;
}
default :
{
ASSERT (0);
break;
}
}
return;
}
VOID
NicReturnPacket(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET pPacket
)
/*++
Routine Description:
This is the return packets handler.
This functikon handles all the instrumentation to catch outstanding packets and
then calls the internal return packets function
Arguments:
MiniportAdapterContext - the pAdapter structure,
pPacket - pPacket that the protocol returns
Return Value:
--*/
{
PADAPTERCB pAdapter = (PADAPTERCB) MiniportAdapterContext;
PPKT_CONTEXT pPktContext = (PPKT_CONTEXT)&pPacket->MiniportReserved;
PINDICATE_RSVD pIndicateRsvd = NULL;
PRSVD pRsvd = NULL;
//
// The first parameter of the MiniportReserved will always contain the VC
//
PVCCB pVc = (PVCCB)pPktContext->AllocateAddressRange.pRecvFIFOVc;
TRACE( TL_T, TM_Recv, ( "==> NicReturnPacket pPacket %x ", pPacket) );
do
{
//
// Mark the packet as returned
//
pRsvd =(PRSVD)(pPacket->ProtocolReserved);
pIndicateRsvd = &pRsvd->IndicateRsvd;
ASSERT (pIndicateRsvd->Tag == NIC1394_TAG_INDICATED);
pIndicateRsvd->Tag = NIC1394_TAG_RETURNED;
#if MISSING_PACKETS
if (pVc == NULL)
{
NDIS_STATUS Status = NDIS_GET_PACKET_STATUS(pPacket);
TRACE (TL_V, TM_Recv, ("Packet %x Status %x\n", pPacket, Status) );
ASSERT (0);
break;
}
#endif
nicInternalReturnPacket (pVc, pPacket);
}while (FALSE);
TRACE( TL_T, TM_Recv, ( " <== NicReturnPacket ") );
return;
}
NDIS_STATUS
nicFindReassemblyStructure (
IN PREMOTE_NODE pRemoteNode,
IN USHORT Dgl,
IN BUS_OPERATION BusOp,
IN PVCCB pVc,
OUT PNDIS1394_REASSEMBLY_STRUCTURE* ppReassembly
)
// Function Description:
// Walk through all the reassembly operations on this remote node
// and see if one is present
//
// If no reassembly is found, it will allocate and initialie a structure.
// All within the context of the reassembly lock
//
// Arguments
// pRemoteNode - Remote Node that is sending the fragments
// dgl - identifier for reassembly packet
// Together they are unique for each reassembly operation
// Return Value:
//
//
{
PNDIS1394_REASSEMBLY_STRUCTURE pTempReassembly = NULL;
PNDIS1394_REASSEMBLY_STRUCTURE pReassembly = NULL;
PLIST_ENTRY pReassemblyList = NULL;
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
TRACE( TL_T, TM_Recv, ( "==>nicFindReassemblyStructure pRemoteNode %x, dgl %x " , pRemoteNode , Dgl) );
//
// Acquire the reassebly lock . Only let go when either a reassembly structure is found or a new
// reassembly structure is inserted into the remote node's reassembly list
//
REMOTE_NODE_ACQUIRE_LOCK (pRemoteNode);
REMOTE_NODE_REASSEMBLY_ACQUIRE_LOCK (pRemoteNode)
pReassemblyList = pRemoteNode->ReassemblyList.Flink;
//
// Find the reassembly with the same dgl
//
while ( pReassemblyList != &pRemoteNode->ReassemblyList)
{
pTempReassembly = CONTAINING_RECORD (pReassemblyList ,
NDIS1394_REASSEMBLY_STRUCTURE,
ReassemblyListEntry );
TRACE( TL_V, TM_Recv, ( "Current Dgl %x, dgl %x " , pTempReassembly->Dgl , Dgl) );
if (pTempReassembly->Dgl == Dgl)
{
pReassembly = pTempReassembly;
break;
}
pReassemblyList = pReassemblyList->Flink;
}
do
{
//
// If we have found a valid reassembly then return
//
if (pReassembly != NULL )
{
*ppReassembly = pReassembly ;
NdisStatus = NDIS_STATUS_SUCCESS;
}
else
{
//
// We need to allocate and initialize a reassembly structure
//
NdisStatus = nicGetReassemblyStructure (&pReassembly);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
BREAK (TM_Recv, (" nicGetReassemblyStructure nicGetReassemblyStructure FAILED") );
}
NdisStatus = nicInitializeReassemblyStructure (pReassembly,
Dgl,
pRemoteNode,
pVc,
BusOp);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
pReassembly = NULL;
BREAK (TM_Recv, (" nicFindReassemblyStructure nicInitializeReassemblyStructure FAILED" ) );
}
}
} while (FALSE);
if (NdisStatus == NDIS_STATUS_SUCCESS)
{
//
// Increment the ref count. Ref Count will be freed when the fragment is inserted into
// the reassembly structure or the packet indicated up
//
nicReferenceReassembly ( pReassembly, "nicFindReassemblyStructure " );
}
REMOTE_NODE_REASSEMBLY_RELEASE_LOCK (pRemoteNode)
REMOTE_NODE_RELEASE_LOCK (pRemoteNode);
if (NdisStatus == NDIS_STATUS_SUCCESS)
{
//
// Update output parameters
//
*ppReassembly = pReassembly;
}
TRACE( TL_T, TM_Recv, ( "<==nicFindReassemblyStructure NdisStatus %x, *ppReassembly %x" , NdisStatus ,*ppReassembly ) );
return NdisStatus ;
}
NDIS_STATUS
nicGetReassemblyStructure (
IN OUT PNDIS1394_REASSEMBLY_STRUCTURE* ppReassembly
)
// Function Description:
// Just allocates a structure and returns
//
// Arguments
// ppReassembly - to point to the newly allocated structure
//
//
//
// Return Value:
// Success - if succeeded
//
// Called with the lock held
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
TRACE( TL_T, TM_Recv, ( "==>nicGetReassemblyStructure ppReassembly %x", ppReassembly ) );
*ppReassembly = ALLOC_NONPAGED (sizeof (NDIS1394_REASSEMBLY_STRUCTURE), MTAG_REASSEMBLY);
if (*ppReassembly == NULL)
{
nicIncrementMallocFailure();
NdisStatus = NDIS_STATUS_FAILURE;
}
else
{
NdisZeroMemory (*ppReassembly, sizeof (NDIS1394_REASSEMBLY_STRUCTURE) );
NdisStatus = NDIS_STATUS_SUCCESS;
(*ppReassembly)->Tag = MTAG_REASSEMBLY;
ReassemblyAllocated++;
}
TRACE( TL_T, TM_Recv, ( " <==nicGetReassemblyStructure NdisStatus %x, pReassembly %x", NdisStatus, *ppReassembly) );
return NdisStatus;
}
VOID
nicFreeReassemblyStructure (
IN PNDIS1394_REASSEMBLY_STRUCTURE pReassembly
)
// Function Description:
// Just Frees the structure and returns
//
// Arguments
// ppReassembly - to point to the newly allocated structure
//
//
// Return Value:
// Success - if succeeded
//
{
TRACE( TL_T, TM_Recv, ( "== nicFreeReassemblyStructure ppReassembly %x", pReassembly ) );
pReassembly->Tag = MTAG_FREED;
nicDereferenceReassembly (pReassembly, "nicFreeReassemblyStructure ");
return;
}
NDIS_STATUS
nicInitializeReassemblyStructure (
IN PNDIS1394_REASSEMBLY_STRUCTURE pReassembly,
IN USHORT Dgl,
IN PREMOTE_NODE pRemoteNode,
IN PVCCB pVc,
IN BUS_OPERATION ReceiveOp
)
// Function Description:
// Goes in and assigns values to all the structures
//
// Arguments
// pReassembly = pReassembly structure all zeroed out,
// Dgl,- Datagram Label to be used in reassembly
// pRemoteNode - pRemoteNode pointing to the sender
//
//
// Return Value:
// Success : - If remote node active and this has been inserted into the remote node's list
// Failure - If remote Node is not active
// Called with the lock held
//
{
BOOLEAN fRemoteNodeActive = FALSE;
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
PADAPTERCB pAdapter = pVc->Hdr.pAF->pAdapter;
TRACE( TL_T, TM_Recv, ( "==> nicInitializeReassemblyStructure pReassembly %x, ReceiveOp %x", pReassembly, ReceiveOp ) );
TRACE( TL_T, TM_Recv, ( " pRemoteNode %x, Dgl %x, pVc %x ", pReassembly, Dgl, pVc ) );
//
// Increment the reassembly count
//
nicReassemblyStarted(pAdapter);
pAdapter->AdaptStats.TempStats.ulMaxOutstandingReassemblies =
max(pAdapter->AdaptStats.TempStats.ulMaxOutstandingReassemblies,
pAdapter->AdaptStats.TempStats.ulNumOutstandingReassemblies);
//
// Dgl - Datagram label. Unique for every reassembly structure gernerated by this local host
//
pReassembly->Dgl = Dgl;
//
// pRemoteNode -> RemoteNode + Dgl are unique for each reassembly structure
//
pReassembly->pRemoteNode = pRemoteNode;
//
// ExpectedFragmentOffset is computed by the Last Fragment's Offset +
// length of fragment. Does not account for gaps in the reassembled packet.
//
pReassembly->ExpectedFragmentOffset = 0;
//
// LastNdisBuffer that was appended to the packet
//
pReassembly->pTailNdisBuffer = NULL;
//
// Packet that is being reassembled
//
pReassembly->pNdisPacket = NULL;
pReassembly->Head.pAddressFifo = NULL;
pReassembly->Tail.pAddressFifo = NULL;
pReassembly->ReceiveOp = ReceiveOp;
pReassembly->pVc = pVc;
//
// Reference the remote node. This will be derefernced when the packet is returned
//
fRemoteNodeActive = (REMOTE_NODE_ACTIVE (pRemoteNode));
TRACE( TL_V, TM_Recv, ( " nicInitializeReassemblyStructure fRemoteNodeActive %x", fRemoteNodeActive) );
if (fRemoteNodeActive == TRUE)
{
//
// REfcount made as the reassembly will happen on the remote node.
// REfcount released when the last fragment is complete
//
nicReferenceRemoteNode (pRemoteNode, "nicInitializeReassemblyStructure");
InsertTailList(&pRemoteNode->ReassemblyList, &pReassembly->ReassemblyListEntry);
//
// Reerence REassembly . Ref removed when this is removed from the Remote node list
//
nicReferenceReassembly (pReassembly, "nicInitializeReassembly" );
}
if (fRemoteNodeActive == FALSE)
{
//
// Temporary assert
//
FREE_NONPAGED (pReassembly);
NdisStatus = NDIS_STATUS_FAILURE;
}
else
{
NdisStatus = NDIS_STATUS_SUCCESS;
}
//
// reference the reassembly for its creation. Dereferenced in the Indicate Packet Code path
//
nicReferenceReassembly (pReassembly, " nicInitializeReassemblyStructure ");
TRACE( TL_T, TM_Recv, ( "<== nicInitializeReassemblyStructure NdisStatus %x, pReassembly%x ", NdisStatus,pReassembly ) );
return NdisStatus;
}
VOID
nicAbortReassembly (
IN PNDIS1394_REASSEMBLY_STRUCTURE pReassembly
)
// Function Description:
//
// Assumes that the Reassembly structure is no longer in the remote node's list
//
// This funciotn will free all allocated NdisBuffers and return all AddressFifo
// elements to the bus driver (or frees them if the VC is closing down).
//
// Arguments
// pReasssembly - Reassembly structure that needs to be freed
//
//
// Return Value:
// None
//
{
PNDIS_BUFFER pNdisBuffer = NULL;
PRECVFIFO_VCCB pRecvFIFOVc = NULL;
PCHANNEL_VCCB pChannelVc = NULL;
PADAPTERCB pAdapter = pReassembly->pVc->Hdr.pAF->pAdapter;
STORE_CURRENT_IRQL;
TRACE( TL_T, TM_Recv, ( "==> nicAbortReassembly pReassembly %x", pReassembly ) );
//
// Free all the ndis buffers and so forth
//
if (pReassembly != NULL)
{
//
// First Chain the reassembly array into a linked so our return functions can deal with it
//
nicChainReassembly (pReassembly);
if (pReassembly->pHeadNdisBuffer != NULL)
{
nicReturnNdisBufferChain(pReassembly->pHeadNdisBuffer, pReassembly->pVc);
}
switch (pReassembly->ReceiveOp)
{
case AddressRange:
{
pRecvFIFOVc = (PRECVFIFO_VCCB) pReassembly->pVc;
//
// Time to return all of our address fifos
//
nicReturnFifoChain (pReassembly->Head.pAddressFifo,
pRecvFIFOVc
);
pReassembly->Head.pAddressFifo = NULL;
break;
}
case IsochReceive:
{
pChannelVc = (PCHANNEL_VCCB)pReassembly->pVc;
nicReturnDescriptorChain ( pReassembly->Head.pIsochDescriptor,
pChannelVc);
pReassembly->Head.pIsochDescriptor = NULL;
break;
}
default:
{
ASSERT (0);
}
}
}
else
{
ASSERT (0);
}
//
// Now deref the reassembly and free it.
//
nicReassemblyAborted (pAdapter);
nicFreeReassemblyStructure (pReassembly);
TRACE( TL_T, TM_Recv, ( "<== nicAbortReassembly pReassembly %x", pReassembly ) );
MATCH_IRQL;
return;
}
NDIS_STATUS
nicDoReassembly (
IN PNIC_RECV_DATA_INFO pRcvInfo,
OUT PNDIS1394_REASSEMBLY_STRUCTURE *ppReassembly,
PBOOLEAN pfReassemblyComplete
)
/*++
Routine Description:
Does the reassembly work .
Allocates an ndisbuffer pointing to the data .
Does In order or out of order reassembly
Arguments:
pRcvInfo - pRcv Information
pReassembly reassmbly structure associated with this fragment
pfReassemblyComplete - Is the REassembly complete
Return Value:
Success - if this fragment was successfully associated with a reassembly structure
--*/
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
PNDIS_BUFFER pNdisBuffer = NULL;
PNDIS1394_REASSEMBLY_STRUCTURE pReassembly = NULL;
BOOLEAN fInOrder = FALSE;
BOOLEAN fNeedToReleaseReassemblyLock = FALSE;
BOOLEAN fReassemblyComplete = FALSE;
PADAPTERCB pAdapter = pRcvInfo->pRemoteNode->pAdapter;
STORE_CURRENT_IRQL;
TRACE( TL_T, TM_Recv, ( "==> nicDoReassembly ppReassembly %x pRcvInfo %x",
ppReassembly, pRcvInfo ) );
do
{
//
// Get an NdisBuffer pointing to the data
//
NdisStatus = nicGetNdisBufferForReassembly( pRcvInfo, &pNdisBuffer);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
//
// If we break from here, the reassmbly will never get completed and the
// garbage collector will eventually free this.
//
pNdisBuffer = NULL;
BREAK (TM_Send, ("nicDoReassembly nicGetNdisBufferForReassembly FAILED" ) );
}
//
// Either there is a reassembly currently going or one will be allocated and initialized
//
NdisStatus = nicFindReassemblyStructure (pRcvInfo->pRemoteNode,
pRcvInfo->Dgl,
pRcvInfo->RecvOp,
(PVCCB)pRcvInfo->pVc,
&pReassembly);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
pReassembly=NULL;
BREAK (TM_Recv, (" nicDoReassembly nicFindReassemblyStructure FAILED"));
}
//
// Now we start doing the actual work . Acquire the
// reassembly lock so no one else can touch the reassembly
//
ASSERT (pReassembly != NULL);
TRACE( TL_V, TM_Recv, ( " ExpectedFragmentOffset %x FragmentHeader Offset %x, ",
pReassembly->ExpectedFragmentOffset , pRcvInfo->FragmentOffset) );
//
// Code expects that if the reassembly is not Null, then the lock is acquired.
//
REASSEMBLY_ACQUIRE_LOCK (pReassembly);
fNeedToReleaseReassemblyLock = TRUE;
if (REASSEMBLY_ACTIVE (pReassembly) == FALSE)
{
//
// Drop the reassembly, as this structure is about to be freed
//
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
//
// This is the new reassembly scheme, which uses a table and does out of order and inorder
// reassembly
//
NdisStatus = nicInsertFragmentInReassembly (pReassembly,
pRcvInfo);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
//
// Do not assert
//
TRACE (TL_V, TM_Reas, ("nicDoReassembly nicInsertFragmentInReassembly FAILED") );
break;
}
fReassemblyComplete = pReassembly->fReassemblyComplete;
} while (FALSE);
//
// Release the reassembly lock (if acquired)
//
if (fNeedToReleaseReassemblyLock == TRUE)
{
REASSEMBLY_RELEASE_LOCK (pReassembly);
if (fReassemblyComplete == TRUE)
{
//
// Dereference the remote node as we are removing the reassembly from the remote node
//
nicDereferenceReassembly (pReassembly, "nicInsertFragmentInReassembly " );
//
// now dereference the remote node. ref was added when the reassembly was
// inserted into the remote node's list
//
nicDereferenceRemoteNode(pReassembly->pRemoteNode, "nicInsertFragmentInReassembly ");
pReassembly->pRemoteNode = NULL;
}
}
//
// Clean up time. First handle the failure case.
// If reassembly is != NULL, then free the lock
// and free the reassembly structure
//
if (NdisStatus == NDIS_STATUS_SUCCESS)
{
*ppReassembly = pReassembly;
//
// Queue a Reassembly timer, if reassembly was not complete
//
if (fReassemblyComplete == FALSE)
{
nicQueueReassemblyTimer(pAdapter, FALSE);
}
}
if (NdisStatus != NDIS_STATUS_SUCCESS )
{
//
// Do not touch the reassembly.
//
if (pNdisBuffer)
{
NdisFreeBuffer (pNdisBuffer);
}
//
// Return NULL as output. The Reassembly structure is
// in the remote node's list. The timer routine will pick it up
//
//
// Deref the ref made the REassembly was found/
//
if (pReassembly != NULL)
{
nicDereferenceReassembly (pReassembly, "nicDoReassembly - failure" );
}
*ppReassembly = pReassembly = NULL;
}
*pfReassemblyComplete = fReassemblyComplete;
TRACE( TL_T, TM_Recv, ( "<== nicDoReassembly NdisStatus %x, , pReassembly %x, Complete %x", NdisStatus, *ppReassembly, *pfReassemblyComplete ) );
MATCH_IRQL;
return NdisStatus;
}
NDIS_STATUS
nicGetNdisBufferForReassembly(
IN PNIC_RECV_DATA_INFO pRcvInfo,
OUT PNDIS_BUFFER *ppNdisBuffer
)
// Function Description:
// This function gets an Ndis Buffer that points to the start of the data
// that the Mdl points to. The Data starts from the point after the
// Fragmentation Header
//
// If this is the First fragment, then 32 bytes of the fragment header are also
// copied to make room for the header that the ARP module expects
// Arguments
//
// Return Value:
// Success - if the mem alloc succeeded
// NdisBuffer - Buffer pointing ot the data ,
// Data Length Length of the NdisBuffer
// StartValidData - StartVa of the NdisBuffer
//
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
PVOID pStartValidData = NULL;
ULONG ulValidDataLength = 0;
PNDIS1394_FRAGMENT_HEADER pNonByteSwappedFragmentHeader = NULL;
USHORT Dgl;
PNDIS_BUFFER pNdisBuffer = NULL;
ULONG IsochPrefix = ISOCH_PREFIX_LENGTH;
PPACKET_FORMAT pIndicatedData = NULL;
TRACE( TL_T, TM_Recv, ( "==> nicGetNdisBufferForReassembly ") );
do
{
//
// Get a pointer to the start of the data, ie. it should point past the encapsulation header
//
pStartValidData = (PVOID)((ULONG_PTR)pRcvInfo->pEncapHeader + sizeof(NDIS1394_FRAGMENT_HEADER));
ulValidDataLength = pRcvInfo->DataLength - sizeof (NDIS1394_FRAGMENT_HEADER);
//
// if this is the first fragment, then leave room for the Unfragmented header that will need
// to be added before sending it up to the IP module
//
if (pRcvInfo->fFirstFragment == TRUE)
{
ULONG ExtraData = (sizeof(NDIS1394_FRAGMENT_HEADER) - sizeof (NDIS1394_UNFRAGMENTED_HEADER)) ;
pStartValidData = (PVOID)((ULONG_PTR)pStartValidData - ExtraData);
ulValidDataLength += ExtraData ;
}
NdisStatus = nicGetNdisBuffer ( ulValidDataLength,
pStartValidData,
&pNdisBuffer);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
BREAK (TM_Recv, ( "nicGetNdisBufferForReassembly: nicGetNdisBuffer Failed" ) );
}
nicIncRecvBuffer(pRcvInfo->pVc->Hdr.VcType == NIC1394_RecvFIFO);
*ppNdisBuffer = pNdisBuffer;
pRcvInfo->pNdisBuffer = pNdisBuffer;
pRcvInfo->pNdisBufferData = pStartValidData;
}while (FALSE);
TRACE( TL_T, TM_Recv, ( "<== nicGetNdisBufferForReassembly NdisStatus %x, *ppNdisbuffer %x, pStartValidData%x ,ulValidDataLength %x",
NdisStatus, *ppNdisBuffer, pStartValidData, ulValidDataLength) );
return NdisStatus;
}
VOID
nicAddUnfragmentedHeader (
IN PNDIS1394_REASSEMBLY_STRUCTURE pReassembly,
IN PVOID pEncapHeader
)
/*++
Routine Description:
Its purpose is to add the fragment header that arp expects.
There is room in the Head NdisBuffer to do this
We own the buffer, so we can manipulate the data
Arguments:
pReassembty Structure - contains all the necessary reasembly info
pEncapHeader - Pointer to where th Unfragmented header will be stored
Return Value:
None
--*/
{
PNDIS1394_UNFRAGMENTED_HEADER pHeader = NULL;
ASSERT (sizeof(NDIS1394_UNFRAGMENTED_HEADER) == sizeof(ULONG));
TRACE( TL_T, TM_Recv, ( "==> nicAddUnfragmentedHeader %x, pEncapHeader %x", pReassembly, pEncapHeader) );
pHeader = (PNDIS1394_UNFRAGMENTED_HEADER) pEncapHeader;
//
// Now we add the unfragmented header. first zero it, then add the approriate values
//
pHeader->HeaderUlong = 0;
pHeader->u.FH_lf = lf_Unfragmented;
pHeader->u.FH_EtherType = pReassembly->EtherType;
//
// Convert the header to network order and indicate it up.
//
pHeader->HeaderUlong = SWAPBYTES_ULONG (pHeader->HeaderUlong);
TRACE( TL_T, TM_Recv, ( "<== nicAddUnfragmentedHeader pReasembly %x, pHeader %x ", pReassembly, pHeader->HeaderUlong) );
return;
}
VOID
nicAbortReassemblyList (
PLIST_ENTRY pToBeFreedList
)
// Function Description:
// Walks the list and calls nicAbortReassembly on each structure
//
// Does not do any lock or refcount work
//
//
// Arguments
// pToBeFreedList - list of reassembly structures that are going to be freed
//
//
// Return Value:
// None
//
//
//
{
PLIST_ENTRY pReassemblyList = ListNext (pToBeFreedList);
PNDIS1394_REASSEMBLY_STRUCTURE pReassembly = NULL;
TRACE( TL_T, TM_Recv, ( "==> nicAbortReassemblyList pToBeFreedList %x", pToBeFreedList));
while (pReassemblyList != pToBeFreedList)
{
pReassembly = CONTAINING_RECORD(pReassemblyList,
NDIS1394_REASSEMBLY_STRUCTURE,
ReassemblyListEntry);
pReassemblyList = ListNext(pReassemblyList);
TRACE( TL_T, TM_Recv, ( " Aborting pReassembly %x", pReassembly));
nicAbortReassembly(pReassembly);
}
}
VOID
nicFreeAllPendingReassemblyStructures(
IN PADAPTERCB pAdapter
)
// Function Description:
// When we are notified of a reset we need to go and invalidate all
// reassemblies
//
// This will always be called from the Reset code path . and will be at dispatch
// It will clear out all the remote node reaassembly and mark them as aborted.
// The Timer routine will then pick them up and free it
//
// Does not actually free anything. Just marks them as aborted
//
// Arguments
// pAdapter - pAdapter that has been resets
//
//
// Return Value:
//
//
{
PLIST_ENTRY pRemoteNodeList = NULL;
PREMOTE_NODE pRemoteNode = NULL;
PLIST_ENTRY pReassemblyList = NULL;
PNDIS1394_REASSEMBLY_STRUCTURE pReassembly = NULL;
ULONG NumMarkedAborted = 0;
TRACE( TL_T, TM_Recv, ( "==> nicFreeAllPendingReassemblyStructures "));
pRemoteNodeList = ListNext(&pAdapter->PDOList);
ADAPTER_ACQUIRE_LOCK (pAdapter);
//
// Walking through the remote nodes
//
while (pRemoteNodeList != &pAdapter->PDOList)
{
pRemoteNode = CONTAINING_RECORD(pRemoteNodeList,
REMOTE_NODE,
linkPdo);
pRemoteNodeList = ListNext (pRemoteNodeList);
//
// Reference the remote node, so we can guarantee its presence
//
if (REMOTE_NODE_ACTIVE (pRemoteNode)== FALSE)
{
//
// The remote node is going away. Skip this remote node
//
continue;
}
if (nicReferenceRemoteNode (pRemoteNode, "nicFreeAllPendingReassemblyStructures" )== FALSE )
{
//
// The remote node is going away. Skip this remote node
//
continue;
}
//
// Now walking through all the reassembly structures on that remote node
//
REMOTE_NODE_REASSEMBLY_ACQUIRE_LOCK(pRemoteNode);
pReassemblyList = ListNext (&pRemoteNode->ReassemblyList);
while (pReassemblyList != &pRemoteNode->ReassemblyList)
{
pReassembly = CONTAINING_RECORD (pReassemblyList,
NDIS1394_REASSEMBLY_STRUCTURE,
ReassemblyListEntry);
pReassemblyList = ListNext(pReassemblyList);
//
// If the reassembly has not been touched since the last timer it needs to be freed.
// Other threads can ask us to free the reassembly by setting the aborted flag
//
if (REASSEMBLY_TEST_FLAG (pReassembly, REASSEMBLY_ABORTED) == FALSE);
{
REASSEMBLY_SET_FLAG (pReassembly, REASSEMBLY_ABORTED);
NdisInterlockedIncrement (&NumMarkedAborted);
}
}
REMOTE_NODE_REASSEMBLY_RELEASE_LOCK(pRemoteNode);
nicDereferenceRemoteNode (pRemoteNode, "nicFreeAllPendingReassemblyStructures" );
}
ADAPTER_RELEASE_LOCK (pAdapter);
TRACE( TL_T, TM_Recv, ( "<== nicFreeAllPendingReassemblyStructures NumMarkedAborted %x"));
}
ULONG
nicReferenceReassembly (
IN PNDIS1394_REASSEMBLY_STRUCTURE pReassembly,
PCHAR pString
)
{
ULONG Ref;
Ref = NdisInterlockedIncrement (&pReassembly->Ref);
TRACE( TL_V, TM_Ref, ( "**nicReferenceReassembly pReassembly %x, to %d, %s ", pReassembly, pReassembly->Ref, pString) );
return Ref;
}
ULONG
nicDereferenceReassembly (
IN PNDIS1394_REASSEMBLY_STRUCTURE pReassembly,
PCHAR pString
)
{
ULONG Ref;
Ref = NdisInterlockedDecrement (&pReassembly->Ref);
TRACE( TL_V, TM_Ref, ( "**nicDereferenceReassembly pReassembly %x, to %d, %s ", pReassembly, pReassembly->Ref, pString) );
if ( Ref ==0 )
{
TRACE( TL_V, TM_Ref, ( "**FREEING pReassembly %x, ", pReassembly) );
FREE_NONPAGED (pReassembly);
}
return Ref;
}
VOID
nicIndicateNdisPacketToNdis (
PNDIS_PACKET pPacket,
PVCCB pVc,
PADAPTERCB pAdapter
)
// Function Description:
// This is to be used to indicate packets to NDIS .
// In Win 9x indications will go through a timer routine
//
// Assumption - There will be only one packet in the array
//
// Arguments
// ppPacket - Packet Array
// pVc -Vc
// MiniportAdapterhandle
// Return Value:
// None
//
{
NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
PRSVD pRsvd = NULL;
PNDIS_MINIPORT_TIMER pRcvTimer= NULL;
PINDICATE_RSVD pIndicateRsvd = NULL;
TRACE( TL_T, TM_Recv, ( "==> nicIndicateNdisPacketToNdis pPacket %x, pVc %x, pAdapter %x ",
pPacket , pVc, pAdapter));
TRACE (TL_V, TM_Reas, ("Indicating packet " ));
do
{
NdisInterlockedIncrement (&pAdapter->AdaptStats.ulRcvOk);
nicDumpPkt (pPacket,"Indicating Rcv ");
ASSERT (pPacket != NULL);
//
// Set up the Context for the indication
//
pRsvd =(PRSVD)(pPacket->ProtocolReserved);
pIndicateRsvd = &pRsvd->IndicateRsvd;
#if QUEUED_PACKETS
pIndicateRsvd->Tag = NIC1394_TAG_QUEUED;
NdisStatus = nicQueueReceivedPacket( pPacket, pVc, pAdapter);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
//
// We were not able to queue the packet.
// Call the return packet handler
//
ASSERT (0);
NicReturnPacket (pAdapter, pPacket);
}
#else // QUEUED_PACKETS
TRACE( TL_V, TM_Recv, ( " Indicating without timer"));
//
// Update the tag increment counter and indicate rcv
//
pIndicateRsvd->Tag = NIC1394_TAG_INDICATED;
ASSERT (pPacket != NULL);
nicIncrementRcvVcPktCount(pVc,pPacket);
NdisMCoIndicateReceivePacket(pVc->Hdr.NdisVcHandle, &pPacket, 1);
ASSERT (pAdapter->MiniportAdapterHandle != NULL);
NdisMCoReceiveComplete(pAdapter->MiniportAdapterHandle);
#endif //QUEUED_PACKETS
}while (FALSE);
TRACE( TL_T, TM_Recv, ( "<==nicIndicateNdisPacketToNdis %x"));
}
NDIS_STATUS
nicValidateRecvDataIsoch(
IN PMDL pMdl,
IN PISOCH_DESCRIPTOR pIsochDescriptor,
IN PVCCB pVc,
OUT PNIC_RECV_DATA_INFO pRcvInfo
)
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
do
{
NODE_ADDRESS NodeAddress;
PGASP_HEADER pGaspHeader;
//
// Isoch header is already byte swapped
//
pRcvInfo->DataLength = pRcvInfo->p1394Data->IsochReceiveFragmented.IsochHeader.IH_Data_Length;
if (pRcvInfo->DataLength <= (UINT)FIELD_OFFSET(DATA_FORMAT,IsochReceiveFragmented.Data))
{
// Too small. Note that for simplicitly we check for the
// fragmented case.
//
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
pRcvInfo->fGasp = TRUE;
//
// The total length of the data indicated by the bus driver
//
pRcvInfo->Length1394 = pRcvInfo->DataLength + sizeof (ISOCH_HEADER) + sizeof(ULONG); // Account for the prefix and isoch header
//
// The valid data does not include the gasp header
//
pRcvInfo->DataLength -= sizeof (GASP_HEADER);
pRcvInfo->NdisPktContext.pIsochContext = pIsochDescriptor;
pRcvInfo->pPacketPool = &((PCHANNEL_VCCB) pVc)->PacketPool;
//
// Get the source Info out.
//
//
// pRcvInfo->p1394Data points to the start of the Mdl's VA that was indicated by the bus driver
//
pGaspHeader = &pRcvInfo->p1394Data->IsochReceiveFragmented.GaspHeader;
//
// Byte swap the Gasp Header in the actual data. we own the buffer, so we can byte swap it
//
pGaspHeader->FirstQuadlet.GaspHeaderHigh = SWAPBYTES_ULONG(pGaspHeader->FirstQuadlet.GaspHeaderHigh);
pGaspHeader->SecondQuadlet.GaspHeaderLow = SWAPBYTES_ULONG(pGaspHeader->SecondQuadlet.GaspHeaderLow);
TRACE (TL_V, TM_Recv, (" Gasp Hi %x, Gasp Lo %x.",
pGaspHeader->FirstQuadlet.GaspHeaderHigh,
pGaspHeader->SecondQuadlet.GaspHeaderLow ) );
pRcvInfo->pGaspHeader = pGaspHeader;
pRcvInfo->SourceID = pGaspHeader->FirstQuadlet.u1.GH_NodeAddress.NA_Node_Number;
pRcvInfo->SourceID = pGaspHeader->FirstQuadlet.u1.GH_NodeAddress.NA_Node_Number;
NdisStatus = NDIS_STATUS_SUCCESS;
} while (FALSE);
return NdisStatus;
}
NDIS_STATUS
nicValidateRecvDataFifo(
IN PMDL pMdl,
IN PNOTIFICATION_INFO pFifoContext,
IN PVCCB pVc,
OUT PNIC_RECV_DATA_INFO pRcvInfo
)
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
do
{
pRcvInfo->DataLength = pFifoContext->nLength;
if (pRcvInfo->DataLength <= (UINT)FIELD_OFFSET(DATA_FORMAT, AsyncWriteFragmented.Data))
{
// Too small. Note that for simplicitly we check for the
// fragmented case.
//
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
pRcvInfo->fGasp = FALSE;
//
//In Fifo receives the DataLength is equal to the total amount of data indicated by the bus driver
//
pRcvInfo->Length1394 = pRcvInfo->DataLength;
pRcvInfo->NdisPktContext.pFifoContext = pFifoContext ->Fifo;
pRcvInfo->pPacketPool = &((PRECVFIFO_VCCB) pVc)->PacketPool;
pRcvInfo->SourceID = ((PASYNC_PACKET)pFifoContext->RequestPacket)->AP_Source_ID.NA_Node_Number;
NdisStatus = NDIS_STATUS_SUCCESS;
}while (FALSE);
return NdisStatus;
}
NDIS_STATUS
nicValidateRecvData(
IN PMDL pMdl,
IN BUS_OPERATION RecvOp,
IN PVOID pIndicatedStruct,
IN PVCCB pVc,
OUT PNIC_RECV_DATA_INFO pRcvInfo
)
/*++
Routine Description:
This routine verifies that the length is not too small
This routine initializes the RecvDataInfo for the default (unfragmented case).
If the data is unfragmented the main recv routine will then call the Fragmented version of this routine
This initializes the length and StartData and fGasp fields of the struct only
Arguments:
pMdl - Mdl that was indicated up by the bus driver
RecvOp - Is this part of isoch callback, or AddrRange Callback
pIndicatedStruct - NotificationInfo or IsochDescriptor
pRcvInfo - Recv Structure that will be updated
Return Value:
Success - if all the operations succeeded
--*/
{
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
PDATA_FORMAT pData = NULL;
NDIS1394_UNFRAGMENTED_HEADER EncapHeader;
PNDIS1394_UNFRAGMENTED_HEADER pEncapHeader = NULL;
NDIS1394_FRAGMENT_LF lf;
ULONG UlongLf;
NdisZeroMemory (pRcvInfo , sizeof (NIC_RECV_DATA_INFO) );
TRACE( TL_T, TM_Recv, ( "==>nicValidateRecvData pMdl %x, RecvOp %x, pIndicatedStruct %x, pRcvInfo %x",
pMdl, RecvOp , pIndicatedStruct, pRcvInfo));
ASSERT (RecvOp == IsochReceive || RecvOp == AddressRange);
pRcvInfo->RecvOp = RecvOp;
pRcvInfo->pVc = pVc;
do
{
if (pMdl == NULL)
{
NdisStatus = NDIS_STATUS_FAILURE;
BREAK (TM_Recv, ("nicValidateRecvData , no Mdl present") );
}
pRcvInfo->p1394Data = (PPACKET_FORMAT)NIC_GET_SYSTEM_ADDRESS_FOR_MDL (pMdl);
if (pRcvInfo->p1394Data == NULL)
{
NdisStatus = NDIS_STATUS_RESOURCES;
break;
}
//
// Check minimum valid packet size . Checks whether the data length that was passed to us includes
// at least the first byte of data
//
if (RecvOp == IsochReceive)
{
NdisStatus = nicValidateRecvDataIsoch (pMdl,
(PISOCH_DESCRIPTOR)pIndicatedStruct,
pVc,
pRcvInfo
);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
// Validation failed . exit
break;
}
//
// Get to the Encap header. Should be at the same position for Fragmented and nonfragmented
//
pEncapHeader = &pRcvInfo->p1394Data->IsochReceiveNonFragmented.NonFragmentedHeader;
}
else
{
NdisStatus = nicValidateRecvDataFifo(pMdl,(PNOTIFICATION_INFO)pIndicatedStruct,pVc,pRcvInfo);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
// Failure
break;
}
pEncapHeader = &pRcvInfo->p1394Data->AsyncWriteNonFragmented.NonFragmentedHeader;
}
//
// Byteswap Unfrag Header into a local variable
//
//EncapHeader.HeaderUlong = SwapBytesUlong (pEncapHeader->HeaderUlong);
EncapHeader.HeaderUlong = SWAPBYTES_ULONG (pEncapHeader->HeaderUlong);
EncapHeader.HeaderUlong = pEncapHeader->HeaderUlong & 0x000000C0;
EncapHeader.HeaderUlong = EncapHeader.HeaderUlong >> 6;
pRcvInfo->lf = EncapHeader.HeaderUlong ;
//
// Update the lf
//
pRcvInfo->lf = EncapHeader.HeaderUlong;
TRACE (TL_V, TM_Reas,("Header %x\n",pRcvInfo->lf ) );
ASSERT (EncapHeader.HeaderUlong <= lf_InteriorFragment);
if (pRcvInfo->lf != lf_Unfragmented)
{
pRcvInfo->fFragmented = TRUE;
}
else
{
pRcvInfo->fFragmented = FALSE;
}
if (pRcvInfo->DataLength > pVc->Hdr.MTU)
{
//
// This cannot belong to us
//
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
NdisStatus = NDIS_STATUS_SUCCESS;
pRcvInfo->pEncapHeader = (PVOID)pEncapHeader;
//
// Spew out all the information discovered
//
TRACE ( TL_V, TM_Recv, ( "lf %x, p1394Data %x, Length1394 %x, DataLength %x, pEncapHeader %x " ,
pRcvInfo->lf,
pRcvInfo->p1394Data,
pRcvInfo->Length1394,
pRcvInfo->DataLength,
pRcvInfo->pEncapHeader ) );
} while (FALSE);
TRACE( TL_T, TM_Recv, ( "<==nicValidateRecvData %x", NdisStatus));
return NdisStatus;
}
VOID
nicInitRecvDataFragmented (
IN PMDL pMdl,
IN BUS_OPERATION RecvOp,
IN PVOID pIndicatedStruct,
OUT PNIC_RECV_DATA_INFO pRcvInfo
)
/*++
Routine Description:
The routine will extract from the packet all the information that is required for reassembly
and store it in the pRcvInfo
Arguments:
pMdl - Indicated Mdl
RecvOp - IsochReceive ot AddressRange Callback
pIndicatedStruct - IsochDesc or Address Fifo
pRcvInfo - output structure
Return Value:
None
--*/
{
PNOTIFICATION_INFO pNotificationInfo = NULL;
PGASP_HEADER pGaspHeader = NULL;
PNDIS1394_FRAGMENT_HEADER pEncapHeader = NULL;
TRACE( TL_T, TM_Recv, ( "==> nicInitRecvDataFragmented pMdl, %x, RecvOp %x, pIndicatedStruct %x, pRcvInfo %x",
pMdl, RecvOp, pIndicatedStruct, pRcvInfo));
do
{
pRcvInfo->pMdl = pMdl;
if (RecvOp == IsochReceive)
{
pRcvInfo->NdisPktContext.pIsochContext = (PISOCH_DESCRIPTOR) pIndicatedStruct;
}
else
{
pNotificationInfo = (PNOTIFICATION_INFO) pIndicatedStruct;
pRcvInfo->NdisPktContext.pFifoContext = pNotificationInfo->Fifo;
}
//
// Now byte swap the fragment header so it can be correctly interpreted
//
pEncapHeader = (PNDIS1394_FRAGMENT_HEADER )pRcvInfo->pEncapHeader;
pRcvInfo->FragmentHeader.u.FH_High = SWAPBYTES_ULONG(pEncapHeader->u.FH_High);
pRcvInfo->FragmentHeader.u1.FH_Low = SWAPBYTES_ULONG(pEncapHeader->u1.FH_Low);
//
// Now get the Dgl
//
pRcvInfo->Dgl = (USHORT)pRcvInfo->FragmentHeader.u1.SecondQuadlet.FH_dgl;
if (pRcvInfo->lf == lf_FirstFragment)
{
pRcvInfo->fFirstFragment = TRUE;
pRcvInfo->EtherType = pRcvInfo->FragmentHeader.u.FirstQuadlet_FirstFragment.FH_EtherType;
pRcvInfo->FragmentOffset = 0;
}
else
{
pRcvInfo->fFirstFragment = FALSE ;
pRcvInfo->FragmentOffset = pRcvInfo->FragmentHeader.u.FirstQuadlet.FH_fragment_offset;
}
pRcvInfo->BufferSize = pRcvInfo->FragmentHeader.u.FirstQuadlet.FH_buffersize ;
//
// Spew out all the information that has been found
//
TRACE ( TL_V, TM_Recv, (" SourceId %x, FragHead Hi %x, FragHead Lo %x, Dgl %x, fFirstFragment %x",
pRcvInfo->SourceID,
pRcvInfo->FragmentHeader.u.FH_High,
pRcvInfo->FragmentHeader.u1.FH_Low ,
pRcvInfo->Dgl,
pRcvInfo->fFirstFragment ) );
TRACE ( TL_V, TM_Recv, (" Fragment Offset %x, bufferSize %x", pRcvInfo->FragmentOffset, pRcvInfo->BufferSize));
ASSERT (pRcvInfo->SourceID < 64);
} while (FALSE);
TRACE( TL_T, TM_Recv, ( "<==nicInitRecvDataFragmented " ));
}
NDIS_STATUS
nicInsertFragmentInReassembly (
PNDIS1394_REASSEMBLY_STRUCTURE pReassembly,
PNIC_RECV_DATA_INFO pRcvInfo
)
/*++
Routine Description:
Checks for over laps and if valid then copies current fragment
into the table
This function does the validation for overlaps
Arguments:
PNDIS1394_REASSEMBLY_STRUCTURE pReassembly,
PNDIS_BUFFER pNdisBuffer,
PMDL pMdl,
PVOID pIndicatedStructure,
ULONG FragOffset,
ULONG IPLength
Return Value:
--*/
{
NDIS_STATUS NdisStatus = NDIS_STATUS_SUCCESS;
BOOLEAN fFragPositionFound = FALSE;
ULONG FragmentNum = 0;
BOOLEAN Completed = FALSE;
PNDIS_BUFFER pNdisBuffer = pRcvInfo->pNdisBuffer;
PMDL pMdl = pRcvInfo->pMdl;
PVOID pIndicatedStructure = pRcvInfo->NdisPktContext.pCommon;
ULONG FragOffset = pRcvInfo->FragmentOffset;
ULONG IPLength = pRcvInfo->DataLength - sizeof (NDIS1394_FRAGMENT_HEADER);
TRACE( TL_T, TM_Recv, ( "==> nicInsertFragmentInReassembly " ));
do
{
if (pReassembly->BufferSize != 0 &&
FragOffset >= pReassembly->BufferSize )
{
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
//
// First Find the correct entry in the frag table.
//
nicFindInsertionPosition (pReassembly,
FragOffset,
IPLength,
&FragmentNum);
if (pReassembly->FragTable[FragmentNum].IPLength != 0)
{
//
// we must copy the current fragments descriptors in the table
// so as not to overwrite the table
//
LONG OffsetIndex =0;
//
// First lets check for overlaps. Do we overlap the last fragment.
// At this point, FragmentNum contains the record for the
// next fragment in the reassembly
//
if (FragmentNum != 0)
{
if (pReassembly->FragTable[FragmentNum-1].Offset + pReassembly->FragTable[FragmentNum-1].IPLength > FragOffset)
{
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
}
//
// Do we overlap the next fragment
//
if (FragmentNum < pReassembly->MaxOffsetTableIndex)
{
if (FragOffset + IPLength > pReassembly->FragTable[FragmentNum].Offset )
{
NdisStatus = NDIS_STATUS_FAILURE;
break;
}
}
//
// Now make room for this fragment
//
OffsetIndex = pReassembly->MaxOffsetTableIndex ;
//
// Signed compare and move the records ahead by one
//
while (OffsetIndex >= (LONG)FragmentNum)
{
pReassembly->FragTable[OffsetIndex+1].Offset = pReassembly->FragTable[OffsetIndex].Offset ;
pReassembly->FragTable[OffsetIndex+1].IPLength = pReassembly->FragTable[OffsetIndex].IPLength;
pReassembly->FragTable[OffsetIndex+1].pMdl = pReassembly->FragTable[OffsetIndex].pMdl;
pReassembly->FragTable[OffsetIndex+1].pNdisBuffer= pReassembly->FragTable[OffsetIndex].pNdisBuffer;
pReassembly->FragTable[OffsetIndex+1].pNdisBuffer= pReassembly->FragTable[OffsetIndex].pNdisBuffer;
pReassembly->FragTable[OffsetIndex+1].IndicatedStructure.pCommon = pReassembly->FragTable[OffsetIndex].IndicatedStructure.pCommon ;
pReassembly->FragTable[OffsetIndex+1].FragHeader = pReassembly->FragTable[OffsetIndex].FragHeader;
OffsetIndex --;
}
}
pNdisBuffer->Next = NULL;
pMdl->Next = NULL;
//
// Copy the current fragment into the table
//
pReassembly->FragTable[FragmentNum].Offset = FragOffset;
pReassembly->FragTable[FragmentNum].IPLength = IPLength;
pReassembly->FragTable[FragmentNum].pNdisBuffer = pNdisBuffer;
pReassembly->FragTable[FragmentNum].pMdl = pMdl;
pReassembly->FragTable[FragmentNum].FragHeader = pRcvInfo->FragmentHeader;
if (pReassembly->ReceiveOp == IsochReceive)
{
pReassembly->FragTable[FragmentNum].IndicatedStructure.pCommon = &((PISOCH_DESCRIPTOR)pIndicatedStructure)->DeviceReserved[IsochNext];
}
else
{
pReassembly->FragTable[FragmentNum].IndicatedStructure.pFifo = (PADDRESS_FIFO)pIndicatedStructure;
}
pReassembly->BytesRecvSoFar += IPLength;
//
// Now increment the Max offset
//
pReassembly->MaxOffsetTableIndex ++;
if (pReassembly->BufferSize == 0)
{
pReassembly->BufferSize = pRcvInfo->BufferSize;
}
//
// Add the unfragmented header here as we have to extract the EtherType here
//
if (pRcvInfo->fFirstFragment == TRUE)
{
pReassembly->EtherType = (USHORT)pRcvInfo->EtherType;
nicAddUnfragmentedHeader (pReassembly, pRcvInfo->pNdisBufferData );
}
if (pReassembly->BytesRecvSoFar == pReassembly->BufferSize + 1)
{
nicChainReassembly (pReassembly);
pReassembly->fReassemblyComplete = TRUE;
RemoveEntryList (&pReassembly->ReassemblyListEntry);
//
// These references are done in nicDoReassembly AFTER the lock has been freed
//
//
// Dereference the remote node as we are removing the reassembly from the remote node
//
//nicDereferenceReassembly (pReassembly, "nicInsertFragmentInReassembly " );
//
// now dereference the remote node. ref was added when the reassembly was
// inserted into the remote node's list
//
//nicDereferenceRemoteNode(pReassembly->pRemoteNode, "nicInsertFragmentInReassembly ");
//pReassembly->pRemoteNode = NULL;
}
} while (FALSE);
TRACE( TL_T, TM_Recv, ( "<== nicInsertFragmentInReassembly Status %x, Complete ", NdisStatus , pReassembly->fReassemblyComplete ));
return NdisStatus;
}
VOID
nicFindInsertionPosition (
PNDIS1394_REASSEMBLY_STRUCTURE pReassembly,
ULONG FragOffset,
ULONG IPLength,
PULONG pFragmentNum
)
{
ULONG FragmentNum = 0;
do
{
//
// First Do quick checks for Inorder reassembly
//
//
// Is it the first arrived fragment
//
if (pReassembly->MaxOffsetTableIndex == 0 ||
FragOffset < pReassembly->FragTable[0].Offset +pReassembly->FragTable[0].IPLength )
{
FragmentNum = 0;
break;
}
//
// Do we need to insert it in the last position
//
if ((pReassembly->FragTable[pReassembly->MaxOffsetTableIndex-1].Offset +
pReassembly->FragTable[pReassembly->MaxOffsetTableIndex-1].IPLength ) <=
FragOffset)
{
FragmentNum = pReassembly->MaxOffsetTableIndex;
break;
}
//
// Now walk the table and try to find the correct offset
// We know there is atleast one entry and the current fragment
// goes is not the last entry
//
while ( FragmentNum != pReassembly->MaxOffsetTableIndex)
{
if (FragOffset < pReassembly->FragTable[FragmentNum].Offset)
{
//
//We have found the Correct position
//
break;
}
FragmentNum++;
}
ASSERT (FragmentNum != pReassembly->MaxOffsetTableIndex);
} while (FALSE);
*pFragmentNum = FragmentNum;
}
VOID
nicChainReassembly (
IN PNDIS1394_REASSEMBLY_STRUCTURE pReassembly
)
/*++
Routine Description:
Chains the mdl, ndis buffers and indicated structures
This can be called from abort on the reasssembly complete code path
Arguments:
preassembly
Return Value:
--*/
{
ULONG i = 0;
//
// first chain all fragments save the last one
//
while (i< pReassembly->MaxOffsetTableIndex-1)
{
PFRAGMENT_DESCRIPTOR pCurr = & pReassembly->FragTable[i];
PFRAGMENT_DESCRIPTOR pNext = & pReassembly->FragTable[i+1];
ASSERT (pNext->IPLength != 0);
pCurr->pMdl->Next = pNext->pMdl;
pCurr->pNdisBuffer->Next = pNext->pNdisBuffer;
pCurr->IndicatedStructure.pListEntry->Next = pNext->IndicatedStructure.pListEntry;
i++;
}
//
// Clear the next pointers for the last descriptor
//
{
PFRAGMENT_DESCRIPTOR pLast = & pReassembly->FragTable[pReassembly->MaxOffsetTableIndex-1];
pLast->pMdl->Next = NULL;
pLast->pNdisBuffer->Next = NULL;
pLast->IndicatedStructure.pListEntry->Next = NULL;
}
pReassembly->pHeadNdisBuffer = pReassembly->FragTable[0].pNdisBuffer;
pReassembly->pHeadMdl = pReassembly->FragTable[0].pMdl;
if (pReassembly->ReceiveOp == IsochReceive)
{
//
// The pointer currently has the Next field. But the Head expects that start of an IsochDescriptor
//
pReassembly->Head.pCommon = CONTAINING_RECORD (pReassembly->FragTable[0].IndicatedStructure.pCommon,
ISOCH_DESCRIPTOR,
DeviceReserved[IsochNext] );
}
else
{
pReassembly->Head.pCommon = pReassembly->FragTable[0].IndicatedStructure.pCommon;
}
}
NDIS_STATUS
nicInitSerializedReceiveStruct(
PADAPTERCB pAdapter
)
/*++
Routine Description:
Initialize the Recv serialization structure
Arguments:
padapter
Return Value:
Success
--*/
{
NdisZeroMemory (&pAdapter->SerRcv, sizeof(pAdapter->SerRcv));
InitializeListHead(&pAdapter->SerRcv.Queue);
NdisMInitializeTimer (&pAdapter->SerRcv.Timer,
pAdapter->MiniportAdapterHandle,
RcvIndicateTimer ,
pAdapter);
return NDIS_STATUS_SUCCESS;
}
VOID
nicDeInitSerializedReceiveStruct(
PADAPTERCB pAdapter
)
/*++
Routine Description:
Deinits the Recv Init routine
Arguments:
Return Value:
--*/
{
}
NDIS_STATUS
nicQueueReceivedPacket(
PNDIS_PACKET pPacket,
PVCCB pVc,
PADAPTERCB pAdapter
)
/*++
Routine Description:
Queues a receive packet in the adapter's receive packet queue.
If no timer routins is in operation or already set, then this thread
will set the timer
Arguments:
Self explanatory
Return Value:
--*/
{
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
BOOLEAN fSetTimer = FALSE;
do
{
PRSVD pRsvd;
PINDICATE_RSVD pIRsvd;
extern ULONG TotRecvs;
TotRecvs++;
pRsvd =(PRSVD)((pPacket)->ProtocolReserved);
pIRsvd = &pRsvd->IndicateRsvd;
pIRsvd->pPacket = pPacket;
pIRsvd->pVc = pVc;
pIRsvd->pAdapter = pAdapter;
ADAPTER_ACQUIRE_LOCK (pAdapter);
//
// Set up a tag used to track the packet
//
pIRsvd->Tag = NIC1394_TAG_QUEUED; // perhaps change to something else
ASSERT (pAdapter->SerRcv.PktsInQueue < 100);
//
// If the timer is not set, then this thread must set it
//
if (pAdapter->SerRcv.bTimerAlreadySet == FALSE)
{
fSetTimer = TRUE;
pAdapter->SerRcv.bTimerAlreadySet = TRUE;
}
InsertTailList(
&pAdapter->SerRcv.Queue,
&pIRsvd->Link
);
pAdapter->SerRcv.PktsInQueue++;
ADAPTER_RELEASE_LOCK (pAdapter);
//
// Now queue the timer
//
if (fSetTimer == TRUE)
{
//
// Set the timer
//
TRACE( TL_V, TM_Recv, ( " Set Timer "));
NdisMSetTimer ( &pAdapter->SerRcv.Timer, 0);
}
Status = NDIS_STATUS_SUCCESS;
} while (FALSE);
ASSERT (Status == NDIS_STATUS_SUCCESS);
return Status;
}
VOID
RcvIndicateTimer (
IN PVOID SystemSpecific1,
IN PVOID FunctionContext,
IN PVOID SystemSpecific2,
IN PVOID SystemSpecific3
)
/*++
Routine Description:
This function dequeues a packet and sends it upto NDIS and the protocols/.
The Function context is the adapter object which has the Receive queue in it.
Arguments:
Function context - adapter
Return Value:
--*/
{
PADAPTERCB pAdapter = (PADAPTERCB)FunctionContext;
TRACE( TL_T, TM_Recv, ( "==>RcvIndicateTimer Context %x", FunctionContext));
if (pAdapter->ulTag != MTAG_ADAPTERCB)
{
ASSERT (pAdapter->ulTag == MTAG_ADAPTERCB);
return;
}
ADAPTER_ACQUIRE_LOCK (pAdapter);
//
// Get the stats out
//
nicSetCountInHistogram(pAdapter->SerRcv.PktsInQueue, RcvStats);
nicSetMax(nicMaxRcv, pAdapter->SerRcv.PktsInQueue);
nicIncrementRcvTimerCount();
//
// Empty the Queue indicating as many packets as possible
//
while (IsListEmpty(&pAdapter->SerRcv.Queue)==FALSE)
{
LIST_ENTRY *pLink;
PINDICATE_RSVD pIRsvd;
pAdapter->SerRcv.PktsInQueue--;
pLink = RemoveHeadList(&pAdapter->SerRcv.Queue);
ADAPTER_RELEASE_LOCK (pAdapter);
//
// Indicate the packet without the lock
//
pIRsvd = CONTAINING_RECORD(
pLink,
INDICATE_RSVD,
Link
);
pIRsvd->Tag = NIC1394_TAG_INDICATED;
nicIncrementRcvVcPktCount(pIRsvd->pVc,pIRsvd->pPacket);
NdisMCoIndicateReceivePacket(pIRsvd->pVc->Hdr.NdisVcHandle, &pIRsvd->pPacket, 1);
if (NDIS_GET_PACKET_STATUS(pIRsvd->pPacket) == NDIS_STATUS_RESOURCES)
{
//
// Complete the packet
//
ASSERT (0);
pIRsvd->Tag = NIC1394_TAG_RETURNED;
nicInternalReturnPacket(pIRsvd->pVc, pIRsvd->pPacket );
}
ADAPTER_ACQUIRE_LOCK (pAdapter);
}
//
// clear the flag
//
ASSERT (pAdapter->SerRcv.PktsInQueue==0);
ASSERT (IsListEmpty(&pAdapter->SerRcv.Queue));
pAdapter->SerRcv.bTimerAlreadySet = FALSE;
ADAPTER_RELEASE_LOCK (pAdapter);
NdisMCoReceiveComplete(pAdapter->MiniportAdapterHandle);
TRACE( TL_T, TM_Recv, ( "<==RcvIndicateTimer "));
}
NDIS_STATUS
nicInitSerializedReassemblyStruct(
PADAPTERCB pAdapter
)
/*++
Routine Description:
Initialize the Reassembly serialization structure
Arguments:
padapter
Return Value:
Success
--*/
{
NdisZeroMemory (&pAdapter->Reassembly, sizeof(pAdapter->Reassembly));
InitializeListHead(&pAdapter->Reassembly.Queue); // Not be Used
NdisInitializeEvent (&pAdapter->Reassembly.CompleteEvent.NdisEvent);
pAdapter->Reassembly.CompleteEvent.EventCode = Nic1394EventCode_InvalidEventCode;
NdisMInitializeTimer (&pAdapter->Reassembly.Timer,
pAdapter->MiniportAdapterHandle,
ReassemblyTimerFunction ,
pAdapter);
return NDIS_STATUS_SUCCESS;
}
VOID
nicDeInitSerializedReassmblyStruct(
PADAPTERCB pAdapter
)
/*++
Routine Description:
Deinits the Reassembly routine routine
if the timer is set, it waits the timer out.
As all the reassemblies will be marked as aborted in nicFreeAllPendingReassemblies (below)
it queues a timer one last time to go in and delete all the reassembly structures.
Arguments:
Return Value:
--*/
{
do
{
BOOLEAN bTimerAlreadySet = FALSE;
//
// If this adapter is halting, then mark all reassemblies as aborted
//
nicFreeAllPendingReassemblyStructures(pAdapter);
//
// First wait for any pending timer to fire.
//
ADAPTER_ACQUIRE_LOCK (pAdapter);
bTimerAlreadySet = pAdapter->Reassembly.bTimerAlreadySet ;
if (bTimerAlreadySet == TRUE)
{
//
// if the (bTimerAlreadySet==TRUE ), it means we can clear/init the event.
// Because the TimerAlreadySet is cleared and the Event is always set within
// the same Acquire-Release Spinlock
//
NdisResetEvent (&pAdapter->Reassembly.CompleteEvent.NdisEvent);
pAdapter->Reassembly.CompleteEvent.EventCode = Nic1394EventCode_InvalidEventCode;
}
ADAPTER_RELEASE_LOCK(pAdapter);
if (bTimerAlreadySet == TRUE)
{
NdisWaitEvent (&pAdapter->Reassembly.CompleteEvent.NdisEvent,WAIT_INFINITE);
}
//
// Reset the event , to prepare for the next wait.
//
pAdapter->Reassembly.CompleteEvent.EventCode = Nic1394EventCode_InvalidEventCode;
NdisResetEvent (&pAdapter->Reassembly.CompleteEvent.NdisEvent);
//
// Now enqueue the timer one last time to free all pending reassemblies.
// and Stop any further reassembly timers
//
nicQueueReassemblyTimer (pAdapter,TRUE);
//
// Wait for the last timer to fire.
//
bTimerAlreadySet = pAdapter->Reassembly.bTimerAlreadySet ;
//
// Only do the wait, if nicQueueReassembly Timer actually queued a reassembly timer
//
if (bTimerAlreadySet == TRUE)
{
NdisWaitEvent (&pAdapter->Reassembly.CompleteEvent.NdisEvent,WAIT_INFINITE);
}
} while (FALSE);
}
NDIS_STATUS
nicQueueReassemblyTimer(
PADAPTERCB pAdapter,
BOOLEAN fIsLastTimer
)
/*++
Routine Description:
Queues a timer to be fired in one second.
If there is already a timer active it quietly exists
Arguments:
Self explanatory
Return Value:
--*/
{
NDIS_STATUS Status = NDIS_STATUS_FAILURE;
BOOLEAN fSetTimer = FALSE;
do
{
ADAPTER_ACQUIRE_LOCK (pAdapter);
//
// If the timer is not set, then this thread must set it
//
if (pAdapter->Reassembly.bTimerAlreadySet == FALSE && // timer is not set
pAdapter->Reassembly.PktsInQueue > 0 && // there are packets to be reassembled
ADAPTER_TEST_FLAG (pAdapter,fADAPTER_NoMoreReassembly) == FALSE ) // the adapter is not halting
{
fSetTimer = TRUE;
pAdapter->Reassembly.bTimerAlreadySet = TRUE;
}
if (fIsLastTimer == TRUE)
{
//
// Stop any further reassembly timers
//
ADAPTER_SET_FLAG (pAdapter, fADAPTER_NoMoreReassembly);
}
ADAPTER_RELEASE_LOCK (pAdapter);
//
// Now queue the timer
//
if (fSetTimer == TRUE)
{
//
// Set the timer
//
TRACE( TL_V, TM_Recv, ( " Set Timer "));
NdisMSetTimer ( &pAdapter->Reassembly.Timer, 2000);
}
Status = NDIS_STATUS_SUCCESS;
} while (FALSE);
ASSERT (Status == NDIS_STATUS_SUCCESS);
return Status;
}
VOID
nicFifoAllocationScheme (
PRECVFIFO_VCCB pRecvFIFOVc
)
/*++
Routine Description:
If there are less than 20 fifo allocated, it starts a workitem to
allocate a lot more fifos
Arguments:
Return Value:
--*/
{
BOOLEAN fQueueWorkItemInThisThread = FALSE;
PNIC_WORK_ITEM pFifoWorkItem = NULL;
do
{
if (pRecvFIFOVc->NumAllocatedFifos != NUM_RECV_FIFO_FIRST_PHASE)
{
break ;
}
if (pRecvFIFOVc->FifoWorkItemInProgress == TRUE)
{
break;
}
pFifoWorkItem = ALLOC_NONPAGED (sizeof(NIC_WORK_ITEM), MTAG_WORKITEM);
if (pFifoWorkItem == NULL)
{
break;
}
VC_ACQUIRE_LOCK(pRecvFIFOVc);
if (pRecvFIFOVc->FifoWorkItemInProgress == FALSE)
{
fQueueWorkItemInThisThread = TRUE;
pRecvFIFOVc->FifoWorkItemInProgress = TRUE;
// Add reference to the VC . Derefed in the WorkItem
//
nicReferenceCall((VCCB*)pRecvFIFOVc, "Queueing miniport Work Item\n");
}
VC_RELEASE_LOCK (pRecvFIFOVc);
if (fQueueWorkItemInThisThread == FALSE)
{
break;
}
//
// Queue the workitem
//
NdisInitializeWorkItem ( &pFifoWorkItem->NdisWorkItem,
(NDIS_PROC) nicAllocateRemainingFifoWorkItem,
(PVOID) pRecvFIFOVc);
NdisScheduleWorkItem (&pFifoWorkItem->NdisWorkItem);
} while (FALSE);
}
VOID
nicAllocateRemainingFifoWorkItem (
PNDIS_WORK_ITEM pNdisWorkItem,
IN PVOID Context
)
/*++
Routine Description:
This follows a simple algorithm. It simply allocates fifos
until we reach our expected number of 100
Arguments:
Return Value:
--*/
{
PRECVFIFO_VCCB pRecvFIFOVc = NULL;
BOOLEAN fIsVcActive = FALSE;
NDIS_STATUS NdisStatus = NDIS_STATUS_FAILURE;
pRecvFIFOVc = (PRECVFIFO_VCCB) (Context);
fIsVcActive = VC_ACTIVE(pRecvFIFOVc);
do
{
PADDRESS_FIFO pRecvFifoElement = NULL;
fIsVcActive = VC_ACTIVE(pRecvFIFOVc);
if (fIsVcActive == FALSE)
{
break;
}
NdisStatus = nicGetInitializedAddressFifoElement (pRecvFIFOVc->Hdr.MTU,
&pRecvFifoElement);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
break;
}
ASSERT (pRecvFifoElement != NULL);
nicSetTagInFifoWrapper(pRecvFifoElement, NIC1394_TAG_QUEUED);
ExInterlockedPushEntrySList ( &pRecvFIFOVc->FifoSListHead,
&pRecvFifoElement->FifoList,
&pRecvFIFOVc->FifoSListSpinLock);
//
// Add this once for every Address Fifo element inserted
// Will be decremented by a call to nicFreeAddressFifo
//
VC_ACQUIRE_LOCK (pRecvFIFOVc);
#if FIFO_WRAPPER
pRecvFIFOVc->pFifoTable[pRecvFIFOVc->NumAllocatedFifos] = pRecvFifoElement;
#endif
nicReferenceCall ((PVCCB) pRecvFIFOVc, "nicWorkItemFileSList");
pRecvFIFOVc->NumAllocatedFifos++;
VC_RELEASE_LOCK (pRecvFIFOVc);
} while (pRecvFIFOVc->NumAllocatedFifos < NUM_RECV_FIFO_BUFFERS);
pRecvFIFOVc->FifoWorkItemInProgress = FALSE;
nicDereferenceCall ((PVCCB)pRecvFIFOVc,"nicAllocateRemainingFifoWorkItem" );
FREE_NONPAGED(pNdisWorkItem);
}