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

   dxbmemdump.cpp

Abstract:

   Dumps selected memory region from Dxb adapter to binary file.

Author:



--*/

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

#include "dxbqdump.h"

#define DEFAULT_MB_TO_DUMP 5
#define B2MB(n)   ((n) / (1024 * 1024))
#define MB2B(n)   ((n) * (1024 * 1024))

typedef struct _DUMP_PARAMS
{
    HANDLE          hAdapter;
    DWORD           bufSize;
    FILE*           fpOut;
    DXB_QUEUE_TYPE  qType;
    HANDLE          hThread;
} DUMP_PARAMS, *PDUMP_PARAMS;  

char *Argv0;
static char const * g_pszAdapter = NULL;
static char const * g_pszFile = "dgdxbqdmp";
static DWORD g_cbToDump = MB2B(DEFAULT_MB_TO_DUMP);
static DWORD g_Delay = 250;
static DWORD g_BufSize = 0x2000;
static BOOL  fTraceEventQ = TRUE;   // Trace host to adapter queue (EventQ)
static BOOL  fTraceCmdQ   = TRUE;   // Trace adapter to host queue (CmdQ)
HANDLE g_hAbortEvent      = NULL;
HANDLE g_hReadyEvent      = NULL;
BYTE FileWrapMarker[16] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; 
CRITICAL_SECTION    PrintfAvailable;

#define DigiPrintf( String )                         \
{                                                         \
		DigiDbgPrintf String;                               \
}

void Usage(void)
{
   printf("\nUsage: %s adapter [-f:filename] [-a:n] [-s:m] [-?]\n\n", Argv0);
   printf("  adapter\tSpecifies Dxb adapter instance (e.g. DigiDxb3).\n");
   printf("  -f:filename\tFile to dump memory to (default is %s).\n", g_pszFile);
   printf("  -s:m\t\tSize in MB to dump (default is %d).\n", B2MB(g_cbToDump));
   printf("  -?\t\tPrints this message.\n");
}

BOOL ParseCmdLine(int argc, char const * const argv[])
{
   BOOL fSuccess = FALSE;
   int i, Result;

   if (argc < 2) goto Error;
   if (((argv[1][0] == '-') || (argv[1][0] == '/')) && (argv[1][1] == '?')) goto Error;     // check for DxbMemDump -?

   g_pszAdapter = argv[1];

   for (i = 2; i < argc; i++)
   {
      if ((argv[i][0] != '-') && (argv[i][0] != '/')) goto Error;
      if (argv[i][2] != ':') goto Error;

      switch (argv[i][1])
      {
         case 'f': g_pszFile = &argv[i][3]; 
             break;
         case 's': 
             g_cbToDump = MB2B(atoi(&argv[i][3])); 
             break;
         default:  
             goto Error; 
             break;
      }
   }

   fSuccess = TRUE;
   goto Done;

Error:
   Usage();

Done:
   return fSuccess;
}

BOOL WINAPI ControlHandler( DWORD dwControlType )
{
    switch( dwControlType )
    {
        case CTRL_C_EVENT:
            // Tell all threads to exit.
            SetEvent( g_hAbortEvent );
            // Double control-C will force process exit.
            SetConsoleCtrlHandler( ControlHandler, FALSE );
            return TRUE;
        default:
            return FALSE;
    }
}
VOID
DigiDbgPrintf(
	IN CONST char* pszFormat, ... )
{
    __try
    {    
        EnterCriticalSection( &PrintfAvailable );
    	{
    		va_list ArgList;
    		va_start( ArgList, pszFormat );
    		vprintf( pszFormat, ArgList );
    		va_end( ArgList );
        }            
	}
    __finally
    {
        LeaveCriticalSection( &PrintfAvailable );
    }                                    
}    

