Logo Search packages:      
Sourcecode: hamlib version File versions  Download package

termios.c

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(WIN32) && !defined(HAVE_TERMIOS_H)

#undef DEBUG
#undef TRACE

#ifdef DEBUG
#define DEBUG_VERBOSE
#define DEBUG_ERRORS
#define report(a) fprintf(stderr,a)
#define report_warning(a) fprintf(stderr,a)
#define report_error(a) fprintf(stderr,a)
#else
#define report(a) do {} while (0)
#define report_warning(a) do {} while (0)
#define report_error(a) do {} while (0)
#endif /* DEBUG */
/*-------------------------------------------------------------------------
|   rxtx is a native interface to serial ports in java.
|   Copyright 1997-2002 by Trent Jarvi taj@www.linux.org.uk.
|   Copyright 1998-2002 by Wayne roberts wroberts1@home.com
|
|   This library is free software; you can redistribute it and/or
|   modify it under the terms of the GNU Library General Public
|   License as published by the Free Software Foundation; either
|   version 2 of the License, or (at your option) any later version.
|
|   If you compile this program with cygwin32 tools this package falls
|   under the GPL.  See COPYING.CYGNUS for details.
|
|   This library 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
|   Library General Public License for more details.
|
|   You should have received a copy of the GNU Library General Public
|   License along with this library; if not, write to the Free
|   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
|
| This file was taken from rxtx-2.1-7pre16 and adaptated for Hamlib.
--------------------------------------------------------------------------*/
#include <windows.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <limits.h>
#include "win32termios.h"

/*
 * odd malloc.h error with lcc compiler
 * winsock has FIONREAD with lcc
 */

#ifdef __LCC__
#     include <winsock.h>
#else
#     include <malloc.h>
#endif /* __LCC__ */

#define SIGIO 0

int my_errno;
extern int errno;
struct termios_list
{
      char filename[80];
      int my_errno;
      int interrupt;
      int event_flag;
      int tx_happened;
      unsigned long *hComm;
      struct termios *ttyset;
      struct serial_struct *sstruct;
      /* for DTR DSR */
      unsigned char MSR;
      struct async_struct *astruct;
      struct serial_icounter_struct *sis;
      int open_flags;
      OVERLAPPED rol;
      OVERLAPPED wol;
      OVERLAPPED sol;
      int fd;
      struct termios_list *next;
      struct termios_list *prev;
};
struct termios_list *first_tl = NULL;

/*----------------------------------------------------------
serial_test

   accept: filename to test     
   perform:     
   return:      1 on success 0 on failure
   exceptions:  
   win32api:    CreateFile CloseHandle
   comments:    if the file opens it should be ok.
----------------------------------------------------------*/
int win32_serial_test( char * filename )
{
      unsigned long *hcomm;
      int ret;
      hcomm = CreateFile( filename, GENERIC_READ |GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0 );
      if ( hcomm == INVALID_HANDLE_VALUE )
            ret = 0;
      else
            ret = 1;
      CloseHandle( hcomm );
      return(ret);
}

void termios_setflags( int fd, int termios_flags[] )
{
      struct termios_list *index = find_port( fd );
      int i, result;
      int windows_flags[11] = { 0, EV_RXCHAR, EV_TXEMPTY, EV_CTS, EV_DSR,
                              EV_RING|0x2000, EV_RLSD, EV_ERR,
                              EV_ERR, EV_ERR, EV_BREAK
                        };
      if( !index )
      {
            LEAVE( "termios_setflags" );
            return;
      }
      index->event_flag = 0;
      for(i=0;i<11;i++)
            if( termios_flags[i] )
                  index->event_flag |= windows_flags[i];
      result = SetCommMask( index->hComm, index->event_flag );
      /*
         This is rank.  0x2000 was used to detect the trailing edge of ring.
         The leading edge is detedted by EV_RING.

         The trailing edge is reliable.  The leading edge is not.
         Softie no longer allows the trailing edge to be detected in NTsp2
         and beyond.

         So... Try the reliable option above and if it fails, use the less
         reliable means.

         The screams for a giveio solution that bypasses the kernel.
      */
      if( index->event_flag & 0x2000 && result == 0 )
      {
            index->event_flag &= ~0x2000;
            SetCommMask( index->hComm, index->event_flag );
      }
}

/*----------------------------------------------------------
get_fd()

   accept:      filename
   perform:     find the file descriptor associated with the filename
   return:      fd
   exceptions:  
   win32api:    None
   comments:    This is not currently called by anything
----------------------------------------------------------*/

int get_fd( char *filename )
{
      struct termios_list *index = first_tl;

      ENTER( "get_fd" );
      if( !index )
      {
            return -1;
      }

      while( strcmp( index->filename, filename ) )
      {
            index = index->next;
            if( !index->next )
                  return( -1 );
      }
      LEAVE( "get_fd" );
      return( index->fd );
}

/*----------------------------------------------------------
get_filename()

   accept:      file descriptor
   perform:     find the filename associated with the file descriptor
   return:      the filename associated with the fd
   exceptions:  None
   win32api:    None
   comments:    This is not currently called by anything
----------------------------------------------------------*/

char *get_filename( int fd )
{
      struct termios_list *index = first_tl;

      ENTER( "get_filename" );
      if( !index )
            return( "bad" );
      while( index->fd != fd )
      {
            if( index->next == NULL )
                  return( "bad" );
            index = index->next;
      }
      LEAVE( "get_filename" );
      return( index->filename );
}

/*----------------------------------------------------------
dump_termios_list()

   accept:      string to print out.
   perform:     
   return:      
   exceptions:  
   win32api:    None
   comments:    used only for debugging eg serial_close()
----------------------------------------------------------*/

void dump_termios_list( char *foo )
{
#ifdef DEBUG
      struct termios_list *index = first_tl;
      printf( "============== %s start ===============\n", foo );
      if ( index )
      {
            printf( "%i filename | %s\n", index->fd, index->filename );
      }
/*
      if ( index->next )
      {
            printf( "%i filename | %s\n", index->fd, index->filename );
      }
*/
      printf( "============== %s end  ===============\n", foo );
#endif
}

/*----------------------------------------------------------
set_errno()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:    None
   comments:   FIXME   
----------------------------------------------------------*/

void set_errno( int error )
{
      my_errno = error;
}

#if 0
/*----------------------------------------------------------
usleep()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:    Sleep()
   comments:    
----------------------------------------------------------*/

void usleep( unsigned long usec )
{
      Sleep( usec/1000 );
}
#endif

/*----------------------------------------------------------
CBR_toB()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

int CBR_to_B( int Baud )
{
      ENTER( "CBR_to_B" );
      switch ( Baud )
      {

            case 0:                 return( B0 );
            case 50:          return( B50 );
            case 75:          return( B75 );
            case CBR_110:           return( B110 );
            case 134:         return( B134 );
            case 150:         return( B150 );
            case 200:         return( B200 );
            case CBR_300:           return( B300 );
            case CBR_600:           return( B600 );
            case CBR_1200:          return( B1200 );
            case 1800:        return( B1800 );
            case CBR_2400:          return( B2400 );
            case CBR_4800:          return( B4800 );
            case CBR_9600:          return( B9600 );
            case CBR_19200:         return( B19200 );
            case CBR_28800:         return( B28800 );
            case CBR_38400:         return( B38400 );
            case CBR_57600:         return( B57600 );
            case CBR_115200:  return( B115200 );
            /*  14400, 128000 and 256000 are windows specific but need to
             *  work.
             *  hosed on my hardware....
             */
            case CBR_14400:         return( B14400 );
            case CBR_128000:  return( B128000 );
            case CBR_256000:  return( B256000 );

            /*  The following could be used on linux and should be able to
             *  work on windows if we get control of baud/divisor.
             */

            case CBR_230400:  return( B230400 );
            case CBR_460800:  return( B460800 );
            case CBR_500000:  return( B500000 );
            case CBR_576000:  return( B576000 );
            case CBR_921600:  return( B921600 );
            case CBR_1000000: return( B1000000 );
            case CBR_1152000: return( B1152000 );
            case CBR_1500000: return( B1500000 );
            case CBR_2000000: return( B2000000 );
            case CBR_2500000: return( B2500000 );
            case CBR_3000000: return( B3000000 );
            case CBR_3500000: return( B3500000 );
            case CBR_4000000: return( B4000000 );
            default:
                  set_errno(EINVAL );
                  return -1;
      }
}

/*----------------------------------------------------------
B_to_CBR()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:
   comments:      None
----------------------------------------------------------*/

int B_to_CBR( int Baud )
{
      int ret;
      ENTER( "B_to_CBR" );
      switch ( Baud )
      {
            case 0:           ret = 0;          break;
            case B50:   ret = 50;         break;
            case B75:   ret = 75;         break;
            case B110:  ret = CBR_110;          break;
            case B134:  ret = 134;        break;
            case B150:  ret = 150;        break;
            case B200:  ret = 200;        break;
            case B300:  ret = CBR_300;          break;
            case B600:  ret = CBR_600;          break;
            case B1200: ret = CBR_1200;         break;
            case B1800: ret = 1800;       break;
            case B2400: ret = CBR_2400;         break;
            case B4800: ret = CBR_4800;         break;
            case B9600: ret = CBR_9600;         break;
            case B19200:      ret = CBR_19200;  break;
            case B38400:      ret = CBR_38400;  break;
            case B57600:      ret = CBR_57600;  break;
            case B115200:     ret = CBR_115200; break;

            /*  14400, 128000 and 256000 are windows specific but need to
             *  work.
             */
            case B14400:      ret = CBR_14400;  break;
            case B128000:     ret = CBR_128000; break;
            case B256000:     ret = CBR_256000; break;

            /*  The following could be used on linux and should be able to
             *  work on windows if we get control of baud/divisor.
             */
            case B230400:     ret = CBR_230400; break;
            case B460800:     ret = CBR_460800; break;
            case B500000:     ret = CBR_500000; break;
            case B576000:     ret = CBR_576000; break;
            case B921600:     ret = CBR_921600; break;
            case B1000000:    ret = CBR_1000000;      break;
            case B1152000:    ret = CBR_1152000;      break;
            case B1500000:    ret = CBR_1500000;      break;
            case B2000000:    ret = CBR_2000000;      break;
            case B2500000:    ret = CBR_2500000;      break;
            case B3000000:    ret = CBR_3000000;      break;
            case B3500000:    ret = CBR_3500000;      break;
            case B4000000:    ret = CBR_4000000;      break;
      
            default:
                  fprintf( stderr, "B_to_CBR: invalid baudrate: %#o\n",
                        Baud );
                  set_errno( EINVAL );
                  return -1;
      }
      LEAVE( "B_to_CBR" );
      return ret;
}

/*----------------------------------------------------------
bytesize_to_termios()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:      None
   comments:    
----------------------------------------------------------*/

int bytesize_to_termios( int ByteSize )
{
      ENTER( "bytesize_to_termios" );
      switch ( ByteSize )
      {
            case 5: return( CS5 );
            case 6: return( CS6 );
            case 7: return( CS7 );
            case 8:
            default: return( CS8 );
      }
}

