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

Copyright (c) 1999  Microsoft Corporation

Module Name:

    UTIL.C

Abstract:

    Utility routines for Remote NDIS Miniport driver

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:

    5/17/99 : created

Author:

    Tom Green

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

#include "precomp.h"


ULONG   MsgFrameAllocs = 0;

/****************************************************************************/
/*                          MemAlloc                                        */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Allocate memory                                                         */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Buffer - pointer to buffer pointer                                      */
/*  Length - length of buffer to allocate                                   */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    NDIS_STATUS                                                           */
/*                                                                          */
/****************************************************************************/
NDIS_STATUS
MemAlloc(OUT PVOID *Buffer, IN UINT Length)
{
    NDIS_STATUS Status;

    TRACE3(("MemAlloc\n"));
    ASSERT(Length != 0);

    Status = NdisAllocateMemoryWithTag(Buffer, 
                                       Length,
                                       RNDISMP_TAG_GEN_ALLOC);

    // zero out allocation
    if(Status == NDIS_STATUS_SUCCESS)
        NdisZeroMemory(*Buffer, Length);

    return Status;
} // MemAlloc

/****************************************************************************/
/*                          MemFree                                         */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Free memory                                                             */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Buffer - pointer to buffer                                              */
/*  Length - length of buffer to allocate                                   */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
MemFree(IN PVOID Buffer, IN UINT Length)
{
    TRACE3(("MemFree\n"));

    NdisFreeMemory(Buffer, Length, 0);
} // MemFree


/****************************************************************************/
/*                          AddAdapter                                      */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Add an adapter to the list of adapters associated with this driver      */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - Adapter object, contains pointer to associated driver block  */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
AddAdapter(IN PRNDISMP_ADAPTER pAdapter)
{
    PDRIVER_BLOCK   DriverBlock = pAdapter->DriverBlock;

    TRACE3(("AddpAdapter\n"));

    CHECK_VALID_ADAPTER(pAdapter);

    // grab the global spinlock
    NdisAcquireSpinLock(&RndismpGlobalLock);

    pAdapter->NextAdapter        = DriverBlock->AdapterList;
    DriverBlock->AdapterList    = pAdapter;

    // keep track of number of adapters associated with this driver block
    DriverBlock->NumberAdapters++;

    // release global spinlock
    NdisReleaseSpinLock(&RndismpGlobalLock);

} // AddAdapter


/****************************************************************************/
/*                          RemoveAdapter                                   */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Remove an adapter from the list of adapters associated with this driver */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - Adapter object, contains pointer to associated driver block  */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
RemoveAdapter(IN PRNDISMP_ADAPTER pAdapter)
{
    PDRIVER_BLOCK   DriverBlock = pAdapter->DriverBlock;

    TRACE3(("RemoveAdapter\n"));

    CHECK_VALID_ADAPTER(pAdapter);

    // remove the adapter from the driver block list of adapters.

    // grab the global spinlock
    NdisAcquireSpinLock(&RndismpGlobalLock);

    // see if it is the first one
    if (DriverBlock->AdapterList == pAdapter) 
    {
        DriverBlock->AdapterList = pAdapter->NextAdapter;

    }
    // not the first one, so walk the list
    else 
    {
        PRNDISMP_ADAPTER * ppAdapter = &DriverBlock->AdapterList;

        while (*ppAdapter != pAdapter)
        {
            ASSERT(*ppAdapter != NULL);
            ppAdapter = &((*ppAdapter)->NextAdapter);
        }

        *ppAdapter = pAdapter->NextAdapter;
    }

    // removing this adapter
    DriverBlock->NumberAdapters--;

    // release global spinlock
    NdisReleaseSpinLock(&RndismpGlobalLock);

} // RemoveAdapter


