830 lines
22 KiB
C
830 lines
22 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
ind.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code which implements the indication handler
|
||
for the NT Sample transport provider.
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "st.h"
|
||
|
||
|
||
|
||
NDIS_STATUS
|
||
StReceiveIndication (
|
||
IN NDIS_HANDLE BindingContext,
|
||
IN NDIS_HANDLE ReceiveContext,
|
||
IN PVOID HeaderBuffer,
|
||
IN UINT HeaderBufferSize,
|
||
IN PVOID LookaheadBuffer,
|
||
IN UINT LookaheadBufferSize,
|
||
IN UINT PacketSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine receives control from the physical provider as an
|
||
indication that a frame has been received on the physical link.
|
||
This routine is time critical, so we only allocate a
|
||
buffer and copy the packet into it. We also perform minimal
|
||
validation on this packet. It gets queued to the device context
|
||
to allow for processing later.
|
||
|
||
Arguments:
|
||
|
||
BindingContext - The Adapter Binding specified at initialization time.
|
||
|
||
ReceiveContext - A magic cookie for the MAC.
|
||
|
||
HeaderBuffer - pointer to a buffer containing the packet header.
|
||
|
||
HeaderBufferSize - the size of the header.
|
||
|
||
LookaheadBuffer - pointer to a buffer containing the negotiated minimum
|
||
amount of buffer I get to look at (not including header).
|
||
|
||
LookaheadBufferSize - the size of the above. May be less than asked
|
||
for, if that's all there is.
|
||
|
||
PacketSize - Overall size of the packet (not including header).
|
||
|
||
Return Value:
|
||
|
||
NDIS_STATUS - status of operation, one of:
|
||
|
||
NDIS_STATUS_SUCCESS if packet accepted,
|
||
NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol,
|
||
NDIS_any_other_thing if I understand, but can't handle.
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_CONTEXT DeviceContext;
|
||
HARDWARE_ADDRESS SourceAddressBuffer;
|
||
PHARDWARE_ADDRESS SourceAddress;
|
||
UINT RealPacketSize;
|
||
PST_HEADER StHeader;
|
||
|
||
DeviceContext = (PDEVICE_CONTEXT)BindingContext;
|
||
|
||
RealPacketSize = 0;
|
||
|
||
//
|
||
// Obtain the packet length; this may optionally adjust
|
||
// the lookahead buffer forward if the header we wish
|
||
// to remove spills over into what the MAC considers
|
||
// data. If it determines that the header is not
|
||
// valid, it keeps RealPacketSize at 0.
|
||
//
|
||
|
||
MacReturnPacketLength(
|
||
&DeviceContext->MacInfo,
|
||
HeaderBuffer,
|
||
HeaderBufferSize,
|
||
PacketSize,
|
||
&RealPacketSize
|
||
);
|
||
|
||
if (RealPacketSize < 2) {
|
||
return NDIS_STATUS_NOT_RECOGNIZED;
|
||
}
|
||
|
||
//
|
||
// We've negotiated at least a contiguous DLC header passed back in the
|
||
// lookahead buffer. Check it to see if we want this packet.
|
||
//
|
||
|
||
StHeader = (PST_HEADER)LookaheadBuffer;
|
||
|
||
if (StHeader->Signature != ST_SIGNATURE) {
|
||
return NDIS_STATUS_NOT_RECOGNIZED; // packet was processed.
|
||
}
|
||
|
||
|
||
//
|
||
// Check that the packet is not too long.
|
||
//
|
||
|
||
if (PacketSize > DeviceContext->MaxReceivePacketSize) {
|
||
#if DBG
|
||
StPrint2("StReceiveIndication: Ignoring packet length %d, max %d\n",
|
||
PacketSize, DeviceContext->MaxReceivePacketSize);
|
||
#endif
|
||
return NDIS_STATUS_NOT_RECOGNIZED;
|
||
}
|
||
|
||
MacReturnSourceAddress(
|
||
&DeviceContext->MacInfo,
|
||
HeaderBuffer,
|
||
&SourceAddressBuffer,
|
||
&SourceAddress
|
||
);
|
||
|
||
|
||
return StGeneralReceiveHandler(
|
||
DeviceContext,
|
||
ReceiveContext,
|
||
SourceAddress,
|
||
HeaderBuffer, // header
|
||
RealPacketSize, // total data length in packet
|
||
(PST_HEADER)LookaheadBuffer, // lookahead data
|
||
LookaheadBufferSize // lookahead data length
|
||
);
|
||
|
||
}
|
||
|
||
|
||
NDIS_STATUS
|
||
StGeneralReceiveHandler (
|
||
IN PDEVICE_CONTEXT DeviceContext,
|
||
IN NDIS_HANDLE ReceiveContext,
|
||
IN PHARDWARE_ADDRESS SourceAddress,
|
||
IN PVOID HeaderBuffer,
|
||
IN UINT PacketSize,
|
||
IN PST_HEADER StHeader,
|
||
IN UINT StSize
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine receives control from StReceiveIndication.
|
||
It continues the processing of indicated data.
|
||
|
||
This routine is time critical, so we only allocate a
|
||
buffer and copy the packet into it. We also perform minimal
|
||
validation on this packet. It gets queued to the device context
|
||
to allow for processing later.
|
||
|
||
Arguments:
|
||
|
||
DeviceContext - The device context of this adapter.
|
||
|
||
ReceiveContext - A magic cookie for the MAC.
|
||
|
||
SourceAddress - The source address of the packet.
|
||
|
||
HeaderBuffer - pointer to the packet header.
|
||
|
||
PacketSize - Overall size of the packet (not including header).
|
||
|
||
DlcHeader - Points to the DLC header of the packet.
|
||
|
||
DlcSize - The length of the packet indicated, starting from DlcHeader.
|
||
|
||
Return Value:
|
||
|
||
NDIS_STATUS - status of operation, one of:
|
||
|
||
NDIS_STATUS_SUCCESS if packet accepted,
|
||
NDIS_STATUS_NOT_RECOGNIZED if not recognized by protocol,
|
||
NDIS_any_other_thing if I understand, but can't handle.
|
||
|
||
--*/
|
||
{
|
||
|
||
KIRQL oldirql;
|
||
NTSTATUS Status;
|
||
NDIS_STATUS NdisStatus;
|
||
PNDIS_PACKET NdisPacket;
|
||
PNDIS_BUFFER NdisBuffer;
|
||
PSINGLE_LIST_ENTRY linkage;
|
||
UINT BytesTransferred;
|
||
PRECEIVE_PACKET_TAG ReceiveTag;
|
||
PBUFFER_TAG BufferTag;
|
||
PUCHAR SourceRouting;
|
||
UINT SourceRoutingLength;
|
||
PTP_ADDRESS DatagramAddress;
|
||
UINT NdisBufferLength;
|
||
PTP_CONNECTION Connection;
|
||
PVOID BufferPointer;
|
||
|
||
|
||
INCREMENT_COUNTER (DeviceContext, PacketsReceived);
|
||
|
||
Status = STATUS_SUCCESS; // assume no further processing required
|
||
|
||
|
||
//
|
||
// See what type of frame this is.
|
||
//
|
||
|
||
if ((StHeader->Command == ST_CMD_CONNECT) ||
|
||
(StHeader->Command == ST_CMD_DATAGRAM)) {
|
||
|
||
MacReturnSourceRouting(
|
||
&DeviceContext->MacInfo,
|
||
HeaderBuffer,
|
||
&SourceRouting,
|
||
&SourceRoutingLength);
|
||
|
||
Status = StProcessConnectionless (
|
||
DeviceContext,
|
||
SourceAddress,
|
||
StHeader,
|
||
StSize,
|
||
SourceRouting,
|
||
SourceRoutingLength,
|
||
&DatagramAddress);
|
||
|
||
} else if ((StHeader->Command == ST_CMD_INFORMATION) ||
|
||
(StHeader->Command == ST_CMD_DISCONNECT)) {
|
||
|
||
//
|
||
// If successful this adds a connection reference.
|
||
//
|
||
|
||
if (!(Connection = StFindConnection(DeviceContext, StHeader->Destination, StHeader->Source))) {
|
||
return NDIS_STATUS_NOT_RECOGNIZED;
|
||
}
|
||
|
||
|
||
if (StHeader->Command == ST_CMD_INFORMATION) {
|
||
|
||
Status = StProcessIIndicate (
|
||
Connection,
|
||
StHeader,
|
||
StSize,
|
||
PacketSize,
|
||
ReceiveContext,
|
||
(BOOLEAN)((StHeader->Flags & ST_FLAGS_LAST) != 0)
|
||
);
|
||
|
||
if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
|
||
StDereferenceConnection ("Information done", Connection);
|
||
} else {
|
||
Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
} else {
|
||
|
||
StStopConnection (Connection, STATUS_REMOTE_DISCONNECT);
|
||
StDereferenceConnection ("Disconnect done", Connection);
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// An unrecognized frame.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// If the above routines return success, the packet has been processed
|
||
// and can be discarded. If they return anything else, the packet needs
|
||
// to be copied to local storage for handling in a more lesurely
|
||
// fashion.
|
||
//
|
||
|
||
if (Status != STATUS_MORE_PROCESSING_REQUIRED) {
|
||
return NDIS_STATUS_SUCCESS;
|
||
}
|
||
|
||
linkage = ExInterlockedPopEntryList(
|
||
&DeviceContext->ReceivePacketPool,
|
||
&DeviceContext->Interlock);
|
||
|
||
if (linkage != NULL) {
|
||
NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0] );
|
||
} else {
|
||
(VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceivePacketExhausted);
|
||
|
||
return NDIS_STATUS_RESOURCES;
|
||
}
|
||
ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved);
|
||
|
||
linkage = ExInterlockedPopEntryList(
|
||
&DeviceContext->ReceiveBufferPool,
|
||
&DeviceContext->Interlock);
|
||
|
||
if (linkage != NULL) {
|
||
BufferTag = CONTAINING_RECORD( linkage, BUFFER_TAG, Linkage);
|
||
} else {
|
||
ExInterlockedPushEntryList(
|
||
&DeviceContext->ReceivePacketPool,
|
||
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
|
||
&DeviceContext->Interlock);
|
||
(VOID)InterlockedIncrement((PLONG)&DeviceContext->ReceiveBufferExhausted);
|
||
|
||
return NDIS_STATUS_RESOURCES;
|
||
}
|
||
|
||
NdisAdjustBufferLength (BufferTag->NdisBuffer, PacketSize);
|
||
NdisChainBufferAtFront (NdisPacket, (PNDIS_BUFFER)BufferTag->NdisBuffer);
|
||
|
||
//
|
||
// DatagramAddress has a reference added already.
|
||
//
|
||
|
||
BufferTag->Address = DatagramAddress;
|
||
|
||
//
|
||
// set up async return status so we can tell when it has happened;
|
||
// can never get return of NDIS_STATUS_PENDING in synch completion routine
|
||
// for NdisTransferData, so we know it has completed when this status
|
||
// changes
|
||
//
|
||
|
||
ReceiveTag->NdisStatus = NDIS_STATUS_PENDING;
|
||
ReceiveTag->PacketType = TYPE_AT_COMPLETE;
|
||
|
||
ExInterlockedInsertTailList(
|
||
&DeviceContext->ReceiveInProgress,
|
||
&ReceiveTag->Linkage,
|
||
&DeviceContext->SpinLock);
|
||
|
||
//
|
||
// receive packet is mapped at initalize
|
||
//
|
||
|
||
NdisTransferData (
|
||
&NdisStatus,
|
||
DeviceContext->NdisBindingHandle,
|
||
ReceiveContext,
|
||
0,
|
||
PacketSize,
|
||
NdisPacket,
|
||
&BytesTransferred);
|
||
|
||
//
|
||
// handle the various error codes
|
||
//
|
||
|
||
switch (NdisStatus) {
|
||
case NDIS_STATUS_SUCCESS: // received packet
|
||
ReceiveTag->NdisStatus = NDIS_STATUS_SUCCESS;
|
||
if (BytesTransferred == PacketSize) { // Did we get the entire packet?
|
||
return NDIS_STATUS_SUCCESS;
|
||
}
|
||
break;
|
||
|
||
case NDIS_STATUS_PENDING: // waiting async complete from NdisTransferData
|
||
return NDIS_STATUS_SUCCESS;
|
||
break;
|
||
|
||
default: // something broke; certainly we'll never get NdisTransferData
|
||
// asynch completion with this error status...
|
||
break;
|
||
}
|
||
|
||
//
|
||
// receive failed, for some reason; cleanup and fail return
|
||
//
|
||
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
RemoveEntryList (&ReceiveTag->Linkage);
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
|
||
ReceiveTag->PacketType = TYPE_AT_INDICATE;
|
||
|
||
ExInterlockedPushEntryList(
|
||
&DeviceContext->ReceivePacketPool,
|
||
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
|
||
&DeviceContext->Interlock);
|
||
|
||
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
|
||
NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength);
|
||
BufferTag = CONTAINING_RECORD (
|
||
BufferPointer,
|
||
BUFFER_TAG,
|
||
Buffer[0]
|
||
);
|
||
NdisAdjustBufferLength (NdisBuffer, BufferTag->Length); // reset to good value
|
||
|
||
ExInterlockedPushEntryList(
|
||
&DeviceContext->ReceiveBufferPool,
|
||
&BufferTag->Linkage,
|
||
&DeviceContext->Interlock);
|
||
|
||
if (DatagramAddress) {
|
||
StDereferenceAddress ("DG TransferData failed", DatagramAddress);
|
||
}
|
||
|
||
return NDIS_STATUS_FAILURE;
|
||
|
||
} // StReceiveIndication
|
||
|
||
|
||
|
||
VOID
|
||
StTransferDataComplete (
|
||
IN NDIS_HANDLE BindingContext,
|
||
IN PNDIS_PACKET NdisPacket,
|
||
IN NDIS_STATUS NdisStatus,
|
||
IN UINT BytesTransferred
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine receives control from the physical provider as an
|
||
indication that an NdisTransferData has completed. We use this indication
|
||
to start stripping buffers from the receive queue.
|
||
|
||
Arguments:
|
||
|
||
BindingContext - The Adapter Binding specified at initialization time.
|
||
|
||
NdisPacket/RequestHandle - An identifier for the request that completed.
|
||
|
||
NdisStatus - The completion status for the request.
|
||
|
||
BytesTransferred - Number of bytes actually transferred.
|
||
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_CONTEXT DeviceContext;
|
||
PRECEIVE_PACKET_TAG ReceiveTag;
|
||
PTP_CONNECTION Connection;
|
||
PNDIS_BUFFER NdisBuffer;
|
||
KIRQL oldirql, cancelirql;
|
||
|
||
//
|
||
// Put the NDIS status into a place we can use in packet processing.
|
||
// Note that this complete indication may be occuring during the call
|
||
// to NdisTransferData in the receive indication.
|
||
//
|
||
|
||
ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved);
|
||
|
||
//
|
||
// note that the processing below depends on having only one packet
|
||
// transfer outstanding at a time. NDIS is supposed to guarentee this.
|
||
//
|
||
|
||
switch (ReceiveTag->PacketType) {
|
||
|
||
case TYPE_AT_COMPLETE: // normal handling
|
||
ReceiveTag->NdisStatus = NdisStatus;
|
||
break;
|
||
|
||
case TYPE_AT_INDICATE:
|
||
|
||
DeviceContext = (PDEVICE_CONTEXT)BindingContext;
|
||
Connection = ReceiveTag->Connection;
|
||
|
||
//
|
||
// The transfer for this packet is complete. Was it successful??
|
||
//
|
||
|
||
if (NdisStatus != NDIS_STATUS_SUCCESS) {
|
||
|
||
ULONG DumpData[1];
|
||
DumpData[0] = BytesTransferred;
|
||
|
||
StWriteGeneralErrorLog(
|
||
DeviceContext,
|
||
EVENT_TRANSPORT_TRANSFER_DATA,
|
||
603,
|
||
NdisStatus,
|
||
NULL,
|
||
1,
|
||
DumpData);
|
||
|
||
//
|
||
// Drop the packet. BUGBUG: The driver should recover
|
||
// from this, but this transport has no way to cause
|
||
// the remote to resend.
|
||
//
|
||
|
||
}
|
||
|
||
//
|
||
// Now dereference the request to say we've got no more local
|
||
// references to the memory owned by it.
|
||
//
|
||
|
||
Connection->CurrentReceiveRequest->IoRequestPacket->IoStatus.Information += BytesTransferred;
|
||
StDereferenceRequest ("TransferData complete", Connection->CurrentReceiveRequest);
|
||
|
||
//
|
||
// see if we've completed the current receive. If so, move to the next one.
|
||
//
|
||
|
||
if (ReceiveTag->CompleteReceive) {
|
||
|
||
if (ReceiveTag->EndOfMessage) {
|
||
|
||
//
|
||
// The messages has been completely received, ack it.
|
||
//
|
||
// We set DEFERRED_ACK and DEFERRED_NOT_Q here, which
|
||
// will cause an ack to be piggybacked if any data is
|
||
// sent during the call to CompleteReceive. If this
|
||
// does not happen, then we will call AcknowledgeDataOnlyLast
|
||
// which will will send a DATA ACK or queue a request for
|
||
// a piggyback ack. We do this *after* calling CompleteReceive
|
||
// so we know that we will complete the receive back to
|
||
// the client before we ack the data, to prevent the
|
||
// next receive from being sent before this one is
|
||
// completed.
|
||
//
|
||
|
||
|
||
IoAcquireCancelSpinLock(&cancelirql);
|
||
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
||
|
||
Connection->Flags2 |= CONNECTION_FLAGS2_RC_PENDING;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If there is a receive posted, make it current and
|
||
// send a receive outstanding.
|
||
//
|
||
|
||
ActivateReceive (Connection);
|
||
|
||
IoAcquireCancelSpinLock(&cancelirql);
|
||
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
||
|
||
}
|
||
|
||
//
|
||
// NOTE: This releases the cancel and connection locks.
|
||
//
|
||
|
||
CompleteReceive (Connection, ReceiveTag->EndOfMessage, oldirql, cancelirql);
|
||
|
||
}
|
||
|
||
//
|
||
// dereference the connection to say we've done the I frame processing.
|
||
// This reference was done before calling NdisTransferData.
|
||
//
|
||
|
||
if (ReceiveTag->TransferDataPended) {
|
||
StDereferenceConnection("TransferData done", Connection);
|
||
}
|
||
|
||
|
||
//
|
||
// rip all of the NDIS_BUFFERs we've used off the chain and return them.
|
||
//
|
||
|
||
if (ReceiveTag->AllocatedNdisBuffer) {
|
||
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
|
||
while (NdisBuffer != NULL) {
|
||
NdisFreeBuffer (NdisBuffer);
|
||
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
|
||
}
|
||
} else {
|
||
NdisReinitializePacket (NdisPacket);
|
||
}
|
||
|
||
|
||
ExInterlockedPushEntryList(
|
||
&DeviceContext->ReceivePacketPool,
|
||
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
|
||
&DeviceContext->Interlock);
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
break;
|
||
}
|
||
|
||
return;
|
||
|
||
} /* StTransferDataComplete */
|
||
|
||
|
||
VOID
|
||
StReceiveComplete (
|
||
IN NDIS_HANDLE BindingContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine receives control from the physical provider as an
|
||
indication that a connection(less) frame has been received on the
|
||
physical link. We dispatch to the correct packet handler here.
|
||
|
||
Arguments:
|
||
|
||
BindingContext - The Adapter Binding specified at initialization time.
|
||
ST uses the DeviceContext for this parameter.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_CONTEXT DeviceContext;
|
||
NTSTATUS Status;
|
||
KIRQL oldirql, oldirql1;
|
||
PLIST_ENTRY linkage;
|
||
PNDIS_PACKET NdisPacket;
|
||
PNDIS_BUFFER NdisBuffer;
|
||
UINT NdisBufferLength;
|
||
PVOID BufferPointer;
|
||
PRECEIVE_PACKET_TAG ReceiveTag;
|
||
PBUFFER_TAG BufferTag;
|
||
PTP_ADDRESS Address;
|
||
PIRP Irp;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PTP_CONNECTION Connection;
|
||
|
||
|
||
DeviceContext = (PDEVICE_CONTEXT) BindingContext;
|
||
|
||
//
|
||
// Complete all pending receives. Do a quick check
|
||
// without the lock.
|
||
//
|
||
|
||
while (!IsListEmpty (&DeviceContext->IrpCompletionQueue)) {
|
||
|
||
linkage = ExInterlockedRemoveHeadList(
|
||
&DeviceContext->IrpCompletionQueue,
|
||
&DeviceContext->SpinLock);
|
||
|
||
if (linkage != NULL) {
|
||
|
||
Irp = CONTAINING_RECORD (linkage, IRP, Tail.Overlay.ListEntry);
|
||
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
||
|
||
Connection = (PTP_CONNECTION)IrpSp->FileObject->FsContext;
|
||
|
||
IoCompleteRequest (Irp, IO_NETWORK_INCREMENT);
|
||
|
||
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql1);
|
||
|
||
if (Connection->Flags2 & CONNECTION_FLAGS2_RC_PENDING) {
|
||
Connection->Flags2 &= ~CONNECTION_FLAGS2_RC_PENDING;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql1);
|
||
|
||
StDereferenceConnection ("receive completed", Connection);
|
||
|
||
} else {
|
||
|
||
//
|
||
// ExInterlockedRemoveHeadList returned NULL, so don't
|
||
// bother looping back.
|
||
//
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Packetize all waiting connections
|
||
//
|
||
|
||
if (!IsListEmpty(&DeviceContext->PacketizeQueue)) {
|
||
|
||
PacketizeConnections (DeviceContext);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Get every waiting packet, in order...
|
||
//
|
||
|
||
|
||
if (!IsListEmpty (&DeviceContext->ReceiveInProgress)) {
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
while (!IsListEmpty (&DeviceContext->ReceiveInProgress)) {
|
||
|
||
linkage = RemoveHeadList (&DeviceContext->ReceiveInProgress);
|
||
NdisPacket = CONTAINING_RECORD( linkage, NDIS_PACKET, ProtocolReserved[0]);
|
||
|
||
//
|
||
// NdisTransferData may have failed at async completion; check and
|
||
// see. If it did, then we discard this packet. If we're still waiting
|
||
// for transfer to complete, go back to sleep and hope (no guarantee!)
|
||
// we get waken up later.
|
||
//
|
||
|
||
ReceiveTag = (PRECEIVE_PACKET_TAG)(NdisPacket->ProtocolReserved);
|
||
if (ReceiveTag->NdisStatus == NDIS_STATUS_PENDING) {
|
||
InsertHeadList (&DeviceContext->ReceiveInProgress, linkage);
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
return;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
|
||
if (ReceiveTag->NdisStatus != NDIS_STATUS_SUCCESS) {
|
||
goto FreePacket; // skip the packet, continue with while loop
|
||
}
|
||
|
||
NdisQueryPacket (NdisPacket, NULL, NULL, &NdisBuffer, NULL);
|
||
|
||
//
|
||
// Have a packet. Since I allocated the storage for it, I know it's
|
||
// virtually contiguous and can treat it that way, which I will
|
||
// henceforth.
|
||
//
|
||
|
||
NdisQueryBuffer (NdisBuffer, &BufferPointer, &NdisBufferLength);
|
||
|
||
//
|
||
// Determine what address this is for, which is stored
|
||
// in the buffer tag header.
|
||
//
|
||
|
||
BufferTag = CONTAINING_RECORD( BufferPointer, BUFFER_TAG, Buffer[0]);
|
||
Address = BufferTag->Address;
|
||
|
||
//
|
||
// Process the frame as a UI frame; only datagrams should
|
||
// be processed here. If Address is NULL then this datagram
|
||
// is not needed for any bound address and should be given
|
||
// to RAS only.
|
||
//
|
||
|
||
ASSERT (Address != NULL);
|
||
|
||
//
|
||
// Indicate it or complete posted datagrams.
|
||
//
|
||
|
||
Status = StIndicateDatagram (
|
||
DeviceContext,
|
||
Address,
|
||
BufferPointer,
|
||
NdisBufferLength);
|
||
|
||
|
||
//
|
||
// Dereference the address.
|
||
//
|
||
|
||
StDereferenceAddress ("Datagram done", Address);
|
||
|
||
//
|
||
// Finished with packet; return to pool.
|
||
//
|
||
|
||
FreePacket:;
|
||
|
||
NdisUnchainBufferAtFront (NdisPacket, &NdisBuffer);
|
||
ReceiveTag->PacketType = TYPE_AT_INDICATE;
|
||
|
||
ExInterlockedPushEntryList(
|
||
&DeviceContext->ReceivePacketPool,
|
||
(PSINGLE_LIST_ENTRY)&ReceiveTag->Linkage,
|
||
&DeviceContext->Interlock);
|
||
|
||
NdisAdjustBufferLength (NdisBuffer, BufferTag->Length);
|
||
ExInterlockedPushEntryList(
|
||
&DeviceContext->ReceiveBufferPool,
|
||
&BufferTag->Linkage,
|
||
&DeviceContext->Interlock);
|
||
|
||
ACQUIRE_SPIN_LOCK (&DeviceContext->SpinLock, &oldirql);
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&DeviceContext->SpinLock, oldirql);
|
||
|
||
} // if queue not empty
|
||
|
||
return;
|
||
|
||
} /* StReceiveComplete */
|
||
|