//
// Copyright (c) 1998-1999, Microsoft Corporation, all rights reserved
//
// util.c
//
// IEEE1394 mini-port/call-manager driver
//
// General utility routines
//
// 12/28/1998 JosephJ Created, adapted from the l2tp sources.
//


#include "precomp.h"


// Debug counts of oddities that should not be happening.
//
ULONG g_ulAllocTwFailures = 0;

//-----------------------------------------------------------------------------
// Local prototypes (alphabetically)
//-----------------------------------------------------------------------------

ULONG
atoul(
    IN CHAR* pszNumber );

VOID
ReversePsz(
    IN OUT CHAR* psz );

VOID
TunnelWork(
    IN NDIS_WORK_ITEM* pWork,
    IN VOID* pContext );

VOID
ultoa(
    IN ULONG ul,
    OUT CHAR* pszBuf );


//-----------------------------------------------------------------------------
// General utility routines (alphabetically)
//-----------------------------------------------------------------------------





CHAR*
nicStrDup(
    IN CHAR* psz )

    // Return a duplicate of 'psz'.  Caller must eventually call FREE_NONPAGED
    // on the returned string.
    //
{
    return nicStrDupSized( psz, strlen( psz ), 0 );
}


CHAR*
nicStrDupNdisString(
    IN NDIS_STRING* pNdisString )

    // Returns null-terminated ASCII copy of the NDIS_STRING 'pNdisString'
    // Caller must eventually call FREE_NONPAGED on the returned string.
    //
{
    CHAR* pszDup;

    pszDup = ALLOC_NONPAGED( pNdisString->Length + 1, MTAG_UTIL );
    if (pszDup)
    {
        NdisZeroMemory( pszDup, pNdisString->Length + 1 );
        if (pNdisString->Length)
        {
            NdisMoveMemory( pszDup, pNdisString->Buffer, pNdisString->Length );
        }

        // NDIS_STRING is UNICODE_STRING on NT but need the corresponding
        // ASCII (not multi-byte ANSI) value from NDIS_STRING on any system.
        // If it looks like a Unicode string then "convert" it by picking out
        // every other byte, hopefully all the non-zero ones.  This is not
        // foolproof, but then Unicode doesn't convert to ASCII in any
        // foolproof way.
        //
        if (pNdisString->Length > 1 && pszDup[ 1 ] == '\0')
        {
            USHORT i;

            for (i = 0; i * 2 < pNdisString->Length; ++i)
            {
                pszDup[ i ] = pszDup[ i * 2 ];
            }

            pszDup[ i ] = '\0';
        }
    }

    return pszDup;
}


CHAR*
nicStrDupSized(
    IN CHAR* psz,
    IN ULONG ulLength,
    IN ULONG ulExtra )

    // Return a duplicate of the first 'ulLength' bytes of 'psz' followed by a
    // null character and 'ulExtra' extra bytes, or NULL on error.  Caller
    // must eventually call FREE_NONPAGED on the returned string.
    //
{
    CHAR* pszDup;

    pszDup = ALLOC_NONPAGED( ulLength + 1 + ulExtra, MTAG_UTIL );
    if (pszDup)
    {
        if (ulLength)
        {
            NdisMoveMemory( pszDup, psz, ulLength );
        }
        pszDup[ ulLength ] = '\0';
    }

    return pszDup;
}



//-----------------------------------------------------------------------------
// Local utility routines (alphabetically)
//-----------------------------------------------------------------------------

ULONG
atoul(
    IN CHAR* pszNumber )

    // Convert string of digits 'pszNumber' to it's ULONG value.
    //
{
    ULONG ulResult;

    ulResult = 0;
    while (*pszNumber && *pszNumber >= '0' && *pszNumber <= '9')
    {
        ulResult *= 10;
        ulResult += *pszNumber - '0';
        ++pszNumber;
    }

    return ulResult;
}


#if DBG
VOID
ReversePsz(
    IN OUT CHAR* psz )

    // Reverse the order of the characters in 'psz' in place.
    //
{
    CHAR* pchLeft;
    CHAR* pchRight;

    pchLeft = psz;
    pchRight = psz + strlen( psz ) - 1;

    while (pchLeft < pchRight)
    {
        CHAR ch;

        ch = *pchLeft;
        *pchLeft = *pchRight;
        *pchRight = *pchLeft;

        ++pchLeft;
        --pchRight;
    }
}
#endif


