/*++                            

Copyright (c) 1997  Microsoft Corporation

Module Name:

    cdpsend.c

Abstract:

    TDI Send datagram routines.

Author:

    Mike Massa (mikemas)           February 20, 1997

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    mikemas     02-20-97    created

Notes:

--*/

#include "precomp.h"
#pragma hdrstop
#include "cdpsend.tmh"

#ifdef ALLOC_PRAGMA

#pragma alloc_text(INIT, CdpInitializeSend)

#endif // ALLOC_PRAGMA


//
// Local Types
//

// CDP_SEND_CONTEXT is currently empty
// typedef struct {
// } CDP_SEND_CONTEXT, *PCDP_SEND_CONTEXT;
typedef PVOID PCDP_SEND_CONTEXT;

#define CDP_SEND_REQUEST_POOL_DEPTH   5

//
// Local Data
//
PCN_RESOURCE_POOL  CdpSendRequestPool = NULL;
PCN_RESOURCE_POOL  CdpMcastSendRequestPool = NULL;


//
// Routines
//
NTSTATUS
CdpInitializeSend(
    VOID
    )
{
    IF_CNDBG(CN_DEBUG_INIT) {
        CNPRINT(("[CDP] Initializing send...\n"));
    }

    CdpSendRequestPool = CnpCreateSendRequestPool(
                             CNP_VERSION_UNICAST,
                             PROTOCOL_CDP,
                             sizeof(CDP_HEADER),
                             0, // sizeof(CDP_SEND_CONTEXT),
                             CDP_SEND_REQUEST_POOL_DEPTH
                             );

    if (CdpSendRequestPool == NULL) {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    CdpMcastSendRequestPool = CnpCreateSendRequestPool(
                                  CNP_VERSION_MULTICAST,
                                  PROTOCOL_CDP,
                                  sizeof(CDP_HEADER),
                                  0, // sizeof(CDP_SEND_CONTEXT)
                                  CDP_SEND_REQUEST_POOL_DEPTH
                                  );

    if (CdpMcastSendRequestPool == NULL) {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    IF_CNDBG(CN_DEBUG_INIT) {
        CNPRINT(("[CDP] Send initialized.\n"));
    }

    return(STATUS_SUCCESS);

}  // CdpInitializeSend


VOID
CdpCleanupSend(
    VOID
    )
{
    IF_CNDBG(CN_DEBUG_INIT) {
        CNPRINT(("[CDP] Cleaning up send...\n"));
    }

    if (CdpSendRequestPool != NULL) {
        CnpDeleteSendRequestPool(CdpSendRequestPool);
    }

    if (CdpMcastSendRequestPool != NULL) {
        CnpDeleteSendRequestPool(CdpMcastSendRequestPool);
    }

    IF_CNDBG(CN_DEBUG_INIT) {
        CNPRINT(("[CDP] Send cleanup complete.\n"));
    }

    return;

}  // CdpCleanupSend


VOID
CdpCompleteSendDatagram(
    IN     NTSTATUS           Status,
    IN OUT PULONG             BytesSent,
    IN     PCNP_SEND_REQUEST  SendRequest,
    IN     PMDL               DataMdl
    )
{
    PCDP_SEND_CONTEXT  sendContext = SendRequest->UpperProtocolContext;
    PCNP_HEADER        cnpHeader = SendRequest->CnpHeader;
    PCDP_HEADER        cdpHeader = SendRequest->UpperProtocolHeader;


    if (NT_SUCCESS(Status)) {
        if (*BytesSent >= sizeof(CDP_HEADER)) {
            *BytesSent -= sizeof(CDP_HEADER);
        }
        else {
            *BytesSent = 0;
            CnAssert(FALSE);
        }

        CnTrace(CDP_SEND_DETAIL, CdpTraceSendComplete,
            "[CDP] Send of dgram to node %u port %u complete, bytes sent %u.",
            cnpHeader->DestinationAddress, // LOGULONG
            cdpHeader->DestinationPort, // LOGUSHORT
            *BytesSent // LOGULONG
            );        
    }
    else {
        CnTrace(CDP_SEND_ERROR, CdpTraceSendFailedBelow,
            "[CDP] Transport failed to send dgram to node %u port %u, "
            "data len %u, status %!status!",
            cnpHeader->DestinationAddress, // LOGULONG
            cdpHeader->DestinationPort, // LOGUSHORT
            cdpHeader->PayloadLength, // LOGUSHORT
            Status // LOGSTATUS
            );

        CnAssert(*BytesSent == 0);
    }

    CnAssert(sendContext == NULL);

    if (cnpHeader->DestinationAddress == ClusterAnyNodeId) {
        //
        // Dereference the network multicast group data structure.
        //
        if (SendRequest->McastGroup != NULL) {
            CnpDereferenceMulticastGroup(SendRequest->McastGroup);
            SendRequest->McastGroup = NULL;
        }
    }

    CnFreeResource((PCN_RESOURCE) SendRequest);

    return;

}  // CdpCompleteSendDatagram


NTSTATUS
CxSendDatagram(
    IN PIRP                  Irp,
    IN PIO_STACK_LOCATION    IrpSp
    )
{
    NTSTATUS                    status = STATUS_NOT_IMPLEMENTED;
    PCX_ADDROBJ                 addrObj;
    PTDI_REQUEST_KERNEL_SENDDG  request;
    ULONG                       bytesSent = 0;
    CN_IRQL                     cancelIrql;
    USHORT                      destPort = 0;
    CL_NODE_ID                  destNode = ClusterInvalidNodeId;


    addrObj = (PCX_ADDROBJ) IrpSp->FileObject->FsContext;
    request = (PTDI_REQUEST_KERNEL_SENDDG) &(IrpSp->Parameters);

    if (request->SendLength <= CDP_MAX_SEND_SIZE(CX_SIGNATURE_LENGTH)) {
        if ( request->SendDatagramInformation->RemoteAddressLength >=
             sizeof(TA_CLUSTER_ADDRESS)
           )
        {
            status = CxParseTransportAddress(
                         request->SendDatagramInformation->RemoteAddress,
                         request->SendDatagramInformation->RemoteAddressLength,
                         &destNode,
                         &destPort
                         );

            if (status == STATUS_SUCCESS) {
                if (destPort != 0) {
                    PCNP_SEND_REQUEST   sendRequest;

                    if (destNode == ClusterAnyNodeId) {

                        //
                        // This is a CNP multicast.
                        //
                        sendRequest = 
                            (PCNP_SEND_REQUEST) CnAllocateResource(
                                                    CdpMcastSendRequestPool
                                                    );
                    } else {

                        //
                        // This is a normal unicast.
                        //
                        sendRequest = 
                            (PCNP_SEND_REQUEST) CnAllocateResource(
                                                    CdpSendRequestPool
                                                    );
                    }

                    if (sendRequest != NULL) {
                        PCDP_HEADER             cdpHeader;
                        PCDP_SEND_CONTEXT       sendContext;
                        BOOLEAN                 checkState;
                        CL_NETWORK_ID           destNet = ClusterAnyNetworkId;

                        checkState = (addrObj->Flags &
                                      CX_AO_FLAG_CHECKSTATE) ?
                                      TRUE : FALSE;
                        
                        //
                        // Fill in the CDP header.
                        //
                        cdpHeader = sendRequest->UpperProtocolHeader;
                        RtlZeroMemory(cdpHeader, sizeof(CDP_HEADER));
                        cdpHeader->SourcePort = addrObj->LocalPort;
                        cdpHeader->DestinationPort = destPort;
                        cdpHeader->PayloadLength = (USHORT)request->SendLength;

                        //
                        // Fill in the caller portion of the CNP
                        // send request.
                        //
                        sendRequest->UpperProtocolIrp = Irp;
                        sendRequest->CompletionRoutine =
                            CdpCompleteSendDatagram;

                        //
                        // Fill in our own send context
                        // (currently nothing).
                        //
                        sendContext = sendRequest->UpperProtocolContext;
                        CnAssert(sendContext == NULL);

                        CnVerifyCpuLockMask(
                            0,                           // Required
                            CNP_LOCK_RANGE,              // Forbidden
                            CNP_PRECEEDING_LOCK_RANGE    // Maximum
                            );

                        //
                        // Send the message.
                        //

                        CnTrace(CDP_SEND_DETAIL, CdpTraceSend,
                            "[CDP] Sending dgram to node %u port %u, "
                            "data len %u.",
                            destNode, // LOGULONG
                            destPort, // LOGUSHORT
                            request->SendLength // LOGULONG
                            );

                        status = CnpSendPacket(
                                     sendRequest,
                                     destNode,
                                     Irp->MdlAddress,
                                     (USHORT) request->SendLength,
                                     checkState,
                                     destNet
                                     );

                        CnVerifyCpuLockMask(
                            0,                           // Required
                            CNP_LOCK_RANGE,              // Forbidden
                            CNP_PRECEEDING_LOCK_RANGE    // Maximum
                            );

                        return(status);
                    }
                    else {
                        status = STATUS_INSUFFICIENT_RESOURCES;
                    }
                }
                else {
                    status = STATUS_INVALID_ADDRESS_COMPONENT;
                }
            }
        }
        else {
            status = STATUS_INVALID_ADDRESS_COMPONENT;
        }
    }
    else {
        status = STATUS_INVALID_BUFFER_SIZE;
    }
    
    CnTrace(CDP_SEND_ERROR, CdpTraceSendFailedInternal,
        "[CDP] Failed to send dgram to node %u port %u, data len %u, "
        "status %!status!",
        destNode, // LOGULONG
        destPort, // LOGUSHORT
        request->SendLength, // LOGULONG
        status // LOGSTATUS
        );
    
    Irp->IoStatus.Status = status;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    CnVerifyCpuLockMask(
        0,                           // Required
        CNP_LOCK_RANGE,              // Forbidden
        CNP_PRECEEDING_LOCK_RANGE    // Maximum
        );

    return(status);

}  // CxSendDatagram