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

   Peter Donovan
   
   Based on kddxb by Michael Zarns

--*/

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

#include "header.h"

#include "kdlib.h"

#ifndef STATIC
#define STATIC static
#endif

#define BOOL ULONG


///////////////////////////////////////////////////////////////////////////////

char *HelpStrings[] =
{
   "cardlist         - display list of card device extensions",
   "card <addr>      - display card device extension",
   "port <addr>      - display port extenstion object",
   "trace <addr>     - display trace buffer as ASCII chars, useful for displaying",
   "                   DigiAsyn!MemPrintBuffer circular buffer when logging debug to a file.",
};

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

// Keep trace of last trace buffer displayed
ULONG          gpTraceBuff;

///////////////////////////////////////////////////////////////////////////////
//
// Internal 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 
GetDeviceExt( VOID )
{
   BOOL Success;
   ULONG cbRead;
   ULONG pHead;
   ULONG Flink, pExt = 0;
   DEVICE_OBJECT *pCard;
   PCARD_EXT   pCardExt;

   if (pCard = (PDEVICE_OBJECT)WdbgGetExpression( "&DigiAsyn!AsynCards" ))
   {
      pCardExt = pCard->DeviceExtension;
   }
   
   return 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( cardlist )
/*++

Description:

        Display list of Asyn Card device extensions.

--*/
{
   BOOL Success;
   ULONG cbRead;
   ULONG pHead, pEntry;
   ULONG          pCard;
   DEVICE_OBJECT  Card;
   ULONG          pCardExt;
   CARD_EXT       CardExt;
   
   WdbgPrint( "List of Asyn Cards\n");
   if (pCard = WdbgGetExpression( "&DigiAsyn!AsynCards"))
   {
      while (pCard)
      {
         WdbgPrint( "   pCard: 0x%x ", pCard);
         Success = WdbgReadMemory( pCard, &Card, sizeof(Card), &cbRead );
         if( !Success || cbRead != sizeof(Card) )
         {
            WdbgPrint( "Unable to read CARD DEVICE_OBJECT\n" );
            goto Exit;
         }
         pCardExt = (ULONG)Card.DeviceExtension;

         WdbgPrint( "   pCardEx: 0x%x\n", pCardExt);

         Success = WdbgReadMemory( pCardExt, &CardExt, sizeof(CardExt), &cbRead );
         if( !Success || cbRead != sizeof(CardExt) )
         {
            WdbgPrint( "Unable to read CARD EXTENSION \n" );
            goto Exit;
         }
         pCard = (ULONG)CardExt.Next;
      }
   }
Exit:
   return ;
}


WDBG_API( card )
/*++

Description:

        Display card device extension.

Arguments:

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

--*/
{
   ULONG cbRead, pExt;
   BOOL Success;
   ULONG          pCard;
   DEVICE_OBJECT  Card;
   ULONG          pCardExt;
   CARD_EXT       CardExt;
   ULONG          Flink;
   ULONG          Count;

   if( args[0] == 0 )
   {
      if (pCard = WdbgGetExpression( "&DigiAsyn!AsynCards"))
      {
         Success = WdbgReadMemory( pCard, &Card, sizeof(Card), &cbRead );
         if( !Success || cbRead != sizeof(Card) )
         {
            WdbgPrint( "Unable to read CARD DEVICE_OBJECT pCard=0x%x cbRead=0x%x\n",
                  pCard, cbRead );
            goto Exit;
         }
         pCardExt = (ULONG)Card.DeviceExtension;
      }
   }
   else
      sscanf( args, "%lx", &pCardExt );

   WdbgPrint( "CardExt: 0x%x\n", pCardExt );
   Success = WdbgReadMemory( pCardExt, &CardExt, sizeof(CardExt), &cbRead );
   if( !Success )
   {
      WdbgPrint( "Unable to read Device Extension 0x%x\n", pCardExt );
      return;
   }

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

   WdbgPrint( "   CardLock: 0x%x\n", CardExt.CardLock);
   WdbgPrint( "   State: 0x%x\n", CardExt.State);
   WdbgPrint( "   Ports: %d\n", CardExt.PhysicalPorts);
   WdbgPrint( "   Number of Active Ports: %d\n", CardExt.NbrActivePorts);
   WdbgPrint( "   Active PortExt List: \n" );
   Flink = (ULONG)CardExt.ActivePorts.Flink;
   Count = 0;
   while (pCardExt +FIELD_OFFSET(CARD_EXT, ActivePorts) != Flink)
   {
      ULONG    pPortExt;
      PORT_EXT PortExt;
      pPortExt = (ULONG)CONTAINING_RECORD( Flink, PORT_EXT, ActivePort);
      WdbgPrint( "    pPortExt: 0x%x", pPortExt );
      Success = WdbgReadMemory( pPortExt, &PortExt, sizeof(PortExt), &cbRead );
      if( !Success )
      {
         WdbgPrint( "Unable to read Port Extension 0x%x\n", pPortExt );
         return;
      }
      if (Count++ > 100)
      {
         WdbgPrint( "Maximum count=%d exceeded\n", Count);
         break;
      }
      Flink = (ULONG)PortExt.ActivePort.Flink;
   }

Exit:        
   return ;
}


WDBG_API( port )
/*++

Description:

        Display port device extension.

Arguments:

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

--*/
{
   ULONG cbRead, pExt;
   BOOL Success;
   ULONG          pCard;
   DEVICE_OBJECT  Card;
   ULONG          pCardExt;
   CARD_EXT       CardExt;
   ULONG          pPortExt;
   PORT_EXT       PortExt;
   ULONG          Flink;
   ULONG          Count;

   if( args[0] == 0 )
   {
      if (pCard = WdbgGetExpression( "&DigiAsyn!AsynCards"))
      {
         Success = WdbgReadMemory( pCard, &Card, sizeof(Card), &cbRead );
         if( !Success || cbRead != sizeof(Card) )
         {
            WdbgPrint( "Unable to read CARD DEVICE_OBJECT pCard=0x%x cbRead=0x%x\n",
                  pCard, cbRead );
            goto Exit;
         }
         pCardExt = (ULONG)Card.DeviceExtension;

         Success = WdbgReadMemory( pCardExt, &CardExt, sizeof(CardExt), &cbRead );
         if( !Success )
         {
            WdbgPrint( "Unable to read Device Extension 0x%x\n", pCardExt );
            return;
         }
      
         if ( cbRead != sizeof(CardExt) )
         {
            WdbgPrint( "Unable to read entire Device Extension, size 0x%x, cbRead 0x%x\n", sizeof(CardExt), cbRead );
            return;
         }
         pPortExt = (ULONG)CardExt.PortList.Flink;
      }
   }
   else
      sscanf( args, "%lx", &pPortExt );

   WdbgPrint( "PortExt: 0x%x\n", pPortExt );

   Success = WdbgReadMemory( pPortExt, &PortExt, sizeof(PortExt), &cbRead );
   if( !Success )
   {
      WdbgPrint( "Unable to read Device Extension 0x%x\n", pPortExt );
      return;
   }

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

   WdbgPrint( "   PortLock: 0x%x\n", PortExt.PortLock);
   WdbgPrint( "   Busy: 0x%x\n", PortExt.PortBusy);
   WdbgPrint( "   ComNumber: %d\n", PortExt.ComNumber);
   WdbgPrint( "   State: 0x%x\n", PortExt.State);
   WdbgPrint( "   OpenMask: 0x%x\n", PortExt.OpenStateMask);
   WdbgPrint( "   DsPortHandle: 0x%x\n", PortExt.DsPortHandle);
   WdbgPrint( "   SyncOut: 0x%x\n", PortExt.SyncOutSequence);
   WdbgPrint( "   SyncIn : 0x%x\n", PortExt.SyncInSequence);
   WdbgPrint( "   SDXB \n");
   WdbgPrint( "      Cflags:0x%x  Iflags:0x%x  Oflags:0x%x  Xflags:0x%x\n", 
            PortExt.SdxbValues.Cflags, 
            PortExt.SdxbValues.Iflags, 
            PortExt.SdxbValues.Oflags, 
            PortExt.SdxbValues.Xflags);

   WdbgPrint( "      Mflow:0x%x  Mctrl:0x%x  Mstat:0x%x  Mint:0x%x\n", 
            PortExt.SdxbValues.Mflow,
            PortExt.SdxbValues.Mctrl, 
            PortExt.SdxbValues.Mstat, 
            PortExt.SdxbValues.Mint);
   WdbgPrint( "   TxBytesPending:0x%x  \n", 
            PortExt.TxBytesPending);

   WdbgPrint( "   WaitMask:0x%x  \n", 
            PortExt.WaitMask);
   
   WdbgPrint( "   RxBufCount:0x%x  \n", 
            PortExt.RxBufCount);

   //
   // RxQ
   //
   WdbgPrint( "   RxQ:");
   Flink = (ULONG)PortExt.RxQ.Flink;
   Count = 0;
   while (pPortExt +FIELD_OFFSET(PORT_EXT, RxQ) != Flink)
   {
      ULONG    pRxBuf;
      RX_BUF  RxBuf;

      if (Count == 1)
      {
         WdbgPrint( "\n");
      }
      pRxBuf = (ULONG)CONTAINING_RECORD( Flink, RX_BUF, ListEntry);

      WdbgPrint( "    pRxBuf: 0x%x", pRxBuf );
      Success = WdbgReadMemory( pRxBuf, &RxBuf, sizeof(RxBuf), &cbRead );
      if( !Success )
      {
         WdbgPrint( "Unable to read RxBuf 0x%x\n", pRxBuf );
         break;
      }
      WdbgPrint( "  Size=0x%x Len=0x%x Start=0x%x\n", 
                  RxBuf.Size, RxBuf.Len, RxBuf.Start);
      if (Count++ > 100)
      {
         WdbgPrint( "Maximum count=%d exceeded\n", Count);
         break;
      }

      Flink = (ULONG)RxBuf.ListEntry.Flink;
   }
   if (Count == 0)
   {
      WdbgPrint( " EMPTY\n");
   }

   //
   // DispatchQ
   //
   WdbgPrint( "   DispatchQ (Irps):");
   Flink = (ULONG)PortExt.DispatchQ.Flink;
   Count = 0;
   while (pPortExt +FIELD_OFFSET(PORT_EXT, DispatchQ) != Flink)
   {
      ULONG    pIrp;
      IRP      Irp;

      if (Count == 1)
      {
         WdbgPrint( "\n");
      }
      pIrp = (ULONG)CONTAINING_RECORD( Flink, IRP, Tail.Overlay.ListEntry);

      WdbgPrint( "    pIrp: 0x%x\n", pIrp );
      Success = WdbgReadMemory( pIrp, &Irp, sizeof(Irp), &cbRead );
      if( !Success )
      {
         WdbgPrint( "Unable to read Irp 0x%x\n", pIrp );
         break;
      }
      if (Count++ > 100)
      {
         WdbgPrint( "Maximum count=%d exceeded\n", Count);
         break;
      }

      Flink = (ULONG)Irp.Tail.Overlay.ListEntry.Flink;
   }
   if (Count == 0)
   {
      WdbgPrint( " EMPTY\n");
   }

   //
   // SdxbRspQ
   //
   WdbgPrint( "   SdxbRspQ:");
   Flink = (ULONG)PortExt.SdxbRspQ.Flink;
   Count = 0;
   while (pPortExt +FIELD_OFFSET(PORT_EXT, SdxbRspQ) != Flink)
   {
      ULONG       pGetEvent;
      GET_MSG_EV  GetEvent;

      if (Count == 1)
      {
         WdbgPrint( "\n");
      }
      pGetEvent = (ULONG)CONTAINING_RECORD( Flink, GET_MSG_EV, ListEntry);

      WdbgPrint( "    pGetEvent: 0x%x ", pGetEvent );
      Success = WdbgReadMemory( pGetEvent, &GetEvent, sizeof(GetEvent), &cbRead );
      if( !Success )
      {
         WdbgPrint( "Unable to read GetEvent 0x%x\n", pGetEvent );
         break;
      }
      WdbgPrint( "  DataLen=0x%x pData=0x%x RspLen=0x%x\n", 
                  GetEvent.DataLength, GetEvent.PData, GetEvent.RspLength);
      if (Count++ > 100)
      {
         WdbgPrint( "Maximum count=%d exceeded\n", Count);
         break;
      }

      Flink = (ULONG)GetEvent.ListEntry.Flink;
   }
   if (Count == 0)
   {
      WdbgPrint( " EMPTY\n");
   }

   //
   // CancelledAndTimedoutIrpQ
   //
   WdbgPrint( "   Cancelled/Timedout Queue (Irps):");
   Flink = (ULONG)PortExt.CancelledAndTimedoutIrpQ.Flink;
   Count = 0;
   while (pPortExt +FIELD_OFFSET(PORT_EXT, CancelledAndTimedoutIrpQ) != Flink)
   {
      ULONG    pIrp;
      IRP      Irp;

      if (Count == 1)
      {
         WdbgPrint( "\n");
      }
      pIrp = (ULONG)CONTAINING_RECORD( Flink, IRP, Tail.Overlay.ListEntry);

      WdbgPrint( "    pIrp: 0x%x\n", pIrp );
      Success = WdbgReadMemory( pIrp, &Irp, sizeof(Irp), &cbRead );
      if( !Success )
      {
         WdbgPrint( "Unable to read Irp 0x%x\n", pIrp );
         break;
      }
      if (Count++ > 100)
      {
         WdbgPrint( "Maximum count=%d exceeded\n", Count);
         break;
      }

      Flink = (ULONG)Irp.Tail.Overlay.ListEntry.Flink;
   }
   if (Count == 0)
   {
      WdbgPrint( " EMPTY\n");
   }

   //
   // CompleteIrpQ
   //
   WdbgPrint( "   Completed Queue (Irps):");
   Flink = (ULONG)PortExt.CompleteIrpQ.Flink;
   Count = 0;
   while (pPortExt +FIELD_OFFSET(PORT_EXT, CompleteIrpQ) != Flink)
   {
      ULONG    pIrp;
      IRP      Irp;

      if (Count == 1)
      {
         WdbgPrint( "\n");
      }
      pIrp = (ULONG)CONTAINING_RECORD( Flink, IRP, Tail.Overlay.ListEntry);

      WdbgPrint( "    pIrp: 0x%x\n", pIrp );
      Success = WdbgReadMemory( pIrp, &Irp, sizeof(Irp), &cbRead );
      if( !Success )
      {
         WdbgPrint( "Unable to read Irp 0x%x\n", pIrp );
         break;
      }
      if (Count++ > 100)
      {
         WdbgPrint( "Maximum count=%d exceeded\n", Count);
         break;
      }

      Flink = (ULONG)Irp.Tail.Overlay.ListEntry.Flink;
   }
   if (Count == 0)
   {
      WdbgPrint( " EMPTY\n");
   }

   WdbgPrint( "   PutQ:");
   Flink = (ULONG)PortExt.PutQ.Flink;
   Count = 0;
   while (pPortExt +FIELD_OFFSET(PORT_EXT, PutQ) != Flink)
   {
      ULONG    pPutEvent;
      PUT_MSG_EV  PutEvent;
      pPutEvent = (ULONG)CONTAINING_RECORD( Flink, PUT_MSG_EV, ListEntry);
      WdbgPrint( "\n    pPutEvent: 0x%x", pPutEvent );
      Success = WdbgReadMemory( pPutEvent, &PutEvent, sizeof(PutEvent), &cbRead );
      if( !Success )
      {
         WdbgPrint( "Unable to read PutEvent 0x%x\n", pPutEvent );
         break;
      }
      if (Count++ > 100)
      {
         WdbgPrint( "Maximum count=%d exceeded\n", Count);
         break;
      }
      Flink = (ULONG)PutEvent.ListEntry.Flink;
   }
   if (Count == 0)
      WdbgPrint( " EMPTY\n");


Exit:        
   return ;
}

WDBG_API( trace )
/*++

Description:

        Display the trace buffer

Arguments:

        Address of trace buffer to display as ASCII test.  If none specified, 
        defaults to continue where the previous trace stopped.

--*/
{
   ULONG          cbRead;
   BOOL           Success;
   ULONG          pTraceBuff;
   CHAR           TraceBuff[0x400];
   ULONG          Count;

   if( args[0] == 0 )
   {
      pTraceBuff = gpTraceBuff;
   }
   else
      sscanf( args, "%lx", &pTraceBuff );

   WdbgPrint( "Tracing Buffer:0x%x\n", pTraceBuff );
   Success = WdbgReadMemory( pTraceBuff, &TraceBuff, sizeof(TraceBuff), &cbRead );
   if ( !Success || ( cbRead != sizeof(TraceBuff) ) )
   {
      WdbgPrint( "Unable to read trace buffer 0x%x\n", pTraceBuff );
      return;
   }

   for (Count=0; Count < sizeof(TraceBuff); Count++)
   {
      if ( (TraceBuff[Count] == 0x0d) || (TraceBuff[Count] == 0x0a) )
         WdbgPrint( "\n");
      else if ( (TraceBuff[Count] >= 0x00) && (TraceBuff[Count] < 0x20) )
         WdbgPrint( ".");
      else
         WdbgPrint( "%c", TraceBuff[Count]);
   }
   WdbgPrint( "\n");
   
   gpTraceBuff = pTraceBuff + sizeof(TraceBuff);
   WdbgPrint( "Last Trace at:0x%x\n", gpTraceBuff );
   return ;
}