#if DBG
VOID
ultoa(
    IN ULONG ul,
    OUT CHAR* pszBuf )

    // Convert 'ul' to null-terminated string form in caller's 'pszBuf'.  It's
    // caller job to make sure 'pszBuf' is long enough to hold the returned
    // string.
    //
{
    CHAR* pch;

    pch = pszBuf;
    do
    {
        *pch++ = (CHAR )((ul % 10) + '0');
        ul /= 10;
    }
    while (ul);

    *pch = '\0';
    ReversePsz( pszBuf );
}
#endif

VOID
nicSetFlags(
    IN OUT ULONG* pulFlags,
    IN ULONG ulMask )

    // Set 'ulMask' bits in '*pulFlags' flags as an interlocked operation.
    //
{
    ULONG ulFlags;
    ULONG ulNewFlags;

    do
    {
        ulFlags = *pulFlags;
        ulNewFlags = ulFlags | ulMask;
    }
    while (InterlockedCompareExchange(
               pulFlags, ulNewFlags, ulFlags ) != (LONG )ulFlags);
}

VOID
nicClearFlags(
    IN OUT ULONG* pulFlags,
    IN ULONG ulMask )

    // Set 'ulMask' bits in '*pulFlags' flags as an interlocked operation.
    //
{
    ULONG ulFlags;
    ULONG ulNewFlags;

    do
    {
        ulFlags = *pulFlags;
        ulNewFlags = ulFlags & ~(ulMask);
    }
    while (InterlockedCompareExchange(
               pulFlags, ulNewFlags, ulFlags ) != (LONG )ulFlags);
}

ULONG
nicReadFlags(
    IN ULONG* pulFlags )

    // Read the value of '*pulFlags' as an interlocked operation.
    //
{
    return *pulFlags;
}



//
// Reference And Dereference functions taken directly from Ndis
//



BOOLEAN
nicReferenceRef(
    IN  PREF                RefP
    )

/*++

Routine Description:

    Adds a reference to an object.

Arguments:

    RefP - A pointer to the REFERENCE portion of the object.

Return Value:

    TRUE if the reference was added.
    FALSE if the object was closing.

--*/

{
    BOOLEAN rc = TRUE;
    KIRQL   OldIrql;

    TRACE( TL_V, TM_Ref, ( "nicReferenceRef, %.8x", RefP ) );

//  NdisAcquireSpinLock (&RefP->SpinLock);

    if (RefP->Closing)
    {
        rc = FALSE;
    }
    else
    {
        NdisInterlockedIncrement (&RefP->ReferenceCount);
    }

//  NdisReleaseSpinLock (&RefP->SpinLock);

    TRACE( TL_V, TM_Ref, ( "nicReferenceRef, Bool %.2x, Ref %d", rc, RefP->ReferenceCount ) );

    return(rc);
}


BOOLEAN
nicDereferenceRef(
    IN  PREF                RefP
    )

/*++

Routine Description:

    Removes a reference to an object.

Arguments:

    RefP - A pointer to the REFERENCE portion of the object.

Return Value:

    TRUE if the reference count is now 0.
    FALSE otherwise.

--*/

{
    BOOLEAN rc = FALSE;
    KIRQL   OldIrql;

    TRACE( TL_V, TM_Ref, ( "==>nicDeReferenceRef, %x", RefP ) );

//  NdisAcquireSpinLock (&RefP->SpinLock);

    NdisInterlockedDecrement (&RefP->ReferenceCount);

    if (RefP->ReferenceCount == 0)
    {
        rc = TRUE;
        NdisSetEvent (&RefP->RefZeroEvent);

    }
    if ((signed long)RefP->ReferenceCount < 0)
    {
        ASSERT ( !"Ref Has Gone BELOW ZERO");
    }

//  NdisReleaseSpinLock (&RefP->SpinLock);


    TRACE( TL_V, TM_Ref, ( "<==nicDeReferenceRef, %.2x, RefCount %d", rc, RefP->ReferenceCount ) );
            
    return(rc);
}


VOID
nicInitializeRef(
    IN  PREF                RefP
    )

/*++

Routine Description:

    Initialize a reference count structure.

Arguments:

    RefP - The structure to be initialized.

Return Value:

    None.

--*/

{
    TRACE( TL_V, TM_Ref, ( "==>nicInitializeRef, %.8x", RefP ) );


    RefP->Closing = FALSE;
    RefP->ReferenceCount = 1;
    
//  NdisAllocateSpinLock (&RefP->SpinLock);
    NdisInitializeEvent (&RefP->RefZeroEvent);

    TRACE( TL_V, TM_Ref, ( "<==nicInitializeRef, %.8x", RefP ) );
}


