/*++
*******************************************************************************
* 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>

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;
}


VOID
DigiDebugInit(
	PUNICODE_STRING pRegistryPath OPTIONAL,
	ULONG           defMask, 
	ULONG           defMode )
/*++

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

Return Value:

	None

--*/
{
	DebugInit();

	if ( pRegistryPath )
	{
		HANDLE hKey;
		if ( NT_SUCCESS( DigiRegOpenKey( &hKey, pRegistryPath->Buffer ) ) )
		{
			DigiRegQueryDword( hKey, L"DebugMask", &gDebugMask, DigiRegFlagOptional, defMask );
			DigiRegQueryDword( hKey, L"DebugMode", &gDebugMode, DigiRegFlagOptional, defMode );
			DigiRegQueryDword( hKey, L"DebugBreak", &gDebugBreak, DigiRegFlagOptional, FALSE );
			DigiRegCloseKey( hKey );
		}
	}
}


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 DBG
	if ( ConsoleModeEnabled( gDebugMode ) )
		DbgPrint( "%s", pOutputBuf );
#endif

#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 DBG
    if ( ConsoleModeEnabled( gDebugMode ) )
		DbgPrint( "%s", pOutputBuf );
#endif

#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