/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved          *
*                                                                             *
* This software contains proprietary and confidential information of Digi     *
* International Inc.  By accepting transfer of this copy, Recipient agrees    *
* to retain this software in confidence, to prevent disclosure to others,     *
* and to make no use of this software other than that for which it was        *
* delivered.  This is an unpublished copyrighted work of Digi International   *
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly   *
* prohibited.                                                                 *
*                                                                             *
* Restricted Rights Legend                                                    *
*                                                                             *
* Use, duplication, or disclosure by the Government is subject to             *
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in         *
* Technical Data and Computer Software clause at DFARS 252.227-7031 or        *
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -          *
* Restricted Rights at 48 CFR 52.227-19, as applicable.                       *
*                                                                             *
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343          *
*                                                                             *
*******************************************************************************

Module Name:

   digidebug.c

Abstract:

   Routines to display debug messages via the kernel debugger.

Author:

   Michael Zarns

--*/

#include "digilib.h"
#include <stdio.h>
#include <stdarg.h>

// Include Stratus Features
#include "sraddk.h"
#include "sradbg.h"
// Included for debug "digi"
#include "digidxb.h"

ULONG gDebugMode  = DEBUGMODE_CONSOLE;
ULONG gDebugMask  = DEBUG_BREAK|DEBUG_ERROR;
ULONG gDebugBreak = FALSE;


#if DBG || DIGIDEBUG_ENABLE

#define BUFFER_MAX  (8 * 1024)

STATIC BOOLEAN       gDebugInitialized = FALSE;

STATIC UCHAR         gNonPagedBuffer[BUFFER_MAX];
STATIC DIGI_SPINLOCK gNonPagedLock = {0};


// **********************************************
// ***       CAUTION - Pageable data          ***
// **********************************************
#pragma data_seg("PAGE")

STATIC UCHAR        gPagedBuffer[BUFFER_MAX];
STATIC FAST_MUTEX   gPagedMutex = {0};

#pragma data_seg()
// **********************************************
// ***          End Pageable data             ***
// **********************************************


STATIC INLINE VOID
DebugInit()
/*++

Description:

    Initialize global debug params.

Parameters:

    None

Return Value:

    None

--*/
{
    DigiInitializeSpinLock( &gNonPagedLock );
    ExInitializeFastMutex( &gPagedMutex );
    gDebugInitialized = TRUE;
}
#endif

VOID
DigiDebugInitEx(
    PUNICODE_STRING pRegistryPath OPTIONAL,
    ULONG           defMask, 
    ULONG           defMode,
    PCHAR           Prefix,
    PWCHAR          Version)
/*++

Description:

    Initialize debug settings.  Optionally read settings from the registry.

Parameters:

    pRegistryPath
        Optional unicode string defining the drivers registry path

    defMask
        Default debug mask

    defMode
        Default debug mode

    Prefix  
        For debug output

    Version
        Pointer to unicode version string

Return Value:

    None

--*/
{
#if DBG || DIGIDEBUG_ENABLE
    DebugInit();
#endif

    if ( pRegistryPath )
    {
        HANDLE hKey;
        if ( NT_SUCCESS( DigiRegOpenKey( &hKey, pRegistryPath->Buffer ) ) )
        {
#if DBG || DIGIDEBUG_ENABLE
            DigiRegQueryDword( hKey, L"DebugMask", &gDebugMask, DigiRegFlagOptional, defMask );
            DigiRegQueryDword( hKey, L"DebugMode", &gDebugMode, DigiRegFlagOptional, defMode );
            DigiRegQueryDword( hKey, L"DebugBreak", &gDebugBreak, DigiRegFlagOptional, FALSE );
#endif
#ifdef STRATUS_DEVICE
            {
                HANDLE hParamKey;
                if ( NT_SUCCESS(DigiRegOpenSubkey( &hParamKey, hKey, L"Parameters" ) ) )
                {
                    ULONG RegVal;

                    DigiRegQueryDword( hParamKey, DEBUGTYPE, &RegVal, DigiRegFlagOptional, DEFAULT_DEBUGTYPE );
                    SRASETTYPE(RegVal);
                    DigiRegQueryDword( hParamKey, DEBUGLEVEL, &RegVal, DigiRegFlagOptional, DEFAULT_DEBUGLEVEL );
                    SRASETLEVEL(RegVal);
                    DigiRegQueryDword( hParamKey, DEBUGFLAGS, &RegVal, DigiRegFlagOptional, DEFAULT_DEBUGFLAGS );
                    SRASETFLAGS(RegVal);

                    DigiRegCloseKey( hParamKey );
                }
            }
#endif

            DigiRegCloseKey( hKey );
        }
    }
#ifdef STRATUS_DEVICE
    SRASETPREFIX(Prefix? Prefix : "Digi");
    if (Version) {
        SRAPRINT(DBGLVL_ALWAYS,("%S\n",Version));
    }
#endif
}

#if DBG || DIGIDEBUG_ENABLE

BOOLEAN
DigiDbgBreakOnEntryEnabled( VOID )
{
    return gDebugBreak == TRUE;
}


VOID
DigiDbgDump(
    IN PCSZ pszFormat, ... )
