From 2aa62f411f1ca1c9293c99ca093a2c095c00c8cf Mon Sep 17 00:00:00 2001 From: Christopher Faylor Date: Fri, 23 Jan 2004 15:23:40 +0000 Subject: * fhandler_serial.cc (fhandler_serial::raw_write): Prevent a deadlock when the input buffer overflows. (fhandler_serial::raw_read): Correct to print the actual error and only call PurgeComm when necessary. --- winsup/cygwin/ChangeLog | 7 + winsup/cygwin/fhandler_serial.cc | 1049 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 1056 insertions(+) create mode 100644 winsup/cygwin/fhandler_serial.cc diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index d3eb606ff31..e7e9414ef03 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,10 @@ +2004-01-22 Brian Ford + + * fhandler_serial.cc (fhandler_serial::raw_write): Prevent a deadlock + when the input buffer overflows. + (fhandler_serial::raw_read): Correct to print the actual error and only + call PurgeComm when necessary. + 2004-01-22 Christopher Faylor * dcrt0.cc (reent_data): Make global. diff --git a/winsup/cygwin/fhandler_serial.cc b/winsup/cygwin/fhandler_serial.cc new file mode 100644 index 00000000000..c5e1f6d9852 --- /dev/null +++ b/winsup/cygwin/fhandler_serial.cc @@ -0,0 +1,1049 @@ +/* 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 +#include +#include "cygerrno.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "sigproc.h" +#include "pinfo.h" +#include +#include + +/**********************************************************************/ +/* fhandler_serial */ + +fhandler_serial::fhandler_serial () + : fhandler_base (), 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: + 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). */ +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: + 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; +} + +void +fhandler_serial::dump (void) +{ + paranoid_printf ("here"); +} + +void +fhandler_serial::init (HANDLE f, DWORD flags, mode_t bin) +{ + (void) 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; + + (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) +{ + 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 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) + { + 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) + 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 (!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); +} -- cgit v1.2.1