/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    rxce.c

Abstract:

    This module implements the RXCE routines related to binding/unbinding, dynamic
    enabling/disabling of transports.

Revision History:

    Balan Sethu Raman     [SethuR]    15-Feb-1995

Notes:

    The number of transport bindings are in all probability very few ( mostly one or two).

--*/

#include "precomp.h"
#pragma  hdrstop

#ifdef  ALLOC_PRAGMA
#pragma alloc_text(PAGE, RxCeBuildTransport)
#pragma alloc_text(PAGE, RxCeTearDownTransport)
#pragma alloc_text(PAGE, RxCeQueryAdapterStatus)
#pragma alloc_text(PAGE, RxCeQueryTransportInformation)
#pragma alloc_text(PAGE, DuplicateTransportAddress)
#pragma alloc_text(PAGE, RxCeBuildAddress)
#pragma alloc_text(PAGE, RxCeTearDownAddress)
#endif

//
//  The debug trace level
//

#define Dbg  (DEBUG_TRACE_RXCEBINDING)


NTSTATUS
RxCeBuildTransport(
    IN  OUT PRXCE_TRANSPORT pTransport,
    IN      PUNICODE_STRING pTransportName,
    IN      ULONG           QualityOfService)
/*++

Routine Description:

    This routine binds to the transport specified.

Arguments:


    pTransportName - the binding string for the desired transport

    QualityOfService - the quality of service desired from the transport.

Return Value:

    STATUS_SUCCESS - if the call was successfull.

Notes:

     The RDBSS or RXCE do not paticipate in the computation of quality
     of service. They essentially use it as a magic number that needs
     to be passed to the underlying transport provider.

     At present we ignore the QualityOfService parameter. How should a request for
     binding to a transport that has been currently bound to with a lower quality of
     service be handled?

--*/
{
    NTSTATUS        Status = STATUS_SUCCESS;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeBinding,RxCeBindToTransport);

    try {
        pTransport->Signature = RXCE_TRANSPORT_SIGNATURE;

        pTransport->ConnectionCount = 0;
        pTransport->VirtualCircuitCount = 0;
        pTransport->pDeviceObject = NULL;
        pTransport->ControlChannel = INVALID_HANDLE_VALUE;
        pTransport->pControlChannelFileObject = NULL;

        pTransport->Name.MaximumLength = pTransportName->Length;
        pTransport->Name.Length = pTransportName->Length;

        pTransport->pProviderInfo
            = RxAllocatePoolWithTag(
                  PagedPool,
                  sizeof(RXCE_TRANSPORT_PROVIDER_INFO),
                  RXCE_TRANSPORT_POOLTAG);


        pTransport->Name.Buffer = RxAllocatePoolWithTag(
                                      NonPagedPool,
                                      pTransport->Name.Length,
                                      RXCE_TRANSPORT_POOLTAG);

        if ((pTransport->pProviderInfo != NULL) &&
            (pTransport->Name.Buffer != NULL)) {
            RtlCopyMemory(
                pTransport->Name.Buffer,
                pTransportName->Buffer,
                pTransport->Name.Length);

            // Initialize the transport information.
            Status = RxTdiBindToTransport(
                         pTransport);

            // Ensure that the quality of service criterion is met.

            // Cleanup if the operation was not successfull.
            if (!NT_SUCCESS(Status)) {
                RxDbgTrace(0, Dbg, ("RxTdiBindToTransport returned %lx\n",Status));
                RxCeTearDownTransport(pTransport);
            } else {
                pTransport->QualityOfService = QualityOfService;
            }
        } else {
            RxCeTearDownTransport(pTransport);

            Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RxCeBindToTransport T: %lx\n",pTransport));
            RxWmiLog(LOG,
                     RxCeBuildTransport,
                     LOGPTR(pTransport));
        }
    }

    return Status;
}

NTSTATUS
RxCeTearDownTransport(
    IN PRXCE_TRANSPORT pTransport)