/****************************************************************************/
/*                       DeviceObjectToAdapter                              */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Given a pointer to an FDO, return the corresponding Adapter structure,  */
/*  if it exists, and the driver block.                                     */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pDeviceObject - pointer to the device object to search for.             */
/*  ppAdapter - place to return pointer to the adapter structure.           */
/*  ppDriverBlock - place to return pointer to driver block.                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
DeviceObjectToAdapterAndDriverBlock(IN PDEVICE_OBJECT pDeviceObject,
                                    OUT PRNDISMP_ADAPTER * ppAdapter,
                                    OUT PDRIVER_BLOCK * ppDriverBlock)
{
    PDRIVER_BLOCK       pDriverBlock;
    PRNDISMP_ADAPTER    pAdapter;

    pAdapter = NULL;
    pDriverBlock = DeviceObjectToDriverBlock(&RndismpMiniportBlockListHead, pDeviceObject);
    if (pDriverBlock != NULL)
    {
        NdisAcquireSpinLock(&RndismpGlobalLock);

        for (pAdapter = pDriverBlock->AdapterList;
             pAdapter != NULL;
             pAdapter = pAdapter->NextAdapter)
        {
            if (pAdapter->pDeviceObject == pDeviceObject)
            {
                break;
            }
        }

        NdisReleaseSpinLock(&RndismpGlobalLock);
    }

    *ppAdapter = pAdapter;
    *ppDriverBlock = pDriverBlock;

} // DeviceObjectToAdapter

/****************************************************************************/
/*                          AddDriverBlock                                  */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Add driver block to list of drivers (microports) associated with this   */
/*  driver                                                                  */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Head - head of list                                                     */
/*  Item - driver block to add to list                                      */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
AddDriverBlock(IN PDRIVER_BLOCK Head, IN PDRIVER_BLOCK Item)
{
    TRACE3(("AddDriverBlock\n"));

    CHECK_VALID_BLOCK(Item);

    // first time through, so allocate global spinlock
    if(!RndismpNumMicroports)
        NdisAllocateSpinLock(&RndismpGlobalLock);

    // grab the global spinlock
    NdisAcquireSpinLock(&RndismpGlobalLock);

    // Link the driver block on the global list of driver blocks
    Item->NextDriverBlock   = Head->NextDriverBlock;
    Head->NextDriverBlock   = Item;

    // keep track of how many microports we support so we can free
    // global resources
    RndismpNumMicroports++;
    
    // release global spinlock
    NdisReleaseSpinLock(&RndismpGlobalLock);

} // AddDriverBlock



/****************************************************************************/
/*                          RemoveDriverBlock                               */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Remove driver block from list of drivers (microports) associated with   */
/*  this driver                                                             */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Head - head of list                                                     */
/*  Item - driver block to remove from list                                 */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
RemoveDriverBlock(IN PDRIVER_BLOCK BlockHead, IN PDRIVER_BLOCK Item)
{
    UINT    NumMicroports;

    PDRIVER_BLOCK   Head = BlockHead;

    TRACE1(("RemoveDriverBlock\n"));

    CHECK_VALID_BLOCK(Item);

    // grab the global spinlock
    NdisAcquireSpinLock(&RndismpGlobalLock);

    // Remove the driver block from the global list of driver blocks
    while(Head->NextDriverBlock != Item) 
    {
        Head = Head->NextDriverBlock;

        // make sure this is valid
        if(!Head)
            break;
    }

    if(Head)
        Head->NextDriverBlock = Head->NextDriverBlock->NextDriverBlock;

    // keep track of how many microports we support so we can free
    // global resources
    RndismpNumMicroports--;

    NumMicroports = RndismpNumMicroports;
    
    // release global spinlock
    NdisReleaseSpinLock(&RndismpGlobalLock);

    // see if we need to free global spinlock
    if(!RndismpNumMicroports)
        NdisFreeSpinLock(&RndismpGlobalLock);

    ASSERT(Head);

} // RemoveDriverBlock


/****************************************************************************/
/*                          DeviceObjectToDriverBlock                       */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Get driver block pointer associated with the PDO passed in              */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Head - head of driver block list                                        */
/*  DeviceObject - device object we want to get associated driver block for */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    PDRIVER_BLOCK                                                         */
/*                                                                          */
/****************************************************************************/
PDRIVER_BLOCK
DeviceObjectToDriverBlock(IN PDRIVER_BLOCK Head, 
                          IN PDEVICE_OBJECT DeviceObject)
{
    PDRIVER_OBJECT  DriverObject;

    TRACE3(("DeviceObjectToDriverBlock\n"));

    // grab the global spinlock
    NdisAcquireSpinLock(&RndismpGlobalLock);

    // get the driver object for this adapter
    DriverObject = DeviceObjectToDriverObject(DeviceObject);

    Head = Head->NextDriverBlock;

    // walk the list of driver blocks to find a match with driver object
    while(Head->DriverObject != DriverObject)
    {
        Head = Head->NextDriverBlock;

        // break out if we are at the end of the list
        if(!Head)
            break;
    }

    // release global spinlock
    NdisReleaseSpinLock(&RndismpGlobalLock);

    CHECK_VALID_BLOCK(Head);

    return Head;

} // DeviceObjectToDriverBlock