/*----------------------------------------------------------
termios_to_bytesize()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

int termios_to_bytesize( int cflag )
{
      ENTER( "termios_to_bytesize" );
      switch ( cflag & CSIZE )
      {
            case CS5: return( 5 );
            case CS6: return( 6 );
            case CS7: return( 7 );
            case CS8:
            default: return( 8 );
      }
}

#if 0
/*----------------------------------------------------------
get_dos_port()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

const char *get_dos_port( char const *name )
{
      ENTER( "get_dos_port" );
      if ( !strcmp( name, "/dev/cua0" ) ) return( "COM1" );
      if ( !strcmp( name, "/dev/cua1" ) ) return( "COM2" );
      if ( !strcmp( name, "/dev/cua2" ) ) return( "COM3" );
      if ( !strcmp( name, "/dev/cua3" ) ) return( "COM4" );
      LEAVE( "get_dos_port" );
      return( ( const char * ) name );
}
#endif

/*----------------------------------------------------------
ClearErrors()

   accept:       
   perform:      keep track of errors for the eventLoop() (SerialImp.c)
   return:       the return value of ClearCommError()
   exceptions:  
   win32api:     ClearCommError()
   comments:    
----------------------------------------------------------*/

int ClearErrors( struct termios_list *index, COMSTAT *Stat )
{
      unsigned long ErrCode;
      int ret;

      ret = ClearCommError( index->hComm, &ErrCode, Stat );
      if ( ret == 0 )
      {
            YACK();
            return( ret );
      }
#ifdef DEBUG_ERRORS
      if ( ErrCode )
      {
            printf("%i frame %i %i overrun %i %i  parity %u %i brk %i %i\n",
                  (int) ErrCode,
                  (int) ErrCode & CE_FRAME,
                  index->sis->frame,
                  (int) (ErrCode & CE_OVERRUN) | ( ErrCode & CE_RXOVER ),
                  index->sis->overrun,
                  (int) ErrCode & CE_RXPARITY,
                  index->sis->parity,
                  (int) ErrCode & CE_BREAK,
                  index->sis->brk
            );
      }
#endif /* DEBUG_ERRORS */
      if( ErrCode & CE_FRAME )
      {
            index->sis->frame++;
            ErrCode &= ~CE_FRAME;
      }
#ifdef LIFE_IS_GOOD
  FIXME OVERRUN is spewing
      if( ErrCode & CE_OVERRUN )
      {
            index->sis->overrun++;
            ErrCode &= ~CE_OVERRUN;
      }
      /* should this be here? */
      else if( ErrCode & CE_RXOVER )
      {
            index->sis->overrun++;
            ErrCode &= ~CE_OVERRUN;
      }
#endif /* LIFE_IS_GOOD */
      if( ErrCode & CE_RXPARITY )
      {
            index->sis->parity++;
            ErrCode &= ~CE_RXPARITY;
      }
      if( ErrCode & CE_BREAK )
      {
            index->sis->brk++;
            ErrCode &= ~CE_BREAK;
      }
      return( ret );
}

/*----------------------------------------------------------
FillDCB()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     GetCommState(),  SetCommState(), SetCommTimeouts()
   comments:    
----------------------------------------------------------*/

BOOL FillDCB( DCB *dcb, unsigned long *hCommPort, COMMTIMEOUTS Timeout )
{

      ENTER( "FillDCB" );
      dcb->DCBlength = sizeof( dcb );
      if ( !GetCommState( hCommPort, dcb ) )
      {
            report( "GetCommState\n" );
            return( -1 );
      }
      dcb->BaudRate        = CBR_9600 ;
      dcb->ByteSize        = 8;
      dcb->Parity          = NOPARITY;
      dcb->StopBits        = ONESTOPBIT;
      dcb->fDtrControl     = DTR_CONTROL_ENABLE;
      dcb->fRtsControl     = RTS_CONTROL_ENABLE;
      dcb->fOutxCtsFlow    = FALSE;
      dcb->fOutxDsrFlow    = FALSE;
      dcb->fDsrSensitivity = FALSE;
      dcb->fOutX           = FALSE;
      dcb->fInX            = FALSE;
      dcb->fTXContinueOnXoff = FALSE;
      dcb->XonChar         = 0x11;
      dcb->XoffChar        = 0x13;
      dcb->XonLim          = 0;
      dcb->XoffLim         = 0;
      dcb->fParity = TRUE;
      if ( EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING|( EV_RLSD & EV_RXFLAG ) )
            dcb->EvtChar = '\n'; 
      else dcb->EvtChar = '\0';
      if ( !SetCommState( hCommPort, dcb ) )
      {
            report( "SetCommState\n" );
            YACK();
            return( -1 );
      }
      if ( !SetCommTimeouts( hCommPort, &Timeout ) )
      {
            YACK();
            report( "SetCommTimeouts\n" );
            return( -1 );
      }
      LEAVE( "FillDCB" );
      return ( TRUE ) ;
}

/*----------------------------------------------------------
serial_close()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:      SetCommMask(), CloseHandle()
   comments:    
----------------------------------------------------------*/

int win32_serial_close( int fd )
{
      struct termios_list *index;
      /* char message[80]; */

      ENTER( "close" );
      if( !first_tl || !first_tl->hComm )
      {
            report( "gotit!" );
            return( 0 );
      }
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "close" );
            return -1;
      }

      /* WaitForSingleObject( index->wol.hEvent, INFINITE ); */
/*
      if ( index->hComm != INVALID_HANDLE_VALUE )
      {
            if ( !SetCommMask( index->hComm, EV_RXCHAR ) )
            {
                  YACK();
                  report( "eventLoop hung\n" );
            }
            CloseHandle( index->hComm );
      }
      else
      {
            sprintf( message, "close():  Invalid Port Reference for %s\n",
                  index->filename );
            report( message );
      }
*/
      if ( index->next  && index->prev )
      {
            index->next->prev = index->prev;
            index->prev->next = index->next;
      }
      else if ( index->prev )
      {
            index->prev->next = NULL;
      }
      else if ( index->next )
      {
            index->next->prev = NULL;
            first_tl = index->next;
      }
      else
            first_tl = NULL;
      if ( index )
      {
            if ( index->rol.hEvent ) CloseHandle( index->rol.hEvent );
            if ( index->wol.hEvent ) CloseHandle( index->wol.hEvent );
            if ( index->sol.hEvent ) CloseHandle( index->sol.hEvent );
            if ( index->hComm ) CloseHandle( index->hComm );
            if ( index->ttyset )   free( index->ttyset );
            if ( index->astruct )  free( index->astruct );
            if ( index->sstruct )  free( index->sstruct );
            if ( index->sis )      free( index->sis );
            /* had problems with strdup
            if ( index->filename ) free( index->filename );
            */
            free( index );
      }
      LEAVE( "close" );
      return 0;
}

/*----------------------------------------------------------
cfmakeraw()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

void cfmakeraw( struct termios *s_termios )
{
      ENTER( "cfmakeraw" );
      s_termios->c_iflag &= ~( IGNBRK|BRKINT|PARMRK|ISTRIP
            |INLCR|IGNCR|ICRNL|IXON );
      s_termios->c_oflag &= ~OPOST;
      s_termios->c_lflag &= ~( ECHO|ECHONL|ICANON|ISIG|IEXTEN );
      s_termios->c_cflag &= ~( CSIZE|PARENB );
      s_termios->c_cflag |= CS8;
      LEAVE( "cfmakeraw" );
}

/*----------------------------------------------------------
init_termios()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:
   comments:    
----------------------------------------------------------*/

BOOL init_serial_struct( struct serial_struct *sstruct )
{
      ENTER( "init_serial_struct" );

      /*
      FIXME

      This needs to use inb() to read the actual baud_base
      and divisor from the UART registers.  Question is how
      far do we take this?

      */

      sstruct->custom_divisor = 0;
      sstruct->baud_base = 115200;

      /* not currently used check values before using */

      /* unsigned short */

      sstruct->close_delay = 0;
      sstruct->closing_wait = 0;
      sstruct->iomem_reg_shift = 0;

      /* int */

      sstruct->type = 0;
      sstruct->line = 0;
      sstruct->irq = 0;
      sstruct->flags = 0;
      sstruct->xmit_fifo_size = 0;
      sstruct->hub6 = 0;

      /* unsigned int */

      sstruct->port = 0;
      sstruct->port_high = 0;

      /* char */

      sstruct->io_type = 0;

      /* unsigned char * */

      sstruct->iomem_base = NULL;

      LEAVE( "init_serial_struct" );
      return TRUE;

}
/*----------------------------------------------------------
init_termios()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:
   comments:    
----------------------------------------------------------*/

BOOL init_termios(struct termios *ttyset )
{
      ENTER( "init_termios" );
      if ( !ttyset )
            return FALSE;
      memset( ttyset, 0, sizeof( struct termios ) );
      cfsetospeed( ttyset, B9600 );
      cfmakeraw( ttyset );
      ttyset->c_cc[VINTR] = 0x03;   /* 0: C-c */
      ttyset->c_cc[VQUIT] = 0x1c;   /* 1: C-\ */
      ttyset->c_cc[VERASE] = 0x7f;  /* 2: <del> */
      ttyset->c_cc[VKILL] = 0x15;   /* 3: C-u */
      ttyset->c_cc[VEOF] = 0x04;    /* 4: C-d */
      ttyset->c_cc[VTIME] = 0;      /* 5: read timeout */
      ttyset->c_cc[VMIN] = 1;       /* 6: read returns after this
                                    many bytes */
      ttyset->c_cc[VSUSP] = 0x1a;   /* 10: C-z */
      ttyset->c_cc[VEOL] = '\r';    /* 11: */
      ttyset->c_cc[VREPRINT] = 0x12;      /* 12: C-r */
/*
      ttyset->c_cc[VDISCARD] = 0x;     13: IEXTEN only 
*/
      ttyset->c_cc[VWERASE] = 0x17; /* 14: C-w */
      ttyset->c_cc[VLNEXT] = 0x16;  /* 15: C-w */
      ttyset->c_cc[VEOL2] = '\n';   /* 16: */
      LEAVE( "init_termios" );
      return TRUE;
      /* default VTIME = 0, VMIN = 1: read blocks forever until one byte */
}

/*----------------------------------------------------------
port_opened()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

int port_opened( const char *filename )
{
      struct termios_list *index = first_tl;
      
      ENTER( "port_opened" );
      if ( ! index )
            return 0;
      if( !strcmp( index->filename, filename ) )
            return index->fd;
      while ( index->next )
      {
            index = index->next;
            if( !strcmp( index->filename, filename ) )
                  return index->fd;
      }
      LEAVE( "port_opened" );
      return 0;
}

/*----------------------------------------------------------
open_port()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:   CreateFile(), SetupComm(), CreateEvent()
   comments:    
      FILE_FLAG_OVERLAPPED allows one to break out the select()
      so RXTXPort.close() does not hang.

      The setDTR() and setDSR() are the functions that noticed
      to be blocked in the java close.  Basically ioctl(TIOCM[GS]ET)
      are where it hangs.

      FILE_FLAG_OVERLAPPED also means we need to create valid OVERLAPPED
      structure in Serial_select.
----------------------------------------------------------*/

int open_port( struct termios_list *port )
{
      ENTER( "open_port" );
      port->hComm = CreateFile( port->filename,
            GENERIC_READ | GENERIC_WRITE,
            0,
            0,
            OPEN_EXISTING,
            FILE_FLAG_OVERLAPPED,
            0
      );
      if ( port->hComm == INVALID_HANDLE_VALUE )
      {
            YACK();
            set_errno( EINVAL );
/*
            printf( "open failed %s\n", port->filename );
*/
            return -1;
      }
      if( !SetupComm( port->hComm, 2048, 1024 ) )
      {
            YACK();
            return -1;
      }

      memset( &port->rol, 0, sizeof( OVERLAPPED ) );
      memset( &port->wol, 0, sizeof( OVERLAPPED ) );
      memset( &port->sol, 0, sizeof( OVERLAPPED ) );

      port->rol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

      if ( !port->rol.hEvent )
      {
            YACK();
            report( "Could not create read overlapped\n" );
            goto fail;
      }

      port->sol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

      if ( !port->sol.hEvent )      
      {
            YACK();
            report( "Could not create select overlapped\n" );
            goto fail;
      }
      port->wol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

      if ( !port->wol.hEvent )      
      {
            YACK();
            report( "Could not create write overlapped\n" );
            goto fail;
      }
      LEAVE( "open_port" );
      return( 0 );
fail:
      return( -1 );
}

