 /* ================================================================= *
 *                                                                    *
 *         File Name: PcComPrt.c                                      *
 *            Device: IBM PC compatible, Windows API32                *
 *         Author(s): Walter Borst (bow), Borst Automation            *
 *                                                                    *
 * Description                                                        *
 * ===========                                                        *
 * Functions to use the com port of a PC by access of the Windows     *
 * API. When a com port is opened a thread is started and handling    *
 * the communication events.                                          *
 *                                                                    *
 * Revision History                                                   *
 * ================                                                   *
 * date:      name:  version:  description:                           *
 * jan-00     bow    00000     module created                         *
 *                                                                    *
 * Functions                                                          *
 * =========                                                          *
 * HANDLE  OpenComPort(uiPort,       // 1..8                          *
 *                     uiBaudrate,   // 1200..56700                   *
 *                     ucDataBits,   // 4..8                          *
 *                     ucParity,     // 0-4 = no,odd,even,mark,space  *
 *                     ucStopBits    // 0,1,2 = 1, 1.5, 2             *
 *                    );                                              *
 * - Opens a com port and starts a thread.                            *
 * - Returns the com port handle.                                     *
 *                                                                    *
 * UINT    CloseComPort(HANDLE       //Handle returned by open        *
 *                     );                                             *
 * - Closes a com port and ends the thread. Returns TRUE or FALSE.    *
 *                                                                    *
 * UINT    WriteToCom(HANDLE,        //Handle returned by open        *
 *                    pbyData,       //Pointer to stream to be sent   *
 *                    uiLen          //number of octets in stream     *
 *                   );                                               *
 * - Writes data to the com port buffer and starts sending.           *
 *                                                                    *
 * Remarks                                                            *
 * =======                                                            *
 * Program using this functions has to be linked with winmm.lib       *
 **********************************************************************/

/**********************************************************************
 * The Standard Include                                               *
 **********************************************************************/
#define __PCCOMPRT_H__
#include "..\Common\PcComPrt.h"

/**********************************************************************
 * Global variables and data objects                                  *
 **********************************************************************/
static typCOMPORTINFO m_strComPortInfo[MAXCOMPORT];
static UCHR           m_ucLastChar;
static UCHR           m_ucLastErr;
static UCHR           m_ucDelayCycles = 0;

/**********************************************************************
 * Functions                                                          *
 **********************************************************************/

   //******************
void PcComPrtInitialize(void)
  //******************
{ //Initializes the internal data of the module
  //Called when DLL is loaded
  UCHR e;

  for(e=0;e<MAXCOMPORT;e++)
  { m_strComPortInfo[e].ulConnected = FALSE;
    m_strComPortInfo[e].iComIdx = 0;
    m_strComPortInfo[e].ulLastTransitionTime = timeGetTime();
    m_strComPortInfo[e].uiSendBufLen = 0;
    m_strComPortInfo[e].uiCount = 0;
    m_strComPortInfo[e].uiState = 0;
    m_strComPortInfo[e].hComFileHandle = INVALID_HANDLE_VALUE;
    m_strComPortInfo[e].hComThread = INVALID_HANDLE_VALUE;
    m_strComPortInfo[e].ulThreadId = 0;
    m_strComPortInfo[e].pAppCallBack = NULL;
    m_strComPortInfo[e].uiThreadRepeat = FALSE;
    m_strComPortInfo[e].uiThreadEnded = FALSE;
    m_strComPortInfo[e].uiSISMstatus = SISM_IDLE;
  }
}

//This is called by the hart initialization (lower layer)
    /***********/
