/***************************************************************************

Copyright (c) 1999  Microsoft Corporation

Module Name:

    COMINI.C

Abstract:

    CO-NDIS Miniport driver entry points for the Remote NDIS miniport.

Environment:

    kernel mode only

Notes:

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

    Copyright (c) 1999 Microsoft Corporation.  All Rights Reserved.


Revision History:

    12/16/99:   Created

Author:

    ArvindM

    
****************************************************************************/

#include "precomp.h"



/****************************************************************************/
/*                          RndismpCoCreateVc                               */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Entry point to create a VC. We allocate a local VC structure, and send  */
/*  off a CreateVc message to the device.                                   */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  MiniportAdapterContext - pointer to our adapter structure               */
/*  NdisVcHandle - the NDIS wrapper's handle for this VC                    */
/*  pMiniportVcContext - place to return our context for this VC            */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  NDIS_STATUS                                                             */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
RndismpCoCreateVc(IN  NDIS_HANDLE    MiniportAdapterContext,
                  IN  NDIS_HANDLE    NdisVcHandle,
                  OUT PNDIS_HANDLE   pMiniportVcContext)
{
    PRNDISMP_ADAPTER        pAdapter;
    PRNDISMP_VC             pVc;
    PRNDISMP_MESSAGE_FRAME  pMsgFrame;
    NDIS_STATUS             Status;
    ULONG                   RefCount = 0;

    pVc = NULL;

    // get adapter context
    pAdapter = PRNDISMP_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);

    do
    {
        if (pAdapter->Halting)
        {
            Status = NDIS_STATUS_NOT_ACCEPTED;
            break;
        }

        pVc = AllocateVc(pAdapter);
        if (pVc == NULL)
        {
            Status = NDIS_STATUS_RESOURCES;
            break;
        }

        RNDISMP_REF_VC(pVc);    // Creation ref

        //
        //  Prepare a CreateVc message to send to the device.
        //
        pMsgFrame = BuildRndisMessageCoMiniport(pAdapter,
                                                pVc,
                                                REMOTE_CONDIS_MP_CREATE_VC_MSG,
                                                NULL);
        if (pMsgFrame == NULL)
        {
            Status = NDIS_STATUS_RESOURCES;
            break;
        }

        pVc->VcState = RNDISMP_VC_CREATING;

        RNDISMP_REF_VC(pVc);    // Pending CreateVc response

        RNDISMP_SEND_TO_MICROPORT(pAdapter, pMsgFrame, TRUE, CompleteSendCoCreateVc);

    }
    while (FALSE);

    //
    //  Clean up if failure.
    //
    if (Status != NDIS_STATUS_SUCCESS)
    {
        if (pVc != NULL)
        {
            RNDISMP_DEREF_VC(pVc, &RefCount);  // Creation ref

            ASSERT(RefCount == 0); // the Vc should be dealloc'ed above.
        }
    }

    return (Status);
}

/****************************************************************************/
/*                          CompleteSendCoCreateVc                          */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Callback routine called when microport completes sending a CreateVc     */
/*  message to the device.                                                  */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pMsgFrame - Pointer to message frame                                    */
/*  SendStatus - status of the microport send.                              */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
CompleteSendCoCreateVc(IN PRNDISMP_MESSAGE_FRAME    pMsgFrame,
                       IN NDIS_STATUS               SendStatus)
{
    PRNDISMP_VC             pVc;
    PRNDISMP_ADAPTER        pAdapter;
    PRNDISMP_MESSAGE_FRAME  pTmpMsgFrame;
    ULONG                   RefCount = 0;

    if (SendStatus == NDIS_STATUS_SUCCESS)
    {
        //
        //  The message was sent successfully. Do nothing until
        //  we get a response from the device.
        //
    }
    else
    {
        pVc = pMsgFrame->pVc;
        pAdapter = pVc->pAdapter;

        TRACE1(("CompleteSendCoCreateVc: VC %x, Adapter %x, fail status %x\n",
                pVc, pAdapter, SendStatus));

        //
        //  Failed to send it to the device. Remove this message from
        //  the pending list and free it.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pTmpMsgFrame, pAdapter, pMsgFrame->RequestId);
        ASSERT(pMsgFrame == pTmpMsgFrame);
        DereferenceMsgFrame(pMsgFrame);

        HandleCoCreateVcFailure(pVc, SendStatus);
    }

} // CompleteSendCoCreateVc


/****************************************************************************/
/*                          HandleCoCreateVcFailure                         */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Utility routine to handle failure of a CreateVc, either due to a local  */
/*  microport send failure, or via explicit rejection by the device.        */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pVc - Pointer to VC on which this failure has occurred                  */
/*  Status - NDIS status associated with this failure                       */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
HandleCoCreateVcFailure(IN PRNDISMP_VC      pVc,
                        IN NDIS_STATUS      Status)
{
    NDIS_HANDLE         NdisVcHandle;
    BOOLEAN             bFailActivateVc = FALSE;
    PCO_CALL_PARAMETERS pCallParameters;
    ULONG               RefCount = 0;
   
    RNDISMP_ACQUIRE_VC_LOCK(pVc);

    NdisVcHandle = pVc->NdisVcHandle;

    switch (pVc->VcState)
    {
        case RNDISMP_VC_CREATING:
            pVc->VcState = RNDISMP_VC_CREATE_FAILURE;
            break;
        
        case RNDISMP_VC_CREATING_DELETE_PENDING:
            pVc->VcState = RNDISMP_VC_ALLOCATED;
            break;

        case RNDISMP_VC_CREATING_ACTIVATE_PENDING:
            bFailActivateVc = TRUE;
            pCallParameters = pVc->pCallParameters;
            pVc->VcState = RNDISMP_VC_CREATE_FAILURE;
            break;

        default:
            ASSERT(FALSE);
            break;
    }

    RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount);    // Pending CreateVc response

    if (RefCount != 0)
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);
    }

    if (bFailActivateVc)
    {
        NdisMCoActivateVcComplete(Status,
                                  NdisVcHandle,
                                  pCallParameters);
    }

} // HandleCoCreateVcFailure


/****************************************************************************/
/*                          RndismpCoDeleteVc                               */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Entry point to delete a VC. We send a DeleteVc message to the device.   */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  MiniportVcContext - pointer to our VC structure                         */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  NDIS_STATUS                                                             */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
RndismpCoDeleteVc(IN NDIS_HANDLE    MiniportVcContext)
{
    PRNDISMP_VC             pVc;
    NDIS_STATUS             Status;

    pVc = PRNDISMP_VC_FROM_CONTEXT_HANDLE(MiniportVcContext);

    Status = StartVcDeletion(pVc);
    return (Status);

} // RndismpCoDeleteVc


