/*
 * Windows communications library
 *
 * Copyright 2005-2009 Alex Strickland <sscc@mweb.co.za>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file LICENSE.txt.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA (or visit https://www.gnu.org/licenses/).
 *
 * As a special exception, the Harbour Project gives permission for
 * additional uses of the text contained in its release of Harbour.
 *
 * The exception is that, if you link the Harbour libraries with other
 * files to produce an executable, this does not by itself cause the
 * resulting executable to be covered by the GNU General Public License.
 * Your use of that executable is in no way restricted on account of
 * linking the Harbour library code into it.
 *
 * This exception does not however invalidate any other reasons why
 * the executable file might be covered by the GNU General Public License.
 *
 * This exception applies only to the code released by the Harbour
 * Project under the name Harbour.  If you copy code from other
 * Harbour Project or Free Software Foundation releases into a copy of
 * Harbour, as the General Public License permits, the exception does
 * not apply to the code that you add in this way.  To avoid misleading
 * anyone as to the status of such modified files, you must delete
 * this exception notice from them.
 *
 * If you write modifications of your own for Harbour, it is your choice
 * whether to permit this exception to apply to your modifications.
 * If you do not wish that, delete this exception notice.
 *
 */

#include "hbwin.h"
#include "hbapierr.h"
#include "hbinit.h"

static struct
{
   HANDLE hPort;
   int    iFunction;
   DWORD  dwError;
} s_PortData[ 256 ];

static void hb_wincom_init( void )
{
   int i;

   for( i = 0; i < ( int ) HB_SIZEOFARRAY( s_PortData ); i++ )
      s_PortData[ i ].hPort = INVALID_HANDLE_VALUE;
}

HB_FUNC( WIN_COMOPEN )
{
   int iPort = hb_parni( 1 );

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) )
   {
      DWORD dwBaudRate = ( DWORD ) hb_parnl( 2 );
      int iParity = hb_parni( 3 );
      int iByteSize = hb_parni( 4 );
      int iStopBits = hb_parni( 5 );
      int iPos, i;

      HANDLE hCommPort;
      DCB NewDCB;

      TCHAR szName[ 11 ] = { TEXT( '\\' ),
                             TEXT( '\\' ),
                             TEXT( '.' ),
                             TEXT( '\\' ),
                             TEXT( 'C' ),
                             TEXT( 'O' ),
                             TEXT( 'M' ),
                             TEXT( '\0' ),
                             TEXT( '\0' ),
                             TEXT( '\0' ),
                             TEXT( '\0' ) };

      i = iPort + 1;
      iPos = 6;
      while( i > 0 )
      {
         iPos++;
         i /= 10;
      }
      i = iPort + 1;
      while( i > 0 )
      {
         szName[ iPos-- ] = ( TCHAR ) ( i % 10 + '0' );
         i /= 10;
      }

      s_PortData[ iPort ].hPort = INVALID_HANDLE_VALUE;
      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_CREATEFILE;
      s_PortData[ iPort ].dwError = 0;

      if( ( hCommPort = CreateFile( szName,
                                    GENERIC_READ | GENERIC_WRITE,
                                    0,
                                    NULL,
                                    OPEN_EXISTING,
                                    FILE_FLAG_NO_BUFFERING, NULL ) ) == INVALID_HANDLE_VALUE )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retnl( -1 );
         return;
      }

      NewDCB.DCBlength = sizeof( DCB );
      if( ! GetCommState( hCommPort, &NewDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         CloseHandle( hCommPort );
         hb_retnl( -1 );
         return;
      }

      /* Initialised with NO flow control or control signals! */
      NewDCB.BaudRate = dwBaudRate;
      NewDCB.fBinary = 1;
      NewDCB.fParity = 0;
      NewDCB.fOutxCtsFlow = 0;
      NewDCB.fOutxDsrFlow = 0;
      NewDCB.fDtrControl = DTR_CONTROL_DISABLE;
      NewDCB.fDsrSensitivity = 0;
      NewDCB.fTXContinueOnXoff = 1;
      NewDCB.fOutX = 0;
      NewDCB.fInX = 0;
      NewDCB.fErrorChar = 1;
      NewDCB.fNull = 0;
      NewDCB.fRtsControl = RTS_CONTROL_DISABLE;
      NewDCB.fAbortOnError = 0;
    /*NewDCB.XonLim*/
    /*NewDCB.XoffLim*/
      NewDCB.ByteSize = ( BYTE ) iByteSize;
      NewDCB.Parity = ( BYTE ) iParity;
      NewDCB.StopBits = ( BYTE ) iStopBits;
    /*NewDCB.XonChar*/
    /*NewDCB.XoffChar*/
      NewDCB.ErrorChar = '?';
    /*NewDCB.EofChar*/
    /*NewDCB.EvtChar*/

      /* function reinitializes all hardware and control settings, but it does not empty output or input queues */
      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_SETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      if( ! SetCommState( hCommPort, &NewDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         CloseHandle( hCommPort );
         hb_retnl( -1 );
         return;
      }

      s_PortData[ iPort ].hPort = hCommPort;
      hb_retnl( hCommPort == INVALID_HANDLE_VALUE ? -1 : 0 );
   }
   else
      hb_retnl( -1 );
}

