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.
385 lines
10 KiB
385 lines
10 KiB
/*++
|
|
|
|
Copyright (c) 1989-1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
send.c
|
|
|
|
Abstract:
|
|
|
|
This module contains code which performs the following TDI services:
|
|
|
|
o TdiSend
|
|
o TdiSendDatagram
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "st.h"
|
|
|
|
|
|
NTSTATUS
|
|
StTdiSend(
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the TdiSend request for the transport provider.
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to the I/O Request Packet for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldirql, cancelirql;
|
|
NTSTATUS status;
|
|
PTP_CONNECTION connection;
|
|
PMDL SendBuffer;
|
|
ULONG SendBufferLength;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PTDI_REQUEST_KERNEL_SEND parameters;
|
|
PIRP TempIrp;
|
|
|
|
//
|
|
// Determine which connection this send belongs on.
|
|
//
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
connection = irpSp->FileObject->FsContext;
|
|
|
|
//
|
|
// Check that this is really a connection.
|
|
//
|
|
|
|
if ((connection->Size != sizeof (TP_CONNECTION)) ||
|
|
(connection->Type != ST_CONNECTION_SIGNATURE)) {
|
|
return STATUS_INVALID_CONNECTION;
|
|
}
|
|
|
|
//
|
|
// Now map the data in to SVA space.
|
|
//
|
|
|
|
parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters);
|
|
SendBuffer = Irp->MdlAddress;
|
|
SendBufferLength = parameters->SendLength;
|
|
|
|
//
|
|
// Interpret send options.
|
|
//
|
|
|
|
//
|
|
// Now we have a reference on the connection object. Queue up this
|
|
// send to the connection object.
|
|
//
|
|
|
|
|
|
// This reference is removed by TdiDestroyRequest
|
|
|
|
StReferenceConnection("TdiSend", connection);
|
|
|
|
IRP_CONNECTION(irpSp) = connection;
|
|
IRP_REFCOUNT(irpSp) = 1;
|
|
|
|
IoAcquireCancelSpinLock(&cancelirql);
|
|
ACQUIRE_SPIN_LOCK (&connection->SpinLock,&oldirql);
|
|
|
|
if ((connection->Flags & CONNECTION_FLAGS_STOPPING) != 0) {
|
|
RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
StCompleteSendIrp(
|
|
Irp,
|
|
connection->Status,
|
|
0);
|
|
status = STATUS_PENDING;
|
|
} else {
|
|
|
|
StReferenceConnection ("Verify Temp Use", connection);
|
|
|
|
//
|
|
// Insert onto the send queue, and make the IRP
|
|
// cancellable.
|
|
//
|
|
|
|
InsertTailList (&connection->SendQueue,&Irp->Tail.Overlay.ListEntry);
|
|
|
|
|
|
//
|
|
// If this IRP has been cancelled, then call the
|
|
// cancel routine.
|
|
//
|
|
|
|
if (Irp->Cancel) {
|
|
RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql);
|
|
Irp->CancelIrql = cancelirql;
|
|
StCancelSend((PDEVICE_OBJECT)(connection->Provider), Irp);
|
|
StDereferenceConnection ("IRP cancelled", connection); // release lookup hold.
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
Irp->CancelRoutine = StCancelSend;
|
|
|
|
//
|
|
// If this connection is waiting for an EOR to appear because a non-EOR
|
|
// send failed at some point in the past, fail this send. Clear the
|
|
// flag that causes this if this request has the EOR set.
|
|
//
|
|
// BUGBUG: Should the FailSend status be clearer here?
|
|
//
|
|
|
|
if ((connection->Flags & CONNECTION_FLAGS_FAILING_TO_EOR) != 0) {
|
|
|
|
RELEASE_SPIN_LOCK (&connection->SpinLock,oldirql);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
//
|
|
// BUGBUG: Should we save status from real failure?
|
|
//
|
|
|
|
FailSend (connection, STATUS_LINK_FAILED, TRUE);
|
|
|
|
if ( (parameters->SendFlags & TDI_SEND_PARTIAL) == 0) {
|
|
connection->Flags &= ~CONNECTION_FLAGS_FAILING_TO_EOR;
|
|
}
|
|
|
|
StDereferenceConnection ("Failing to EOR", connection); // release lookup hold.
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
|
|
//
|
|
// If the send state is either IDLE or W_EOR, then we should
|
|
// begin packetizing this send. Otherwise, some other event
|
|
// will cause it to be packetized.
|
|
//
|
|
|
|
//
|
|
// NOTE: If we call StartPacketizingConnection, we make
|
|
// sure that it is the last operation we do on this
|
|
// connection. This allows us to "hand off" the reference
|
|
// we have to that function, which converts it into
|
|
// a reference for being on the packetize queue.
|
|
//
|
|
|
|
switch (connection->SendState) {
|
|
|
|
case CONNECTION_SENDSTATE_IDLE:
|
|
|
|
InitializeSend (connection); // sets state to PACKETIZE
|
|
|
|
//
|
|
// If we can, packetize right now.
|
|
//
|
|
|
|
if ((!(connection->Flags & CONNECTION_FLAGS_PACKETIZE)) &&
|
|
(!(connection->Flags & CONNECTION_FLAGS_STOPPING))) {
|
|
|
|
connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
|
|
|
|
RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
PacketizeSend (connection);
|
|
|
|
} else {
|
|
|
|
RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
StDereferenceConnection ("Stopping or already packetizing", connection); // release lookup hold.
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CONNECTION_SENDSTATE_W_EOR:
|
|
connection->SendState = CONNECTION_SENDSTATE_PACKETIZE;
|
|
|
|
//
|
|
// Adjust the send variables on the connection so that
|
|
// they correctly point to this new send. We can't call
|
|
// InitializeSend to do that, because we need to keep
|
|
// track of the other outstanding sends on this connection
|
|
// which have been sent but are a part of this message.
|
|
//
|
|
|
|
TempIrp = CONTAINING_RECORD(
|
|
connection->SendQueue.Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry);
|
|
|
|
connection->sp.CurrentSendIrp = TempIrp;
|
|
connection->sp.CurrentSendMdl = TempIrp->MdlAddress;
|
|
connection->sp.SendByteOffset = 0;
|
|
connection->CurrentSendLength +=
|
|
IRP_SEND_LENGTH(IoGetCurrentIrpStackLocation(TempIrp));
|
|
|
|
StartPacketizingConnection (connection, TRUE, oldirql, cancelirql);
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// The connection is in another state (such as
|
|
// W_ACK or W_LINK), we just need to make sure
|
|
// to call InitializeSend if the new one is
|
|
// the first one on the list.
|
|
//
|
|
|
|
//
|
|
// BUGBUG: Currently InitializeSend sets SendState,
|
|
// we should fix this.
|
|
//
|
|
|
|
if (connection->SendQueue.Flink == &Irp->Tail.Overlay.ListEntry) {
|
|
ULONG SavedSendState;
|
|
SavedSendState = connection->SendState;
|
|
InitializeSend (connection);
|
|
connection->SendState = SavedSendState;
|
|
}
|
|
RELEASE_SPIN_LOCK (&connection->SpinLock, oldirql);
|
|
IoReleaseCancelSpinLock(cancelirql);
|
|
|
|
StDereferenceConnection("temp TdiSend", connection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
status = STATUS_PENDING;
|
|
|
|
|
|
return status;
|
|
} /* TdiSend */
|
|
|
|
|
|
NTSTATUS
|
|
StTdiSendDatagram(
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the TdiSendDatagram request for the transport
|
|
provider.
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to the I/O Request Packet for this request.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status of operation.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
KIRQL oldirql;
|
|
PTP_REQUEST tpRequest;
|
|
PTP_ADDRESS_FILE addressFile;
|
|
PTP_ADDRESS address;
|
|
PMDL SendBuffer;
|
|
ULONG SendBufferLength;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PTDI_REQUEST_KERNEL_SENDDG parameters;
|
|
LARGE_INTEGER timeout = {0,0};
|
|
UINT MaxUserData;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation (Irp);
|
|
addressFile = irpSp->FileObject->FsContext;
|
|
|
|
status = StVerifyAddressObject (addressFile);
|
|
if (!NT_SUCCESS (status)) {
|
|
return status;
|
|
}
|
|
|
|
address = addressFile->Address;
|
|
parameters = (PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters);
|
|
SendBuffer = Irp->MdlAddress;
|
|
SendBufferLength = parameters->SendLength;
|
|
|
|
//
|
|
// Check that the length is short enough.
|
|
//
|
|
|
|
MacReturnMaxDataSize(
|
|
&address->Provider->MacInfo,
|
|
NULL,
|
|
0,
|
|
address->Provider->MaxSendPacketSize,
|
|
&MaxUserData);
|
|
|
|
if (SendBufferLength >
|
|
(MaxUserData - sizeof(ST_HEADER))) {
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
//
|
|
// We need a request object to keep track of this TDI request.
|
|
// Attach this request to the address object.
|
|
//
|
|
|
|
status = StCreateRequest (
|
|
Irp, // IRP for this request.
|
|
address, // context.
|
|
REQUEST_FLAGS_ADDRESS, // partial flags.
|
|
SendBuffer, // the data to be sent.
|
|
SendBufferLength, // length of the data.
|
|
timeout,
|
|
&tpRequest);
|
|
|
|
if (!NT_SUCCESS (status)) {
|
|
StDereferenceAddress ("no send request", address);
|
|
return status; // if we couldn't queue the request.
|
|
}
|
|
|
|
StReferenceAddress ("Send datagram", address);
|
|
tpRequest->Owner = AddressType;
|
|
|
|
ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql);
|
|
|
|
if ((address->Flags & ADDRESS_FLAGS_STOPPING) != 0) {
|
|
RELEASE_SPIN_LOCK (&address->SpinLock,oldirql);
|
|
StCompleteRequest (tpRequest, STATUS_NETWORK_NAME_DELETED, 0);
|
|
return STATUS_PENDING;
|
|
} else {
|
|
InsertTailList (
|
|
&address->SendDatagramQueue,
|
|
&tpRequest->Linkage);
|
|
RELEASE_SPIN_LOCK (&address->SpinLock,oldirql);
|
|
}
|
|
|
|
//
|
|
// The request is queued. Ship the next request at the head of the queue,
|
|
// provided the completion handler is not active. We serialize this so
|
|
// that only one MDL and ST datagram header needs to be statically
|
|
// allocated for reuse by all send datagram requests.
|
|
//
|
|
|
|
(VOID)StSendDatagramsOnAddress (address);
|
|
|
|
StDereferenceAddress("tmp send datagram", address);
|
|
|
|
return STATUS_PENDING;
|
|
|
|
} /* StTdiSendDatagram */
|