/*----------------------------------------------------------
termios_list()

   accept:       fd which is a fake # for the port assigned when the port
             is opened
   perform:      walk through a double linked list to see if the given
             fd is in any of the termios_list members.
   return:       the termios_list if it is found.
             NULL if no matches are found.
   exceptions:   None
   win32api:     None
   comments:    
----------------------------------------------------------*/

struct termios_list *find_port( int fd )
{

      char message[80];
      struct termios_list *index = first_tl;

      ENTER( "find_port" );
      if ( fd <= 0 || !first_tl ) goto fail;

      while( index->fd )
      {
            if ( index->fd == fd )
            {
                  LEAVE( "find_port" );
                  return index;
            }
            if ( !index->next )
                  break;
            index = index->next;
      }
fail:
      sprintf( message, "No info known about the port. %i\n", fd );
      report( message );
      set_errno( EBADF );
      LEAVE( "find_port" );
      return NULL;
}

/*----------------------------------------------------------
get_free_fd()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:       None
   comments:    
----------------------------------------------------------*/

int get_free_fd()
{
      int next, last;
      struct termios_list *index = first_tl;

      ENTER( "get_free_fd" );
      if ( !index )
      {
            return( 1 );
      }
      if ( !index->fd )
      {
            report( "!index->fd\n" );
            return( 1 );
      }
      if ( index->fd > 1)
      {
            first_tl = index;
            return ( 1 );
      }
      
      last = index->fd;

      while( index->next )
      {
            next = index->next->fd;
            if ( next !=  last + 1 )
            {
                  return( last + 1 );
                  
            }
            index = index->next;
            last = next;
      }
      LEAVE( "get_free_fd" );
      return( index->fd + 1 );
}

/*----------------------------------------------------------
add_port()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:      None
   comments:    
----------------------------------------------------------*/

struct termios_list *add_port( const char *filename )
{
      struct termios_list *index = first_tl;
      struct termios_list *port;

      ENTER( "add_port" );

      port = malloc( sizeof( struct termios_list ) );
      if( !port )
            goto fail;
      memset( port, 0, sizeof( struct termios_list ) );

      port->ttyset = malloc( sizeof( struct termios ) );
      if( ! port->ttyset )
            goto fail;
      memset( port->ttyset, 0, sizeof( struct termios ) );

      port->sstruct = malloc( sizeof( struct serial_struct ) );
      if( ! port->sstruct )
            goto fail;
      memset( port->sstruct, 0, sizeof( struct serial_struct ) );
      port->sis = malloc( sizeof( struct serial_icounter_struct ) );
      if( ! port->sis )
            goto fail;
      memset( port->sis, 0, sizeof( struct serial_icounter_struct ) );

/*    FIXME  the async_struct is being defined by mingw32 headers?
      port->astruct = malloc( sizeof( struct async_struct ) );
      if( ! port->astruct )
            goto fail;
      memset( port->astruct, 0, sizeof( struct async_struct ) );
*/
      port->MSR = 0;

      strcpy(port->filename, filename );
      /* didnt free well? strdup( filename ); */
      if( ! port->filename )
            goto fail;

      port->fd = get_free_fd();

      
      if ( !first_tl )
      {
            port->prev = NULL;
            first_tl = port;
      }
      else
      {
            while ( index->next )
                  index = index->next;
            if ( port == first_tl )
            {
                  port->prev = NULL;
                  port->next = first_tl;
                  first_tl->prev = port;
                  first_tl = port;
            }
            else
            {
                  port->prev = index;
                  index->next = port;
            }
      }
      port->next = NULL;
      LEAVE( "add_port" );
      return port;

fail:
      report( "add_port:  Out Of Memory\n");
      if ( port->ttyset )   free( port->ttyset );
      if ( port->astruct )  free( port->astruct );
      if ( port->sstruct )  free( port->sstruct );
      if ( port->sis )      free( port->sis );
      /* had problems with strdup
      if ( port->filename ) free( port->filename );
      */
      if ( port ) free( port );
      return port;
}

/*----------------------------------------------------------
check_port_capabilities()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:      GetCommProperties(), GetCommState()
   comments:    
----------------------------------------------------------*/

int check_port_capabilities( struct termios_list *index )
{
      COMMPROP cp;
      DCB   dcb;
      char message[80];

      ENTER( "check_port_capabilities" );
      /* check for capabilities */
      GetCommProperties( index->hComm, &cp );
      if ( !( cp.dwProvCapabilities & PCF_DTRDSR ) )
      {
            sprintf( message,
                  "%s: no DTR & DSR support\n", index->filename );
            report( message );
      }
      if ( !( cp.dwProvCapabilities & PCF_RLSD ) )
      {
            sprintf( message, "%s: no carrier detect (RLSD) support\n",
                  index->filename );
            report( message );
      }
      if ( !( cp.dwProvCapabilities & PCF_RTSCTS ) )
      {
            sprintf( message,
                  "%s: no RTS & CTS support\n", index->filename );
            report( message );
      }
      if ( !( cp.dwProvCapabilities & PCF_TOTALTIMEOUTS ) )
      {
            sprintf( message, "%s: no timeout support\n", index->filename );
            report( message );
      }
      if ( !GetCommState( index->hComm, &dcb ) )
      {
            YACK();
            report( "GetCommState\n" );
            return -1;
      }
      LEAVE( "check_port_capabilities" );
      return 0;

}

/*----------------------------------------------------------
serial_open()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:    None
   comments:    
----------------------------------------------------------*/

int win32_serial_open( const char *filename, int flags, ... )
{
      struct termios_list *index;
      char message[80];

      ENTER( "serial_open" );
      if ( port_opened( filename ) )
      {
            report( "Port is already opened\n" );
            return( -1 );
      }
      index = add_port( filename );
      if( !index )
      {
            report( "open !index\n" );
            return( -1 );
      }
      
      index->interrupt = 0;
      index->tx_happened = 0;
      if ( open_port( index ) )
      {
            sprintf( message, "open():  Invalid Port Reference for %s\n",
                  filename );
            report( message );
            close( index->fd );
            return -1;
      }

      if( check_port_capabilities( index ) )
      {
            report( "check_port_capabilites!" );
            close( index->fd );
            return -1;
      }

      init_termios( index->ttyset );
      init_serial_struct( index->sstruct );

      /* set default condition */
      tcsetattr( index->fd, 0, index->ttyset );

      /* if opened with non-blocking, then operating non-blocking */
      if ( flags & O_NONBLOCK )
            index->open_flags = O_NONBLOCK;
      else
            index->open_flags = 0;


      if( !first_tl->hComm )
      {
            sprintf( message, "open():  Invalid Port Reference for %s\n",
                  index->filename );
            report( message );
      }
      if ( first_tl->hComm == INVALID_HANDLE_VALUE )
            report( "serial_open: test\n" );
      LEAVE( "serial_open" );
      return( index->fd );
}


/*----------------------------------------------------------
serial_write()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     WriteFile(), GetLastError(),
                 WaitForSingleObject(),  GetOverlappedResult(),
                 FlushFileBuffers(), Sleep()
   comments:    
----------------------------------------------------------*/

int win32_serial_write( int fd, const char *Str, int length )
{
      unsigned long nBytes;
      struct termios_list *index;
      //COMSTAT Stat;
      int old_flag;

      ENTER( "serial_write" );

      if ( fd <= 0 )
      {
            return 0;
      }
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "serial_write");
            return -1;
      }
      old_flag = index->event_flag;
/*
      index->event_flag &= ~EV_TXEMPTY;
      SetCommMask( index->hComm, index->event_flag );
      index->tx_happened = 1; 
*/
      index->wol.Offset = index->wol.OffsetHigh = 0; 
      ResetEvent( index->wol.hEvent );
      if ( !WriteFile( index->hComm, Str, length, &nBytes, &index->wol ) )
      {
            WaitForSingleObject( index->wol.hEvent,100 );
            if ( GetLastError() != ERROR_IO_PENDING )
            {
                  //ClearErrors( index, &Stat );
                  report( "serial_write error\n" );
                  //report("Condition 1 Detected in write()\n");
                  YACK();
                  errno = EIO;
                  nBytes=-1;
                  goto end;
            }
            /* This is breaking on Win2K, WinXP for some reason */
            else while( !GetOverlappedResult( index->hComm, &index->wol,
                              &nBytes, TRUE ) )
            {
                  if ( GetLastError() != ERROR_IO_INCOMPLETE )
                  {
                        //report("Condition 2 Detected in write()\n");
                        YACK();
                        errno = EIO;
                        nBytes = -1;
                        goto end;
                        //ClearErrors( index, &Stat );
                  }
            }
      }
      else
      {
            //ClearErrors( index, &Stat );
            //report("Condition 3 Detected in write()\n");
            YACK();
            errno = EIO;
            //report( "serial_write bailing!\n" );
            return(-1);
      }
end:
      /* FlushFileBuffers( index->hComm ); */
      index->event_flag |= EV_TXEMPTY;
      //ClearErrors( index, &Stat );
      SetCommMask( index->hComm, index->event_flag );
      //ClearErrors( index, &Stat );
      index->event_flag = old_flag;
      index->tx_happened = 1; 
      LEAVE( "serial_write" );
      return nBytes;
}

/*----------------------------------------------------------
serial_read()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:      ReadFile(), GetLastError(), WaitForSingleObject()
                  GetOverLappedResult()
   comments:    If setting errno make sure not to use EWOULDBLOCK
                In that case use EAGAIN.  See SerialImp.c:testRead() 
----------------------------------------------------------*/

