////////////////////////////////////////////////////////////////
// File - WDDEBUG.C
//
// A utility that turns WinDriver's debug mode on and off.
// Debug mode checks every IO and memory transfer command,
// making sure it fits in with the card's registered
// resources. If an illegal command is given, WinDriver
// will ignore the command and show a warning message on
// screen. Debug mode slows down transfer operations, 
// therefore it should be used only in the development proccess.
// Running this command without parameters will print the
// version of WinDriver installed.
// 
// If debug mode is set, this utility also enables you to print
// out debug messages from the kernel, by the dump command.
// 
////////////////////////////////////////////////////////////////

#include "../../include/windrvr.h"
#include <stdio.h>
#include <stdlib.h>
#if defined(UNIX) || defined(OS2)
	#include <unistd.h>
	#include <fcntl.h>
#elif !defined(WINCE)
  #include <conio.h>
#endif
#if !defined(WINCE)
  #include <time.h>
#else
    extern time_t time();
#endif
#if defined(UNIX) || defined(WINCE)
    int _stricmp(const char *s1, const char *s2)
    {
        int i;
        for (i=0; s1[i] && s2[i] && toupper(s1[i])==toupper(s2[i]); i++);
        if (s1[i]=='\0' && s2[i]=='\0') return 0;
        return -1;
    }
#endif
#if defined(UNIX) || defined(OS2)
    void Sleep(int nMilli)
    {
		#if defined(VXWORKS)
			taskDelay( 1 );
		#elif defined(OS2)
			DosSleep(nMilli);
		#else
        	usleep(nMilli * 1000);
		#endif
    }
#endif

void Usage()
{
    printf ("This program sets debug mode of " WD_PROD_NAME " on or off\n");
    printf ("WDDEBUG off      - sets debugging mode OFF\n");
    printf ("WDDEBUG on       - sets debugging mode ON at ERROR level, for all sections\n");
    printf ("WDDEBUG on [level] [sections...] - sets debugging mode on for specific sections\n");
    printf ("WDDEBUG status   - prints the current version of " WD_PROD_NAME " and debug status\n");
    printf ("WDDEBUG dump     - prints out debug messages\n");
    printf ("\n");
    printf ("    level - ERROR, WARN, INFO, TRACE\n");
    printf ("    sections - ALL, IO, MEM, INT, PCI, PCMCIA, ISAPNP, \n");
    printf ("               DMA, KER_PLUG, MISC, LICENSE, CARD_REG\n");
    printf ("               ISAPNP, PCMCIA, KER_PLUG, KER_DRV\n");
    printf ("\n");
    printf ("Example: Turn on and view debugging for PCI and DMA routines, at info level\n");
    printf ("    WDDEBUG on info \"pci dma\"\n");
    printf ("    WDDEBUG dump\n");
}

BOOL LevelToDword(char *sLevel, DWORD *pdwLevel)
{
    DWORD dwLevel = (DWORD)-1;
    if (_stricmp(sLevel,"ERROR")==0) dwLevel = D_ERROR;
    if (_stricmp(sLevel,"WARN")==0) dwLevel = D_WARN;
    if (_stricmp(sLevel,"INFO")==0) dwLevel = D_INFO;
    if (_stricmp(sLevel,"TRACE")==0) dwLevel = D_TRACE;
    if (_stricmp(sLevel,"OFF")==0) dwLevel = D_OFF;
    if (dwLevel==(DWORD)-1) return FALSE;
    *pdwLevel = dwLevel;
    return TRUE;
}

char *LevelToString(DWORD dwLevel)
{
    if (dwLevel==D_OFF) return "OFF";
    if (dwLevel==D_ERROR) return "ERROR";
    if (dwLevel==D_WARN) return "WARN";
    if (dwLevel==D_INFO) return "INFO";
    if (dwLevel==D_TRACE) return "TRACE";
    return "";
}

