/*
 * Serial communication functions
 *
 * Copyright 2010 Przemyslaw Czerpak <druzus / at / priv.onet.pl>
 *
 * 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.
 *
 */

#ifndef _GNU_SOURCE
#  define _GNU_SOURCE
#endif

#include "hbapi.h"

#if defined( HB_OS_UNIX ) && ( ! defined( __WATCOMC__ ) || __WATCOMC__ > 1290 ) && \
    ! defined( HB_OS_SYMBIAN ) /* || defined( __DJGPP__ ) */
#  if defined( HB_OS_VXWORKS )
#     if ! defined( HB_HAS_SIOLIB )
#        define HB_HAS_SIOLIB
#     endif
#  else
#     if ! defined( HB_HAS_TERMIOS )
#        define HB_HAS_TERMIOS
#     endif
#  endif
#  if defined( HB_OS_SUNOS )
#     if ! defined( BSD_COMP )
#        define BSD_COMP
#     endif
#  endif
#elif defined( HB_OS_DOS ) && \
      ! defined( HB_HAS_PMCOM ) && ! defined( HB_HAS_DOSSRL )
#  define HB_HAS_DOSSRL
#endif


#if defined( HB_HAS_TERMIOS )
#  include <termios.h>
#  include <fcntl.h>
#  include <sys/ioctl.h>
#  include <unistd.h>
#  include <errno.h>
#  if defined( HB_OS_UNIX )
#     include <sys/time.h>
#     include <sys/types.h>
#     if ! defined( HB_HAS_POLL ) && ! defined( HB_NO_POLL ) && \
         defined( _POSIX_C_SOURCE ) && _POSIX_C_SOURCE >= 200112L
         /* use poll() instead of select() to avoid FD_SETSIZE (1024 in Linux)
            file handle limit */
#        define HB_HAS_POLL
#     endif
#     if defined( HB_HAS_POLL )
#        include <poll.h>
#     endif
#  endif
#  if defined( HB_OS_HPUX )
#     include <sys/modem.h>
#  endif
#elif defined( HB_HAS_SIOLIB )
#  include <sioLib.h>
#elif defined( HB_HAS_DOSSRL )
#  include "../../src/3rd/hbdossrl/serial.h"
#elif defined( HB_HAS_PMCOM )
#  include "../../src/3rd/hbpmcom/com.h"
#elif defined( HB_OS_WIN )
#  include <windows.h>
#  include "hbwinuni.h"
#elif defined( HB_OS_OS2 )
#  define INCL_BASE
#  define INCL_DOS
#  define INCL_DOSERROR
#  define INCL_DOSDEVICES
#  define INCL_DOSDEVIOCTL
#  include <os2.h>
#elif defined( HB_OS_LINUX ) && defined( __WATCOMC__ )
#  include <unistd.h>
#endif

#include "hbapifs.h"
#include "hbapiitm.h"
#include "hbapicom.h"
#include "hbvm.h"
#include "hbinit.h"
#include "hbdate.h"
#include "hbthread.h"

typedef struct
{
#if defined( HB_HAS_TERMIOS )
   HB_FHANDLE     fd;
#  if ! defined( HB_OS_UNIX )
   HB_MAXINT      rdtimeout;
#  endif
#elif defined( HB_OS_WIN )
   HANDLE         hComm;
   HB_MAXINT      rdtimeout;
   HB_MAXINT      wrtimeout;
#elif defined( HB_OS_OS2 )
   HFILE          hFile;
   USHORT         rdtimeout;
   USHORT         wrtimeout;
#endif
   int            status;
   int            error;
   int            oserr;
   int            port;
   char *         name;
   #if 0
   struct termios tio;
   #endif
}
HB_COM, * PHB_COM;

static HB_CRITICAL_NEW( s_comMtx );
#define HB_COM_LOCK()      do { hb_threadEnterCriticalSection( &s_comMtx )
#define HB_COM_UNLOCK()    hb_threadLeaveCriticalSection( &s_comMtx ); } while( 0 )

static HB_COM s_comList[ HB_COM_PORT_MAX ];

static void hb_comCloseAll( void )
{
   int iPort;

   for( iPort = 0; iPort < HB_COM_PORT_MAX; ++iPort )
   {
      if( s_comList[ iPort ].status & HB_COM_OPEN )
         hb_comClose( iPort + 1 );

      if( s_comList[ iPort ].name )
      {
         hb_xfree( s_comList[ iPort ].name );
         s_comList[ iPort ].name = NULL;
      }
   }
}

static void hb_comSetComError( PHB_COM pCom, int iError )
{
   pCom->error = iError;
   pCom->oserr = 0;
}

static PHB_COM hb_comGetPort( int iPort, int iStatus )
{
   if( iPort >= 1 && iPort <= HB_COM_PORT_MAX )
   {
      PHB_COM pCom = &s_comList[ iPort - 1 ];
      if( iStatus == HB_COM_ANY || ( iStatus & pCom->status ) != 0 )
         return pCom;
      if( iStatus & HB_COM_ENABLED )
         hb_comSetComError( pCom, HB_COM_ERR_WRONGPORT );
      else
         hb_comSetComError( pCom, HB_COM_ERR_CLOSED );
   }
   return NULL;
}

static const char * hb_comGetNameRaw( PHB_COM pCom, char * buffer, int size )
{
   const char * name = pCom->name;

   if( name == NULL )
   {
#if defined( HB_OS_UNIX )
#  if defined( HB_OS_SUNOS )
      hb_snprintf( buffer, size, "/dev/tty%c", pCom->port + 'a' - 1 );
#  elif defined( HB_OS_HPUX )
      hb_snprintf( buffer, size, "/dev/tty%dp0", pCom->port );
#  elif defined( HB_OS_AIX )
      hb_snprintf( buffer, size, "/dev/tty%d", pCom->port );
#  elif defined( HB_OS_MINIX )
      hb_snprintf( buffer, size, "/dev/tty%02d", pCom->port - 1 );
#  elif defined( HB_OS_IRIX )
      hb_snprintf( buffer, size, "/dev/ttyf%d", pCom->port );
#  elif defined( HB_OS_DIGITAL_UNIX )
      hb_snprintf( buffer, size, "/dev/ttyf%02d", pCom->port );
#  elif defined( HB_OS_DARWIN )
      hb_snprintf( buffer, size, "/dev/cuaa%d", pCom->port - 1 );
#  else /* defined( HB_OS_LINUX ) || defined( HB_OS_CYGWIN ) || ... */
      hb_snprintf( buffer, size, "/dev/ttyS%d", pCom->port - 1 );
#  endif
#elif defined( HB_OS_WIN_CE )
      hb_snprintf( buffer, size, "COM%d:", pCom->port );
#else
      if( hb_iswinnt() || hb_iswince() )
         hb_snprintf( buffer, size, "\\\\.\\COM%d", pCom->port );
      else
         hb_snprintf( buffer, size, "COM%d", pCom->port );
#endif
      name = buffer;
   }
   return name;
}

static const char * hb_comGetName( PHB_COM pCom, char * buffer, int size )
{
   const char * name;

   HB_COM_LOCK();
   name = hb_comGetNameRaw( pCom, buffer, size );
   if( name != buffer )
         name = hb_strncpy( buffer, name, size - 1 );
   HB_COM_UNLOCK();

   return name;
}

static int hb_comGetPortNum( const char * pszName )
{
   int iPort = 0;

#if defined( HB_OS_UNIX )
#  if defined( HB_OS_SUNOS )
   if( strncmp( pszName, "/dev/tty", 8 ) == 0 &&
       pszName[ 8 ] >= 'a' && pszName[ 9 ] == '\0' )
      iPort = pszName[ 8 ] - 'a' + 1;
#  else
   int iLen = 0;
#     if defined( HB_OS_HPUX ) || defined( HB_OS_AIX ) || defined( HB_OS_MINIX )
   if( strncmp( pszName, "/dev/tty", 8 ) == 0 )
      iLen = 8;
#     elif defined( HB_OS_IRIX ) || defined( HB_OS_DIGITAL_UNIX )
   if( strncmp( pszName, "/dev/ttyf", 9 ) == 0 )
      iLen = 9;
#     elif defined( HB_OS_DARWIN )
   if( strncmp( pszName, "/dev/cuaa", 9 ) == 0 )
      iLen = 9;
#     else /* defined( HB_OS_LINUX ) || defined( HB_OS_CYGWIN ) || ... */
   if( strncmp( pszName, "/dev/ttyS", 9 ) == 0 )
      iLen = 9;
#     endif
   if( iLen > 0 )
   {
      pszName += iLen;
      while( HB_ISDIGIT( *pszName ) )
         iPort = iPort * 10 + ( *pszName++ - '0' );

#     if ! defined( HB_OS_HPUX ) && \
         ! defined( HB_OS_AIX ) && \
         ! defined( HB_OS_IRIX ) && \
         ! defined( HB_OS_DIGITAL_UNIX )
      ++iPort;
#     endif

#     if defined( HB_OS_HPUX )
      if( strcmp( pszName, "p0" ) != 0 )
#     else
      if( *pszName != '\0' )
#     endif
         iPort = 0;
   }
#  endif
#else
   if( pszName[ 0 ] == '\\' && pszName[ 1 ] == '\\' &&
       pszName[ 2 ] == '.'  && pszName[ 3 ] == '\\' )
      pszName += 4;
   if( HB_TOUPPER( pszName[ 0 ] ) == 'C' &&
       HB_TOUPPER( pszName[ 1 ] ) == 'O' &&
       HB_TOUPPER( pszName[ 2 ] ) == 'M' &&
       pszName[ 3 ] >= '1' && pszName[ 3 ] <= '9' )
   {
      pszName += 3;
      while( HB_ISDIGIT( *pszName ) )
         iPort = iPort * ( 10 + *pszName++ - '0' );
      if( *pszName != '\0' )
         iPort = 0;
   }
#endif

   return iPort;
}

static HB_BOOL hb_comPortCmp( const char * pszDevName1, const char * pszDevName2 )
{
#if defined( HB_OS_UNIX )
   return strcmp( pszDevName1, pszDevName2 ) == 0;
#else
#  if defined( HB_OS_WIN )
   if( pszDevName1[ 0 ] == '\\' && pszDevName1[ 1 ] == '\\' &&
       pszDevName1[ 2 ] == '.'  && pszDevName1[ 3 ] == '\\' )
      pszDevName1 += 4;
   if( pszDevName2[ 0 ] == '\\' && pszDevName2[ 1 ] == '\\' &&
       pszDevName2[ 2 ] == '.'  && pszDevName2[ 3 ] == '\\' )
      pszDevName2 += 4;
#  endif
   return hb_stricmp( pszDevName1, pszDevName2 ) == 0;
#endif
}

int hb_comFindPort( const char * pszDevName, HB_BOOL fCreate )
{
   char buffer[ HB_COM_DEV_NAME_MAX ];
   PHB_COM pCom;
   int iPort;

   if( pszDevName == NULL || *pszDevName == '\0' )
      return 0;

   iPort = hb_comGetPortNum( pszDevName );
   HB_COM_LOCK();
   if( iPort > 0 )
   {
      pCom = hb_comGetPort( iPort, HB_COM_ANY );
      if( pCom == NULL ||
          ! hb_comPortCmp( hb_comGetNameRaw( pCom, buffer, sizeof( buffer ) ),
                           pszDevName ) )
         iPort = 0;
   }

   if( iPort == 0 )
   {
      int iPortFree = 0;

      for( iPort = HB_COM_PORT_MAX; iPort > 0; --iPort )
      {
         pCom = &s_comList[ iPort - 1 ];
         if( pCom->name == NULL )
         {
            if( iPortFree == 0 && iPort > 16 )
               iPortFree = iPort;
         }
         else if( hb_comPortCmp( pCom->name, pszDevName ) )
            break;
      }
#if defined( HB_OS_UNIX )
      if( iPort == 0 && fCreate && access( pszDevName, F_OK ) == 0 )
#else
      if( iPort == 0 && fCreate )
#endif
      {
         if( iPortFree != 0 )
            iPort = iPortFree;
         else
         {
            for( iPort = HB_COM_PORT_MAX; iPort > 0; --iPort )
            {
               pCom = &s_comList[ iPort - 1 ];
               if( ( pCom->status & HB_COM_OPEN ) == 0 )
               {
                  if( pCom->name )
                  {
                     hb_xfree( pCom->name );
                     pCom->name = NULL;
                  }
                  break;
               }
            }
         }
         if( iPort != 0 )
         {
            pCom = &s_comList[ iPort - 1 ];
            if( ! hb_comPortCmp( hb_comGetNameRaw( pCom, buffer, sizeof( buffer ) ),
                                 pszDevName ) )
               pCom->name = hb_strdup( pszDevName );
         }
      }
   }
   HB_COM_UNLOCK();

   return iPort;
}

const char * hb_comGetDevice( int iPort, char * buffer, int size )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ANY );
   const char * pszName = NULL;

   if( pCom )
   {
      if( buffer && size > 0 )
         pszName = hb_comGetName( pCom, buffer, size );
      else
         pszName = pCom->name;
   }

   return pszName;
}

int hb_comSetDevice( int iPort, const char * szDevName )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ANY );

   if( pCom )
   {
      HB_COM_LOCK();
      if( pCom->name )
         hb_xfree( pCom->name );
      pCom->name = szDevName && *szDevName ? hb_strdup( szDevName ) : NULL;
      HB_COM_UNLOCK();
   }

   return pCom ? 0 : -1;
}

HB_FHANDLE hb_comGetDeviceHandle( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ANY );
   HB_FHANDLE hFile = FS_ERROR;

   if( pCom )
   {
#if defined( HB_HAS_TERMIOS )
      hFile = pCom->fd;
#elif defined( HB_OS_WIN )
      hFile = ( HB_FHANDLE ) pCom->hComm;
#elif defined( HB_OS_OS2 )
      hFile = ( HB_FHANDLE ) pCom->hFile;
#endif
   }

   return hFile;
}

void hb_comSetError( int iPort, int iError )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ANY );

   if( pCom )
      pCom->error = iError;
}