BOOLEAN
nicCloseRef(
    IN  PREF                RefP
    )

/*++

Routine Description:

    Closes a reference count structure.

Arguments:

    RefP - The structure to be closed.

Return Value:

    FALSE if it was already closing.
    TRUE otherwise.

--*/

{
    KIRQL   OldIrql;
    BOOLEAN rc = TRUE;

    TRACE( TL_N, TM_Ref, ( "==>ndisCloseRef, %.8x", RefP ) );

//  NdisAcquireSpinLock (&RefP->SpinLock);

    if (RefP->Closing)
    {
        rc = FALSE;
    }
    else RefP->Closing = TRUE;

//  NdisReleaseSpinLock (&RefP->SpinLock);

    TRACE( TL_N, TM_Ref, ( "<==ndisCloseRef, %.8x, RefCount %.8x", RefP, RefP->ReferenceCount ) );
            
    return(rc);
}

//
//
// These are self expanatory Pdo Reference functions
// which will be turned into macros once we have functionality 
// working
//



BOOLEAN
nicReferenceRemoteNode (
    IN REMOTE_NODE *pPdoCb,
    IN PCHAR pDebugPrint
    )
/*++

Routine Description:
    

Arguments:


Return Value:

--*/
{
    BOOLEAN bRefClosing = FALSE;


    bRefClosing = nicReferenceRef (&pPdoCb->Ref);
    TRACE( TL_V, TM_RemRef, ( "**nicReferenceRemoteNode pPdoCb %x, to %d, %s, ret %x ", 
                          pPdoCb, pPdoCb->Ref.ReferenceCount, pDebugPrint, bRefClosing  ) );

    return bRefClosing ; 
}


BOOLEAN
nicDereferenceRemoteNode (
    IN REMOTE_NODE *pPdoCb,
    IN PCHAR pDebugPrint
    )
/*++

Routine Description:
    

Arguments:


Return Value:

--*/
{
    TRACE( TL_V, TM_RemRef, ( "**nicDereferenceRemoteNode  %x to %d , %s", 
                              pPdoCb , pPdoCb->Ref.ReferenceCount -1, pDebugPrint  ) );
    
    return nicDereferenceRef (&pPdoCb->Ref);
}


VOID
nicInitalizeRefRemoteNode(
    IN REMOTE_NODE *pPdoCb
    )
/*++

Routine Description:
    
    Closes Ref on the remote node
Arguments:

    IN REMOTE_NODE *pPdoCb - RemoteNode

Return Value:

    None
--*/
{
    TRACE( TL_N, TM_Ref, ( "**nicinitalizeRefPdoCb pPdoCb %.8x", pPdoCb   ) );

    nicInitializeRef (&pPdoCb->Ref);
}


BOOLEAN
nicCloseRefRemoteNode(
    IN REMOTE_NODE *pPdoCb
    )
/*++

Routine Description:
    
    Closes Ref on the remote node
Arguments:

    IN REMOTE_NODE *pPdoCb - RemoteNode

Return Value:

    Return value of nicCloseRef

--*/


{
    TRACE( TL_N, TM_Ref, ( "**nicClosePdoCb pPdoCb %.8x", pPdoCb   ) );

    return nicCloseRef (&pPdoCb->Ref);
}


NDIS_STATUS
NtStatusToNdisStatus (
    NTSTATUS NtStatus 
    )

/*++

Routine Description:
    
    Dumps the packet , if the appropriate Debuglevels are set

Arguments:

    NTSTATUS NtStatus  - NtStatus to be converted


Return Value:

    NdisStatus - NtStatus' corresponding NdisStatus

--*/


