/*++
*******************************************************************************
* Copyright (c) 1990-2000 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:

   kddxb.c

Abstract:

   Windbg extension dll for dxb.

   Notice lots of normal kd extension dll code is missing or seems different...
   That's because we've moved the common code out into kdlib and wrapped up all 
   interfaces defined by wdbgexts.h in that lib.  Why you say?  Because 
   incompatibilities between the NT4 DDK headers and newer Platform SDK headers 
   means we can't include both within a single C source file.  And, as a side benefit
	we could actually share code between kd extension dlls by putting it in that lib.

Author:

   Michael Zarns

--*/

#include "digidxb.h"

#include <stdio.h>
#include <stdlib.h>
#include "kdlib.h"

#ifndef STATIC
#define STATIC static
#endif

#define BOOL ULONG

///////////////////////////////////////////////////////////////////////////////
//
// Globals and constants
//

char *HelpStrings[] =
{
   "extlist            - display list of device extensions",
   "streamlist <addr>  - display list of active streams for the device extension",
   "ext <addr> [flags] - display device extension",
   "stream <addr>      - display stream object",
   "densepanic <addr>  - display densemodem adapter panic struct by device extension",
	"denseglobal <addr> - display densemodem adapter global struct by device extension"
};

int nHelpStrings = sizeof HelpStrings / sizeof HelpStrings[0];


///////////////////////////////////////////////////////////////////////////////
//
// Internal Routines
//


STATIC ULONG_PTR
GetUlongValue( PCHAR String )
{
   ULONG_PTR Location;
   ULONG Value;
   ULONG cbRead;
   PCHAR p;

   p = malloc( strlen( String ) + 2 );	// 1 address-of char, 1 terminating null: 2 xtra chars
   if (p == NULL)
   {
      WdbgPrint( "out of memory error\n" );
      return 0;
   }
   strcpy( p, "&" );
   strcat( p, String );
   Location = WdbgGetExpression( p );
   free( p );

   if( !Location )
   {
      WdbgPrint( "unable to get %s\n", String );
      return 0;
   }

   if( WdbgReadMemory( Location, &Value, sizeof Value, &cbRead ) == 0 || 
       cbRead != sizeof Value )
   {
      WdbgPrint("unable to get %s\n",String);
      return 0;
   }

   return Value;
}


STATIC ULONG_PTR
GetListFlink( ULONG_PTR pListEntry )
{
	ULONG_PTR Flink;
	ULONG cbRead;
	BOOL Success = WdbgReadMemory( pListEntry + FIELD_OFFSET( LIST_ENTRY, Flink ), &Flink, sizeof Flink, &cbRead );
	if ( !Success || cbRead != sizeof Flink )
		Flink = 0;

	return Flink;
}


STATIC ULONG_PTR
GetDeviceExt( VOID )
{
	BOOL Success;
	ULONG cbRead;
	ULONG_PTR pHead;
	ULONG_PTR Flink, pExt = 0;
	DXB_DEVICE_EXTENSION Ext;
   
   pHead = WdbgGetExpression( "&DigiDxb!gDeviceExtList" );

	Flink = GetListFlink( pHead );

	if ( Flink != 0 && Flink != pHead )
		pExt = (ULONG_PTR)CONTAINING_RECORD( Flink, DXB_DEVICE_EXTENSION, ExtListEntry );

	return pExt;
}


//////////////////////////////////////////////////////////////////////////////
//
// Dxb Specific Exported Routines
//

WDBG_API( help )
/*++

Routine Description:

   This function is called as a KD extension to dump all the commands
   available.

   Called as:

       !kddxb.help

--*/
{
   int i;

   WdbgPrint( "kddxb functions:\n" );
   for( i = 0; i < nHelpStrings; i++ )
   {
      WdbgPrint( "   %s\n", HelpStrings[i] );
   }
}