int hb_comGetError( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ANY );

   return pCom ? pCom->error : HB_COM_ERR_WRONGPORT;
}

int hb_comGetOsError( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ANY );

   return pCom ? pCom->oserr : 0;
}

int hb_comLastNum( void )
{
   int iPort;

   for( iPort = HB_COM_PORT_MAX; iPort; --iPort )
   {
      if( s_comList[ iPort - 1 ].status & HB_COM_ENABLED )
         break;
   }
   return iPort;
}

#if defined( HB_HAS_TERMIOS )

#define HB_COM_IS_EINTR( pCom )  ( ( pCom )->oserr == EINTR )
#define HB_COM_IS_EBADF( pCom )  ( ( pCom )->oserr  == EBADF )
#define HB_COM_GETERROR()        ( errno )

#if defined( HB_OS_LINUX )
#  define HB_HAS_SELECT_TIMER
#endif

static void hb_comSetOsError( PHB_COM pCom, HB_BOOL fError )
{
   pCom->oserr = fError ? HB_COM_GETERROR() : 0;
   switch( pCom->oserr )
   {
      case 0:
         pCom->error = 0;
         break;
      case EIO:
         pCom->error = HB_COM_ERR_IO;
         break;
      case EPIPE:
         pCom->error = HB_COM_ERR_PIPE;
         break;
      case EBUSY:
         pCom->error = HB_COM_ERR_BUSY;
         break;
      case EAGAIN:
         pCom->error = HB_COM_ERR_TIMEOUT;
         break;
      case EACCES:
#if defined( ETXTBSY )
      case ETXTBSY:
#endif
#if defined( EPERM )
      case EPERM:
#endif
         pCom->error = HB_COM_ERR_ACCESS;
         break;
      case ENOTTY:
      case ENOENT:
#if defined( ENOTDIR )
      case ENOTDIR:
#endif
         pCom->error = HB_COM_ERR_NOCOM;
         break;
      default:
         pCom->error = HB_COM_ERR_OTHER;
         break;
   }
}

#if defined( HB_OS_UNIX )
static int hb_comCanRead( PHB_COM pCom, HB_MAXINT timeout )
{
   int iResult;

#if defined( HB_HAS_POLL )
   HB_MAXUINT timer = hb_timerInit( timeout );
   struct pollfd fds;

   fds.fd = pCom->fd;
   fds.events = POLLIN;
   fds.revents = 0;

   do
   {
      int tout = timeout < 0 || timeout > 1000 ? 1000 : ( int ) timeout;
      iResult = poll( &fds, 1, tout );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult > 0 && ( fds.revents & POLLIN ) == 0 )
      {
         if( ( fds.revents & ( POLLHUP | POLLNVAL | POLLERR ) ) != 0 )
         {
            if( fds.revents & POLLNVAL )
               pCom->fd = -1;
            hb_comSetComError( pCom, HB_COM_ERR_PIPE );
            iResult = -1;
            break;
         }
         iResult = 0;
      }
      else if( iResult == -1 && HB_COM_IS_EINTR( pCom ) )
         iResult = 0;
   }
   while( iResult == 0 && ( timeout = hb_timerTest( timeout, &timer ) ) != 0 &&
          hb_vmRequestQuery() == 0 );
#else /* ! HB_HAS_POLL */
   struct timeval tv;
   fd_set rfds;
#  if ! defined( HB_HAS_SELECT_TIMER )
   HB_MAXUINT timer = hb_timerInit( timeout );
#  else
   tv.tv_sec = ( long ) ( timeout / 1000 );
   tv.tv_usec = ( long ) ( timeout % 1000 ) * 1000;
#  endif

   for( ;; )
   {
      if( timeout < 0 )
      {
         tv.tv_sec = 1;
         tv.tv_usec = 0;
      }
#  if ! defined( HB_HAS_SELECT_TIMER )
      else
      {
         tv.tv_sec = ( long ) ( timeout / 1000 );
         tv.tv_usec = ( long ) ( timeout % 1000 ) * 1000;
      }
#  endif

      FD_ZERO( &rfds );
      FD_SET( pCom->fd, &rfds );
      iResult = select( ( int ) ( pCom->fd + 1 ), &rfds, NULL, NULL, &tv );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult > 0 && ! FD_ISSET( pCom->fd, &rfds ) )
         iResult = 0;
      else if( iResult == -1 && HB_COM_IS_EINTR( pCom ) )
         iResult = 0;
#  if defined( HB_HAS_SELECT_TIMER )
      if( iResult != 0 || timeout >= 0 || hb_vmRequestQuery() != 0 )
         break;
#  else
      if( iResult != 0 || ( timeout = hb_timerTest( timeout, &timer ) ) == 0 ||
          hb_vmRequestQuery() != 0 )
         break;
#  endif
   }
#endif /* ! HB_HAS_POLL */

   return iResult;
}

static int hb_comCanWrite( PHB_COM pCom, HB_MAXINT timeout )
{
   int iResult;

#if defined( HB_HAS_POLL )
   HB_MAXUINT timer = hb_timerInit( timeout );
   struct pollfd fds;

   fds.fd = pCom->fd;
   fds.events = POLLOUT;
   fds.revents = 0;

   do
   {
      int tout = timeout < 0 || timeout > 1000 ? 1000 : ( int ) timeout;
      iResult = poll( &fds, 1, tout );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult > 0 && ( fds.revents & POLLOUT ) == 0 )
      {
         if( ( fds.revents & ( POLLHUP | POLLNVAL | POLLERR ) ) != 0 )
         {
            if( fds.revents & POLLNVAL )
               pCom->fd = -1;
            hb_comSetComError( pCom, HB_COM_ERR_PIPE );
            iResult = -1;
            break;
         }
         iResult = 0;
      }
      else if( iResult == -1 && HB_COM_IS_EINTR( pCom ) )
         iResult = 0;
   }
   while( iResult == 0 && ( timeout = hb_timerTest( timeout, &timer ) ) != 0 &&
          hb_vmRequestQuery() == 0 );
#else /* ! HB_HAS_POLL */
   struct timeval tv;
   fd_set wfds;
#  if ! defined( HB_HAS_SELECT_TIMER )
   HB_MAXUINT timer = hb_timerInit( timeout );
#  else
   tv.tv_sec = ( long ) ( timeout / 1000 );
   tv.tv_usec = ( long ) ( timeout % 1000 ) * 1000;
#  endif

   for( ;; )
   {
      if( timeout < 0 )
      {
         tv.tv_sec = 1;
         tv.tv_usec = 0;
      }
#  if ! defined( HB_HAS_SELECT_TIMER )
      else
      {
         tv.tv_sec = ( long ) ( timeout / 1000 );
         tv.tv_usec = ( long ) ( timeout % 1000 ) * 1000;
      }
#  endif

      FD_ZERO( &wfds );
      FD_SET( pCom->fd, &wfds );
      iResult = select( ( int ) ( pCom->fd + 1 ), NULL, &wfds, NULL, &tv );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult > 0 && ! FD_ISSET( pCom->fd, &wfds ) )
         iResult = 0;
      else if( iResult == -1 && HB_COM_IS_EINTR( pCom ) )
         iResult = 0;
#  if defined( HB_HAS_SELECT_TIMER )
      if( iResult != 0 || timeout >= 0 || hb_vmRequestQuery() != 0 )
         break;
#  else
      if( iResult != 0 || ( timeout = hb_timerTest( timeout, &timer ) ) == 0 ||
          hb_vmRequestQuery() != 0 )
         break;
#  endif
   }
#endif /* ! HB_HAS_POLL */

   return iResult;
}
#endif

int hb_comInputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = 0;

   if( pCom )
   {
#if defined( TIOCINQ )
      int iResult = ioctl( pCom->fd, TIOCINQ, &iCount );
      if( iResult == -1 )
         iCount = 0;
      hb_comSetOsError( pCom, iResult == -1 );
#elif defined( FIONREAD ) && ! defined( HB_OS_CYGWIN )
      /* Cygwin sys/termios.h explicitly says that "TIOCINQ is
       * utilized instead of FIONREAD which has been occupied for
       * other purposes under CYGWIN", so don't give Cygwin
       * even a chance to hit this code path. */
      int iResult = ioctl( pCom->fd, FIONREAD, &iCount );
      if( iResult == -1 )
         iCount = 0;
      hb_comSetOsError( pCom, iResult == -1 );
#else
      int TODO_TIOCINQ;
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
#endif
   }
   else
      iCount = -1;

   return iCount;
}

int hb_comOutputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = 0;

   if( pCom )
   {
#if defined( TIOCOUTQ )
      int iResult = ioctl( pCom->fd, TIOCOUTQ, &iCount );
      if( iResult == -1 )
         iCount = 0;
      hb_comSetOsError( pCom, iResult == -1 );
#elif defined( FIONWRITE )
      int iResult = ioctl( pCom->fd, FIONWRITE, &iCount );
      if( iResult == -1 )
         iCount = 0;
      hb_comSetOsError( pCom, iResult == -1 );
#else
      int TODO_TIOCOUTQ;
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
#endif
   }
   else
      iCount = -1;

   return iCount;
}

int hb_comFlush( int iPort, int iType )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      switch( iType )
      {
         case HB_COM_IFLUSH:
            iResult = tcflush( pCom->fd, TCIFLUSH );
            hb_comSetOsError( pCom, iResult == -1 );
            break;
         case HB_COM_OFLUSH:
            iResult = tcflush( pCom->fd, TCOFLUSH );
            hb_comSetOsError( pCom, iResult == -1 );
            break;
         case HB_COM_IOFLUSH:
            iResult = tcflush( pCom->fd, TCIOFLUSH );
            hb_comSetOsError( pCom, iResult == -1 );
            break;
         default:
            iResult = -1;
            hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
            break;
      }
   }
   return iResult;
}

/*
   TIOCM_LE          DSR (data set ready/line enable)
   TIOCM_DTR         DTR (data terminal ready)
   TIOCM_RTS         RTS (request to send)
   TIOCM_ST          Secondary TXD (transmit)
   TIOCM_SR          Secondary RXD (receive)
   TIOCM_CTS         CTS (clear to send)
   TIOCM_CAR         DCD (data carrier detect)
   TIOCM_CD           see TIOCM_CAR
   TIOCM_RNG         RNG (ring)
   TIOCM_RI           see TIOCM_RNG
   TIOCM_DSR         DSR (data set ready)

   supported only by few platforms (i.e. newer Linux kernels >= 2.4)
   TIOCM_OUT1        OUT 1 (auxiliary user-designated output 2)
   TIOCM_OUT2        OUT 2 (auxiliary user-designated output 1)
   TIOCM_LOOP        LOOP (loopback mode)
 */

#ifdef HB_OS_LINUX
   /* hack for missing definitions in standard header files */
#  ifndef TIOCM_OUT1
#     define TIOCM_OUT1    0x2000
#  endif
#  ifndef TIOCM_OUT2
#     define TIOCM_OUT2    0x4000
#  endif
#  ifndef TIOCM_LOOP
#     define TIOCM_LOOP    0x8000
#  endif
#endif

int hb_comMCR( int iPort, int * piValue, int iClr, int iSet )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
   {
#if defined( TIOCMGET ) && defined( TIOCMSET )
      int iRawVal, iOldVal;

      iResult = ioctl( pCom->fd, TIOCMGET, &iRawVal );
      if( iResult == 0 )
      {
         if( iRawVal & TIOCM_DTR )
            iValue |= HB_COM_MCR_DTR;
         if( iRawVal & TIOCM_RTS )
            iValue |= HB_COM_MCR_RTS;
#ifdef TIOCM_OUT1
         if( iRawVal & TIOCM_OUT1 )
            iValue |= HB_COM_MCR_OUT1;
#endif
#ifdef TIOCM_OUT2
         if( iRawVal & TIOCM_OUT2 )
            iValue |= HB_COM_MCR_OUT2;
#endif
#ifdef TIOCM_LOOP
         if( iRawVal & TIOCM_LOOP )
            iValue |= HB_COM_MCR_LOOP;
#endif

         iOldVal = iRawVal;

         if( iSet & HB_COM_MCR_DTR )
            iRawVal |= TIOCM_DTR;
         else if( iClr & HB_COM_MCR_DTR )
            iRawVal &= ~TIOCM_DTR;

         if( iSet & HB_COM_MCR_RTS )
            iRawVal |= TIOCM_RTS;
         else if( iClr & HB_COM_MCR_RTS )
            iRawVal &= ~TIOCM_RTS;

#ifdef TIOCM_OUT1
         if( iSet & HB_COM_MCR_OUT1 )
            iRawVal |= TIOCM_OUT1;
         else if( iClr & HB_COM_MCR_OUT1 )
            iRawVal &= ~TIOCM_OUT1;
#endif
#ifdef TIOCM_OUT2
         if( iSet & HB_COM_MCR_OUT2 )
            iRawVal |= TIOCM_OUT2;
         else if( iClr & HB_COM_MCR_OUT2 )
            iRawVal &= ~TIOCM_OUT2;
#endif
#ifdef TIOCM_LOOP
         if( iSet & HB_COM_MCR_LOOP )
            iRawVal |= TIOCM_LOOP;
         else if( iClr & HB_COM_MCR_LOOP )
            iRawVal &= ~TIOCM_LOOP;
#endif

         if( iRawVal != iOldVal )
            iResult = ioctl( pCom->fd, TIOCMSET, &iRawVal );
      }
      hb_comSetOsError( pCom, iResult == -1 );
#else
      int TODO_TIOCMGET_MCR;
      HB_SYMBOL_UNUSED( iClr );
      HB_SYMBOL_UNUSED( iSet );
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
#endif
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comMSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
   {
#if defined( TIOCMGET ) && defined( TIOCMSET )
      int iRawVal;

      iResult = ioctl( pCom->fd, TIOCMGET, &iRawVal );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult == 0 )
      {
         if( iRawVal & TIOCM_CTS )
            iValue |= HB_COM_MSR_CTS;
         if( iRawVal & TIOCM_DSR )
            iValue |= HB_COM_MSR_DSR;
         if( iRawVal & TIOCM_RI )
            iValue |= HB_COM_MSR_RI;
         if( iRawVal & TIOCM_CD )
            iValue |= HB_COM_MSR_DCD;
      }
#else
      int TODO_TIOCMGET_MSR;
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
#endif
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comLSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
   {
#ifdef TIOCSERGETLSR
      iResult = ioctl( pCom->fd, TIOCSERGETLSR, &iValue );
      hb_comSetOsError( pCom, iResult == -1 );
#else
      /* NOTE: most of systems do not give access to the
       *       Line Status Register (LSR)
       */
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
#endif
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comSendBreak( int iPort, int iDurationInMilliSecs )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      /* NOTE: duration is implementation defined non portable extension
       *       we use 0 what means 'transmit zero-valued bits for at
       *       least 0.25 seconds, and not more that 0.5 seconds'
       */
      HB_SYMBOL_UNUSED( iDurationInMilliSecs );

      hb_vmUnlock();

      iResult = tcsendbreak( pCom->fd, 0 );
      hb_comSetOsError( pCom, iResult == -1 );

      hb_vmLock();
   }
   return iResult;
}