int win32_serial_read( int fd, void *vb, int size )
{
      long start, now;
      unsigned long nBytes = 0, total = 0, error;
      /* unsigned long waiting = 0; */
      int err, vmin; 
      struct termios_list *index;
      char message[80];
      COMSTAT stat;
      clock_t c;
      unsigned char *dest = vb;
      
      start = GetTickCount();
      ENTER( "serial_read" );

      if ( fd <= 0 )
      {
            return 0;
      }
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "serial_read" );
            return -1;
      }

      /* FIXME: CREAD: without this, data cannot be read
         FIXME: PARMRK: mark framing & parity errors
         FIXME: IGNCR: ignore \r
         FIXME: ICRNL: convert \r to \n
         FIXME: INLCR: convert \n to \r
      */

      if ( index->open_flags & O_NONBLOCK  )
      {
            int ret;
            vmin = 0;
            /* pull mucho-cpu here? */
            do {
#ifdef DEBUG_VERBOSE
                  report( "vmin=0\n" );
#endif /* DEBUG_VERBOSE */
                  ret = ClearErrors( index, &stat);
/*
                  usleep(1000);
                  usleep(50);
*/
                  /* we should use -1 instead of 0 for disabled timeout */
                  now = GetTickCount();
                  if ( index->ttyset->c_cc[VTIME] &&
                        now-start >= (index->ttyset->c_cc[VTIME]*100)) {
/*
                        sprintf( message, "now = %i start = %i time = %i total =%i\n", now, start, index->ttyset->c_cc[VTIME]*100, total);
                        report( message );
*/
                        return total;     /* read timeout */
                  }
            } while( stat.cbInQue < size && size > 1 );
      }
      else
      {
            /* VTIME is in units of 0.1 seconds */

#ifdef DEBUG_VERBOSE
            report( "vmin!=0\n" );
#endif /* DEBUG_VERBOSE */
            vmin = index->ttyset->c_cc[VMIN];

            c = clock() + index->ttyset->c_cc[VTIME] * CLOCKS_PER_SEC / 10;
            do {
                  error = ClearErrors( index, &stat);
                  usleep(1000);
            } while ( c > clock() );

      }

      total = 0;
      while ( size > 0 )
      {
            nBytes = 0;
            //ret = ClearErrors( index, &stat);

            index->rol.Offset = index->rol.OffsetHigh = 0;
            ResetEvent( index->rol.hEvent );

            err = ReadFile( index->hComm, dest + total, size, &nBytes, &index->rol ); 
#ifdef DEBUG_VERBOSE
      /* warning Roy Rogers! */
            sprintf(message, " ========== ReadFile = %i %s\n",
                  ( int ) nBytes, (char *) dest + total );
            report( message );
#endif /* DEBUG_VERBOSE */
            size -= nBytes;
            total += nBytes;
            
            if ( !err )
            {
                  switch ( GetLastError() )
                  {
                        case ERROR_BROKEN_PIPE:
                              report( "ERROR_BROKEN_PIPE\n ");
                              nBytes = 0;
                              break;
                        case ERROR_MORE_DATA:
/*
                              usleep(1000);
*/
                              report( "ERROR_MORE_DATA\n" );
                              break;
                        case ERROR_IO_PENDING:
                              while( ! GetOverlappedResult(
                                          index->hComm,
                                          &index->rol,
                                          &nBytes,
                                          TRUE ) )
                              {
                                    if( GetLastError() !=
                                          ERROR_IO_INCOMPLETE )
                                    {
                                          ClearErrors(
                                                index,
                                                &stat);
                                          return( total );
                                    }
                              }
                              size -= nBytes;
                              total += nBytes;
                              if (size > 0) {
                                    now = GetTickCount();
                                    sprintf(message, "size > 0: spent=%ld have=%d\n", now-start, index->ttyset->c_cc[VTIME]*100);
                                    report( message );
                                    /* we should use -1 for disabled
                                       timouts */
                                    if ( index->ttyset->c_cc[VTIME] && now-start >= (index->ttyset->c_cc[VTIME]*100)) {
                                          report( "TO " );
                                          /* read timeout */
                                          return total;
                                    }
                              }
                              sprintf(message, "end nBytes=%ld] ", nBytes);
                              report( message );
/*
                              usleep(1000);
*/
                              report( "ERROR_IO_PENDING\n" );
                              break;
                        default:
/*
                              usleep(1000);
*/
                              YACK();
                              return -1;
                  }
            }
            else
            {
/*
                  usleep(1000);
*/
                  ClearErrors( index, &stat);
                  return( total );
            }
      }
      LEAVE( "serial_read" );
      return total;
}

#ifdef asdf
int win32_serial_read( int fd, void *vb, int size )
{
      long start, now;
      unsigned long nBytes = 0, total = 0, error;
      /* unsigned long waiting = 0; */
      int err, vmin; 
      struct termios_list *index;
      char message[80];
      COMSTAT Stat;
      clock_t c;
      unsigned char *dest = vb;
      
      start = GetTickCount();
      ENTER( "serial_read" );

      if ( fd <= 0 )
      {
            printf("1\n");
            return 0;
      }
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "serial_read 7" );
            errno = EIO;
            printf("2\n");
            return -1;
      }

      /* FIXME: CREAD: without this, data cannot be read
         FIXME: PARMRK: mark framing & parity errors
         FIXME: IGNCR: ignore \r
         FIXME: ICRNL: convert \r to \n
         FIXME: INLCR: convert \n to \r
      */

      ClearErrors( index, &Stat );
      if ( index->open_flags & O_NONBLOCK  )
      {
            int ret;
            vmin = 0;
            /* pull mucho-cpu here? */
            do {
#ifdef DEBUG_VERBOSE
                  report( "vmin=0\n" );
#endif /* DEBUG_VERBOSE */
                  ret = ClearErrors( index, &Stat);
/*
                  usleep(1000);
                  usleep(50);
*/
                  /* we should use -1 instead of 0 for disabled timeout */
                  now = GetTickCount();
                  if ( index->ttyset->c_cc[VTIME] &&
                        now-start >= (index->ttyset->c_cc[VTIME]*100)) {
/*
                        sprintf( message, "now = %i start = %i time = %i total =%i\n", now, start, index->ttyset->c_cc[VTIME]*100, total);
                        report( message );
*/
                        errno = EAGAIN;
                        printf("3\n");
                        return -1;  /* read timeout */
                  }
            } while( Stat.cbInQue < size && size > 1 );
      }
      else
      {
            /* VTIME is in units of 0.1 seconds */

#ifdef DEBUG_VERBOSE
            report( "vmin!=0\n" );
#endif /* DEBUG_VERBOSE */
            vmin = index->ttyset->c_cc[VMIN];

            c = clock() + index->ttyset->c_cc[VTIME] * CLOCKS_PER_SEC / 10;
            do {
                  error = ClearErrors( index, &Stat);
                  usleep(1000);
            } while ( c > clock() );

      }

      total = 0;
      while ( size > 0 )
      {
            nBytes = 0;
            //ret = ClearErrors( index, &Stat);

            index->rol.Offset = index->rol.OffsetHigh = 0;
            ResetEvent( index->rol.hEvent );

            err = ReadFile( index->hComm, dest + total, size, &nBytes, &index->rol ); 
#ifdef DEBUG_VERBOSE
      /* warning Roy Rogers! */
            sprintf(message, " ========== ReadFile = %i %s\n",
                  ( int ) nBytes, (char *) dest + total );
            report( message );
#endif /* DEBUG_VERBOSE */
            size -= nBytes;
            total += nBytes;
            
            if ( !err )
            {
                  switch ( GetLastError() )
                  {
                        case ERROR_BROKEN_PIPE:
                              report( "ERROR_BROKEN_PIPE\n ");
                              nBytes = 0;
                              break;
                        case ERROR_MORE_DATA:
/*
                              usleep(1000);
*/
                              report( "ERROR_MORE_DATA\n" );
                              break;
                        case ERROR_IO_PENDING:
                              while( ! GetOverlappedResult(
                                          index->hComm,
                                          &index->rol,
                                          &nBytes,
                                          TRUE ) )
                              {
                                    if( GetLastError() !=
                                          ERROR_IO_INCOMPLETE )
                                    {
                                          ClearErrors(
                                                index,
                                                &Stat);
                                          printf("4\n");
                                          return( total );
                                    }
                              }
                              size -= nBytes;
                              total += nBytes;
                              if (size > 0) {
                                    now = GetTickCount();
                                    sprintf(message, "size > 0: spent=%ld have=%d\n", now-start, index->ttyset->c_cc[VTIME]*100);
                                    report( message );
                                    /* we should use -1 for disabled
                                       timouts */
                                    if ( index->ttyset->c_cc[VTIME] && now-start >= (index->ttyset->c_cc[VTIME]*100)) {
                                          report( "TO " );
                                          /* read timeout */
                                          printf("5\n");
                                          return total;
                                    }
                              }
                              sprintf(message, "end nBytes=%ld] ", nBytes);
                              report( message );
/*
                              usleep(1000);
*/
                              report( "ERROR_IO_PENDING\n" );
                              break;
                        default:
/*
                              usleep(1000);
*/
                              YACK();
                              errno = EIO;
                              printf("6\n");
                              return -1;
                  }
            }
            else
            {
/*
                  usleep(1000);
*/
                  ClearErrors( index, &Stat);
                  printf("7\n");
                  return( total );
            }
      }
      LEAVE( "serial_read" );
      ClearErrors( index, &Stat);
      return total;
}
#endif /* asdf */

/*----------------------------------------------------------
cfsetospeed()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

int cfsetospeed( struct termios *s_termios, speed_t speed )
{
      char message[80];
      ENTER( "cfsetospeed" );
      if ( speed & ~CBAUD )
      {
            sprintf( message, "cfsetospeed: not speed: %#o\n", speed );
            report( message );
            return 0;
      }
      s_termios->c_ispeed = s_termios->c_ospeed = speed;
      /* clear baudrate */
      s_termios->c_cflag &= ~CBAUD;
      if( speed )
      {
            s_termios->c_cflag |= speed;
      }
      else 
      {
            /* PC blows up with speed 0 handled in Java */
            s_termios->c_cflag |= B9600;
      }
      LEAVE( "cfsetospeed" );
      return 1;
}

/*----------------------------------------------------------
cfsetispeed()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

int cfsetispeed( struct termios *s_termios, speed_t speed )
{
      return cfsetospeed( s_termios, speed );
}

/*----------------------------------------------------------
cfsetspeed()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

int cfsetspeed( struct termios *s_termios, speed_t speed )
{
      return cfsetospeed( s_termios, speed );
}

/*----------------------------------------------------------
cfgetospeed()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

speed_t cfgetospeed( struct termios *s_termios )
{
      ENTER( "cfgetospeed" );
      return s_termios->c_ospeed;
}

/*----------------------------------------------------------
cfgetispeed()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/

speed_t cfgetispeed( struct termios *s_termios )
{
      ENTER( "cfgetospeed" );
      return s_termios->c_ispeed;
}

/*----------------------------------------------------------
serial_struct_to_DCB()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/
int serial_struct_to_DCB( struct serial_struct *sstruct, DCB *dcb )
{
      /* 5 Baud rate fix
      sstruct.baud_base
      sstruct.custom_divisor = ( sstruct.baud_base/cspeed );
      */
      return(0);
}