/****************************************************************************/
/*                          StartVcDeletion                                 */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Initiate a DeleteVc operation on the specified VC.                      */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pVc - Pointer to VC structure                                           */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  NDIS_STATUS                                                             */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
StartVcDeletion(IN PRNDISMP_VC      pVc)
{
    PRNDISMP_ADAPTER        pAdapter;
    PRNDISMP_MESSAGE_FRAME  pMsgFrame;
    NDIS_STATUS             Status;
    ULONG                   RefCount = 0;
    BOOLEAN                 bSendDeleteVc;

    pAdapter = pVc->pAdapter;

    bSendDeleteVc = FALSE;
    pMsgFrame = NULL;

    do
    {
        //
        //  Prepare a DeleteVc message to send to the device.
        //
        pMsgFrame = BuildRndisMessageCoMiniport(pAdapter,
                                                pVc,
                                                REMOTE_CONDIS_MP_DELETE_VC_MSG,
                                                NULL);

        Status = NDIS_STATUS_SUCCESS;

        TRACE2(("StartVcDeletion: VC %x, state %d, Msg %x\n", pVc, pVc->VcState, pMsgFrame));

        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        switch (pVc->VcState)
        {
            case RNDISMP_VC_CREATED:
                if (pMsgFrame != NULL)
                {
                    pVc->VcState = RNDISMP_VC_DELETING;
                    bSendDeleteVc = TRUE;
                }
                else
                {
                    Status = NDIS_STATUS_RESOURCES;
                    bSendDeleteVc = FALSE;
                }
                break;

            case RNDISMP_VC_CREATING:
                bSendDeleteVc = FALSE;
                pVc->VcState = RNDISMP_VC_CREATING_DELETE_PENDING;
                break;
            
            case RNDISMP_VC_CREATE_FAILURE:
                bSendDeleteVc = FALSE;
                pVc->VcState = RNDISMP_VC_ALLOCATED;
                break;
            
            default:
                bSendDeleteVc = FALSE;
                Status = NDIS_STATUS_NOT_ACCEPTED;
                break;
        }

        RNDISMP_RELEASE_VC_LOCK(pVc);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            break;
        }

        if (bSendDeleteVc)
        {
            ASSERT(pMsgFrame != NULL);
            RNDISMP_REF_VC(pVc);    // pending DeleteVc message

            RNDISMP_SEND_TO_MICROPORT(pAdapter, pMsgFrame, TRUE, CompleteSendCoDeleteVc);
        }

        RNDISMP_DEREF_VC(pVc, &RefCount); // successful DeleteVc

    }
    while (FALSE);

    if (!bSendDeleteVc)
    {
        if (pMsgFrame != NULL)
        {
            DereferenceMsgFrame(pMsgFrame);
        }
    }

    return (Status);

} // StartVcDeletion


/****************************************************************************/
/*                          CompleteSendCoDeleteVc                          */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Callback routine called when microport completes sending a DeleteVc     */
/*  message to the device.                                                  */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pMsgFrame - Pointer to message frame                                    */
/*  SendStatus - status of the microport send.                              */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
CompleteSendCoDeleteVc(IN PRNDISMP_MESSAGE_FRAME    pMsgFrame,
                       IN NDIS_STATUS               SendStatus)
{
    PRNDISMP_VC             pVc;
    PRNDISMP_ADAPTER        pAdapter;
    PRNDISMP_MESSAGE_FRAME  pTmpMsgFrame;

    if (SendStatus == NDIS_STATUS_SUCCESS)
    {
        //
        //  The message was sent successfully. Do nothing until
        //  we get a response from the device.
        //
    }
    else
    {
        pVc = pMsgFrame->pVc;
        pAdapter = pVc->pAdapter;

        TRACE1(("CompleteSendCoDeleteVc: VC %x, Adapter %x, fail status %x\n",
                pVc, pAdapter, SendStatus));

        //
        //  Failed to send it to the device. Remove this message from
        //  the pending list and free it.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pTmpMsgFrame, pAdapter, pMsgFrame->RequestId);
        ASSERT(pMsgFrame == pTmpMsgFrame);
        DereferenceMsgFrame(pMsgFrame);

        //
        //  Take care of the VC now.
        //
        HandleCoDeleteVcFailure(pVc, SendStatus);
    }

} // CompleteSendCoDeleteVc


/****************************************************************************/
/*                          HandleCoDeleteVcFailure                         */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Utility routine to handle failure of a DeleteVc, either due to a local  */
/*  microport send failure, or via explicit rejection by the device.        */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pVc - Pointer to VC on which this failure has occurred                  */
/*  Status - NDIS status associated with this failure                       */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
HandleCoDeleteVcFailure(IN PRNDISMP_VC      pVc,
                        IN NDIS_STATUS      Status)
{
    ULONG       RefCount = 0;

    RNDISMP_ACQUIRE_VC_LOCK(pVc);

    switch (pVc->VcState)
    {
        case RNDISMP_VC_DELETING:
            pVc->VcState = RNDISMP_VC_DELETE_FAIL;
            break;

        default:
            ASSERT(FALSE);
            break;
    }

    RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount);    // Pending DeleteVc response

    if (RefCount != 0)
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);
    }

} // HandleCoDeleteVcFailure


/****************************************************************************/
/*                          RndismpCoActivateVc                             */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Entry point to activate a VC. We send an ActivateVc message to the      */
/*  device.                                                                 */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  MiniportVcContext - pointer to our VC structure                         */
/*  pCallParameters - CONDIS parameters for the VC                          */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  NDIS_STATUS                                                             */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
RndismpCoActivateVc(IN NDIS_HANDLE          MiniportVcContext,
                    IN PCO_CALL_PARAMETERS  pCallParameters)
{
    PRNDISMP_VC             pVc;
    NDIS_STATUS             Status;

    pVc = PRNDISMP_VC_FROM_CONTEXT_HANDLE(MiniportVcContext);

    pVc->pCallParameters = pCallParameters;
    Status = StartVcActivation(pVc);

    return (Status);

} // RndismpCoActivateVc