#if defined( CCTS_OFLOW ) && defined( CRTS_IFLOW )
   #define _HB_OCRTSCTS       CCTS_OFLOW
   #define _HB_ICRTSCTS       CRTS_IFLOW
#elif defined( CRTSCTS ) && defined( CRTSXOFF )
   #define _HB_OCRTSCTS       CRTSCTS
   #define _HB_ICRTSCTS       CRTSXOFF
#elif defined( CRTSCTS )
   #define _HB_OCRTSCTS       CRTSCTS
   #define _HB_ICRTSCTS       CRTSCTS
#elif defined( CNEW_RTSCTS )
   #define _HB_OCRTSCTS       CNEW_RTSCTS
   #define _HB_ICRTSCTS       CNEW_RTSCTS
#elif defined( CCTS_OFLOW )
   #define _HB_OCRTSCTS       CCTS_OFLOW
   #define _HB_ICRTSCTS       0
#elif defined( CRTS_IFLOW )
   #define _HB_OCRTSCTS       0
   #define _HB_ICRTSCTS       CCTS_IFLOW
#elif defined( CRTSXOFF )
   #define _HB_OCRTSCTS       0
   #define _HB_ICRTSCTS       CRTSXOFF
#else
   /* if you find compiler which does not support it then please check
    * if such flow control is supported by OS. If yes then check exact
    * value for this switch on given OS and define it only for given
    * compiler and OS
    */
   #if defined( HB_OS_LINUX ) && defined( __WATCOMC__ )
      #define _HB_OCRTSCTS    020000000000
      #define _HB_ICRTSCTS    020000000000
   #endif
#endif

int hb_comFlowControl( int iPort, int * piFlow, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
   {
      /* NOTE: there is no support for DTR/DSR so we cannot use
       *       DTR/DSR handshake instead of the RTS/CTS handshake
       *       BSD systems support MDMBUF flags which enable output
       *       flow control using CD (Carrier Detect) flag.
       *       In SunOS TIOCSSOFTCAR can be used to control CLOCAL flag.
       *       CLOCAL termios structure c_cflag can be used to enable CD
       *       flow control in most portable way.
       */
      struct termios tio;

      iResult = tcgetattr( pCom->fd, &tio );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult == 0 )
      {
         tcflag_t c_cflag = tio.c_cflag;
         tcflag_t c_iflag = tio.c_iflag;

#if defined( _HB_OCRTSCTS )
         if( ( tio.c_cflag & _HB_OCRTSCTS ) == _HB_OCRTSCTS )
            iValue |= HB_COM_FLOW_ORTSCTS;
         if( ( tio.c_cflag & _HB_ICRTSCTS ) == _HB_ICRTSCTS )
            iValue |= HB_COM_FLOW_IRTSCTS;

         if( iFlow >= 0 )
         {
            if( iFlow & HB_COM_FLOW_ORTSCTS )
               tio.c_cflag |= _HB_OCRTSCTS;
            else
               tio.c_cflag &= ~_HB_OCRTSCTS;
            if( iFlow & HB_COM_FLOW_IRTSCTS )
               tio.c_cflag |= _HB_ICRTSCTS;
            else
               tio.c_cflag &= ~_HB_ICRTSCTS;
         }
#else
         {
            int TODO_CRTSCTS;
         }
#endif

         if( ( tio.c_cflag & CLOCAL ) != CLOCAL )
            iValue |= HB_COM_FLOW_DCD;

         if( iFlow >= 0 )
         {
            if( iFlow & HB_COM_FLOW_DCD )
               tio.c_cflag &= ~CLOCAL;
            else
               tio.c_cflag |= CLOCAL;
         }


         if( ( tio.c_iflag & IXON ) == IXON )
            iValue |= HB_COM_FLOW_XON;
         if( ( tio.c_iflag & IXOFF ) == IXOFF )
            iValue |= HB_COM_FLOW_XOFF;

         if( iFlow >= 0 )
         {
            if( iFlow & HB_COM_FLOW_XON )
               tio.c_iflag |= IXON;
            else
               tio.c_iflag &= ~IXON;
            if( iFlow & HB_COM_FLOW_XOFF )
               tio.c_iflag |= IXOFF;
            else
               tio.c_iflag &= ~IXOFF;
         }

         if( c_cflag != tio.c_cflag || c_iflag != tio.c_iflag )
         {
            iResult = tcsetattr( pCom->fd, TCSANOW, &tio );
            hb_comSetOsError( pCom, iResult == -1 );
         }
      }
   }

   if( piFlow )
      *piFlow = iValue;

   return iResult;
}

int hb_comFlowSet( int iPort, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      /* NOTE: HB_COM_FL_SOFT is ignored, we assume that user chose
       *       correct hardware/software flow control type which is
       *       the same as set in terminal device parameters
       */
      if( iFlow & HB_COM_FL_OON )
         iResult = tcflow( pCom->fd, TCOON );
      else if( iFlow & HB_COM_FL_OOFF )
         iResult = tcflow( pCom->fd, TCOOFF );
      else
         iResult = 0;

      if( iFlow & HB_COM_FL_ION )
      {
         if( tcflow( pCom->fd, TCION ) == -1 )
            iResult = -1;
      }
      else if( iFlow & HB_COM_FL_IOFF )
      {
         if( tcflow( pCom->fd, TCIOFF ) == -1 )
            iResult = -1;
      }
      hb_comSetOsError( pCom, iResult == -1 );
   }

   return iResult;
}

int hb_comFlowChars( int iPort, int iXONchar, int iXOFFchar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      iResult = 0;
      if( iXONchar >= 0 || iXOFFchar >= 0 )
      {
         struct termios tio;

         iResult = tcgetattr( pCom->fd, &tio );
         if( iResult == 0 )
         {
            if( iXONchar >= 0 )
               tio.c_cc[ VSTART ] = iXONchar;
            if( iXOFFchar >= 0 )
               tio.c_cc[ VSTOP ] = iXOFFchar;
            iResult = tcsetattr( pCom->fd, TCSANOW, &tio );
         }
      }
      hb_comSetOsError( pCom, iResult == -1 );
   }
   return iResult;
}

#if ! defined( _POSIX_VDISABLE )
#  define _POSIX_VDISABLE  '\0'
#endif

int hb_comDiscardChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
#if defined( VDISCARD ) && defined( IEXTEN )
      struct termios tio;

      iResult = tcgetattr( pCom->fd, &tio );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult == 0 )
      {
         if( ( tio.c_lflag & IEXTEN ) != 0 &&
             tio.c_cc[ VDISCARD ] != _POSIX_VDISABLE )
            iResult = 1;

         if( iChar == -1 ? iResult != 0 :
             ( iResult == 0 || tio.c_cc[ VDISCARD ] != iChar ) )
         {
            if( iChar == -1 )
            {
               tio.c_lflag &= ~IEXTEN;
               tio.c_cc[ VDISCARD ] = _POSIX_VDISABLE;
            }
            else
            {
               tio.c_lflag |= IEXTEN;
               tio.c_cc[ VDISCARD ] = iChar;
#if defined( VLNEXT )
               tio.c_cc[ VLNEXT ] = _POSIX_VDISABLE;
#endif
            }
            if( tcsetattr( pCom->fd, TCSANOW, &tio ) == -1 )
            {
               hb_comSetOsError( pCom, HB_TRUE );
               iResult = -1;
            }
         }
      }
#else
      int TODO_VDISCARD;
      HB_SYMBOL_UNUSED( iChar );
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
#endif
   }

   return iResult;
}

int hb_comErrorChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      /* NOTE: there is no support for setting user defined error character
       */

      HB_SYMBOL_UNUSED( iChar );
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
   }

   return iResult;
}

int hb_comOutputState( int iPort )
{
   /* NOTE: checking HB_COM_TX_* output flow states is unsupported */
   int iResult = hb_comOutputCount( iPort );

   if( iResult == 0 )
      iResult = HB_COM_TX_EMPTY;
   else if( iResult > 0 )
      iResult = 0;

   return iResult;
}

int hb_comInputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      /* NOTE: checking HB_COM_RX_* input flow states is unsupported */
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
   }

   return iResult;
}

long hb_comSend( int iPort, const void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lSent = -1;

   if( pCom )
   {
      hb_vmUnlock();

#if defined( HB_OS_UNIX )
      if( timeout >= 0 )
      {
         lSent = hb_comCanWrite( pCom, timeout );
         if( lSent == 0 )
         {
            hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
            lSent = -1;
         }
      }
      else
         lSent = 0;
#else
      /* NOTE: write timeout is unsupported */
      HB_SYMBOL_UNUSED( timeout );
      lSent = 0;
#endif

      if( lSent >= 0 )
      {
         do
         {
            lSent = write( pCom->fd, data, len );
            hb_comSetOsError( pCom, lSent == -1 );
         }
         while( lSent == -1 && HB_COM_IS_EINTR( pCom ) && hb_vmRequestQuery() == 0 );
      }
      hb_vmLock();
   }

   return lSent;
}

long hb_comRecv( int iPort, void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lReceived = -1;

   if( pCom )
   {
      hb_vmUnlock();

#if defined( HB_OS_UNIX )
      if( timeout >= 0 )
      {
         lReceived = hb_comCanRead( pCom, timeout );
         if( lReceived == 0 )
         {
            hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
            lReceived = -1;
         }
      }
      else
         lReceived = 0;
#else
      if( timeout != pCom->rdtimeout )
      {
         /* TODO: implement timeout settings
          *          tio.c_cc[ VTIME ] = ( timeout + 50 ) / 100;
          *          tio.c_cc[ VMIN ]  = 0;
          *       in DJGPP builds
          */
      }
      lReceived = 0;
#endif

      if( lReceived >= 0 )
      {
         do
         {
            lReceived = read( pCom->fd, ( char * ) data, len );
            hb_comSetOsError( pCom, lReceived == -1 );
         }
         while( lReceived == -1 && HB_COM_IS_EINTR( pCom ) && hb_vmRequestQuery() == 0 );
      }
      hb_vmLock();
   }

   return lReceived;
}

int hb_comInit( int iPort, int iBaud, int iParity, int iSize, int iStop )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      struct termios tio;

      iResult = tcgetattr( pCom->fd, &tio );
      hb_comSetOsError( pCom, iResult == -1 );
      if( iResult == 0 )
      {
#if defined( __WATCOMC__ ) && __WATCOMC__ <= 1300
         /* cfmakeraw is declared only in header files
            but does not exist in CRTL in OpenWatcom 2.0 */
#elif defined( cfmakeraw ) || defined( HB_OS_LINUX )
         /* Raw input from device */
         cfmakeraw( &tio );
#endif
         tio.c_iflag &= ~( IGNBRK | IGNPAR | BRKINT | PARMRK | ISTRIP |
                           INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF );
         tio.c_oflag &= ~OPOST;
         tio.c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
         tio.c_cflag &= ~( CSIZE | PARENB );
         /* Enable the receiver and set local mode... */
         tio.c_cflag |= ( CLOCAL | CREAD );

         tio.c_cc[ VTIME ] = 0;  /* inter-character timer in 1/10 sec. */
#if 0
         tio.c_cc[ VMIN ]  = 0;  /* minimum number of characters for read */
#else
         /* workaround for bug in some Linux kernels (i.e. 3.13.0-64-generic
            *buntu) in which select() unconditionally accepts stdin for
            reading if c_cc[ VMIN ] = 0 [druzus] */
         tio.c_cc[ VMIN ] = 1;
#endif

         if( iBaud )
         {
            switch( iBaud )
            {
               case        0: iBaud =      B0; break;
               case       50: iBaud =     B50; break;
               case       75: iBaud =     B75; break;
               case      110: iBaud =    B110; break;
               case      150: iBaud =    B150; break;
               case      200: iBaud =    B200; break;
               case      300: iBaud =    B300; break;
               case      600: iBaud =    B600; break;
               case     1200: iBaud =   B1200; break;
               case     1800: iBaud =   B1800; break;
               case     2400: iBaud =   B2400; break;
               case     4800: iBaud =   B4800; break;
               case     9600: iBaud =   B9600; break;
               case    19200: iBaud =  B19200; break;
               case    38400: iBaud =  B38400; break;
#ifdef B57600
               case    57600: iBaud =  B57600; break;
#endif
#ifdef B115200
               case   115200: iBaud = B115200; break;
#endif
#ifdef B230400
               case   230400: iBaud = B230400; break;
#endif
#ifdef B460800
               case   460800: iBaud = B460800; break;
#endif
#ifdef B500000
               case   500000: iBaud = B500000; break;
#endif
#ifdef B576000
               case   576000: iBaud = B576000; break;
#endif
#ifdef B921600
               case   921600: iBaud = B921600; break;
#endif
#ifdef B1000000
               case  1000000: iBaud = B1000000; break;
#endif
               default:
                  iResult = -1;
            }
         }
         switch( iParity )
         {
            case 0:
            case 'N':
            case 'n':
               tio.c_cflag &= ~( PARENB | PARODD );
               tio.c_iflag &= ~INPCK;
               break;
            case 'E':
            case 'e':
               tio.c_cflag |= PARENB;
               tio.c_cflag &= ~PARODD;
               tio.c_iflag |= INPCK;
               break;
            case 'O':
            case 'o':
               tio.c_cflag |= PARENB | PARODD;
               tio.c_iflag |= INPCK;
               break;
#if defined( CMSPAR )
            case 'S':
            case 's':
               tio.c_cflag |= CMSPAR | PARENB;
               tio.c_cflag &= ~PARODD;
               tio.c_iflag |= INPCK;
               break;
            case 'M':
            case 'm':
               tio.c_cflag |= CMSPAR | PARENB | PARODD;
               tio.c_iflag |= INPCK;
               break;
#endif
            default:
               iResult = -1;
         }
         switch( iSize )
         {
            case 0:
            case 8: tio.c_cflag |= CS8; break;
            case 7: tio.c_cflag |= CS7; break;
            case 6: tio.c_cflag |= CS6; break;
            case 5: tio.c_cflag |= CS5; break;
            default:
               iResult = -1;
         }
         switch( iStop )
         {
            case 0:
            case 1: tio.c_cflag &= ~CSTOPB; break;
            case 2: tio.c_cflag |= CSTOPB; break;
            default:
               iResult = -1;
         }

         if( iResult == 0 )
         {
            if( iBaud )
            {
               cfsetispeed( &tio, iBaud );
               cfsetospeed( &tio, iBaud );
            }

            iResult = tcsetattr( pCom->fd, TCSAFLUSH, &tio );
#if ! defined( HB_OS_UNIX )
            if( iResult == 0 )
               pCom->rdtimeout = 0;
#endif
            hb_comSetOsError( pCom, iResult == -1 );
         }
         else
            hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
      }
   }

   return iResult;
}