/*++

Routine Description:

    This routine unbinds from the transport specified.

Arguments:

    pTransport - the transport instance

Return Value:

    STATUS_SUCCESS - if the call was successfull.

Notes:

    if a transport that has not been bound to is specified no error is
    returned. The operation trivially succeeds.

--*/
{
    NTSTATUS        Status = STATUS_SUCCESS;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeBinding,RxCeUnbindFromTransport);

    try {
        if (RxCeIsTransportValid(pTransport)) {
            if (pTransport->pDeviceObject != NULL) {
                Status = RxTdiUnbindFromTransport(pTransport);
            }

            RxDbgTrace(0, Dbg,("RxTdiUnbindFromTransport returned %lx\n",Status));

            if (pTransport->Name.Buffer != NULL) {
                RxFreePool(pTransport->Name.Buffer);
            }

            if (pTransport->pProviderInfo != NULL ) {
                RxFreePool(pTransport->pProviderInfo);
            }

            pTransport->ConnectionCount = 0;
            pTransport->VirtualCircuitCount = 0;
            pTransport->pProviderInfo = NULL;
            pTransport->pDeviceObject = NULL;
            pTransport->ControlChannel = INVALID_HANDLE_VALUE;
            pTransport->pControlChannelFileObject = NULL;

            Status = STATUS_SUCCESS;
        }
    } finally {
        if (AbnormalTermination()) {
            RxLog(("RxCeTdT: T: %lx\n",pTransport));
            RxWmiLog(LOG,
                     RxCeTearDownTransport,
                     LOGPTR(pTransport));
            Status = STATUS_INVALID_PARAMETER;
        }
    }

    return Status;
}


NTSTATUS
RxCeQueryAdapterStatus(
    PRXCE_TRANSPORT pTransport,
    PADAPTER_STATUS pAdapterStatus)
/*++

Routine Description:

    This routine returns the name of a given transport in a caller allocated buffer

Arguments:

    pTransport     - the RXCE_TRANSPORT instance

    pAdapterStatus - the adapter status of the transport

Return Value:

    STATUS_SUCCESS - if the call was successfull.

--*/
{
    NTSTATUS Status = STATUS_INVALID_PARAMETER;

    PAGED_CODE();

    try {
        if (RxCeIsTransportValid(pTransport)) {
            Status = RxTdiQueryAdapterStatus(pTransport,pAdapterStatus);
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RXCeQAS: T: %lx\n",pTransport));
            RxWmiLog(LOG,
                     RxCeQueryAdapterStatus,
                     LOGPTR(pTransport));
        }
    }

    return Status;
}

NTSTATUS
RxCeQueryTransportInformation(
    PRXCE_TRANSPORT             pTransport,
    PRXCE_TRANSPORT_INFORMATION pTransportInformation)
/*++

Routine Description:

    This routine returns the transport information for a given transport

Arguments:

    pTransport            - the RXCE_TRANSPORT

    pTransportInformation - the information for the transport

Return Value:

    STATUS_SUCCESS - if the call was successfull.

--*/
{
    NTSTATUS Status = STATUS_INVALID_PARAMETER;

    PRXCE_TRANSPORT_PROVIDER_INFO pProviderInfo;

    PAGED_CODE();

    try {
        if (RxCeIsTransportValid(pTransport)) {
            pProviderInfo = (PRXCE_TRANSPORT_PROVIDER_INFO)pTransportInformation;

            *pProviderInfo = *(pTransport->pProviderInfo);
            pTransportInformation->ConnectionCount  = pTransport->ConnectionCount;
            pTransportInformation->QualityOfService = pTransport->QualityOfService;

            Status = STATUS_SUCCESS;
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RXCeQTI: T: %lx\n",pTransport));
            RxWmiLog(LOG,
                     RxCeQueryTransportInformation,
                     LOGPTR(pTransport));
        }
    }

    return Status;
}

NTSTATUS
DuplicateTransportAddress(
    PTRANSPORT_ADDRESS *pCopy,
    PTRANSPORT_ADDRESS pOriginal,
    POOL_TYPE          PoolType)
/*++

Routine Description:

    This routine duplicates a transport addresses.

Arguments:

    pCopy  - the pointer to the new copy

    pOriginal - the original.

    PoolType - type of pool for memory allocation

Return Value:

    STATUS_SUCCESS if successful.

Notes:

--*/
{
    ULONG Size = ComputeTransportAddressLength(pOriginal);

    PAGED_CODE();

    *pCopy = (PTRANSPORT_ADDRESS)
             RxAllocatePoolWithTag(
                 PoolType,
                 Size,
                 RXCE_TRANSPORT_POOLTAG);

    if (*pCopy != NULL) {

        RtlCopyMemory(*pCopy,pOriginal,Size);

        return STATUS_SUCCESS;
    } else
        return STATUS_INSUFFICIENT_RESOURCES;
}

NTSTATUS
RxCeBuildAddress(
    IN OUT PRXCE_ADDRESS               pAddress,
    IN     PRXCE_TRANSPORT             pTransport,
    IN     PTRANSPORT_ADDRESS          pTransportAddress,
    IN     PRXCE_ADDRESS_EVENT_HANDLER pHandler,
    IN     PVOID                       pEventContext)