BOOL OpenComPort(
    /***********/ UINT uiComPort,
                  UINT uiBaudrate,
                  UCHR ucDataBits,
                  UCHR ucParity,
                  UCHR ucStopBits
                )
{ CHR            szPort[15];
  COMMTIMEOUTS   strCommTimeOuts;
  DCB            dcb;
  INT            iComIdx = (INT) uiComPort-1;

  if (uiComPort>MAXCOMPORT)
    return FALSE;
  else if (uiComPort < 1)
  {
    return FALSE;
  }
  else
  {
    wsprintf(szPort,"COM%d",uiComPort) ;
  }

  if (COMPORTINFO.hComFileHandle != INVALID_HANDLE_VALUE)
  {
    // Allready registered !!!
    return FALSE; //Only one caller may use the com port
  }
  // open COMM device
  COMPORTINFO.hComFileHandle =
  CreateFile( szPort, GENERIC_READ | GENERIC_WRITE,
              0,                    // exclusive access
              NULL,                 // no security attrs
              OPEN_EXISTING,
              FILE_ATTRIBUTE_NORMAL |
              FILE_FLAG_OVERLAPPED, // overlapped I/O
              NULL
            );
  if (COMPORTINFO.hComFileHandle == INVALID_HANDLE_VALUE)
    return FALSE; //No success

  // get any early notifications
  SetCommMask(COMPORTINFO.hComFileHandle,EV_RXCHAR);
  // setup device buffers
  SetupComm(COMPORTINFO.hComFileHandle,256,256);
  // purge any information in the buffer
  PurgeComm(COMPORTINFO.hComFileHandle,
            PURGE_TXABORT | PURGE_RXABORT |
            PURGE_TXCLEAR | PURGE_RXCLEAR
           );
  // set up for overlapped I/O
  strCommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF ;
  strCommTimeOuts.ReadTotalTimeoutMultiplier = 0 ;
  strCommTimeOuts.ReadTotalTimeoutConstant = 1000 ;
  strCommTimeOuts.WriteTotalTimeoutMultiplier = 0 ;
  strCommTimeOuts.WriteTotalTimeoutConstant = 1000 ;
  SetCommTimeouts(COMPORTINFO.hComFileHandle,&strCommTimeOuts) ;

  /******************************
   * Set the com port parameter *
   ******************************/
  GetCommState(COMPORTINFO.hComFileHandle,&dcb) ;
  dcb.BaudRate = uiBaudrate;
  dcb.ByteSize = ucDataBits;
  dcb.Parity = ucParity;
  dcb.StopBits = ucStopBits;
  dcb.fBinary = TRUE;
  dcb.fParity = TRUE;
  dcb.fErrorChar = FALSE;
  dcb.ErrorChar = (char) 0;
  dcb.fAbortOnError = FALSE;
  dcb.fDtrControl = DTR_CONTROL_DISABLE;
  dcb.fRtsControl = RTS_CONTROL_DISABLE;
  if (SetCommState(COMPORTINFO.hComFileHandle,&dcb)==FALSE)
    return TRUE;
  COMPORTINFO.ulConnected = TRUE;
  COMPORTINFO.iComIdx = iComIdx;
  //***************************************************
  //* Start Thread to run the application state machine
  //***************************************************
  if (COMPORTINFO.uiThreadRepeat == FALSE)
  { COMPORTINFO.uiThreadRepeat = TRUE;
    COMPORTINFO.hComThread =
      CreateThread((LPSECURITY_ATTRIBUTES) NULL,  //Security Flag
                   0,                             //Stack size = default
                   (LPTHREAD_START_ROUTINE) CommWatchProc,
                                                  //Start procedure
                   (LPVOID) &(COMPORTINFO),       //Thread's data
                   0,
                   //THREAD_PRIORITY_TIME_CRITICAL, //Creation flags
                   &COMPORTINFO.ulThreadId
                  );
  }
  if (COMPORTINFO.hComThread == 0)
  {
    COMPORTINFO.uiThreadRepeat = FALSE;
    CloseHandle(COMPORTINFO.hComFileHandle);
    COMPORTINFO.hComFileHandle = INVALID_HANDLE_VALUE;
    return FALSE; //Thread did not start
  }
  else
  {
    //Give thread highest priority possible
    SetThreadPriority(COMPORTINFO.hComThread,
                      THREAD_PRIORITY_TIME_CRITICAL);
    //Com port handshake and carrier control signals
    EscapeCommFunction(COMPORTINFO.hComFileHandle,CLRRTS);
    EscapeCommFunction(COMPORTINFO.hComFileHandle,SETDTR);
  }
  return TRUE;
} // end of OpenComPort()

    /************/
VOID CloseComPort(INT iComPort)
    /************/
{
  INT iComIdx = iComPort - 1;

  //End the thread
  if (COMPORTINFO.uiThreadRepeat != FALSE)
  {
    COMPORTINFO.uiThreadRepeat = FALSE;
    while (COMPORTINFO.uiThreadEnded == FALSE)
    { Sleep(10);
    }
    COMPORTINFO.uiThreadEnded = FALSE;
  }
  //Close the com port
  if (COMPORTINFO.hComFileHandle != INVALID_HANDLE_VALUE)
    CloseHandle(COMPORTINFO.hComFileHandle);
}

    /**********/
VOID WriteToCom(
    /**********/ INT    iComPort,
                 BYTE*  pbBuf,
                 UINT   uiLen)
{
  INT     iComIdx = iComPort - 1;
  DWORD   dwLen;
  UCHR    e;

  for (e=0;e<HRT_NO_PREAMBLES;e++)
    COMPORTINFO.bySendBuf[e] = 0xff;
  dwLen = uiLen;
  memcpy(&COMPORTINFO.bySendBuf[HRT_NO_PREAMBLES],pbBuf,dwLen);
  COMPORTINFO.uiSendBufLen = dwLen+HRT_NO_PREAMBLES;
  COMPORTINFO.uiSISMstatus = SISM_TRANSMITTING;
}

    /**********/
