cygerrno.h. * include/cygwin/config.h (__DYNAMIC_REENT__): Define. * include/cygwin/version.h: Bump API minor version. * cygwin.din: Export __getreent * cygerrno.h: Include errno.h. Fix places where _impure_ptr is used directly to store the errno value. * debug.cc (__set_errno): Ditto. * errno.cc: Remove _RRENT_ONLY define to get errno.cc compiled. * signal.cc: Rename _reent_clib to _REENT throughout. * thread.h (reent_clib): Remove prototype. * thread.cc (reent_clib): Rename reent_clib to __getreent. Return _impure_ptr until MTinterface is initialized. (reent_winsup): Fix a possible SEGV when _r == NULL. Return NULL instead. * MTinterface::fixup_after_fork: Switch reent back to _impure_ptr to keep signal handling running when fork is called from a thread other than the mainthread.
		
			
				
	
	
		
			1036 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1036 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* fhandler_serial.cc
 | 
						|
 | 
						|
   Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 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 <stdlib.h>
 | 
						|
#include "cygerrno.h"
 | 
						|
#include "security.h"
 | 
						|
#include "fhandler.h"
 | 
						|
#include "sigproc.h"
 | 
						|
#include "pinfo.h"
 | 
						|
#include <sys/termios.h>
 | 
						|
#include <ddk/ntddser.h>
 | 
						|
 | 
						|
/**********************************************************************/
 | 
						|
/* fhandler_serial */
 | 
						|
 | 
						|
fhandler_serial::fhandler_serial (int unit)
 | 
						|
  : fhandler_base (FH_SERIAL, unit), 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;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
fhandler_serial::raw_read (void *ptr, size_t& ulen)
 | 
						|
{
 | 
						|
  int tot;
 | 
						|
  DWORD n;
 | 
						|
  HANDLE w4[2];
 | 
						|
  size_t 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)
 | 
						|
    {
 | 
						|
      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 (!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 (!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, 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:
 | 
						|
  ulen = 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;
 | 
						|
  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:
 | 
						|
	  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;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
fhandler_serial::dump (void)
 | 
						|
{
 | 
						|
  paranoid_printf ("here");
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
fhandler_serial::init (HANDLE f, DWORD flags, mode_t bin)
 | 
						|
{
 | 
						|
  (void) open (NULL, flags, bin & (O_BINARY | O_TEXT));
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
fhandler_serial::open (path_conv *, 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 (NULL, flags, mode))
 | 
						|
    return 0;
 | 
						|
 | 
						|
  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 ());
 | 
						|
    }
 | 
						|
 | 
						|
  /* setting rts and dtr to known state so that ioctl() function with
 | 
						|
  request TIOCMGET could return correct value of RTS and DTR lines.
 | 
						|
  Important only for Win 9x systems */
 | 
						|
 | 
						|
  if (!wincap.supports_reading_modem_output_lines ())
 | 
						|
    {
 | 
						|
      if (EscapeCommFunction (get_handle (), SETDTR) == 0)
 | 
						|
	system_printf ("couldn't set initial state of DTR for %s, %E", get_name ());
 | 
						|
      if (EscapeCommFunction (get_handle (), SETRTS) == 0)
 | 
						|
	system_printf ("couldn't set initial state of RTS for %s, %E", get_name ());
 | 
						|
 | 
						|
      /* even though one of above functions fail I have to set rts and dtr
 | 
						|
      variables to initial value. */
 | 
						|
      rts = TIOCM_RTS;
 | 
						|
      dtr = TIOCM_DTR;
 | 
						|
    }
 | 
						|
 | 
						|
  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 ()
 | 
						|
{
 | 
						|
  (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 = 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 (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;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* 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 (ipbuffer & TIOCM_RTS)
 | 
						|
	  {
 | 
						|
	    if (EscapeCommFunction (get_handle (), SETRTS))
 | 
						|
	      rts = TIOCM_RTS;
 | 
						|
	    else
 | 
						|
	      {
 | 
						|
		__seterrno ();
 | 
						|
		res = -1;
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	else
 | 
						|
	  {
 | 
						|
	    if (EscapeCommFunction (get_handle (), CLRRTS))
 | 
						|
	      rts = 0;
 | 
						|
	    else
 | 
						|
	      {
 | 
						|
		__seterrno ();
 | 
						|
		res = -1;
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	if (ipbuffer & TIOCM_DTR)
 | 
						|
	  {
 | 
						|
	    if (EscapeCommFunction (get_handle (), SETDTR))
 | 
						|
	      dtr = TIOCM_DTR;
 | 
						|
	    else
 | 
						|
	      {
 | 
						|
		__seterrno ();
 | 
						|
		res = -1;
 | 
						|
	      }
 | 
						|
	  }
 | 
						|
	else if (EscapeCommFunction (get_handle (), CLRDTR))
 | 
						|
	  dtr = 0;
 | 
						|
	else
 | 
						|
	  {
 | 
						|
	    __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;
 | 
						|
     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)
 | 
						|
{
 | 
						|
  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--)
 | 
						|
      {
 | 
						|
	COMSTAT st;
 | 
						|
	if (!PurgeComm (get_handle (), PURGE_RXABORT | PURGE_RXCLEAR))
 | 
						|
	  break;
 | 
						|
	low_priority_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_;
 | 
						|
  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 B230400:
 | 
						|
      state.BaudRate = 230400 /* CBR_230400 - 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;
 | 
						|
    }
 | 
						|
 | 
						|
  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);
 | 
						|
      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));
 | 
						|
 | 
						|
  /* -------------- Baud rate ------------------ */
 | 
						|
 | 
						|
  /* If DTR is NOT set, return B0 as our speed */
 | 
						|
  if (dtr != TIOCM_DTR)
 | 
						|
    t->c_cflag = t->c_ospeed = t->c_ispeed = B0;
 | 
						|
  else
 | 
						|
    switch (state.BaudRate)
 | 
						|
      {
 | 
						|
      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;
 | 
						|
      case 230400: /* CBR_230400 - not defined */
 | 
						|
	t->c_cflag = t->c_ospeed = t->c_ispeed = B230400;
 | 
						|
	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;
 | 
						|
 | 
						|
  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 (get_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 (HANDLE)
 | 
						|
{
 | 
						|
  overlapped_setup ();
 | 
						|
  debug_printf ("io_status.hEvent %p", io_status.hEvent);
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
}
 |