/*
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

    (C) Copyright 1998
        All rights reserved.

§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

  Portions of this software are:

    (C) Copyright 1994 TriplePoint, Inc. -- http://www.TriplePoint.com
        License to use this software is granted under the same terms
        outlined in the Microsoft Windows Device Driver Development Kit.

    (C) Copyright 1992 Microsoft Corp. -- http://www.Microsoft.com
        License to use this software is granted under the terms outlined in
        the Microsoft Windows Device Driver Development Kit.

§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@doc INTERNAL TpiDebug TpiDebug_c

@module TpiDebug.c |

    This module, along with <f TpiDebug\.h>, implements code and macros to
    support NDIS driver debugging.  This file must be linked with the driver
    to support debug dumps and logging.

@comm

    The code and macros defined by these modules is only generated during
    development debugging when the C pre-processor macro flag (DBG == 1).
    If (DBG == 0) no code will be generated, and all debug strings will be
    removed from the image.

    This is a device independent module which can be re-used, without
    change, by any driver or application.

@head3 Contents |
@index class,mfunc,func,msg,mdata,struct,enum | TpiDebug_c

@end
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§
*/

#if defined(_EXE_) || defined(_DLL_)
typedef char CHAR, *PCHAR;
typedef unsigned char  UCHAR,  *PUCHAR;
typedef unsigned short USHORT, *PUSHORT;
typedef unsigned long  ULONG,  *PULONG;
typedef unsigned int  *PUINT;

# include <windows.h>
#elif defined(_VXD_)
# include <basedef.h>
# include <vmm.h>
# pragma VxD_LOCKED_CODE_SEG
# pragma VxD_LOCKED_DATA_SEG
#else
# include <windef.h>
#endif

#include "TpiDebug.h"

#if DBG

/*
// Sometimes the debug output seriously impacts the run-time performance,
// so it is necessary to turn off the debug output.  In this case, you can
// capture some debug trace information into the DbgLogBuffer, and it can
// be examined later without impacting the run-time performance.
*/
#define DBG_LOG_ENTRIES     100     // Maximum number of FIFO log entries.
#define DBG_LOG_SIZE        128     // Maximum number of bytes per entry.

#if defined(_VXD_)
DBG_SETTINGS    DbgSettings = { DBG_DEFAULTS, {'V','X','D',0 } };
#elif defined(_EXE_)
DBG_SETTINGS    DbgSettings = { DBG_DEFAULTS, {'E','X','E',0 } };
#elif defined(_DLL_)
DBG_SETTINGS    DbgSettings = { DBG_DEFAULTS, {'D','L','L',0 } };
#elif defined(_SYS_)
DBG_SETTINGS    DbgSettings = { DBG_DEFAULTS, {'S','Y','S',0 } };
#else
DBG_SETTINGS    DbgSettings = { DBG_DEFAULTS, {'T','P','I',0 } };
#endif

PDBG_SETTINGS   DbgInfo = &DbgSettings;
UINT            DbgLogIndex = 0;
UCHAR           DbgLogBuffer[DBG_LOG_ENTRIES][DBG_LOG_SIZE] = { { 0 } };


/* @doc INTERNAL TpiDebug TpiDebug_c DbgPrintData
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DbgPrintData> outputs data to the debug display formatted in HEX and
    ASCII for easy viewing.

    <f Note>: This routine is used for debug output only.
    It is not compiled into the retail version.

@ex <tab> |
    DbgPrintData(ReceiveBuffer, 14, 0);                     // Packet header
    DbgPrintData(ReceiveBuffer+14, BytesReceived-14, 14);   // Packet data

0000: ff ff ff ff ff ff 0a 22 23 01 02 03 00 10        ......."#.....
000E: 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 40  ABCDEFGHIJKMNOPQ

*/