/****************************************************************************/
/*                          DriverObjectToDriverBlock                       */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Get driver block pointer associated with the Driver Object passed in    */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Head - head of driver block list                                        */
/*  DriverObject - Driver object we want to get associated driver block for */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    PDRIVER_BLOCK                                                         */
/*                                                                          */
/****************************************************************************/
PDRIVER_BLOCK
DriverObjectToDriverBlock(IN PDRIVER_BLOCK Head, 
                          IN PDRIVER_OBJECT DriverObject)
{
    TRACE3(("DriverObjectToDriverBlock\n"));

    // grab the global spinlock
    NdisAcquireSpinLock(&RndismpGlobalLock);

    Head = Head->NextDriverBlock;

    // walk the list of driver blocks to find a match with driver object
    while(Head->DriverObject != DriverObject)
    {
        Head = Head->NextDriverBlock;

        // break out if we are at the end of the list
        if(!Head)
            break;
    }

    // release global spinlock
    NdisReleaseSpinLock(&RndismpGlobalLock);

    CHECK_VALID_BLOCK(Head);

    return Head;

} // DriverObjectToDriverBlock


/****************************************************************************/
/*                          AllocateMsgFrame                                */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Allocate a frame that holds context about a message we are about to     */
/*  send.                                                                   */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - Adapter object                                               */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    PRNDISMP_MESSAGE_FRAME                                                */
/*                                                                          */
/****************************************************************************/
PRNDISMP_MESSAGE_FRAME
AllocateMsgFrame(IN PRNDISMP_ADAPTER pAdapter)
{
    PRNDISMP_MESSAGE_FRAME  pMsgFrame;

#ifndef DONT_USE_LOOKASIDE_LIST
    pMsgFrame = (PRNDISMP_MESSAGE_FRAME)
                    NdisAllocateFromNPagedLookasideList(&pAdapter->MsgFramePool);

#else
    {
        NDIS_STATUS Status;
        Status = MemAlloc(&pMsgFrame, sizeof(RNDISMP_MESSAGE_FRAME));
        if (Status != NDIS_STATUS_SUCCESS)
        {
            pMsgFrame = NULL;
        }
    }
#endif // DONT_USE_LOOKASIDE_LIST

    if (pMsgFrame)
    {
        NdisZeroMemory(pMsgFrame, sizeof(*pMsgFrame));
        pMsgFrame->pAdapter = pAdapter;
        pMsgFrame->RequestId = NdisInterlockedIncrement(&pAdapter->RequestId);
        pMsgFrame->Signature = FRAME_SIGNATURE;

        pMsgFrame->RefCount = 1;
        NdisInterlockedIncrement(&MsgFrameAllocs);
    }
#if DBG
    else
    {
        TRACE1(("AllocateMsgFrame: pAdapter %x, MsgFramePool at %x, alloc failed, count %d\n",
            pAdapter, &pAdapter->MsgFramePool, MsgFrameAllocs));
        DbgBreakPoint();
    }
#endif // DBG

    return (pMsgFrame);
}

/****************************************************************************/
/*                          DereferenceMsgFrame                             */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Free a message frame and any associated resources.                      */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Frame - pointer to frame                                                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
DereferenceMsgFrame(IN PRNDISMP_MESSAGE_FRAME pMsgFrame)
{
    PRNDISMP_ADAPTER        pAdapter;
    PMDL                    pMdl;
    PUCHAR                  pMessage;

    CHECK_VALID_FRAME(pMsgFrame);

    if (NdisInterlockedDecrement(&pMsgFrame->RefCount) == 0)
    {
        //
        // Mess up the contents slightly to catch bugs resulting from
        // improper reuse of this frame after it is freed.
        //
        pMsgFrame->Signature++;

        pMdl = pMsgFrame->pMessageMdl;
        pMsgFrame->pMessageMdl = NULL;
    
        if (pMdl)
        {
            pMessage = MmGetMdlVirtualAddress(pMdl);
        }
        else
        {
            pMessage = NULL;
        }
    
        if (pMessage)
        {
            MemFree(pMessage, -1);
            IoFreeMdl(pMdl);
        }

        pAdapter = pMsgFrame->pAdapter;

#ifndef DONT_USE_LOOKASIDE_LIST
        NdisFreeToNPagedLookasideList(&pAdapter->MsgFramePool, pMsgFrame);
#else
        MemFree(pMsgFrame, sizeof(RNDISMP_MESSAGE_FRAME));
#endif
        NdisInterlockedDecrement(&MsgFrameAllocs);
    }

} // DereferenceMsgFrame