WDBG_API( extlist )
/*++

Description:

	Display list of dxb driver device extensions using dxb global ext list.

--*/
{
	BOOL Success;
	ULONG cbRead;
	ULONG_PTR pHead, pEntry;
   
   pHead = WdbgGetExpression( "&DigiDxb!gDeviceExtList" );

   Success = WdbgReadMemory( (ULONG_PTR)&((PLIST_ENTRY)pHead)->Flink, &pEntry, sizeof pEntry, &cbRead );
   if( !Success || cbRead != sizeof pEntry )
   {
      WdbgPrint( "Unable to read pHead->Flink\n" );
		goto Exit;
   }

	while( pEntry != pHead )
	{
      WdbgPrint( "   0x%p\n", CONTAINING_RECORD( pEntry, DXB_DEVICE_EXTENSION, ExtListEntry ) );

	   Success = WdbgReadMemory( (ULONG_PTR)&((PLIST_ENTRY)pEntry)->Flink, &pEntry, sizeof pEntry, &cbRead );
	   if( !Success || cbRead != sizeof pEntry )
	   {
	      WdbgPrint( "Unable to read pEntry->Flink\n" );
			goto Exit;
	   }
	}

Exit:
	return ;
}


WDBG_API( ext )
/*++

Description:

	Display device extension.

Arguments:

	Address of device extension to display.  If none specified, defaults to the first
	device extension found.

--*/
{
    ULONG i;
	ULONG cbRead;
	ULONG_PTR QIn;
	ULONG_PTR QOut;
    ULONG_PTR Head;
    ULONG_PTR Flink;
	ULONG_PTR pExt;
	BOOL Success;
	DXB_DEVICE_EXTENSION Ext;
    int Result = 0;

   if( args[0] == 0 )
   {
	  pExt = GetDeviceExt();
   }
   else
   {
      Result = sscanf( args, "%lx", &pExt );
      if ((Result == 0) || (Result == EOF))
      {
          WdbgPrint( "Invalid parameter: Device Extension\n");
          return;
      }
   }

   if (!pExt)
   {
      WdbgPrint( "Invalid parameter: Device Extension\n");
      return;
   }
    
   WdbgPrint( "ext: pExt 0x%p\n", pExt );
   Success = WdbgReadMemory( pExt, &Ext, sizeof Ext, &cbRead );
   if( !Success )
   {
      WdbgPrint( "Unable to read Device Extension 0x%p\n", pExt );
      return;
   }

	if ( cbRead != sizeof Ext )
	{
      WdbgPrint( "Unable to read entire Device Extension, size 0x%x, cbRead 0x%x\n", sizeof Ext, cbRead );
      return;
	}

   WdbgPrint( "Device Extension 0x%p\n", pExt );


	if ( Ext.Type != DXB_TYPE_DEVICE_EXT )
	{
		WdbgPrint( "0x%p not a valid Dxb device extension!\n", pExt );
		return ;
	} 

	WdbgPrint( "   Name: %S\n", Ext.NameBuffer );
	
	{
	   WCHAR Option[128];
	   Success = WdbgReadMemory( (ULONG_PTR)Ext.Option.Buffer, Option, Ext.Option.MaximumLength, &cbRead );
	   if( !Success )
	      WdbgPrint( "   Option: ????\n" );
		else
			WdbgPrint( "   Option: %S\n", Option );
	}

	WdbgPrint( "   Lock: 0x%x, Irql 0x%x\n", Ext.DxbLock.Lock, Ext.DxbLock.Irql );

	WdbgPrint( "   Resources:\n" );
	WdbgPrint( "      Memory: 0x%p, Length 0x%x, cbWindow 0x%x\n", Ext.Hardware.Memory.Virtual, Ext.Hardware.Memory.Length, Ext.Hardware.Memory.cbWindow );
	WdbgPrint( "      Io:     0x%p, Length 0x%x\n", Ext.Hardware.Io.Virtual, Ext.Hardware.Io.Length );
	WdbgPrint( "      BusType: 0x%x, BusNumber 0x%x, SlotNumber 0x%x\n", Ext.Hardware.BusType, Ext.Hardware.BusNumber, Ext.Hardware.SlotNumber );
	WdbgPrint( "   DxbBusMode: 0x%x, %s\n", Ext.DxbBusMode, Ext.DxbBusMode == DXB_BUSMODE_SLAVE ? "DXB_BUSMODE_SLAVE" :
	                                Ext.DxbBusMode == DXB_BUSMODE_MASTER_WINDOWED ? "DXB_BUSMODE_MASTER_WINDOWED" :
	                                Ext.DxbBusMode == DXB_BUSMODE_MASTER_FLAT ? "DXB_BUSMODE_MASTER_FLAT" : "Invalid!" );

	//
	// Command Queue
	//
	WdbgPrint( "   DxbCmdQ:\n" );
	WdbgPrint( "      cbTotal 0x%x, QOffset 0x%x, cbPage 0x%x\n", Ext.DxbCmdQ.cbTotal, Ext.DxbCmdQ.QOffset, Ext.DxbCmdQ.cbPage );

	if ( Ext.DxbBusMode == DXB_BUSMODE_SLAVE )
	{
		WdbgPrint( "      Slave.Offset: 0x%x\n", Ext.DxbCmdQ.Mode.Slave.Offset );

		WdbgPrint( "      QInOffset:  0x%x\n", Ext.DxbCmdQ.Descriptor.QInOffset );
		WdbgPrint( "      QOutOffset: 0x%x\n", Ext.DxbCmdQ.Descriptor.QOutOffset );
	}
	else
	{
		WdbgPrint( "      Master.nPages: 0x%x\n", Ext.DxbCmdQ.Mode.Master.nPages );
		WdbgPrint( "         Pages: " );
		for ( i = 0; i < Ext.DxbCmdQ.Mode.Master.nPages; i++ )
			WdbgPrint( "0x%p ", Ext.DxbCmdQ.Mode.Master.VirtualPages[i] );
		WdbgPrint( "\n" );

		{
			Success = WdbgReadMemory( (ULONG_PTR)Ext.DxbCmdQ.Descriptor.pQIn, &QIn, sizeof QIn, &cbRead );
			WdbgPrint( "      pQIn  0x%08x, QIn  0x%08x\n", Ext.DxbCmdQ.Descriptor.pQIn, QIn );
		}
		{
			Success = WdbgReadMemory( (ULONG_PTR)Ext.DxbCmdQ.Descriptor.pQOut, &QOut, sizeof QOut, &cbRead );
			WdbgPrint( "      pQOut 0x%08x, QOut 0x%08x\n", Ext.DxbCmdQ.Descriptor.pQOut, QOut );
		}
	}


	//
	// Event Queue
	//
	WdbgPrint( "   DxbEventQ:\n" );
	WdbgPrint( "      cbTotal: 0x%x\n", Ext.DxbEventQ.cbTotal );
	WdbgPrint( "      QOffset: 0x%x\n", Ext.DxbEventQ.QOffset );
	WdbgPrint( "      cbPage: 0x%x\n", Ext.DxbEventQ.cbPage );

	if ( Ext.DxbBusMode == DXB_BUSMODE_SLAVE )
	{
		WdbgPrint( "      Slave.Offset: 0x%x\n", Ext.DxbEventQ.Mode.Slave.Offset );
		WdbgPrint( "      pQIn:  0x%p\n", Ext.DxbEventQ.Descriptor.pQIn );
		WdbgPrint( "      pQOut: 0x%p\n", Ext.DxbEventQ.Descriptor.pQOut );
	}
	else
	{
		WdbgPrint( "      Master.nPages: 0x%x\n", Ext.DxbEventQ.Mode.Master.nPages );
		WdbgPrint( "         Pages: " );
		for ( i = 0; i < Ext.DxbEventQ.Mode.Master.nPages; i++ )
			WdbgPrint( "0x%p ", Ext.DxbEventQ.Mode.Master.VirtualPages[i] );
		WdbgPrint( "\n" );

		{
			Success = WdbgReadMemory( (ULONG_PTR)Ext.DxbEventQ.Descriptor.pQIn, &QIn, sizeof QIn, &cbRead );
			WdbgPrint( "      pQIn  0x%08x, QIn  0x%08x\n", Ext.DxbEventQ.Descriptor.pQIn, QIn );
		}
		{
			Success = WdbgReadMemory( (ULONG_PTR)Ext.DxbEventQ.Descriptor.pQOut, &QOut, sizeof QOut, &cbRead );
			WdbgPrint( "      pQOut 0x%08x, QOut 0x%08x\n", Ext.DxbEventQ.Descriptor.pQOut, QOut );
		}
	}

	WdbgPrint( "   HlcGlobalHost: Physical 0x%08x%08x, Virtual 0x%08x, cb 0x%x\n", Ext.HlcGlobalHost.Physical.HighPart, Ext.HlcGlobalHost.Physical.LowPart, Ext.HlcGlobalHost.Virtual, Ext.HlcQueue.Length );
	WdbgPrint( "   HlcQueue:      Physical 0x%08x%08x, Virtual 0x%08x, cb 0x%x\n", Ext.HlcQueue.Physical.HighPart, Ext.HlcQueue.Physical.LowPart, Ext.HlcQueue.Virtual, Ext.HlcQueue.Length );

	//
	// Work.Command 
	//
	{
		Head = pExt + FIELD_OFFSET( DXB_DEVICE_EXTENSION, Work.Command.Head );
		Flink = Head + FIELD_OFFSET( LIST_ENTRY, Flink );
		WdbgPrint( "   Work.Command streams list:\n" );

		WdbgPrint( "      " );
		do
		{
			if ( !WdbgReadMemory( Flink, &Flink, sizeof( Flink ), &cbRead ) )
				break;
			WdbgPrint( "0x%p ", CONTAINING_RECORD( Flink, DXB_STREAM_OBJECT, Entries.Command.Entry.Flink ) );
		}
		while ( Flink != Head );

		WdbgPrint( "\n" );
	}

	//
	// Work.Credit 
	//
	{
		Head = pExt + FIELD_OFFSET( DXB_DEVICE_EXTENSION, Work.Credit.Head );
		Flink = Head + FIELD_OFFSET( LIST_ENTRY, Flink );
		WdbgPrint( "   Work.Credit streams list:\n" );

		WdbgPrint( "      " );
		while ( Flink != Head )
		{
			if ( !WdbgReadMemory( Flink, &Flink, sizeof( Flink ), &cbRead ) )
				break;
			WdbgPrint( "      0x%p ", CONTAINING_RECORD( Flink, DXB_STREAM_OBJECT, Entries.Credit.Entry.Flink ) );
		}
		WdbgPrint( "\n" );
	}

	WdbgPrint( "   CreditDefault:\n" );
	WdbgPrint( "      nCreditsMax: 0x%x\n", Ext.CreditDefault.nCreditsMax );
	WdbgPrint( "      nCredits:    0x%x\n", Ext.CreditDefault.nCredits );
	WdbgPrint( "      nRefundable: 0x%x\n", Ext.CreditDefault.nRefundable );
	WdbgPrint( "      FCost:       0x%x\n", Ext.CreditDefault.FCost );
	WdbgPrint( "      Max:         0x%x\n", Ext.CreditDefault.Max );

	WdbgPrint( "   TotalDpcTime 0x%x, Count 0x%x\n", Ext.TotalDpcTime.QuadPart, Ext.DpcCount );
	WdbgPrint( "   Channels array at address 0x%p\n", pExt + FIELD_OFFSET( DXB_DEVICE_EXTENSION, Channels ) );
	return ;
}