/*----------------------------------------------------------
termios_to_DCB()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/
int termios_to_DCB( struct termios *s_termios, DCB *dcb )
{
      ENTER( "termios_to_DCB" );
      s_termios->c_ispeed = s_termios->c_ospeed = s_termios->c_cflag & CBAUD;
      dcb->BaudRate        = B_to_CBR( s_termios->c_ispeed );
      dcb->ByteSize = termios_to_bytesize( s_termios->c_cflag );

      if ( s_termios->c_cflag & PARENB )
      {
            if ( s_termios->c_cflag & PARODD 
                  && s_termios->c_cflag & CMSPAR )
            {
                  dcb->Parity = MARKPARITY;
            }
            else if ( s_termios->c_cflag & PARODD )
            {
                  dcb->Parity = ODDPARITY;
            }
            else if ( s_termios->c_cflag & CMSPAR )
            {
                  dcb->Parity = SPACEPARITY;
            }
            else
            {
                  dcb->Parity = EVENPARITY;
            }
      }
      else
      {
            dcb->Parity = NOPARITY;
      }     

      if ( s_termios->c_cflag & CSTOPB ) dcb->StopBits = TWOSTOPBITS;
            else dcb->StopBits = ONESTOPBIT;

      if ( s_termios->c_cflag & HARDWARE_FLOW_CONTROL )
      {
            dcb->fRtsControl = RTS_CONTROL_HANDSHAKE;
            dcb->fOutxCtsFlow = TRUE;
      }
      else
      {
            dcb->fRtsControl = RTS_CONTROL_DISABLE;
            dcb->fOutxCtsFlow = FALSE;
      }

      LEAVE( "termios_to_DCB" );
      return 0;
}

/*----------------------------------------------------------
DCB_to_serial_struct()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/
int DCB_to_serial_struct( DCB *dcb, struct serial_struct *sstruct  )
{
      return( 0 );
}
/*----------------------------------------------------------
DCB_to_termios()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/
void DCB_to_termios( DCB *dcb, struct termios *s_termios )
{
      ENTER( "DCB_to_termios" );
      s_termios->c_ispeed = CBR_to_B( dcb->BaudRate );
      s_termios->c_ospeed = s_termios->c_ispeed;
      s_termios->c_cflag |= s_termios->c_ispeed & CBAUD;
      LEAVE( "DCB_to_termios" );
}

/*----------------------------------------------------------
show_DCB()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
----------------------------------------------------------*/
void show_DCB( myDCB )
{

#ifdef DEBUG_HOSED
      char message[80];

      sprintf( message, "DCBlength: %ld\n", myDCB.DCBlength );
      report( message );
      sprintf( "BaudRate: %ld\n", myDCB.BaudRate );
      report( message );
      if ( myDCB.fBinary )
            report( "fBinary\n" );
      if ( myDCB.fParity )
      {
            report( "fParity: " );
            if ( myDCB.fErrorChar )
            {
                  sprintf( message, "fErrorChar: %#x\n", myDCB.ErrorChar );
                  report( message );
            }
            else
            {
                  report( "fErrorChar == false\n" );
            }
      }
      if ( myDCB.fOutxCtsFlow )
            report( "fOutxCtsFlow\n" );
      if ( myDCB.fOutxDsrFlow )
            report( "fOutxDsrFlow\n" );
      if ( myDCB.fDtrControl & DTR_CONTROL_HANDSHAKE );
            report( "DTR_CONTROL_HANDSHAKE\n" );
      if ( myDCB.fDtrControl & DTR_CONTROL_ENABLE );
            report( "DTR_CONTROL_ENABLE\n" );
      if ( myDCB.fDtrControl & DTR_CONTROL_DISABLE );
            report( "DTR_CONTROL_DISABLE\n" );
      if ( myDCB.fDsrSensitivity )
            report( "fDsrSensitivity\n" );
      if ( myDCB.fTXContinueOnXoff )
            report( "fTXContinueOnXoff\n" );
      if ( myDCB.fOutX )
            report( "fOutX\n" );
      if ( myDCB.fInX )
            report( "fInX\n" );
      if ( myDCB.fNull )
            report( "fNull\n" );
      if ( myDCB.fRtsControl & RTS_CONTROL_TOGGLE )
            report( "RTS_CONTROL_TOGGLE\n" );
      if ( myDCB.fRtsControl == 0 )
            report( "RTS_CONTROL_HANDSHAKE ( fRtsControl==0 )\n" );
      if ( myDCB.fRtsControl & RTS_CONTROL_HANDSHAKE )
            report( "RTS_CONTROL_HANDSHAKE\n" );
      if ( myDCB.fRtsControl & RTS_CONTROL_ENABLE )
            report( "RTS_CONTROL_ENABLE\n" );
      if ( myDCB.fRtsControl & RTS_CONTROL_DISABLE )
            report( "RTS_CONTROL_DISABLE\n" );
      if ( myDCB.fAbortOnError )
            report( "fAbortOnError\n" );
      sprintf( message, "XonLim: %d\n", myDCB.XonLim );
      report( message );
      sprintf( message, "XoffLim: %d\n", myDCB.XoffLim );
      report( message );
      sprintf( message, "ByteSize: %d\n", myDCB.ByteSize );
      report( message );
      switch ( myDCB.Parity )
      {
            case EVENPARITY:
                  report( "EVENPARITY" );
                  break;
            case MARKPARITY:
                  report( "MARKPARITY" );
                  break;
            case NOPARITY:
                  report( "NOPARITY" );
                  break;
            case ODDPARITY:
                  report( "ODDPARITY" );
                  break;
            default:
                  sprintf( message,
                        "unknown Parity (%#x ):", myDCB.Parity );
                  report( message );
                  break;
      }
      report( "\n" );
      switch( myDCB.StopBits )
      {
            case ONESTOPBIT:
                  report( "ONESTOPBIT" );
                  break;
            case ONE5STOPBITS:
                  report( "ONE5STOPBITS" );
                  break;
            case TWOSTOPBITS:
                  report( "TWOSTOPBITS" );
                  break;
            default:
                  report( "unknown StopBits (%#x ):", myDCB.StopBits );
                  break;
      }
      report( "\n" );
      sprintf( message,  "XonChar: %#x\n", myDCB.XonChar );
      report( message );
      sprintf(  message, "XoffChar: %#x\n", myDCB.XoffChar );
      report( message );
      sprintf(  message, "EofChar: %#x\n", myDCB.EofChar );
      report( message );
      sprintf(  message, "EvtChar: %#x\n", myDCB.EvtChar );
      report( message );
      report( "\n" );
#endif /* DEBUG_HOSED */
}

/*----------------------------------------------------------
tcgetattr()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:    GetCommState(), GetCommTimeouts()
   comments:    
----------------------------------------------------------*/

int tcgetattr( int fd, struct termios *s_termios )
{
      DCB myDCB;
      COMMTIMEOUTS timeouts;
      struct termios_list *index;
      char message[80];

      ENTER( "tcgetattr" );
      if ( fd <= 0 )
            return 0;
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "tcgetattr" );
            return -1;
      }
      if ( !GetCommState( index->hComm, &myDCB ) )
      {
            sprintf( message, "GetCommState failed\n" );
            report( message );
            return -1;
      }
      memcpy( s_termios, index->ttyset, sizeof( struct termios ) );

      show_DCB( myDCB );

      /***** input mode flags (c_iflag ) ****/
      /* parity check enable */
      if ( myDCB.fParity )
      {
            s_termios->c_iflag |= INPCK;
            s_termios->c_iflag &= ~IGNPAR;
      } else {
            s_termios->c_iflag &= ~INPCK;
            s_termios->c_iflag |= IGNPAR;
      }
      /* FIXME: IGNBRK: ignore break */
      /* FIXME: BRKINT: interrupt on break */

      if ( myDCB.fOutX )
      {
            s_termios->c_iflag |= IXON;
      }
      else
      {
            /* IXON: output start/stop control */
            s_termios->c_iflag &= ~IXON;
      }
      if ( myDCB.fInX )
      {
            s_termios->c_iflag |= IXOFF;
      }
      else
      {
            /* IXOFF: input start/stop control */
            s_termios->c_iflag &= ~IXOFF;
      }

      if ( myDCB.fTXContinueOnXoff )
      {
            s_termios->c_iflag |= IXANY;
      }
      else
      {
            /* IXANY: any char restarts output */
            s_termios->c_iflag &= ~IXANY;
      }
      /* FIXME: IMAXBEL: if input buffer full, send bell */

      /***** control mode flags (c_cflag ) *****/
      /* FIXME: CLOCAL: DONT send SIGHUP on modem disconnect */
      /* FIXME: HUPCL: generate modem disconnect when all has closed or
            exited */
      /* CSTOPB two stop bits ( otherwise one) */
      if ( myDCB.StopBits == TWOSTOPBITS )
      {
            s_termios->c_cflag |= CSTOPB; 
      }
      if ( myDCB.StopBits == ONESTOPBIT )
      {
            s_termios->c_cflag &= ~CSTOPB;      
      }


      /* PARENB enable parity bit */
      s_termios->c_cflag &= ~( PARENB | PARODD | CMSPAR );
      myDCB.fParity = 1;
      if( myDCB.fParity )
      {
            report( "tcgetattr getting parity\n" );
            s_termios->c_cflag |= PARENB;
            if ( myDCB.Parity == MARKPARITY )
            {
                  s_termios->c_cflag |= ( PARODD | CMSPAR );
            }
            else if ( myDCB.Parity == SPACEPARITY )
            {
                  s_termios->c_cflag |= CMSPAR;
            }
            else if ( myDCB.Parity == ODDPARITY )
            {
                  report( "ODDPARITY\n" );
                  s_termios->c_cflag |= PARODD;
            }
            else if ( myDCB.Parity == EVENPARITY )
            {
                  report( "EVENPARITY\n" );
                  s_termios->c_cflag &= ~PARODD;
            }
            else if ( myDCB.Parity == NOPARITY )
            {
                  s_termios->c_cflag &= ~(PARODD | CMSPAR | PARENB);
            }
      } else
      {
            s_termios->c_cflag &= ~PARENB;
      }
      /* CSIZE */
      s_termios->c_cflag |= bytesize_to_termios( myDCB.ByteSize );
      /* HARDWARE_FLOW_CONTROL: hardware flow control */
      if (( myDCB.fOutxCtsFlow == TRUE ) ||
            ( myDCB.fRtsControl == RTS_CONTROL_HANDSHAKE))
      {
            s_termios->c_cflag |= HARDWARE_FLOW_CONTROL;
      }
        else
      {
            s_termios->c_cflag &= ~HARDWARE_FLOW_CONTROL;
      }
      /* MDMBUF: carrier based flow control of output */
      /* CIGNORE: tcsetattr will ignore control modes & baudrate */

      /***** NOT SUPPORTED: local mode flags (c_lflag) *****/
      /* ICANON: canonical (not raw) mode */
      /* ECHO: echo back to terminal */
      /* ECHOE: echo erase */
      /* ECHOPRT: hardcopy echo erase */
      /* ECHOK: show KILL char */
      /* ECHOKE: BSD ECHOK */
      /* ECHONL: ICANON only: echo newline even with no ECHO */
      /* ECHOCTL: if ECHO, then control-A are printed as '^A' */
      /* ISIG: recognize INTR, QUIT & SUSP */
      /* IEXTEN: implmentation defined */
      /* NOFLSH: dont clear i/o queues on INTR, QUIT or SUSP */
      /* TOSTOP: background process generate SIGTTOU */
      /* ALTWERASE: alt-w erase distance */
      /* FLUSHO: user DISCARD char */
      /* NOKERNINFO: disable STATUS char */
      /* PENDIN: input line needsd reprinting, set by REPRINT char */
      /***** END - NOT SUPPORTED *****/

      /***** control characters (c_cc[NCCS] ) *****/

      if ( !GetCommTimeouts( index->hComm, &timeouts ) )
      {
            YACK();
            report( "GetCommTimeouts\n" );
            return -1;
      }

      s_termios->c_cc[VTIME] = timeouts.ReadTotalTimeoutConstant/100;
/*
      handled in SerialImp.c?
      s_termios->c_cc[VMIN] = ?
*/

      s_termios->c_cc[VSTART] = myDCB.XonChar;
      s_termios->c_cc[VSTOP] = myDCB.XoffChar;
      s_termios->c_cc[VEOF] = myDCB.EofChar;

#ifdef DEBUG_VERBOSE
      sprintf( message,
            "tcgetattr: VTIME:%d, VMIN:%d\n", s_termios->c_cc[VTIME],
            s_termios->c_cc[VMIN] );
      report( message );
#endif /* DEBUG_VERBOSE */

      /***** line discipline ( c_line ) ( == c_cc[33] ) *****/

      DCB_to_termios( &myDCB, s_termios ); /* baudrate */
      LEAVE( "tcgetattr" );
      return 0;
}

