mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
383 lines
9.4 KiB
383 lines
9.4 KiB
/*++
|
|
|
|
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);
|
|
}
|
|
|
|
}
|