BOOL SectionToDword(char *sSection, DWORD *pdwSection)
{
    char tokBuf[1024];
    char *tok;
    *pdwSection = 0;
    strcpy (tokBuf, sSection);
    for (tok = strtok(tokBuf, " "); tok; tok = strtok(NULL, " "))
    {
        if (_stricmp(tok, "ALL")==0) *pdwSection |= S_ALL;
        else if (_stricmp(tok, "IO")==0)        *pdwSection |= S_IO;
        else if (_stricmp(tok, "MEM")==0)       *pdwSection |= S_MEM;
        else if (_stricmp(tok, "INT")==0)       *pdwSection |= S_INT;
        else if (_stricmp(tok, "PCI")==0)       *pdwSection |= S_PCI;
        else if (_stricmp(tok, "DMA")==0)       *pdwSection |= S_DMA;
        else if (_stricmp(tok, "ISAPNP")==0)    *pdwSection |= S_ISAPNP;
        else if (_stricmp(tok, "PCMCIA")==0)    *pdwSection |= S_PCMCIA;
        else if (_stricmp(tok, "KER_PLUG")==0)  *pdwSection |= S_KER_PLUG;
        else if (_stricmp(tok, "MISC")==0)      *pdwSection |= S_MISC;
        else if (_stricmp(tok, "LICENSE")==0)   *pdwSection |= S_LICENSE;
        else if (_stricmp(tok, "ISAPNP")==0)    *pdwSection |= S_ISAPNP;
        else if (_stricmp(tok, "PCMCIA")==0)    *pdwSection |= S_PCMCIA;
        else if (_stricmp(tok, "KER_PLUG")==0)  *pdwSection |= S_KER_PLUG;
        else if (_stricmp(tok, "CARD_REG")==0)  *pdwSection |= S_CARD_REG;
        else if (_stricmp(tok, "KER_DRV")==0)   *pdwSection |= S_KER_DRV;
        else if (tok[0]=='0' && toupper(tok[1])=='x')
        {
            DWORD dwSection;
            sscanf(tok+2, "%x", &dwSection);
            *pdwSection |= dwSection;
        }
        else return FALSE;
    }

    return TRUE;
}

char *SectionToString(DWORD dwSection)
{
    static char sSection[1024];

    sSection[0] = '\0';
    if (dwSection==S_ALL) 
    {
        strcat (sSection, "ALL ");
        return sSection;
    }
    if (dwSection & S_IO)       { strcat (sSection, "IO ");       dwSection &= ~S_IO; }
    if (dwSection & S_MEM)      { strcat (sSection, "MEM ");      dwSection &= ~S_MEM; }
    if (dwSection & S_INT)      { strcat (sSection, "INT ");      dwSection &= ~S_INT; }
    if (dwSection & S_PCI)      { strcat (sSection, "PCI ");      dwSection &= ~S_PCI; }
    if (dwSection & S_DMA)      { strcat (sSection, "DMA ");      dwSection &= ~S_DMA; }
    if (dwSection & S_ISAPNP)   { strcat (sSection, "ISAPNP ");   dwSection &= ~S_ISAPNP; }
    if (dwSection & S_PCMCIA)   { strcat (sSection, "PCMCIA ");   dwSection &= ~S_PCMCIA; }
    if (dwSection & S_KER_PLUG) { strcat (sSection, "KER_PLUG "); dwSection &= ~S_KER_PLUG; }
    if (dwSection & S_MISC)     { strcat (sSection, "MISC ");     dwSection &= ~S_MISC; }
    if (dwSection & S_LICENSE)  { strcat (sSection, "LICENSE ");  dwSection &= ~S_LICENSE; }
    if (dwSection & S_ISAPNP)   { strcat (sSection, "ISAPNP ");   dwSection &= ~S_ISAPNP; }
    if (dwSection & S_PCMCIA)   { strcat (sSection, "PCMCIA ");   dwSection &= ~S_PCMCIA; }
    if (dwSection & S_KER_PLUG) { strcat (sSection, "KER_PLUG "); dwSection &= ~S_KER_PLUG; }
    if (dwSection & S_CARD_REG) { strcat (sSection, "CARD_REG "); dwSection &= ~S_CARD_REG; }
    if (dwSection & S_KER_DRV)  { strcat (sSection, "KER_DRV ");  dwSection &= ~S_KER_DRV; }

    if (dwSection)
        sprintf (sSection+strlen(sSection), "0x%08x", dwSection);
    return sSection;
}

