/*++

Copyright (c) 1998-1999 Microsoft Corporation

Module Name:

    sendbuffer.c

Abstract:

    This module implements the buffered send package.

Author:

    Keith Moore (keithmo)       14-Aug-1998

Revision History:

--*/


#include "precomp.h"


//
// Private constants.
//


//
// Private types.
//


//
// Private prototypes.
//


//
// Private globals.
//


#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, UlBufferedSendFile )
#endif  // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlInitializeSendBuffer
NOT PAGEABLE -- UlBufferedSendData
NOT PAGEABLE -- UlFlushSendBuffer
NOT PAGEABLE -- UlCleanupSendBuffer
#endif


//
// Public functions.
//

/***************************************************************************++

Routine Description:

    Initializes a send buffer.

Arguments:

    pSendBuffer - Supplies the MDL_SEND_BUFFER to initialize.

    pConnection - Supplies the UL_CONNECTION to associate with the
        send buffer.

--***************************************************************************/
VOID
UlInitializeSendBuffer(
    OUT PMDL_SEND_BUFFER pSendBuffer,
    IN PUL_CONNECTION pConnection
    )
{
    pSendBuffer->pConnection = pConnection;
    pSendBuffer->pMdlChain = NULL;
    pSendBuffer->pMdlLink = &pSendBuffer->pMdlChain;
    pSendBuffer->BytesBuffered = 0;

}   // UlInitializeSendBuffer


