384 lines
9.4 KiB
C
384 lines
9.4 KiB
C
/*++
|
||
|
||
Copyright (c) 1989-1993 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
rcveng.c
|
||
|
||
Abstract:
|
||
|
||
This module contains code that implements the receive engine for the
|
||
Sample transport provider.
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "st.h"
|
||
|
||
|
||
VOID
|
||
ActivateReceive(
|
||
PTP_CONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine activates the next TdiReceive request on the specified
|
||
connection object if there is no active request on that connection
|
||
already. This allows the request to accept data on the connection.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a TP_CONNECTION object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
PTP_REQUEST Request;
|
||
|
||
//
|
||
// The ACTIVE_RECEIVE bitflag will be set on the connection if
|
||
// the receive-fields in the CONNECTION object are valid. If
|
||
// this flag is cleared, then we try to make the next TdiReceive
|
||
// request in the ReceiveQueue the active request.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
||
if (!(Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE)) {
|
||
if (!IsListEmpty (&Connection->ReceiveQueue)) {
|
||
|
||
//
|
||
// Found a receive, so make it the active one.
|
||
//
|
||
|
||
Connection->Flags |= CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
|
||
Request = CONTAINING_RECORD (
|
||
Connection->ReceiveQueue.Flink,
|
||
TP_REQUEST,
|
||
Linkage);
|
||
Connection->MessageBytesReceived = 0;
|
||
Connection->MessageBytesAcked = 0;
|
||
Connection->CurrentReceiveRequest = Request;
|
||
Connection->CurrentReceiveMdl = Request->Buffer2;
|
||
Connection->ReceiveLength = Request->Buffer2Length;
|
||
Connection->ReceiveByteOffset = 0;
|
||
}
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
|
||
} /* ActivateReceive */
|
||
|
||
|
||
VOID
|
||
AwakenReceive(
|
||
PTP_CONNECTION Connection
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to reactivate a sleeping connection with the
|
||
RECEIVE_WAKEUP bitflag set because data arrived for which no receive
|
||
was available. The caller has made a receive available at the connection,
|
||
so here we activate the next receive, and send the appropriate protocol
|
||
to restart the message at the first byte offset past the one received
|
||
by the last receive.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a TP_CONNECTION object.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
|
||
//
|
||
// If the RECEIVE_WAKEUP bitflag is set, then awaken the connection.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
||
if (Connection->Flags & CONNECTION_FLAGS_RECEIVE_WAKEUP) {
|
||
if (Connection->ReceiveQueue.Flink != &Connection->ReceiveQueue) {
|
||
Connection->Flags &= ~CONNECTION_FLAGS_RECEIVE_WAKEUP;
|
||
|
||
//
|
||
// Found a receive, so turn off the wakeup flag, and activate
|
||
// the next receive.
|
||
//
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
ActivateReceive (Connection);
|
||
|
||
return;
|
||
}
|
||
}
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
} /* AwakenReceive */
|
||
|
||
|
||
VOID
|
||
CompleteReceive(
|
||
PTP_CONNECTION Connection,
|
||
BOOLEAN EndOfRecord,
|
||
KIRQL ConnectionIrql,
|
||
KIRQL CancelIrql
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by ProcessIncomingData when the current receive
|
||
must be completed. Depending on whether the current frame being
|
||
processed is a DATA_FIRST_MIDDLE or DATA_ONLY_LAST, and also whether
|
||
all of the data was processed, the EndOfRecord flag will be set accordingly
|
||
by the caller to indicate that a message boundary was received.
|
||
|
||
NOTE: This function is called with the connection and cancel
|
||
IRQLs held, and returns with them released.
|
||
|
||
Arguments:
|
||
|
||
Connection - Pointer to a TP_CONNECTION object.
|
||
|
||
EndOfRecord - BOOLEAN set to true if TDI_END_OF_RECORD should be reported.
|
||
|
||
ConnectionIrql - The IRQL at which the connection spinlock was acquired.
|
||
|
||
CancelIrql - The IRQL at which the cancel spinlock was acquired.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLIST_ENTRY p;
|
||
PTP_REQUEST Request;
|
||
KIRQL oldirql = ConnectionIrql;
|
||
KIRQL cancelirql = CancelIrql;
|
||
ULONG BytesReceived;
|
||
|
||
|
||
if (IsListEmpty (&Connection->ReceiveQueue)) {
|
||
|
||
ASSERT ((Connection->Flags & CONNECTION_FLAGS_STOPPING) != 0);
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
return;
|
||
}
|
||
|
||
Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
BytesReceived = Connection->MessageBytesReceived;
|
||
|
||
|
||
//
|
||
// Complete the TdiReceive request at the head of the
|
||
// connection's ReceiveQueue.
|
||
//
|
||
|
||
p = RemoveHeadList (&Connection->ReceiveQueue);
|
||
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
|
||
Request->IoRequestPacket->CancelRoutine = (PDRIVER_CANCEL)NULL;
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock(cancelirql);
|
||
|
||
|
||
Request->Flags |= REQUEST_FLAGS_DELAY;
|
||
|
||
StCompleteRequest(
|
||
Request,
|
||
EndOfRecord ? STATUS_SUCCESS : STATUS_BUFFER_OVERFLOW,
|
||
BytesReceived);
|
||
|
||
} /* CompleteReceive */
|
||
|
||
|
||
VOID
|
||
StCancelReceive(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called by the I/O system to cancel a receive.
|
||
The receive is found on the connection's receive queue; if it
|
||
is the current request it is cancelled and the connection
|
||
goes into "cancelled receive" mode, otherwise it is cancelled
|
||
silently.
|
||
|
||
In "cancelled receive" mode the connection makes it appear to
|
||
the remote the data is being received, but in fact it is not
|
||
indicated to the transport or buffered on our end
|
||
|
||
NOTE: This routine is called with the CancelSpinLock held and
|
||
is responsible for releasing it.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to the device object for this driver.
|
||
|
||
Irp - Pointer to the request packet representing the I/O request.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldirql;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
PTP_CONNECTION Connection;
|
||
PTP_REQUEST Request;
|
||
PLIST_ENTRY p;
|
||
ULONG BytesReceived;
|
||
BOOLEAN Found;
|
||
|
||
UNREFERENCED_PARAMETER (DeviceObject);
|
||
|
||
//
|
||
// Get a pointer to the current stack location in the IRP. This is where
|
||
// the function codes and parameters are stored.
|
||
//
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation (Irp);
|
||
|
||
ASSERT ((IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL) &&
|
||
(IrpSp->MinorFunction == TDI_RECEIVE));
|
||
|
||
Connection = IrpSp->FileObject->FsContext;
|
||
|
||
//
|
||
// Since this IRP is still in the cancellable state, we know
|
||
// that the connection is still around (although it may be in
|
||
// the process of being torn down).
|
||
//
|
||
|
||
//
|
||
// See if this is the IRP for the current receive request.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK (&Connection->SpinLock, &oldirql);
|
||
|
||
BytesReceived = Connection->MessageBytesReceived;
|
||
|
||
p = Connection->ReceiveQueue.Flink;
|
||
|
||
//
|
||
// If there is a receive active, then see if this is it.
|
||
//
|
||
|
||
if ((Connection->Flags & CONNECTION_FLAGS_ACTIVE_RECEIVE) != 0) {
|
||
|
||
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
|
||
if (Request->IoRequestPacket == Irp) {
|
||
|
||
//
|
||
// yes, it is the active receive. Turn on the RCV_CANCELLED
|
||
// bit instructing the connection to drop the rest of the
|
||
// data received (until the DOL comes in).
|
||
//
|
||
|
||
Connection->Flags2 |= CONNECTION_FLAGS2_RCV_CANCELLED;
|
||
Connection->Flags &= ~CONNECTION_FLAGS_ACTIVE_RECEIVE;
|
||
|
||
(VOID)RemoveHeadList (&Connection->ReceiveQueue);
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
//
|
||
// The following dereference will complete the I/O, provided removes
|
||
// the last reference on the request object. The I/O will complete
|
||
// with the status and information stored in the Irp. Therefore,
|
||
// we set those values here before the dereference.
|
||
//
|
||
|
||
StCompleteRequest (Request, STATUS_CANCELLED, 0);
|
||
return;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// If we fall through to here, the IRP was not the active receive.
|
||
// Scan through the list, looking for this IRP.
|
||
//
|
||
|
||
Found = FALSE;
|
||
|
||
while (p != &Connection->ReceiveQueue) {
|
||
|
||
Request = CONTAINING_RECORD (p, TP_REQUEST, Linkage);
|
||
if (Request->IoRequestPacket == Irp) {
|
||
|
||
//
|
||
// Found it, remove it from the list here.
|
||
//
|
||
|
||
RemoveEntryList (p);
|
||
Found = TRUE;
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
|
||
//
|
||
// The following dereference will complete the I/O, provided removes
|
||
// the last reference on the request object. The I/O will complete
|
||
// with the status and information stored in the Irp. Therefore,
|
||
// we set those values here before the dereference.
|
||
//
|
||
|
||
StCompleteRequest (Request, STATUS_CANCELLED, 0);
|
||
break;
|
||
|
||
}
|
||
|
||
p = p->Flink;
|
||
|
||
}
|
||
|
||
if (!Found) {
|
||
|
||
//
|
||
// We didn't find it!
|
||
//
|
||
|
||
RELEASE_SPIN_LOCK (&Connection->SpinLock, oldirql);
|
||
IoReleaseCancelSpinLock (Irp->CancelIrql);
|
||
}
|
||
|
||
}
|