VOID DbgPrintData(
    IN PUCHAR                   Data,                       // @parm
    // Pointer to first byte of data to be displayed.

    IN UINT                     NumBytes,                   // @parm
    // Number of bytes to be displayed.

    IN ULONG                    Offset                      // @parm
    // Value to be added to the offset counter displayed at the start of each
    // line.  This is useful for viewing data whose base offset is relative to
    // another, non-zero starting address.

    )
{
    UINT                        LineStart;
    UINT                        LineIndex;

    /*
    // Display the caller's buffer with up to 16 bytes per line.
    */
    for (LineStart = 0; LineStart < NumBytes; LineStart += 16)
    {
        /*
        // Display the starting offset of the line.
        */
        DbgPrint("%04lx: ", LineStart + Offset);

        /*
        // Display a line of HEX byte values.
        */
        for (LineIndex = LineStart; LineIndex < (LineStart+16); LineIndex++)
        {
            if (LineIndex < NumBytes)
            {
                DbgPrint("%02x ",(UINT)((UCHAR)*(Data+LineIndex)));
            }
            else
            {
                DbgPrint("   ");
            }
        }
        DbgPrint("  ");     // A little white space between HEX and ASCII.

        /*
        // Display the corresponding ASCII byte values if they are printable.
        // (i.e. 0x20 <= N <= 0x7F).
        */
        for (LineIndex = LineStart; LineIndex < (LineStart+16); LineIndex++)
        {
            if (LineIndex < NumBytes)
            {
                char c = *(Data+LineIndex);

                if (c < ' ' || c > 'z')
                {
                    c = '.';
                }
                DbgPrint("%c", (UINT)c);
            }
            else
            {
                DbgPrint(" ");
            }
        }
        DbgPrint("\n");     // End of line.
    }
}


/* @doc INTERNAL TpiDebug TpiDebug_c DbgQueueData
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DbgQueueData> saves data to the DbgLogBuffer so it can be viewed later
    with the debugger.

    <f Note>: This routine is used for debug output only.
    It is not compiled into the retail version.

*/

VOID DbgQueueData(
    IN PUCHAR                   Data,                       // @parm
    // Pointer to first byte of data to be displayed.

    IN UINT                     NumBytes,                   // @parm
    // Number of bytes to be displayed.

    IN UINT                     Flags                       // @parm
    // A flag descriptor to help identify the log entry.
    )
{
    /*
    // Point to the next available entry in the DbgLogBuffer.
    */
    PUCHAR LogEntry = &DbgLogBuffer[DbgLogIndex++][0];

    /*
    // Wrap around on the next entry if needed.
    */
    if (DbgLogIndex >= DBG_LOG_ENTRIES)
    {
        DbgLogIndex = 0;
    }

    /*
    // Save the flags parameter in the first WORD of the log buffer.
    */
    *((PUSHORT) LogEntry) = (USHORT) Flags;
    LogEntry += sizeof(PUSHORT);

    /*
    // Save the NumBytes parameter in the second WORD of the log buffer.
    */
    *((PUSHORT) LogEntry) = (USHORT) NumBytes;
    LogEntry += sizeof(NumBytes);

    /*
    // Don't try to save more than we have room for.
    */
    if (NumBytes > DBG_LOG_SIZE - sizeof(USHORT) * 2)
    {
        NumBytes = DBG_LOG_SIZE - sizeof(USHORT) * 2;
    }

    /*
    // Save the rest of the data in the remaining portion of the log buffer.
    */
    while (NumBytes--)
    {
        *LogEntry++ = *Data++;
    }
}


/* @doc INTERNAL TpiDebug TpiDebug_c DbgBreakPoint
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func VOID | DbgBreakPoint |

    <f DbgBreakPoint> is defined in the NT kernel for SYS drivers, but we
    override it here so we can support for SYS's, EXE's, VXD's, and DLL's.

*/
#if defined(_MSC_VER) && (_MSC_VER <= 800)
// Must be building with 16-bit compiler
VOID __cdecl DbgBreakPoint(VOID)
#else
// Must be building with 32-bit compiler
VOID __stdcall DbgBreakPoint(VOID)
#endif
{
#if !defined(_WIN64)
    __asm int 3;
#endif
}


/* @doc INTERNAL TpiDebug TpiDebug_c DbgPrint
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func ULONG __cdecl | DbgPrint |

    <f DbgPrint> is defined in the kernel for SYS drivers, otherwise it is
    supported here for EXE's, VXD's, and DLL's.

@parm PCHAR | Format |
    printf style format string.

@parm OPTIONAL | Params |
    Zero or more optional parameters as needed by the format string.

*/

#if defined(_VXD_)

#if !defined(NDIS_DOS)
ULONG __cdecl DbgPrint(PCHAR Format, ...)
{
    ULONG   result = 0;

    __asm lea  eax, (Format + 4)
    __asm push eax
    __asm push Format
    VMMCall(_Debug_Printf_Service)
    __asm add esp, 4*2
    __asm mov result, eax

    return (result);
}
#endif

#elif defined(_EXE_) || defined(_DLL_)

UCHAR   DbgString[1024];

ULONG __cdecl DbgPrint(PCHAR Format, ...)
{
    ULONG   result;

    result = wvsprintf(DbgString, Format, ((PCHAR) &Format) + sizeof(PCHAR));

    OutputDebugString(DbgString);

    if (result >= sizeof(DbgString))
    {
        // We just blew the stack!
        // Since we can't return, we have to generate a stack-fault interrupt
        __asm int 1;
        __asm int 3;
        __asm int 12;
    }
    return (result);
}
#endif // DbgPrint