int hb_comClose( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      hb_vmUnlock();
#if defined( TIOCNXCL )
      ioctl( pCom->fd, TIOCNXCL, 0 );
#endif
      do
      {
         iResult = close( pCom->fd );
         hb_comSetOsError( pCom, iResult == -1 );
      }
      while( iResult == -1 && HB_COM_IS_EINTR( pCom ) && hb_vmRequestQuery() == 0 );

      if( iResult != -1 || HB_COM_IS_EBADF( pCom ) )
      {
         pCom->fd = ( HB_FHANDLE ) FS_ERROR;
         pCom->status &= ~HB_COM_OPEN;
      }
      hb_vmLock();
   }

   return iResult;
}

int hb_comOpen( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ENABLED );
   int iResult = -1;

   if( pCom )
   {
      if( pCom->status & HB_COM_OPEN )
      {
         hb_comSetComError( pCom, HB_COM_ERR_ALREADYOPEN );
      }
      else
      {
         char buffer[ HB_COM_DEV_NAME_MAX ];
         const char * name = hb_comGetName( pCom, buffer, sizeof( buffer ) );

         hb_vmUnlock();

         pCom->fd = open( name, O_RDWR | O_NOCTTY );
         if( pCom->fd != -1 )
         {
#if defined( TIOCEXCL ) /* TIOCNXCL */
            iResult = ioctl( pCom->fd, TIOCEXCL, 0 );
            if( iResult != 0 )
            {
               close( pCom->fd );
               pCom->fd = -1;
               hb_comSetComError( pCom, HB_COM_ERR_BUSY );
            }
            else
#else
            iResult = 0;
#endif
            pCom->status |= HB_COM_OPEN;
         }
         hb_comSetOsError( pCom, iResult == -1 );

         hb_vmLock();
      }
   }

   return iResult;
}

/* end of HB_HAS_TERMIOS */

#elif defined( HB_OS_WIN )

static void hb_comSetOsError( PHB_COM pCom, BOOL fError )
{
   pCom->oserr = fError ? GetLastError() : 0;

   switch( pCom->oserr )
   {
      case 0:
         pCom->error = 0;
         break;
      case ERROR_TIMEOUT:
         pCom->error = HB_COM_ERR_TIMEOUT;
         break;
      case ERROR_ACCESS_DENIED:
      case ERROR_SHARING_VIOLATION:
         pCom->error = HB_COM_ERR_BUSY;
         break;
      case ERROR_FILE_NOT_FOUND:
      case ERROR_PATH_NOT_FOUND:
         pCom->error = HB_COM_ERR_NOCOM;
         break;
      default:
         pCom->error = HB_COM_ERR_OTHER;
         break;
   }
}

int hb_comInputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = 0;

   if( pCom )
   {
      COMSTAT comStat;

      if( ClearCommError( pCom->hComm, NULL, &comStat ) )
      {
         iCount = comStat.cbInQue;
         hb_comSetOsError( pCom, HB_FALSE );
      }
      else
         hb_comSetOsError( pCom, HB_TRUE );
   }
   else
      iCount = -1;

   return iCount;
}

int hb_comOutputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = 0;

   if( pCom )
   {
      COMSTAT comStat;

      if( ClearCommError( pCom->hComm, NULL, &comStat ) )
      {
         iCount = comStat.cbOutQue;
         hb_comSetOsError( pCom, HB_FALSE );
      }
      else
         hb_comSetOsError( pCom, HB_TRUE );
   }
   else
      iCount = -1;

   return iCount;
}

int hb_comFlush( int iPort, int iType )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;

   if( pCom )
   {
      switch( iType )
      {
         case HB_COM_IFLUSH:
            fResult = PurgeComm( pCom->hComm, PURGE_RXCLEAR );
            hb_comSetOsError( pCom, ! fResult );
            break;
         case HB_COM_OFLUSH:
            fResult = PurgeComm( pCom->hComm, PURGE_TXCLEAR );
            hb_comSetOsError( pCom, ! fResult );
            break;
         case HB_COM_IOFLUSH:
            fResult = PurgeComm( pCom->hComm, PURGE_TXCLEAR | PURGE_RXCLEAR );
            hb_comSetOsError( pCom, ! fResult );
            break;
         default:
            hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
            break;
      }
   }
   return fResult ? 0 : -1;
}

int hb_comMCR( int iPort, int * piValue, int iClr, int iSet )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;
   int iValue = 0;

   if( pCom )
   {
      if( iSet & HB_COM_MCR_DTR )
         fResult = EscapeCommFunction( pCom->hComm, SETDTR );
      else if( iClr & HB_COM_MCR_DTR )
         fResult = EscapeCommFunction( pCom->hComm, CLRDTR );

      if( iSet & HB_COM_MCR_RTS )
         fResult = EscapeCommFunction( pCom->hComm, SETRTS );
      else if( iClr & HB_COM_MCR_RTS )
         fResult = EscapeCommFunction( pCom->hComm, CLRRTS );

      /* MCR_OUT1, MCR_OUT2, MCR_LOOP and reading current state
       * is unsupported
       */
      hb_comSetOsError( pCom, ! fResult );
   }

   if( piValue )
      *piValue = iValue;

   return fResult ? 0 : -1;
}

int hb_comMSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;
   int iValue = 0;

   if( pCom )
   {
      DWORD dwModemStat = 0;

      fResult = GetCommModemStatus( pCom->hComm, &dwModemStat );
      if( fResult )
      {
         if( dwModemStat & MS_CTS_ON )
            iValue |= HB_COM_MSR_CTS;
         if( dwModemStat & MS_DSR_ON )
            iValue |= HB_COM_MSR_DSR;
         if( dwModemStat & MS_RING_ON )
            iValue |= HB_COM_MSR_RI;
         if( dwModemStat & MS_RLSD_ON )
            iValue |= HB_COM_MSR_DCD;

         /* MSR_DELTA_CTS, MSR_DELTA_DSR, MSR_TERI, MSR_DELTA_DCD
          * are unsupported
          */

      }
      hb_comSetOsError( pCom, ! fResult );
   }

   if( piValue )
      *piValue = iValue;

   return fResult ? 0 : -1;
}

int hb_comLSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;
   int iValue = 0;

   if( pCom )
   {
      DWORD dwErrors = 0;

      fResult = ClearCommError( pCom->hComm, &dwErrors, NULL );
      if( fResult )
      {
         if( dwErrors & CE_BREAK )
            iValue |= HB_COM_LSR_BREAK;
         if( dwErrors & CE_FRAME )
            iValue |= HB_COM_LSR_FRAMING_ERR;
         if( dwErrors & CE_OVERRUN )
            iValue |= HB_COM_LSR_OVERRUN_ERR;
         if( dwErrors & CE_RXPARITY )
            iValue |= HB_COM_LSR_PARITY_ERR;

         /* LSR_DATA_READY, LSR_TRANS_HOLD_EMPTY, LSR_TRANS_EMPTY
          * are unsupported
          */
      }
      hb_comSetOsError( pCom, ! fResult );
   }

   if( piValue )
      *piValue = iValue;

   return fResult ? 0 : -1;
}

int hb_comSendBreak( int iPort, int iDurationInMilliSecs )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;

   if( pCom )
   {
      hb_vmUnlock();

      fResult = SetCommBreak( pCom->hComm );
      if( fResult )
      {
         Sleep( iDurationInMilliSecs );
         fResult = ClearCommBreak( pCom->hComm );
      }
      hb_comSetOsError( pCom, ! fResult );

      hb_vmLock();
   }
   return fResult ? 0 : -1;
}

int hb_comFlowControl( int iPort, int * piFlow, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;
   int iValue = 0;

   if( pCom )
   {
      DCB dcb;

      dcb.DCBlength = sizeof( DCB );
      fResult = GetCommState( pCom->hComm, &dcb );
      if( fResult )
      {
         if( dcb.fRtsControl == RTS_CONTROL_HANDSHAKE )
            iValue |= HB_COM_FLOW_IRTSCTS;
         if( dcb.fOutxCtsFlow )
            iValue |= HB_COM_FLOW_ORTSCTS;

         if( dcb.fDtrControl == DTR_CONTROL_HANDSHAKE )
            iValue |= HB_COM_FLOW_IDTRDSR;
         if( dcb.fOutxDsrFlow )
            iValue |= HB_COM_FLOW_ODTRDSR;

         if( dcb.fInX )
            iValue |= HB_COM_FLOW_XOFF;
         if( dcb.fOutX )
            iValue |= HB_COM_FLOW_XON;

         if( iFlow >= 0 )
         {
            if( iFlow & HB_COM_FLOW_IRTSCTS )
               dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
            else if( dcb.fRtsControl == RTS_CONTROL_HANDSHAKE )
               dcb.fRtsControl = RTS_CONTROL_ENABLE;
            dcb.fOutxCtsFlow = ( iFlow & HB_COM_FLOW_ORTSCTS ) != 0;

            if( iFlow & HB_COM_FLOW_IDTRDSR )
               dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;
            else if( dcb.fDtrControl == DTR_CONTROL_HANDSHAKE )
               dcb.fDtrControl = DTR_CONTROL_ENABLE;
            dcb.fOutxDsrFlow = ( iFlow & HB_COM_FLOW_ODTRDSR ) != 0;

            dcb.fInX = ( iFlow & HB_COM_FLOW_XOFF ) != 0;
            dcb.fOutX = ( iFlow & HB_COM_FLOW_XON ) != 0;

            fResult = SetCommState( pCom->hComm, &dcb );
         }
      }
      hb_comSetOsError( pCom, ! fResult );
   }

   if( piFlow )
      *piFlow = iValue;

   return fResult ? 0 : -1;
}

int hb_comFlowSet( int iPort, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE, fNotSup = FALSE;

   if( pCom )
   {
      if( iFlow & HB_COM_FL_SOFT )
      {
         if( iFlow & HB_COM_FL_OOFF )
            fResult = EscapeCommFunction( pCom->hComm, SETXOFF );
         else if( iFlow & HB_COM_FL_OON )
            fResult = EscapeCommFunction( pCom->hComm, SETXON );
         else
            fNotSup = TRUE;
         hb_comSetOsError( pCom, ! fResult );
      }
      else if( iFlow & HB_COM_FL_RTSCTS )
      {
         if( iFlow & HB_COM_FL_IOFF )
            fResult = EscapeCommFunction( pCom->hComm, CLRRTS );
         else if( iFlow & HB_COM_FL_ION )
            fResult = EscapeCommFunction( pCom->hComm, SETRTS );
         else
            fNotSup = TRUE;
      }
      else if( iFlow & HB_COM_FL_DTRDSR )
      {
         if( iFlow & HB_COM_FL_IOFF )
            fResult = EscapeCommFunction( pCom->hComm, CLRDTR );
         else if( iFlow & HB_COM_FL_ION )
            fResult = EscapeCommFunction( pCom->hComm, SETDTR );
         else
            fNotSup = TRUE;
      }
      else
         fNotSup = TRUE;

      if( fNotSup )
         hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
      else
         hb_comSetOsError( pCom, ! fResult );
   }

   return fResult ? 0 : -1;
}

int hb_comFlowChars( int iPort, int iXONchar, int iXOFFchar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;

   if( pCom )
   {
      fResult = TRUE;
      if( iXONchar >= 0 || iXOFFchar >= 0 )
      {
         DCB dcb;

         dcb.DCBlength = sizeof( DCB );
         fResult = GetCommState( pCom->hComm, &dcb );
         if( fResult )
         {
            if( iXONchar >= 0 )
               dcb.XonChar = ( char ) iXONchar;
            if( iXOFFchar >= 0 )
               dcb.XoffChar = ( char ) iXOFFchar;
            fResult = SetCommState( pCom->hComm, &dcb );
         }
      }
      hb_comSetOsError( pCom, ! fResult );
   }
   return fResult ? 0 : -1;
}

int hb_comDiscardChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      /* NOTE: there is no support for setting user defined character
       * discarding input buffer
       */
      HB_SYMBOL_UNUSED( iChar );
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
   }

   return iResult;
}