/*
      `TCSANOW'
            Make the change immediately.

      `TCSADRAIN'
            Make the change after waiting until all queued output has
            been written.  You should usually use this option when
            changing parameters that affect output.

      `TCSAFLUSH'
            This is like `TCSADRAIN', but also discards any queued input.

      `TCSASOFT'
            This is a flag bit that you can add to any of the above
            alternatives.  Its meaning is to inhibit alteration of the
            state of the terminal hardware.  It is a BSD extension; it is
            only supported on BSD systems and the GNU system.

            Using `TCSASOFT' is exactly the same as setting the `CIGNORE'
            bit in the `c_cflag' member of the structure TERMIOS-P points
            to.  *Note Control Modes::, for a description of `CIGNORE'.
*/

/*----------------------------------------------------------
tcsetattr()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     GetCommState(), GetCommTimeouts(), SetCommState(),
                 SetCommTimeouts()
   comments:    
----------------------------------------------------------*/
int tcsetattr( int fd, int when, struct termios *s_termios )
{
      int vtime;
      DCB dcb;
      COMMTIMEOUTS timeouts;
      struct termios_list *index;
      char message[80];

      ENTER( "tcsetattr" );
      if ( fd <= 0 )
            return 0;
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "tcsetattr" );
            return -1;
      }
      fflush( stdout );
      if ( s_termios->c_lflag & ICANON )
      {
            report( "tcsetattr: no canonical mode support\n" );
            /* and all other c_lflags too */
            return -1;
      }
      if ( !GetCommState( index->hComm, &dcb ) )
      {
            YACK();
            report( "tcsetattr:GetCommState\n" );
            return -1;
      }
      if ( !GetCommTimeouts( index->hComm, &timeouts ) )
      {
            YACK();
            report( "tcsetattr:GetCommTimeouts\n" );
            return -1;
      }

      /*** control flags, c_cflag **/
      if ( !( s_termios->c_cflag & CIGNORE ) )
      {
            dcb.fParity=1;
            /* CIGNORE: ignore control modes and baudrate */
            /* baudrate */
            if ( termios_to_DCB( s_termios, &dcb ) < 0 ) return -1;
      }
      else
      {
      }

      /*** input flags, c_iflag **/
/*  This is wrong.  It disables Parity  FIXME
      if( ( s_termios->c_iflag & INPCK ) && !( s_termios->c_iflag & IGNPAR ) )
      {
            dcb.fParity = TRUE;
      } else
      {
            dcb.fParity = FALSE;
      }
*/
      /* not in win95?
         Some years later...
         eww..  FIXME This is used for changing the Parity 
         error character 

         I think this code is hosed.  See VEOF below

         Trent
      */

      if ( s_termios->c_iflag & ISTRIP ) dcb.fBinary = FALSE;
      /* ISTRIP: strip to seven bits */
      else dcb.fBinary = TRUE;

      /* FIXME: IGNBRK: ignore break */
      /* FIXME: BRKINT: interrupt on break */
      if ( s_termios->c_iflag & IXON )
      {
            dcb.fOutX = TRUE;
      }
      else
      {
            dcb.fOutX = FALSE;
      }
      if ( s_termios->c_iflag & IXOFF )
      {
            dcb.fInX = TRUE;
      }
      else
      {
            dcb.fInX = FALSE;
      }
      dcb.fTXContinueOnXoff = ( s_termios->c_iflag & IXANY ) ? TRUE : FALSE;
      /* FIXME: IMAXBEL: if input buffer full, send bell */

      /* no DTR control in termios? */
      dcb.fDtrControl     = DTR_CONTROL_ENABLE;
      /* no DSR control in termios? */
      dcb.fOutxDsrFlow    = FALSE;
      /* DONT ignore rx bytes when DSR is OFF */
      dcb.fDsrSensitivity = FALSE;
      dcb.XonChar         = s_termios->c_cc[VSTART];
      dcb.XoffChar        = s_termios->c_cc[VSTOP];
      dcb.XonLim          = 0;      /* ? */
      dcb.XoffLim         = 0;      /* ? */
      dcb.EofChar         = s_termios->c_cc[VEOF];
      if( dcb.EofChar != '\0' )
      {
            dcb.fBinary = 0;
      }
      else
      {
            dcb.fBinary = 1;
      }
      if ( EV_BREAK|EV_CTS|EV_DSR|EV_ERR|EV_RING | ( EV_RLSD & EV_RXFLAG ) )
            dcb.EvtChar = '\n';
      else
            dcb.EvtChar = '\0';

      if ( !SetCommState( index->hComm, &dcb ) )
      {
            report( "SetCommState error\n" );
            YACK();
            return -1;
      }

#ifdef DEBUG_VERBOSE
      sprintf( message, "VTIME:%d, VMIN:%d\n", s_termios->c_cc[VTIME],
            s_termios->c_cc[VMIN] );
      report( message );
#endif /* DEBUG_VERBOSE */
      vtime = s_termios->c_cc[VTIME] * 100;
      timeouts.ReadTotalTimeoutConstant = vtime;
      timeouts.ReadIntervalTimeout = 0;
      timeouts.ReadTotalTimeoutMultiplier = 0;

      timeouts.WriteTotalTimeoutConstant = vtime;
      timeouts.WriteTotalTimeoutMultiplier = 0;
      /* max between bytes */
      if ( s_termios->c_cc[VMIN] > 0 && vtime > 0 )
      {
            /* read blocks forever on VMIN chars */
      } else if ( s_termios->c_cc[VMIN] == 0 && vtime == 0 )
      {
            /* read returns immediately */
            timeouts.ReadIntervalTimeout = MAXDWORD;
            timeouts.ReadTotalTimeoutConstant = 0;
            timeouts.ReadTotalTimeoutMultiplier = 0;
      }
#ifdef DEBUG_VERBOSE
      sprintf( message, "ReadIntervalTimeout=%ld\n",
            timeouts.ReadIntervalTimeout );
      report( message );
      sprintf( message, "c_cc[VTIME] = %d, c_cc[VMIN] = %d\n",
            s_termios->c_cc[VTIME], s_termios->c_cc[VMIN] );
      report( message );
      sprintf( message, "ReadTotalTimeoutConstant: %ld\n",
                  timeouts.ReadTotalTimeoutConstant );
      report( message );
      sprintf( message, "ReadIntervalTimeout : %ld\n",
            timeouts.ReadIntervalTimeout );
      report( message );
      sprintf( message, "ReadTotalTimeoutMultiplier: %ld\n",
            timeouts.ReadTotalTimeoutMultiplier );
      report( message );
#endif /* DEBUG_VERBOSE */
      if ( !SetCommTimeouts( index->hComm, &timeouts ) )
      {
            YACK();
            report( "SetCommTimeouts\n" );
            return -1;
      }
      memcpy( index->ttyset, s_termios, sizeof( struct termios ) );
      LEAVE( "tcsetattr" );
      return 0;
}

/*----------------------------------------------------------
tcsendbreak()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:    
            break for duration*0.25 seconds or
            0.25 seconds if duration = 0.
----------------------------------------------------------*/

int tcsendbreak( int fd, int duration )
{
      struct termios_list *index;
      COMSTAT Stat;

      ENTER( "tcsendbreak" );

      index = find_port( fd );

      if ( !index )
      {
            LEAVE( "tcdrain" );
            return -1;
      }

      if ( duration <= 0 ) duration = 1;

      if( !SetCommBreak( index->hComm ) )
            ClearErrors( index, &Stat );
      /* 0.25 seconds == 250000 usec */
      usleep( duration * 250000 );
      if( !ClearCommBreak( index->hComm ) )
            ClearErrors( index, &Stat );
      LEAVE( "tcsendbreak" );
      return 1;
}

/*----------------------------------------------------------
tcdrain()

   accept:       file descriptor
   perform:      wait for ouput to be written.
   return:       0 on success, -1 otherwise
   exceptions:   None
   win32api:     FlushFileBuffers
   comments:    
----------------------------------------------------------*/

int tcdrain ( int fd )
{
      struct termios_list *index;
      char message[80];
      int old_flag;

      ENTER( "tcdrain" );
      index = find_port( fd );

      if ( !index )
      {
            LEAVE( "tcdrain" );
            return -1;
      }
      old_flag = index->event_flag;
/*
      index->event_flag &= ~EV_TXEMPTY;
      SetCommMask( index->hComm, index->event_flag );
      index->tx_happened = 1; 
*/
      if ( !FlushFileBuffers( index->hComm ) )
      {
            /* FIXME  Need to figure out what the various errors are in
                      windows.  YACK() should report them and we can
                    handle them as we find them


                    Something funky is happening on NT.  GetLastError =
                    0.
            */
            sprintf( message,  "FlushFileBuffers() %i\n",
                  (int) GetLastError() );
            report( message );
            if( GetLastError() == 0 )
            {
                  set_errno( 0 ); 
                  return(0);
            }
            set_errno( EAGAIN );
            YACK();
            LEAVE( "tcdrain" );
            return -1;
      }
/*
      sprintf( message,  "FlushFileBuffers() %i\n",
            (int) GetLastError() );
      report( message );
*/
      LEAVE( "tcdrain success" );
      index->event_flag |= EV_TXEMPTY;
      SetCommMask( index->hComm, index->event_flag );
      index->event_flag = old_flag;
/*
      index->tx_happened = 1; 
*/
      return 0;
}

/*----------------------------------------------------------
tcflush()

   accept:       file descriptor, queue_selector 
   perform:      discard data not transmitted or read
             TCIFLUSH:  flush data not read
             TCOFLUSH:  flush data not transmitted
             TCIOFLUSH: flush both
   return:       0 on success, -1 on error
   exceptions:   none
   win32api:     PurgeComm
   comments:    
----------------------------------------------------------*/

int tcflush( int fd, int queue_selector )
{
      struct termios_list *index;
      int old_flag;

      index = find_port( fd );
      if( !index)
      {
            LEAVE( "tclflush" );
            return(-1);
      }

      old_flag = index->event_flag;
/*
      index->event_flag &= ~EV_TXEMPTY;
      SetCommMask( index->hComm, index->event_flag );
      index->tx_happened = 1; 
*/
      ENTER( "tcflush" );
      if ( !index )
      {
            LEAVE( "tcflush" );
            return -1;
      }

      index->tx_happened = 1; 
      switch( queue_selector )
      {
            case TCIFLUSH:
                  if ( !PurgeComm( index->hComm, PURGE_RXABORT ) )
                  {
                        goto fail;
                  }
                  break;
            case TCOFLUSH:
                  if ( !PurgeComm( index->hComm, PURGE_TXABORT ) )
                  {
                        goto fail;
                  }
                  break;
            case TCIOFLUSH:
                  if ( !PurgeComm( index->hComm, PURGE_TXABORT ) )
                  {
                        goto fail;
                  }
                  if ( !PurgeComm( index->hComm, PURGE_RXABORT ) )
                  {
                        goto fail;
                  }
                  break;
            default:
/*
                  set_errno( ENOTSUP );
*/
                  report( "tcflush: Unknown queue_selector\n" );
                  LEAVE( "tcflush" );
                  return -1;
      }
      index->event_flag |= EV_TXEMPTY;
      SetCommMask( index->hComm, index->event_flag );
      index->event_flag = old_flag;
      index->tx_happened = 1; 
      LEAVE( "tcflush" );
      return( 0 );

/* FIXME  Need to figure out what the various errors are in
          windows.  YACK() should report them and we can
        handle them as we find them
*/

fail:
      LEAVE( "tcflush" );
      set_errno( EAGAIN );
      YACK();
      return -1;

}

/*----------------------------------------------------------
tcflow()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     None
   comments:   FIXME 
----------------------------------------------------------*/