/***************************************************************************++

Routine Description:

    Initiates a send on the specified send buffer. If possible, new MDLs
    are created and linked onto the buffer's MDL chain and the send is
    deferred.

Arguments:

    pSendBuffer - Supplies the MDL_SEND_BUFFER to send.

    pMdlChain - Supplies a pointer to a MDL chain describing the
        data buffers to send.

    ForceFlush - Supplies TRUE if the buffer should be always be
        flushed. In other words, a send will always be initiated
        regardless of the amount of data buffered. Supplies FALSE
        if the incoming MDL should be linked onto the MDL chain
        for a deferred send, if possible.

    pCompletionRoutine - Supplies a pointer to a completion routine to
        invoke after the send is complete.

    pCompletionContext - Supplies an uninterpreted context value for the
        completion routine.

Return Value:

    NTSTATUS - Completion status. Will be STATUS_SUCCESS if the buffer
        was simply linked onto the MDL chain for a deferred send. In this
        case, the completion routine will NOT be called.

--***************************************************************************/
NTSTATUS
UlBufferedSendData(
    IN PMDL_SEND_BUFFER pSendBuffer,
    IN PMDL pMdl,
    IN BOOLEAN ForceFlush,
    IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
    IN PVOID pCompletionContext
    )
{
    NTSTATUS status;
    ULONG mdlLength;
    PMDL pMdlClone;
    PVOID pMdlAddress;

    //
    // Determine the size of the incoming MDL and update the number
    // of bytes we've buffered so far.
    //

    mdlLength = UlGetMdlChainByteCount( pMdl );
    pSendBuffer->BytesBuffered += mdlLength;

    //
    // If we've exceeded the max buffer threshold, or if the caller
    // is forcing a flush, then chain the incoming MDL onto the MDL
    // chain and initiate a send.
    //
    // CODEWORK: To make life easier, I'll also flush if the incoming
    // MDL is actually the head of a MDL chain. As an optimization,
    // it may be worth the effort to clone & attach the chain.
    //

    if (pSendBuffer->BytesBuffered >= MAX_SEND_BUFFER ||
        ForceFlush ||
        pMdl->Next != NULL)
    {
        (*pSendBuffer->pMdlLink) = pMdl;

        status = UlFlushSendBuffer(
                        pSendBuffer,
                        pCompletionRoutine,
                        pCompletionContext
                        );
    }
    else
    {
        //
        // Clone the incoming MDL, then link it onto the chain.
        //

        ASSERT( pMdl->Next == NULL );
        pMdlClone = UlCloneMdl( pMdl );

        if (pMdlClone != NULL)
        {
            IF_DEBUG( SEND_BUFFER )
            {
                KdPrint((
                    "UlBufferedSendData: buffering MDL %p [%lu] on %p\n",
                    pMdlClone,
                    mdlLength,
                    pSendBuffer
                    ));
            }

            (*pSendBuffer->pMdlLink) = pMdlClone;
            pSendBuffer->pMdlLink = &pMdlClone->Next;
            status = STATUS_SUCCESS;
        }
        else
        {
            //
            // Couldn't allocate a new MDL.
            //

            status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    return status;

}   // UlBufferedSendData


/***************************************************************************++

Routine Description:

    Initiates a send on the specified file cache entry. If possible, new
    MDLs are created and linked onto the buffer's MDL chain and the send is
    deferred.

Arguments:

    pSendBuffer - Supplies the MDL_SEND_BUFFER to send.

    pFileCacheEntry - Supplies the UL_FILE_CACHE_ENTRY describing the
        file to send.

    pByteRange - Supplies the byte range within the file to send.

    pCompletionRoutine - Supplies a pointer to a completion routine to
        invoke after the send is complete.

    pCompletionContext - Supplies an uninterpreted context value for the
        completion routine.

Return Value:

    NTSTATUS - Completion status. Will be STATUS_SUCCESS if the buffer
        was simply linked onto the MDL chain for a deferred send. In this
        case, the completion routine will NOT be called.

--***************************************************************************/
NTSTATUS
UlBufferedSendFile(
    IN PMDL_SEND_BUFFER pSendBuffer,
    IN PUL_FILE_CACHE_ENTRY pFileCacheEntry,
    IN PUL_BYTE_RANGE pByteRange,
    IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
    IN PVOID pCompletionContext
    )
{
    //
    // Sanity check.
    //

    PAGED_CODE();

    ASSERT( IS_VALID_FILE_CACHE_ENTRY( pFileCacheEntry ) );

    return STATUS_NOT_SUPPORTED;    // NYI

}   // UlBufferedSendFile


/***************************************************************************++

Routine Description:

    Flushes any pending MDLs attached to the send buffer.

Arguments:

    pSendBuffer - Supplies the MDL_SEND_BUFFER to flush.

    pCompletionRoutine - Supplies a pointer to a completion routine to
        invoke after the buffer is flushed.

    pCompletionContext - Supplies an uninterpreted context value for the
        completion routine.

Return Value:

    NTSTATUS - Completion status.

--***************************************************************************/
NTSTATUS
UlFlushSendBuffer(
    IN PMDL_SEND_BUFFER pSendBuffer,
    IN PUL_COMPLETION_ROUTINE pCompletionRoutine,
    IN PVOID pCompletionContext
    )
{
    NTSTATUS status;
    ULONG bytesBuffered;

    bytesBuffered = pSendBuffer->BytesBuffered;
    pSendBuffer->BytesBuffered = 0;

    IF_DEBUG( SEND_BUFFER )
    {
        KdPrint((
            "UlFlushSendBuffer: flushing %p [%lu]\n",
            pSendBuffer,
            bytesBuffered
            ));
    }

    if (bytesBuffered > 0)
    {
        status = UlSendData(
                        pSendBuffer->pConnection,
                        pSendBuffer->pMdlChain,
                        bytesBuffered,
                        pCompletionRoutine,
                        pCompletionContext,
                        FALSE
                        );
    }
    else
    {
        status = UlInvokeCompletionRoutine(
                        STATUS_SUCCESS,
                        0,
                        pCompletionRoutine,
                        pCompletionContext
                        );
    }

    return status;

}   // UlFlushSendBuffer


/***************************************************************************++

Routine Description:

    Cleans up any MDLs allocated & attached to the send buffer.

Arguments:

    pSendBuffer - Supplies the MDL_SEND_BUFFER to cleanup.

Return Value:

    PMDL - Returns a pointer to the last MDL on the chain, NULL
        if the chain is empty.

--***************************************************************************/
PMDL
UlCleanupSendBuffer(
    IN OUT PMDL_SEND_BUFFER pSendBuffer
    )
{
    PMDL pMdl;
    PMDL pNextMdl;

    IF_DEBUG( SEND_BUFFER )
    {
        KdPrint((
            "UlCleanupSendBuffer: cleaning %p\n",
            pSendBuffer
            ));
    }

    //
    // Walk the MDL chain and free all *except* the last one.
    // (The last MDL attached to the chain was not one of the clones
    // we allocated, it's a 'client' MDL. It's the client code's
    // responsibility to free it appropriately.)
    //

    pMdl = pSendBuffer->pMdlChain;

    while (TRUE)
    {
        pNextMdl = pMdl->Next;

        if (pNextMdl == NULL)
        {
            break;
        }

        IoFreeMdl( pMdl );
        pMdl = pNextMdl;
    }

    return pMdl;

}   // UlCleanupSendBuffer