DWORD WINAPI DumpThread( PDUMP_PARAMS pParam ) 
{ 
    FILE* fpOut = pParam->fpOut;
    HANDLE hAdapter = pParam->hAdapter;
    DXB_QUEUE_TYPE qType = pParam->qType;
    
    BYTE abDump[0x2000];
    DWORD cbDumped = 0;
    BOOL fExit = FALSE;
    DWORD retStatus = 1;
    
    if ( fpOut == NULL )
        goto ExitThread;
    
    if ( hAdapter == INVALID_HANDLE_VALUE )
        goto ExitThread;
        
    if ( qType > DXB_QUEUE_TYPE_COUNT )        
        goto ExitThread;
        
    if ( WAIT_TIMEOUT == WaitForSingleObject( g_hReadyEvent, 30000 ))
    {
        DigiPrintf(("Timeout waiting for Ready Event\n"));
        goto ExitThread;
    }        
        
    DigiPrintf( ("Ready...%x\n",qType) );        
    while ( fExit == FALSE )
    {
        TRACE_HEADER TraceQueue = {0};     
        BOOL fDevIocltSucceeded = FALSE;
        DWORD cbDevIoctlReturned = 0;
        DWORD cbFile = (g_cbToDump/10)*8;
        
        memset(abDump, 0, sizeof(abDump));

        TraceQueue.Length = sizeof(abDump);
        TraceQueue.pBuffer = abDump;
        TraceQueue.QueueType = qType;

        fDevIocltSucceeded = DeviceIoControl(hAdapter, (DWORD)IOCTL_DXBQ_GET_TRACE,
                                          &TraceQueue, sizeof(TraceQueue),
                                          &TraceQueue, sizeof(TraceQueue),
                                          &cbDevIoctlReturned, NULL);

        if ((!fDevIocltSucceeded) || (cbDevIoctlReturned != sizeof(TraceQueue)))
        {
            DigiPrintf(("ERROR: Unable to read %d bytes from adapter %s!\n", sizeof(abDump), g_pszAdapter));
            goto ExitThread;
        }
        
        {
            ULONG count = fwrite( abDump, 1, TraceQueue.BytesRead, fpOut );
            if ( count != TraceQueue.BytesRead )
            {
                DigiPrintf(("ERROR: Writing to file. Bytes %x BytesWritten %x\n",TraceQueue.BytesRead,count));        
                goto ExitThread;
            }                
        }
        
        cbDumped += TraceQueue.BytesRead;
        if ( cbDumped > cbFile )
        {
            // Wrap file to beginning...
            DigiPrintf(( "%x",qType));        
            cbFile = cbDumped;
            cbDumped = 0;
    
            if ( fseek( fpOut, 0, SEEK_SET ) != 0)
            {
                DigiPrintf(("ERROR: fsetpos error file %s!\n",g_pszFile));
                goto ExitThread;
            }                    
        }                                
                        
        if ( TraceQueue.InPtr == TraceQueue.OutPtr )
        {
            if ( TraceQueue.QueueType == DXB_CMD_QUEUE )
            {
                DigiPrintf(("+"));
            }                
            else
            {
                DigiPrintf(("."));
            }                
            // Delay before making the next request
            if (WaitForSingleObject(g_hAbortEvent, g_Delay)!=WAIT_TIMEOUT)
            {
                fExit = TRUE;
                retStatus = 0;
            }                
        }                        
    }
ExitThread:
    return retStatus;
}