/****************************************************************************/
/*                          ReferenceMsgFrame                               */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Add a ref count to a message frame                                      */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Frame - pointer to frame                                                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    VOID                                                                  */
/*                                                                          */
/****************************************************************************/
VOID
ReferenceMsgFrame(IN PRNDISMP_MESSAGE_FRAME pMsgFrame)
{
    NdisInterlockedIncrement(&pMsgFrame->RefCount);
}

/****************************************************************************/
/*                          KeepAliveTimerHandler                           */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Timer that keeps tabs on messages coming up from the device and         */
/*  sends a "KeepAlive" message if the device has been inactive too long    */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  SystemSpecific1 - Don't care                                            */
/*  Context - pAdapter object                                               */
/*  SystemSpecific2 - Don't care                                            */
/*  SystemSpecific3 - Don't care                                            */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  PNDIS_PACKET                                                            */
/*                                                                          */
/****************************************************************************/
VOID
KeepAliveTimerHandler(IN PVOID SystemSpecific1,
                      IN PVOID Context,
                      IN PVOID SystemSpecific2,
                      IN PVOID SystemSpecific3)
{
    PRNDISMP_ADAPTER            pAdapter;
    PRNDISMP_MESSAGE_FRAME      pMsgFrame;
    ULONG                       CurrentTime;

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

    TRACE2(("KeepAliveTimerHandler\n"));

    do
    {
        // get current tick (in milliseconds)
        NdisGetSystemUpTime(&CurrentTime);

        // check and see if too much time has elapsed since we
        // got the last message from the device

        RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter);

        if (((CurrentTime - pAdapter->LastMessageFromDevice) > KEEP_ALIVE_TIMER))
        {
            // see if we have a keep alive message pending, so let's bong this
            if (pAdapter->KeepAliveMessagePending)
            {
                TRACE1(("KeepAliveTimer: Adapter %x, message pending\n", pAdapter));

                // indicate later from check for hang handler
                pAdapter->NeedReset = TRUE;

                RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);

                RNDISMP_INCR_STAT(pAdapter, KeepAliveTimeout);

                break;
            }

            RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);


            // too much time has elapsed, send down a keep alive message
            pMsgFrame = BuildRndisMessageCommon(pAdapter, 
                                                NULL,
                                                REMOTE_NDIS_KEEPALIVE_MSG,
                                                0,
                                                NULL,
                                                0);

            if (pMsgFrame)
            {
                RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter);

                pAdapter->KeepAliveMessagePending = TRUE;
                pAdapter->KeepAliveMessagePendingId = pMsgFrame->RequestId;

                RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);

                // send the message to the microport
                RNDISMP_SEND_TO_MICROPORT(pAdapter, pMsgFrame, FALSE, CompleteSendKeepAlive);
            }
        }
        else
        {
            RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);
        }
    }
    while (FALSE);

    // see if the timer was cancelled somewhere
    if (!pAdapter->TimerCancelled)
    {
        // restart timer
        NdisSetTimer(&pAdapter->KeepAliveTimer, KEEP_ALIVE_TIMER / 2);
    }
} // KeepAliveTimerHandler


/****************************************************************************/
/*                          CompleteSendKeepAlive                           */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Callback routine to handle completion of send by the microport, for     */
/*  a keepalive message.                                                    */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pMsgFrame - Pointer to message frame describing the message             */
/*  SendStatus - Status returned by microport                               */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
CompleteSendKeepAlive(IN PRNDISMP_MESSAGE_FRAME pMsgFrame,
                      IN NDIS_STATUS SendStatus)
{
    PRNDISMP_ADAPTER    pAdapter;

    pAdapter = pMsgFrame->pAdapter;

    DereferenceMsgFrame(pMsgFrame);

    if (SendStatus != NDIS_STATUS_SUCCESS)
    {
        TRACE1(("KeepAlive send failure %x on Adapter %x\n",
                SendStatus, pAdapter));

        RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter);

        pAdapter->KeepAliveMessagePending = FALSE;
        pAdapter->NeedReset = FALSE;

        RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);
    }

} // CompleteSendKeepAlive