/****************************************************************************/
/*                          StartVcActivation                               */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Start an Activate-VC operation on the specified VC.                     */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pVc - Pointer to VC structure                                           */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  NDIS_STATUS                                                             */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
StartVcActivation(IN PRNDISMP_VC            pVc)
{
    NDIS_STATUS             Status;
    PRNDISMP_MESSAGE_FRAME  pMsgFrame;
    PRNDISMP_ADAPTER        pAdapter;
    BOOLEAN                 bSendActivateVc;
    NDIS_HANDLE             NdisVcHandle;
    PCO_CALL_PARAMETERS     pCallParameters;

    Status = NDIS_STATUS_PENDING;
    bSendActivateVc = FALSE;

    NdisVcHandle = pVc->NdisVcHandle;
    pCallParameters = pVc->pCallParameters;
    pAdapter = pVc->pAdapter;

    do
    {
        //
        //  Prepare an ActivateVc message to send to the device.
        //
        pMsgFrame = BuildRndisMessageCoMiniport(pAdapter,
                                                pVc,
                                                REMOTE_CONDIS_MP_ACTIVATE_VC_MSG,
                                                pCallParameters);

        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        switch (pVc->VcState)
        {
            case RNDISMP_VC_CREATING:

                pVc->VcState = RNDISMP_VC_CREATING_ACTIVATE_PENDING;
                break;

            case RNDISMP_VC_CREATED:

                if (pMsgFrame != NULL)
                {
                    pVc->VcState = RNDISMP_VC_ACTIVATING;
                    bSendActivateVc = TRUE;
                }
                else
                {
                    TRACE1(("StartVcAct: VC %x, failed to build msg!\n", pVc));
                    Status = NDIS_STATUS_RESOURCES;
                }
                break;

            default:

                TRACE1(("StartVcAct: VC %x in invalid state %d\n", pVc, pVc->VcState));
                Status = NDIS_STATUS_NOT_ACCEPTED;
                break;
        }

        RNDISMP_RELEASE_VC_LOCK(pVc);

        if (Status != NDIS_STATUS_PENDING)
        {
            break;
        }

        if (bSendActivateVc)
        {
            ASSERT(pMsgFrame != NULL);
            RNDISMP_REF_VC(pVc);    // pending ActivateVc message

            RNDISMP_SEND_TO_MICROPORT(pAdapter, pMsgFrame, TRUE, CompleteSendCoActivateVc);
        }
    }
    while (FALSE);

    if (!bSendActivateVc)
    {
        if (pMsgFrame != NULL)
        {
            DereferenceMsgFrame(pMsgFrame);
        }
    }

    if (Status != NDIS_STATUS_PENDING)
    {
        NdisMCoActivateVcComplete(
            Status,
            NdisVcHandle,
            pCallParameters);
        
        Status = NDIS_STATUS_PENDING;
    }

    return (Status);

} // StartVcActivation


/****************************************************************************/
/*                          CompleteSendCoActivateVc                        */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Callback routine to handle send-completion of an Activate VC message.   */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pMsgFrame - Pointer to message frame                                    */
/*  SendStatus - status of the microport send.                              */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
CompleteSendCoActivateVc(IN PRNDISMP_MESSAGE_FRAME      pMsgFrame,
                         IN NDIS_STATUS                 SendStatus)
{
    PRNDISMP_VC             pVc;
    PRNDISMP_ADAPTER        pAdapter;
    PRNDISMP_MESSAGE_FRAME  pTmpMsgFrame;
    PCO_CALL_PARAMETERS     pCallParameters;
    ULONG                   RefCount = 0;
    NDIS_HANDLE             NdisVcHandle;

    if (SendStatus == NDIS_STATUS_SUCCESS)
    {
        //
        //  The message was sent successfully. Do nothing until
        //  we get a response from the device.
        //
    }
    else
    {
        pVc = pMsgFrame->pVc;
        pAdapter = pVc->pAdapter;

        TRACE1(("CompleteSendCoActivateVc: VC %x, Adapter %x, fail status %x\n",
                pVc, pAdapter, SendStatus));

        ASSERT(SendStatus != NDIS_STATUS_PENDING);

        //
        //  Failed to send it to the device. Remove this message from
        //  the pending list and free it.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pTmpMsgFrame, pAdapter, pMsgFrame->RequestId);
        ASSERT(pMsgFrame == pTmpMsgFrame);
        DereferenceMsgFrame(pMsgFrame);

        //
        //  Take care of the VC now.
        //
        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        NdisVcHandle = pVc->NdisVcHandle;
        pCallParameters = pVc->pCallParameters;

        pVc->VcState = RNDISMP_VC_CREATED;

        RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount); // pending ActivateVc

        if (RefCount != 0)
        {
            RNDISMP_RELEASE_VC_LOCK(pVc);
        }

        NdisMCoActivateVcComplete(
            SendStatus,
            NdisVcHandle,
            pCallParameters);
        
    }

} // CompleteSendCoActivateVc


/****************************************************************************/
/*                        RndismpCoDeactivateVc                             */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Entry point to de-activate a VC. We send an DeactivateVc message to the */
/*  device.                                                                 */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  MiniportVcContext - pointer to our VC structure                         */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  NDIS_STATUS                                                             */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
RndismpCoDeactivateVc(IN NDIS_HANDLE          MiniportVcContext)
{
    PRNDISMP_VC             pVc;
    PRNDISMP_ADAPTER        pAdapter;
    PRNDISMP_MESSAGE_FRAME  pMsgFrame;
    NDIS_STATUS             Status;
    NDIS_HANDLE             NdisVcHandle;
    BOOLEAN                 bVcLockAcquired = FALSE;
    BOOLEAN                 bSendDeactivateVc = FALSE;

    pMsgFrame = NULL;
    pVc = PRNDISMP_VC_FROM_CONTEXT_HANDLE(MiniportVcContext);
    pAdapter = pVc->pAdapter;
    Status = NDIS_STATUS_PENDING;

    do
    {
        //
        //  Prepare a DeactivateVc message to send to the device.
        //
        pMsgFrame = BuildRndisMessageCoMiniport(pAdapter,
                                                pVc,
                                                REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG,
                                                NULL);

        bVcLockAcquired = TRUE;
        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        NdisVcHandle = pVc->NdisVcHandle;

        if (pVc->VcState != RNDISMP_VC_ACTIVATED)
        {
            Status = NDIS_STATUS_NOT_ACCEPTED;
            break;
        }

        switch (pVc->VcState)
        {
            case RNDISMP_VC_ACTIVATED:

                if (pMsgFrame != NULL)
                {
                    bSendDeactivateVc = TRUE;
                    pVc->VcState = RNDISMP_VC_DEACTIVATING;
                }
                else
                {
                    bSendDeactivateVc = FALSE;
                    Status = NDIS_STATUS_RESOURCES;
                }
                break;

            default:

                bSendDeactivateVc = FALSE;
                break;
         }

         if (bSendDeactivateVc)
         {
            RNDISMP_REF_VC(pVc);    // pending Deactivate VC

            RNDISMP_SEND_TO_MICROPORT(pAdapter, pMsgFrame, TRUE, CompleteSendCoDeactivateVc);
        }
    }
    while (FALSE);


    if (!bSendDeactivateVc)
    {
        if (pMsgFrame != NULL)
        {
            DereferenceMsgFrame(pMsgFrame);
        }
    }

    if (Status != NDIS_STATUS_PENDING)
    {
        ASSERT(Status != NDIS_STATUS_SUCCESS);
        NdisMCoDeactivateVcComplete(
            Status,
            NdisVcHandle);
        
        Status = NDIS_STATUS_PENDING;
    }

    return (Status);
}