HB_FUNC( WIN_COMCLOSE )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      long lDrain = hb_parnl( 2 );

      if( lDrain > 0 )
         Sleep( lDrain * 1000 );

      s_PortData[ iPort ].hPort = INVALID_HANDLE_VALUE;

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_CLOSEHANDLE;
      s_PortData[ iPort ].dwError = 0;

      hb_retl( CloseHandle( hCommPort ) != 0 );
      s_PortData[ iPort ].dwError = GetLastError();
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMWRITE )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      const char * lpBuffer = hb_parcx( 2 );
      DWORD dwNumberofBytesToWrite = ( DWORD ) hb_parclen( 2 );
      DWORD dwNumberofBytesWritten;

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_WRITEFILE;
      s_PortData[ iPort ].dwError = 0;
      if( WriteFile( hCommPort, lpBuffer, dwNumberofBytesToWrite, &dwNumberofBytesWritten, NULL ) )
         hb_retnl( dwNumberofBytesWritten );
      else
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retnl( -1 );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMREAD )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      char * lpBuffer;
      DWORD dwNumberOfBytesToRead = ( DWORD ) hb_parclen( 2 );
      DWORD dwNumberOfBytesRead;

      lpBuffer = ( char * ) hb_xgrab( dwNumberOfBytesToRead + 1 );
      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_READFILE;
      s_PortData[ iPort ].dwError = 0;
      if( ReadFile( hCommPort, lpBuffer, dwNumberOfBytesToRead, &dwNumberOfBytesRead, NULL ) )
      {
         if( ! hb_storclen_buffer( lpBuffer, dwNumberOfBytesRead, 2 ) )
            hb_xfree( lpBuffer );
         hb_retnl( dwNumberOfBytesRead );
      }
      else
      {
         hb_storc( NULL, 2 );
         hb_xfree( lpBuffer );
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retnl( -1 );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMRECV )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      char * lpBuffer;
      DWORD dwNumberOfBytesToRead = ( DWORD ) hb_parnl( 2 );
      DWORD dwNumberOfBytesRead;

      lpBuffer = ( char * ) hb_xgrab( dwNumberOfBytesToRead + 1 );
      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_READFILE;
      s_PortData[ iPort ].dwError = 0;
      if( ReadFile( hCommPort, lpBuffer, dwNumberOfBytesToRead, &dwNumberOfBytesRead, NULL ) )
      {
         hb_retclen_buffer( lpBuffer, dwNumberOfBytesRead );
         hb_stornl( dwNumberOfBytesRead, 3 );
      }
      else
      {
         hb_retc_null();
         hb_xfree( lpBuffer );
         s_PortData[ iPort ].dwError = GetLastError();
         hb_stornl( -1, 3 );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMSTATUS )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DWORD dwModemStat = 0;

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMMODEMSTATUS;
      s_PortData[ iPort ].dwError = 0;
      if( GetCommModemStatus( hCommPort, &dwModemStat ) )
      {
         hb_storl( ( dwModemStat & MS_CTS_ON )  != 0, 2 );     /* The CTS (clear-to-send) signal is on. */
         hb_storl( ( dwModemStat & MS_DSR_ON )  != 0, 3 );     /* The DSR (data-set-ready) signal is on. */
         hb_storl( ( dwModemStat & MS_RING_ON ) != 0, 4 );     /* The ring indicator signal is on. */
         hb_storl( ( dwModemStat & MS_RLSD_ON ) != 0, 5 );     /* The RLSD (receive-line-signal-detect) signal is on. Also is DCD. */

         hb_retl( HB_TRUE );
      }
      else
      {
         s_PortData[ iPort ].dwError = GetLastError();

         hb_storl( HB_FALSE, 2 );
         hb_storl( HB_FALSE, 3 );
         hb_storl( HB_FALSE, 4 );
         hb_storl( HB_FALSE, 5 );

         hb_retl( HB_FALSE );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMPURGE )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DWORD dwFlags;

      dwFlags = ( hb_parl( 2 ) ? PURGE_RXCLEAR : 0 ) | ( hb_parl( 3 ) ? PURGE_TXCLEAR : 0 );
      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_PURGECOMM;
      s_PortData[ iPort ].dwError = 0;
      if( PurgeComm( hCommPort, dwFlags ) )
         hb_retl( HB_TRUE );
      else
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMQUEUESTATUS )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DWORD dwErrors = 0;
      COMSTAT ComStat;

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_CLEARCOMMERROR;
      s_PortData[ iPort ].dwError = 0;
      if( ClearCommError( hCommPort, &dwErrors, &ComStat ) )
      {
         hb_storl( ComStat.fCtsHold, 2 );
         hb_storl( ComStat.fDsrHold, 3 );
         hb_storl( ComStat.fRlsdHold, 4 );
         hb_storl( ComStat.fXoffHold, 5 );
         hb_storl( ComStat.fXoffSent, 6 );
         hb_stornl( ComStat.cbInQue, 7 );
         hb_stornl( ComStat.cbOutQue, 8 ); /* This value will be zero for a nonoverlapped write */

         hb_retl( HB_TRUE );
      }
      else
      {
         s_PortData[ iPort ].dwError = GetLastError();

         hb_storl( HB_FALSE, 2 );
         hb_storl( HB_FALSE, 3 );
         hb_storl( HB_FALSE, 4 );
         hb_storl( HB_FALSE, 5 );
         hb_storl( HB_FALSE, 6 );
         hb_stornl( 0, 7 );
         hb_stornl( 0, 8 );

         hb_retl( HB_FALSE );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

/* If handshaking is enabled, it is an error for the application to adjust the line by
   using the EscapeCommFunction function */

HB_FUNC( WIN_COMSETRTS )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DWORD dwFunc = hb_parl( 2 ) ? SETRTS : CLRRTS;

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_ESCAPECOMMFUNCTION;
      s_PortData[ iPort ].dwError = 0;
      if( EscapeCommFunction( hCommPort, dwFunc ) )
         hb_retl( HB_TRUE );
      else
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

/* If handshaking is enabled, it is an error for the application to adjust the line by
   using the EscapeCommFunction function */

HB_FUNC( WIN_COMSETDTR )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DWORD dwFunc = hb_parl( 2 ) ? SETDTR : CLRDTR;

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_ESCAPECOMMFUNCTION;
      s_PortData[ iPort ].dwError = 0;
      if( EscapeCommFunction( hCommPort, dwFunc ) )
         hb_retl( HB_TRUE );
      else
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMRTSFLOW )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DCB CurDCB;
      int iRtsControl = hb_parni( 2 );

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      CurDCB.DCBlength = sizeof( DCB );
      if( ! GetCommState( hCommPort, &CurDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
         return;
      }

      if( iRtsControl == RTS_CONTROL_DISABLE )
      {
         CurDCB.fOutxCtsFlow = 0;
         CurDCB.fRtsControl = RTS_CONTROL_DISABLE;
      }
      else if( iRtsControl == RTS_CONTROL_ENABLE )
      {
         CurDCB.fOutxCtsFlow = 1;
         CurDCB.fRtsControl = RTS_CONTROL_ENABLE;
      }
      else if( iRtsControl == RTS_CONTROL_HANDSHAKE )
      {
         CurDCB.fOutxCtsFlow = 1;
         CurDCB.fRtsControl = RTS_CONTROL_HANDSHAKE;
      }
      else    /* RTS_CONTROL_TOGGLE - RS485? */
      {
         hb_retl( HB_FALSE );
         return;
      }

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_SETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      if( ! SetCommState( hCommPort, &CurDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
      else
         hb_retl( HB_TRUE );
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMDTRFLOW )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DCB CurDCB;
      int DtrControl = hb_parni( 2 );

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      CurDCB.DCBlength = sizeof( DCB );
      if( ! GetCommState( hCommPort, &CurDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
         return;
      }

      if( DtrControl == DTR_CONTROL_DISABLE )
      {
         CurDCB.fOutxDsrFlow = 0;
         CurDCB.fDtrControl = DTR_CONTROL_DISABLE;
      }
      else if( DtrControl == DTR_CONTROL_ENABLE )
      {
         CurDCB.fOutxDsrFlow = 1;
         CurDCB.fDtrControl = DTR_CONTROL_ENABLE;
      }
      else if( DtrControl == DTR_CONTROL_HANDSHAKE )
      {
         CurDCB.fOutxDsrFlow = 1;
         CurDCB.fDtrControl = DTR_CONTROL_HANDSHAKE;
      }
      else
      {
         hb_retl( HB_FALSE );
         return;
      }

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_SETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      if( ! SetCommState( hCommPort, &CurDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
      else
         hb_retl( HB_TRUE );
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMXONXOFFFLOW )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DCB CurDCB;

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      CurDCB.DCBlength = sizeof( DCB );
      if( ! GetCommState( hCommPort, &CurDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
         return;
      }

      if( hb_parl( 2 ) )
      {
         CurDCB.fInX = 1;
         CurDCB.fOutX = 1;
      }
      else
      {
         CurDCB.fInX = 0;
         CurDCB.fOutX = 0;
      }

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_SETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      if( ! SetCommState( hCommPort, &CurDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
      else
         hb_retl( HB_TRUE );
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

static int hb_win_ComSetTimeouts( HANDLE hCommPort, LPCOMMTIMEOUTS Timeouts, DWORD dwBaudRate, int iParity, int iByteSize, int iStopBits )
{
   COMMTIMEOUTS NewTimeouts;

   /* Maximum time, in milliseconds, allowed to elapse between the arrival of two characters on
      the communications line. During a ReadFile operation, the time period begins when the first
      character is received. If the interval between the arrival of any two characters exceeds this
      amount, the ReadFile operation is completed and any buffered data is returned. A value of zero
      indicates that interval time-outs are not used. */

   /* A value of MAXDWORD, combined with zero values for both the ReadTotalTimeoutConstant and
      ReadTotalTimeoutMultiplier members, specifies that the read operation is to return
      immediately with the characters that have already been received, even if no characters
      have been received. */
   NewTimeouts.ReadIntervalTimeout = ( Timeouts->ReadIntervalTimeout == ( DWORD ) -1 ? MAXDWORD : Timeouts->ReadIntervalTimeout );

   /* Multiplier, in milliseconds, used to calculate the total time-out period for read operations.
      For each read operation, this value is multiplied by the requested number of bytes to be read. */
   NewTimeouts.ReadTotalTimeoutMultiplier = ( Timeouts->ReadTotalTimeoutMultiplier == ( DWORD ) -1 ? 0 : Timeouts->ReadTotalTimeoutMultiplier );

   /* Constant, in milliseconds, used to calculate the total time-out period for read operations.
      For each read operation, this value is added to the product of the ReadTotalTimeoutMultiplier
      member and the requested number of bytes. */
   NewTimeouts.ReadTotalTimeoutConstant = ( Timeouts->ReadTotalTimeoutConstant == ( DWORD ) -1 ? 0 : Timeouts->ReadTotalTimeoutConstant );

   /* A value of zero for both the ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant members
      indicates that total time-outs are not used for read operations ...
      and MAXDWORD, 0 and 0 are what we use by default */

   /* Multiplier, in milliseconds, used to calculate the total time-out period for write operations.
      For each write operation, this value is multiplied by the number of bytes to be written. */
   if( Timeouts->WriteTotalTimeoutMultiplier == ( DWORD ) -1 )
   {
      /* float of 1.0 makes whole expression float */
      NewTimeouts.WriteTotalTimeoutMultiplier = HB_MIN( 1, ( DWORD ) ( ( 1.0 / dwBaudRate ) *
          ( iByteSize + 1 + ( iParity == NOPARITY ? 0 : 1 ) + ( iStopBits == ONESTOPBIT ? 1 : iStopBits == ONE5STOPBITS ? 1.5 : 2 ) ) * 1000 ) );
   }
   /* Constant, in milliseconds, used to calculate the total time-out period for write operations.
      For each write operation, this value is added to the product of the WriteTotalTimeoutMultiplier member and the number of bytes to be written. */
   else
      NewTimeouts.WriteTotalTimeoutMultiplier = Timeouts->WriteTotalTimeoutMultiplier;

   /* 50 ms is a thumbsuck - seems long enough and not too long! */
   NewTimeouts.WriteTotalTimeoutConstant = Timeouts->WriteTotalTimeoutConstant == ( DWORD ) -1 ? 50 : Timeouts->WriteTotalTimeoutConstant;

   /* A value of zero for both the WriteTotalTimeoutMultiplier and WriteTotalTimeoutConstant members
      indicates that total time-outs are not used for write operations ...
      and if flow control is enabled the program will "hang" or if it is not enabled the data will
      be lost (potentially), so we set a minimum of 1ms (baud rates higher than 4800) */

   return SetCommTimeouts( hCommPort, &NewTimeouts );
}

HB_FUNC( WIN_COMSETTIMEOUTS )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      DCB CurDCB;
      COMMTIMEOUTS Timeouts;

      Timeouts.ReadIntervalTimeout         = ( DWORD ) hb_parnldef( 2, -1 );
      Timeouts.ReadTotalTimeoutMultiplier  = ( DWORD ) hb_parnldef( 3, -1 );
      Timeouts.ReadTotalTimeoutConstant    = ( DWORD ) hb_parnldef( 4, -1 );
      Timeouts.WriteTotalTimeoutMultiplier = ( DWORD ) hb_parnldef( 5, -1 );
      Timeouts.WriteTotalTimeoutConstant   = ( DWORD ) hb_parnldef( 6, -1 );

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;

      CurDCB.DCBlength = sizeof( DCB );
      if( ! GetCommState( hCommPort, &CurDCB ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
         return;
      }

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_SETCOMMTIMEOUTS;
      s_PortData[ iPort ].dwError = 0;
      if( ! hb_win_ComSetTimeouts( hCommPort, &Timeouts, CurDCB.BaudRate, CurDCB.Parity, CurDCB.ByteSize, CurDCB.StopBits ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
      else
         hb_retl( HB_TRUE );
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMSETQUEUESIZE )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE && HB_ISNUM( 2 ) && HB_ISNUM( 3 ) )
   {
      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_SETUPCOMM;
      s_PortData[ iPort ].dwError = 0;
      if( ! SetupComm( hCommPort, hb_parni( 2 ), hb_parni( 3 ) ) )
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retl( HB_FALSE );
      }
      else
         hb_retl( HB_TRUE );
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMISVALID )
{
   int iPort = hb_parni( 1 );

   hb_retl( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) );
}

HB_FUNC( WIN_COMERRORCLEAR )
{
   int iPort = hb_parni( 1 );

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) )
   {
      s_PortData[ iPort ].dwError = 0;
      s_PortData[ iPort ].iFunction = 0;
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMERROR )
{
   int iPort = hb_parni( 1 );

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) )
      hb_retnl( s_PortData[ iPort ].dwError );
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMFUNCLAST )
{
   int iPort = hb_parni( 1 );

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) )
      hb_retni( s_PortData[ iPort ].iFunction );
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_FUNC( WIN_COMDEBUGDCB )
{
   int iPort = hb_parni( 1 );
   HANDLE hCommPort;

   if( iPort >= 0 && iPort < ( int ) HB_SIZEOFARRAY( s_PortData ) && ( hCommPort = s_PortData[ iPort ].hPort ) != INVALID_HANDLE_VALUE )
   {
      int iDebugLevel = hb_parnidef( 2, HB_WIN_COM_DBGBASIC );
      DCB CurDCB;
      COMMTIMEOUTS CurCOMMTIMEOUTS;
      COMMPROP CurCOMMPROP;
      char szDebugString[ 1024 ] = "";
      char buffer[ 80 ];

      s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMSTATE;
      s_PortData[ iPort ].dwError = 0;
      CurDCB.DCBlength = sizeof( DCB );
      if( GetCommState( hCommPort, &CurDCB ) )
      {
         if( iDebugLevel & HB_WIN_COM_DBGBASIC )
         {
            hb_snprintf( buffer, sizeof( buffer ), "Baud     : %lu\n", CurDCB.BaudRate ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "ByteSize : %i\n" , CurDCB.ByteSize ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "Parity   : %i\n" , CurDCB.Parity   ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "StopBits : %i\n" , CurDCB.StopBits ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
         }
         if( iDebugLevel & HB_WIN_COM_DBGFLOW )
         {
            hb_strncat( szDebugString, "fRtsControl : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fRtsControl == RTS_CONTROL_DISABLE ? "RTS_CONTROL_DISABLE\n" :
                                       CurDCB.fRtsControl == RTS_CONTROL_ENABLE ? "RTS_CONTROL_ENABLE\n" :
                                       CurDCB.fRtsControl == RTS_CONTROL_HANDSHAKE ? "RTS_CONTROL_HANDSHAKE\n" : "RTS_CONTROL_TOGGLE\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fOutxCtsFlow : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fOutxCtsFlow ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fOutX : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fOutX ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fInX : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fInX ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fDtrControl : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fDtrControl == DTR_CONTROL_DISABLE ? "DTR_CONTROL_DISABLE\n" :
                                       CurDCB.fDtrControl == DTR_CONTROL_ENABLE ? "DTR_CONTROL_ENABLE\n" : "DTR_CONTROL_HANDSHAKE\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fOutxDsrFlow : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fOutxDsrFlow ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
         }
         if( iDebugLevel & HB_WIN_COM_DBGXTRAFLOW )
         {
            hb_strncat( szDebugString, "fDsrSensitivity : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fDsrSensitivity ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fTXContinueOnXoff : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fTXContinueOnXoff ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "XonLim : %i\n"    , CurDCB.XonLim   ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "XoffLim : %i\n"   , CurDCB.XoffLim  ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "XonChar : 0x%i\n" , CurDCB.XonChar  ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "XoffChar : 0x%i\n", CurDCB.XoffChar ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
         }
         if( iDebugLevel & HB_WIN_COM_DBGOTHER )
         {
            hb_strncat( szDebugString, "fBinary : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fBinary ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fParity : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fParity ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fErrorChar : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fErrorChar ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fNull : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fNull ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, "fAbortOnError : ", sizeof( szDebugString ) - 1 );
            hb_strncat( szDebugString, CurDCB.fAbortOnError ? "true\n" : "false\n", sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "ErrorChar : 0x%i\n", CurDCB.ErrorChar ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "EofChar : 0x%i\n"  , CurDCB.EofChar   ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "EvtChar : 0x%i\n"  , CurDCB.EvtChar   ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
         }
      }
      else
      {
         s_PortData[ iPort ].dwError = GetLastError();
         hb_retc_null();
         return;
      }

      if( iDebugLevel & HB_WIN_COM_DBGTIMEOUTS )
      {
         s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMTIMEOUTS;
         s_PortData[ iPort ].dwError = 0;
         if( GetCommTimeouts( hCommPort, &CurCOMMTIMEOUTS ) )
         {
            hb_snprintf( buffer, sizeof( buffer ), "ReadIntervalTimeout : %lu\n"        , CurCOMMTIMEOUTS.ReadIntervalTimeout         ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "ReadTotalTimeoutMultiplier : %ld\n" , CurCOMMTIMEOUTS.ReadTotalTimeoutMultiplier  ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "ReadTotalTimeoutConstant : %ld\n"   , CurCOMMTIMEOUTS.ReadTotalTimeoutConstant    ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "WriteTotalTimeoutMultiplier : %ld\n", CurCOMMTIMEOUTS.WriteTotalTimeoutMultiplier ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "WriteTotalTimeoutConstant : %ld\n"  , CurCOMMTIMEOUTS.WriteTotalTimeoutConstant   ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
         }
         else
         {
            s_PortData[ iPort ].dwError = GetLastError();
            hb_retc_null();
            return;
         }
      }

      if( iDebugLevel & HB_WIN_COM_DBGQUEUE )
      {
         s_PortData[ iPort ].iFunction = HB_WIN_COM_FUN_GETCOMMPROPERTIES;
         s_PortData[ iPort ].dwError = 0;
         if( GetCommProperties( hCommPort, &CurCOMMPROP ) )
         {
            hb_snprintf( buffer, sizeof( buffer ), "dwCurrentTxQueue : %lu\n", CurCOMMPROP.dwCurrentTxQueue ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
            hb_snprintf( buffer, sizeof( buffer ), "dwCurrentRxQueue : %lu\n", CurCOMMPROP.dwCurrentRxQueue ); hb_strncat( szDebugString, buffer, sizeof( szDebugString ) - 1 );
         }
         else
         {
            s_PortData[ iPort ].dwError = GetLastError();
            hb_retc_null();
            return;
         }
      }

      hb_retc( szDebugString );
   }
   else
      hb_errRT_BASE( EG_ARG, 2010, NULL, HB_ERR_FUNCNAME, HB_ERR_ARGS_BASEPARAMS );
}

HB_CALL_ON_STARTUP_BEGIN( _hb_wincom_init_ )
   hb_wincom_init();
HB_CALL_ON_STARTUP_END( _hb_wincom_init_ )

#if defined( HB_PRAGMA_STARTUP )
   #pragma startup _hb_wincom_init_
#elif defined( HB_DATASEG_STARTUP )
   #define HB_DATASEG_BODY  HB_DATASEG_FUNC( _hb_wincom_init_ )
   #include "hbiniseg.h"
#endif