int hb_comErrorChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;

   if( pCom )
   {
      DCB dcb;

      dcb.DCBlength = sizeof( DCB );
      fResult = GetCommState( pCom->hComm, &dcb );
      if( fResult )
      {
         if( iChar >= 0 )
         {
            dcb.fErrorChar = TRUE;
            dcb.ErrorChar = ( char ) iChar;
         }
         else
            dcb.fErrorChar = FALSE;
         fResult = SetCommState( pCom->hComm, &dcb );
      }
      hb_comSetOsError( pCom, ! fResult );
   }
   return fResult ? 0 : -1;
}

int hb_comOutputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;
   int iValue = 0;

   if( pCom )
   {
      COMSTAT comStat;

      fResult = ClearCommError( pCom->hComm, NULL, &comStat );
      if( fResult )
      {
         /* NOTE: HB_COM_TX_RFLUSH is unsupported */

         if( comStat.fCtsHold )
            iValue |= HB_COM_TX_CTS;
         if( comStat.fDsrHold )
            iValue |= HB_COM_TX_DSR;
         if( comStat.fRlsdHold )
            iValue |= HB_COM_TX_DCD;
         if( comStat.fXoffHold )
            iValue |= HB_COM_TX_XOFF;
         if( comStat.cbOutQue == 0 )
            iValue |= HB_COM_TX_EMPTY;
      }
      hb_comSetOsError( pCom, ! fResult );
   }

   return fResult ? iValue : -1;
}

int hb_comInputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;
   int iValue = 0;

   if( pCom )
   {
      COMSTAT comStat;

      fResult = ClearCommError( pCom->hComm, NULL, &comStat );
      if( fResult )
      {
         if( comStat.fXoffSent )
            iValue |= HB_COM_RX_XOFF;
      }
      hb_comSetOsError( pCom, ! fResult );
   }

   return fResult ? iValue : -1;
}

static BOOL hb_comSetTimeouts( PHB_COM pCom, HB_MAXINT rdtimeout,
                                             HB_MAXINT wrtimeout )
{
   COMMTIMEOUTS timeouts;
   BOOL fResult;

   if( rdtimeout == 0 )
   {
      timeouts.ReadIntervalTimeout = MAXDWORD;
      timeouts.ReadTotalTimeoutMultiplier = 0;
      timeouts.ReadTotalTimeoutConstant = 0;
   }
   else
   {
      timeouts.ReadIntervalTimeout = MAXDWORD;
      timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
      timeouts.ReadTotalTimeoutConstant = ( DWORD ) rdtimeout;
   }
   timeouts.WriteTotalTimeoutMultiplier = 0;
   timeouts.WriteTotalTimeoutConstant = ( DWORD ) HB_MAX( wrtimeout, 1 );

   fResult = SetCommTimeouts( pCom->hComm, &timeouts );
   if( fResult )
   {
      pCom->rdtimeout = rdtimeout;
      pCom->wrtimeout = wrtimeout;
   }

   return fResult;
}

long hb_comSend( int iPort, const void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lSent = -1;

   if( pCom )
   {
      hb_vmUnlock();

      if( timeout < 0 )
         timeout = 0;

      if( pCom->wrtimeout == timeout ||
          hb_comSetTimeouts( pCom, pCom->rdtimeout, timeout ) )
      {
         DWORD dwWritten = 0;
         BOOL fResult;

         fResult = WriteFile( pCom->hComm, data, ( DWORD ) len, &dwWritten, NULL );
         lSent = fResult ? ( long ) dwWritten : -1;
         if( lSent == 0 )
         {
            hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
            lSent = -1;
         }
         else
            hb_comSetOsError( pCom, ! fResult );
      }
      else
         hb_comSetOsError( pCom, HB_TRUE );

      hb_vmLock();
   }

   return lSent;
}

long hb_comRecv( int iPort, void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lReceived = -1;

   if( pCom )
   {
      hb_vmUnlock();

      if( timeout < 0 )
         timeout = 0;

      if( pCom->rdtimeout == timeout ||
          hb_comSetTimeouts( pCom, timeout, pCom->wrtimeout ) )
      {
         DWORD dwRead = 0;
         BOOL fResult;

         fResult = ReadFile( pCom->hComm, data, ( DWORD ) len, &dwRead, NULL );
         lReceived = fResult ? ( long ) dwRead : -1;
         if( lReceived == 0 )
         {
            hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
            lReceived = -1;
         }
         else
            hb_comSetOsError( pCom, ! fResult );
      }
      else
         hb_comSetOsError( pCom, HB_TRUE );

      hb_vmLock();
   }

   return lReceived;
}

int hb_comInit( int iPort, int iBaud, int iParity, int iSize, int iStop )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;

   if( pCom )
   {
      DCB dcb;

      dcb.DCBlength = sizeof( DCB );
      fResult = GetCommState( pCom->hComm, &dcb );
      if( fResult )
      {
         switch( iParity )
         {
            case 0:
            case 'N':
            case 'n':
               iParity = NOPARITY;
               break;
            case 'E':
            case 'e':
               iParity = EVENPARITY;
               break;
            case 'O':
            case 'o':
               iParity = ODDPARITY;
               break;
            case 'S':
            case 's':
               iParity = SPACEPARITY;
               break;
            case 'M':
            case 'm':
               iParity = MARKPARITY;
               break;
            default:
               fResult = FALSE;
         }
         switch( iSize )
         {
            case 0:
               iSize = 8;
            case 8:
            case 7:
            case 6:
            case 5:
               break;
            default:
               fResult = FALSE;
         }
         switch( iStop )
         {
            case 0:
            case 1: iStop = ONESTOPBIT; break;
            case 2: iStop = TWOSTOPBITS; break;
            default:
               fResult = FALSE;
         }
         if( fResult )
         {
            if( iBaud )
               dcb.BaudRate = ( DWORD ) iBaud;
            dcb.fBinary = 1;
            dcb.fParity = 0;
            dcb.fOutxCtsFlow = 0;
            dcb.fOutxDsrFlow = 0;
            dcb.fDtrControl = DTR_CONTROL_ENABLE;
            dcb.fDsrSensitivity = 0;
            dcb.fTXContinueOnXoff = 1;
            dcb.fOutX = 0;
            dcb.fInX = 0;
            dcb.fErrorChar = 0;
            dcb.fNull = 0;
            dcb.fRtsControl = RTS_CONTROL_ENABLE;
            dcb.fAbortOnError = 0;
          /*dcb.XonLim*/
          /*dcb.XoffLim*/
            dcb.ByteSize = ( BYTE ) iSize;
            dcb.Parity = ( BYTE ) iParity;
            dcb.StopBits = ( BYTE ) iStop;
          /*dcb.XonChar*/
          /*dcb.XoffChar*/
            dcb.ErrorChar = '?';
          /*dcb.EofChar*/
          /*dcb.EvtChar*/

            fResult = SetCommState( pCom->hComm, &dcb );
            if( fResult )
               fResult = hb_comSetTimeouts( pCom, 0, 0 );

            hb_comSetOsError( pCom, ! fResult );
         }
         else
            hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
      }
      else
         hb_comSetOsError( pCom, ! fResult );
   }

   return fResult ? 0 : -1;
}

int hb_comClose( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   BOOL fResult = FALSE;

   if( pCom )
   {
      hb_vmUnlock();
      #if 0
      FlushFileBuffers( pCom->hComm );
      #endif
      fResult = CloseHandle( pCom->hComm );
      pCom->hComm = INVALID_HANDLE_VALUE;
      pCom->status &= ~HB_COM_OPEN;
      hb_comSetOsError( pCom, ! fResult );
      hb_vmLock();
   }

   return fResult ? 0 : -1;
}

int hb_comOpen( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ENABLED );
   BOOL fResult = FALSE;

   if( pCom )
   {
      if( pCom->status & HB_COM_OPEN )
      {
         hb_comSetComError( pCom, HB_COM_ERR_ALREADYOPEN );
      }
      else
      {
         char buffer[ HB_COM_DEV_NAME_MAX ];
         const char * szName = hb_comGetName( pCom, buffer, sizeof( buffer ) );
         LPCTSTR lpName;
         LPTSTR lpFree;

         lpName = HB_FSNAMECONV( szName, &lpFree );

         hb_vmUnlock();

         pCom->hComm = CreateFile( lpName,
                                   GENERIC_READ | GENERIC_WRITE,
                                   0,
                                   NULL,
                                   OPEN_EXISTING,
                                   FILE_FLAG_NO_BUFFERING, NULL );
         if( pCom->hComm != INVALID_HANDLE_VALUE )
         {
            fResult = TRUE;
            pCom->status |= HB_COM_OPEN;
         }
         hb_comSetOsError( pCom, ! fResult );

         hb_vmLock();

         if( lpFree )
            hb_xfree( lpFree );
      }
   }

   return fResult ? 0 : -1;
}

/* end of HB_OS_WIN */

#elif defined( HB_OS_OS2 )

static void hb_comSetOsError( PHB_COM pCom, APIRET rc )
{
   pCom->oserr = rc;
   switch( pCom->oserr )
   {
      case NO_ERROR:
         pCom->error = 0;
         break;
      case ERROR_TIMEOUT:
         pCom->error = HB_COM_ERR_TIMEOUT;
         break;
      case ERROR_ACCESS_DENIED:
      case ERROR_SHARING_VIOLATION:
         pCom->error = HB_COM_ERR_BUSY;
         break;
      case ERROR_FILE_NOT_FOUND:
      case ERROR_PATH_NOT_FOUND:
         pCom->error = HB_COM_ERR_NOCOM;
         break;
      default:
         pCom->error = HB_COM_ERR_OTHER;
         break;
   }
}

int hb_comInputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = -1;

   if( pCom )
   {
      APIRET rc;
      RXQUEUE rxqueue;

      memset( &rxqueue, 0, sizeof( rxqueue ) );
      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETINQUECOUNT,
                        NULL, 0, NULL, &rxqueue, sizeof( RXQUEUE ), NULL );
      if( rc == NO_ERROR )
         iCount = rxqueue.cch;

      hb_comSetOsError( pCom, rc );
   }

   return iCount;
}

int hb_comOutputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = -1;

   if( pCom )
   {
      APIRET rc;
      RXQUEUE rxqueue;

      memset( &rxqueue, 0, sizeof( rxqueue ) );
      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETOUTQUECOUNT,
                        NULL, 0, NULL, &rxqueue, sizeof( RXQUEUE ), NULL );
      if( rc == NO_ERROR )
         iCount = rxqueue.cch;

      hb_comSetOsError( pCom, rc );
   }

   return iCount;
}

int hb_comFlush( int iPort, int iType )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE;

   if( pCom )
   {
      UCHAR cmdinfo = 0;
      USHORT reserved = 0;

      switch( iType )
      {
         case HB_COM_IFLUSH:
         case HB_COM_IOFLUSH:
            rc = DosDevIOCtl( pCom->hFile, IOCTL_GENERAL, DEV_FLUSHINPUT,
                              &cmdinfo, sizeof( cmdinfo ), NULL,
                              &reserved, sizeof( reserved ), NULL );
            if( iType == HB_COM_IFLUSH )
               break;
            cmdinfo = 0;
            reserved = 0;
         case HB_COM_OFLUSH:
            rc = DosDevIOCtl( pCom->hFile, IOCTL_GENERAL, DEV_FLUSHOUTPUT,
                              &cmdinfo, sizeof( cmdinfo ), NULL,
                              &reserved, sizeof( reserved ), NULL );
            break;
         default:
            rc = ERROR_INVALID_PARAMETER;
            break;
      }
   }
   hb_comSetOsError( pCom, rc );

   return ( rc == NO_ERROR ) ? 0 : -1;
}

int hb_comMCR( int iPort, int * piValue, int iClr, int iSet )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;
   int iValue = 0;

   if( pCom )
   {
      APIRET rc;
      UCHAR mcos = 0;

      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETMODEMOUTPUT,
                        NULL, 0, NULL, &mcos, sizeof( mcos ), NULL );
      if( rc == NO_ERROR )
      {
         MODEMSTATUS ms;

         /* MCR_OUT1, MCR_OUT2, MCR_LOOP are unsupported
          */

         if( mcos & DTR_ON )
            iValue |= HB_COM_MCR_DTR;
         if( mcos & RTS_ON )
            iValue |= HB_COM_MCR_RTS;

         ms.fbModemOn = 0x00;
         ms.fbModemOff = 0xFF;
         if( iSet & HB_COM_MCR_DTR )
            ms.fbModemOn |= DTR_ON;
         else if( iClr & HB_COM_MCR_DTR )
            ms.fbModemOff &= DTR_OFF;
         if( iSet & HB_COM_MCR_RTS )
            ms.fbModemOn |= RTS_ON;
         else if( iClr & HB_COM_MCR_RTS )
            ms.fbModemOff &= RTS_OFF;

         if( ms.fbModemOn != 0x00 || ms.fbModemOff != 0xFF )
         {
            USHORT comError = 0;
            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETMODEMCTRL,
                              &ms, sizeof( ms ), NULL,
                              &comError, sizeof( comError ), NULL );
         }
      }
      hb_comSetOsError( pCom, rc );
      if( rc == NO_ERROR )
         iResult = 0;
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comMSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;
   int iValue = 0;

   if( pCom )
   {
      APIRET rc;
      UCHAR mcis = 0;
      USHORT comEvent = 0;

      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETMODEMINPUT,
                        NULL, 0, NULL, &mcis, sizeof( mcis ), NULL );
      if( rc == NO_ERROR )
      {
         if( mcis & CTS_ON )
            iValue |= HB_COM_MSR_CTS;
         if( mcis & DSR_ON )
            iValue |= HB_COM_MSR_DSR;
         if( mcis & RI_ON )
            iValue |= HB_COM_MSR_RI;
         if( mcis & DCD_ON )
            iValue |= HB_COM_MSR_DCD;

         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETCOMMEVENT,
                           NULL, 0, NULL, &comEvent, sizeof( comEvent ), NULL );

         if( rc == NO_ERROR )
         {
            if( comEvent & CTS_CHANGED )
               iValue |= HB_COM_MSR_DELTA_CTS;
            if( comEvent & DSR_CHANGED )
               iValue |= HB_COM_MSR_DELTA_DSR;
            if( comEvent & DCD_CHANGED )
               iValue |= HB_COM_MSR_DELTA_DCD;
            if( comEvent & RI_DETECTED )
               iValue |= HB_COM_MSR_TERI;
            iResult = 0;
         }
      }
      hb_comSetOsError( pCom, rc );
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comLSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;
   int iValue = 0;

   if( pCom )
   {
      APIRET rc;
      USHORT comError = 0;

      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETCOMMERROR,
                        NULL, 0, NULL, &comError, sizeof( comError ), NULL );
      if( rc == NO_ERROR )
      {
         if( comError & ( RX_QUE_OVERRUN | RX_HARDWARE_OVERRUN ) )
            iValue |= HB_COM_LSR_OVERRUN_ERR;
         if( comError & PARITY_ERROR )
            iValue |= HB_COM_LSR_PARITY_ERR;
         if( comError & FRAMING_ERROR )
            iValue |= HB_COM_LSR_FRAMING_ERR;

         /* LSR_DATA_READY, LSR_TRANS_HOLD_EMPTY, LSR_TRANS_EMPTY, LSR_BREAK
          * are unsupported
          */
         iResult = 0;
      }
      hb_comSetOsError( pCom, rc );
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comSendBreak( int iPort, int iDurationInMilliSecs )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      APIRET rc;
      USHORT comError = 0;

      hb_vmUnlock();

      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETBREAKON,
                        NULL, 0, NULL, &comError, sizeof( comError ), NULL );
      if( rc == NO_ERROR )
      {
         DosSleep( iDurationInMilliSecs );
         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETBREAKOFF,
                           NULL, 0, NULL, &comError, sizeof( comError ), NULL );

         if( rc == NO_ERROR )
            iResult = 0;
      }
      hb_comSetOsError( pCom, rc );

      hb_vmLock();
   }
   return iResult;
}