{
    NDIS_STATUS NdisStatus;
    
    switch (NtStatus)
    {
        case STATUS_SUCCESS:
        {
            NdisStatus = NDIS_STATUS_SUCCESS;
            break;
        }

        case STATUS_UNSUCCESSFUL:
        {   
            NdisStatus = NDIS_STATUS_FAILURE;
            break;
        }

        case STATUS_PENDING:
        {
            NdisStatus = NDIS_STATUS_PENDING;
            break;
        }

        case STATUS_INVALID_BUFFER_SIZE:
        {
            NdisStatus = NDIS_STATUS_INVALID_LENGTH;
            break;
        }

        case STATUS_INSUFFICIENT_RESOURCES:
        {
            NdisStatus = NDIS_STATUS_RESOURCES;
            break;
        }

        case STATUS_INVALID_GENERATION:
        {
            NdisStatus = NDIS_STATUS_DEST_OUT_OF_ORDER;
            break;
        }
        case STATUS_ALREADY_COMMITTED:
        {
            NdisStatus = NDIS_STATUS_RESOURCE_CONFLICT;
            break;
        }

        case STATUS_DEVICE_BUSY:
        {
            NdisStatus = NDIS_STATUS_MEDIA_BUSY;
            break;
        }

        case STATUS_INVALID_PARAMETER:
        {
            NdisStatus = NDIS_STATUS_INVALID_DATA;
            break;

        }
        case STATUS_DEVICE_DATA_ERROR:
        {
            NdisStatus = NDIS_STATUS_DEST_OUT_OF_ORDER;
            break;
        }

        case STATUS_TIMEOUT:
        {
            NdisStatus = NDIS_STATUS_FAILURE;
            break;
        }
        case STATUS_IO_DEVICE_ERROR:
        {
            NdisStatus = NDIS_STATUS_NETWORK_UNREACHABLE;
            break;
        }
        default:
        {
            NdisStatus = NDIS_STATUS_FAILURE;
            TRACE( TL_A, TM_Send, ( "Cause: Don't know, INVESTIGATE %x", NtStatus  ) );

            //ASSERT (0);
        }

    }

    return NdisStatus;



}




VOID
PrintNdisPacket (
    ULONG TM_Comp,
    PNDIS_PACKET pMyPacket
    )
/*++

Routine Description:
    
    Dumps the packet , if the appropriate Debuglevels are set

Arguments:

    ULONG TM_Comp = Debug Component

    PNDIS_PACKET pMyPacket - Packet to be printed 


Return Value:

    None

--*/
{


    PNDIS_BUFFER pPrintNdisBuffer = pMyPacket->Private.Head;

    //
    // Print out the complete NdisPacket . Change to DEBUG ONLY
    //
    while (pPrintNdisBuffer != NULL)
    {
        PVOID pPrintData = NdisBufferVirtualAddress(pPrintNdisBuffer);
        ULONG PrintLength = NdisBufferLength (pPrintNdisBuffer);

        TRACE( TL_D, TM_Comp, ( "  pNdisbuffer %x, pData %x, Len %x", pPrintNdisBuffer, pPrintData, PrintLength) );   

        if (pPrintData != NULL)
        {
            DUMPB (TL_D, TM_Recv, pPrintData, PrintLength);
        }

        pPrintNdisBuffer = pPrintNdisBuffer->Next;
    }


}



VOID
nicAllocatePacket(
    OUT PNDIS_STATUS pNdisStatus,
    OUT PNDIS_PACKET *ppNdisPacket,
    IN PNIC_PACKET_POOL pPacketPool
    )
/*++

Routine Description:
    
    Calls the ndis API to allocate a packet. On Win9X calls to  allocate packets are serialized


Arguments:

    pNdisStatus  - pointer to NdisStatus 

    *ppNdisPacket - Ndis packet Allocated by Ndis,

    pPacketPool - packet pool from which the packet is allocated


Return Value:

    return value of the call to Ndis

--*/

{
    KIRQL OldIrql;

#ifdef Win9X
#if PACKETPOOL_LOCK

    KeAcquireSpinLock(&pPacketPool->Lock, &OldIrql);
#endif
#endif


    NdisAllocatePacket (pNdisStatus,
                        ppNdisPacket,
                        pPacketPool->Handle );
    
#ifdef Win9X
#if PACKETPOOL_LOCK

    KeReleaseSpinLock(&pPacketPool->Lock, OldIrql);
#endif
#endif

    if (*pNdisStatus == NDIS_STATUS_SUCCESS)
    {
            PRSVD pRsvd = NULL;
            PINDICATE_RSVD  pIndicateRsvd = NULL;
            pRsvd =(PRSVD)((*ppNdisPacket)->ProtocolReserved);

            pIndicateRsvd = &pRsvd->IndicateRsvd;

            pIndicateRsvd->Tag  = NIC1394_TAG_ALLOCATED;

            NdisInterlockedIncrement (&pPacketPool->AllocatedPackets);

    }
    else
    {
        *ppNdisPacket = NULL;
        nicIncrementMallocFailure();
    }


}




VOID
nicFreePacket(
    IN PNDIS_PACKET pNdisPacket,
    IN PNIC_PACKET_POOL pPacketPool
    )