/*++

Description:

    Write a debug string to either the console, or to a log file if enabled.  Uses
    library global gDebugMask mask to determine whether to print the message.

    Small dilemna here.  We can't hold a spinlock while we access pointers, since they
    may point to pageable code, and we can't wait for an event since we may be called 
    at DISPATCH_LEVEL.  Two possible solutions: alloc a buffer while the default one is
    in use, or spin waiting for the default buffer to become available.

Parameters:

    pszFormat
        Format string to display.

    ...
        Additional params specified by Format string.

Return Value:

    None

--*/
{
    BOOLEAN fAcquired = FALSE;
    PVOID pOutputBuf;

    if ( !gDebugInitialized )
        DebugInit();

    // Acquire exclusive access to the global output buffer
    // Two global buffers exist, one protected by spinlock, the other protected by a 
    // mutex.  Use the Mutex if IRQL < DISPATCH_LEVEL, otherwise use the SpinLock.
    if ( KeGetCurrentIrql() < DISPATCH_LEVEL )
    {
        ExAcquireFastMutex( &gPagedMutex );
        pOutputBuf = gPagedBuffer;
    }
    else
    {
        DigiAcquireSpinLock( &gNonPagedLock );
        pOutputBuf = gNonPagedBuffer;
    }

    // sprintf the debug message to the output buffer
    {
        va_list ArgList;
        va_start( ArgList, pszFormat );
        _vsnprintf( pOutputBuf, BUFFER_MAX, pszFormat, ArgList );
        va_end( ArgList );
    }

    // Display the message on the appropriate devices

    if ( ConsoleModeEnabled( gDebugMode ) )
        SraDbgPrint( "%s\n",pOutputBuf );

#ifndef NO_MEMPRINT
    if ( FileModeEnabled( gDebugMode ) )
        MemPrint( pOutputBuf );
#endif

    // Release exclusive access to the global output buffer
    if ( KeGetCurrentIrql() < DISPATCH_LEVEL )
        ExReleaseFastMutex( &gPagedMutex );
    else
        DigiReleaseSpinLock( &gNonPagedLock );
}


STATIC VOID
DumpBytes(
    IN PUCHAR        pBuffer, 
    IN UCHAR const * pData, 
    IN ULONG         cbData )
/*++

Description:

    Build display string containing bytes in hex and ascii.  

Parameters:

    pBuffer
        Output buffer.

    pData
        Source data

    cbData
        Count of bytes to display.

Return Value:

    None.

--*/
{
// Cost = (cb buffer space per line) / (cb data displayed per line)
#define COST    (ULONG)( 81/16 )

    ULONG cb = cbData * COST < BUFFER_MAX ? cbData : BUFFER_MAX / COST - 1;
    UCHAR const * pIn = pData;
    PUCHAR pOut = pBuffer;

    // Write debug data one line at a time.  16 bytes per line.

    while ( cb >= 16 )
    {
        ULONG i;
        // *************** WARNING WARNING WARNING WARNING ******************************
        // Be careful here!  Changing this display string affects how pOut is 
        // incremented next.  pOut *must* be incremented by the actual length of this
        // display string!
        // *************** WARNING WARNING WARNING WARNING ******************************
        sprintf( pOut, "   %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x    ",
                 pIn[0], pIn[1], pIn[2], pIn[3], pIn[4], pIn[5], pIn[6], pIn[7], 
                 pIn[8], pIn[9], pIn[10], pIn[11], pIn[12], pIn[13], pIn[14], pIn[15] );
        pOut += 54;
        ASSERT( *pOut == 0 );   // should be pointing at the null terminator from previous string

        for ( i = 0; i < 16; i++ )
            *pOut++ = pIn[i] >= 0x20 && pIn[i] <= 0x7E ? pIn[i] : '.';

        *pOut++ = '\n';
        pIn += 16;
        cb -= 16;
    }

    *pOut = 0;  // Null terminate it!

    // Less than 16 bytes left to display, write the rest.
    if ( cb != 0 )
    {
        ULONG i;

        sprintf( pOut, "   " );
        pOut += 3;

        for ( i = 0; i < 16; i++ )
        {
            sprintf( pOut, i < cb ? "%02x " : "   ", pIn[i] );
            pOut += 3;
        }

        sprintf( pOut, "   " );
        pOut += 3;

        for ( i = 0; i < cb; i++ )
            *pOut++ = pIn[i] >= 0x20 && pIn[i] <= 0x7E ? pIn[i] : '.';

        sprintf( pOut, "\n" );
    }
}


VOID
DigiDbgDumpData(
    IN VOID const * pData, 
    IN ULONG        cbData )
/*++

Description:

    Dump memory to the debugger.  Display in bytes.

Parameters:

    pData
        Data to display

    cbData
        Count of bytes to display

Return Value:

    None

--*/
{
    ULONG cb = cbData;
    BOOLEAN fAcquired = FALSE;
    PVOID pOutputBuf;

    if ( !gDebugInitialized )
        DebugInit();

    // Acquire exclusive access to the global output buffer
    // Two global buffers exist, one protected by spinlock, the other protected by a 
    // mutex.  Use the Mutex if IRQL < DISPATCH_LEVEL, otherwise use the SpinLock.
    if ( KeGetCurrentIrql() < DISPATCH_LEVEL )
    {
        ExAcquireFastMutex( &gPagedMutex );
        pOutputBuf = gPagedBuffer;
    }
    else
    {
        DigiAcquireSpinLock( &gNonPagedLock );
        pOutputBuf = gNonPagedBuffer;
    }
    
    // sprintf the debug message to the output buffer
    DumpBytes( pOutputBuf, pData, cb );

    // Display the message on the appropriate devices
    if ( ConsoleModeEnabled( gDebugMode ) )
        SraDbgPrint( "%s\n",pOutputBuf );

#ifndef NO_MEMPRINT
    if ( FileModeEnabled( gDebugMode ) )
        MemPrint( pOutputBuf );
#endif

    // Release exclusive access to the global output buffer
    if ( KeGetCurrentIrql() < DISPATCH_LEVEL )
        ExReleaseFastMutex( &gPagedMutex );
    else
        DigiReleaseSpinLock( &gNonPagedLock );
}

#endif DBG || DIGIDEBUG_ENABLE