/* some missing defines in bsedev.h */
#define MODE_RTS_MASK         ( MODE_RTS_CONTROL | MODE_RTS_HANDSHAKE )
#define MODE_DTR_MASK         ( MODE_DTR_CONTROL | MODE_RTS_HANDSHAKE )
#define MODE_FULL_DUPLEX      0x20

int hb_comFlowControl( int iPort, int *piFlow, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE;
   int iValue = 0;

   if( pCom )
   {
      DCBINFO dcb;

      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETDCBINFO,
                        NULL, 0, NULL, &dcb, sizeof( dcb ), NULL );
      if( rc == NO_ERROR )
      {
         if( ( dcb.fbFlowReplace & MODE_RTS_MASK ) == MODE_RTS_HANDSHAKE )
            iValue |= HB_COM_FLOW_IRTSCTS;

         if( dcb.fbCtlHndShake & MODE_CTS_HANDSHAKE )
            iValue |= HB_COM_FLOW_ORTSCTS;

         if( ( dcb.fbCtlHndShake & MODE_DTR_MASK ) == MODE_DTR_HANDSHAKE )
            iValue |= HB_COM_FLOW_IDTRDSR;

         if( dcb.fbCtlHndShake & MODE_DSR_HANDSHAKE )
            iValue |= HB_COM_FLOW_ODTRDSR;

         if( dcb.fbCtlHndShake & MODE_DCD_HANDSHAKE )
            iValue |= HB_COM_FLOW_DCD;

         if( dcb.fbFlowReplace & MODE_AUTO_TRANSMIT )
            iValue |= HB_COM_FLOW_XON;

         if( dcb.fbFlowReplace & MODE_AUTO_RECEIVE )
            iValue |= HB_COM_FLOW_XOFF;

         if( iFlow >= 0 )
         {
            if( iFlow & HB_COM_FLOW_IRTSCTS )
            {
               dcb.fbFlowReplace &= ~MODE_RTS_MASK;
               dcb.fbFlowReplace |= MODE_RTS_HANDSHAKE;
            }
            else
               dcb.fbFlowReplace &= ~MODE_RTS_HANDSHAKE;

            if( iFlow & HB_COM_FLOW_ORTSCTS )
               dcb.fbCtlHndShake |= MODE_CTS_HANDSHAKE;
            else
               dcb.fbCtlHndShake &= ~MODE_CTS_HANDSHAKE;

            if( iFlow & HB_COM_FLOW_IDTRDSR )
            {
               dcb.fbCtlHndShake &= ~MODE_DTR_MASK;
               dcb.fbCtlHndShake |= MODE_DTR_HANDSHAKE;
            }
            else
               dcb.fbCtlHndShake &= ~MODE_DTR_HANDSHAKE;

            if( iFlow & HB_COM_FLOW_ODTRDSR )
               dcb.fbCtlHndShake |= MODE_DSR_HANDSHAKE;
            else
               dcb.fbCtlHndShake &= ~MODE_DSR_HANDSHAKE;

            if( iFlow & HB_COM_FLOW_DCD )
               dcb.fbCtlHndShake |= MODE_DCD_HANDSHAKE;
            else
               dcb.fbCtlHndShake &= ~MODE_DCD_HANDSHAKE;

            if( iFlow & HB_COM_FLOW_XON )
               dcb.fbFlowReplace |= MODE_AUTO_TRANSMIT;
            else
               dcb.fbFlowReplace &= ~MODE_AUTO_TRANSMIT;

            if( iFlow & HB_COM_FLOW_XOFF )
               dcb.fbFlowReplace |= MODE_AUTO_RECEIVE;
            else
               dcb.fbFlowReplace &= ~MODE_AUTO_RECEIVE;

            dcb.fbCtlHndShake &= ~MODE_DSR_SENSITIVITY;
            dcb.fbFlowReplace |= MODE_FULL_DUPLEX;

            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETDCBINFO,
                              &dcb, sizeof( dcb ), NULL, NULL, 0, NULL );
         }
      }
      hb_comSetOsError( pCom, rc );
   }

   if( piFlow )
      *piFlow = iValue;

   return ( rc == NO_ERROR ) ? 0 : -1;
}

int hb_comFlowSet( int iPort, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE, fNotSup = FALSE;

   if( pCom )
   {
      if( iFlow & HB_COM_FL_SOFT )
      {
         if( iFlow & HB_COM_FL_OOFF )
            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_STOPTRANSMIT,
                              NULL, 0, NULL, NULL, 0, NULL );
         else if( iFlow & HB_COM_FL_OON )
            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_STARTTRANSMIT,
                              NULL, 0, NULL, NULL, 0, NULL );
         else
            fNotSup = TRUE;
      }
      else
         fNotSup = TRUE;

      if( fNotSup )
         hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
      else
         hb_comSetOsError( pCom, rc );
   }

   return ( rc == NO_ERROR ) ? 0 : -1;
}

int hb_comFlowChars( int iPort, int iXONchar, int iXOFFchar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE;

   if( pCom )
   {
      rc = NO_ERROR;
      if( iXONchar >= 0 || iXOFFchar >= 0 )
      {
         DCBINFO dcb;

         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETDCBINFO,
                           NULL, 0, NULL, &dcb, sizeof( dcb ), NULL );
         if( rc == NO_ERROR )
         {
            if( iXONchar >= 0 )
               dcb.bXONChar = ( BYTE ) iXONchar;
            if( iXOFFchar >= 0 )
               dcb.bXOFFChar = ( BYTE ) iXOFFchar;
            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETDCBINFO,
                              &dcb, sizeof( dcb ), NULL, NULL, 0, NULL );
         }
      }
      hb_comSetOsError( pCom, rc );
   }
   return ( rc == NO_ERROR ) ? 0 : -1;
}

int hb_comDiscardChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      /* NOTE: there is no support for setting user defined character
       * discarding input buffer
       */
      HB_SYMBOL_UNUSED( iChar );
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
   }

   return iResult;
}

int hb_comErrorChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE;

   if( pCom )
   {
      DCBINFO dcb;

      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETDCBINFO,
                        NULL, 0, NULL, &dcb, sizeof( dcb ), NULL );
      if( rc == NO_ERROR )
      {
         if( iChar >= 0 )
         {
            dcb.fbFlowReplace |= MODE_ERROR_CHAR;
            dcb.bErrorReplacementChar = ( BYTE ) iChar;
         }
         else
            dcb.fbFlowReplace &= ~MODE_ERROR_CHAR;
         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETDCBINFO,
                           &dcb, sizeof( dcb ), NULL, NULL, 0, NULL );
      }
      hb_comSetOsError( pCom, rc );
   }
   return ( rc == NO_ERROR ) ? 0 : -1;
}

int hb_comOutputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE;
   int iValue = 0;

   if( pCom )
   {
      UCHAR comStatus = 0;

      rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETCOMMSTATUS,
                        NULL, 0, NULL, &comStatus, sizeof( comStatus ), NULL );
      if( rc == NO_ERROR )
      {
         /* NOTE: HB_COM_TX_RFLUSH is unsupported */

         if( comStatus & TX_WAITING_FOR_CTS )
            iValue |= HB_COM_TX_CTS;
         if( comStatus & TX_WAITING_FOR_DSR )
            iValue |= HB_COM_TX_DSR;
         if( comStatus & TX_WAITING_FOR_DCD )
            iValue |= HB_COM_TX_DCD;
         if( comStatus & TX_WAITING_FOR_XON )
            iValue |= HB_COM_TX_XOFF;

         if( hb_comOutputCount( iPort ) == 0 )
            iValue |= HB_COM_TX_EMPTY;
      }

      hb_comSetOsError( pCom, rc );
   }

   return ( rc == NO_ERROR ) ? iValue : -1;
}

int hb_comInputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      /* TODO: checking HB_COM_RX_* input flow states is unsupported */
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );
   }

   return iResult;
}

long hb_comSend( int iPort, const void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lSent = -1;

   if( pCom )
   {
      APIRET rc = NO_ERROR;

      hb_vmUnlock();

      if( timeout < 0 )
         timeout = 0;
      else if( timeout > 0 )
      {
         timeout = ( timeout / 10 ) + 1;
         if( timeout > USHRT_MAX )
            timeout = USHRT_MAX;
      }

      if( pCom->wrtimeout != ( USHORT ) timeout )
      {
         DCBINFO dcb;

         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETDCBINFO,
                           NULL, 0, NULL, &dcb, sizeof( dcb ), NULL );
         if( rc == NO_ERROR )
         {
            dcb.fbTimeout &= ~MODE_NO_WRITE_TIMEOUT;
            /* dcb.usWriteTimeout is 0 based (0 = 0.01 sec.) and this is
             * the minimal write timeout what seems to be reasonable.
             */
            if( timeout )
               dcb.usWriteTimeout = ( USHORT ) ( timeout - 1 );
            else
               dcb.usWriteTimeout = 0;

            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETDCBINFO,
                              &dcb, sizeof( dcb ), NULL, NULL, 0, NULL );
            if( rc == NO_ERROR )
               pCom->wrtimeout = ( USHORT ) timeout;
         }
      }

      if( rc == NO_ERROR )
      {
         ULONG ulWritten = 0;

         rc = DosWrite( pCom->hFile, ( void * ) data, len, &ulWritten );
         if( rc == NO_ERROR )
            lSent = ( long ) ulWritten;
      }

      if( lSent == 0 )
      {
         hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
         lSent = -1;
      }
      else
         hb_comSetOsError( pCom, rc );

      hb_vmLock();
   }

   return lSent;
}

long hb_comRecv( int iPort, void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lReceived = -1;

   if( pCom )
   {
      APIRET rc = NO_ERROR;

      hb_vmUnlock();

      if( timeout < 0 )
         timeout = 0;
      else if( timeout > 0 )
      {
         timeout = ( timeout / 10 ) + 1;
         if( timeout > USHRT_MAX )
            timeout = USHRT_MAX;
      }

      if( pCom->rdtimeout != ( USHORT ) timeout )
      {
         DCBINFO dcb;

         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETDCBINFO,
                           NULL, 0, NULL, &dcb, sizeof( dcb ), NULL );
         if( rc == NO_ERROR )
         {
            dcb.fbTimeout &= ~( MODE_READ_TIMEOUT | MODE_NOWAIT_READ_TIMEOUT );
            if( timeout )
            {
               dcb.fbTimeout |= MODE_READ_TIMEOUT;
               dcb.usReadTimeout = ( USHORT ) ( timeout - 1 );
            }
            else
            {
               dcb.fbTimeout |= MODE_NOWAIT_READ_TIMEOUT;
               dcb.usReadTimeout = 0;
            }

            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETDCBINFO,
                              &dcb, sizeof( dcb ), NULL, NULL, 0, NULL );
            if( rc == NO_ERROR )
               pCom->rdtimeout = ( USHORT ) timeout;
         }
      }

      if( rc == NO_ERROR )
      {
         ULONG ulRead = 0;

         rc = DosRead( pCom->hFile, data, len, &ulRead );
         if( rc == NO_ERROR )
            lReceived = ( long ) ulRead;
      }

      if( lReceived == 0 )
      {
         hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
         lReceived = -1;
      }
      else
         hb_comSetOsError( pCom, rc );

      hb_vmLock();
   }

   return lReceived;
}

