/*++
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 */