void Print_version(WD_VERSION *pVer)
{
    printf (WD_PROD_NAME " v%d.%02d installed (%s)\n", pVer->dwVer / 100, pVer->dwVer % 100, pVer->cVer);
}

void Print_status(HANDLE hWD)
{
    WD_DEBUG debug;
    BZERO (debug);
    debug.dwCmd = DEBUG_STATUS;
    WD_Debug (hWD, &debug);
    printf ("Debug level (%d) %s, Debug sections (0x%08x) %s, Buffer size %d\n", 
        debug.dwLevel, LevelToString(debug.dwLevel), debug.dwSection, SectionToString(debug.dwSection), debug.dwBufferSize);
}

#if defined(VXWORKS)
	int main (char *arg1, char *arg2, char *arg3 )
#else
	int main (int argc, char *argv[]) 
#endif
{
    WD_VERSION verBuf;
    DWORD debug_mode = 2;
	HANDLE hWD = INVALID_HANDLE_VALUE;
	
	#if defined(VXWORKS)
		int argc=1;
		char *argv[4];

		argv[0] = "wddebug_main";

		if(arg1 != NULL)
		{	argv[1] = arg1;
			argc++;
		}
		if(arg2 != NULL)
		{	argv[2] = arg2;
			argc++;
		}
		if(arg3 != NULL)
		{	argv[3] = arg3;
			argc++;
		}
	#endif
	hWD = WD_Open();
	
    if (hWD==INVALID_HANDLE_VALUE)
    {
        printf ("Error: " WD_PROD_NAME " device not installed.\n");
        return EXIT_FAILURE;
    }

    BZERO(verBuf);
    WD_Version (hWD, &verBuf);

    if (argc<2)
    {
        Print_version (&verBuf);
        printf ("\n");
        Usage();
        return EXIT_SUCCESS;
   }

    if (verBuf.dwVer<WD_VER)
    {
        Print_version (&verBuf);
        printf ("Please update the " WD_PROD_NAME " installed to v%d.%02d, or newer.\n", WD_VER / 100, WD_VER % 100);
		WD_Close(hWD);
        return EXIT_FAILURE;
    }

    if (_stricmp(argv[1],"on")==0)
    {
        WD_DEBUG debug;

        BZERO (debug);
        debug.dwCmd = DEBUG_SET_FILTER;
        debug.dwLevel = D_ERROR;
        debug.dwSection = (DWORD) S_ALL;

	
        if (argc>2)
        {
            if (argc>4)
            {
                printf ("Too many arguments\n");
                Usage();
                return EXIT_FAILURE;
            }

            if (!LevelToDword(argv[2], &debug.dwLevel))
            {
                printf ("invalid level name (%s)\n", argv[2]);
                Usage();
                return EXIT_FAILURE;
            }

            if (argc==4 && !SectionToDword(argv[3], &debug.dwSection))
            {
                printf ("invalid section name (%s)\n", argv[3]);
                Usage();
                return EXIT_FAILURE;
            }
        }

        WD_Debug (hWD, &debug);
        Print_status (hWD);
    }
    else if (_stricmp(argv[1],"off")==0)
    {
        WD_DEBUG debug;

        if (argc>2)
        {
            printf ("Too many arguments\n");
            Usage();
            return EXIT_FAILURE;
        }

        BZERO (debug);
        debug.dwCmd = DEBUG_SET_FILTER;
        debug.dwLevel = D_OFF;
        debug.dwSection = 0;
        WD_Debug (hWD, &debug);
    }
    else if (_stricmp(argv[1],"status")==0)
    {
        if (argc>2)
        {
            printf ("Too many arguments\n");
            Usage();
            return EXIT_FAILURE;
        }

        Print_version (&verBuf);
        Print_status (hWD);
    }
    else if (_stricmp(argv[1],"dump")==0)
    {
        WD_DEBUG_DUMP debugDump;
		char sOSName[50]; 
        char buf[2048];
        time_t ltime;

		#if defined(UNIX) || defined(OS2)
			int stdin_fileno = 
			#if defined(VXWORKS)
				STD_IN;
			#else
				STDIN_FILENO;
			#endif
		#elif defined(WIN32)
			OSVERSIONINFO lVerInfo;
        #endif

        if (argc>2)
        {
            printf ("Too many arguments\n");
            Usage();
            return EXIT_FAILURE;
        }

        time(&ltime);
        #if defined(WIN32)
			lVerInfo.dwOSVersionInfoSize = sizeof (lVerInfo);
			GetVersionEx (&lVerInfo);
        #endif

        printf ("WDDEBUG v%d.%02d Debugging Monitor.\n", WD_VER / 100, 
			WD_VER % 100);
        printf ("Running %s\n", verBuf.cVer);
        #if !defined(WINCE)
			printf ("Time: %s", ctime (&ltime));
        #endif
        #if defined(WIN32)
			switch (lVerInfo.dwPlatformId)
			{
			case VER_PLATFORM_WIN32_NT:
				strcpy(sOSName, "NT");
				break;
			case VER_PLATFORM_WIN32_WINDOWS:
				if (lVerInfo.dwMinorVersion)
					strcpy(sOSName, "98");
				else
					strcpy(sOSName, "95");
			break;
			#if defined(VER_PLATFORM_WIN32_CE)
				case VER_PLATFORM_WIN32_CE:
					strcpy(sOSName, "CE");
					break;
			#endif
			default:
				strcpy(sOSName, "");
			}
			printf ("OS: Windows %s %d.%d Build %d.%d.%d %s\n",
				sOSName,
				lVerInfo.dwMajorVersion, lVerInfo.dwMinorVersion,
				lVerInfo.dwBuildNumber >> 24 & 0xff,
				lVerInfo.dwBuildNumber >> 16 & 0xff,
				lVerInfo.dwBuildNumber & 0xffff,
				lVerInfo.szCSDVersion);
		#elif defined(VXWORKS)
			printf("OS: VxWorks\n");
		#elif defined(OS2)
			printf("OS: OS/2\n");
		#elif defined(LINUX)
			printf("OS: Linux\n");
		#elif defined(SOLARIS)
			printf("OS: Solaris\n");
		#else
			printf("OS: Unknown\n");
		#endif
		#if defined(UNIX)
			#if defined(VXWORKS)
				printf("Press CTRL-BREAK to exit\n");
			#else
		        printf("Press enter to exit\n");
			#endif
		#else
	        printf ("Press ESC to exit\n");
		#endif
        printf ("\n");
        BZERO (debugDump);
        debugDump.pcBuffer = buf;
        debugDump.dwSize = sizeof (buf);
		#if (defined(UNIX) && !defined(VXWORKS)) || defined(OS2)
			fcntl(stdin_fileno, F_SETFL, fcntl(stdin_fileno, F_GETFL, 0) |
				O_NONBLOCK);
		#endif
        for (;;)
        {
			#if (defined(UNIX) && !defined(VXWORKS)) || defined(OS2)
				char buf[4];
				int nRead = read(stdin_fileno, buf, 4);
				if (nRead>0)
					break;
            #elif defined(VXWORKS)
				 // Will be implemented	
			#elif defined(WIN32) && !defined(WINCE)
                if (_kbhit() && getch()==27) break;
            #endif
            do
            {
                WD_DebugDump(hWD, &debugDump);
                printf ("%s", debugDump.pcBuffer);
            } while (debugDump.pcBuffer[0]);
            Sleep(100);
        }
    }
    else
    {
        printf ("invalid option (%s)\n", argv[1]);
        Usage();
    }

    WD_Close (hWD);
	return EXIT_SUCCESS;
}