int __cdecl main(int argc, char * argv[])
{
    int nRet = 1;
    DUMP_PARAMS CmdQueue = {0};
    DUMP_PARAMS EventQueue = {0};
    HANDLE hAdapter = INVALID_HANDLE_VALUE;

    InitializeCriticalSection(&PrintfAvailable);
    Argv0 = argv[0];
    if (!ParseCmdLine(argc, argv)) goto Error;

    if (g_pszAdapter == NULL)
    {
        DigiPrintf(("ERROR: No adapter specified!\n"));
        goto Error;
    }
    // TODO: Check available disk space
    if ((g_cbToDump == 0))
    {
        DigiPrintf(("ERROR: Invalid number of MBs to dump specified!\n"));
        goto Error;
    }

    {
        char szAdapterDeviceName[32];
        sprintf(szAdapterDeviceName, "\\\\.\\%s", g_pszAdapter);

        hAdapter = CreateFile(szAdapterDeviceName,
                            GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ | FILE_SHARE_WRITE,
                            NULL,
                            CREATE_ALWAYS,
                            FILE_ATTRIBUTE_NORMAL,
                            NULL);
        if (hAdapter == INVALID_HANDLE_VALUE)
        {
            DigiPrintf(("ERROR: Unable to open adapter %s!\n", g_pszAdapter));
            goto Error;
        }
    }
    g_hReadyEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    g_hAbortEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
    if( !SetConsoleCtrlHandler( ControlHandler, TRUE ) )
    {
        DigiPrintf(("ERROR: Unable to establish break handler.\n"));
        goto Error;
    }
    if ( fTraceEventQ )
    {
        char szEventQFile[32];
        FILE * fpOutEvent = NULL;

        sprintf( szEventQFile, "%s.A2H",g_pszFile);
        fpOutEvent = fopen(szEventQFile, "wb");
        if (fpOutEvent == NULL)
        {
            DigiPrintf(("ERROR: Unable to open file %s for writing!\n", szEventQFile));
            goto Error;
        }
        else
        {
            DigiPrintf(("Starting Adapter to Host DXB queue trace to file: %s\n",szEventQFile ));
        }
   
        EventQueue.bufSize = g_BufSize;
        EventQueue.fpOut = fpOutEvent;
        EventQueue.hAdapter = hAdapter;
        EventQueue.qType = DXB_EVENT_QUEUE;
        EventQueue.hThread = CreateThread( NULL, 0, DumpThread, &EventQueue, 0, NULL );
    }
    
    if ( fTraceCmdQ )
    {
        char szCmdQFile[32];
        FILE * fpOutCmd   = NULL;

        sprintf( szCmdQFile, "%s.H2A",g_pszFile);
        fpOutCmd = fopen(szCmdQFile, "wb");
        if (fpOutCmd == NULL)
        {
            DigiPrintf(("ERROR: Unable to open file %s for writing!\n", szCmdQFile));
            goto Error;
        }
        else
        {
            DigiPrintf(("Starting Host to Adapter DXB queue trace to file: %s\n",szCmdQFile ));
        }
        
        CmdQueue.bufSize = g_BufSize;
        CmdQueue.fpOut = fpOutCmd;
        CmdQueue.hAdapter = hAdapter;
        CmdQueue.qType = DXB_CMD_QUEUE;
        CmdQueue.hThread = CreateThread( NULL, 0, DumpThread, &CmdQueue, 0, NULL );
    }
    
    if ( !SetEvent( g_hReadyEvent ))
    {
        DigiPrintf(("ERROR: Couldn't set ready to start event\n"));
        goto Error;
    }    
    goto Done;
    
Error:
    SetEvent( g_hAbortEvent );
    SetConsoleCtrlHandler( ControlHandler, FALSE );
    
Done:
    if ( CmdQueue.hThread != NULL )
        while ( WAIT_TIMEOUT == WaitForSingleObject( CmdQueue.hThread, 1000 ));
    if ( EventQueue.hThread != NULL )        
        while ( WAIT_TIMEOUT == WaitForSingleObject( EventQueue.hThread, 1000 ));

    if (EventQueue.fpOut != NULL)
    {
        fwrite(FileWrapMarker, 1, sizeof(FileWrapMarker),EventQueue.fpOut);                            
        fclose(EventQueue.fpOut);
        
    }       
    if (CmdQueue.fpOut != NULL)
    {    
        fwrite(FileWrapMarker, 1, sizeof(FileWrapMarker),CmdQueue.fpOut);
        fclose(CmdQueue.fpOut);
    }     
    if (hAdapter != INVALID_HANDLE_VALUE) 
        CloseHandle(hAdapter);

    DeleteCriticalSection( &PrintfAvailable );
    printf("\nDone\n");
    return nRet;
}