int hb_comInit( int iPort, int iBaud, int iParity, int iSize, int iStop )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE;

   if( pCom )
   {
      LINECONTROL lctrl;

      rc = NO_ERROR;
      switch( iSize )
      {
         case 0:
            iSize = 8;
         case 8:
         case 7:
         case 6:
         case 5:
            lctrl.bDataBits = iSize;
            break;
         default:
            rc = ERROR_INVALID_PARAMETER;
      }
      switch( iParity )
      {
         case 0:
         case 'N':
         case 'n':
            lctrl.bParity = 0; break;
         case 'O':
         case 'o':
            lctrl.bParity = 1; break;
         case 'E':
         case 'e':
            lctrl.bParity = 2; break;
         case 'M':
         case 'm':
            lctrl.bParity = 3; break;
         case 'S':
         case 's':
            lctrl.bParity = 4; break;
         default:
            rc = ERROR_INVALID_PARAMETER;
      }
      switch( iStop )
      {
         case 0:
         case 1: lctrl.bStopBits = 0; break;
         case 2: lctrl.bStopBits = 2; break;
         default:
            rc = ERROR_INVALID_PARAMETER;
      }
      lctrl.fTransBreak = 0;

      if( iBaud < 0 || iBaud > USHRT_MAX )
         rc = ERROR_INVALID_PARAMETER;

      if( rc == NO_ERROR )
      {
         if( iBaud )
         {
            USHORT baud = ( USHORT ) iBaud;

            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETBAUDRATE,
                              &baud, sizeof( baud ), NULL, NULL, 0L, NULL );
         }
      }
      if( rc == NO_ERROR )
         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETLINECTRL,
                           &lctrl, sizeof( lctrl ), NULL, NULL, 0L, NULL );

      if( rc == NO_ERROR )
      {
         DCBINFO dcb;

         rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_GETDCBINFO,
                           NULL, 0, NULL, &dcb, sizeof( dcb ), NULL );
         if( rc == NO_ERROR )
         {
            dcb.usWriteTimeout = 0;
            dcb.usReadTimeout  = 0;

            dcb.fbCtlHndShake = 0x00;
            dcb.fbFlowReplace = MODE_FULL_DUPLEX;
            dcb.fbTimeout &= ~MODE_NO_WRITE_TIMEOUT;
            dcb.fbTimeout |= MODE_NOWAIT_READ_TIMEOUT;

            rc = DosDevIOCtl( pCom->hFile, IOCTL_ASYNC, ASYNC_SETDCBINFO,
                              &dcb, sizeof( dcb ), NULL, NULL, 0, NULL );
            pCom->rdtimeout = pCom->wrtimeout = 0;
         }
      }
      hb_comSetOsError( pCom, rc );
   }

   return ( rc == NO_ERROR ) ? 0 : -1;
}

int hb_comClose( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   APIRET rc = ERROR_INVALID_HANDLE;

   if( pCom )
   {
      hb_vmUnlock();
      #if 0
      DosResetBuffer( pCom->hFile );
      #endif
      rc = DosClose( pCom->hFile );
      pCom->hFile = 0;
      pCom->status &= ~HB_COM_OPEN;
      hb_comSetOsError( pCom, rc );
      hb_vmLock();
   }

   return ( rc == NO_ERROR ) ? 0 : -1;
}

int hb_comOpen( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ENABLED );
   APIRET rc = ERROR_INVALID_HANDLE;

   if( pCom )
   {
      if( pCom->status & HB_COM_OPEN )
      {
         hb_comSetComError( pCom, HB_COM_ERR_ALREADYOPEN );
      }
      else
      {
         char buffer[ HB_COM_DEV_NAME_MAX ];
         const char * pszName = hb_comGetName( pCom, buffer, sizeof( buffer ) );
         ULONG ulAction = 0;
         HB_FHANDLE hFile;


         hb_vmUnlock();

         rc = hb_fsOS2DosOpen( pszName,
                               &hFile,
                               &ulAction,
                               0L,
                               FILE_NORMAL,
                               OPEN_ACTION_OPEN_IF_EXISTS,
                               OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE );
         if( rc == NO_ERROR )
         {
            pCom->hFile = ( HFILE ) hFile;
            pCom->status |= HB_COM_OPEN;
         }

         hb_comSetOsError( pCom, rc );

         hb_vmLock();
      }
   }

   return ( rc == NO_ERROR ) ? 0 : -1;
}

/* end of HB_OS_OS2 */

#elif defined( HB_HAS_DOSSRL )

static void hb_comSetOsError( PHB_COM pCom, int iError )
{
   pCom->oserr = iError;

   switch( iError )
   {
      case SER_SUCCESS:                   /* Function completed successfully */
         pCom->error = 0;
         break;
      case SER_ERR_NOT_OPEN:              /* The specified COM port is not opened */
         pCom->error = HB_COM_ERR_CLOSED;
         break;
      case SER_ERR_ALREADY_OPEN:          /* The specified COM port is already opened */
         pCom->error = HB_COM_ERR_ALREADYOPEN;
         break;
      case SER_ERR_NO_UART:               /* Could not find a UART for this COM port */
         pCom->error = HB_COM_ERR_NOCOM;
         break;
      case SER_ERR_INVALID_COMPORT:       /* User specified an invalid COM port */
         pCom->error = HB_COM_ERR_WRONGPORT;
         break;
      case SER_ERR_INVALID_BPS:           /* User specified an invalid BPS rate */
      case SER_ERR_INVALID_DATA_BITS:     /* User specified an invalid number of data bits */
      case SER_ERR_INVALID_PARITY:        /* User specified an invalid parity type */
      case SER_ERR_INVALID_STOP_BITS:     /* User specified an invalid number of stop bits */
      case SER_ERR_INVALID_HANDSHAKING:   /* User specified an invalid handshaking type */
      case SER_ERR_INVALID_FIFO_THRESHOLD:/* User specified an invalid fifo threshold value */
      case SER_ERR_NULL_PTR:              /* User specified a buffer address that was NULL */
         pCom->error = HB_COM_ERR_PARAMVALUE;
         break;
      case SER_ERR_INVALID_BASE:          /* User specified an invalid base address */
      case SER_ERR_INVALID_IRQ:           /* User specified an invalid IRQ number */
         pCom->error = HB_COM_ERR_PARAMVALUE;
         break;
      case SER_ERR_IRQ_NOT_FOUND:         /* Could not find an IRQ for the specified COM port */
      case SER_ERR_LOCK_MEM:              /* Could not lock memory in DPMI mode */
      case SER_ERR_UNKNOWN:               /* An unknown error occurred */
      default:
         pCom->error = iError < 0 ? HB_COM_ERR_OTHER : 0;
   }
}

int hb_comInputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = -1;

   if( pCom )
   {
      iCount = serial_get_rx_buffered( iPort - 1 );
      hb_comSetOsError( pCom, iCount );
      if( iCount < 0 )
         iCount = -1;
   }

   return iCount;
}

int hb_comOutputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = -1;

   if( pCom )
   {
      iCount = serial_get_tx_buffered( iPort - 1 );
      hb_comSetOsError( pCom, iCount );
      if( iCount < 0 )
         iCount = -1;
   }

   return iCount;
}

int hb_comFlush( int iPort, int iType )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      switch( iType )
      {
         case HB_COM_IFLUSH:
         case HB_COM_IOFLUSH:
            iResult = serial_clear_rx_buffer( iPort - 1 );
            if( iType == HB_COM_IFLUSH || iResult != SER_SUCCESS )
               break;
            break;
         case HB_COM_OFLUSH:
            iResult = serial_clear_tx_buffer( iPort - 1 );
            break;
         default:
            hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
            return -1;
      }

      hb_comSetOsError( pCom, iResult );
      if( iResult < 0 )
         iResult = -1;
   }

   return iResult;
}

int hb_comMCR( int iPort, int * piValue, int iClr, int iSet )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;
   int iValue = 0;

   if( pCom )
   {
      iResult = serial_get_mcr( iPort - 1 );
      if( iResult >= 0 )
      {
         int iNewVal = ( iResult & ~iClr ) | iSet;
         iValue = iResult;
         iResult = iValue == iNewVal ? 0 : serial_set_mcr( iPort - 1, iNewVal );
      }
      hb_comSetOsError( pCom, iResult );
      if( iResult < 0 )
         iResult = -1;
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comMSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;
   int iValue = 0;

   if( pCom )
   {
      iValue = serial_get_msr( iPort - 1 );
      hb_comSetOsError( pCom, iValue );
      if( iValue < 0 )
      {
         iResult = -1;
         iValue = 0;
      }
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comLSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
   {
      iValue = serial_get_lsr( iPort - 1 );
      hb_comSetOsError( pCom, iValue );
      if( iValue < 0 )
      {
         iResult = -1;
         iValue = 0;
      }
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comSendBreak( int iPort, int iDurationInMilliSecs )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iDurationInMilliSecs );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlowControl( int iPort, int *piFlow, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
   {
      iResult = serial_get_handshaking( iPort - 1 );

      if( iResult >= 0 )
      {
         switch( iResult )
         {
            case SER_HANDSHAKING_XONXOFF:
               iValue |= HB_COM_FLOW_XOFF | HB_COM_FLOW_XOFF;
               break;
            case SER_HANDSHAKING_RTSCTS:
               iValue |= HB_COM_FLOW_IRTSCTS | HB_COM_FLOW_ORTSCTS;
               break;
            case SER_HANDSHAKING_DTRDSR:
               iValue |= HB_COM_FLOW_IDTRDSR | HB_COM_FLOW_ODTRDSR;
               break;
            case SER_HANDSHAKING_NONE:
               break;
         }
         if( iFlow >= 0 )
         {
            int iFlowVal = 0;

            if( iFlow & ( HB_COM_FLOW_IRTSCTS | HB_COM_FLOW_ORTSCTS ) )
               iFlowVal = SER_HANDSHAKING_RTSCTS;
            else if( iFlow & ( HB_COM_FLOW_IDTRDSR | HB_COM_FLOW_ODTRDSR ) )
               iFlowVal = SER_HANDSHAKING_DTRDSR;
            else if( iFlow & ( HB_COM_FLOW_XON | HB_COM_FLOW_XOFF ) )
               iFlowVal = SER_HANDSHAKING_XONXOFF;

            if( iFlowVal != iResult )
               iResult = serial_set_handshaking( iPort - 1, iFlowVal );
         }
      }
      hb_comSetOsError( pCom, iResult );
      if( iResult < 0 )
         iResult = -1;
   }

   if( piFlow )
      *piFlow = iValue;

   return iResult;
}

int hb_comFlowSet( int iPort, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iFlow );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlowChars( int iPort, int iXONchar, int iXOFFchar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   HB_SYMBOL_UNUSED( iXONchar );
   HB_SYMBOL_UNUSED( iXOFFchar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return iResult;
}

int hb_comDiscardChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iChar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comErrorChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iChar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comOutputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      iResult = serial_get_tx_buffered( iPort - 1 );
      hb_comSetOsError( pCom, iResult );
      if( iResult < 0 )
         iResult = -1;
      else if( iResult == 0 )
         iResult = HB_COM_TX_EMPTY;
      else
         iResult = 0;
   }

   return iResult;
}

int hb_comInputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return iResult;
}

long hb_comSend( int iPort, const void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lSent = -1;

   if( pCom )
   {
      const char * buffer = ( const char * ) data;
      HB_MAXUINT timer = hb_timerInit( timeout );

      hb_comSetOsError( pCom, SER_SUCCESS );
      lSent = 0;

      hb_vmUnlock();

      while( len > 0 )
      {
         int iSent;

         iSent = serial_write_buffered( iPort - 1, buffer, len );
         if( iSent < 0 )
         {
            hb_comSetOsError( pCom, iSent );
            if( lSent == 0 )
               lSent = -1;
            break;
         }

         lSent += iSent;
         buffer += iSent;
         len -= iSent;

         if( len > 0 )
         {
            if( ( timeout = hb_timerTest( timeout, &timer ) ) == 0 )
            {
               if( lSent == 0 )
               {
                  hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
                  lSent = -1;
               }
               break;
            }
            hb_releaseCPU();
         }
      }

      hb_vmLock();
   }

   return lSent;
}

long hb_comRecv( int iPort, void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lReceived = -1;

   if( pCom )
   {
      char * buffer = ( char * ) data;
      HB_MAXUINT timer = hb_timerInit( timeout );

      hb_comSetOsError( pCom, SER_SUCCESS );
      lReceived = 0;

      hb_vmUnlock();

      while( len > 0 )
      {
         int iRecv = serial_read( iPort - 1, buffer, len );

         if( iRecv < 0 )
         {
            hb_comSetOsError( pCom, iRecv );
            if( lReceived == 0 )
               lReceived = -1;
            break;
         }

         lReceived += iRecv;
         buffer += iRecv;
         len -= iRecv;

         if( lReceived > 0 || ( timeout = hb_timerTest( timeout, &timer ) ) == 0 )
         {
            if( lReceived == 0 )
            {
               hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
               lReceived = -1;
            }
            break;
         }
         hb_releaseCPU();
      }

      hb_vmLock();
   }

   return lReceived;
}

static int s_comChkPortParam( int *piBaud, int *piParity,
                              int *piSize, int *piStop )
{
   int iResult = 0;

   if( *piBaud == 0 )
      *piBaud = 9600;

   *piParity = HB_TOLOWER( *piParity );
   switch( *piParity )
   {
      case 0:
         *piParity = 'n';
      case 'n':
      case 'e':
      case 'o':
      case 's':
      case 'm':
         break;

      default:
         iResult = -1;
   }

   switch( *piSize )
   {
      case 0:
         *piSize = 8;
      case 8:
      case 7:
      case 6:
      case 5:
         break;
      default:
         iResult = -1;
   }

   switch( *piStop )
   {
      case 0:
         *piStop = 1;
      case 1:
      case 2:
         break;
      default:
         iResult = -1;
   }

   return iResult;
}

int hb_comInit( int iPort, int iBaud, int iParity, int iSize, int iStop )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      iResult = s_comChkPortParam( &iBaud, &iParity, &iSize, &iStop );
      if( iResult == 0 )
      {
         iResult = serial_set_bps( iPort - 1, iBaud );
         if( iResult == SER_SUCCESS )
            iResult = serial_set_data( iPort - 1, iSize );
         if( iResult == SER_SUCCESS )
            iResult = serial_set_parity( iPort - 1, iParity );
         if( iResult == SER_SUCCESS )
            iResult = serial_set_stop( iPort - 1, iStop );
         hb_comSetOsError( pCom, iResult );
         if( iResult < 0 )
            iResult = -1;
      }
      else
         hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
   }

   return iResult;
}

int hb_comClose( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      hb_vmUnlock();
      iResult = serial_close( iPort - 1 );
      pCom->status &= ~HB_COM_OPEN;
      hb_comSetOsError( pCom, iResult );
      if( iResult < 0 )
         iResult = -1;
      hb_vmLock();
   }

   return iResult;
}

