884 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			884 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
| /* fhandler_serial.cc
 | |
| 
 | |
|    Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
 | |
| 
 | |
| This file is part of Cygwin.
 | |
| 
 | |
| This software is a copyrighted work licensed under the terms of the
 | |
| Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
 | |
| details. */
 | |
| 
 | |
| #include <fcntl.h>
 | |
| #include <errno.h>
 | |
| #include <unistd.h>
 | |
| #include <stdlib.h>
 | |
| #include "winsup.h"
 | |
| 
 | |
| /**********************************************************************/
 | |
| /* fhandler_serial */
 | |
| 
 | |
| fhandler_serial::fhandler_serial (const char *name, DWORD devtype, int unit) :
 | |
| 	fhandler_base (devtype, name, unit)
 | |
| {
 | |
|   set_cb (sizeof *this);
 | |
|   vmin_ = 0;
 | |
|   vtime_ = 0;
 | |
|   pgrp_ = myself->pgid;
 | |
|   set_need_fork_fixup ();
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_serial::overlapped_setup ()
 | |
| {
 | |
|   memset (&io_status, 0, sizeof (io_status));
 | |
|   io_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
 | |
|   ProtectHandle (io_status.hEvent);
 | |
|   overlapped_armed = 0;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::raw_read (void *ptr, size_t ulen)
 | |
| {
 | |
|   int tot;
 | |
|   DWORD n;
 | |
|   HANDLE w4[2];
 | |
|   DWORD minchars = vmin_ ?: ulen;
 | |
| 
 | |
|   w4[0] = io_status.hEvent;
 | |
|   w4[1] = signal_arrived;
 | |
| 
 | |
|   debug_printf ("ulen %d, vmin_ %d, vtime_ %d, hEvent %p", ulen, vmin_, vtime_,
 | |
| 		io_status.hEvent);
 | |
|   if (!overlapped_armed)
 | |
|     {
 | |
|       (void) SetCommMask (get_handle (), EV_RXCHAR);
 | |
|       ResetEvent (io_status.hEvent);
 | |
|     }
 | |
| 
 | |
|   for (n = 0, tot = 0; ulen; ulen -= n, ptr = (char *)ptr + n)
 | |
|     {
 | |
|       DWORD ev;
 | |
|       COMSTAT st;
 | |
|       DWORD inq = 1;
 | |
| 
 | |
|       n = 0;
 | |
| 
 | |
|       if (!vtime_ && !vmin_)
 | |
| 	inq = ulen;
 | |
|       else if (vtime_)
 | |
| 	{
 | |
| 	  inq = ulen;	// non-interruptible -- have to use kernel timeouts
 | |
| 			// also note that this is not strictly correct.
 | |
| 			// if vmin > ulen then things won't work right.
 | |
| 	  overlapped_armed = -1;
 | |
| 	}
 | |
|       if (!overlapped_armed)
 | |
| 	{
 | |
| 	  if (!ClearCommError (get_handle (), &ev, &st))
 | |
| 	    goto err;
 | |
| 	  else if (ev)
 | |
| 	    termios_printf ("error detected %x", ev);
 | |
| 	  else if (st.cbInQue)
 | |
| 	    inq = st.cbInQue;
 | |
| 	  else if ((size_t)tot >= minchars)
 | |
| 	    break;
 | |
| 	  else if (WaitCommEvent (get_handle (), &ev, &io_status))
 | |
| 	    {
 | |
| 	      debug_printf ("WaitCommEvent succeeded: ev %x", ev);
 | |
| 	      if (!ev)
 | |
| 		continue;
 | |
| 	    }
 | |
| 	  else if (GetLastError () != ERROR_IO_PENDING)
 | |
| 	    goto err;
 | |
| 	  else
 | |
| 	    {
 | |
| 	      overlapped_armed = 1;
 | |
| 	      switch (WaitForMultipleObjects (2, w4, FALSE, INFINITE))
 | |
| 		{
 | |
| 		case WAIT_OBJECT_0:
 | |
| 		  if (!GetOverlappedResult (get_handle (), &io_status, &n, FALSE))
 | |
| 		    goto err;
 | |
| 		  debug_printf ("n %d, ev %x", n, ev);
 | |
| 		  break;
 | |
| 		case WAIT_OBJECT_0 + 1:
 | |
| 		  tot = -1;
 | |
| 		  PurgeComm (get_handle (), PURGE_RXABORT);
 | |
| 		  overlapped_armed = 0;
 | |
| 		  set_sig_errno (EINTR);
 | |
| 		  goto out;
 | |
| 		default:
 | |
| 		  goto err;
 | |
| 		}
 | |
| 	    }
 | |
| 	}
 | |
| 
 | |
|       overlapped_armed = 0;
 | |
|       ResetEvent (io_status.hEvent);
 | |
|       if (inq > ulen)
 | |
| 	inq = ulen;
 | |
|       debug_printf ("inq %d", inq);
 | |
|       if (ReadFile (get_handle(), ptr, min (inq, ulen), &n, &io_status))
 | |
| 	/* Got something */;
 | |
|       else if (GetLastError () != ERROR_IO_PENDING)
 | |
| 	goto err;
 | |
|       else if (!GetOverlappedResult (get_handle (), &io_status, &n, TRUE))
 | |
| 	goto err;
 | |
| 
 | |
|       tot += n;
 | |
|       debug_printf ("vtime_ %d, vmin_ %d, n %d, tot %d", vtime_, vmin_, n, tot);
 | |
|       if (vtime_ || !vmin_ || !n)
 | |
| 	break;
 | |
|       continue;
 | |
| 
 | |
|     err:
 | |
|       PurgeComm (get_handle (), PURGE_RXABORT);
 | |
|       debug_printf ("err %E");
 | |
|       if (GetLastError () == ERROR_OPERATION_ABORTED)
 | |
| 	n = 0;
 | |
|       else
 | |
| 	{
 | |
| 	  tot = -1;
 | |
| 	  __seterrno ();
 | |
| 	  break;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
| out:
 | |
|   return tot;
 | |
| }
 | |
| 
 | |
| /* Cover function to WriteFile to provide Posix interface and semantics
 | |
|    (as much as possible).  */
 | |
| int
 | |
| fhandler_serial::raw_write (const void *ptr, size_t len)
 | |
| {
 | |
|   DWORD bytes_written;
 | |
| 
 | |
|   if (overlapped_armed)
 | |
|     PurgeComm (get_handle (), PURGE_TXABORT | PURGE_RXABORT);
 | |
|   ResetEvent (io_status.hEvent);
 | |
| 
 | |
|   for (;;)
 | |
|     {
 | |
|       overlapped_armed = TRUE;
 | |
|       if (WriteFile (get_handle(), ptr, len, &bytes_written, &io_status))
 | |
| 	break;
 | |
| 
 | |
|       switch (GetLastError ())
 | |
| 	{
 | |
| 	  case ERROR_OPERATION_ABORTED:
 | |
| 	    continue;
 | |
| 	  case ERROR_IO_PENDING:
 | |
| 	    break;
 | |
| 	  default:
 | |
| 	    goto err;
 | |
| 	}
 | |
| 
 | |
|       if (!GetOverlappedResult (get_handle (), &io_status, &bytes_written, TRUE))
 | |
| 	goto err;
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   overlapped_armed = FALSE;
 | |
|   return bytes_written;
 | |
| 
 | |
| err:
 | |
|   __seterrno ();
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_serial::dump (void)
 | |
| {
 | |
|   paranoid_printf ("here");
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_serial::init (HANDLE f, DWORD flags, mode_t bin)
 | |
| {
 | |
|   fhandler_base::init (f, flags, bin);
 | |
|   (void) open (NULL, flags, bin ? O_BINARY : 0);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::open (const char *name, int flags, mode_t mode)
 | |
| {
 | |
|   int res;
 | |
|   COMMTIMEOUTS to;
 | |
|   extern BOOL reset_com;
 | |
| 
 | |
|   syscall_printf ("fhandler_serial::open (%s, %p, %p)",
 | |
| 			get_name (), flags, mode);
 | |
| 
 | |
|   if (name && !(res = this->fhandler_base::open (flags, mode)))
 | |
|     return 0;
 | |
|   else
 | |
|     res = 1;
 | |
| 
 | |
|   (void) SetCommMask (get_handle (), EV_RXCHAR);
 | |
| 
 | |
|   set_r_no_interrupt (1);	// Handled explicitly in read code
 | |
| 
 | |
|   overlapped_setup ();
 | |
| 
 | |
|   memset (&to, 0, sizeof (to));
 | |
|   (void) SetCommTimeouts (get_handle (), &to);
 | |
| 
 | |
|   /* Reset serial port to known state of 9600-8-1-no flow control
 | |
|      on open for better behavior under Win 95.
 | |
| 
 | |
|      FIXME:  This should only be done when explicitly opening the com
 | |
|      port.  It should not be reset if an fd is inherited.
 | |
|      Using __progname in this way, to determine how far along in the
 | |
|      initialization we are, is really a terrible kludge and should
 | |
|      be fixed ASAP.
 | |
|   */
 | |
|   extern char *__progname;
 | |
|   if (reset_com && __progname)
 | |
|     {
 | |
|       DCB state;
 | |
|       GetCommState (get_handle (), &state);
 | |
|       syscall_printf ("setting initial state on %s (reset_com %d)",
 | |
| 		      get_name (), reset_com);
 | |
|       state.BaudRate = CBR_9600;
 | |
|       state.ByteSize = 8;
 | |
|       state.StopBits = ONESTOPBIT;
 | |
|       state.Parity = NOPARITY; /* FIXME: correct default? */
 | |
|       state.fBinary = TRUE; /* binary xfer */
 | |
|       state.EofChar = 0; /* no end-of-data in binary mode */
 | |
|       state.fNull = FALSE; /* don't discard nulls in binary mode */
 | |
|       state.fParity = FALSE; /* ignore parity errors */
 | |
|       state.fErrorChar = FALSE;
 | |
|       state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */
 | |
|       state.fOutX = FALSE; /* disable transmission flow control */
 | |
|       state.fInX = FALSE; /* disable reception flow control */
 | |
|       state.XonChar = 0x11;
 | |
|       state.XoffChar = 0x13;
 | |
|       state.fOutxDsrFlow = FALSE; /* disable DSR flow control */
 | |
|       state.fRtsControl = RTS_CONTROL_ENABLE; /* ignore lead control except
 | |
| 						  DTR */
 | |
|       state.fOutxCtsFlow = FALSE; /* disable output flow control */
 | |
|       state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR */
 | |
|       state.fDsrSensitivity = FALSE; /* don't assert DSR */
 | |
|       state.fAbortOnError = TRUE;
 | |
|       if (!SetCommState (get_handle (), &state))
 | |
| 	system_printf ("couldn't set initial state for %s, %E", get_name ());
 | |
|     }
 | |
| 
 | |
|   SetCommMask (get_handle (), EV_RXCHAR);
 | |
|   syscall_printf ("%p = fhandler_serial::open (%s, %p, %p)",
 | |
| 			res, get_name (), flags, mode);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::close ()
 | |
| {
 | |
|   (void) ForceCloseHandle (io_status.hEvent);
 | |
|   return fhandler_base::close ();
 | |
| }
 | |
| 
 | |
| /* tcsendbreak: POSIX 7.2.2.1 */
 | |
| /* Break for 250-500 milliseconds if duration == 0 */
 | |
| /* Otherwise, units for duration are undefined */
 | |
| int
 | |
| fhandler_serial::tcsendbreak (int duration)
 | |
| {
 | |
|   unsigned int sleeptime = 300;
 | |
| 
 | |
|   if (duration > 0)
 | |
|     sleeptime *= duration;
 | |
| 
 | |
|   if (SetCommBreak (get_handle ()) == 0)
 | |
|     return -1;
 | |
| 
 | |
|   /* FIXME: need to send zero bits during duration */
 | |
|   usleep (sleeptime);
 | |
| 
 | |
|   if (ClearCommBreak (get_handle ()) == 0)
 | |
|     return -1;
 | |
| 
 | |
|   syscall_printf ("0 = fhandler_serial:tcsendbreak (%d)", duration);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* tcdrain: POSIX 7.2.2.1 */
 | |
| int
 | |
| fhandler_serial::tcdrain (void)
 | |
| {
 | |
|   if (FlushFileBuffers (get_handle ()) == 0)
 | |
|     return -1;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* tcflow: POSIX 7.2.2.1 */
 | |
| int
 | |
| fhandler_serial::tcflow (int action)
 | |
| {
 | |
|   DWORD win32action = 0;
 | |
|   DCB dcb;
 | |
|   char xchar;
 | |
| 
 | |
|   termios_printf ("action %d", action);
 | |
| 
 | |
|   switch (action)
 | |
|     {
 | |
|       case TCOOFF:
 | |
| 	win32action = SETXOFF;
 | |
| 	break;
 | |
|       case TCOON:
 | |
| 	win32action = SETXON;
 | |
| 	break;
 | |
|       case TCION:
 | |
|       case TCIOFF:
 | |
| 	if (GetCommState (get_handle (), &dcb) == 0)
 | |
| 	  return -1;
 | |
| 	if (action == TCION)
 | |
| 	  xchar = (dcb.XonChar ? dcb.XonChar : 0x11);
 | |
| 	else
 | |
| 	  xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13);
 | |
| 	if (TransmitCommChar (get_handle (), xchar) == 0)
 | |
| 	  return -1;
 | |
| 	return 0;
 | |
| 	break;
 | |
|       default:
 | |
| 	return -1;
 | |
| 	break;
 | |
|     }
 | |
| 
 | |
|   if (EscapeCommFunction (get_handle (), win32action) == 0)
 | |
|     return -1;
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* tcflush: POSIX 7.2.2.1 */
 | |
| int
 | |
| fhandler_serial::tcflush (int queue)
 | |
| {
 | |
|   if (queue == TCOFLUSH || queue == TCIOFLUSH)
 | |
|     PurgeComm (get_handle (), PURGE_TXABORT | PURGE_TXCLEAR);
 | |
| 
 | |
|   if (queue == TCIFLUSH | queue == TCIOFLUSH)
 | |
|     /* Input flushing by polling until nothing turns up
 | |
|        (we stop after 1000 chars anyway) */
 | |
|     for (int max = 1000; max > 0; max--)
 | |
|       {
 | |
| 	DWORD ev;
 | |
| 	COMSTAT st;
 | |
| 	if (!PurgeComm (get_handle (), PURGE_RXABORT | PURGE_RXCLEAR))
 | |
| 	  break;
 | |
| 	Sleep (100);
 | |
| 	if (!ClearCommError (get_handle (), &ev, &st) || !st.cbInQue)
 | |
| 	  break;
 | |
|       }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* tcsetattr: POSIX 7.2.1.1 */
 | |
| int
 | |
| fhandler_serial::tcsetattr (int action, const struct termios *t)
 | |
| {
 | |
|   /* Possible actions:
 | |
|     TCSANOW:   immediately change attributes.
 | |
|     TCSADRAIN: flush output, then change attributes.
 | |
|     TCSAFLUSH: flush output and discard input, then change attributes.
 | |
|   */
 | |
| 
 | |
|   BOOL dropDTR = FALSE;
 | |
|   COMMTIMEOUTS to;
 | |
|   DCB ostate, state;
 | |
|   unsigned int ovtime = vtime_, ovmin = vmin_;
 | |
| 
 | |
|   termios_printf ("action %d", action);
 | |
|   if ((action == TCSADRAIN) || (action == TCSAFLUSH))
 | |
|     {
 | |
|       FlushFileBuffers (get_handle ());
 | |
|       termios_printf ("flushed file buffers");
 | |
|     }
 | |
|   if (action == TCSAFLUSH)
 | |
|     PurgeComm (get_handle (), (PURGE_RXABORT | PURGE_RXCLEAR));
 | |
| 
 | |
|   /* get default/last comm state */
 | |
|   if (!GetCommState (get_handle (), &ostate))
 | |
|     return -1;
 | |
| 
 | |
|   state = ostate;
 | |
| 
 | |
|   /* -------------- Set baud rate ------------------ */
 | |
|   /* FIXME: WIN32 also has 14400, 56000, 128000, and 256000.
 | |
|      Unix also has 230400. */
 | |
| 
 | |
|   switch (t->c_ospeed)
 | |
|     {
 | |
|       case B0:	/* drop DTR */
 | |
| 	dropDTR = TRUE;
 | |
| 	state.BaudRate = 0;
 | |
| 	break;
 | |
|       case B110:
 | |
| 	state.BaudRate = CBR_110;
 | |
| 	break;
 | |
|       case B300:
 | |
| 	state.BaudRate = CBR_300;
 | |
| 	break;
 | |
|       case B600:
 | |
| 	state.BaudRate = CBR_600;
 | |
| 	break;
 | |
|       case B1200:
 | |
| 	state.BaudRate = CBR_1200;
 | |
| 	break;
 | |
|       case B2400:
 | |
| 	state.BaudRate = CBR_2400;
 | |
| 	break;
 | |
|       case B4800:
 | |
| 	state.BaudRate = CBR_4800;
 | |
| 	break;
 | |
|       case B9600:
 | |
| 	state.BaudRate = CBR_9600;
 | |
| 	break;
 | |
|       case B19200:
 | |
| 	state.BaudRate = CBR_19200;
 | |
| 	break;
 | |
|       case B38400:
 | |
| 	state.BaudRate = CBR_38400;
 | |
| 	break;
 | |
|       case B57600:
 | |
| 	state.BaudRate = CBR_57600;
 | |
| 	break;
 | |
|       case B115200:
 | |
| 	state.BaudRate = CBR_115200;
 | |
| 	break;
 | |
|       default:
 | |
| 	/* Unsupported baud rate! */
 | |
| 	termios_printf ("Invalid t->c_ospeed %d", t->c_ospeed);
 | |
| 	set_errno (EINVAL);
 | |
| 	return -1;
 | |
|     }
 | |
| 
 | |
|   /* -------------- Set byte size ------------------ */
 | |
| 
 | |
|   switch (t->c_cflag & CSIZE)
 | |
|     {
 | |
|       case CS5:
 | |
| 	state.ByteSize = 5;
 | |
| 	break;
 | |
|       case CS6:
 | |
| 	state.ByteSize = 6;
 | |
| 	break;
 | |
|       case CS7:
 | |
| 	state.ByteSize = 7;
 | |
| 	break;
 | |
|       case CS8:
 | |
| 	state.ByteSize = 8;
 | |
| 	break;
 | |
|       default:
 | |
| 	/* Unsupported byte size! */
 | |
| 	termios_printf ("Invalid t->c_cflag byte size %d",
 | |
| 			t->c_cflag & CSIZE);
 | |
| 	set_errno (EINVAL);
 | |
| 	return -1;
 | |
|     }
 | |
| 
 | |
|   /* -------------- Set stop bits ------------------ */
 | |
| 
 | |
|   if (t->c_cflag & CSTOPB)
 | |
|     state.StopBits = TWOSTOPBITS;
 | |
|   else
 | |
|     state.StopBits = ONESTOPBIT;
 | |
| 
 | |
|   /* -------------- Set parity ------------------ */
 | |
| 
 | |
|   if (t->c_cflag & PARENB)
 | |
|     state.Parity = (t->c_cflag & PARODD) ? ODDPARITY : EVENPARITY;
 | |
|   else
 | |
|     state.Parity = NOPARITY;
 | |
| 
 | |
|   state.fBinary = TRUE;     /* Binary transfer */
 | |
|   state.EofChar = 0;        /* No end-of-data in binary mode */
 | |
|   state.fNull = FALSE;      /* Don't discard nulls in binary mode */
 | |
| 
 | |
|   /* -------------- Parity errors ------------------ */
 | |
|   /* fParity combines the function of INPCK and NOT IGNPAR */
 | |
| 
 | |
|   if ((t->c_iflag & INPCK) && !(t->c_iflag & IGNPAR))
 | |
|     state.fParity = TRUE;   /* detect parity errors */
 | |
|   else
 | |
|     state.fParity = FALSE;  /* ignore parity errors */
 | |
| 
 | |
|   /* Only present in Win32, Unix has no equivalent */
 | |
|   state.fErrorChar = FALSE;
 | |
|   state.ErrorChar = 0;
 | |
| 
 | |
|   /* -------------- Set software flow control ------------------ */
 | |
|   /* Set fTXContinueOnXoff to FALSE.  This prevents the triggering of a
 | |
|      premature XON when the remote device interprets a received character
 | |
|      as XON (same as IXANY on the remote side).  Otherwise, a TRUE
 | |
|      value separates the TX and RX functions. */
 | |
| 
 | |
|   state.fTXContinueOnXoff = TRUE;     /* separate TX and RX flow control */
 | |
| 
 | |
|   /* Transmission flow control */
 | |
|   if (t->c_iflag & IXON)
 | |
|     state.fOutX = TRUE;   /* enable */
 | |
|   else
 | |
|     state.fOutX = FALSE;  /* disable */
 | |
| 
 | |
|   /* Reception flow control */
 | |
|   if (t->c_iflag & IXOFF)
 | |
|     state.fInX = TRUE;    /* enable */
 | |
|   else
 | |
|     state.fInX = FALSE;   /* disable */
 | |
| 
 | |
|   /* XoffLim and XonLim are left at default values */
 | |
| 
 | |
|   state.XonChar = (t->c_cc[VSTART] ? t->c_cc[VSTART] : 0x11);
 | |
|   state.XoffChar = (t->c_cc[VSTOP] ? t->c_cc[VSTOP] : 0x13);
 | |
| 
 | |
|   /* -------------- Set hardware flow control ------------------ */
 | |
| 
 | |
|   /* Disable DSR flow control */
 | |
|   state.fOutxDsrFlow = FALSE;
 | |
| 
 | |
|   /* Some old flavors of Unix automatically enabled hardware flow
 | |
|      control when software flow control was not enabled.  Since newer
 | |
|      Unices tend to require explicit setting of hardware flow-control,
 | |
|      this is what we do. */
 | |
| 
 | |
|   /* RTS/CTS flow control */
 | |
|   if (t->c_cflag & CRTSCTS)
 | |
|     {							/* enable */
 | |
|       state.fOutxCtsFlow = TRUE;
 | |
|       state.fRtsControl = RTS_CONTROL_HANDSHAKE;
 | |
|     }
 | |
|   else
 | |
|     {							/* disable */
 | |
|       state.fRtsControl = RTS_CONTROL_ENABLE;
 | |
|       state.fOutxCtsFlow = FALSE;
 | |
|     }
 | |
| 
 | |
|   if (t->c_cflag & CRTSXOFF)
 | |
|     state.fRtsControl = RTS_CONTROL_HANDSHAKE;
 | |
| 
 | |
|   /* -------------- DTR ------------------ */
 | |
|   /* Assert DTR on device open */
 | |
| 
 | |
|   state.fDtrControl = DTR_CONTROL_ENABLE;
 | |
| 
 | |
|   /* -------------- DSR ------------------ */
 | |
|   /* Assert DSR at the device? */
 | |
| 
 | |
|   if (t->c_cflag & CLOCAL)
 | |
|     state.fDsrSensitivity = FALSE;  /* no */
 | |
|   else
 | |
|     state.fDsrSensitivity = TRUE;   /* yes */
 | |
| 
 | |
|   /* -------------- Error handling ------------------ */
 | |
|   /* Since read/write operations terminate upon error, we
 | |
|      will use ClearCommError() to resume. */
 | |
| 
 | |
|   state.fAbortOnError = TRUE;
 | |
| 
 | |
|   /* -------------- Set state and exit ------------------ */
 | |
|   if (memcmp (&ostate, &state, sizeof (state)) != 0)
 | |
|     SetCommState (get_handle (), &state);
 | |
| 
 | |
|   set_r_binary ((t->c_iflag & IGNCR) ? 0 : 1);
 | |
|   set_w_binary ((t->c_oflag & ONLCR) ? 0 : 1);
 | |
| 
 | |
|   if (dropDTR == TRUE)
 | |
|     EscapeCommFunction (get_handle (), CLRDTR);
 | |
|   else
 | |
|     {
 | |
|       /* FIXME: Sometimes when CLRDTR is set, setting
 | |
|       state.fDtrControl = DTR_CONTROL_ENABLE will fail.  This
 | |
|       is a problem since a program might want to change some
 | |
|       parameters while DTR is still down. */
 | |
| 
 | |
|       EscapeCommFunction (get_handle (), SETDTR);
 | |
|     }
 | |
| 
 | |
|   /*
 | |
|   The following documentation on was taken from "Linux Serial Programming
 | |
|   HOWTO".  It explains how MIN (t->c_cc[VMIN] || vmin_) and TIME
 | |
|   (t->c_cc[VTIME] || vtime_) is to be used.
 | |
| 
 | |
|   In non-canonical input processing mode, input is not assembled into
 | |
|   lines and input processing (erase, kill, delete, etc.) does not
 | |
|   occur. Two parameters control the behavior of this mode: c_cc[VTIME]
 | |
|   sets the character timer, and c_cc[VMIN] sets the minimum number of
 | |
|   characters to receive before satisfying the read.
 | |
| 
 | |
|   If MIN > 0 and TIME = 0, MIN sets the number of characters to receive
 | |
|   before the read is satisfied. As TIME is zero, the timer is not used.
 | |
| 
 | |
|   If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will
 | |
|   be satisfied if a single character is read, or TIME is exceeded (t =
 | |
|   TIME *0.1 s). If TIME is exceeded, no character will be returned.
 | |
| 
 | |
|   If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The
 | |
|   read will be satisfied if MIN characters are received, or the time
 | |
|   between two characters exceeds TIME. The timer is restarted every time
 | |
|   a character is received and only becomes active after the first
 | |
|   character has been received.
 | |
| 
 | |
|   If MIN = 0 and TIME = 0, read will be satisfied immediately. The
 | |
|   number of characters currently available, or the number of characters
 | |
|   requested will be returned. According to Antonino (see contributions),
 | |
|   you could issue a fcntl(fd, F_SETFL, FNDELAY); before reading to get
 | |
|   the same result.
 | |
|   */
 | |
| 
 | |
|   if (t->c_lflag & ICANON)
 | |
|     {
 | |
|       vmin_ = MAXDWORD;
 | |
|       vtime_ = 0;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       vtime_ = t->c_cc[VTIME] * 100;
 | |
|       vmin_ = t->c_cc[VMIN];
 | |
|     }
 | |
| 
 | |
|   debug_printf ("vtime %d, vmin %d\n", vtime_, vmin_);
 | |
| 
 | |
|   if (ovmin == vmin_ && ovtime == vtime_)
 | |
|     return 0;
 | |
| 
 | |
|   memset (&to, 0, sizeof (to));
 | |
| 
 | |
|   if ((vmin_ > 0) && (vtime_ == 0))
 | |
|     {
 | |
|       /* Returns immediately with whatever is in buffer on a ReadFile();
 | |
| 	 or blocks if nothing found.  We will keep calling ReadFile(); until
 | |
| 	 vmin_ characters are read */
 | |
|       to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD;
 | |
|       to.ReadTotalTimeoutConstant = MAXDWORD - 1;
 | |
|     }
 | |
|   else if ((vmin_ == 0) && (vtime_ > 0))
 | |
|     {
 | |
|       /* set timeoout constant appropriately and we will only try to
 | |
| 	 read one character in ReadFile() */
 | |
|       to.ReadTotalTimeoutConstant = vtime_;
 | |
|       to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD;
 | |
|     }
 | |
|   else if ((vmin_ > 0) && (vtime_ > 0))
 | |
|     {
 | |
|       /* time applies to the interval time for this case */
 | |
|       to.ReadIntervalTimeout = vtime_;
 | |
|     }
 | |
|   else if ((vmin_ == 0) && (vtime_ == 0))
 | |
|     {
 | |
|       /* returns immediately with whatever is in buffer as per
 | |
| 	 Time-Outs docs in Win32 SDK API docs */
 | |
|       to.ReadIntervalTimeout = MAXDWORD;
 | |
|     }
 | |
| 
 | |
|   debug_printf ("ReadTotalTimeoutConstant %d, ReadIntervalTimeout %d, ReadTotalTimeoutMultiplier %d",
 | |
| 		to.ReadTotalTimeoutConstant, to.ReadIntervalTimeout, to.ReadTotalTimeoutMultiplier);
 | |
|   int res = SetCommTimeouts (get_handle (), &to);
 | |
|   if (!res)
 | |
|     {
 | |
|       system_printf ("SetCommTimeout failed, %E");
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /* tcgetattr: POSIX 7.2.1.1 */
 | |
| int
 | |
| fhandler_serial::tcgetattr (struct termios *t)
 | |
| {
 | |
|   DCB state;
 | |
| 
 | |
|   /* Get current Win32 comm state */
 | |
|   if (GetCommState (get_handle (), &state) == 0)
 | |
|     return -1;
 | |
| 
 | |
|   /* for safety */
 | |
|   memset (t, 0, sizeof (*t));
 | |
| 
 | |
|   /* -------------- Baud rate ------------------ */
 | |
| 
 | |
|   switch (state.BaudRate)
 | |
|     {
 | |
|       case 0:
 | |
| 	/* FIXME: need to drop DTR */
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B0;
 | |
| 	break;
 | |
|       case CBR_110:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B110;
 | |
| 	break;
 | |
|       case CBR_300:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B300;
 | |
| 	break;
 | |
|       case CBR_600:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B600;
 | |
| 	break;
 | |
|       case CBR_1200:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B1200;
 | |
| 	break;
 | |
|       case CBR_2400:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B2400;
 | |
| 	break;
 | |
|       case CBR_4800:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B4800;
 | |
| 	break;
 | |
|       case CBR_9600:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B9600;
 | |
| 	break;
 | |
|       case CBR_19200:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B19200;
 | |
| 	break;
 | |
|       case CBR_38400:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B38400;
 | |
| 	break;
 | |
|       case CBR_57600:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B57600;
 | |
| 	break;
 | |
|       case CBR_115200:
 | |
| 	t->c_cflag = t->c_ospeed = t->c_ispeed = B115200;
 | |
| 	break;
 | |
|       default:
 | |
| 	/* Unsupported baud rate! */
 | |
| 	termios_printf ("Invalid baud rate %d", state.BaudRate);
 | |
| 	set_errno (EINVAL);
 | |
| 	return -1;
 | |
|     }
 | |
| 
 | |
|   /* -------------- Byte size ------------------ */
 | |
| 
 | |
|   switch (state.ByteSize)
 | |
|     {
 | |
|       case 5:
 | |
| 	t->c_cflag |= CS5;
 | |
| 	break;
 | |
|       case 6:
 | |
| 	t->c_cflag |= CS6;
 | |
| 	break;
 | |
|       case 7:
 | |
| 	t->c_cflag |= CS7;
 | |
| 	break;
 | |
|       case 8:
 | |
| 	t->c_cflag |= CS8;
 | |
| 	break;
 | |
|       default:
 | |
| 	/* Unsupported byte size! */
 | |
| 	termios_printf ("Invalid byte size %d", state.ByteSize);
 | |
| 	set_errno (EINVAL);
 | |
| 	return -1;
 | |
|     }
 | |
| 
 | |
|   /* -------------- Stop bits ------------------ */
 | |
| 
 | |
|   if (state.StopBits == TWOSTOPBITS)
 | |
|     t->c_cflag |= CSTOPB;
 | |
| 
 | |
|   /* -------------- Parity ------------------ */
 | |
| 
 | |
|   if (state.Parity == ODDPARITY)
 | |
|     t->c_cflag |= (PARENB | PARODD);
 | |
|   if (state.Parity == EVENPARITY)
 | |
|     t->c_cflag |= PARENB;
 | |
| 
 | |
|   /* -------------- Parity errors ------------------ */
 | |
| 
 | |
|   /* fParity combines the function of INPCK and NOT IGNPAR */
 | |
|   if (state.fParity == TRUE)
 | |
|     t->c_iflag |= INPCK;
 | |
|   else
 | |
|     t->c_iflag |= IGNPAR;	/* not necessarily! */
 | |
| 
 | |
|   /* -------------- Software flow control ------------------ */
 | |
| 
 | |
|   /* transmission flow control */
 | |
|   if (state.fOutX)
 | |
|     t->c_iflag |= IXON;
 | |
| 
 | |
|   /* reception flow control */
 | |
|   if (state.fInX)
 | |
|     t->c_iflag |= IXOFF;
 | |
| 
 | |
|   t->c_cc[VSTART] = (state.XonChar ? state.XonChar : 0x11);
 | |
|   t->c_cc[VSTOP] = (state.XoffChar ? state.XoffChar : 0x13);
 | |
| 
 | |
|   /* -------------- Hardware flow control ------------------ */
 | |
|   /* Some old flavors of Unix automatically enabled hardware flow
 | |
|      control when software flow control was not enabled.  Since newer
 | |
|      Unices tend to require explicit setting of hardware flow-control,
 | |
|      this is what we do. */
 | |
| 
 | |
|   /* Input flow-control */
 | |
|   if ((state.fRtsControl == RTS_CONTROL_HANDSHAKE) &&
 | |
|       (state.fOutxCtsFlow == TRUE))
 | |
|     t->c_cflag |= CRTSCTS;
 | |
|   if (state.fRtsControl == RTS_CONTROL_HANDSHAKE)
 | |
|     t->c_cflag |= CRTSXOFF;
 | |
| 
 | |
|   /* -------------- CLOCAL --------------- */
 | |
|   /* DSR is only lead toggled only by CLOCAL.  Check it to see if
 | |
|      CLOCAL was called. */
 | |
|   /* FIXME: If tcsetattr() hasn't been called previously, this may
 | |
|      give a false CLOCAL. */
 | |
| 
 | |
|   if (state.fDsrSensitivity == FALSE)
 | |
|     t->c_cflag |= CLOCAL;
 | |
| 
 | |
|   /* FIXME: need to handle IGNCR */
 | |
| #if 0
 | |
|   if (!get_r_binary ())
 | |
|     t->c_iflag |= IGNCR;
 | |
| #endif
 | |
| 
 | |
|   if (!get_w_binary ())
 | |
|     t->c_oflag |= ONLCR;
 | |
| 
 | |
|   debug_printf ("vmin_ %d, vtime_ %d", vmin_, vtime_);
 | |
|   if (vmin_ == MAXDWORD)
 | |
|     {
 | |
|       t->c_lflag |= ICANON;
 | |
|       t->c_cc[VTIME] = t->c_cc[VMIN] = 0;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       t->c_cc[VTIME] = vtime_ / 100;
 | |
|       t->c_cc[VMIN] = vmin_;
 | |
|     }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_serial::fixup_after_fork (HANDLE parent)
 | |
| {
 | |
|   if (get_close_on_exec ())
 | |
|     this->fhandler_base::fixup_after_fork (parent);
 | |
|   overlapped_setup ();
 | |
|   debug_printf ("io_status.hEvent %p", io_status.hEvent);
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::de_linearize (const char *buf, const char *unix_name,
 | |
| 			       const char *win32_name)
 | |
| {
 | |
|   int res = fhandler_base::de_linearize (buf, unix_name, win32_name);
 | |
|   overlapped_setup ();
 | |
|   debug_printf ("io_status.hEvent %p", io_status.hEvent);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::dup (fhandler_base *child)
 | |
| {
 | |
|   fhandler_serial *fhc = (fhandler_serial *) child;
 | |
|   overlapped_setup ();
 | |
|   fhc->vmin_ = vmin_;
 | |
|   fhc->vtime_ = vtime_;
 | |
|   return fhandler_base::dup (child);
 | |
| }
 |