/* fhandler_serial.cc Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011 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 "cygerrno.h" #include "security.h" #include "path.h" #include "fhandler.h" #include "sigproc.h" #include "pinfo.h" #include /**********************************************************************/ /* 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; size_t minchars = vmin_ ? min (vmin_, ulen) : ulen; 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 if (is_nonblocking ()) { PurgeComm (get_handle (), PURGE_RXABORT); if (tot == 0) { tot = -1; set_errno (EAGAIN); } goto out; } else { HANDLE w4[3] = { io_status.hEvent, signal_arrived, pthread::get_cancel_event () }; DWORD cnt = w4[2] ? 3 : 2; overlapped_armed = 1; restart: switch (WaitForMultipleObjects (cnt, 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: if (_my_tls.call_signal_handler ()) goto restart; tot = -1; PurgeComm (get_handle (), PURGE_RXABORT); overlapped_armed = 0; set_sig_errno (EINTR); goto out; case WAIT_OBJECT_0 + 2: PurgeComm (get_handle (), PURGE_RXABORT); overlapped_armed = 0; pthread::static_cancel_self (); /*NOTREACHED*/ 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 (is_nonblocking ()) { PurgeComm (get_handle (), PURGE_RXABORT); if (tot == 0) { tot = -1; set_errno (EAGAIN); } goto out; } 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 (!is_nonblocking ()) { HANDLE w4[3] = { write_status.hEvent, signal_arrived, pthread::get_cancel_event () }; DWORD cnt = w4[2] ? 3 : 2; restart: switch (WaitForMultipleObjects (cnt, w4, FALSE, INFINITE)) { case WAIT_OBJECT_0: break; case WAIT_OBJECT_0 + 1: if (_my_tls.call_signal_handler ()) goto restart; PurgeComm (get_handle (), PURGE_TXABORT); set_sig_errno (EINTR); ForceCloseHandle (write_status.hEvent); return -1; case WAIT_OBJECT_0 + 2: PurgeComm (get_handle (), PURGE_TXABORT); pthread::static_cancel_self (); /*NOTREACHED*/ 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); 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); }