/*++

Routine Description:

    This routine associated a transport address with a transport binding.

Arguments:

    pAddress           - the address instance

    pTransport         - the transport with whihc this address is to be associated

    pTransportAddress  - the transport address to be associated with the binding

    pHandler           - the event handler associated with the registration.

    pEventContext      - the context parameter to be passed back to the event handler

Return Value:

    STATUS_SUCCESS if successfull.

Notes:

--*/
{
    NTSTATUS        Status       = STATUS_INVALID_PARAMETER;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeRegisterClientAddress);

    try {
        if (RxCeIsTransportValid(pTransport)) {
            pAddress->Signature = RXCE_ADDRESS_SIGNATURE;

            pAddress->pTransport = pTransport;
            pAddress->hAddress = INVALID_HANDLE_VALUE;
            pAddress->pFileObject = NULL;
            pAddress->pHandler = NULL;
            pAddress->pTransportAddress = NULL;
            pAddress->pReceiveMdl = NULL;

            // Allocate the mmeory for the event handling dispatch vector
            pAddress->pHandler = (PRXCE_ADDRESS_EVENT_HANDLER)
                                 RxAllocatePoolWithTag(
                                     NonPagedPool,
                                     sizeof(RXCE_ADDRESS_EVENT_HANDLER),
                                     RXCE_ADDRESS_POOLTAG);

            if (pAddress->pHandler != NULL) {
                RtlZeroMemory(
                    pAddress->pHandler,
                    sizeof(RXCE_ADDRESS_EVENT_HANDLER));

                // Duplicate the transport address for future searches
                Status = DuplicateTransportAddress(
                             &pAddress->pTransportAddress,
                             pTransportAddress,
                             PagedPool);
            } else {
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }

            if (NT_SUCCESS(Status)) {
                // Open the address w.r.t a transport provider
                Status = RxTdiOpenAddress(
                             pTransport,
                             pTransportAddress,
                             pAddress);

                if (NT_SUCCESS(Status)) {
                    // Initialize the handler and the associated context
                    if (pHandler != NULL) {
                        *(pAddress->pHandler) = *pHandler;
                        pAddress->pContext = pEventContext;
                    }
                } else {
                    RxCeTearDownAddress(pAddress);
                    RxDbgTrace(0, Dbg,("RxTdiOpenAddress returned %lx\n",Status));
                }
            } else {
                RxDbgTrace(0, Dbg,("RxCeOpenAddress returned %lx\n",Status));
            }
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
            RxLog(("RxCeBA: T: %lx A: %lx\n",pTransport,pAddress));
            RxWmiLog(LOG,
                     RxCeBuildAddress,
                     LOGPTR(pTransport)
                     LOGPTR(pAddress));
        }
    }

    return Status;
}

NTSTATUS
RxCeTearDownAddress(
    IN PRXCE_ADDRESS pAddress)
/*++

Routine Description:

    This routine deregisters a transport address from a transport binding

Arguments:

    pAddress - the RxCe address denoting the transport binding/Transport address
               tuple.

Return Value:

    STATUS_SUCCESS if successful.

Notes:

--*/
{
    NTSTATUS        Status = STATUS_INVALID_PARAMETER;
    PRXCE_TRANSPORT pTransport;

    PAGED_CODE();

    // Update profiling info.
    RxProfile(RxCeManagement,RxCeDeregisterClientAddress);

    try {
        pTransport = pAddress->pTransport;

        if (RxCeIsAddressValid(pAddress) &&
            RxCeIsTransportValid(pTransport)) {
            // close the address object.

            if (pAddress->hAddress != INVALID_HANDLE_VALUE) {
                Status = RxTdiCloseAddress(pAddress);

                if (!NT_SUCCESS(Status)) {
                    RxDbgTrace(0, Dbg,("RxTdiCloseAddress returned %lx\n",Status));
                }
            }

            if (pAddress->pHandler != NULL) {
                RxFreePool(pAddress->pHandler);
            }

            if (pAddress->pTransportAddress != NULL) {
                RxFreePool(pAddress->pTransportAddress);
            }

            pAddress->pTransport = pTransport;
            pAddress->hAddress = INVALID_HANDLE_VALUE;
            pAddress->pFileObject = NULL;
            pAddress->pHandler = NULL;
            pAddress->pTransportAddress = NULL;
            pAddress->pReceiveMdl = NULL;
        }
    } finally {
        if (AbnormalTermination()) {
            Status = STATUS_INVALID_PARAMETER;
        }
    }

   return Status;
}