/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    tranrecv.c

Abstract:

    This module contains the TransmitFileAndRecv code for SPUD.

Author:

    John Ballard (jballard)    21-Oct-1996

Revision History:

--*/

#include "spudp.h"


NTSTATUS
SPUDTransmitFileAndRecv(
    HANDLE                  hSocket,                // Socket handle to use for operation
    PAFD_TRANSMIT_FILE_INFO transmitInfo,           // transmit file req info
    PAFD_RECV_INFO          recvInfo,               // recv req info
    PSPUD_REQ_CONTEXT       reqContext              // context info for req
    )
{
    NTSTATUS                Status;
    KPROCESSOR_MODE         requestorMode;
    PFILE_OBJECT            fileObject;
    IO_STATUS_BLOCK         localIoStatus;
    PIRP                    irp;
    PIO_STACK_LOCATION      irpSp;
    PULONG                  majorFunction;
    PSPUD_AFD_REQ_CONTEXT   SpudReqContext;


    if (!DriverInitialized) {
        return STATUS_INVALID_DEVICE_REQUEST;
    }

#if USE_SPUD_COUNTERS
    BumpCount(CtrTransmitfileAndRecv);
#endif

    requestorMode = KeGetPreviousMode();

    if (requestorMode != KernelMode) {
        try {

            //
            // Make sure we can write to reqContext
            //

            //
            // Make initial status invalid
            //

            reqContext->IoStatus1.Status = 0xffffffff;
            reqContext->IoStatus1.Information = 0;
            reqContext->IoStatus2.Status = 0xffffffff;
            reqContext->IoStatus2.Information = 0;
            reqContext->ReqType = TransmitFileAndRecv;
            reqContext->KernelReqInfo = NULL;

            //
            // Make sure the buffer looks good
            //

            if ( recvInfo->BufferCount < 1 ) {
                ExRaiseStatus(STATUS_INVALID_PARAMETER);
            }

            ProbeForWrite( recvInfo->BufferArray->buf,
                           recvInfo->BufferArray->len,
                           sizeof(UCHAR) );

        } except(EXCEPTION_EXECUTE_HANDLER) {

            //
            // An exception has occurred while trying to probe one
            // of the callers parameters. Simply return the error
            // status code.
            //

            return GetExceptionCode();

        }
    } else {
        reqContext->IoStatus1.Status = 0xffffffff;
        reqContext->IoStatus1.Information = 0;
        reqContext->IoStatus2.Status = 0xffffffff;
        reqContext->IoStatus2.Information = 0;
        reqContext->ReqType = TransmitFileAndRecv;
        reqContext->KernelReqInfo = NULL;
    }


    //
    // Reference the socket handle
    //

    Status = ObReferenceObjectByHandle( hSocket,
                                        0L,
                                        NULL,
                                        requestorMode,
                                        (PVOID *) &fileObject,
                                        NULL );
    if (Status != STATUS_SUCCESS) {
        return Status;
    }

    //
    // If we haven't already cached the Device Object and FastIoControl
    // pointers, then do so now.
    //

    if (!AfdDeviceObject) {

        if (!(fileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
            AfdDeviceObject = IoGetRelatedDeviceObject( fileObject );
        } else {
            AfdDeviceObject = IoGetAttachedDevice( fileObject->DeviceObject );
        }

        if (!AfdDeviceObject) {
            ObDereferenceObject( fileObject );
            return STATUS_INVALID_DEVICE_REQUEST;
        }

        AfdFastIoDeviceControl =
            AfdDeviceObject->DriverObject->FastIoDispatch->FastIoDeviceControl;

        if (!AfdFastIoDeviceControl) {
            AfdDeviceObject = NULL;
            ObDereferenceObject( fileObject );
            return STATUS_INVALID_DEVICE_REQUEST;
        }

    }

    //
    // Let's check to see if fast io will work
    //

    if (AfdFastIoDeviceControl( fileObject,
                                TRUE,
                                (PVOID) transmitInfo,
                                sizeof(AFD_TRANSMIT_FILE_INFO),
                                NULL,
                                0,
                                IOCTL_AFD_TRANSMIT_FILE,
                                &localIoStatus,
                                AfdDeviceObject
                                )) {


#if USE_SPUD_COUNTERS
    BumpCount(CtrTransRecvFastTrans);
#endif

        //
        // Lets remember the completion status for this operation
        //

        try {
            reqContext->IoStatus1 = localIoStatus;
        } except( EXCEPTION_EXECUTE_HANDLER) {
            localIoStatus.Status = GetExceptionCode();
            localIoStatus.Information = 0;
        }

        if (localIoStatus.Status == STATUS_SUCCESS) {
            localIoStatus.Status = SpudAfdRecvFastReq( fileObject,
                                               recvInfo,
                                               reqContext,
                                               requestorMode );
        }

        //
        // If everything completed without pending then we can queue
        // a completion packet to the port now.
        //

        if (localIoStatus.Status != STATUS_PENDING) {

            PIO_MINI_COMPLETION_PACKET miniPacket = NULL;

            try {
                miniPacket = ExAllocatePoolWithQuotaTag( NonPagedPool,
                                                         sizeof( *miniPacket ),
                                                         'pcUI' );
            } except( EXCEPTION_EXECUTE_HANDLER ) {
                NOTHING;
            }

            if (miniPacket) {
                miniPacket->TypeFlag = 0xffffffff;
                miniPacket->KeyContext = (ULONG)reqContext;
                miniPacket->ApcContext = NULL;
                miniPacket->IoStatus = 0;
                miniPacket->IoStatusInformation = 0xffffffff;

                KeInsertQueue( (PKQUEUE) ATQIoCompletionPort,
                                &miniPacket->ListEntry );

            } else {
                localIoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
            }

            ObDereferenceObject( fileObject );
        }

        return localIoStatus.Status;
    }

#if USE_SPUD_COUNTERS
    BumpCount(CtrTransRecvSlowTrans);
#endif

    //
    // It looks like we will have to it the hard way
    // We will now build an irp for AFD
    //

    KeClearEvent( &fileObject->Event );

    //
    // Allocate and initialize the irp
    //

    irp = IoAllocateIrp( AfdDeviceObject->StackSize, TRUE );

    if (!irp) {
        ObDereferenceObject( fileObject );
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    SpudReqContext = ExAllocateFromNPagedLookasideList(
                         &SpudLookasideLists->ReqContextList);

    if (!SpudReqContext) {
        ObDereferenceObject( fileObject );
        IoFreeIrp( irp );
        return STATUS_INSUFFICIENT_RESOURCES;
    }

#if STATUS_SUPPORTED
    KeInitializeSpinLock( &SpudReqContext->Lock );
#endif
    SpudReqContext->Signature = SPUD_REQ_CONTEXT_SIGNATURE;
    SpudReqContext->Irp = irp;
    SpudReqContext->AtqContext = reqContext;
    reqContext->KernelReqInfo = SpudReqContext;

    //
    // Allocate MDL for receive buffer and Probe and Lock it.
    //

    try {

        if (recvInfo != NULL) {
            SpudReqContext->IoStatus2.Information = recvInfo->BufferArray->len;
            SpudReqContext->Mdl = IoAllocateMdl( recvInfo->BufferArray->buf,
                                                recvInfo->BufferArray->len,
                                                FALSE,
                                                FALSE,
                                                NULL );

            if ( SpudReqContext->Mdl == NULL ) {
                ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
            }

            MmProbeAndLockPages( SpudReqContext->Mdl,
                                 requestorMode,
                                 IoWriteAccess );

        }
    } except(EXCEPTION_EXECUTE_HANDLER) {
        ObDereferenceObject( fileObject );
        IoFreeIrp( irp );
        ExFreeToNPagedLookasideList( &SpudLookasideLists->ReqContextList,
                                     SpudReqContext );
        return GetExceptionCode();
    }

#if USE_SPUD_COUNTERS
    BumpCount(CtrTransRecvSlowRecv);
#endif

    IoSetCompletionRoutine( irp, SpudAfdContinueRecv, SpudReqContext, TRUE, TRUE, TRUE);

    irp->Tail.Overlay.OriginalFileObject = fileObject;
    irp->Tail.Overlay.Thread = PsGetCurrentThread();
    irp->Tail.Overlay.AuxiliaryBuffer = (PVOID)NULL;
    irp->RequestorMode = requestorMode;
    irp->PendingReturned = FALSE;
    irp->Cancel = FALSE;
    irp->CancelRoutine = (PDRIVER_CANCEL)NULL;
    irp->UserEvent = NULL;
    irp->UserIosb = &reqContext->IoStatus1;
    irp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
    irp->Overlay.AsynchronousParameters.UserApcContext = NULL;

    irpSp = IoGetNextIrpStackLocation( irp );

    majorFunction = (PULONG) (&irpSp->MajorFunction);
    *majorFunction = IRP_MJ_DEVICE_CONTROL;
    irpSp->FileObject = fileObject;
    irpSp->Control = SL_INVOKE_ON_SUCCESS |
                     SL_INVOKE_ON_ERROR   |
                     SL_INVOKE_ON_CANCEL;
    irpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
    irpSp->Parameters.DeviceIoControl.InputBufferLength = sizeof(AFD_TRANSMIT_FILE_INFO);
    irpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_AFD_TRANSMIT_FILE;
    irp->UserBuffer = NULL;
    irpSp->Parameters.DeviceIoControl.Type3InputBuffer = transmitInfo;

    IoCallDriver( AfdDeviceObject, irp );
    return STATUS_PENDING;

}