/****************************************************************************/
/*                          CompleteSendCoDeactivateVc                      */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Callback routine to handle send-completion of a deactivate VC message.  */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pMsgFrame - Pointer to message frame                                    */
/*  SendStatus - status of the microport send.                              */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
CompleteSendCoDeactivateVc(IN PRNDISMP_MESSAGE_FRAME    pMsgFrame,
                           IN NDIS_STATUS               SendStatus)
{
    PRNDISMP_VC             pVc;
    PRNDISMP_ADAPTER        pAdapter;
    PRNDISMP_MESSAGE_FRAME  pTmpMsgFrame;
    PCO_CALL_PARAMETERS     pCallParameters;
    ULONG                   RefCount = 0;
    NDIS_HANDLE             NdisVcHandle;

    if (SendStatus == NDIS_STATUS_SUCCESS)
    {
        //
        //  The message was sent successfully. Do nothing until
        //  we get a response from the device.
        //
    }
    else
    {
        pVc = pMsgFrame->pVc;
        pAdapter = pVc->pAdapter;

        TRACE1(("CompleteSendCoDeactivateVc: VC %x, Adapter %x, fail status %x\n",
                pVc, pAdapter, SendStatus));

        ASSERT(SendStatus != NDIS_STATUS_PENDING);

        //
        //  Failed to send it to the device. Remove this message from
        //  the pending list and free it.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pTmpMsgFrame, pAdapter, pMsgFrame->RequestId);
        ASSERT(pMsgFrame == pTmpMsgFrame);
        DereferenceMsgFrame(pMsgFrame);

        //
        //  Take care of the VC now.
        //
        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        NdisVcHandle = pVc->NdisVcHandle;
        pCallParameters = pVc->pCallParameters;

        pVc->VcState = RNDISMP_VC_ACTIVATED;

        RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount); // pending DeactivateVc

        if (RefCount != 0)
        {
            RNDISMP_RELEASE_VC_LOCK(pVc);
        }

        NdisMCoDeactivateVcComplete(
            SendStatus,
            NdisVcHandle);
        
    }

} // CompleteSendCoDeactivateVc


/****************************************************************************/
/*                          RndismpCoRequest                                */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Entry point to handle a CO-request. We send a MiniportCoRequest message */
/*  to the device.                                                          */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  MiniportAdapterContext - pointer to our adapter structure               */
/*  MiniportVcContext - pointer to our VC structure                         */
/*  pRequest - Pointer to NDIS request                                      */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  NDIS_STATUS                                                             */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
RndismpCoRequest(IN NDIS_HANDLE          MiniportAdapterContext,
                 IN NDIS_HANDLE          MiniportVcContext,
                 IN OUT PNDIS_REQUEST    pRequest)
{
    PRNDISMP_ADAPTER    pAdapter;
    PRNDISMP_VC         pVc;
    NDIS_STATUS         Status;
    NDIS_OID            Oid;

    pAdapter = PRNDISMP_ADAPTER_FROM_CONTEXT_HANDLE(MiniportAdapterContext);
    pVc = PRNDISMP_VC_FROM_CONTEXT_HANDLE(MiniportVcContext);

    switch (pRequest->RequestType)
    {
        case NdisRequestQueryInformation:
        case NdisRequestQueryStatistics:

            Oid = pRequest->DATA.QUERY_INFORMATION.Oid;

            TRACE2(("CoReq: Adapter %x, Req %x, QueryInfo/Stat (%d) Oid %x\n",
                pAdapter, pRequest, pRequest->RequestType, Oid));

            Status = ProcessQueryInformation(pAdapter,
                                             pVc,
                                             pRequest,
                                             Oid,
                                             pRequest->DATA.QUERY_INFORMATION.InformationBuffer,
                                             pRequest->DATA.QUERY_INFORMATION.InformationBufferLength,
                                             &pRequest->DATA.QUERY_INFORMATION.BytesWritten,
                                             &pRequest->DATA.QUERY_INFORMATION.BytesNeeded);
            break;
        
        case NdisRequestSetInformation:

            Oid = pRequest->DATA.SET_INFORMATION.Oid;

            TRACE1(("CoReq: Adapter %x, Req %x, SetInfo Oid %x\n",
                 pAdapter, pRequest, Oid));

            Status = ProcessSetInformation(pAdapter,
                                           pVc,
                                           pRequest,
                                           Oid,
                                           pRequest->DATA.SET_INFORMATION.InformationBuffer,
                                           pRequest->DATA.SET_INFORMATION.InformationBufferLength,
                                           &pRequest->DATA.SET_INFORMATION.BytesRead,
                                           &pRequest->DATA.SET_INFORMATION.BytesNeeded);
            break;
        
        default:
            TRACE1(("CoReq: Unsupported request type %d\n",
                        pRequest->RequestType));
                
            Status = NDIS_STATUS_NOT_SUPPORTED;
            break;
    }

    return (Status);
}