/****************************************************************************/
/*                          BuildRndisMessageCommon                         */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*    Allocate resources for meesage and frame and build RNDIS message      */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*    pAdapter - adapter object                                             */
/*    pVc - optionally, VC on which this message is sent.                   */
/*    NdisMessageType - RNDIS message type                                  */
/*    Oid - the NDIS_OID to process.                                        */
/*    InformationBuffer - Holds the data to be set.                         */
/*    InformationBufferLength - The length of InformationBuffer.            */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*    PRNDISMP_MESSAGE_FRAME                                                */
/*                                                                          */
/****************************************************************************/
PRNDISMP_MESSAGE_FRAME
BuildRndisMessageCommon(IN  PRNDISMP_ADAPTER  pAdapter, 
                        IN  PRNDISMP_VC       pVc OPTIONAL,
                        IN  UINT              NdisMessageType,
                        IN  NDIS_OID          Oid,
                        IN  PVOID             InformationBuffer,
                        IN  ULONG             InformationBufferLength)
{
    PRNDIS_MESSAGE              pMessage;
    UINT                        MessageSize;
    PRNDISMP_MESSAGE_FRAME      pMsgFrame;

    TRACE2(("BuildRndisMessageCommon\n"));

    pMsgFrame = NULL;

    switch(NdisMessageType)
    {
        case REMOTE_NDIS_INITIALIZE_MSG:
        {
            PRNDIS_INITIALIZE_REQUEST   pInitRequest;

            MessageSize = RNDIS_MESSAGE_SIZE(RNDIS_INITIALIZE_REQUEST);

            // get a message and request frame
            pMsgFrame = AllocateMessageAndFrame(pAdapter,
                                                MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

            pMessage = RNDISMP_GET_MSG_FROM_FRAME(pMsgFrame);
            pMessage->NdisMessageType = NdisMessageType;
            pMsgFrame->NdisMessageType = NdisMessageType;
            TRACE1(("RNDISMP: Init Req message %x, Type %d, Length %d, MaxRcv %d\n",
                    pMessage, pMessage->NdisMessageType, pMessage->MessageLength, pAdapter->MaxReceiveSize));

            pInitRequest = &pMessage->Message.InitializeRequest;
            pInitRequest->RequestId = pMsgFrame->RequestId;
            pInitRequest->MajorVersion = RNDIS_MAJOR_VERSION;
            pInitRequest->MinorVersion = RNDIS_MINOR_VERSION;
            pInitRequest->MaxTransferSize = pAdapter->MaxReceiveSize;

            break;
        }
        case REMOTE_NDIS_HALT_MSG:
        {
            PRNDIS_HALT_REQUEST   pHaltRequest;

            MessageSize = RNDIS_MESSAGE_SIZE(RNDIS_HALT_REQUEST);

            // get a message and request frame
            pMsgFrame = AllocateMessageAndFrame(pAdapter,
                                                MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

            pMessage = RNDISMP_GET_MSG_FROM_FRAME(pMsgFrame);
            pMessage->NdisMessageType = NdisMessageType;
            pMsgFrame->NdisMessageType = NdisMessageType;
            pHaltRequest = &pMessage->Message.HaltRequest;
            pHaltRequest->RequestId = pMsgFrame->RequestId;

            break;
        }
        case REMOTE_NDIS_QUERY_MSG:
        {
            PRNDIS_QUERY_REQUEST   pQueryRequest;

            MessageSize = RNDIS_MESSAGE_SIZE(RNDIS_QUERY_REQUEST) + InformationBufferLength;

            // get a message and request frame
            pMsgFrame = AllocateMessageAndFrame(pAdapter,
                                                MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

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

            pQueryRequest = &pMessage->Message.QueryRequest;
            pQueryRequest->RequestId = pMsgFrame->RequestId;
            pQueryRequest->Oid = Oid;
            pQueryRequest->InformationBufferLength = InformationBufferLength;
            pQueryRequest->InformationBufferOffset = sizeof(RNDIS_QUERY_REQUEST);

            if (pVc == NULL)
            {
                pQueryRequest->DeviceVcHandle = NULL_DEVICE_CONTEXT;
            }
            else
            {
                pQueryRequest->DeviceVcHandle = pVc->DeviceVcContext;
            }

            TRACE2(("Query OID %x, Len %d, RequestId %08X\n",
                    Oid, InformationBufferLength, pQueryRequest->RequestId));

            // copy information buffer
            RNDISMP_MOVE_MEM(RNDISMP_GET_INFO_BUFFER_FROM_QUERY_MSG(pQueryRequest),
                             InformationBuffer,
                             InformationBufferLength);
            break;
        }
        case REMOTE_NDIS_SET_MSG:
        {
            PRNDIS_SET_REQUEST   pSetRequest;

            MessageSize = RNDIS_MESSAGE_SIZE(RNDIS_SET_REQUEST) + InformationBufferLength;

            // get a message and request frame
            pMsgFrame = AllocateMessageAndFrame(pAdapter,
                                                MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

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

            pSetRequest = &pMessage->Message.SetRequest;
            pSetRequest->RequestId = pMsgFrame->RequestId;
            pSetRequest->Oid = Oid;
            pSetRequest->InformationBufferLength = InformationBufferLength;
            pSetRequest->InformationBufferOffset = sizeof(RNDIS_SET_REQUEST);

            if (pVc == NULL)
            {
                pSetRequest->DeviceVcHandle = NULL_DEVICE_CONTEXT;
            }
            else
            {
                pSetRequest->DeviceVcHandle = pVc->DeviceVcContext;
            }

            // copy information buffer
            RNDISMP_MOVE_MEM(RNDISMP_GET_INFO_BUFFER_FROM_QUERY_MSG(pSetRequest),
                             InformationBuffer,
                             InformationBufferLength);
            break;
        }
        case REMOTE_NDIS_RESET_MSG:
        {
            PRNDIS_RESET_REQUEST   pResetRequest;

            MessageSize = RNDIS_MESSAGE_SIZE(RNDIS_RESET_REQUEST);

            // get a message and request frame
            pMsgFrame = AllocateMessageAndFrame(pAdapter,
                                                MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

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

            pResetRequest = &pMessage->Message.ResetRequest;
            pResetRequest->Reserved = 0;
            break;
        }
        case REMOTE_NDIS_KEEPALIVE_MSG:
        {
            PRNDIS_KEEPALIVE_REQUEST   pKeepAliveRequest;

            MessageSize = RNDIS_MESSAGE_SIZE(RNDIS_KEEPALIVE_REQUEST);

            // get a message and request frame
            pMsgFrame = AllocateMessageAndFrame(pAdapter,
                                                MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

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

            pKeepAliveRequest = &pMessage->Message.KeepaliveRequest;
            pKeepAliveRequest->RequestId = pMsgFrame->RequestId;
            break;
        }
        case REMOTE_NDIS_KEEPALIVE_CMPLT:
        {
            PRNDIS_KEEPALIVE_COMPLETE   pKeepAliveComplete;

            MessageSize = RNDIS_MESSAGE_SIZE(RNDIS_KEEPALIVE_COMPLETE);

            // get a message and request frame
            pMsgFrame = AllocateMessageAndFrame(pAdapter,
                                                MessageSize);

            if (pMsgFrame == NULL)
            {
                break;
            }

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

            pKeepAliveComplete = &pMessage->Message.KeepaliveComplete;
            pKeepAliveComplete->RequestId = *(RNDIS_REQUEST_ID *)InformationBuffer;
            pKeepAliveComplete->Status = NDIS_STATUS_SUCCESS;
            break;
        }

        default:
            TRACE2(("Invalid NdisMessageType (%08X)\n", NdisMessageType));
            ASSERT(FALSE);
            break;
    }

    return pMsgFrame;
} // BuildRndisMessageCommon


/****************************************************************************/
/*                          AllocateMessageAndFrame                         */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Allocate a message and frame for an RNDIS message                       */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - pAdapter object                                              */
/*  MessageSize - size of RNDIS message                                     */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  PRNDISMP_MESSAGE_FRAME                                                  */
/*                                                                          */
/****************************************************************************/
PRNDISMP_MESSAGE_FRAME
AllocateMessageAndFrame(IN PRNDISMP_ADAPTER pAdapter, 
                        IN UINT MessageSize)
{
    PRNDIS_MESSAGE          pMessage = NULL;
    PRNDISMP_MESSAGE_FRAME  pMsgFrame = NULL;
    NDIS_STATUS             Status = NDIS_STATUS_SUCCESS;
    PMDL                    pMdl = NULL;

    TRACE3(("AllocateMessageAndFrame\n"));

    do
    {
        // allocate a buffer for RNDIS message
        Status = MemAlloc(&pMessage, MessageSize);

        // see if we got our buffer
        if (Status != NDIS_STATUS_SUCCESS)
        {
            break;
        }

        // allocate an MDL to describe this message.
        pMdl = IoAllocateMdl(
                    pMessage,
                    MessageSize,
                    FALSE,
                    FALSE,
                    NULL);

        if (pMdl == NULL)
        {
            Status = NDIS_STATUS_RESOURCES;
            break;
        }

        MmBuildMdlForNonPagedPool(pMdl);

        // got the message buffer, now allocate a frame
        pMsgFrame = AllocateMsgFrame(pAdapter);

        if (pMsgFrame == NULL)
        {
            Status = NDIS_STATUS_RESOURCES;
            break;
        }

        // got everything, so fill in some frame things
        pMsgFrame->pMessageMdl = pMdl;

        pMessage->MessageLength = MessageSize;

    }
    while (FALSE);

    if (Status != NDIS_STATUS_SUCCESS)
    {
        if (pMdl)
        {
            IoFreeMdl(pMdl);
        }

        if (pMessage)
        {
            MemFree(pMessage, MessageSize);
        }
    }

    return pMsgFrame;

} // AllocateMessageAndFrame


/****************************************************************************/
/*                          FreeAdapter                                     */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Free all memory allocations to do with an Adapter structure             */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - pointer to the adapter to be freed.                          */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/****************************************************************************/
VOID
FreeAdapter(IN PRNDISMP_ADAPTER Adapter)
{
    // free up transport resources
    FreeTransportResources(Adapter);

    if (Adapter->DriverOIDList)
    {
        MemFree(Adapter->DriverOIDList, RndismpSupportedOidsNum*sizeof(NDIS_OID));
    }

    if (Adapter->FriendlyNameAnsi.Buffer)
    {
        MemFree(Adapter->FriendlyNameAnsi.Buffer, Adapter->FriendlyNameAnsi.MaximumLength);
    }

    if (Adapter->FriendlyNameUnicode.Buffer)
    {
        MemFree(Adapter->FriendlyNameUnicode.Buffer, Adapter->FriendlyNameUnicode.MaximumLength);
    }

#if DBG
    if (Adapter->pSendLogBuffer)
    {
        MemFree(Adapter->pSendLogBuffer, Adapter->LogBufferSize);
        Adapter->pSendLogBuffer = NULL;
    }
#endif // DBG

    MemFree(Adapter, sizeof(RNDISMP_ADAPTER));
}


/****************************************************************************/
/*                          AllocateVc                                      */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Allocate a VC structure                                                 */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - adapter object                                               */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  PRNDISMP_VC                                                             */
/*                                                                          */
/****************************************************************************/
PRNDISMP_VC
AllocateVc(IN PRNDISMP_ADAPTER      pAdapter)
{
    PRNDISMP_VC     pVc;
    NDIS_STATUS     Status;

    Status = MemAlloc(&pVc, sizeof(RNDISMP_VC));
    if (Status == NDIS_STATUS_SUCCESS)
    {
        pVc->pAdapter = pAdapter;
        pVc->VcState = RNDISMP_VC_ALLOCATED;
        pVc->CallState = RNDISMP_CALL_IDLE;
        pVc->RefCount = 0;
        RNDISMP_INIT_LOCK(&pVc->Lock);

        EnterVcIntoHashTable(pAdapter, pVc);
    }
    else
    {
        pVc = NULL;
    }

    return pVc;
}

/****************************************************************************/
/*                          DeallocateVc                                    */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Deallocate a VC structure.                                              */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pVc - Pointer to VC being deallocated.                                  */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
DeallocateVc(IN PRNDISMP_VC         pVc)
{
    ASSERT(pVc->RefCount == 0);
    ASSERT(pVc->VcState == RNDISMP_VC_ALLOCATED);

    RemoveVcFromHashTable(pVc->pAdapter, pVc);

    MemFree(pVc, sizeof(RNDISMP_VC));
}
    
/****************************************************************************/
/*                          LookupVcId                                      */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Search for a VC structure that matches a given VC Id.                   */
/*  If we find the VC, we reference it and return it.                       */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  Adapter - adapter object                                                */
/*  VcId - Id to search for                                                 */
/*                                                                          */
/* Notes:                                                                   */
/*                                                                          */
/*  This routine is called with the adapter lock held!                      */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  PRNDISMP_VC - pointer to VC, if one exists                              */
/*                                                                          */
/****************************************************************************/
PRNDISMP_VC
LookupVcId(IN PRNDISMP_ADAPTER  pAdapter,
           IN UINT32            VcId)
{
    PLIST_ENTRY             pVcEnt;
    PRNDISMP_VC             pVc;
    ULONG                   VcIdHash;
    PRNDISMP_VC_HASH_TABLE  pVcHashTable;
    BOOLEAN                 bFound = FALSE;

    VcIdHash = RNDISMP_HASH_VCID(VcId);

    pVcHashTable = pAdapter->pVcHashTable;

    do
    {
        if (pVcHashTable == NULL)
        {
            pVc = NULL;
            break;
        }

        for (pVcEnt = pVcHashTable->HashEntry[VcIdHash].Flink;
             pVcEnt != &pVcHashTable->HashEntry[VcIdHash];
             pVcEnt = pVcEnt->Flink)
        {
            pVc = CONTAINING_RECORD(pVcEnt, RNDISMP_VC, VcList);
            if (pVc->VcId == VcId)
            {
                bFound = TRUE;

                RNDISMP_REF_VC(pVc);

                break;
            }
        }

        if (!bFound)
        {
            pVc = NULL;
        }
    }
    while (FALSE);

    return pVc;
}


/****************************************************************************/
/*                          EnterVcIntoHashTable                            */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Link a VC into the hash table after assigning it a VC Id.               */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - adapter object                                               */
/*  pVc - VC to link to the above adapter                                   */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
EnterVcIntoHashTable(IN PRNDISMP_ADAPTER    pAdapter,
                     IN PRNDISMP_VC         pVc)
{
    PRNDISMP_VC             pExistingVc;
    PRNDISMP_VC_HASH_TABLE  pVcHashTable;
    UINT32                  VcId;
    ULONG                   VcIdHash;

    RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter);

    //
    //  We pick the next sequentially higher Vc Id value for this VC,
    //  but check to see if it is already in use...
    //
    do
    {
        pAdapter->LastVcId++;

        // Never allocate the value 0.
        if (pAdapter->LastVcId == 0)
        {
            pAdapter->LastVcId++;
        }

        VcId = pAdapter->LastVcId;

        pExistingVc = LookupVcId(pAdapter, VcId);
    }
    while (pExistingVc != NULL);

    pVcHashTable = pAdapter->pVcHashTable;
    pVc->VcId = VcId;
    VcIdHash = RNDISMP_HASH_VCID(VcId);

    InsertTailList(&pVcHashTable->HashEntry[VcIdHash], &pVc->VcList);
    pVcHashTable->NumEntries++;

    RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);
}


/****************************************************************************/
/*                        RemoveVcFromHashTable                             */
/****************************************************************************/
/*                                                                          */
/* Routine Description:                                                     */
/*                                                                          */
/*  Unlink a VC from the adapter hash table.                                */
/*                                                                          */
/* Arguments:                                                               */
/*                                                                          */
/*  pAdapter - adapter object                                               */
/*  pVc - VC to be unlinked.                                                */
/*                                                                          */
/* Return:                                                                  */
/*                                                                          */
/*  VOID                                                                    */
/*                                                                          */
/****************************************************************************/
VOID
RemoveVcFromHashTable(IN PRNDISMP_ADAPTER   pAdapter,
                      IN PRNDISMP_VC        pVc)
{
    RNDISMP_ACQUIRE_ADAPTER_LOCK(pAdapter);

    RemoveEntryList(&pVc->VcList);

    pAdapter->pVcHashTable->NumEntries--;

    RNDISMP_RELEASE_ADAPTER_LOCK(pAdapter);
}