|
|
/*++
Copyright (c) 1989, 1990, 1991 Microsoft Corporation
Module Name:
send.c
Abstract:
This module contains code which performs the following TDI services:
o TdiSend o TdiSendDatagram
Author:
David Beaver (dbeaver) 1-July-1991
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
NTSTATUS NbfTdiSend( IN PIRP Irp )
/*++
Routine Description:
This routine performs the TdiSend request for the transport provider.
NOTE: THIS FUNCTION MUST BE CALLED AT DPC LEVEL.
Arguments:
Irp - Pointer to the I/O Request Packet for this request.
Return Value:
NTSTATUS - status of operation.
--*/
{ KIRQL oldirql, cancelIrql; PTP_CONNECTION connection; 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 ((irpSp->FileObject->FsContext2 == UlongToPtr(NBF_FILE_TYPE_CONTROL)) || (connection->Size != sizeof (TP_CONNECTION)) || (connection->Type != NBF_CONNECTION_SIGNATURE)) { #if DBG
NbfPrint2 ("TdiSend: Invalid Connection %lx Irp %lx\n", connection, Irp); #endif
return STATUS_INVALID_CONNECTION; }
#if DBG
Irp->IoStatus.Information = 0; // initialize it.
Irp->IoStatus.Status = 0x01010101; // initialize it.
#endif
//
// Interpret send options.
//
#if DBG
parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); if ((parameters->SendFlags & TDI_SEND_PARTIAL) != 0) { IF_NBFDBG (NBF_DEBUG_SENDENG) { NbfPrint0 ("NbfTdiSend: TDI_END_OF_RECORD not found.\n"); } } #endif
//
// Now we have a reference on the connection object. Queue up this
// send to the connection object.
//
//
// We would normally add a connection reference of type
// CREF_SEND_IRP, however we delay doing this until we
// know we are not going to call PacketizeSend with the
// second parameter TRUE. If we do call that it assumes
// we have not added the reference.
//
IRP_SEND_IRP(irpSp) = Irp; IRP_SEND_REFCOUNT(irpSp) = 1;
KeRaiseIrql (DISPATCH_LEVEL, &oldirql);
ACQUIRE_DPC_C_SPIN_LOCK (&connection->SpinLock);
if ((connection->Flags & CONNECTION_FLAGS_READY) == 0) {
RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock);
Irp->IoStatus.Status = connection->Status; Irp->IoStatus.Information = 0;
NbfDereferenceSendIrp ("Complete", irpSp, RREF_CREATION); // remove creation reference.
} else {
//
// Once the reference is in, LinkSpinLock will stay valid.
//
NbfReferenceConnection ("Verify Temp Use", connection, CREF_BY_ID); RELEASE_DPC_C_SPIN_LOCK (&connection->SpinLock);
IoAcquireCancelSpinLock(&cancelIrql); ACQUIRE_DPC_SPIN_LOCK (connection->LinkSpinLock);
#if DBG
NbfSends[NbfSendsNext].Irp = Irp; NbfSends[NbfSendsNext].Request = NULL; NbfSends[NbfSendsNext].Connection = (PVOID)connection; { ULONG i,j; PUCHAR va; PMDL mdl;
mdl = Irp->MdlAddress; if (parameters->SendLength > TRACK_TDI_CAPTURE) { NbfSends[NbfSendsNext].Contents[0] = 0xFF; } else { NbfSends[NbfSendsNext].Contents[0] = (UCHAR)parameters->SendLength; }
i = 1; while (i < TRACK_TDI_CAPTURE) { if (mdl == NULL) break; for ( va = MmGetSystemAddressForMdl (mdl), j = MmGetMdlByteCount (mdl); (i < TRACK_TDI_CAPTURE) && (j > 0); i++, j-- ) { NbfSends[NbfSendsNext].Contents[i] = *va++; } mdl = mdl->Next; } }
NbfSendsNext++; if (NbfSendsNext >= TRACK_TDI_LIMIT) NbfSendsNext = 0; #endif
//
// If this IRP has been cancelled already, complete it now.
//
if (Irp->Cancel) {
#if DBG
NbfCompletedSends[NbfCompletedSendsNext].Irp = Irp; NbfCompletedSends[NbfCompletedSendsNext].Status = STATUS_CANCELLED; NbfCompletedSendsNext = (NbfCompletedSendsNext++) % TRACK_TDI_LIMIT; #endif
NbfReferenceConnection("TdiSend cancelled", connection, CREF_SEND_IRP); RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock); IoReleaseCancelSpinLock(cancelIrql);
NbfCompleteSendIrp (Irp, STATUS_CANCELLED, 0); KeLowerIrql (oldirql);
NbfDereferenceConnection ("IRP cancelled", connection, CREF_BY_ID); // release lookup hold.
return STATUS_PENDING; }
//
// Insert onto the send queue, and make the IRP
// cancellable.
//
InsertTailList (&connection->SendQueue,&Irp->Tail.Overlay.ListEntry); IoSetCancelRoutine(Irp, NbfCancelSend);
//
// Release the cancel spinlock out of order. We were at DPC level
// when we acquired both the cancel and link spinlocks, so the irqls
// don't need to be swapped.
//
ASSERT(cancelIrql == DISPATCH_LEVEL); IoReleaseCancelSpinLock(cancelIrql);
//
// 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.
//
// Should the FailSend status be clearer here?
//
if ((connection->Flags & CONNECTION_FLAGS_FAILING_TO_EOR) != 0) {
NbfReferenceConnection("TdiSend failing to EOR", connection, CREF_SEND_IRP);
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
//
// Should we save status from real failure?
//
FailSend (connection, STATUS_LINK_FAILED, TRUE);
parameters = (PTDI_REQUEST_KERNEL_SEND)(&irpSp->Parameters); if ( (parameters->SendFlags & TDI_SEND_PARTIAL) == 0) { connection->Flags &= ~CONNECTION_FLAGS_FAILING_TO_EOR; }
KeLowerIrql (oldirql);
NbfDereferenceConnection ("Failing to EOR", connection, CREF_BY_ID); // 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.
//
// NbfPrint2 ("TdiSend: Sending, connection %lx send state %lx\n",
// connection, connection->SendState);
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)) {
ASSERT (!(connection->Flags2 & CONNECTION_FLAGS2_STOPPING)); connection->Flags |= CONNECTION_FLAGS_PACKETIZE;
#if DBG
NbfReferenceConnection ("Packetize", connection, CREF_PACKETIZE_QUEUE); NbfDereferenceConnection("temp TdiSend", connection, CREF_BY_ID); #endif
//
// This releases the spinlock. Note that PacketizeSend
// assumes that the current SendIrp has a reference
// of type RREF_PACKET;
//
#if DBG
NbfReferenceSendIrp ("Packetize", irpSp, RREF_PACKET); #else
++IRP_SEND_REFCOUNT(irpSp); // OK since it was just queued.
#endif
PacketizeSend (connection, TRUE);
} else {
#if DBG
NbfReferenceConnection("TdiSend packetizing", connection, CREF_SEND_IRP); NbfDereferenceConnection ("Stopping or already packetizing", connection, CREF_BY_ID); // release lookup hold.
#endif
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
}
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 removes the CREF_BY_ID
// reference.
//
NbfReferenceConnection("TdiSend W_EOR", connection, CREF_SEND_IRP);
StartPacketizingConnection (connection, TRUE); break;
default: // NbfPrint2 ("TdiSend: Sending, unknown state! connection %lx send state %lx\n",
// connection, connection->SendState);
//
// 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.
//
//
// 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; }
#if DBG
NbfReferenceConnection("TdiSend other", connection, CREF_SEND_IRP); NbfDereferenceConnection("temp TdiSend", connection, CREF_BY_ID); #endif
RELEASE_DPC_SPIN_LOCK (connection->LinkSpinLock);
}
}
KeLowerIrql (oldirql); return STATUS_PENDING;
} /* TdiSend */
NTSTATUS NbfTdiSendDatagram( 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_ADDRESS_FILE addressFile; PTP_ADDRESS address; PIO_STACK_LOCATION irpSp; PTDI_REQUEST_KERNEL_SENDDG parameters; UINT MaxUserData;
irpSp = IoGetCurrentIrpStackLocation (Irp);
if (irpSp->FileObject->FsContext2 != (PVOID) TDI_TRANSPORT_ADDRESS_FILE) { return STATUS_INVALID_ADDRESS; }
addressFile = irpSp->FileObject->FsContext;
status = NbfVerifyAddressObject (addressFile); if (!NT_SUCCESS (status)) { IF_NBFDBG (NBF_DEBUG_SENDENG) { NbfPrint2 ("TdiSendDG: Invalid address %lx Irp %lx\n", addressFile, Irp); } return status; }
address = addressFile->Address; parameters = (PTDI_REQUEST_KERNEL_SENDDG)(&irpSp->Parameters);
//
// Check that the length is short enough.
//
MacReturnMaxDataSize( &address->Provider->MacInfo, NULL, 0, address->Provider->MaxSendPacketSize, FALSE, &MaxUserData);
if (parameters->SendLength > (MaxUserData - sizeof(DLC_FRAME) - sizeof(NBF_HDR_CONNECTIONLESS))) {
NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); return STATUS_INVALID_PARAMETER;
}
//
// If we are on a disconnected RAS link, then fail the datagram
// immediately.
//
if ((address->Provider->MacInfo.MediumAsync) && (!address->Provider->MediumSpeedAccurate)) {
NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); return STATUS_DEVICE_NOT_READY; }
//
// Check that the target address includes a Netbios component.
//
if (!(NbfValidateTdiAddress( parameters->SendDatagramInformation->RemoteAddress, parameters->SendDatagramInformation->RemoteAddressLength)) || (NbfParseTdiAddress(parameters->SendDatagramInformation->RemoteAddress, TRUE) == NULL)) {
NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY); return STATUS_BAD_NETWORK_PATH; }
ACQUIRE_SPIN_LOCK (&address->SpinLock,&oldirql);
if ((address->Flags & (ADDRESS_FLAGS_STOPPING | ADDRESS_FLAGS_CONFLICT)) != 0) {
RELEASE_SPIN_LOCK (&address->SpinLock,oldirql); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = (address->Flags & ADDRESS_FLAGS_STOPPING) ? STATUS_NETWORK_NAME_DELETED : STATUS_DUPLICATE_NAME; IoCompleteRequest (Irp, IO_NETWORK_INCREMENT);
} else {
NbfReferenceAddress ("Send datagram", address, AREF_REQUEST); Irp->IoStatus.Information = parameters->SendLength; InsertTailList ( &address->SendDatagramQueue, &Irp->Tail.Overlay.ListEntry); 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 NBF datagram header needs to be statically
// allocated for reuse by all send datagram requests.
//
(VOID)NbfSendDatagramsOnAddress (address);
}
NbfDereferenceAddress("tmp send datagram", address, AREF_VERIFY);
return STATUS_PENDING;
} /* NbfTdiSendDatagram */
|