WDBG_API( streamlist )
/*++

Description:

	Display list of stream objects on this device extension.  Or, if no device ext addr
	is	specified, default to first device ext.

--*/
{
	ULONG cbRead;
	ULONG_PTR pExt, pChannels;
	ULONG i;
	BOOL Success;
	DXB_DEVICE_EXTENSION Ext;
	DXB_CHANNEL aChannels[DXB_MAX_CHANNELS];
    int Result = 0;

   if( args[0] == 0 )
   {
		pExt = GetDeviceExt();
   }
   else
   {
	  Result = sscanf( args, "%lx", &pExt );
      if ((Result == 0) || (Result == EOF))
      {
          WdbgPrint( "Invalid parameter: Device Extension\n");
          return;
      }
   }

   if (!pExt)
   {
      WdbgPrint( "Invalid parameter: Device Extension\n");
      return;
   }

   Success = WdbgReadMemory( pExt, &Ext, sizeof Ext, &cbRead );
   if( !Success || Ext.Type != DXB_TYPE_DEVICE_EXT || cbRead != sizeof Ext )
   {
      WdbgPrint( "Unable to read Device Extension 0x%p, Success 0x%x, Type 0x%x, cbRead 0x%x\n", pExt, Success, Ext.Type, cbRead );
      return;
   }

	pChannels = pExt + FIELD_OFFSET(DXB_DEVICE_EXTENSION, Channels);
	WdbgPrint( "pExt 0x%p, Channels array 0x%p\n", pExt, pChannels );

	Success = WdbgReadMemory( pChannels, aChannels, sizeof aChannels, &cbRead );
   if( !Success || cbRead != sizeof aChannels )
   {
      WdbgPrint( "Unable to read Channels array, Success 0x%x, cbRead 0x%x\n", Success, cbRead );
      return;
   }

   WdbgPrint( "%S Streams:\n", Ext.NameBuffer );
//	WdbgPrint( "   sizeof descriptor 0x%x\n", sizeof Ext.Channels[0] );
//	WdbgPrint( "   Channels array at 0x%p\n", pExt + FIELD_OFFSET( DXB_DEVICE_EXTENSION, Channels ) );
	for ( i = 0; i < DXB_MAX_CHANNELS; i++ )
	{
		if ( aChannels[i].pStream != NULL && aChannels[i].State != STREAM_STATE_INACTIVE )
		WdbgPrint( "   Channel %03d, Stream 0x%08x, State 0x%x\n", i, aChannels[i].pStream, aChannels[i].State );
	}
}