/*++

Routine Description:
    
    Free the packet and decrements the outstanding Packet count.

    All calls are serialized on Win9x

Arguments:

    IN PNDIS_PACKET pNdisPacket - Packet to be freed
    IN PNIC_PACKET_POOL pPacketPool  - PacketPool to which the packet belongs 


Return Value:

    None

--*/

{

    KIRQL OldIrql;
    PRSVD pRsvd = NULL;
    PINDICATE_RSVD pIndicateRsvd = NULL;

    pRsvd =(PRSVD)(pNdisPacket->ProtocolReserved);

    pIndicateRsvd = &pRsvd->IndicateRsvd;

    pIndicateRsvd->Tag  = NIC1394_TAG_FREED;

#ifdef Win9X
#if PACKETPOOL_LOCK

    KeAcquireSpinLock(&pPacketPool->Lock, &OldIrql);

#endif
#endif

    NdisInterlockedDecrement (&pPacketPool->AllocatedPackets);

    NdisFreePacket (pNdisPacket);

#ifdef Win9X
#if PACKETPOOL_LOCK

    KeReleaseSpinLock(&pPacketPool->Lock, OldIrql);
#endif
#endif

}



VOID
nicFreePacketPool (
    IN PNIC_PACKET_POOL pPacketPool
    )
/*++

Routine Description:

    frees the packet pool after waiting for the outstanding packet count to go to zero      

Arguments:

    IN PNIC_PACKET_POOL pPacketPool  - PacketPool which is to be freed


Return Value:

    None

--*/
{
    ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
    

    while (NdisPacketPoolUsage (pPacketPool->Handle)!=0)
    {
        TRACE( TL_V, TM_Cm, ( "  Waiting PacketPool %x, AllocatedPackets %x", 
        pPacketPool->Handle, pPacketPool->AllocatedPackets  ) );   

        NdisMSleep (10000);
    }
    
    NdisFreePacketPool (pPacketPool->Handle);

    pPacketPool->Handle = NULL;
    ASSERT (pPacketPool->AllocatedPackets   == 0);
}

VOID
nicAcquireSpinLock (
    IN PNIC_SPIN_LOCK pNicSpinLock,
    IN PUCHAR   FileName,
    IN UINT LineNumber
    )
/*++

Routine Description:

    Acquires a spin lock and if the Dbg, then it will spew out the line and file

Arguments:

    NIC_SPIN_LOCK - Lock to be acquired

Return Value:

    None

--*/
{
    
        PKTHREAD                pThread;

        TRACE (TL_V, TM_Lock, ("Lock %x, Acquired by File %s, Line %x" , pNicSpinLock, FileName, LineNumber)) ; 

        NdisAcquireSpinLock(&(pNicSpinLock->NdisLock));

#if TRACK_LOCKS     
        pThread = KeGetCurrentThread();


        pNicSpinLock->OwnerThread = pThread;
        NdisMoveMemory(pNicSpinLock->TouchedByFileName, FileName, LOCK_FILE_NAME_LEN);
        pNicSpinLock->TouchedByFileName[LOCK_FILE_NAME_LEN - 1] = 0x0;
        pNicSpinLock->TouchedInLineNumber = LineNumber;
        pNicSpinLock->IsAcquired++;

#endif
}



VOID
nicReleaseSpinLock (
    IN PNIC_SPIN_LOCK pNicSpinLock,
    IN PUCHAR   FileName,
    IN UINT LineNumber
)
/*++

Routine Description:

    Release a spin lock and if Dbg is On, then it will spew out the line and file

Arguments:

    pNicSpinLock - Lock to be Release
    FileName - File Name
    LineNumber - Line

Return Value:

    None

--*/
{
    
        PKTHREAD                pThread;

        TRACE (TL_V, TM_Lock, ("Lock %x, Released by File %s, Line %x" , pNicSpinLock, FileName, LineNumber)) ; 

#if TRACK_LOCKS     
        
        pThread = KeGetCurrentThread();

        NdisMoveMemory(pNicSpinLock->TouchedByFileName, FileName, LOCK_FILE_NAME_LEN);
        pNicSpinLock->TouchedByFileName[LOCK_FILE_NAME_LEN - 1] = 0x0;
        pNicSpinLock->TouchedInLineNumber = LineNumber;
        pNicSpinLock->IsAcquired--;
        pNicSpinLock->OwnerThread = 0;
#endif
        NdisReleaseSpinLock(&(pNicSpinLock->NdisLock));

}




VOID
nicInitializeNicSpinLock (
    IN PNIC_SPIN_LOCK pNicSpinLock
    )