/*
 * If DBG_SILENT is set, all TERSE debug goes here. An assertion
 * will dump the block.
 */
#define DBG_QUEUE_LEN       4096
UINT    DbgIndex=0;
UINT    DbgLen=0;
UCHAR   DbgQueue[DBG_QUEUE_LEN] =  {0};
UCHAR   DbgLock=0;


/* @doc INTERNAL TpiDebug TpiDebug_c DbgDumpSilentQueue
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DbgDumpSilentQueue> dumps the contents of the silent debug queue to
    the monitor.

*/

void DbgDumpSilentQueue(
    void
    )
{
    if (DbgLen >= DBG_QUEUE_LEN)
    {
        DbgPrintData(
            &DbgQueue[DbgIndex],
            DBG_QUEUE_LEN-DbgIndex,
            0);
        if (DbgIndex)
        {
            DbgPrint("\n");
            DbgPrintData(
                DbgQueue,
                DbgIndex-1,
                0);
        }
        DbgPrint("\n");
    }
    else if (DbgLen)
    {
        DbgPrintData(
                DbgQueue,
                DbgIndex-1,
                0);
        DbgPrint("\n");
    }
}

#if NDIS_NT

/* @doc INTERNAL TpiDebug TpiDebug_c _assert
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f _assert> overrides the assertion function provided by the operating
    system. Dumps the contents of debug queue, prints the assertion, and
    then traps to the debugger.  Used for debugging only.

*/

void _CRTAPI1 _assert(
    void *                      exp,                        // @parm
    // ASCIIZ pointer to the expression causing the fault.

    void *                      file,                       // @parm
    // ASCIIZ pointer to the name of the file.

    unsigned                    line                        // @parm
    // Line offset within the file where the assertion is defined.
    )
{
    DbgDumpSilentQueue();
    DbgPrint("Assertion Failed: %s at %s:%d\n",exp,file,line);
    DbgBreakPoint();
}
#endif


/* @doc INTERNAL TpiDebug TpiDebug_c DbgSilentQueue
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DbgSilentQueue> logs a string to the debug queue which can be
    displayed later using <f DbgDumpSilentQueue>.  Used for debugging only.

*/

void DbgSilentQueue(
    PUCHAR                      str                         // @parm
    // Pointer to string to be placed in DbgQueue.
    )
{
    /*
    // If the debug queue is busy, just
    // bail out.
    */
    if ((++DbgLock) > 1)
    {
        goto exit;
    }

    while (str && *str)
    {
        DbgQueue[DbgIndex] = *str++;
        DbgLen++;
        if ((++DbgIndex) >= DBG_QUEUE_LEN)
        {
            DbgIndex = 0;
        }
    }
exit:
    DbgLock--;
}


/* @doc INTERNAL TpiDebug TpiDebug_c DbgPrintFieldTable
§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§§

@func

    <f DbgPrintFieldTable> displays the contents of a C data structure in
    a formatted output to the debugger.  This can be used when symbolic
    debugging is not available on the target platform.

*/

void DbgPrintFieldTable(
    IN PDBG_FIELD_TABLE         pFields,                    // @parm
    // A pointer to an array of field records <t DBG_FIELD_TABLE>.

    IN PUCHAR                   pBaseContext,               // @parm
    // References the base of the structure where the values will be displayed
    // from.  This should be a pointer to the first byte of the structure.

    IN PUCHAR                   pBaseName                   // @parm
    // Pointer to C string containing the name of the structure being displayed.
    )
{
    DbgPrint("STRUCTURE: @0x%08X %s\n", pBaseContext, pBaseName);

    while (pFields->FieldName)
    {
        switch (pFields->FieldType)
        {
        case sizeof(ULONG):
            DbgPrint("\t%04X: %-32s=0x%08X\n", pFields->FieldOffset,
                     pFields->FieldName,
                     *(PULONG)(pBaseContext+pFields->FieldOffset));
            break;

        case sizeof(USHORT):
            DbgPrint("\t%04X: %-32s=0x%04X\n", pFields->FieldOffset,
                     pFields->FieldName,
                     *(PUSHORT)(pBaseContext+pFields->FieldOffset));
            break;

        case sizeof(UCHAR):
            DbgPrint("\t%04X: %-32s=0x%02X\n", pFields->FieldOffset,
                     pFields->FieldName,
                     *(PUCHAR)(pBaseContext+pFields->FieldOffset));
            break;

        default:
            ASSERT(0);
            break;
        }
        pFields++;
    }
}

#endif // DBG