summaryrefslogtreecommitdiffstats
path: root/private/ntos/tdi/nbf/send.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/tdi/nbf/send.c503
1 files changed, 503 insertions, 0 deletions
diff --git a/private/ntos/tdi/nbf/send.c b/private/ntos/tdi/nbf/send.c
new file mode 100644
index 000000000..a8a6cc7f3
--- /dev/null
+++ b/private/ntos/tdi/nbf/send.c
@@ -0,0 +1,503 @@
+/*++
+
+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 ((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.
+ //
+ // BUGBUG: 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);
+
+ //
+ // BUGBUG: 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.
+ //
+
+ //
+ // 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;
+ }
+
+#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);
+ 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 */