summaryrefslogblamecommitdiffstats
path: root/private/ntos/tdi/st/send.c
blob: 0e6a2b9f6fb7331208c54c5d54ddac58cb4dd6fd (plain) (tree)
































































































































































































































































































































































































                                                                                                                  
/*++

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