/*****************************************************************************
 * Copyright (C) Cinterion Wireless Modules GmbH
 *****************************************************************************
 * Project: Quinn-USB-Driver
 *****************************************************************************
 * Author : Siemens AG Berlin COM WM RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/HcUsbDrv/UsbTrace.cpp $
 * $Revision: /main/ms-d/7 $
 *     $Date: 2013-10-24 13:27:48 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Implementation of the Quinn USB Driver trace interface.
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/HcUsbDrv/UsbTrace.cpp$ 
 * 
 * /main/ms-d/7   2013-10-24 13:27:48   pmackent 
 * Support Windows Embedded Compact 2013
 *
 * /main/ms-d/6   2011-01-14 18:18:59   ctna0152
 * added support for USB_RETAIL_KEEP_FILE_ON_BOOT
 *
 * /main/ms-d/5   2011-01-11 11:41:57   ctna0152
 * corrected initialization of critical section in debug build (bugfix for IPIS100069383) - Part 2
 *
 * /main/ms-d/4   2011-01-07 17:11:32   ctna0152
 * corrected initialization of critical section in debug build (bugfix for IPIS100069383)
 *
 * /main/ms-d/3   2009-02-18 11:49:45   ctna0256
 * Set copyright text
 *
 * /main/ms-d/2   2008-08-20 11:40:02   ctna0450
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/1   2008-07-31 15:50:53   ht37010
 * derived from RilTrace.cpp
 *
 * /main/ms-d/19   2008-06-06 16:45:37   mp49210
 * Handling of multiple %a added
 *
 * /main/ms-d/18   2007-07-06 10:13:55   mp49210
 * Lint compatibility
 *
 * /main/ms-d/17   2007-07-04 08:57:11   mp49210
 * Introduction of maximum trace file length
 *
 * /main/ms-d/16   2007-02-15 17:42:15   mp49210
 * PreFast compatible version without use of the '...z(' functions
 *
 * /main/ms-d/13   2007-02-09 15:25:49   mp49210
 * PreFast compatibility
 *
 * /main/ms-d/12   2007-02-09 15:10:56   mp49210
 * Initialize buffer before first use
 *
 * /main/ms-d/11   2007-02-09 08:52:21   mp49210
 * Added optional timestamps for retail traces
 * Fixed '%a' bug
 *
 * /main/ms-d/10   2006-11-14 12:55:48   wm36264
 * Added missing trace functionality required by USB
 *
 * /main/ms-d/9   2006-11-10 16:36:35   wm36264
 * Use only standard functions available on common CE build environments!!!
 *
 * /main/ms-d/8   2006-11-10 14:22:14   mp49210
 * Fixed PreFast warning
 *
 * /main/ms-d/7   2006-11-08 15:00:13   wm36264
 * Improved hex trace
 *
 * /main/ms-d/6   2006-10-26 16:51:23   wm36264
 * Fixed unicode and trace including problems
 *
 * /main/ms-d/5   2006-10-24 14:52:24   wm36264
 * Added TraceCRLFString()
 *
 * /main/ms-d/4   2006-10-17 12:20:56   wm36264
 * Make critical section global to lock more than one trace function
 *
 * /main/ms-d/3   2006-10-13 16:58:55   wm36264
 * Added hex trace
 *
 * /main/ms-d/2   2006-09-29 15:35:09   wm36264
 * Cosmetics
 *
 * /main/ms-d/1   2006-09-27 17:04:32   wm36264
 * Central trace handling of USB driver
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Cinterion Wireless Modules GmbH
// Copyright (C) 2006-2008 Siemens AG
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// UsbTrace.cpp
//
// This file contains the functions for trace output of the Cinterion USB
// driver and conneted modules.
//
//////////////////////////////////////////////////////////////////////////////

#include <Windows.h>
#include "UsbTrace.h"

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

#if defined(USB_TRACE_REQUIRED) || defined (DEBUG)
static CRITICAL_SECTION g_csTrace;
#endif
#if defined(USB_RETAIL_OUTPUT_PORT) || defined(USB_RETAIL_OUTPUT_FILE)
DWORD g_dwDisableTrace = 0;
DWORD g_dwTraceFileLength = 0;
#endif

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

//////////////////////////////////////////////////////////////////////////////
//
// Open and inintialize the serial port used for debug traces.
//
// Parameters:
// None.
//
// Return:
// The handle of the opened port on success, "INVALID_HANDLE_VALUE" in case
// of failure.
//
//////////////////////////////////////////////////////////////////////////////
#ifdef USB_RETAIL_OUTPUT_PORT
HANDLE UsbPortTraceOpen(void) {
HANDLE        hPort;
DCB           Dcb;
COMMTIMEOUTS  CommTimeouts;

  // Open the parent port
  hPort = CreateFile(USB_RETAIL_OUTPUT_PORT, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
  if (hPort == INVALID_HANDLE_VALUE) {
    return INVALID_HANDLE_VALUE;
  }

  // Setup the required buffer sizes
  if (!SetupComm(hPort, 32, 2048)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  // Get the parent port's DCB
  if (!GetCommState(hPort, &Dcb)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  // Set our default settings
  Dcb.DCBlength          = sizeof(DCB);
  Dcb.BaudRate           = CBR_115200;
  Dcb.fBinary            = TRUE;
  Dcb.fParity            = TRUE;
  Dcb.fOutxCtsFlow       = TRUE;
  Dcb.fOutxDsrFlow       = FALSE;
  Dcb.fDtrControl        = DTR_CONTROL_ENABLE;
  Dcb.fDsrSensitivity    = FALSE;
  Dcb.fTXContinueOnXoff  = TRUE;
  Dcb.fOutX              = FALSE;
  Dcb.fInX               = FALSE;
  Dcb.fErrorChar         = FALSE;
  Dcb.fNull              = FALSE;
  Dcb.fRtsControl        = RTS_CONTROL_ENABLE;
  Dcb.fAbortOnError      = FALSE;
  Dcb.ByteSize           = 8;
  Dcb.Parity             = NOPARITY;
  Dcb.StopBits           = ONESTOPBIT;

  // Set the parent port's DCB
  if (!SetCommState(hPort, &Dcb)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  // Set the parent port's timeouts
  CommTimeouts.ReadIntervalTimeout          = MAXDWORD;
  CommTimeouts.ReadTotalTimeoutMultiplier   = 0;
  CommTimeouts.ReadTotalTimeoutConstant     = 0;
  CommTimeouts.WriteTotalTimeoutMultiplier  = 0;
  CommTimeouts.WriteTotalTimeoutConstant    = 1000;
  if (!SetCommTimeouts(hPort, &CommTimeouts)) {
    CloseHandle(hPort);
    return INVALID_HANDLE_VALUE;
  }

  return hPort;
}
#endif // USB_RETAIL_OUTPUT_PORT

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

#define USB_RETAIL_DEBUG_BUFSIZE        1024

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

//////////////////////////////////////////////////////////////////////////////
//
// Write formatted output to serial trace port or trace file.
// To avoid unnecessary trace traffic WCHAR strings are converted to char
// strings before they are sent over the serial interface.
//
// Parameters:
// lpszFmt: Pointer to fromat control string.
// ...    : Optional arguments (see e.g. "printf()").
//
// Return:
// The number of written characters.
//
//////////////////////////////////////////////////////////////////////////////
int UsbTrace(LPCTSTR lpszFmt, ...) {
int iRet = 0;
static BOOL     fFirstCall = TRUE;

#if defined(USB_RETAIL_OUTPUT_PORT) || defined(USB_RETAIL_OUTPUT_FILE)

static TCHAR    Buffer[USB_RETAIL_DEBUG_BUFSIZE];
static va_list  ap;
static DWORD    dwWritten;
static size_t   len;

#ifdef USB_RETAIL_OUTPUT_PORT
static HANDLE   hPort = INVALID_HANDLE_VALUE;
#endif // USB_RETAIL_OUTPUT_PORT

#ifdef USB_RETAIL_OUTPUT_FILE
static HANDLE   hFile = INVALID_HANDLE_VALUE;
#endif // USB_RETAIL_OUTPUT_FILE

#ifdef WANT_USBDRV_RTC_TIMESTAMPS
SYSTEMTIME      sTime;
#endif

  if (!lpszFmt) {
#endif // defined(USB_RETAIL_OUTPUT_PORT) || defined(USB_RETAIL_OUTPUT_FILE)
    if (fFirstCall) {
      // We're called for the first time
#if defined(USB_TRACE_REQUIRED) || defined (DEBUG)
      InitializeCriticalSection(&g_csTrace);
      EnterCriticalSection(&g_csTrace);
#endif
#ifdef USB_RETAIL_OUTPUT_PORT
      hPort = UsbPortTraceOpen();
#endif // USB_RETAIL_OUTPUT_PORT
#ifdef USB_RETAIL_OUTPUT_FILE
#ifndef USB_RETAIL_KEEP_FILE_ON_BOOT
      CloseHandle(CreateFile(USB_RETAIL_OUTPUT_FILE, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS | TRUNCATE_EXISTING, 0, NULL));
#endif // #ifndef USB_RETAIL_KEEP_FILE_ON_BOOT
#endif // USB_RETAIL_OUTPUT_FILE
      fFirstCall = FALSE;
#if defined(USB_TRACE_REQUIRED) || defined (DEBUG)
      LeaveCriticalSection(&g_csTrace);
#endif
    } else {
#if defined(USB_TRACE_REQUIRED) || defined (DEBUG)
      // We're called for the last time
      EnterCriticalSection(&g_csTrace);
#endif
#ifdef USB_RETAIL_OUTPUT_PORT
      CloseHandle(hPort);
      hPort = INVALID_HANDLE_VALUE;
#endif // USB_RETAIL_OUTPUT_PORT
      fFirstCall = TRUE;
#if defined(USB_TRACE_REQUIRED) || defined (DEBUG)
      LeaveCriticalSection(&g_csTrace);
      DeleteCriticalSection(&g_csTrace);
#endif
    }

#if defined(USB_RETAIL_OUTPUT_PORT) || defined(USB_RETAIL_OUTPUT_FILE)
  } else {
    if (!g_dwDisableTrace) {
#ifdef USB_RETAIL_OUTPUT_FILE
      DWORD   dwFilePos;
#endif
      WCHAR  *pWC;
      WCHAR   UniBuffer2[USB_RETAIL_DEBUG_BUFSIZE];
      // We're asked to do some debug trace
      EnterCriticalSection(&g_csTrace);
#ifdef WANT_USBDRV_RTC_TIMESTAMPS
      GetLocalTime(&sTime);
      iRet = sprintf((char*)Buffer, "%02d:%02d:%02d:%02d: ", sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond);
#else // WANT_USBDRV_RTC_TIMESTAMPS
      iRet = sprintf((char*)Buffer, "%8d TID:%8x ", GetTickCount(), GetCurrentThreadId());
#endif // WANT_USBDRV_RTC_TIMESTAMPS

#ifdef USB_RETAIL_OUTPUT_PORT
      WriteFile(hPort, Buffer, strlen((char*)Buffer), &dwWritten, NULL);
#endif // USB_RETAIL_OUTPUT_PORT
#ifdef USB_RETAIL_OUTPUT_FILE
      hFile = CreateFile(USB_RETAIL_OUTPUT_FILE, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
      dwFilePos = SetFilePointer(hFile, 0, 0, FILE_END);
      if (g_dwTraceFileLength != 0 && g_dwTraceFileLength < dwFilePos + (DWORD)iRet) {
        // Maximum file length reached, delete file content and begin again.
        (void)SetFilePointer(hFile, 0, 0, FILE_BEGIN);
        (void)SetEndOfFile(hFile);
      }
      WriteFile(hFile, Buffer, strlen((char*)Buffer), &dwWritten, NULL);
#endif // USB_RETAIL_OUTPUT_FILE

      (void)wcsncpy(UniBuffer2, lpszFmt, USB_RETAIL_DEBUG_BUFSIZE);
      do {
          pWC = wcsstr(UniBuffer2, L"%a");
          if (pWC != NULL) {
            (void)wcsncpy(pWC, L"%S", 2);
          }
      }
      while (pWC != NULL);
      va_start(ap, lpszFmt);
      iRet += _vsntprintf(Buffer, USB_RETAIL_DEBUG_BUFSIZE - 1, UniBuffer2, ap);
      va_end(ap);
      len = _tcslen(Buffer);
      if (sizeof(char) != sizeof(TCHAR)) {
        DWORD i;
        for (i = 0; i <= len; i++) {
          ((char*)Buffer)[i] = (char)(Buffer[i]);
        }
      }

#ifdef USB_RETAIL_OUTPUT_PORT
      WriteFile(hPort, Buffer, len, &dwWritten, NULL);
#endif // USB_RETAIL_OUTPUT_PORT
#ifdef USB_RETAIL_OUTPUT_FILE
      WriteFile(hFile, Buffer, len, &dwWritten, NULL);
#endif // USB_RETAIL_OUTPUT_FILE
      if ((((char*)Buffer)[len - 1] != '\r') && (((char*)Buffer)[len - 1] != '\n')) {
#ifdef USB_RETAIL_OUTPUT_PORT
        WriteFile(hPort, "\r\n", 2, &dwWritten, NULL);
#endif // USB_RETAIL_OUTPUT_PORT
#ifdef USB_RETAIL_OUTPUT_FILE
        WriteFile(hFile, "\r\n", 2, &dwWritten, NULL);
#endif // USB_RETAIL_OUTPUT_FILE
      }
#ifdef USB_RETAIL_OUTPUT_FILE
      CloseHandle(hFile);
#endif // USB_RETAIL_OUTPUT_FILE
      LeaveCriticalSection(&g_csTrace);
    }
  }

#endif // defined(USB_RETAIL_OUTPUT_PORT) || defined(USB_RETAIL_OUTPUT_FILE)

  return iRet;
}

//////////////////////////////////////////////////////////////////////////////
//
// Write formatted output to debug trace port.
//
// Parameters:
// lpszFmt: Pointer to fromat control string.
// ...    : Optional arguments (see e.g. "printf()").
//
// Return:
// Always 0.
//
//////////////////////////////////////////////////////////////////////////////
#ifdef USB_INCLUDE_TIMESTAMPS_TO_RETAIL
int USBRetailTraceWithTimestamps(LPCTSTR lpszFmt, ...) {
#ifdef WANT_USBDRV_RTC_TIMESTAMPS
  SYSTEMTIME  sTime;
#endif
  va_list ap;
  WCHAR       wszBuffer[USB_RETAIL_DEBUG_BUFSIZE];
  WCHAR      *pWC;
  WCHAR       UniBuffer2[USB_RETAIL_DEBUG_BUFSIZE];

  (void)wcsncpy(UniBuffer2, lpszFmt, USB_RETAIL_DEBUG_BUFSIZE);
  do {
    pWC = wcsstr(UniBuffer2, L"%a");
    if (pWC != NULL)
      wcsncpy(pWC, L"%S", 2);
  }
  while (pWC != NULL);
  va_start(ap, lpszFmt);
  wvsprintf(wszBuffer, UniBuffer2, ap);
  va_end(ap);

#ifdef WANT_USBDRV_RTC_TIMESTAMPS
  GetLocalTime(&sTime);
  NKDbgPrintfW(L"%02d:%02d:%02d:%02d: %s", sTime.wDay, sTime.wHour, sTime.wMinute, sTime.wSecond ,wszBuffer);
#else
  NKDbgPrintfW(L"%06d %s", GetTickCount()%1000000, wszBuffer);
#endif
  return 0;
}
#endif

//////////////////////////////////////////////////////////////////////////////
//
// Trace raw hex data.
//
// Parameters:
// dwDbgZone: The debug zone to use.
// lpszPre  : Pre string to be added at start of each hex line.
// pData    : Data to be traced.
// dwLen    : The number of bytes to be traced.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void TraceHexData(DWORD dwDbgZone, LPCTSTR lpszPre, BYTE* pData, DWORD dwLen) {
#if defined(USB_TRACE_REQUIRED) || defined (DEBUG)
  if (dwDbgZone) {
    TCHAR  szOut[512], szNum[16], szAscii[128];
    DWORD  i, j, k;
    DWORD  dwCharsPerLine = 16;

    EnterCriticalSection(&g_csTrace);
    for (i = 0; i < dwLen; i += dwCharsPerLine) {
      _tcsncpy(szOut, lpszPre, 512);
      szOut[511] = 0;
      _tcscpy(szAscii, TEXT("  |  "));
      for (j = 0; (j < dwCharsPerLine) && ((i + j) < dwLen); j++) {
#ifdef HCUSBDRV_CE6
        _stprintf_s(szNum, sizeof(szNum), TEXT(" 0x%02X"), pData[i + j]);
#else
        _stprintf(szNum, TEXT(" 0x%02X"), pData[i + j]);
#endif
        _tcscat(szOut, szNum);
#ifdef HCUSBDRV_CE6
        _stprintf_s(szNum, sizeof(szNum), TEXT("%c"), ((pData[i + j] >= 0x20) && (pData[i + j] < 255)) ? pData[i + j] : TEXT('.'));
#else
        _stprintf(szNum, TEXT("%c"), ((pData[i + j] >= 0x20) && (pData[i + j] < 255)) ? pData[i + j] : TEXT('.'));
#endif
        _tcscat(szAscii, szNum);
      }
      for (k = j; k < dwCharsPerLine; k++) {
        _tcscat(szOut, TEXT("     "));
      }
      _tcscat(szOut, szAscii);
      _tcscat(szOut, TEXT("\r\n"));
      DEBUGMSG(dwDbgZone, (szOut));
    }
    LeaveCriticalSection(&g_csTrace);
  }
#endif // defined(USB_TRACE_REQUIRED) || defined (DEBUG)
}

#define TRACECRLFSTRING_LENGTH   256
#ifdef USB_INCLUDE_TIMESTAMPS_TO_RETAIL
#ifdef WANT_USBDRV_RTC_TIMESTAMPS
#define RETAIL_TRACE_CHARS_PER_LINE   186
#else
#define RETAIL_TRACE_CHARS_PER_LINE   192
#endif
#else
#define RETAIL_TRACE_CHARS_PER_LINE   200
#endif

//////////////////////////////////////////////////////////////////////////////
//
// Format a CHAR string into a WCHAR string replacing '\r' and '\n' with
// visable chars.
//
// Parameters:
// lpszPre: Pre string to be added at start of each <cr><lf> line.
// szText : Data to be traced.
// szIn   : String to be printed into szText if it contains a string
//          placeholder "%s".
// szCrLf : String to be traced with the <cr><lf> replacement.
//
// Return:
// None.
//
//////////////////////////////////////////////////////////////////////////////
void TraceCRLFString(LPCTSTR szPre, LPCTSTR szText, LPCTSTR szIn, LPCSTR szCrLf) {
#if defined(USB_TRACE_REQUIRED) || defined (DEBUG)
  TCHAR       szCR[] = TEXT("<cr>");
  TCHAR       szLF[] = TEXT("<lf>");
  TCHAR       szBuf[TRACECRLFSTRING_LENGTH];
  UINT        iLenCR = wcslen(szCR);
  UINT        iLenLF = wcslen(szLF);
  UINT        iPrint;
  UINT        i;
  UINT        iPreLen = 0;
  const UINT  iMaxCharsPerLine = RETAIL_TRACE_CHARS_PER_LINE;

  EnterCriticalSection(&g_csTrace);
  memset(szBuf, 0, sizeof(TCHAR)*TRACECRLFSTRING_LENGTH);
  if (szPre) {
    iPreLen = _tcslen(szPre);
    _tcsncpy(szBuf, szPre, TRACECRLFSTRING_LENGTH);
  }
  if (szText) {
    if (!szIn) {
      _tcsncat(&(szBuf[iPreLen]), szText, TRACECRLFSTRING_LENGTH - iPreLen);
    } else {
#ifdef HCUSBDRV_CE6
      _stprintf_s(&(szBuf[iPreLen]), TRACECRLFSTRING_LENGTH - iPreLen, szText, szIn);
#else
      _stprintf(&(szBuf[iPreLen]), szText, szIn);
#endif
    }
  }

  iPrint = wcslen(szBuf);
  for (i = 0; i < strlen(szCrLf); i++) {
    // Check if we have to start a new line
    if (iPrint >= iMaxCharsPerLine) {
      _tcsncpy(&szBuf[iPrint], TEXT("\r\n"), TRACECRLFSTRING_LENGTH - iPrint);
      USBRetailTrace((szBuf));
      _tcsncpy(szBuf, szPre, TRACECRLFSTRING_LENGTH);
      _tcsncat(&(szBuf[iPreLen]), TEXT(" ... "), TRACECRLFSTRING_LENGTH - iPreLen);
      iPrint = wcslen(szBuf);
    }

    // Check for chars to be replaced
    if (szCrLf[i] == '\r') {
      _tcsncpy(&szBuf[iPrint], szCR, TRACECRLFSTRING_LENGTH - iPrint);
      iPrint += iLenCR;
    } else if (szCrLf[i] == '\n') {
      _tcsncpy(&szBuf[iPrint], szLF, TRACECRLFSTRING_LENGTH - iPrint);
      iPrint += iLenLF;
    } else {
      szBuf[iPrint] = szCrLf[i];
      iPrint++;
      szBuf[iPrint] = TEXT('\0');
    }
  }
  _tcsncpy(&szBuf[iPrint], TEXT("\r\n"), TRACECRLFSTRING_LENGTH - iPrint);
  USBRetailTrace((szBuf));
  LeaveCriticalSection(&g_csTrace);
#endif // defined(USB_TRACE_REQUIRED) || defined (DEBUG)
}