/****************************************************************************/
/*                          RndismpCoSendPackets                            */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Entry point to send one or more packets on a VC.                        */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  MiniportVcContext - pointer to our VC structure                         */
/*  PacketArray - Array of packet pointers                                  */
/*  NumberOfPackets - number of packets in array above                      */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
RndismpCoSendPackets(IN NDIS_HANDLE          MiniportVcContext,
                     IN PNDIS_PACKET *       PacketArray,
                     IN UINT                 NumberOfPackets)
{
    PRNDISMP_VC         pVc;
    UINT                i;

    pVc = PRNDISMP_VC_FROM_CONTEXT_HANDLE(MiniportVcContext);

    RNDISMP_ACQUIRE_VC_LOCK(pVc);

    pVc->RefCount += NumberOfPackets;

    if (pVc->VcState == RNDISMP_VC_ACTIVATED)
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);

        DoMultipleSend(pVc->pAdapter,
                       pVc,
                       PacketArray,
                       NumberOfPackets);
    }
    else
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);

        for (i = 0; i < NumberOfPackets; i++)
        {
            CompleteSendDataOnVc(pVc, PacketArray[i], NDIS_STATUS_VC_NOT_ACTIVATED);
        }
    }

} // RndismpCoSendPackets

/****************************************************************************/
/*                          ReceiveCreateVcComplete                         */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Process a CONDIS CreateVcComplete message from the device               */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - pointer to our Adapter structure                             */
/*  pMessage - pointer to RNDIS message                                     */
/*  pMdl - pointer to MDL received from microport                           */
/*  TotalLength - length of complete message                                */
/*  MicroportMessageContext - context for message from micorport            */
/*  ReceiveStatus - used by microport to indicate it is low on resource     */
/*  bMessageCopied - is this a copy of the original message?                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  BOOLEAN - should the message be returned to the microport?              */
/*                                                                          */
/****************************************************************************/
BOOLEAN
ReceiveCreateVcComplete(IN PRNDISMP_ADAPTER    pAdapter,
                        IN PRNDIS_MESSAGE      pMessage,
                        IN PMDL                pMdl,
                        IN ULONG               TotalLength,
                        IN NDIS_HANDLE         MicroportMessageContext,
                        IN NDIS_STATUS         ReceiveStatus,
                        IN BOOLEAN             bMessageCopied)
{
    BOOLEAN                         bDiscardPkt = TRUE;
    PRNDISMP_VC                     pVc;
    PRNDISMP_MESSAGE_FRAME          pCreateVcMsgFrame;
    PRCONDIS_MP_CREATE_VC_COMPLETE  pCreateVcComp;
    RNDISMP_VC_STATE                VcState;
    BOOLEAN                         bVcLockAcquired = FALSE;
    ULONG                           RefCount = 0;
    NDIS_STATUS                     Status;

    pVc = NULL;

    do
    {
        pCreateVcComp = RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(pMessage);

        //
        //  TBD - Validate lengths?
        //

        //
        //  Check the request Id.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pCreateVcMsgFrame, pAdapter, pCreateVcComp->RequestId);
        if (pCreateVcMsgFrame == NULL)
        {
            TRACE1(("CreateVcComp: Adapter %x, Invalid ReqId %d!\n",
                    pAdapter, pCreateVcComp->RequestId));
            break;
        }

        pVc = pCreateVcMsgFrame->pVc;
        Status = pCreateVcComp->Status;

        DereferenceMsgFrame(pCreateVcMsgFrame);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            HandleCoCreateVcFailure(pVc, Status);
            break;
        }

        bVcLockAcquired = TRUE;
        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount); // pending CreateVc

        if (RefCount == 0)
        {
            bVcLockAcquired = FALSE;
            break;
        }

        pVc->DeviceVcContext = pCreateVcComp->DeviceVcHandle;

        VcState = pVc->VcState;

        switch (VcState)
        {
            case RNDISMP_VC_CREATING:

                pVc->VcState = RNDISMP_VC_CREATED;
                break;
            
            case RNDISMP_VC_CREATING_ACTIVATE_PENDING:

                pVc->VcState = RNDISMP_VC_CREATED;
                RNDISMP_RELEASE_VC_LOCK(pVc);
                bVcLockAcquired = FALSE;

                Status = StartVcActivation(pVc);
                ASSERT(Status == NDIS_STATUS_PENDING);
                break;

            case RNDISMP_VC_CREATING_DELETE_PENDING:

                pVc->VcState = RNDISMP_VC_CREATED;
                RNDISMP_RELEASE_VC_LOCK(pVc);
                bVcLockAcquired = FALSE;

                Status = StartVcDeletion(pVc);
                break;
                
            default:

                TRACE1(("CreateVcComp: VC %x, wrong state %d\n",
                        pVc, VcState));
                break;
        }

    }
    while (FALSE);

    if (bVcLockAcquired)
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);
    }

    return (bDiscardPkt);
}

/****************************************************************************/
/*                        ReceiveActivateVcComplete                         */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Process a CONDIS ActivateVcComplete message from the device             */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - pointer to our Adapter structure                             */
/*  pMessage - pointer to RNDIS message                                     */
/*  pMdl - pointer to MDL received from microport                           */
/*  TotalLength - length of complete message                                */
/*  MicroportMessageContext - context for message from micorport            */
/*  ReceiveStatus - used by microport to indicate it is low on resource     */
/*  bMessageCopied - is this a copy of the original message?                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  BOOLEAN - should the message be returned to the microport?              */
/*                                                                          */
/****************************************************************************/
BOOLEAN
ReceiveActivateVcComplete(IN PRNDISMP_ADAPTER    pAdapter,
                          IN PRNDIS_MESSAGE      pMessage,
                          IN PMDL                pMdl,
                          IN ULONG               TotalLength,
                          IN NDIS_HANDLE         MicroportMessageContext,
                          IN NDIS_STATUS         ReceiveStatus,
                          IN BOOLEAN             bMessageCopied)
{
    BOOLEAN                         bDiscardPkt = TRUE;
    PRNDISMP_VC                     pVc;
    PRNDISMP_MESSAGE_FRAME          pActVcMsgFrame;
    PRCONDIS_MP_ACTIVATE_VC_COMPLETE        pActVcComp;
    BOOLEAN                         bVcLockAcquired = FALSE;
    ULONG                           RefCount = 0;
    NDIS_STATUS                     Status;
    NDIS_HANDLE                     NdisVcHandle;
    PCO_CALL_PARAMETERS             pCallParameters;

    pVc = NULL;

    do
    {
        pActVcComp = RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(pMessage);

        //
        //  TBD - Validate lengths?
        //

        //
        //  Check the request Id.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pActVcMsgFrame, pAdapter, pActVcComp->RequestId);
        if (pActVcMsgFrame == NULL)
        {
            TRACE1(("ActVcComp: Adapter %x, Invalid ReqId %d!\n",
                    pAdapter, pActVcComp->RequestId));
            break;
        }

        pVc = pActVcMsgFrame->pVc;

        DereferenceMsgFrame(pActVcMsgFrame);

        bVcLockAcquired = TRUE;
        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount); // pending ActivateVc

        if (RefCount == 0)
        {
            bVcLockAcquired = FALSE;
            break;
        }

        if (pVc->VcState != RNDISMP_VC_ACTIVATING)
        {
            TRACE1(("ActVcComp: Adapter %x, VC %x: invalid state %d\n",
                    pAdapter, pVc, pVc->VcState));
            break;
        }

        Status = pActVcComp->Status;

        if (Status == NDIS_STATUS_SUCCESS)
        {
            pVc->VcState = RNDISMP_VC_ACTIVATED;
        }
        else
        {
            pVc->VcState = RNDISMP_VC_CREATED;
        }
            
        NdisVcHandle = pVc->NdisVcHandle;
        pCallParameters = pVc->pCallParameters;
        
        RNDISMP_RELEASE_VC_LOCK(pVc);
        bVcLockAcquired = FALSE;

        NdisMCoActivateVcComplete(
            pActVcComp->Status,
            NdisVcHandle,
            pCallParameters);

    }
    while (FALSE);

    if (bVcLockAcquired)
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);
    }

    return (bDiscardPkt);
}