int tcflow( int fd, int action )
{
      ENTER( "tcflow" );
      switch ( action )
      {
            /* Suspend transmission of output */
            case TCOOFF: break;
            /* Restart transmission of output */
            case TCOON: break;
            /* Transmit a STOP character */
            case TCIOFF: break;
            /* Transmit a START character */
            case TCION: break;
            default: return -1;
      }
      LEAVE( "tcflow" );
      return 1;
}
/*----------------------------------------------------------
fstat()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:    
   comments:  this is just to keep the eventLoop happy.
----------------------------------------------------------*/

#if 0
int fstat( int fd, ... )
{
      return( 0 );
}
#endif

/*----------------------------------------------------------
ioctl()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:     GetCommError(), GetCommModemStatus, EscapeCommFunction()
   comments:  FIXME
      the DCB struct is:

      typedef struct _DCB
      {
            unsigned long DCBlength, BaudRate, fBinary:1, fParity:1;
            unsigned long fOutxCtsFlow:1, fOutxDsrFlow:1, fDtrControl:2;
            unsigned long fDsrSensitivity:1, fTXContinueOnXoff:1;
            unsigned long fOutX:1, fInX:1, fErrorChar:1, fNull:1;
            unsigned long fRtsControl:2, fAbortOnError:1, fDummy2:17;
            WORD wReserved, XonLim, XoffLim;
            BYTE ByteSize, Parity, StopBits;
            char XonChar, XoffChar, ErrorChar, EofChar, EvtChar;
            WORD wReserved1;
      } DCB;

----------------------------------------------------------*/