/*++

Routine Description:

    Initializes the lock in the SpinLock

Arguments:
    pNicSpinLock - SpinLock
    
Return Value:

    None

--*/
{
    NdisAllocateSpinLock (&pNicSpinLock->NdisLock); 
}


VOID 
nicFreeNicSpinLock (
    IN PNIC_SPIN_LOCK pNicSpinLock
    )
/*++

Routine Description:

        Frees the spinlock
        
Arguments:
    pNicSpinLock - SpinLock
    
Return Value:

    None

--*/
{
    ASSERT ((ULONG)pNicSpinLock->NdisLock.SpinLock == 0);
    NdisFreeSpinLock (&pNicSpinLock->NdisLock); 
}


UINT
nicGetSystemTime(
    VOID
    )
/*++
    Returns system time in seconds.

    Since it's in seconds, we won't overflow unless the system has been up 
for over
    a  100 years :-)
--*/
{
    LARGE_INTEGER Time;
    NdisGetCurrentSystemTime(&Time);
    Time.QuadPart /= 10000000;          //100-nanoseconds to seconds.

    return Time.LowPart;
}


UINT
nicGetSystemTimeMilliSeconds(
    VOID
    )
/*++
    Returns system time in seconds.

    Since it's in seconds, we won't overflow unless the system has been up 
for over
    a  100 years :-)
--*/
{
    LARGE_INTEGER Time;
    NdisGetCurrentSystemTime(&Time);
    Time.QuadPart /= 10000;          //10-nanoseconds to seconds.

    return Time.LowPart;
}




ULONG 
SwapBytesUlong(
    IN ULONG Val)
{
            return  ((((Val) & 0x000000ff) << 24)   |   (((Val) & 0x0000ff00) << 8) |   (((Val) & 0x00ff0000) >> 8) |   (((Val) & 0xff000000) >> 24) );
}




void
nicTimeStamp(
    char *szFormatString,
    UINT Val
    )
/*++

Routine Description:
  Execute and print a time stamp
 
Arguments:


Return Value:


--*/
{
    UINT Minutes;
    UINT Seconds;
    UINT Milliseconds;
    LARGE_INTEGER Time;


    NdisGetCurrentSystemTime(&Time);



    Time.QuadPart /= 10000;         //10-nanoseconds to milliseconds.
    Milliseconds = Time.LowPart; // don't care about highpart.
    Seconds = Milliseconds/1000;
    Milliseconds %= 1000;
    Minutes = Seconds/60;
    Seconds %= 60;


    DbgPrint( szFormatString, Minutes, Seconds, Milliseconds, Val);
}




VOID
nicDumpPkt (
    IN PNDIS_PACKET pPacket,
    CHAR * str
    )
{
    PNDIS_BUFFER pBuffer;
    extern BOOLEAN g_ulNicDumpPacket ;
    
    if ( g_ulNicDumpPacket == FALSE)
    {
        return ;
    }


    pBuffer = pPacket->Private.Head;


    DbgPrint (str);
    DbgPrint ("Packet %p TotLen %x", pPacket, pPacket->Private.TotalLength);
     
    do
    {
        ULONG Length = nicNdisBufferLength (pBuffer);
        PUCHAR pVa = nicNdisBufferVirtualAddress (pBuffer);


        DbgPrint ("pBuffer %p, Len %x \n", pBuffer, Length);    
        Dump( pVa, Length, 0, 1 );

        pBuffer = pBuffer->Next;


    } while (pBuffer != NULL);



}


VOID 
nicDumpMdl (
    IN PMDL pMdl,
    IN ULONG LengthToPrint,
    IN CHAR *str
    )
{

    ULONG MdlLength ;
    PUCHAR pVa;
    extern BOOLEAN g_ulNicDumpPacket ;

    if ( g_ulNicDumpPacket == FALSE )
    {
        return;
    }

    MdlLength =  MmGetMdlByteCount(pMdl);
    //
    // if Length is zero then use MdlLength
    //
    if (LengthToPrint == 0)
    {
        LengthToPrint = MdlLength;
    }
    //
    // Check for invalid length
    // 
    
    if (MdlLength < LengthToPrint)
    {
        return;
    }

    pVa =  MmGetSystemAddressForMdlSafe(pMdl,LowPagePriority );

    if (pVa == NULL)
    {
        return;
    }
    
    DbgPrint (str);
    DbgPrint ("pMdl %p, Len %x\n", pMdl, LengthToPrint);    
    
    Dump( pVa, LengthToPrint, 0, 1 );


}