/****************************************************************************/
/*                        ReceiveDeleteVcComplete                           */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Process a CONDIS DeleteVcComplete message from the device               */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - pointer to our Adapter structure                             */
/*  pMessage - pointer to RNDIS message                                     */
/*  pMdl - pointer to MDL received from microport                           */
/*  TotalLength - length of complete message                                */
/*  MicroportMessageContext - context for message from micorport            */
/*  ReceiveStatus - used by microport to indicate it is low on resource     */
/*  bMessageCopied - is this a copy of the original message?                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  BOOLEAN - should the message be returned to the microport?              */
/*                                                                          */
/****************************************************************************/
BOOLEAN
ReceiveDeleteVcComplete(IN PRNDISMP_ADAPTER    pAdapter,
                        IN PRNDIS_MESSAGE      pMessage,
                        IN PMDL                pMdl,
                        IN ULONG               TotalLength,
                        IN NDIS_HANDLE         MicroportMessageContext,
                        IN NDIS_STATUS         ReceiveStatus,
                        IN BOOLEAN             bMessageCopied)
{
    BOOLEAN                         bDiscardPkt = TRUE;
    PRNDISMP_VC                     pVc;
    PRCONDIS_MP_DELETE_VC_COMPLETE  pDeleteVcComp;
    PRNDISMP_MESSAGE_FRAME          pDeleteVcMsgFrame;
    RNDISMP_VC_STATE                VcState;
    BOOLEAN                         bVcLockAcquired = FALSE;
    ULONG                           RefCount = 0;
    NDIS_STATUS                     Status;

    pVc = NULL;

    do
    {
        pDeleteVcComp = RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(pMessage);

        //
        //  TBD - Validate lengths?
        //

        //
        //  Check the request Id.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pDeleteVcMsgFrame, pAdapter, pDeleteVcComp->RequestId);
        if (pDeleteVcMsgFrame == NULL)
        {
            TRACE1(("DeleteVcComp: Adapter %x, Invalid ReqId %d!\n",
                    pAdapter, pDeleteVcComp->RequestId));
            break;
        }

        pVc = pDeleteVcMsgFrame->pVc;
        Status = pDeleteVcComp->Status;

        DereferenceMsgFrame(pDeleteVcMsgFrame);

        if (Status != NDIS_STATUS_SUCCESS)
        {
            HandleCoDeleteVcFailure(pVc, Status);
            break;
        }

        bVcLockAcquired = TRUE;
        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount); // pending DeleteVc

        if (RefCount == 0)
        {
            bVcLockAcquired = FALSE;
            break;
        }

        if (pVc->VcState != RNDISMP_VC_DELETING)
        {
            TRACE1(("DeleteVcComp: Adapter %x, VC %x: invalid state %d\n",
                    pAdapter, pVc, pVc->VcState));
            break;
        }

        pVc->VcState = RNDISMP_VC_ALLOCATED;

        RNDISMP_DEREF_VC(pVc, &RefCount);   // remove create ref

        if (RefCount == 0)
        {
            bVcLockAcquired = FALSE;
        }
    }
    while (FALSE);

    if (bVcLockAcquired)
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);
    }

    return (bDiscardPkt);
}

/****************************************************************************/
/*                        ReceiveDeactivateVcComplete                       */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Process a CONDIS DeActivateVcComplete message from the device           */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - pointer to our Adapter structure                             */
/*  pMessage - pointer to RNDIS message                                     */
/*  pMdl - pointer to MDL received from microport                           */
/*  TotalLength - length of complete message                                */
/*  MicroportMessageContext - context for message from micorport            */
/*  ReceiveStatus - used by microport to indicate it is low on resource     */
/*  bMessageCopied - is this a copy of the original message?                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  BOOLEAN - should the message be returned to the microport?              */
/*                                                                          */
/****************************************************************************/
BOOLEAN
ReceiveDeactivateVcComplete(IN PRNDISMP_ADAPTER    pAdapter,
                            IN PRNDIS_MESSAGE      pMessage,
                            IN PMDL                pMdl,
                            IN ULONG               TotalLength,
                            IN NDIS_HANDLE         MicroportMessageContext,
                            IN NDIS_STATUS         ReceiveStatus,
                            IN BOOLEAN             bMessageCopied)
{
    BOOLEAN                         bDiscardPkt = TRUE;
    PRNDISMP_VC                     pVc;
    RNDISMP_VC_STATE                VcState;
    PRNDISMP_MESSAGE_FRAME          pDeactivateVcMsgFrame;
    PRCONDIS_MP_DEACTIVATE_VC_COMPLETE  pDeactivateVcComp;
    BOOLEAN                         bVcLockAcquired = FALSE;
    BOOLEAN                         bAddTempRef = FALSE;
    ULONG                           RefCount = 0;
    NDIS_STATUS                     Status;

    pVc = NULL;

    do
    {
        pDeactivateVcComp = RNDIS_MESSAGE_PTR_TO_MESSAGE_PTR(pMessage);

        //
        //  TBD - Validate lengths?
        //

        //
        //  Check the request Id.
        //
        RNDISMP_LOOKUP_PENDING_MESSAGE(pDeactivateVcMsgFrame, pAdapter, pDeactivateVcComp->RequestId);
        if (pDeactivateVcMsgFrame == NULL)
        {
            TRACE1(("DeactivateVcComp: Adapter %x, Invalid ReqId %d!\n",
                    pAdapter, pDeactivateVcComp->RequestId));
            break;
        }

        pVc = pDeactivateVcMsgFrame->pVc;

        DereferenceMsgFrame(pDeactivateVcMsgFrame);

        bVcLockAcquired = TRUE;
        RNDISMP_ACQUIRE_VC_LOCK(pVc);

        RNDISMP_DEREF_VC_LOCKED(pVc, &RefCount); // pending DeactivateVc

        if (RefCount == 0)
        {
            bVcLockAcquired = FALSE;
            break;
        }

        if (pVc->VcState != RNDISMP_VC_DEACTIVATING)
        {
            TRACE1(("DeactVcComp: Adapter %x, VC %x: invalid state %d\n",
                    pAdapter, pVc, pVc->VcState));
            ASSERT(FALSE);
            break;
        }

        if (pDeactivateVcComp->Status == NDIS_STATUS_SUCCESS)
        {
            pVc->VcState = RNDISMP_VC_DEACTIVATED;

            //
            //  We add a temp ref on the VC to help complete deactivate-VC
            //  from a common place (see bAddTempRef below).
            //
            RNDISMP_REF_VC(pVc);    // temp ref, deactivate vc complete OK
            bAddTempRef = TRUE;
        }
        else
        {
            pVc->VcState = RNDISMP_VC_ACTIVATED;
        }

        RNDISMP_RELEASE_VC_LOCK(pVc);
        bVcLockAcquired = FALSE;

        if (bAddTempRef)
        {
            //
            //  The following deref will check and call NDIS'
            //  deactivate vc complete API if we don't have any
            //  outstanding sends or receives on this VC.
            //
            RNDISMP_DEREF_VC(pVc, &RefCount); // temp ref
        }
    }
    while (FALSE);

    if (bVcLockAcquired)
    {
        RNDISMP_RELEASE_VC_LOCK(pVc);
    }

    return (bDiscardPkt);
}