UCHR GetRcvChar(VOID)
    /**********/
{
  return m_ucLastChar;
}

    /*********/
UCHR GetRcvErr(VOID)
    /*********/
{
  return m_ucLastErr;
}

         //*************
static INT ReadCommBlock(LPSTR pParam, LPSTR lpszBlock)
         //*************
  // Copies received characters from the com port buffer
  // to the user buffer (lpszBlock)
{
  COMSTAT    ComStat;
  int        fReadStat = FALSE;
  DWORD      dwErrorFlags = 0;
  DWORD      dwLength = 0;

  // only try to read number of bytes in queue
  ClearCommError(PCOMPORTINFO->hComFileHandle,&dwErrorFlags,&ComStat);
  m_ucLastErr = 0;
  if (dwErrorFlags)
  {
    if (dwErrorFlags & CE_FRAME)
      m_ucLastErr |= HRT_ERR_FRAMING;
    if (dwErrorFlags & CE_OVERRUN)
      m_ucLastErr |= HRT_ERR_OVERRUN;
    if (dwErrorFlags & CE_RXPARITY)
      m_ucLastErr |= HRT_ERR_VERT_PARITY;
  }
  dwLength = min(MAXBLOCK,ComStat.cbInQue);
  if (dwLength > 0)
  { fReadStat = ReadFile(PCOMPORTINFO->hComFileHandle,
                         lpszBlock,
                         dwLength,
                         &dwLength,
                         &(PCOMPORTINFO->osRead));
    if (!fReadStat)
    { // I/O error
      // not handled here but by the communication state machine
    }
  }
  return ( dwLength ) ;
}

                  //#############
static DWORD WINAPI CommWatchProc(LPSTR pData)
                  //#############
  // Thread for com port i/o handling
{
//  COMSTAT           ComStat;
  DWORD             dwEvtMask;
  DWORD             dwErrorFlags;
  DWORD             dwWritten;
  OVERLAPPED        os;
  INT               iLen;
  BYTE              abIn[MAXBLOCK+1];
  typCOMPORTINFO*   pParam = (typCOMPORTINFO*) pData;
  INT               e;
  COMSTAT           ComStat;

  memset(&os,0,sizeof(OVERLAPPED));
  // create I/O event used for overlapped read
  os.hEvent = CreateEvent(NULL,    // no security
                          TRUE,    // explicit reset req
                          FALSE,   // initial event reset
                          NULL     // no name
                         );
  if (os.hEvent == NULL)
  { MessageBox( NULL, "Failed to create event for thread!", "COMPORT Error!",
                MB_ICONEXCLAMATION | MB_OK ) ;
    return (FALSE) ;
  }

  if (!SetCommMask(PCOMPORTINFO->hComFileHandle,
                   EV_RXCHAR | EV_TXEMPTY ))
    return (FALSE);
  // ### THE LOOP OF THE THREAD ###############################
  while (PCOMPORTINFO->uiThreadRepeat)
  { 
    if (PCOMPORTINFO->uiSISMstatus == SISM_TRANSMITTING)
    { //Transmit
      COMPORT_START_SEND;
      while(PCOMPORTINFO->uiSISMstatus == SISM_TRANSMITTING)
      { dwEvtMask = 0;
        GetCommMask(PCOMPORTINFO->hComFileHandle,&dwEvtMask);
        if ((dwEvtMask & EV_TXEMPTY) == EV_TXEMPTY)
        {
          COMPORT_TRANSMIT_DONE; //This will set .uiSISMstatus = SISM_IDLE
          hrtTransmitDone();     //CALL THE HART DRIVER
        //===============  
        }
        Sleep(5);
      }
    }
    else
    { iLen = ReadCommBlock((LPSTR) pParam,(LPSTR) abIn);
      if (iLen)
      { //Receive
        m_ucDelayCycles = (UCHR)(iLen - 1);
        for (e=0;e<iLen;e++)
        {
        m_ucLastChar = abIn[e];
        hrtGetChar();  //CALL THE HART DRIVER
      //==========
        }
      }
      else
      { //Do Cycle
        if (!m_ucDelayCycles)
          hrt5msCycle(); //CALL THE HART DRIVER
        //===========  
        else
          m_ucDelayCycles--;
        Sleep(3); //Wait five ms before next action
      }
    }
  } // while (uiThreadRepeat)
  // get rid of event handle
  CloseHandle(os.hEvent);
  PCOMPORTINFO->uiThreadEnded = TRUE;
  ExitThread(0);
  return(0);
} // end of CommWatchProc()