WDBG_API( stream )
/*++

Description:

	Display stream object

--*/
{
	ULONG cbRead;
	ULONG_PTR StreamAddr;
	BOOL Success;
	DXB_STREAM_OBJECT Stream;
	PDXB_STREAM_OBJECT pStream;
    int Result = 0;

	if ( args[0] == 0 )
	{
		WdbgPrint( "Missing required address argument!\n" );
		return ;
	}
	
	Result = sscanf( args, "%lx", &StreamAddr );
    if ((Result == 0) || (Result == EOF))
    {
        WdbgPrint( "Invalid parameter: Stream Address\n");
        return;
    }

    if (!StreamAddr)
    {
       WdbgPrint( "Invalid parameter: Stream Address\n");
       return;
    }
	Success = WdbgReadMemory( StreamAddr, &Stream, sizeof Stream, &cbRead );
	if ( !Success )
	{
		WdbgPrint( "Unable to read Stream 0x%p\n", StreamAddr );
		return ;
	}

	if ( Stream.Type != DXB_TYPE_STREAM_OBJECT )
	{
		WdbgPrint( "Invalid stream object, type 0x%x\n", Stream.Type );
		return ;
	}

	pStream = (PVOID)StreamAddr;

	WdbgPrint( "Stream object 0x%p\n", StreamAddr );

	WdbgPrint( "   Channel: 0x%x\n", Stream.Channel );
	WdbgPrint( "   pExt: 0x%p\n", Stream.pExt );
	WdbgPrint( "   Control\n" );
	WdbgPrint( "      OpenIrp:  0x%p\n", Stream.Control.Open.pIrp );
	WdbgPrint( "      CloseIrp: 0x%p\n", Stream.Control.Close.pIrp );
	WdbgPrint( "      IoctlIrp: 0x%p\n", Stream.Control.Ioctl.pIrp );
	WdbgPrint( "      LinkIrp:  0x%p\n", Stream.Control.Link.pIrp );
	WdbgPrint( "      LinkedAbove: 0x%x\n", Stream.Control.LinkedAbove );
	WdbgPrint( "      LinkedBelow: 0x%x\n", Stream.Control.LinkedUnder );
	
	WdbgPrint( "   Data\n" );
	WdbgPrint( "      &PutQ[0]: 0x%p\n", &pStream->Data.PutQ[0] );
	WdbgPrint( "      &PutQ[1]: 0x%p\n", &pStream->Data.PutQ[1] );
	WdbgPrint( "      &GetQ[0]: 0x%p\n", &pStream->Data.GetQ[0] );
	WdbgPrint( "      &GetQ[1]: 0x%p\n", &pStream->Data.GetQ[1] );
	WdbgPrint( "      &IncomingQ[0]: 0x%p\n", &pStream->Data.IncomingQ[0] );
	WdbgPrint( "      &IncomingQ[1]: 0x%p\n", &pStream->Data.IncomingQ[1] );

	WdbgPrint( "   Entries.Command: Flink 0x%p, Blink 0x%p, fInList 0x%x\n", Stream.Entries.Command.Entry.Flink, Stream.Entries.Command.Entry.Blink, Stream.Entries.Command.fInList );
	WdbgPrint( "   Entries.Credit:  Flink 0x%p, Blink 0x%p, fInList 0x%x\n", Stream.Entries.Credit.Entry.Flink, Stream.Entries.Credit.Entry.Blink, Stream.Entries.Credit.fInList );

	
	WdbgPrint( "   Write Credits:\n" );
	WdbgPrint( "      nCreditsMax: 0x%x\n", Stream.Write.nCreditsMax );
	WdbgPrint( "      nCredits:    0x%x\n", Stream.Write.nCredits );
	WdbgPrint( "      nRefundable: 0x%x\n", Stream.Write.nRefundable );
	WdbgPrint( "      FCost:       0x%x\n", Stream.Write.FCost );
	WdbgPrint( "      Max:         0x%x\n", Stream.Write.Max );

	WdbgPrint( "   Read Credits\n" );
	WdbgPrint( "      nCreditsMax: 0x%x\n", Stream.Read.nCreditsMax );
	WdbgPrint( "      nCredits:    0x%x\n", Stream.Read.nCredits );
	WdbgPrint( "      nRefundable: 0x%x\n", Stream.Read.nRefundable );
	WdbgPrint( "      FCost:       0x%x\n", Stream.Read.FCost );
	WdbgPrint( "      Max:         0x%x\n", Stream.Read.Max );

	WdbgPrint( "   Stats:\n" );
	WdbgPrint( "      nIoctls      0x%x\n", Stream.Stats.nIoctls );
	WdbgPrint( "      nLinks       0x%x\n", Stream.Stats.nLinks );
	WdbgPrint( "      nPutRequests 0x%x\n", Stream.Stats.nPutRequests );
	WdbgPrint( "      nPuts[0]     0x%x\n", Stream.Stats.nPuts[0] );
	WdbgPrint( "      nPuts[1]     0x%x\n", Stream.Stats.nPuts[1] );
	WdbgPrint( "      nGets[0]     0x%x\n", Stream.Stats.nGets[0] );
	WdbgPrint( "      nGets[1]     0x%x\n", Stream.Stats.nGets[1] );
	WdbgPrint( "      nIncoming[0] 0x%x\n", Stream.Stats.nIncoming[0] );
	WdbgPrint( "      nIncoming[1] 0x%x\n", Stream.Stats.nIncoming[1] );
	return ;
}


WDBG_API( densepanic )
/*++

Description:

	Display dense modem adapter panic structure

--*/
{
	
}