/****************************************************************************/
/*                          BuildRndisMessageCoMiniport                     */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Allocate resources for message and frame and build an RNDIS message     */
/*  for sending to a remote CONDIS Miniport device.                         */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - Pointer to adapter structure                                 */
/*  pVc - Pointer to VC structure                                           */
/*  NdisMessageType - RNDIS message type                                    */
/*  pCallParameters - optional pointer to call parameters, applicable to    */
/*       certain message types.                                             */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  PRNDISMP_MESSAGE_FRAME                                                  */
/*                                                                          */
/****************************************************************************/
PRNDISMP_MESSAGE_FRAME
BuildRndisMessageCoMiniport(IN  PRNDISMP_ADAPTER    pAdapter,
                            IN  PRNDISMP_VC         pVc,
                            IN  UINT                NdisMessageType,
                            IN  PCO_CALL_PARAMETERS pCallParameters OPTIONAL)
{
    PRNDIS_MESSAGE          pMessage;
    UINT                    MessageSize;
    PRNDISMP_MESSAGE_FRAME  pMsgFrame;

    switch (NdisMessageType)
    {
        case REMOTE_CONDIS_MP_CREATE_VC_MSG:
        {
            PRCONDIS_MP_CREATE_VC       pCreateVc;

            MessageSize = RNDIS_MESSAGE_SIZE(RCONDIS_MP_CREATE_VC);

            pMsgFrame = AllocateMessageAndFrame(pAdapter, MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

            pMessage = RNDISMP_GET_MSG_FROM_FRAME(pMsgFrame);
            pMessage->NdisMessageType = NdisMessageType;
            pMsgFrame->NdisMessageType = NdisMessageType;

            pCreateVc = &pMessage->Message.CoMiniportCreateVc;
            pCreateVc->RequestId = pMsgFrame->RequestId;
            pCreateVc->NdisVcHandle = pVc->VcId;

            break;
        }

        case REMOTE_CONDIS_MP_DELETE_VC_MSG:
        {
            PRCONDIS_MP_DELETE_VC       pDeleteVc;

            MessageSize = RNDIS_MESSAGE_SIZE(RCONDIS_MP_DELETE_VC);

            pMsgFrame = AllocateMessageAndFrame(pAdapter, MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

            pMessage = RNDISMP_GET_MSG_FROM_FRAME(pMsgFrame);
            pMessage->NdisMessageType = NdisMessageType;
            pMsgFrame->NdisMessageType = NdisMessageType;

            pDeleteVc = &pMessage->Message.CoMiniportDeleteVc;
            pDeleteVc->RequestId = pMsgFrame->RequestId;
            pDeleteVc->DeviceVcHandle = pVc->DeviceVcContext;

            break;
        }

        case REMOTE_CONDIS_MP_ACTIVATE_VC_MSG:
        {
            PRCONDIS_MP_ACTIVATE_VC_REQUEST             pActivateVc;
            PRCONDIS_CALL_MANAGER_PARAMETERS    pCallMgrParams;
            PRCONDIS_MEDIA_PARAMETERS           pMediaParams;
            ULONG_PTR                           FillLocation;
            UINT                                FillOffset;

            ASSERT(pCallParameters != NULL);
            MessageSize = RNDIS_MESSAGE_SIZE(RCONDIS_MP_ACTIVATE_VC_REQUEST);

            if (pCallParameters->CallMgrParameters)
            {
                MessageSize += (sizeof(RCONDIS_CALL_MANAGER_PARAMETERS) +
                                pCallParameters->CallMgrParameters->CallMgrSpecific.Length);
            }

            if (pCallParameters->MediaParameters)
            {
                MessageSize += (sizeof(RCONDIS_MEDIA_PARAMETERS) +
                                pCallParameters->MediaParameters->MediaSpecific.Length);
            }

            pMsgFrame = AllocateMessageAndFrame(pAdapter, MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

            pMessage = RNDISMP_GET_MSG_FROM_FRAME(pMsgFrame);
            pMessage->NdisMessageType = NdisMessageType;
            pMsgFrame->NdisMessageType = NdisMessageType;

            pActivateVc = &pMessage->Message.CoMiniportActivateVc;
            pActivateVc->RequestId = pMsgFrame->RequestId;
            pActivateVc->DeviceVcHandle = pVc->DeviceVcContext;
            pActivateVc->Flags = pCallParameters->Flags;

            FillOffset = RNDIS_MESSAGE_SIZE(RCONDIS_MP_ACTIVATE_VC_REQUEST);
            FillLocation = ((ULONG_PTR)pMessage + FillOffset);

            //
            //  Fill in Media parameters, if present.
            //
            if (pCallParameters->MediaParameters)
            {
                PCO_SPECIFIC_PARAMETERS     pMediaSpecific;

                pMediaSpecific = &pCallParameters->MediaParameters->MediaSpecific;
                pMediaParams = (PRCONDIS_MEDIA_PARAMETERS)FillLocation;
                pActivateVc->MediaParamsOffset = (UINT32)(FillLocation - (ULONG_PTR)pActivateVc);
                pActivateVc->MediaParamsLength = sizeof(RCONDIS_MEDIA_PARAMETERS) +
                                                    pMediaSpecific->Length;
                RNDISMP_MOVE_MEM(pMediaParams,
                                 pCallParameters->MediaParameters,
                                 FIELD_OFFSET(RCONDIS_MEDIA_PARAMETERS, MediaSpecific));

                FillLocation += sizeof(RCONDIS_MEDIA_PARAMETERS);
                FillOffset += sizeof(RCONDIS_MEDIA_PARAMETERS);

                pMediaParams->MediaSpecific.ParameterOffset =
                                 sizeof(RCONDIS_SPECIFIC_PARAMETERS);
                pMediaParams->MediaSpecific.ParameterType =
                                 pMediaSpecific->ParamType;
                pMediaParams->MediaSpecific.ParameterLength =
                                 pMediaSpecific->Length;

                RNDISMP_MOVE_MEM((PVOID)FillLocation,
                                 &pMediaSpecific->Parameters[0],
                                 pMediaSpecific->Length);

                FillLocation += pMediaSpecific->Length;
                FillOffset += pMediaSpecific->Length;
            }
            else
            {
                pActivateVc->MediaParamsOffset = 0;
                pActivateVc->MediaParamsLength = 0;
            }

            //
            //  Fill in Call manager parameters, if present.
            //
            if (pCallParameters->CallMgrParameters)
            {
                PCO_SPECIFIC_PARAMETERS     pCallMgrSpecific;

                pCallMgrSpecific = &pCallParameters->CallMgrParameters->CallMgrSpecific;

                pCallMgrParams = (PRCONDIS_CALL_MANAGER_PARAMETERS)FillLocation;
                pActivateVc->CallMgrParamsOffset = (UINT32)(FillLocation - (ULONG_PTR)pActivateVc);
                pActivateVc->CallMgrParamsLength = sizeof(RCONDIS_CALL_MANAGER_PARAMETERS) +
                                                    pCallMgrSpecific->Length;
                
                RNDISMP_MOVE_MEM(pCallMgrParams,
                                 pCallParameters->CallMgrParameters,
                                 FIELD_OFFSET(RCONDIS_CALL_MANAGER_PARAMETERS, CallMgrSpecific));

                FillLocation += sizeof(RCONDIS_CALL_MANAGER_PARAMETERS);
                FillOffset += sizeof(RCONDIS_CALL_MANAGER_PARAMETERS);
                
                pCallMgrParams->CallMgrSpecific.ParameterOffset =
                                 sizeof(RCONDIS_SPECIFIC_PARAMETERS);
                pCallMgrParams->CallMgrSpecific.ParameterType =
                                 pCallMgrSpecific->ParamType;
                pCallMgrParams->CallMgrSpecific.ParameterLength =
                                 pCallMgrSpecific->Length;
                

                RNDISMP_MOVE_MEM((PVOID)FillLocation,
                                 &pCallMgrSpecific->Parameters[0],
                                 pCallMgrSpecific->Length);

                FillLocation += pCallMgrSpecific->Length;
                FillOffset += pCallMgrSpecific->Length;
            }
            else
            {
                pActivateVc->CallMgrParamsOffset = 0;
                pActivateVc->CallMgrParamsLength = 0;
            }

            break;
        }

        case REMOTE_CONDIS_MP_DEACTIVATE_VC_MSG:
        {
            PRCONDIS_MP_DEACTIVATE_VC_REQUEST       pDeactivateVc;

            MessageSize = RNDIS_MESSAGE_SIZE(RCONDIS_MP_DEACTIVATE_VC_REQUEST);

            pMsgFrame = AllocateMessageAndFrame(pAdapter, MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

            pMessage = RNDISMP_GET_MSG_FROM_FRAME(pMsgFrame);
            pMessage->NdisMessageType = NdisMessageType;
            pMsgFrame->NdisMessageType = NdisMessageType;

            pDeactivateVc = &pMessage->Message.CoMiniportDeactivateVc;
            pDeactivateVc->RequestId = pMsgFrame->RequestId;
            pDeactivateVc->DeviceVcHandle = pVc->DeviceVcContext;

            break;
        }

        default:

            ASSERT(FALSE);
            pMsgFrame = NULL;
            break;
    }


    return (pMsgFrame);

} // BuildRndisMessageCoMiniport

/****************************************************************************/
/*                          CompleteSendDataOnVc                            */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Handle send-completion of CONDIS data                                   */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pVc - Pointer to Vc                                                     */
/*  pNdisPacket - Packet being completed                                    */
/*  Status - send completion status                                         */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
CompleteSendDataOnVc(IN PRNDISMP_VC         pVc,
                     IN PNDIS_PACKET        pNdisPacket,
                     IN NDIS_STATUS         Status)
{
    ULONG   RefCount;

    NdisMCoSendComplete(Status,
                        pVc->NdisVcHandle,
                        pNdisPacket);

    RNDISMP_DEREF_VC(pVc, &RefCount);
}

/****************************************************************************/
/*                   IndicateReceiveDataOnVc                                */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Handle reception of a bunch of CONDIS packets on a Vc.                  */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pVc - Pointer to VC on which data arrived.                              */
/*  PacketArray - Array of packets                                          */
/*  NumberOfPackets - size of above array                                   */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
IndicateReceiveDataOnVc(IN PRNDISMP_VC         pVc,
                        IN PNDIS_PACKET *      PacketArray,
                        IN UINT                NumberOfPackets)
{
    UINT            i;

    do
    {
        if (pVc->VcState != RNDISMP_VC_ACTIVATED)
        {
            TRACE1(("Rcv VC data: VC %x, invalid state %d\n", pVc, pVc->VcState));
            break;
        }

        for (i = 0; i < NumberOfPackets; i++)
        {
            RNDISMP_REF_VC(pVc);
        }

        NdisMCoIndicateReceivePacket(pVc->NdisVcHandle,
                                     PacketArray,
                                     NumberOfPackets);
    }
    while (FALSE);

} // IndicateReceiveDataOnVc