* sigproc.cc (wait_for_sigthread): Ditto. Don't synchronize with wait_sig after receiving an event that it is ready to go. (init_sig_pipe): New function. (wait_sig): Call init_sig_pipe to create pipes for communicating signals to this process. Don't send sigCONT signal when initializing. * fork.cc (frok::child): Accommodate wait_for_sigpipe parameter change. * fhandler.h (fhandler_*::write): Make ssize_t/__stdcall. (fhandler_*::write_overlapped): Ditto. (fhandler_*::raw_write): Ditto. (fhandler_*::readv): Ditto. (fhandler_*::writev): Ditto. (fhandler_*::raw_read): Make __stdcall. * fhandler: Accommodate changes to read/write functions throughout. * fhandler_clipboard.cc: Ditto. * fhandler_console.cc: Ditto. * fhandler_dsp.cc: Ditto. * fhandler_fifo.cc: Ditto. * fhandler_mailslot.cc: Ditto. * fhandler_mem.cc: Ditto. * fhandler_mem.cc: Ditto. * fhandler_random.cc: Ditto. * fhandler_tape.cc: Ditto. * fhandler_tty.cc: Ditto. * fhandler_virtual.cc: Ditto. * fhandler_windows.cc: Ditto. * fhandler_zero.cc: Ditto. * syscalls.cc (readv): Use ssize_t as temp variable. * fhandler.cc (fhandler_base::read): Coerce returned len to signed or it will never be treated as < 0. (fhandler_base::wait_overlapped): Minimize calls to GetLastError. Remove duplicate debugging test. Fix error return. * fhandler.h (fhandler_fifo::fifo_name): Declare new function. (fhandler_fifo::close): Ditto. (fhandler_fifo::dup): Ditto. (fhandler_fifo::close_on_exec): Ditto. * fhandler.cc (fhandler_fifo::fifo_name): Define new function. (FIFO_BUF_SIZE): New define. (cnp): Ditto. (fhandler_fifo::open): Rework. Use cnp to open named pipe. Always open write side as a client. Open dummy client when writing and can't connect. (wait): Rework. Implement fifo_wait_for_next_client. Handle signals during connect better. Add new fifo_wait_for_server code which polls (sigh) waiting for server. (fhandler_fifo::raw_read): Handle transition states when one client closes and another is available. (fhandler_fifo::close): Define. (fhandler_fifo::dup): Ditto. (fhandler_fifo::close_on_exec): Ditto.
		
			
				
	
	
		
			1128 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1128 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* fhandler_serial.cc
 | |
| 
 | |
|    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
 | |
|    2006, 2007, 2008, 2009 Red Hat, Inc.
 | |
| 
 | |
| 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 "winsup.h"
 | |
| #include <unistd.h>
 | |
| #include "cygerrno.h"
 | |
| #include "security.h"
 | |
| #include "path.h"
 | |
| #include "fhandler.h"
 | |
| #include "sigproc.h"
 | |
| #include "pinfo.h"
 | |
| #include <ddk/ntddser.h>
 | |
| 
 | |
| /**********************************************************************/
 | |
| /* fhandler_serial */
 | |
| 
 | |
| fhandler_serial::fhandler_serial ()
 | |
|   : fhandler_base (), vmin_ (0), vtime_ (0), pgrp_ (myself->pgid)
 | |