int win32_serial_ioctl( int fd, int request, ... )
{
      unsigned long dwStatus = 0;
      va_list ap;
      int *arg, ret, result, old_flag;
      char message[80];

#ifdef TIOCGSERIAL
      DCB   *dcb;
      struct serial_struct *sstruct;
#endif /* TIOCGSERIAL */
      COMSTAT Stat;

      struct termios_list *index;
      struct async_struct *astruct;
      struct serial_multiport_struct *mstruct;
#ifdef TIOCGICOUNT
      struct serial_icounter_struct *sistruct;
#endif  /* TIOCGICOUNT */

      ENTER( "ioctl" );
      if ( fd <= 0 )
            return 0;
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "ioctl" );
            return -1;
      }

      va_start( ap, request );
      
      ClearErrors( index, &Stat );
      switch( request )
      {
            case TCSBRK:
                  arg = va_arg( ap, int * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            case TCSBRKP:
                  arg = va_arg( ap, int * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            case TIOCGSOFTCAR:
                  arg = va_arg( ap, int * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            case TIOCSSOFTCAR:
                  arg = va_arg( ap, int * );
                  va_end( ap );
                  return -ENOIOCTLCMD;

            case TIOCCBRK:
            case TIOCSBRK:
                  arg = va_arg( ap, int * );
                  if ( EscapeCommFunction( index->hComm, 
                        ( request == TIOCSBRK ) ? SETBREAK :
                        CLRBREAK ) )
                        report( "EscapeCommFunction: True\n" );
                  else
                        report( "EscapeCommFunction: False\n" );
                  break;

            case TIOCMGET:
                  arg = va_arg( ap, int * );
            /* DORITOS */
                  if ( !GetCommModemStatus( index->hComm, &dwStatus ) )
                        report_error( "GetCommMOdemStatus failed!\n" );
                  if ( dwStatus & MS_RLSD_ON ) *arg |= TIOCM_CAR;
                  else *arg &= ~TIOCM_CAR;
                  if ( dwStatus & MS_RING_ON ) *arg |= TIOCM_RNG;
                  else *arg &= ~TIOCM_RNG;
                  if ( dwStatus & MS_DSR_ON ) *arg |= TIOCM_DSR;
                  else *arg &= ~TIOCM_DSR;
                  if ( dwStatus & MS_CTS_ON ) *arg |= TIOCM_CTS;
                  else *arg &= ~TIOCM_CTS;
                  /*  I'm not seeing a way to read the MSR directly
                      we store the state using TIOCM_*

                      Trent
                  */
                  if ( index->MSR & TIOCM_DTR )
                        *arg |= TIOCM_DTR;
                  else *arg &= ~TIOCM_DTR;
                  if ( index->MSR & TIOCM_RTS )
                        *arg |= TIOCM_RTS;
                  else *arg &= ~TIOCM_RTS;

/*

                  TIOCM_LE
                  TIOCM_ST
                  TIOCM_SR
*/
                  va_end( ap );
                  return( 0 );
            case TIOCMBIS:
                  arg = va_arg( ap, int * );
                  if ( *arg & TIOCM_DTR )
                  {
                        index->MSR |= TIOCM_DTR;

                        if (EscapeCommFunction( index->hComm, SETDTR))
                              report( "EscapeCommFunction: True\n" );
                        else
                              report( "EscapeCommFunction: False\n" );
                  }

                  if ( *arg & TIOCM_RTS )
                  {
                        index->MSR |= TIOCM_RTS;

                        if(EscapeCommFunction( index->hComm, SETRTS))
                              report( "EscapeCommFunction: True\n" );
                        else
                              report( "EscapeCommFunction: False\n" );
                  }
                  break;

            case TIOCMBIC:
                  arg = va_arg( ap, int * );
                  if ( *arg & TIOCM_DTR )
                  {
                        index->MSR &= ~TIOCM_DTR;
                        if ( EscapeCommFunction( index->hComm, CLRDTR))
                              report( "EscapeCommFunction: True\n" );
                        else
                              report( "EscapeCommFunction: False\n" );
                  }

                  if ( *arg & TIOCM_RTS )
                  {
                        index->MSR &= ~TIOCM_RTS;
                        if( EscapeCommFunction( index->hComm, CLRRTS))
                              report( "EscapeCommFunction: True\n" );
                        else
                              report( "EscapeCommFunction: False\n" );
                  }
                  break;

            case TIOCMSET:
                  arg = va_arg( ap, int * );
                  if (( *arg & TIOCM_DTR) == (index->MSR & TIOCM_DTR) )
                  {
                        report( "DTR is unchanged\n" );
                  }
                  sprintf(message, "DTR %i %i\n", *arg&TIOCM_DTR, index->MSR & TIOCM_DTR );
                  report( message );
                  if ( *arg & TIOCM_DTR )
                  {
                        index->MSR |= TIOCM_DTR;
                  }
                  else
                  {
                        index->MSR &= ~TIOCM_DTR;
                  }
                  if ( EscapeCommFunction( index->hComm, 
                        ( *arg & TIOCM_DTR ) ? SETDTR :
                        CLRDTR ) )
                        report( "EscapeCommFunction: True\n" );
                  else
                        report( "EscapeCommFunction: False\n" );
                  if ( (*arg & TIOCM_RTS) == ( index->MSR & TIOCM_RTS) )
                  {
                        report( "RTS is unchanged\n" );
                  }
                  sprintf( message, "RTS %i %i\n", *arg&TIOCM_RTS, index->MSR & TIOCM_RTS );
                  report( message );
                  if ( *arg & TIOCM_RTS )
                  {
                        index->MSR |= TIOCM_RTS;
                        result &= SETRTS;
                  }
                  else
                  {
                        index->MSR &= ~TIOCM_RTS;
                        result &= CLRRTS;
                  }
                  if( EscapeCommFunction( index->hComm,
                        ( *arg & TIOCM_RTS ) ? SETRTS : CLRRTS ) )
                        report( "EscapeCommFunction: True\n" );
                  else
                        report( "EscapeCommFunction: False\n" );
                  break;

#ifdef TIOCGSERIAL
            case TIOCGSERIAL:
                  report( "TIOCGSERIAL\n" );

                  dcb = malloc( sizeof( DCB ) );
                  if( !dcb )
                  {
                        va_end( ap );
                        return -1;
                  }
                  memset( dcb, 0, sizeof( DCB ) );
                  GetCommState( index->hComm, dcb );

                  sstruct = va_arg( ap, struct serial_struct * );
                  if ( DCB_to_serial_struct( dcb, sstruct ) < 0 )
                  {
                        va_end( ap );
                        return -1;
                  }
                  index->sstruct = sstruct;

                  report( "TIOCGSERIAL\n" );
                  free(dcb);
                  break;

#endif /* TIOCGSERIAL */
#ifdef TIOCSSERIAL
            case TIOCSSERIAL:
                  report( "TIOCSSERIAL\n" );

                  dcb = malloc( sizeof( DCB ) );
                  if( !dcb )
                  {
                        va_end( ap );
                        return -1;
                  }
                  memset( dcb, 0, sizeof( DCB ) );
                  GetCommState( index->hComm, dcb );

                  index->sstruct = va_arg( ap, struct serial_struct * );
                  if ( serial_struct_to_DCB( index->sstruct, dcb ) < 0 )
                  {
                        va_end( ap );
                        return -1;
                  }

                  report( "TIOCSSERIAL\n" );
                  free(dcb);
                  break;

#endif /* TIOCSSERIAL */
            case TIOCSERCONFIG:
            case TIOCSERGETLSR:
                  arg = va_arg( ap, int * );
                  /*
                  do {
                        wait = WaitForSingleObject( index->sol.hEvent, 5000 );
                  } while ( wait == WAIT_TIMEOUT );
                  */
                  ret = ClearErrors( index, &Stat );
                  if ( ret == 0 )
                  {
                        /* FIXME ? */
                        set_errno( EBADFD );
                        YACK();
                        report( "TIOCSERGETLSR EBADFD" );
                        va_end( ap );
                        return -1;
                  }
                  if ( (int ) Stat.cbOutQue == 0 )
                  {
                        /* output is empty */
                        if( index->tx_happened == 1 )
                        {
                              old_flag = index->event_flag;
                              index->event_flag &= ~EV_TXEMPTY;
                              SetCommMask( index->hComm,
                                    index->event_flag );
                              index->event_flag = old_flag;
                              *arg = 1;
                              index->tx_happened = 0;
                              report( "ioctl: ouput empty\n" );
                        }
                        else
                        {
                              *arg = 0;
                        }
                        ret = 0;
                  }
                  else
                  {
                        /* still data out there */
                        *arg = 0;
                        ret = 0;
                  }
                  va_end( ap );
                  return(0);
                  break;
            case TIOCSERGSTRUCT:
                  astruct = va_arg( ap, struct async_struct * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            case TIOCSERGETMULTI:
                  mstruct = va_arg( ap, struct serial_multiport_struct * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            case TIOCSERSETMULTI:
                  mstruct = va_arg( ap, struct serial_multiport_struct * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            case TIOCMIWAIT:
                  arg = va_arg( ap, int * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            /*
                  On linux this fills a struct with all the line info
                  (data available, bytes sent, ...
            */
#ifdef TIOCGICOUNT
            case TIOCGICOUNT:
                  sistruct= va_arg( ap, struct  serial_icounter_struct * );
                  ret = ClearErrors( index, &Stat );
                  if ( ret == 0 )
                  {
                        /* FIXME ? */
                        report( "TIOCGICOUNT failed\n" );
                        set_errno( EBADFD );
                        va_end( ap );
                        return -1;
                  }
                  if( sistruct->frame != index->sis->frame )
                  {
                        sistruct->frame = index->sis->frame;
/*
                        printf( "---------------frame = %i\n", sistruct->frame++ );
*/
                  }
                  if( sistruct->overrun != index->sis->overrun )
                  {
/*
                        printf( "---------------overrun\n" );
*/
                        sistruct->overrun = index->sis->overrun;
                        //ErrCode &= ~CE_OVERRUN;
                  }
                  if( sistruct->parity != index->sis->parity )
                  {
/*
                        printf( "---------------parity\n" );
*/
                        sistruct->parity = index->sis->parity;
                  }
                  if( sistruct->brk != index->sis->brk )
                  {
/*
                        printf( "---------------brk\n" );
*/
                        sistruct->brk = index->sis->brk;
                  }
                  va_end( ap );
                  return 0;
            /* abolete ioctls */
#endif /* TIOCGICOUNT */
            case TIOCSERGWILD:
            case TIOCSERSWILD:
                  report( "TIOCSER[GS]WILD absolete\n" );
                  va_end( ap );
                  return 0;
            /*  number of bytes available for reading */
            case FIONREAD:
                  arg = va_arg( ap, int * );
                  ret = ClearErrors( index, &Stat );
                  if ( ret == 0 )
                  {
                        /* FIXME ? */
                        report( "FIONREAD failed\n" );
                        set_errno( EBADFD );
                        va_end( ap );
                        return -1;
                  }
                  *arg = ( int ) Stat.cbInQue;
#ifdef DEBUG_VERBOSE
                  sprintf( message, "FIONREAD:  %i bytes available\n",
                        (int) Stat.cbInQue );
                  report( message );
                  if( *arg )
                  {
                        sprintf( message, "FIONREAD: %i\n", *arg );
                        report( message );
                  }
#endif /* DEBUG_VERBOSE */
                  ret = 0;
                  break;

            /* pending bytes to be sent */
            case TIOCOUTQ:
                  arg = va_arg( ap, int * );
                  va_end( ap );
                  return -ENOIOCTLCMD;
            default:
                  sprintf( message,
                        "FIXME:  ioctl: unknown request: %#x\n",
                        request );
                  report( message );
                  va_end( ap );
                  return -ENOIOCTLCMD;
      }
      va_end( ap );
      LEAVE( "ioctl" );
      return 0;
}

/*----------------------------------------------------------
fcntl()

   accept:      
   perform:     
   return:      
   exceptions:  
   win32api:    None
   comments:    FIXME
----------------------------------------------------------*/

int fcntl( int fd, int command, ... )
{
      int arg, ret = 0;
      va_list ap;
      struct termios_list *index;
      char message[80];

      ENTER( "fcntl" );
      if ( fd <= 0 )
            return 0;
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "fcntl" );
            return -1;
      }

      va_start( ap, command );

      arg = va_arg( ap, int );
      switch ( command )
      {
            case F_SETOWN:    /* set ownership of fd */
                  break;
            case F_SETFL:     /* set operating flags */
#ifdef DEBUG
                  sprintf( message, "F_SETFL fd=%d flags=%d\n", fd, arg );
                  report( message );
#endif
                  index->open_flags = arg;
                  break;
            case F_GETFL:     /* get operating flags */
                  ret = index->open_flags;
                  break;
            default:
                  sprintf( message, "unknown fcntl command %#x\n", command );
                  report( message );
                  break;
      }

      va_end( ap );
      LEAVE( "fcntl" );
      return ret;
}

/*----------------------------------------------------------
termios_interrupt_event_loop()

   accept:      
   perform:     
   return:  let Serial_select break out so the thread can die
   exceptions:  
   win32api:    
   comments:    
----------------------------------------------------------*/
void termios_interrupt_event_loop( int fd, int flag )
{
      struct termios_list * index = find_port( fd );
      if ( !index )
      {
            LEAVE( "termios_interrupt_event_loop" );
            return;
      }
/*
      index->event_flag = 0;
       TRENT SetCommMask( index->hComm, index->event_flag );
      usleep(2000);
      tcdrain( index->fd );
      SetEvent( index->sol.hEvent ); 
*/
      index->interrupt = flag;
      return;
}

/*----------------------------------------------------------
Serial_select()

   accept:      
   perform:     
   return:      number of fd's changed on success or -1 on error.
   exceptions:  
   win32api:    SetCommMask(), GetCommEvent(), WaitSingleObject() 
   comments:    
----------------------------------------------------------*/
#ifndef __LCC__
int  win32_serial_select( int  n,  fd_set  *readfds,  fd_set  *writefds,
                  fd_set *exceptfds, struct timeval *timeout )
{

      unsigned long dwCommEvent, wait = WAIT_TIMEOUT;
      int fd = n-1;
      struct termios_list *index;
      char message[80];
      COMSTAT Stat;
      int ret;

      ENTER( "serial_select" );
      if ( fd <= 0 )
      {
            /*  Baby did a bad baad thing */
            goto fail;
      }

      index = find_port( fd );

      if ( !index )
            goto fail;

#define DATA_AVAILABLE     1

        //nativeSetEventFlag( fd, SerialPortEvent.DATA_AVAILABLE, enable );
      if (readfds) {
            int eventflags[12];
            memset(eventflags, 0, sizeof(eventflags));

            eventflags[DATA_AVAILABLE] = 1;
            termios_setflags( fd, eventflags );
      }

      if ( !index || !index->event_flag )
      {
            /* still setting up the port? hold off for a Sec so
               things can fire up

               this does happen.  loops ~twice on a 350 Mzh with 
               usleep(1000000)
            */
            usleep(10000);
            LEAVE( "serial_uselect" );
            return(0);
      }

      ResetEvent( index->wol.hEvent );
      ResetEvent( index->sol.hEvent );
      ResetEvent( index->rol.hEvent );
      ret = ClearErrors( index, &Stat );
#if 1
      if ( ret == 0 ) {
            goto fail;
      }
      /* look only after read */
      if (readfds && !writefds && !exceptfds) {
            int timeout_usec = timeout ? timeout->tv_sec*1000000 + timeout->tv_usec : INT_MAX;

            while (timeout_usec > 0) {
                  sprintf( message, "wait for data in read buffer%d\n", (int)Stat.cbInQue );
                  report( message );

                  if (Stat.cbInQue != 0) {
                        goto end;
                  }

                  usleep(10000);
                  /* FIXME: not very accurate wrt process time */
                  timeout_usec -= 10000;

                  report( "sleep...\n" );

                  ret = ClearErrors( index, &Stat );
                  if ( ret == 0 ) {
                        goto fail;
                  }
            }
      }
#endif
      while ( wait == WAIT_TIMEOUT && index->sol.hEvent )
      {
            if( index->interrupt == 1 )
            {
                  goto fail;
            }
            SetCommMask( index->hComm, index->event_flag );
            ClearErrors( index, &Stat );
            if ( !WaitCommEvent( index->hComm, &dwCommEvent,
                  &index->sol ) )
            {
                  /* WaitCommEvent failed probably overlapped though */
                  if ( GetLastError() != ERROR_IO_PENDING )
                  {
                        ClearErrors( index, &Stat );
                        goto fail;
                  }
                  /* thought so... */
            }
            /*  could use the select timeout here but it should not
                be needed
            */
            ClearErrors( index, &Stat );
            wait = WaitForSingleObject( index->sol.hEvent, 100 );
            switch ( wait )
            {
                  case WAIT_OBJECT_0:
                        goto end;
                  case WAIT_TIMEOUT:
                        goto timeout;
                  case WAIT_ABANDONED:
                  default:
                        goto fail;
                  
            }
      }
end:
      /*  You may want to chop this out for lower latency */
      usleep(1000);
      LEAVE( "serial_select" );
      return( 1 );
timeout:
      LEAVE( "serial_select" );
      return( 0 );
fail:
      YACK();
      sprintf( message, "< select called error %i\n", n );
      report( message );
      errno = EBADFD;
      LEAVE( "serial_select" );
      return( 1 );
}
#ifdef asdf
int  win32_serial_select( int  n,  fd_set  *readfds,  fd_set  *writefds,
                  fd_set *exceptfds, struct timeval *timeout )
{

      unsigned long nBytes, dwCommEvent, wait = WAIT_TIMEOUT;
      int fd = n-1;
      struct termios_list *index;
      char message[80];

      ENTER( "serial_select" );
      if ( fd <= 0 )
      {
            usleep(1000);
            return 1;
      }
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "serial_select" );
            return -1;
      }
      if( index->interrupt == 1 )
      {
            goto end;
      }
      while(!index->event_flag )
      {
            usleep(1000);
            return -1;
      }
      
      while ( wait == WAIT_TIMEOUT && index->sol.hEvent )
      {
            if( index->interrupt == 1 )
            {
                  goto end;
            }
            if( !index->sol.hEvent )
            {
                  return 1;
            }
            if ( !WaitCommEvent( index->hComm, &dwCommEvent,
                  &index->sol ) )
            {
                  /* WaitCommEvent failed */
                  if( index->interrupt == 1 )
                  {
                        goto end;
                  }
                  if ( GetLastError() != ERROR_IO_PENDING )
                  {
                        sprintf( message, "WaitCommEvent filename = %s\n", index->filename);
                        report( message );
                        return(1);
/*
                        goto fail;
*/
                  }
                  return(1);
            }
            if( index->interrupt == 1 )
            {
                  goto end;
            }
            wait = WaitForSingleObject( index->sol.hEvent, 1000 );
            switch ( wait )
            {
                  case WAIT_OBJECT_0:
                        if( index->interrupt == 1 )
                        {
                              goto end;
                        }
                        if( !index->sol.hEvent ) return(1);
                        if (!GetOverlappedResult( index->hComm,
                              &index->sol, &nBytes, TRUE ))
                        {
                              goto end;
                        }
                        else if( index->tx_happened == 1 )
                        {
                              goto end;
                        }
                        else
                              goto end;
                        break;
                  case WAIT_TIMEOUT:
                  default:
                        return(1); /* WaitFor error */
                  
            }
      }
end:
/*
      usleep(1000);
*/
      LEAVE( "serial_select" );
      return( 1 );
#ifdef asdf
      /* FIXME this needs to be cleaned up... */
fail:
      sprintf( message, "< select called error %i\n", n );
      YACK();
      report( message );
      set_errno( EBADFD );
      LEAVE( "serial_select" );
      return( 1 );
#endif /* asdf */
      
}
#endif /* asdf */
#endif /* __LCC__ */

/*----------------------------------------------------------
termiosSetParityError()

   accept:      fd The device opened
   perform:     Get the Parity Error Char
   return:      the Parity Error Char
   exceptions:  none
   win32api:    GetCommState()
   comments:    No idea how to do this in Unix  (handle in read?)
----------------------------------------------------------*/

int termiosGetParityErrorChar( int fd )
{
      struct termios_list *index;
      DCB   dcb;

      ENTER( "termiosGetParityErrorChar" );
      index = find_port( fd );
      if( !index )
      {
            LEAVE( "termiosGetParityErrorChar" );
            return(-1);
      }
      GetCommState( index->hComm, &dcb );
      LEAVE( "termiosGetParityErrorChar" );
      return( dcb.ErrorChar );
}

/*----------------------------------------------------------
termiosSetParityError()

   accept:      fd The device opened, value the new Parity Error Char
   perform:     Set the Parity Error Char
   return:      void
   exceptions:  none
   win32api:    GetCommState(), SetCommState()
   comments:    No idea how to do this in Unix  (handle in read?)
----------------------------------------------------------*/

void termiosSetParityError( int fd, char value )
{
      DCB   dcb;
      struct termios_list *index;

      ENTER( "termiosGetParityErrorChar" );
      index = find_port( fd );
      if ( !index )
      {
            LEAVE( "termiosSetParityError" );
            return;
      }
      GetCommState( index->hComm, &dcb );
      dcb.ErrorChar = value;
      SetCommState( index->hComm, &dcb );
      LEAVE( "termiosGetParityErrorChar" );
}
/*----------------------- END OF LIBRARY -----------------*/

#endif      /* WIN32 */

Generated by  Doxygen 1.6.0   Back to index