int hb_comOpen( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ENABLED );
   int iResult = -1;

   if( pCom )
   {
      if( pCom->status & HB_COM_OPEN )
      {
         hb_comSetComError( pCom, HB_COM_ERR_ALREADYOPEN );
      }
      else
      {
         int iBaud, iParity, iSize, iStop, iFlowControl;

         hb_vmUnlock();

         iBaud = iParity = iSize = iStop = 0;
         iFlowControl = SER_HANDSHAKING_NONE;
         s_comChkPortParam( &iBaud, &iParity, &iSize, &iStop );
         iResult = serial_open( iPort - 1, iBaud, iSize, iParity, iStop,
                                iFlowControl );
         if( iResult == 0 )
         {
            pCom->status |= HB_COM_OPEN;
            hb_comSetOsError( pCom, 0 );
         }
         else
         {
            serial_close( iPort - 1 );
            hb_comSetOsError( pCom, iResult );
            iResult = -1;
         }

         hb_vmLock();
      }
   }

   return iResult;
}

/* end of HB_HAS_DOSSRL */

#elif defined( HB_HAS_PMCOM )

static void hb_comSetOsError( PHB_COM pCom, int iError )
{
   pCom->oserr = iError;

   switch( iError )
   {
      case COMERR_NOCHIP:
         pCom->error = HB_COM_ERR_WRONGPORT;
         break;
      case COMERR_RXOVERFLOW:
      case COMERR_NOMEMORY:
      case COMERR_GENERAL:
      default:
         pCom->error = HB_COM_ERR_OTHER;
   }
}

int hb_comInputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = -1;

   if( pCom )
      iCount = COMTXBufferUsed( iPort - 1 );

   return iCount;
}

int hb_comOutputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iCount = -1;

   if( pCom )
      iCount = COMRXBufferUsed( iPort - 1 );

   return iCount;
}

int hb_comFlush( int iPort, int iType )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      iResult = 0;
      hb_comSetOsError( pCom, 0 );
      switch( iType )
      {
         case HB_COM_IFLUSH:
         case HB_COM_IOFLUSH:
            COMClearRXBuffer( iPort - 1 );
            if( iType == HB_COM_IFLUSH )
               break;
            break;
         case HB_COM_OFLUSH:
            COMClearTXBuffer( iPort - 1 );
            break;
         default:
            iResult = -1;
            hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
            break;
      }
   }

   return iResult;
}

int hb_comMCR( int iPort, int * piValue, int iClr, int iSet )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;
   int iValue = 0;

   if( pCom )
   {
      /* MCR_OUT1, MCR_OUT2, MCR_LOOP and reading current state
       * is unsupported
       */
      if( iSet & HB_COM_MCR_DTR )
         COMSetDtr( iPort - 1, 1 );
      else if( iClr & HB_COM_MCR_DTR )
         COMSetDtr( iPort - 1, 0 );

      if( iSet & HB_COM_MCR_RTS )
         COMSetRts( iPort - 1, 1 );
      else if( iClr & HB_COM_MCR_RTS )
         COMSetRts( iPort - 1, 0 );

      iResult = 0;
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comMSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;
   int iValue = 0;

   if( pCom )
   {
      int iMSR = COMGetModemStatus( iPort - 1 );

      if( iMSR & DELTA_CTS )
         iValue |= HB_COM_MSR_DELTA_CTS;
      if( iMSR & DELTA_DSR )
         iValue |= HB_COM_MSR_DELTA_DSR;
      if( iMSR & DELTA_RI )
         iValue |= HB_COM_MSR_TERI;
      if( iMSR & DELTA_CD )
         iValue |= HB_COM_MSR_DELTA_DCD;

      if( iMSR & CTS_LINE )
         iValue |= HB_COM_MSR_CTS;
      if( iMSR & DSR_LINE )
         iValue |= HB_COM_MSR_DSR;
      if( iMSR & RI_LINE )
         iValue |= HB_COM_MSR_RI;
      if( iMSR & CD_LINE )
         iValue |= HB_COM_MSR_DCD;

      iResult = 0;
   }

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comLSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   if( piValue )
      *piValue = iValue;

   return iResult;
}

int hb_comSendBreak( int iPort, int iDurationInMilliSecs )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iDurationInMilliSecs );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlowControl( int iPort, int *piFlow, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1, iValue = 0;

   if( pCom )
   {
      int iFlowVal = COMGetFlowControl( iPort - 1 );

      if( iFlowVal & FLOW_RTS )
         iValue |= HB_COM_FLOW_IRTSCTS;
      if( iFlowVal & FLOW_CTS )
         iValue |= HB_COM_FLOW_ORTSCTS;
      if( iFlowVal & FLOW_DTR )
         iValue |= HB_COM_FLOW_IDTRDSR;
      if( iFlowVal & FLOW_DSR )
         iValue |= HB_COM_FLOW_ODTRDSR;
      if( iFlowVal & FLOW_DCD )
         iValue |= HB_COM_FLOW_DCD;
      if( iFlowVal & FLOW_XOFF )
         iValue |= HB_COM_FLOW_XOFF;
      if( iFlowVal & FLOW_XON )
         iValue |= HB_COM_FLOW_XON;

      if( iFlow >= 0 )
      {
         iFlowVal = 0;
         if( iFlow & HB_COM_FLOW_IRTSCTS )
            iFlowVal |= FLOW_RTS;
         if( iFlow & HB_COM_FLOW_ORTSCTS )
            iFlowVal |= FLOW_CTS;
         if( iFlow & HB_COM_FLOW_IDTRDSR )
            iFlowVal |= FLOW_DTR;
         if( iFlow & HB_COM_FLOW_ODTRDSR )
            iFlowVal |= FLOW_DSR;
         if( iFlow & HB_COM_FLOW_DCD )
            iFlowVal |= FLOW_DCD;
         if( iFlow & HB_COM_FLOW_XOFF )
            iFlowVal |= FLOW_XOFF;
         if( iFlow & HB_COM_FLOW_XON )
            iFlowVal |= FLOW_XON;

         COMSetFlowControl( iPort - 1, iFlowVal );
      }

      iResult = 0;
   }

   if( piFlow )
      *piFlow = iValue;

   return iResult;
}

int hb_comFlowSet( int iPort, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iFlow );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlowChars( int iPort, int iXONchar, int iXOFFchar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      COMSetFlowChars( iPort - 1, iXONchar, iXOFFchar );
      iResult = 0;
   }

   return iResult;
}

int hb_comDiscardChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iChar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comErrorChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iChar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comOutputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      iResult = COMRXBufferUsed( iPort - 1 );
      if( iResult == 0 )
         iResult = HB_COM_TX_EMPTY;
      else if( iResult > 0 )
         iResult = 0;
   }

   return iResult;
}

int hb_comInputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return iResult;
}

long hb_comSend( int iPort, const void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lSent = -1;

   if( pCom )
   {
      const char * buffer = ( const char * ) data;
      HB_MAXUINT timer = hb_timerInit( timeout );

      hb_comSetOsError( pCom, 0 );
      lSent = 0;

      hb_vmUnlock();

      while( len > 0 )
      {
         int iSent, iErr;

         iErr = COMWriteBuffer( iPort - 1, buffer, NULL, len, &iSent );
         lSent += iSent;
         if( iErr == COMERR_TXOVERFLOW )
         {
            buffer += iSent;
            len -= iSent;
            if( ( timeout = hb_timerTest( timeout, &timer ) ) == 0 )
            {
               if( lSent == 0 )
               {
                  hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
                  lSent = -1;
               }
               break;
            }
            hb_releaseCPU();
         }
         else
            break;
      }

      hb_vmLock();
   }

   return lSent;
}

long hb_comRecv( int iPort, void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   long lReceived = -1;

   if( pCom )
   {
      char * buffer = ( char * ) data;
      HB_MAXUINT timer = hb_timerInit( timeout );

      hb_comSetOsError( pCom, 0 );
      lReceived = 0;

      hb_vmUnlock();

      while( len > 0 )
      {
         int iErr = COMReadChar( iPort - 1, buffer, NULL );

         if( iErr == 0 )
         {
            ++buffer;
            --len;
            ++lReceived;
         }
         else if( iErr == COM_BUFEMPTY )
         {
            if( lReceived > 0 || ( timeout = hb_timerTest( timeout, &timer ) ) == 0 )
            {
               if( lReceived == 0 )
               {
                  hb_comSetComError( pCom, HB_COM_ERR_TIMEOUT );
                  lReceived = -1;
               }
               break;
            }
            hb_releaseCPU();
         }
         else
         {
            hb_comSetOsError( pCom, iErr );
            break;
         }
      }

      hb_vmLock();
   }

   return lReceived;
}

static int s_comChkPortParam( int *piBaud, int *piParity,
                              int *piSize, int *piStop )
{
   int iResult = 0;

   if( *piBaud == 0 )
      *piBaud = 9600;

   *piParity = HB_TOUPPER( *piParity );
   switch( *piParity )
   {
      case 0:
         *piParity = 'N';
      case 'N':
      case 'E':
      case 'O':
      case 'S':
      case 'M':
         break;

      default:
         iResult = -1;
   }

   switch( *piSize )
   {
      case 0:
         *piSize = 8;
      case 8:
      case 7:
      case 6:
      case 5:
         break;
      default:
         iResult = -1;
   }

   switch( *piStop )
   {
      case 0:
         *piStop = 1;
      case 1:
      case 2:
         break;
      default:
         iResult = -1;
   }

   return iResult;
}

int hb_comInit( int iPort, int iBaud, int iParity, int iSize, int iStop )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      iResult = s_comChkPortParam( &iBaud, &iParity, &iSize, &iStop );
      if( iResult == 0 )
      {
         COMSetTransmitParameters( iPort - 1, iBaud, iSize, iParity, iStop );
         hb_comSetOsError( pCom, 0 );
      }
      else
         hb_comSetComError( pCom, HB_COM_ERR_PARAMVALUE );
   }

   return iResult;
}

int hb_comClose( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );
   int iResult = -1;

   if( pCom )
   {
      hb_vmUnlock();
      COMPortClose( iPort - 1 );
      pCom->status &= ~HB_COM_OPEN;
      hb_comSetOsError( pCom, 0 );
      iResult = 0;
      hb_vmLock();
   }

   return iResult;
}

int hb_comOpen( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ENABLED );
   int iResult = -1;

   if( pCom )
   {
      if( pCom->status & HB_COM_OPEN )
      {
         hb_comSetComError( pCom, HB_COM_ERR_ALREADYOPEN );
      }
      else
      {
         int iBaud, iParity, iSize, iStop, iFlowControl;

         hb_vmUnlock();

         iBaud = iParity = iSize = iStop = 0;
         iFlowControl = 0;
         s_comChkPortParam( &iBaud, &iParity, &iSize, &iStop );
         iResult = COMPortOpen( iPort - 1, iBaud, iSize, iParity, iStop,
                                iFlowControl, NULL );
         if( iResult == 0 )
         {
            pCom->status |= HB_COM_OPEN;
            hb_comSetOsError( pCom, 0 );
         }
         else
         {
            hb_comSetOsError( pCom, iResult );
            iResult = -1;
         }

         hb_vmLock();
      }
   }

   return iResult;
}

/* end of HB_HAS_PMCOM */

#else

int hb_comInputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comOutputCount( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlush( int iPort, int iType )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iType );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comMCR( int iPort, int * piValue, int iClr, int iSet )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( piValue );
   HB_SYMBOL_UNUSED( iClr );
   HB_SYMBOL_UNUSED( iSet );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comMSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( piValue );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comLSR( int iPort, int * piValue )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( piValue );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comSendBreak( int iPort, int iDurationInMilliSecs )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iDurationInMilliSecs );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlowControl( int iPort, int *piFlow, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( piFlow );
   HB_SYMBOL_UNUSED( iFlow );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlowSet( int iPort, int iFlow )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iFlow );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comFlowChars( int iPort, int iXONchar, int iXOFFchar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iXONchar );
   HB_SYMBOL_UNUSED( iXOFFchar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comDiscardChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iChar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comErrorChar( int iPort, int iChar )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iChar );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comOutputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comInputState( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

long hb_comSend( int iPort, const void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( data );
   HB_SYMBOL_UNUSED( len );
   HB_SYMBOL_UNUSED( timeout );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

long hb_comRecv( int iPort, void * data, long len, HB_MAXINT timeout )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( data );
   HB_SYMBOL_UNUSED( len );
   HB_SYMBOL_UNUSED( timeout );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comInit( int iPort, int iBaud, int iParity, int iSize, int iStop )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   HB_SYMBOL_UNUSED( iBaud );
   HB_SYMBOL_UNUSED( iParity );
   HB_SYMBOL_UNUSED( iSize );
   HB_SYMBOL_UNUSED( iStop );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comClose( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_OPEN );

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

int hb_comOpen( int iPort )
{
   PHB_COM pCom = hb_comGetPort( iPort, HB_COM_ENABLED );
   int iTODO_serial_port_support;

   if( pCom )
      hb_comSetComError( pCom, HB_COM_ERR_NOSUPPORT );

   return -1;
}

#endif


static int s_iComInit = 0;

static void hb_com_exit( void* cargo )
{
   HB_SYMBOL_UNUSED( cargo );

   if( s_iComInit )
   {
      hb_comCloseAll();
      s_iComInit = 0;
   }
}

static void hb_com_init( void* cargo )
{
   HB_SYMBOL_UNUSED( cargo );

   if( ! s_iComInit )
   {
      int iPort;

      for( iPort = 0; iPort < HB_COM_PORT_MAX; ++iPort )
      {
         s_comList[ iPort ].port = iPort + 1;
         s_comList[ iPort ].status = HB_COM_ENABLED;
      }

      hb_vmAtQuit( hb_com_exit, NULL );

      s_iComInit = 1;
   }
}

HB_CALL_ON_STARTUP_BEGIN( _hb_com_init_ )
   hb_vmAtInit( hb_com_init, NULL );
HB_CALL_ON_STARTUP_END( _hb_com_init_ )

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