| {
 | |
|   need_fork_fixup (true);
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| void __stdcall
 | |
| fhandler_serial::raw_read (void *ptr, size_t& ulen)
 | |
| {
 | |
|   int tot;
 | |
|   DWORD n;
 | |
|   HANDLE w4[2];
 | |
|   size_t minchars = vmin_ ? min (vmin_, ulen) : 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)
 | |
|     {
 | |
|       SetCommMask (get_handle (), EV_RXCHAR);
 | |
|       ResetEvent (io_status.hEvent);
 | |
|     }
 | |
| 
 | |
|   for (n = 0, tot = 0; ulen; ulen -= n, ptr = (char *) ptr + n)
 | |
|     {
 | |
|       COMSTAT st;
 | |
|       DWORD inq = vmin_ ? minchars : vtime_ ? ulen : 1;
 | |
| 
 | |
|       n = 0;
 | |
| 
 | |
|       if (vtime_) // non-interruptible -- have to use kernel timeouts
 | |
| 	overlapped_armed = -1;
 | |
| 
 | |
|       if (!ClearCommError (get_handle (), &ev, &st))
 | |
| 	goto err;
 | |
|       else if (ev)
 | |
| 	termios_printf ("error detected %x", ev);
 | |
|       else if (st.cbInQue && !vtime_)
 | |
| 	inq = st.cbInQue;
 | |
|       else if (!overlapped_armed)
 | |
| 	{
 | |
| 	  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, inq, &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:
 | |
|       debug_printf ("err %E");
 | |
|       if (GetLastError () != ERROR_OPERATION_ABORTED)
 | |
| 	{
 | |
| 	  PurgeComm (get_handle (), PURGE_RXABORT);
 | |
| 	  tot = -1;
 | |
| 	  __seterrno ();
 | |
| 	  break;
 | |
| 	}
 | |
| 
 | |
|       n = 0;
 | |
|     }
 | |
| 
 | |
| out:
 | |
|   ulen = tot;
 | |
| }
 | |
| 
 | |
| /* Cover function to WriteFile to provide Posix interface and semantics
 | |
|    (as much as possible).  */
 | |
| ssize_t __stdcall
 | |
| fhandler_serial::raw_write (const void *ptr, size_t len)
 | |
| {
 | |
|   DWORD bytes_written;
 | |
|   OVERLAPPED write_status;
 | |
| 
 | |
|   memset (&write_status, 0, sizeof (write_status));
 | |
|   write_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
 | |
|   ProtectHandle (write_status.hEvent);
 | |
| 
 | |
|   for (;;)
 | |
|     {
 | |
|       if (WriteFile (get_handle (), ptr, len, &bytes_written, &write_status))
 | |
| 	break;
 | |
| 
 | |
|       switch (GetLastError ())
 | |
| 	{
 | |
| 	case ERROR_OPERATION_ABORTED:
 | |
| 	  DWORD ev;
 | |
| 	  if (!ClearCommError (get_handle (), &ev, NULL))
 | |
| 	    goto err;
 | |
| 	  if (ev)
 | |
| 	    termios_printf ("error detected %x", ev);
 | |
| 	  continue;
 | |
| 	case ERROR_IO_PENDING:
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  goto err;
 | |
| 	}
 | |
| 
 | |
|       if (!GetOverlappedResult (get_handle (), &write_status, &bytes_written, TRUE))
 | |
| 	goto err;
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   ForceCloseHandle (write_status.hEvent);
 | |
| 
 | |
|   return bytes_written;
 | |
| 
 | |
| err:
 | |
|   __seterrno ();
 | |
|   ForceCloseHandle (write_status.hEvent);
 | |
|   return -1;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::init (HANDLE f, DWORD flags, mode_t bin)
 | |
| {
 | |
|   return open (flags, bin & (O_BINARY | O_TEXT));
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::open (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 (!fhandler_base::open (flags, mode))
 | |
|     return 0;
 | |
| 
 | |
|   res = 1;
 | |
| 
 | |
|   SetCommMask (get_handle (), EV_RXCHAR);
 | |
| 
 | |
|   uninterruptible_io (true);	// Handled explicitly in read code
 | |
| 
 | |
|   overlapped_setup ();
 | |
| 
 | |
|   memset (&to, 0, sizeof (to));
 | |
|   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.
 | |
|   */
 | |
|   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);
 | |
|   set_open_status ();
 | |
|   syscall_printf ("%p = fhandler_serial::open (%s, %p, %p)",
 | |
| 			res, get_name (), flags, mode);
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::close ()
 | |
| {
 | |
|   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 = 300000;
 | |
| 
 | |
|   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 ()
 | |
| {
 | |
|   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;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* switch_modem_lines: set or clear RTS and/or DTR */
 | |
| int
 | |
| fhandler_serial::switch_modem_lines (int set, int clr)
 | |
| {
 | |
|   int res = 0;
 | |
| 
 | |
|   if (set & TIOCM_RTS)
 | |
|     {
 | |
|       if (EscapeCommFunction (get_handle (), SETRTS))
 | |
| 	rts = TIOCM_RTS;
 | |
|       else
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  res = -1;
 | |
| 	}
 | |
|     }
 | |
|   else if (clr & TIOCM_RTS)
 | |
|     {
 | |
|       if (EscapeCommFunction (get_handle (), CLRRTS))
 | |
| 	rts = 0;
 | |
|       else
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  res = -1;
 | |
| 	}
 | |
|     }
 | |
|   if (set & TIOCM_DTR)
 | |
|     {
 | |
|       if (EscapeCommFunction (get_handle (), SETDTR))
 | |
| 	rts = TIOCM_DTR;
 | |
|       else
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  res = -1;
 | |
| 	}
 | |
|     }
 | |
|   else if (clr & TIOCM_DTR)
 | |
|     {
 | |
|       if (EscapeCommFunction (get_handle (), CLRDTR))
 | |
| 	rts = 0;
 | |
|       else
 | |
| 	{
 | |
| 	  __seterrno ();
 | |
| 	  res = -1;
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /* ioctl: */
 | |
| int
 | |
| fhandler_serial::ioctl (unsigned int cmd, void *buffer)
 | |
| {
 | |
|   int res = 0;
 | |
| 
 | |
| # define ibuffer ((int) buffer)
 | |
| # define ipbuffer (*(int *) buffer)
 | |
| 
 | |
|   DWORD ev;
 | |
|   COMSTAT st;
 | |
|   if (!ClearCommError (get_handle (), &ev, &st))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       res = -1;
 | |
|     }
 | |
|   else
 | |
|     switch (cmd)
 | |
|       {
 | |
|       case TCFLSH:
 | |
| 	res = tcflush (ibuffer);
 | |
| 	break;
 | |
|       case TIOCMGET:
 | |
| 	DWORD modem_lines;
 | |
| 	if (!GetCommModemStatus (get_handle (), &modem_lines))
 | |
| 	  {
 | |
| 	    __seterrno ();
 | |
| 	    res = -1;
 | |
| 	  }
 | |
| 	else
 | |
| 	  {
 | |
| 	    ipbuffer = 0;
 | |
| 	    if (modem_lines & MS_CTS_ON)
 | |
| 	      ipbuffer |= TIOCM_CTS;
 | |
| 	    if (modem_lines & MS_DSR_ON)
 | |
| 	      ipbuffer |= TIOCM_DSR;
 | |
| 	    if (modem_lines & MS_RING_ON)
 | |
| 	      ipbuffer |= TIOCM_RI;
 | |
| 	    if (modem_lines & MS_RLSD_ON)
 | |
| 	      ipbuffer |= TIOCM_CD;
 | |
| 
 | |
| 	    DWORD cb;
 | |
| 	    DWORD mcr;
 | |
| 	    if (!DeviceIoControl (get_handle (), IOCTL_SERIAL_GET_DTRRTS,
 | |
| 				  NULL, 0, &mcr, 4, &cb, 0) || cb != 4)
 | |
| 	      ipbuffer |= rts | dtr;
 | |
| 	    else
 | |
| 	      {
 | |
| 		if (mcr & 2)
 | |
| 		  ipbuffer |= TIOCM_RTS;
 | |
| 		if (mcr & 1)
 | |
| 		  ipbuffer |= TIOCM_DTR;
 | |
| 	      }
 | |
| 	  }
 | |
| 	break;
 | |
|       case TIOCMSET:
 | |
| 	if (switch_modem_lines (ipbuffer, ~ipbuffer))
 | |
| 	  res = -1;
 | |
| 	break;
 | |
|       case TIOCMBIS:
 | |
| 	if (switch_modem_lines (ipbuffer, 0))
 | |
| 	  res = -1;
 | |
| 	break;
 | |
|       case TIOCMBIC:
 | |
| 	if (switch_modem_lines (0, ipbuffer))
 | |
| 	  res = -1;
 | |
| 	break;
 | |
|       case TIOCCBRK:
 | |
| 	if (ClearCommBreak (get_handle ()) == 0)
 | |
| 	  {
 | |
| 	    __seterrno ();
 | |
| 	    res = -1;
 | |
| 	  }
 | |
| 	break;
 | |
|       case TIOCSBRK:
 | |
| 	if (SetCommBreak (get_handle ()) == 0)
 | |
| 	  {
 | |
| 	    __seterrno ();
 | |
| 	    res = -1;
 | |
| 	  }
 | |
| 	break;
 | |
|      case TIOCINQ:
 | |
|        if (ev & CE_FRAME || ev & CE_IOE || ev & CE_OVERRUN || ev & CE_RXOVER
 | |
| 	   || ev & CE_RXPARITY)
 | |
| 	 {
 | |
| 	   set_errno (EINVAL);	/* FIXME: Use correct errno */
 | |
| 	   res = -1;
 | |
| 	 }
 | |
|        else
 | |
| 	 ipbuffer = st.cbInQue;
 | |
|        break;
 | |
|      case TIOCGWINSZ:
 | |
|        ((struct winsize *) buffer)->ws_row = 0;
 | |
|        ((struct winsize *) buffer)->ws_col = 0;
 | |
|        break;
 | |
|      default:
 | |
|        set_errno (ENOSYS);
 | |
|        res = -1;
 | |
|        break;
 | |
|      }
 | |
| 
 | |
|   termios_printf ("%d = ioctl (%p, %p)", res, cmd, buffer);
 | |
| # undef ibuffer
 | |
| # undef ipbuffer
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /* tcflush: POSIX 7.2.2.1 */
 | |
| int
 | |
| fhandler_serial::tcflush (int queue)
 | |
| {
 | |
|   DWORD flags;
 | |
| 
 | |
|   switch (queue)
 | |
|     {
 | |
|     case TCOFLUSH:
 | |
|       flags = PURGE_TXABORT | PURGE_TXCLEAR;
 | |
|       break;
 | |
|     case TCIFLUSH:
 | |
|       flags = PURGE_RXABORT | PURGE_RXCLEAR;
 | |
|       break;
 | |
|     case TCIOFLUSH:
 | |
|       flags = PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_RXCLEAR;
 | |
|       break;
 | |
|     default:
 | |
|       termios_printf ("Invalid tcflush queue %d", queue);
 | |
|       set_errno (EINVAL);
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   if (!PurgeComm (get_handle (), flags))
 | |
|     {
 | |
|       __seterrno ();
 | |
|       return -1;
 | |
|     }
 | |
| 
 | |
|   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_;
 | |
|   int tmpDtr, tmpRts, res;
 | |
|   res = tmpDtr = tmpRts = 0;
 | |
| 
 | |
|   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 - but leave DCB-resident bitrate as-is since
 | |
| 	 0 is an invalid bitrate in Win32 */
 | |
|       dropDTR = true;
 | |
|       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;
 | |
|     case B128000:
 | |
|       state.BaudRate = CBR_128000;
 | |
|       break;
 | |
|     case B230400:
 | |
|       state.BaudRate = 230400 /* CBR_230400 - not defined */;
 | |
|       break;
 | |
|     case B256000:
 | |
|       state.BaudRate = CBR_256000;
 | |
|       break;
 | |
|     case B460800:
 | |
|       state.BaudRate = 460800 /* CBR_460800 - not defined */;
 | |
|       break;
 | |
|     case B500000:
 | |
|       state.BaudRate = 500000 /* CBR_500000 - not defined */;
 | |
|       break;
 | |
|     case B576000:
 | |
|       state.BaudRate = 576000 /* CBR_576000 - not defined */;
 | |
|       break;
 | |
|     case B921600:
 | |
|       state.BaudRate = 921600 /* CBR_921600 - not defined */;
 | |
|       break;
 | |
|     case B1000000:
 | |
|       state.BaudRate = 1000000 /* CBR_1000000 - not defined */;
 | |
|       break;
 | |
|     case B1152000:
 | |
|       state.BaudRate = 1152000 /* CBR_1152000 - not defined */;
 | |
|       break;
 | |
|     case B1500000:
 | |
|       state.BaudRate = 1500000 /* CBR_1500000 - not defined */;
 | |
|       break;
 | |
|     case B2000000:
 | |
|       state.BaudRate = 2000000 /* CBR_2000000 - not defined */;
 | |
|       break;
 | |
|     case B2500000:
 | |
|       state.BaudRate = 2500000 /* CBR_2500000 - not defined */;
 | |
|       break;
 | |
|     case B3000000:
 | |
|       state.BaudRate = 3000000 /* CBR_3000000 - not defined */;
 | |
|       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;
 | |
|       tmpRts = TIOCM_RTS;
 | |
|     }
 | |
| 
 | |
|   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;
 | |
| 
 | |
|   if ((memcmp (&ostate, &state, sizeof (state)) != 0)
 | |
|       && !SetCommState (get_handle (), &state))
 | |
|     {
 | |
|       /* SetCommState() failed, usually due to invalid DCB param.
 | |
| 	 Keep track of this so we can set errno to EINVAL later
 | |
| 	 and return failure */
 | |
|       termios_printf ("SetCommState() failed, %E");
 | |
|       __seterrno ();
 | |
|       res = -1;
 | |
|     }
 | |
| 
 | |
|   rbinary ((t->c_iflag & IGNCR) ? false : true);
 | |
|   wbinary ((t->c_oflag & ONLCR) ? false : true);
 | |
| 
 | |
|   if (dropDTR)
 | |
|     {
 | |
|       EscapeCommFunction (get_handle (), CLRDTR);
 | |
|       tmpDtr = 0;
 | |
|     }
 | |
|   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);
 | |
|       tmpDtr = TIOCM_DTR;
 | |
|     }
 | |
| 
 | |
|   rts = tmpRts;
 | |
|   dtr = tmpDtr;
 | |
| 
 | |
|   /* 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_ = 0;
 | |
|       vtime_ = 0;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       vtime_ = t->c_cc[VTIME] * 100;
 | |
|       vmin_ = t->c_cc[VMIN];
 | |
|     }
 | |
| 
 | |
|   debug_printf ("vtime %d, vmin %d", vtime_, vmin_);
 | |
| 
 | |
|   if (ovmin != vmin_ || ovtime != vtime_)
 | |
|   {
 | |
|     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);
 | |
| 
 | |
|     if (!SetCommTimeouts(get_handle (), &to))
 | |
|       {
 | |
| 	/* SetCommTimeouts() failed. Keep track of this so we
 | |
| 	   can set errno to EINVAL later and return failure */
 | |
| 	termios_printf ("SetCommTimeouts() failed, %E");
 | |
| 	__seterrno ();
 | |
| 	res = -1;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| /* 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));
 | |
| 
 | |
|   t->c_cflag = 0;
 | |
|   /* -------------- Baud rate ------------------ */
 | |
|   switch (state.BaudRate)
 | |
|     {
 | |
|     case CBR_110:
 | |
| 	t->c_ospeed = t->c_ispeed = B110;
 | |
| 	break;
 | |
|     case CBR_300:
 | |
| 	t->c_ospeed = t->c_ispeed = B300;
 | |
| 	break;
 | |
|     case CBR_600:
 | |
| 	t->c_ospeed = t->c_ispeed = B600;
 | |
| 	break;
 | |
|     case CBR_1200:
 | |
| 	t->c_ospeed = t->c_ispeed = B1200;
 | |
| 	break;
 | |
|     case CBR_2400:
 | |
| 	t->c_ospeed = t->c_ispeed = B2400;
 | |
| 	break;
 | |
|     case CBR_4800:
 | |
| 	t->c_ospeed = t->c_ispeed = B4800;
 | |
| 	break;
 | |
|     case CBR_9600:
 | |
| 	t->c_ospeed = t->c_ispeed = B9600;
 | |
| 	break;
 | |
|     case CBR_19200:
 | |
| 	t->c_ospeed = t->c_ispeed = B19200;
 | |
| 	break;
 | |
|     case CBR_38400:
 | |
| 	t->c_ospeed = t->c_ispeed = B38400;
 | |
| 	break;
 | |
|     case CBR_57600:
 | |
| 	t->c_ospeed = t->c_ispeed = B57600;
 | |
| 	break;
 | |
|     case CBR_115200:
 | |
| 	t->c_ospeed = t->c_ispeed = B115200;
 | |
| 	break;
 | |
|     case CBR_128000:
 | |
| 	t->c_ospeed = t->c_ispeed = B128000;
 | |
| 	break;
 | |
|     case 230400: /* CBR_230400 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B230400;
 | |
| 	break;
 | |
|     case CBR_256000:
 | |
| 	t->c_ospeed = t->c_ispeed = B256000;
 | |
| 	break;
 | |
|     case 460800: /* CBR_460000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B460800;
 | |
| 	break;
 | |
|     case 500000: /* CBR_500000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B500000;
 | |
| 	break;
 | |
|     case 576000: /* CBR_576000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B576000;
 | |
| 	break;
 | |
|     case 921600: /* CBR_921600 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B921600;
 | |
| 	break;
 | |
|     case 1000000: /* CBR_1000000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B1000000;
 | |
| 	break;
 | |
|     case 1152000: /* CBR_1152000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B1152000;
 | |
| 	break;
 | |
|     case 1500000: /* CBR_1500000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B1500000;
 | |
| 	break;
 | |
|     case 2000000: /* CBR_2000000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B2000000;
 | |
| 	break;
 | |
|     case 2500000: /* CBR_2500000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B2500000;
 | |
| 	break;
 | |
|     case 3000000: /* CBR_3000000 - not defined */
 | |
| 	t->c_ospeed = t->c_ispeed = B3000000;
 | |
| 	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)
 | |
|     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)
 | |
|     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)
 | |
|     t->c_cflag |= CLOCAL;
 | |
| 
 | |
|   /* FIXME: need to handle IGNCR */
 | |
| #if 0
 | |
|   if (!rbinary ())
 | |
|     t->c_iflag |= IGNCR;
 | |
| #endif
 | |
| 
 | |
|   if (!wbinary ())
 | |
|     t->c_oflag |= ONLCR;
 | |
| 
 | |
|   t->c_cc[VTIME] = vtime_ / 100;
 | |
|   t->c_cc[VMIN] = vmin_;
 | |
| 
 | |
|   debug_printf ("vmin_ %d, vtime_ %d", vmin_, vtime_);
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_serial::fixup_after_fork (HANDLE parent)
 | |
| {
 | |
|   if (close_on_exec ())
 | |
|     fhandler_base::fixup_after_fork (parent);
 | |
|   overlapped_setup ();
 | |
|   debug_printf ("io_status.hEvent %p", io_status.hEvent);
 | |
| }
 | |
| 
 | |
| void
 | |
| fhandler_serial::fixup_after_exec ()
 | |
| {
 | |
|   if (!close_on_exec ())
 | |
|     overlapped_setup ();
 | |
|   debug_printf ("io_status.hEvent %p, close_on_exec %d", io_status.hEvent, close_on_exec ());
 | |
| }
 | |
| 
 | |
| int
 | |
| fhandler_serial::dup (fhandler_base *child)
 | |
| {
 | |
|   fhandler_serial *fhc = (fhandler_serial *) child;
 | |
|   fhc->overlapped_setup ();
 | |
|   fhc->vmin_ = vmin_;
 | |
|   fhc->vtime_ = vtime_;
 | |
|   return fhandler_base::dup (child);
 | |
| }
 |