summaryrefslogtreecommitdiffstats
path: root/private/ntos/afd/recvvc.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/ntos/afd/recvvc.c')
-rw-r--r--private/ntos/afd/recvvc.c1884
1 files changed, 1884 insertions, 0 deletions
diff --git a/private/ntos/afd/recvvc.c b/private/ntos/afd/recvvc.c
new file mode 100644
index 000000000..f246c64ea
--- /dev/null
+++ b/private/ntos/afd/recvvc.c
@@ -0,0 +1,1884 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ recvvc.c
+
+Abstract:
+
+ This module contains routines for handling data receive for connection-
+ oriented endpoints.
+
+Author:
+
+ David Treadwell (davidtr) 21-Oct-1993
+
+Revision History:
+
+--*/
+
+#include "afdp.h"
+
+VOID
+AfdCancelReceive (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+PIRP
+AfdGetPendedReceiveIrp (
+ IN PAFD_CONNECTION Connection,
+ IN BOOLEAN Expedited
+ );
+
+PAFD_BUFFER
+AfdGetReceiveBuffer (
+ IN PAFD_CONNECTION Connection,
+ IN ULONG ReceiveFlags,
+ IN PAFD_BUFFER StartingAfdBuffer OPTIONAL
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGEAFD, AfdBReceive )
+#pragma alloc_text( PAGEAFD, AfdBReceiveEventHandler )
+#pragma alloc_text( PAGEAFD, AfdBReceiveExpeditedEventHandler )
+#pragma alloc_text( PAGEAFD, AfdCancelReceive )
+#pragma alloc_text( PAGEAFD, AfdGetPendedReceiveIrp )
+#pragma alloc_text( PAGEAFD, AfdGetReceiveBuffer )
+#pragma alloc_text( PAGEAFD, AfdRestartBufferReceive )
+#endif
+
+
+NTSTATUS
+AfdBReceive (
+ IN PIRP Irp,
+ IN PIO_STACK_LOCATION IrpSp,
+ IN ULONG RecvFlags,
+ IN ULONG AfdFlags,
+ IN ULONG RecvLength
+ )
+{
+ NTSTATUS status;
+ KIRQL oldIrql;
+ PAFD_ENDPOINT endpoint;
+ PAFD_CONNECTION connection;
+ ULONG bytesReceived;
+ BOOLEAN peek;
+ PAFD_BUFFER afdBuffer;
+ BOOLEAN completeMessage;
+ BOOLEAN partialReceivePossible;
+ PAFD_BUFFER newAfdBuffer;
+
+ //
+ // Set up some local variables.
+ //
+
+ endpoint = IrpSp->FileObject->FsContext;
+ ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
+
+ connection = endpoint->Common.VcConnecting.Connection;
+ ASSERT( connection != NULL );
+ ASSERT( connection->Type == AfdBlockTypeConnection );
+
+ //
+ // Determine if this is a peek operation.
+ //
+
+ ASSERT( ( RecvFlags & TDI_RECEIVE_EITHER ) != 0 );
+ ASSERT( ( RecvFlags & TDI_RECEIVE_EITHER ) != TDI_RECEIVE_EITHER );
+
+ peek = ( RecvFlags & TDI_RECEIVE_PEEK ) != 0;
+
+ //
+ // Determine whether it is legal to complete this receive with a
+ // partial message.
+ //
+
+ if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
+
+ partialReceivePossible = TRUE;
+
+ } else {
+
+ if ( (RecvFlags & TDI_RECEIVE_PARTIAL) != 0 ) {
+ partialReceivePossible = TRUE;
+ } else {
+ partialReceivePossible = FALSE;
+ }
+ }
+
+ //
+ // Reset the InputBufferLength field of our stack location. We'll
+ // use this to keep track of how much data we've placed into the IRP
+ // so far.
+ //
+
+ IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
+
+ //
+ // If this is an inline endpoint, then either type of receive data
+ // can be used to satisfy this receive.
+ //
+
+ if ( endpoint->InLine ) {
+ RecvFlags |= TDI_RECEIVE_EITHER;
+ }
+
+ //
+ // Check whether the remote end has aborted the connection, in which
+ // case we should complete the receive.
+ //
+
+ if ( connection->AbortIndicated ) {
+ status = STATUS_CONNECTION_RESET;
+ goto complete;
+ }
+
+ //
+ // Try to get data already bufferred on the connection to satisfy
+ // this receive.
+ //
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+ AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
+
+ if( RecvFlags & TDI_RECEIVE_EXPEDITED ) {
+ endpoint->EventsActive &= ~AFD_POLL_RECEIVE_EXPEDITED;
+ }
+
+ if( RecvFlags & TDI_RECEIVE_NORMAL ) {
+ endpoint->EventsActive &= ~AFD_POLL_RECEIVE;
+ }
+
+ IF_DEBUG(EVENT_SELECT) {
+ KdPrint((
+ "AfdBReceive: Endp %08lX, Active %08lX\n",
+ endpoint,
+ endpoint->EventsActive
+ ));
+ }
+
+ newAfdBuffer = NULL;
+ afdBuffer = NULL;
+ afdBuffer = AfdGetReceiveBuffer( connection, RecvFlags, afdBuffer );
+
+ while ( afdBuffer != NULL ) {
+
+ //
+ // Copy the data to the MDL in the IRP. Note that we do not
+ // handle the case where, for a stream type endpoint, the
+ // receive IRP is large enough to take multiple data buffers
+ // worth of information. Our method works fine, albeit a little
+ // slower. The faster, where the output buffer is filled up as
+ // much as possible, is done in the fast path. We should only
+ // be here if we hit a timing window between a fast path attempt
+ // and a receive indication.
+ //
+
+
+ if ( Irp->MdlAddress != NULL ) {
+
+ status = TdiCopyBufferToMdl(
+ afdBuffer->Buffer,
+ afdBuffer->DataOffset,
+ afdBuffer->DataLength,
+ Irp->MdlAddress,
+ IrpSp->Parameters.DeviceIoControl.InputBufferLength,
+ &bytesReceived
+ );
+
+ } else {
+
+ if ( afdBuffer->DataLength == 0 ) {
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ bytesReceived = 0;
+ }
+
+ ASSERT( status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW );
+
+ ASSERT( afdBuffer->PartialMessage == TRUE || afdBuffer->PartialMessage == FALSE );
+
+ completeMessage = !afdBuffer->PartialMessage;
+
+ //
+ // If this wasn't a peek IRP, update information on the
+ // connection based on whether the entire buffer of data was
+ // taken.
+ //
+
+ if ( !peek ) {
+
+ //
+ // If all the data in the buffer was taken, remove the buffer
+ // from the connection's list and return it to the buffer pool.
+ //
+
+ if (status == STATUS_SUCCESS) {
+
+ ASSERT(afdBuffer->DataLength == bytesReceived);
+
+ //
+ // Update the counts of bytes bufferred on the connection.
+ //
+
+ if ( afdBuffer->ExpeditedData ) {
+
+ ASSERT( connection->VcBufferredExpeditedBytes >= bytesReceived );
+ ASSERT( connection->VcBufferredExpeditedCount > 0 );
+
+ connection->VcBufferredExpeditedBytes -= bytesReceived;
+ connection->VcBufferredExpeditedCount -= 1;
+
+ } else {
+
+ ASSERT( connection->VcBufferredReceiveBytes >= bytesReceived );
+ ASSERT( connection->VcBufferredReceiveCount > 0 );
+
+ connection->VcBufferredReceiveBytes -= bytesReceived;
+ connection->VcBufferredReceiveCount -= 1;
+ }
+
+ RemoveEntryList( &afdBuffer->BufferListEntry );
+
+ afdBuffer->DataOffset = 0;
+ afdBuffer->ExpeditedData = FALSE;
+
+ AfdReturnBuffer( afdBuffer );
+
+ //
+ // Reset the afdBuffer local so that we know that the
+ // buffer is gone.
+ //
+
+ afdBuffer = NULL;
+
+ } else {
+
+ //
+ // Update the counts of bytes bufferred on the connection.
+ //
+
+ if ( afdBuffer->ExpeditedData ) {
+ ASSERT( connection->VcBufferredExpeditedBytes >= bytesReceived );
+ connection->VcBufferredExpeditedBytes -= bytesReceived;
+ } else {
+ ASSERT( connection->VcBufferredReceiveBytes >= bytesReceived );
+ connection->VcBufferredReceiveBytes -= bytesReceived;
+ }
+
+ //
+ // Not all of the buffer's data was taken. Update the
+ // counters in the AFD buffer structure to reflect the
+ // amount of data that was actually received.
+ //
+ ASSERT(afdBuffer->DataLength > bytesReceived);
+
+ afdBuffer->DataOffset += bytesReceived;
+ afdBuffer->DataLength -= bytesReceived;
+
+ ASSERT( afdBuffer->DataOffset < afdBuffer->BufferLength );
+ }
+
+ //
+ // If there is indicated but unreceived data in the TDI
+ // provider, and we have available buffer space, fire off an
+ // IRP to receive the data.
+ //
+
+ if ( connection->VcReceiveCountInTransport > 0
+
+ &&
+
+ connection->VcBufferredReceiveBytes <
+ connection->MaxBufferredReceiveBytes
+
+ &&
+
+ connection->VcBufferredReceiveCount <
+ connection->MaxBufferredReceiveCount ) {
+
+ CLONG bytesToReceive;
+
+ //
+ // Remember the count of data that we're going to
+ // receive, then reset the fields in the connection
+ // where we keep track of how much data is available in
+ // the transport. We reset it here before releasing the
+ // lock so that another thread doesn't try to receive
+ // the data at the same time as us.
+ //
+
+ if ( connection->VcReceiveBytesInTransport > AfdLargeBufferSize ) {
+ bytesToReceive = connection->VcReceiveBytesInTransport;
+ } else {
+ bytesToReceive = AfdLargeBufferSize;
+ }
+
+ ASSERT( connection->VcReceiveCountInTransport == 1 );
+ connection->VcReceiveBytesInTransport = 0;
+ connection->VcReceiveCountInTransport = 0;
+
+ //
+ // Get an AFD buffer structure to hold the data.
+ //
+
+ newAfdBuffer = AfdGetBuffer( bytesToReceive, 0 );
+ if ( newAfdBuffer == NULL ) {
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ AfdBeginAbort( connection );
+ status = STATUS_LOCAL_DISCONNECT;
+ goto complete;
+ }
+
+ //
+ // We need to remember the connection in the AFD buffer
+ // because we'll need to access it in the completion
+ // routine.
+ //
+
+ newAfdBuffer->Context = connection;
+
+ //
+ // Finish building the receive IRP to give to the TDI
+ // provider.
+ //
+
+ TdiBuildReceive(
+ newAfdBuffer->Irp,
+ connection->DeviceObject,
+ connection->FileObject,
+ AfdRestartBufferReceive,
+ newAfdBuffer,
+ newAfdBuffer->Mdl,
+ TDI_RECEIVE_NORMAL,
+ bytesToReceive
+ );
+
+ //
+ // Wait to hand off the IRP until we can safely release
+ // the endpoint lock.
+ //
+ }
+ }
+
+ //
+ // For stream type endpoints, it does not make sense to return
+ // STATUS_BUFFER_OVERFLOW. That status is only sensible for
+ // message-oriented transports.
+ //
+
+ if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
+ status = STATUS_SUCCESS;
+ }
+
+ //
+ // We've set up all return information. If we got a full
+ // message OR if we can complete with a partial message OR if
+ // the IRP is full of data, clean up and complete the IRP.
+ //
+
+ if ( completeMessage || partialReceivePossible ||
+ status == STATUS_BUFFER_OVERFLOW ) {
+
+ if( ( RecvFlags & TDI_RECEIVE_NORMAL ) &&
+ IS_DATA_ON_CONNECTION( connection ) ) {
+
+ AfdIndicateEventSelectEvent(
+ endpoint,
+ AFD_POLL_RECEIVE_BIT,
+ STATUS_SUCCESS
+ );
+
+ }
+
+ if( ( RecvFlags & TDI_RECEIVE_EXPEDITED ) &&
+ IS_EXPEDITED_DATA_ON_CONNECTION( connection ) ) {
+
+ AfdIndicateEventSelectEvent(
+ endpoint,
+ endpoint->InLine
+ ? AFD_POLL_RECEIVE_BIT
+ : AFD_POLL_RECEIVE_EXPEDITED_BIT,
+ STATUS_SUCCESS
+ );
+
+ }
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ //
+ // If there was data bufferred in the transport, fire off
+ // the IRP to receive it.
+ //
+
+ if ( newAfdBuffer != NULL ) {
+ (VOID)IoCallDriver( connection->DeviceObject, newAfdBuffer->Irp );
+ }
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = bytesReceived +
+ IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+
+ IoCompleteRequest( Irp, 0 );
+
+ return status;
+ }
+
+ //
+ // Update the count of bytes we've received so far into the IRP,
+ // get another buffer of data, and continue.
+ //
+
+ IrpSp->Parameters.DeviceIoControl.InputBufferLength += bytesReceived;
+ afdBuffer = AfdGetReceiveBuffer( connection, RecvFlags, afdBuffer );
+ }
+
+ //
+ // If there was no data bufferred on the endpoint and the connection
+ // has been disconnected by the remote end, complete the receive
+ // with 0 bytes read if this is a stream endpoint, or a failure
+ // code if this is a message endpoint.
+ //
+
+ if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0 &&
+ connection->DisconnectIndicated ) {
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_GRACEFUL_DISCONNECT;
+ }
+
+ goto complete;
+ }
+
+ //
+ // If this is a nonblocking endpoint and the request was a normal
+ // receive (as opposed to a read IRP), fail the request. We don't
+ // fail reads under the asumption that if the application is doing
+ // reads they don't want nonblocking behavior.
+ //
+
+ if ( IrpSp->Parameters.DeviceIoControl.InputBufferLength == 0 &&
+ endpoint->NonBlocking && !( AfdFlags & AFD_OVERLAPPED ) ) {
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ status = STATUS_DEVICE_NOT_READY;
+ goto complete;
+ }
+
+ //
+ // We'll have to pend the IRP. Remember the receive flags in the
+ // Type3InputBuffer field of our IO stack location.
+ //
+
+ IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = (PVOID)RecvFlags;
+
+ //
+ // Place the IRP on the connection's list of pended receive IRPs and
+ // mark the IRP ad pended.
+ //
+
+ InsertTailList(
+ &connection->VcReceiveIrpListHead,
+ &Irp->Tail.Overlay.ListEntry
+ );
+
+ IoMarkIrpPending( Irp );
+ Irp->IoStatus.Status = STATUS_SUCCESS;
+
+ //
+ // Set up the cancellation routine in the IRP. If the IRP has already
+ // been cancelled, just call the cancellation routine here.
+ //
+
+ if ( Irp->Cancel ) {
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ AfdCancelReceive( IrpSp->DeviceObject, Irp );
+ return STATUS_CANCELLED;
+ }
+
+ IoSetCancelRoutine( Irp, AfdCancelReceive );
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ //
+ // If there was data bufferred in the transport, fire off the IRP to
+ // receive it. We have to wait until here because it is not legal
+ // to do an IoCallDriver() while holding a spin lock.
+ //
+
+ if ( newAfdBuffer != NULL ) {
+ (VOID)IoCallDriver( connection->DeviceObject, newAfdBuffer->Irp );
+ }
+
+ return STATUS_PENDING;
+
+complete:
+
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+
+ IoCompleteRequest( Irp, 0 );
+
+ return status;
+
+} // AfdBReceive
+
+
+NTSTATUS
+AfdBReceiveEventHandler (
+ IN PVOID TdiEventContext,
+ IN CONNECTION_CONTEXT ConnectionContext,
+ IN ULONG ReceiveFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+
+/*++
+
+Routine Description:
+
+ Handles receive events for nonbufferring transports.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ KIRQL oldIrql;
+ KIRQL cancelIrql;
+ PAFD_ENDPOINT endpoint;
+ PAFD_CONNECTION connection;
+ PLIST_ENTRY listEntry;
+ PAFD_BUFFER afdBuffer;
+ PIRP irp;
+ ULONG requiredAfdBufferSize;
+ NTSTATUS status;
+ ULONG receiveLength;
+ BOOLEAN userIrp;
+ BOOLEAN expedited;
+ BOOLEAN completeMessage;
+
+ DEBUG receiveLength = 0xFFFFFFFF;
+
+ connection = (PAFD_CONNECTION)ConnectionContext;
+ ASSERT( connection != NULL );
+
+ endpoint = connection->Endpoint;
+ ASSERT( endpoint != NULL );
+ *BytesTaken = 0;
+
+ ASSERT( connection->Type == AfdBlockTypeConnection );
+ ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
+ endpoint->Type == AfdBlockTypeVcListening );
+ ASSERT( !connection->DisconnectIndicated );
+
+ ASSERT( !endpoint->TdiBufferring );
+ ASSERT( endpoint->EndpointType == AfdEndpointTypeStream ||
+ endpoint->EndpointType == AfdEndpointTypeSequencedPacket ||
+ endpoint->EndpointType == AfdEndpointTypeReliableMessage );
+
+#if AFD_PERF_DBG
+ if ( BytesAvailable == BytesIndicated ) {
+ AfdFullReceiveIndications++;
+ } else {
+ AfdPartialReceiveIndications++;
+ }
+#endif
+
+ //
+ // If the receive side of the endpoint has been shut down, tell the
+ // provider that we took all the data and reset the connection.
+ // Also, account for these bytes in our count of bytes taken from
+ // the transport.
+ //
+
+ IoAcquireCancelSpinLock( &cancelIrql );
+ AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
+
+ if ( (endpoint->DisconnectMode & AFD_PARTIAL_DISCONNECT_RECEIVE) != 0 ||
+ endpoint->EndpointCleanedUp ) {
+
+#if DBG
+ DbgPrint( "AfdBReceiveEventHandler: receive shutdown, "
+ "%ld bytes, aborting endp %lx\n",
+ BytesAvailable, endpoint );
+#endif
+
+ *BytesTaken = BytesAvailable;
+
+ //
+ // Abort the connection. Note that if the abort attempt fails
+ // we can't do anything about it.
+ //
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( cancelIrql );
+
+ (VOID)AfdBeginAbort( connection );
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Figure out whether this is a receive indication for normal
+ // or expedited data, and whether this is a complete message.
+ //
+
+ expedited = (BOOLEAN)( (ReceiveFlags & TDI_RECEIVE_EXPEDITED) != 0 );
+
+ ASSERT( expedited || connection->VcReceiveBytesInTransport == 0 );
+ ASSERT( expedited || connection->VcReceiveCountInTransport == 0 );
+
+ completeMessage = (BOOLEAN)((ReceiveFlags & TDI_RECEIVE_ENTIRE_MESSAGE) != 0);
+
+ //
+ // Check whether there are any IRPs waiting on the connection. If
+ // there is such an IRP and normal data is being indicated, use the
+ // IRP to receive the data.
+ //
+
+ if ( !IsListEmpty( &connection->VcReceiveIrpListHead ) && !expedited ) {
+
+ PIO_STACK_LOCATION irpSp;
+
+ ASSERT( *BytesTaken == 0 );
+
+ listEntry = RemoveHeadList( &connection->VcReceiveIrpListHead );
+
+ //
+ // Get a pointer to the IRP and reset the cancel routine in
+ // the IRP. The IRP is no longer cancellable.
+ //
+
+ irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
+ IoSetCancelRoutine( irp, NULL );
+
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ //
+ // If the IRP is not large enough to hold the available data, or
+ // if it is a peek or expedited receive IRP, or if we've already
+ // placed some data into the IRP, then we'll just buffer the
+ // data manually and complete the IRP in the receive completion
+ // routine.
+ //
+
+ if ( irpSp->Parameters.DeviceIoControl.OutputBufferLength >=
+ BytesAvailable &&
+ irpSp->Parameters.DeviceIoControl.InputBufferLength == 0 &&
+ (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer == 0 &&
+ !endpoint->TdiMessageMode ) {
+
+ //
+ // If all of the data was indicated to us here AND this is a
+ // complete message in and of itself, then just copy the
+ // data to the IRP and complete the IRP.
+ //
+
+ if ( completeMessage && BytesIndicated == BytesAvailable ) {
+
+ //
+ // The IRP is off the endpoint's list and is no longer
+ // cancellable. We can release the locks we hold.
+ //
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( cancelIrql );
+
+ //
+ // Set BytesTaken to indicate that we've taken all the
+ // data. We do it here because we already have
+ // BytesAvailable in a register, which probably won't
+ // be true after making function calls.
+ //
+
+ *BytesTaken = BytesAvailable;
+
+ //
+ // Copy the data to the IRP.
+ //
+
+ if ( irp->MdlAddress != NULL ) {
+
+ status = TdiCopyBufferToMdl(
+ Tsdu,
+ 0,
+ BytesAvailable,
+ irp->MdlAddress,
+ 0,
+ &irp->IoStatus.Information
+ );
+
+ } else {
+
+ ASSERT( BytesAvailable == 0 );
+ status = STATUS_SUCCESS;
+ irp->IoStatus.Information = 0;
+ }
+
+ //
+ // We should never get STATUS_BUFFER_OVERFLOW from
+ // TdiCopyBufferToMdl() because the user's buffer
+ // should have been large enough to hold all the data.
+ //
+
+ ASSERT( status == STATUS_SUCCESS );
+
+ //
+ // We have already set up the status field of the IRP
+ // when we pended the IRP, so there's no need to
+ // set it again here.
+ //
+
+ ASSERT( irp->IoStatus.Status == STATUS_SUCCESS );
+
+ //
+ // Complete the IRP. We've already set BytesTaken
+ // to tell the provider that we have taken all the data.
+ //
+
+ IoCompleteRequest( irp, AfdPriorityBoost );
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Some of the data was not indicated, so remember that we
+ // want to pass back this IRP to the TDI provider. Passing
+ // back this IRP directly is good because it avoids having
+ // to copy the data from one of our buffers into the user's
+ // buffer.
+ //
+
+ userIrp = TRUE;
+ requiredAfdBufferSize = 0;
+
+ receiveLength =
+ AfdIgnorePushBitOnReceives
+ ? BytesAvailable
+ : irpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ } else {
+
+ //
+ // The first pended IRP is too tiny to hold all the
+ // available data or else it is a peek or expedited receive
+ // IRP. Put the IRP back on the head of the list and buffer
+ // the data and complete the IRP in the restart routine.
+ //
+
+ InsertHeadList(
+ &connection->VcReceiveIrpListHead,
+ &irp->Tail.Overlay.ListEntry
+ );
+
+ userIrp = FALSE;
+ requiredAfdBufferSize = BytesAvailable;
+ receiveLength = BytesAvailable;
+ }
+
+ } else if ( !expedited ) {
+
+ ASSERT( IsListEmpty( &connection->VcReceiveIrpListHead ) );
+
+ //
+ // Check whether we've already bufferred the maximum amount of
+ // data that we'll allow ourselves to buffer for this
+ // connection. If we're at the limit, then we need to exert
+ // back pressure by not accepting this indicated data (flow
+ // control).
+ //
+ // Note that we have no flow control mechanisms for expedited
+ // data. We always accept any expedited data that is indicated
+ // to us.
+ //
+
+ if ( connection->VcBufferredReceiveBytes >=
+ connection->MaxBufferredReceiveBytes
+
+ ||
+
+ connection->VcBufferredReceiveCount >=
+ connection->MaxBufferredReceiveCount ) {
+
+ ASSERT( connection->VcReceiveBytesInTransport == 0 );
+ ASSERT( connection->VcReceiveCountInTransport == 0 );
+
+ //
+ // Just remember the amount of data that is available. When
+ // buffer space frees up, we'll actually receive this data.
+ //
+
+ connection->VcReceiveBytesInTransport = BytesAvailable;
+ connection->VcReceiveCountInTransport = 1;
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( cancelIrql );
+
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ //
+ // There were no prepended IRPs. We'll have to buffer the data
+ // here in AFD. If all of the available data is being indicated
+ // to us AND this is a complete message, just copy the data
+ // here.
+ //
+
+ if ( completeMessage && BytesIndicated == BytesAvailable ) {
+
+ //
+ // We don't need the cancel spin lock any more, so we can
+ // release it. However, since we acquired the cancel spin lock
+ // after the endpoint spin lock and we still need the endpoint
+ // spin lock, be careful to switch the IRQLs.
+ //
+
+ IoReleaseCancelSpinLock( oldIrql );
+ oldIrql = cancelIrql;
+
+ //
+ // Get an AFD buffer to hold the data.
+ //
+
+ afdBuffer = AfdGetBuffer( BytesAvailable, 0 );
+
+ if ( afdBuffer == NULL ) {
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+
+ //
+ // If we couldn't get a buffer, abort the connection.
+ // This is pretty brutal, but the only alternative is
+ // to attempt to receive the data sometime later, which
+ // is very complicated to implement.
+ //
+
+ AfdBeginAbort( connection );
+ *BytesTaken = BytesAvailable;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Use the special function to copy the data instead of
+ // RtlCopyMemory in case the data is coming from a special
+ // place (DMA, etc.) which cannot work with RtlCopyMemory.
+ //
+
+ TdiCopyLookaheadData(
+ afdBuffer->Buffer,
+ Tsdu,
+ BytesAvailable,
+ ReceiveFlags
+ );
+
+ //
+ // Store the data length and set the offset to 0.
+ //
+
+ afdBuffer->DataLength = BytesAvailable;
+ ASSERT( afdBuffer->DataOffset == 0 );
+
+ afdBuffer->PartialMessage = FALSE;
+
+ //
+ // Place the buffer on this connection's list of bufferred data
+ // and update the count of data bytes on the connection.
+ //
+
+ InsertTailList(
+ &connection->VcReceiveBufferListHead,
+ &afdBuffer->BufferListEntry
+ );
+
+ connection->VcBufferredReceiveBytes += BytesAvailable;
+ connection->VcBufferredReceiveCount += 1;
+
+ //
+ // All done. Release the lock and tell the provider that we
+ // took all the data.
+ //
+
+ *BytesTaken = BytesAvailable;
+
+ //
+ // Indicate that it is possible to receive on the endpoint now.
+ //
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+
+ AfdIndicatePollEvent(
+ endpoint,
+ AFD_POLL_RECEIVE_BIT,
+ STATUS_SUCCESS
+ );
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // There were no prepended IRPs and not all of the data was
+ // indicated to us. We'll have to buffer it by handing an IRP
+ // back to the TDI privider.
+ //
+ // Note that in this case we sometimes hand a large buffer to
+ // the TDI provider. We do this so that it can hold off
+ // completion of our IRP until it gets EOM or the buffer is
+ // filled. This reduces the number of receive indications that
+ // the TDI provider has to perform and also reduces the number
+ // of kernel/user transitions the application will perform
+ // because we'll tend to complete receives with larger amounts
+ // of data.
+ //
+ // We do not hand back a "large" AFD buffer if the indicated data
+ // is greater than the large buffer size or if the TDI provider
+ // is message mode. The reason for not giving big buffers back
+ // to message providers is that they will hold on to the buffer
+ // until a full message is received and this would be incorrect
+ // behavior on a SOCK_STREAM.
+ //
+
+ userIrp = FALSE;
+
+ if ( AfdLargeBufferSize >= BytesAvailable &&
+ !AfdIgnorePushBitOnReceives &&
+ !endpoint->TdiMessageMode ) {
+ requiredAfdBufferSize = AfdLargeBufferSize;
+ receiveLength = AfdLargeBufferSize;
+ } else {
+ requiredAfdBufferSize = BytesAvailable;
+ receiveLength = BytesAvailable;
+ }
+
+ } else {
+
+ //
+ // We're being indicated with expedited data. Buffer it and
+ // complete any pended IRPs in the restart routine. We always
+ // buffer expedited data to save complexity and because expedited
+ // data is not an important performance case.
+ //
+ // !!! do we need to perform flow control with expedited data?
+ //
+
+ userIrp = FALSE;
+ requiredAfdBufferSize = BytesAvailable;
+ receiveLength = BytesAvailable;
+ }
+
+ //
+ // We're able to buffer the data. First acquire a buffer of
+ // appropriate size.
+ //
+
+ afdBuffer = AfdGetBuffer( requiredAfdBufferSize, 0 );
+
+ if ( afdBuffer == NULL ) {
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( cancelIrql );
+
+ //
+ // If we couldn't get a buffer, abort the connection. This is
+ // pretty brutal, but the only alternative is to attempt to
+ // receive the data sometime later, which is very complicated to
+ // implement.
+ //
+
+ AfdBeginAbort( connection );
+
+ *BytesTaken = BytesAvailable;
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // We'll have to format up an IRP and give it to the provider to
+ // handle. We don't need any locks to do this--the restart routine
+ // will check whether new receive IRPs were pended on the endpoint.
+ //
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( cancelIrql );
+
+ //
+ // Use the IRP in the AFD buffer if appropriate. If userIrp is
+ // TRUE, then the local variable irp will already point to the
+ // user's IRP which we'll use for this IO.
+ //
+
+ if ( !userIrp ) {
+ irp = afdBuffer->Irp;
+ ASSERT( afdBuffer->Mdl == irp->MdlAddress );
+ }
+
+ //
+ // We need to remember the connection in the AFD buffer because
+ // we'll need to access it in the completion routine.
+ //
+
+ afdBuffer->Context = connection;
+
+ //
+ // Remember the type of data that we're receiving.
+ //
+
+ afdBuffer->ExpeditedData = expedited;
+ afdBuffer->PartialMessage = !completeMessage;
+
+ //
+ // Finish building the receive IRP to give to the TDI provider.
+ //
+
+ ASSERT( receiveLength != 0xFFFFFFFF );
+
+ TdiBuildReceive(
+ irp,
+ connection->DeviceObject,
+ connection->FileObject,
+ AfdRestartBufferReceive,
+ afdBuffer,
+ irp->MdlAddress,
+ ReceiveFlags & TDI_RECEIVE_EITHER,
+ receiveLength
+ );
+
+ //
+ // Make the next stack location current. Normally IoCallDriver would
+ // do this, but since we're bypassing that, we do it directly.
+ //
+
+ IoSetNextIrpStackLocation( irp );
+
+ *IoRequestPacket = irp;
+ *BytesTaken = 0;
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // AfdBReceiveEventHandler
+
+
+NTSTATUS
+AfdBReceiveExpeditedEventHandler (
+ IN PVOID TdiEventContext,
+ IN CONNECTION_CONTEXT ConnectionContext,
+ IN ULONG ReceiveFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+
+/*++
+
+Routine Description:
+
+ Handles receive expedited events for nonbufferring transports.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ return AfdBReceiveEventHandler (
+ TdiEventContext,
+ ConnectionContext,
+ ReceiveFlags | TDI_RECEIVE_EXPEDITED,
+ BytesIndicated,
+ BytesAvailable,
+ BytesTaken,
+ Tsdu,
+ IoRequestPacket
+ );
+
+} // AfdBReceiveExpeditedEventHandler
+
+
+NTSTATUS
+AfdRestartBufferReceive (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ Handles completion of bufferred receives that were started in the
+ receive indication handler.
+
+Arguments:
+
+ DeviceObject - not used.
+
+ Irp - the IRP that is completing.
+
+ Context - the endpoint which received the data.
+
+Return Value:
+
+ NTSTATUS - if this is our IRP, then always
+ STATUS_MORE_PROCESSING_REQUIRED to indicate to the IO system that we
+ own the IRP and the IO system should stop processing the it.
+
+ If this is a user's IRP, then STATUS_SUCCESS to indicate that
+ IO completion should continue.
+
+--*/
+
+{
+ PAFD_ENDPOINT endpoint;
+ PAFD_CONNECTION connection;
+ KIRQL oldIrql;
+ KIRQL cancelIrql;
+ PAFD_BUFFER afdBuffer;
+ PLIST_ENTRY listEntry;
+ LIST_ENTRY completeIrpListHead;
+ NTSTATUS status;
+ PIRP userIrp;
+ BOOLEAN expedited;
+ NTSTATUS irpStatus;
+
+ afdBuffer = Context;
+
+ connection = afdBuffer->Context;
+ endpoint = connection->Endpoint;
+
+ ASSERT( connection->Type == AfdBlockTypeConnection );
+ ASSERT( endpoint->Type == AfdBlockTypeVcConnecting ||
+ endpoint->Type == AfdBlockTypeVcListening );
+
+ ASSERT( !endpoint->TdiBufferring );
+ ASSERT( endpoint->EndpointType == AfdEndpointTypeStream ||
+ endpoint->EndpointType == AfdEndpointTypeSequencedPacket ||
+ endpoint->EndpointType == AfdEndpointTypeReliableMessage );
+
+ //
+ // If the IRP being completed is actually a user's IRP, set it up
+ // for completion and allow IO completion to finish.
+ //
+
+ if ( Irp != afdBuffer->Irp ) {
+
+ //
+ // Free the AFD buffer we've been using to track this request.
+ //
+
+ AfdReturnBuffer( afdBuffer );
+
+ //
+ // If pending has be returned for this IRP then mark the current
+ // stack as pending.
+ //
+
+ if ( Irp->PendingReturned ) {
+ IoMarkIrpPending( Irp );
+ }
+
+ //
+ // Tell the IO system that it is OK to continue with IO
+ // completion.
+ //
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // If the receive failed, abort the connection.
+ //
+
+ irpStatus = Irp->IoStatus.Status;
+
+ if ( !NT_SUCCESS(irpStatus) ) {
+
+ //
+ // We treat STATUS_BUFFER_OVERFLOW just like STATUS_RECEIVE_PARTIAL.
+ //
+
+ if ( irpStatus == STATUS_BUFFER_OVERFLOW ) {
+
+ irpStatus = STATUS_RECEIVE_PARTIAL;
+
+ } else {
+
+ afdBuffer->Mdl->ByteCount = afdBuffer->BufferLength;
+ AfdReturnBuffer( afdBuffer );
+
+ //
+ // !!! We can't abort the connection if the connection has
+ // not yet been accepted because we'll still be pointing
+ // at the listening endpoint. We should do something,
+ // however. How common is this failure?
+ //
+
+ KdPrint(( "AfdRestartBufferReceive: IRP %lx failed on endp %lx\n",
+ irpStatus, endpoint ));
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+ }
+
+ //
+ // Remember the length of the received data.
+ //
+
+ afdBuffer->DataLength = Irp->IoStatus.Information;
+
+ //
+ // Initialize the local list we'll use to complete any receive IRPs.
+ // We use a list like this because we may need to complete multiple
+ // IRPs and we usually cannot complete IRPs at any random point due
+ // to any locks we might hold.
+ //
+
+ InitializeListHead( &completeIrpListHead );
+
+ //
+ // If there are any pended IRPs on the connection, complete as
+ // appropriate with the new information. Note that we'll try to
+ // complete as many pended IRPs as possible with this new buffer of
+ // data.
+ //
+
+ IoAcquireCancelSpinLock( &cancelIrql );
+ AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
+
+ expedited = afdBuffer->ExpeditedData;
+
+ while ( afdBuffer != NULL &&
+ (userIrp = AfdGetPendedReceiveIrp(
+ connection,
+ expedited )) != NULL ) {
+
+ PIO_STACK_LOCATION irpSp;
+ ULONG receiveFlags;
+ ULONG bytesCopied = 0;
+ BOOLEAN peek;
+ BOOLEAN partialReceivePossible;
+
+ //
+ // Set up some locals.
+ //
+
+ irpSp = IoGetCurrentIrpStackLocation( userIrp );
+
+ receiveFlags = (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+ peek = (BOOLEAN)( (receiveFlags & TDI_RECEIVE_PEEK) != 0 );
+
+
+ if ( endpoint->EndpointType == AfdEndpointTypeStream ||
+ (receiveFlags & TDI_RECEIVE_PARTIAL) != 0 ) {
+ partialReceivePossible = TRUE;
+ } else {
+ partialReceivePossible = FALSE;
+ }
+
+ //
+ // We're about to complete the IRP, so reset its cancel routine.
+ //
+
+ IoSetCancelRoutine( userIrp, NULL );
+
+ //
+ // Copy data to the user's IRP.
+ //
+
+ if ( userIrp->MdlAddress != NULL ) {
+
+ status = TdiCopyBufferToMdl(
+ afdBuffer->Buffer,
+ afdBuffer->DataOffset,
+ afdBuffer->DataLength,
+ userIrp->MdlAddress,
+ irpSp->Parameters.DeviceIoControl.InputBufferLength,
+ &bytesCopied
+ );
+
+ userIrp->IoStatus.Information =
+ irpSp->Parameters.DeviceIoControl.InputBufferLength + bytesCopied;
+
+ } else {
+
+ if ( afdBuffer->DataLength == 0 ) {
+ status = STATUS_SUCCESS;
+ } else {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ userIrp->IoStatus.Information = 0;
+ }
+
+ ASSERT( status == STATUS_SUCCESS || status == STATUS_BUFFER_OVERFLOW );
+
+ //
+ // If the IRP was not a peek IRP, update the AFD buffer
+ // accordingly. If it was a peek IRP then the data should be
+ // reread, so keep it around.
+ //
+
+ if ( !peek ) {
+
+ //
+ // If we copied all of the data from the buffer to the IRP,
+ // free the AFD buffer structure.
+ //
+
+ if ( status == STATUS_SUCCESS ) {
+
+ ASSERT(afdBuffer->DataLength == bytesCopied);
+
+ afdBuffer->DataOffset = 0;
+ afdBuffer->ExpeditedData = FALSE;
+
+ AfdReturnBuffer( afdBuffer );
+ afdBuffer = NULL;
+
+ //
+ // *** NOTE THAT AFTER THIS POINT WE CANNOT TOUCH EITHER
+ // THE AFD BUFFER OR THE IRP!
+ //
+
+ } else {
+
+ //
+ // There is more data left in the buffer. Update counts in
+ // the AFD buffer structure.
+ //
+
+ ASSERT(afdBuffer->DataLength > bytesCopied);
+
+ afdBuffer->DataOffset += bytesCopied;
+ afdBuffer->DataLength -= bytesCopied;
+
+ ASSERT(afdBuffer->DataOffset < afdBuffer->BufferLength);
+ }
+ }
+
+ //
+ // For stream type endpoints, it does not make sense to return
+ // STATUS_BUFFER_OVERFLOW. That status is only sensible for
+ // message-oriented transports. We have already set up the
+ // status field of the IRP when we pended it, so we don't
+ // need to do it again here.
+ //
+
+ if ( endpoint->EndpointType == AfdEndpointTypeStream ) {
+ ASSERT( userIrp->IoStatus.Status == STATUS_SUCCESS );
+ } else {
+ userIrp->IoStatus.Status = status;
+ }
+
+ //
+ // We can complete the IRP under any of the following
+ // conditions:
+ //
+ // - the buffer contains a complete message of data.
+ //
+ // - it is OK to complete the IRP with a partial message.
+ //
+ // - the IRP is already full of data.
+ //
+
+ if ( irpStatus == STATUS_SUCCESS
+
+ ||
+
+ partialReceivePossible
+
+ ||
+
+ status == STATUS_BUFFER_OVERFLOW ) {
+
+ //
+ // Add the IRP to the list of IRPs we'll need to complete once we
+ // can release locks.
+ //
+
+ InsertTailList(
+ &completeIrpListHead,
+ &userIrp->Tail.Overlay.ListEntry
+ );
+
+ } else {
+
+ //
+ // Update the count of data placed into the IRP thus far.
+ //
+
+
+ irpSp->Parameters.DeviceIoControl.InputBufferLength += bytesCopied;
+
+ //
+ // Put the IRP back on the connection's list of pended IRPs.
+ //
+
+ InsertHeadList(
+ &connection->VcReceiveIrpListHead,
+ &userIrp->Tail.Overlay.ListEntry
+ );
+
+ //
+ // Stop processing this buffer for now.
+ //
+ // !!! This could cause a problem if there is a regular
+ // receive pended behind a peek IRP! But that is a
+ // pretty unlikely scenario.
+ //
+
+ break;
+ }
+ }
+
+ //
+ // If there is any data left, place the buffer at the end of the
+ // connection's list of bufferred data and update counts of data on
+ // the connection.
+ //
+
+ if ( afdBuffer != NULL ) {
+
+ InsertTailList(
+ &connection->VcReceiveBufferListHead,
+ &afdBuffer->BufferListEntry
+ );
+
+ if ( expedited ) {
+ connection->VcBufferredExpeditedBytes += afdBuffer->DataLength;
+ connection->VcBufferredExpeditedCount += 1;
+ } else {
+ connection->VcBufferredReceiveBytes += afdBuffer->DataLength;
+ connection->VcBufferredReceiveCount += 1;
+ }
+
+ //
+ // Remember whether we got a full or partial receive in the
+ // AFD buffer.
+ //
+
+ if ( irpStatus == STATUS_RECEIVE_PARTIAL ||
+ irpStatus == STATUS_RECEIVE_PARTIAL_EXPEDITED ) {
+ afdBuffer->PartialMessage = TRUE;
+ } else {
+ afdBuffer->PartialMessage = FALSE;
+ }
+ }
+
+ //
+ // Release locks and indicate that there is bufferred data on the
+ // endpoint.
+ //
+
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+ IoReleaseCancelSpinLock( cancelIrql );
+
+ //
+ // If there was leftover data, complete polls as necessary. Indicate
+ // expedited data if the endpoint is not InLine and expedited data
+ // was received; otherwise, indicate normal data.
+ //
+
+ if ( afdBuffer != NULL ) {
+
+ if ( expedited && !endpoint->InLine ) {
+
+ AfdIndicatePollEvent(
+ endpoint,
+ AFD_POLL_RECEIVE_EXPEDITED_BIT,
+ STATUS_SUCCESS
+ );
+
+ } else {
+
+ AfdIndicatePollEvent(
+ endpoint,
+ AFD_POLL_RECEIVE_BIT,
+ STATUS_SUCCESS
+ );
+
+ }
+
+ }
+
+ //
+ // Complete IRPs as necessary.
+ //
+
+ while ( !IsListEmpty( &completeIrpListHead ) ) {
+
+ listEntry = RemoveHeadList( &completeIrpListHead );
+ userIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
+
+ IoCompleteRequest( userIrp, AfdPriorityBoost );
+ }
+
+ //
+ // Tell the IO system to stop processing the AFD IRP, since we now
+ // own it as part of the AFD buffer.
+ //
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+} // AfdRestartBufferReceive
+
+
+VOID
+AfdCancelReceive (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels a receive IRP that is pended in AFD.
+
+Arguments:
+
+ DeviceObject - not used.
+
+ Irp - the IRP to cancel.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PIO_STACK_LOCATION irpSp;
+ PAFD_ENDPOINT endpoint;
+ PAFD_CONNECTION connection;
+ KIRQL oldIrql;
+
+ //
+ // Get the endpoint pointer from our IRP stack location and the
+ // connection pointer from the endpoint.
+ //
+
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ endpoint = irpSp->FileObject->FsContext;
+ ASSERT( endpoint->Type == AfdBlockTypeVcConnecting );
+
+ connection = endpoint->Common.VcConnecting.Connection;
+ ASSERT( connection->Type == AfdBlockTypeConnection );
+
+ //
+ // Remove the IRP from the connection's IRP list, synchronizing with
+ // the endpoint lock which protects the lists. Note that the IRP
+ // *must* be on one of the connection's lists if we are getting
+ // called here--anybody that removes the IRP from the list must do
+ // so while holding the cancel spin lock and reset the cancel
+ // routine to NULL before releasing the cancel spin lock.
+ //
+
+ AfdAcquireSpinLock( &endpoint->SpinLock, &oldIrql );
+ RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
+ AfdReleaseSpinLock( &endpoint->SpinLock, oldIrql );
+
+ //
+ // Reset the cancel routine in the IRP.
+ //
+
+ IoSetCancelRoutine( Irp, NULL );
+
+ //
+ // Release the cancel spin lock and complete the IRP with a
+ // cancellation status code.
+ //
+
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ Irp->IoStatus.Information = 0;
+ Irp->IoStatus.Status = STATUS_CANCELLED;
+
+ IoCompleteRequest( Irp, AfdPriorityBoost );
+
+ return;
+
+} // AfdCancelReceive
+
+
+PAFD_BUFFER
+AfdGetReceiveBuffer (
+ IN PAFD_CONNECTION Connection,
+ IN ULONG ReceiveFlags,
+ IN PAFD_BUFFER StartingAfdBuffer OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a pointer to a receive data buffer that contains the
+ appropriate type of data. Note that this routine DOES NOT remove
+ the buffer structure from the list it is on.
+
+ This routine MUST be called with the connection's endpoint lock
+ held!
+
+Arguments:
+
+ Connection - a pointer to the connection to search for data.
+
+ ReceiveFlags - the type of receive data to look for.
+
+ StartingAfdBuffer - if non-NULL, start looking for a buffer AFTER
+ this buffer.
+
+Return Value:
+
+ PAFD_BUFFER - a pointer to an AFD buffer of the appropriate data type,
+ or NULL if there was no appropriate buffer on the connection.
+
+--*/
+
+{
+ PLIST_ENTRY listEntry;
+ PAFD_BUFFER afdBuffer;
+
+ ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
+
+ //
+ // Start with the first AFD buffer on the connection.
+ //
+
+ listEntry = Connection->VcReceiveBufferListHead.Flink;
+ afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
+
+ //
+ // If a starting AFD buffer was specified, walk past that buffer in
+ // the connection list.
+ //
+
+ if ( ARGUMENT_PRESENT( StartingAfdBuffer ) ) {
+
+ while ( TRUE ) {
+
+ if ( afdBuffer == StartingAfdBuffer ) {
+ listEntry = listEntry->Flink;
+ afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
+ break;
+ }
+
+ listEntry = listEntry->Flink;
+ afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
+
+ ASSERT( listEntry != &Connection->VcReceiveBufferListHead );
+ }
+ }
+
+ //
+ // Act based on the type of data we're trying to get.
+ //
+
+ switch ( ReceiveFlags & TDI_RECEIVE_EITHER ) {
+
+ case TDI_RECEIVE_NORMAL:
+
+ //
+ // Walk the connection's list of data buffers until we find the
+ // first data buffer that is of the appropriate type.
+ //
+
+ while ( listEntry != &Connection->VcReceiveBufferListHead &&
+ afdBuffer->ExpeditedData ) {
+
+ listEntry = afdBuffer->BufferListEntry.Flink;
+ afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
+ }
+
+ if ( listEntry != &Connection->VcReceiveBufferListHead ) {
+ return afdBuffer;
+ } else {
+ return NULL;
+ }
+
+ case TDI_RECEIVE_EITHER :
+
+ //
+ // Just return the first buffer, if there is one.
+ //
+
+ if ( listEntry != &Connection->VcReceiveBufferListHead ) {
+ return afdBuffer;
+ } else {
+ return NULL;
+ }
+
+ case TDI_RECEIVE_EXPEDITED:
+
+ if ( Connection->VcBufferredExpeditedCount == 0 ) {
+ return NULL;
+ }
+
+ //
+ // Walk the connection's list of data buffers until we find the
+ // first data buffer that is of the appropriate type.
+ //
+
+ while ( listEntry != &Connection->VcReceiveBufferListHead &&
+ !afdBuffer->ExpeditedData ) {
+
+ listEntry = afdBuffer->BufferListEntry.Flink;
+ afdBuffer = CONTAINING_RECORD( listEntry, AFD_BUFFER, BufferListEntry );
+ }
+
+ if ( listEntry != &Connection->VcReceiveBufferListHead ) {
+ return afdBuffer;
+ } else {
+ return NULL;
+ }
+
+ default:
+
+ ASSERT( !"Invalid ReceiveFlags" );
+ return NULL;
+ }
+
+} // AfdGetReceiveBuffer
+
+
+PIRP
+AfdGetPendedReceiveIrp (
+ IN PAFD_CONNECTION Connection,
+ IN BOOLEAN Expedited
+ )
+
+/*++
+
+Routine Description:
+
+ Removes a receive IRP from the connection's list of receive IRPs.
+ Only returns an IRP which is valid for the specified type of
+ data, normal or expedited. If there are no IRPs pended or only
+ IRPs of the wrong type, returns NULL.
+
+ This routine MUST be called with the connection's endpoint lock
+ held!
+
+Arguments:
+
+ Connection - a pointer to the connection to search for an IRP.
+
+ Expedited - TRUE if this routine should return a receive IRP which
+ can receive expedited data.
+
+Return Value:
+
+ PIRP - a pointer to an IRP which can receive data of the specified
+ type. The IRP IS removed from the connection's list of pended
+ receive IRPs.
+
+--*/
+
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG receiveFlags;
+ PLIST_ENTRY listEntry;
+
+ ASSERT( KeGetCurrentIrql( ) == DISPATCH_LEVEL );
+
+ //
+ // Walk the list of pended receive IRPs looking for one which can
+ // be completed with the specified type of data.
+ //
+
+ for ( listEntry = Connection->VcReceiveIrpListHead.Flink;
+ listEntry != &Connection->VcReceiveIrpListHead;
+ listEntry = listEntry->Flink ) {
+
+ //
+ // Get a pointer to the IRP and our stack location in the IRP.
+ //
+
+ irp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry );
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ //
+ // Determine whether this IRP can receive the data type we need.
+ //
+
+ receiveFlags = (ULONG)irpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+ receiveFlags &= TDI_RECEIVE_EITHER;
+ ASSERT( receiveFlags != 0 );
+
+ if ( receiveFlags == TDI_RECEIVE_NORMAL && !Expedited ) {
+
+ //
+ // We have a normal receive and normal data. Remove this
+ // IRP from the connection's list and return it.
+ //
+
+ RemoveEntryList( listEntry );
+ return irp;
+ }
+
+ if ( receiveFlags == TDI_RECEIVE_EITHER ) {
+
+ //
+ // This is an "either" receive. It can take the data
+ // regardless of the data type.
+ //
+
+ RemoveEntryList( listEntry );
+ return irp;
+ }
+
+ if ( receiveFlags == TDI_RECEIVE_EXPEDITED && Expedited ) {
+
+ //
+ // We have an expedited receive and expedited data. Remove
+ // this IRP from the connection's list and return it.
+ //
+
+ RemoveEntryList( listEntry );
+ return irp;
+ }
+
+ //
+ // This IRP did not meet our criteria. Continue scanning the
+ // connection's list of pended IRPs for a good IRP.
+ //
+ }
+
+ //
+ // There were no IRPs which could be completed with the specified
+ // type of data.
+ //
+
+ return NULL;
+
+} // AfdGetPendedReceiveIrp