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

   kdasyn.c

Abstract:

   Windbg extension dll for DigiAsyn.sys.  
   
   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 "digiasyn.h"

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

#ifndef STATIC
#define STATIC static
#endif

#define BOOL ULONG


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

char *HelpStrings[] =
{
   "portlist [addr] - display list of port device extensions",
   "port <addr>     - display port device extension"
};

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


///////////////////////////////////////////////////////////////////////////////
//
// Helper Routines
//

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

	p = malloc( strlen(String) + 2 );  // 1 address-of char, 1 terminating null: 2 xtra chars
	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
GetListFlink( ULONG pListEntry )
{
	ULONG 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 
GetFirstDeviceExt( VOID )
{
   ULONG pHead = 0;
   ULONG Flink = 0;
   PASYN_PORT_EXT pExt = NULL;

	pHead = WdbgGetExpression( "&DigiAsyn!gDeviceExtList" );
	if (pHead)
		Flink = GetListFlink(pHead);
	if (Flink)
		pExt = CONTAINING_RECORD( Flink, ASYN_PORT_EXT, ListEntry );

   return (ULONG)pExt;
}


///////////////////////////////////////////////////////////////////////////////
//
// Asyn Specific Exported Routines
//

WDBG_API( help )
/*++

Routine Description:

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

   Called as:

       !kdasyn.help

--*/
{
   int i;

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



WDBG_API( portlist )
/*++

Description:

	Display list of port device extensions using asyn global ext list.  If argument
	specified, it is assumed to be the address of the gDeviceExtList list.

--*/
{
	BOOL Success = FALSE;
	ULONG cbRead;
	ULONG pHead = 0, pEntry = 0;
   
	// Find the head of the list
	if ( args[0] )
		sscanf(args, "%lx", &pHead);
	else
	   pHead = WdbgGetExpression("&DigiAsyn!gDeviceExtList");

	// Get the first entry in the list
	if (pHead)
		pEntry = GetListFlink(pHead);

	// Ok, walk the list
	if (pEntry)
	{
	   WdbgPrint("   PortExt,    iSdxbPort,  DsHandle,   PortName\n");
		while(pEntry && pEntry != pHead)
		{
			// Read the port device extension, so we can display a few identifying fields
			ULONG pExt = (ULONG)CONTAINING_RECORD(pEntry, ASYN_PORT_EXT, ListEntry);
			ASYN_PORT_EXT Ext = {0};
		   Success = WdbgReadMemory(pExt, &Ext, sizeof Ext, &cbRead);
		   if(Success && cbRead == sizeof Ext)
		   {
		   	// Got the extension.  Get the PortName and display.
					UCHAR Name[256] = {0};
				   (void)WdbgReadMemory((ULONG)Ext.PortName.Buffer, &Name, Ext.PortName.Length, &cbRead);
					WdbgPrint("   0x%08x, 0x%08x, 0x%08x, %S\n", pExt, Ext.iSdxbPort, Ext.DsHandle, Name);
		   }
			else
		   {
				// failed to get extension.  Skip display of additional fields, only display
				// port ext address.
				WdbgPrint("   0x%08x\n", pExt);
		   }

			pEntry = GetListFlink(pEntry);
		}
	}
	else
		WdbgPrint( "   <Empty>\n" );
}


STATIC VOID
DumpRxEngine( 
	ULONG pRxEngine, 
	PCHAR pPrefix )
/*++

Description:

	Display ASYN_RXENGINE struct.

Parameters:

	pRxEngine
		Address of engine to display

	pPrefix
		Prefix for every displayed line.  Typically a bunch of spaces.  

--*/
{
	ULONG cbRead;
	ASYN_RXENGINE RxEngine = {0};
	WdbgPrint( "%sRxEngine 0x%x\n", pPrefix, pRxEngine );

	if ( pRxEngine && WdbgReadMemory(pRxEngine, &RxEngine, sizeof RxEngine, &cbRead) )
	{
		WdbgPrint( "%s   hStream 0x%x\n", pPrefix, RxEngine.hStream );
		WdbgPrint( "%s   nGetMsgMax     0x%x\n", pPrefix, RxEngine.nGetMsgMax );
		WdbgPrint( "%s   nGetMsgPending 0x%x\n", pPrefix, RxEngine.nGetMsgPending );
		WdbgPrint( "%s   fPaused 0x%x\n", pPrefix, RxEngine.fPaused );
		WdbgPrint( "%s   fHalt   0x%x\n", pPrefix, RxEngine.fHalt );
		WdbgPrint( "%s   cbMaxCtrl    0x%x\n", pPrefix, RxEngine.cbMaxCtrl );
		WdbgPrint( "%s   cbMaxData    0x%x\n", pPrefix, RxEngine.cbMaxData );
		WdbgPrint( "%s   cbDataHeader 0x%x\n", pPrefix, RxEngine.cbDataHeader );
		switch ( RxEngine.Priority )
		{
			case DS_PRIORITY_LOW:	WdbgPrint( "%s   Priority DS_PRIORITY_LOW\n", pPrefix );	break;
			case DS_PRIORITY_HIGH:	WdbgPrint( "%s   Priority DS_PRIORITY_HIGH\n", pPrefix );	break;
			default:						WdbgPrint( "%s   Priority Unknown (0x%x)\n", pPrefix, RxEngine.Priority );	break;
		}
		WdbgPrint( "%s   hOwner   0x%x\n", pPrefix, RxEngine.hOwner );
		WdbgPrint( "%s   pOnData  0x%x\n", pPrefix, RxEngine.pOnData );
		WdbgPrint( "%s   pOnCdata 0x%x\n", pPrefix, RxEngine.pOnCdata );
	}
}


WDBG_API( port )
/*++

Description:

	Display port device extension

--*/
{
	ULONG cbRead, portAddr;
	BOOL Success;
	ASYN_PORT_EXT PortExt;
	PASYN_PORT_EXT pPortExt;

	if ( args[0] == 0 )
	{
		WdbgPrint( "Missing required address argument!\n" );
		return ;
	}
	
	sscanf( args, "%lx", &portAddr );
	Success = WdbgReadMemory( portAddr, &PortExt, sizeof PortExt, &cbRead );
	if ( !Success )
	{
		WdbgPrint( "Unable to read PortExt 0x%x\n", portAddr );
		return ;
	}

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

	pPortExt = (PVOID)portAddr;

	WdbgPrint( "PortExt object 0x%x\n", portAddr );

	WdbgPrint( "   SDxb Port index: 0x%x\n", PortExt.iSdxbPort );
	WdbgPrint( "   \n" );

	// PortName
	{
		char Name[256] = {0};
		if ( PortExt.PortName.Buffer && PortExt.PortName.Length && WdbgReadMemory((ULONG)PortExt.PortName.Buffer, &Name, PortExt.PortName.Length, &cbRead) )
			WdbgPrint( "      PortName:  %S\n", Name );
		else
			WdbgPrint( "      PortName: <NULL>\n" );
	}

	// DeviceName
	{
		char Name[256] = {0};
		if ( PortExt.DeviceName.Buffer && PortExt.DeviceName.Length && WdbgReadMemory((ULONG)PortExt.DeviceName.Buffer, &Name, PortExt.DeviceName.Length, &cbRead) )
			WdbgPrint( "      DeviceName:  %S\n", Name );
		else
			WdbgPrint( "      DeviceName: <NULL>\n" );
	}

	switch ( PortExt.State )
	{
		case IdleState:		WdbgPrint( "      State: Idle\n" );			break;
		case OpenInProgressState:	
      							WdbgPrint( "      State: OpenInProgressState\n" );		break;
		case OpenState:		WdbgPrint( "      State: Open\n" );			break;
		case CloseWaitTxDrainState:	
									WdbgPrint( "      State: CloseWaitTxDrainState\n" );		break;
		case CloseInProgressState:
      							WdbgPrint( "      State: CloseInProgressState\n"); break;                           
		case ShutdownState:	WdbgPrint( "      State: Shutdown\n" );	break;
		default:					WdbgPrint( "      State: <Unknown> (0x%x)\n", PortExt.State );	break;
	}

	WdbgPrint( "      DsPortHandle: 0x%x\n", PortExt.DsPortHandle );
	WdbgPrint( "      Pending Create or Close Irp: 0x%x\n", PortExt.pPendingIrp );
	WdbgPrint( "      hRxEngine_HighPriority:  0x%x\n", PortExt.hRxEngine_HighPriority );
	WdbgPrint( "      hRxEngine_LowPriority: 0x%x\n", PortExt.hRxEngine_LowPriority );
	WdbgPrint( "      IsModem: 0x%x\n", PortExt.IsModem );
	WdbgPrint( "      PnpModemDetectHandle: 0x%x\n", PortExt.PnpModemDetectHandle );
	
	WdbgPrint( "   SdxbValues\n" );
	WdbgPrint( "      SyncSeqNum: 0x%x\tSyncSeqAck: 0x%x\n",  
   									PortExt.SdxbValues.SyncSeqNum,
                              PortExt.SdxbValues.SyncSeqAck);
	WdbgPrint( "      PortType: 0x%x\tPortCapabilities: 0x%x\n",  
   									PortExt.SdxbValues.PortType,
                              PortExt.SdxbValues.PortCapabilities);
	WdbgPrint( "      Port Parameters:\n");
   WdbgPrint( "        Speed: 0x%x\n",
   									PortExt.SdxbValues.PortParams.speed);				
   WdbgPrint( "        Cflags:0x%x  Iflags:0x%x  Oflags:0x%x  Xflags:0x%x\n", 
						            PortExt.SdxbValues.PortParams.cflags, 
						            PortExt.SdxbValues.PortParams.iflags, 
						            PortExt.SdxbValues.PortParams.oflags, 
						            PortExt.SdxbValues.PortParams.xflags);
                              
	WdbgPrint( "      Estat: 0x%x\n", PortExt.SdxbValues.Estat );
	WdbgPrint( "      Modem Control:\n");
	WdbgPrint( "        Mout: 0x%x  Mint: 0x%x  Mflow: 0x%x  Mctrl: 0x%x  Mstat: 0x%x\n",  
   									PortExt.SdxbValues.Mout,
   									PortExt.SdxbValues.Mint,
   									PortExt.SdxbValues.Mflow,
   									PortExt.SdxbValues.Mctrl,
                              PortExt.SdxbValues.Mstat);
	WdbgPrint( "      Line Error Counters:\n");
	WdbgPrint( "        Norun: 0x%x  Noflow: 0x%x  Nframe: 0x%x  Nparity: 0x%x  Nbreak: 0x%x\n",  
   									PortExt.SdxbValues.Norun,
   									PortExt.SdxbValues.Noflow,
   									PortExt.SdxbValues.Nframe,
   									PortExt.SdxbValues.Nparity,
                              PortExt.SdxbValues.Nbreak);

	// ASYN_WRITE    
   WdbgPrint( "      Write:\n" );
	WdbgPrint( "         fInProgress 0x%x\n", PortExt.Write.fInProgress );
	WdbgPrint( "         cbMaxPutMsg 0x%x\n", PortExt.Write.cbMaxPutMsg );
	WdbgPrint( "         PendingQueue:\n" );
	if ( IsListEmpty( &PortExt.Write.PendingQueue ) )
		WdbgPrint( "            Empty\n" );
	else
	{
		ULONG pHead = (ULONG)&pPortExt->Write.PendingQueue;
		ULONG Flink = GetListFlink( pHead );
		while ( Flink != pHead )
		{
			WdbgPrint( "            Irp 0x%08x\n", CONTAINING_RECORD( Flink, IRP, Tail.Overlay.ListEntry ) );
			Flink = GetListFlink( Flink );
		}
	}
	WdbgPrint( "         pActiveIrp  0x%x\n", PortExt.Write.pActiveIrp );

	// ASYN_RXENGINEs
	WdbgPrint( "         RxEngine_LowPriority\n" );
	DumpRxEngine( (ULONG)PortExt.hRxEngine_LowPriority, "         " );

	WdbgPrint( "         RxEngine_HighPriority\n" );
	DumpRxEngine( (ULONG)PortExt.hRxEngine_HighPriority, "         " );

	//ASYN_READ
   WdbgPrint( "      Read:\n" );
	WdbgPrint( "         RxQ:\n" );
	if ( IsListEmpty( &PortExt.Read.RxQ ) )
		WdbgPrint( "            Empty\n" );
	else
	{
		ULONG pHead = (ULONG)&pPortExt->Read.RxQ;
		ULONG Flink = GetListFlink( pHead );
		while ( Flink != pHead )
		{
			WdbgPrint( "            RxQ 0x%08x\n", CONTAINING_RECORD( Flink, RX_BUF, ListEntry ) );
			Flink = GetListFlink( Flink );
		}
	}
	WdbgPrint( "         cbRxQ 0x%x\n", PortExt.Read.cbRxQ );
	WdbgPrint( "         cbMaxRxQ 0x%x\n", PortExt.Read.cbMaxRxQ );
	WdbgPrint( "         fRxPaused 0x%x\n", PortExt.Read.fRxPaused );
	WdbgPrint( "         PendingIrps:\n" );
	if ( IsListEmpty( &PortExt.Read.PendingIrps ) )
		WdbgPrint( "            Empty\n" );
	else
	{
		ULONG pHead = (ULONG)&pPortExt->Read.PendingIrps;
		ULONG Flink = GetListFlink( pHead );
		while ( Flink != pHead )
		{
			WdbgPrint( "            Irp 0x%08x\n", CONTAINING_RECORD( Flink, IRP, Tail.Overlay.ListEntry ) );
			Flink = GetListFlink( Flink );
		}
	}
	WdbgPrint( "         cbTotalRead 0x%x\n", PortExt.Read.cbTotalRead );
   WdbgPrint( "         RxCount 	 0x%x\n", PortExt.SdxbValues.PerfStats.ReceivedCount );			
   

   return ;
}

