newlib/winsup/cygwin/fhandler_serial.cc
Corinna Vinschen 50ad198085 Cygwin: Add 'fallthrough' pseudo keyword for switch/case use
This patch has been inspired by the Linux kernel patch

  294f69e662d1 compiler_attributes.h: Add 'fallthrough' pseudo keyword for switch/case use

written by Joe Perches <joe AT perches DOT com> based on an idea from
Dan Carpenter <dan DOT carpenter AT oracle DOT com>.  The following text
is from the original log message:

Reserve the pseudo keyword 'fallthrough' for the ability to convert the
various case block /* fallthrough */ style comments to appear to be an
actual reserved word with the same gcc case block missing fallthrough
warning capability.

All switch/case blocks now should end in one of:

	break;
	fallthrough;
	goto <label>;
	return [expression];
	continue;

In C mode, GCC supports the __fallthrough__ attribute since 7.1,
the same time the warning and the comment parsing were introduced.

Cygwin-only: add an explicit -Wimplicit-fallthrough=5 to the build
flags.
2020-08-05 21:58:22 +02:00

1130 lines
28 KiB
C++

/* fhandler_serial.cc
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 <sys/param.h>
#include "cygerrno.h"
#include "security.h"
#include "path.h"
#include "fhandler.h"
#include "sigproc.h"
#include "pinfo.h"
#include <asm/socket.h>
#include <devioctl.h>
#include <ntddser.h>
#include "cygwait.h"
/**********************************************************************/
/* fhandler_serial */
fhandler_serial::fhandler_serial ()
: fhandler_base (), vmin_ (0), vtime_ (0), pgrp_ (myself->pgid)
{
need_fork_fixup (true);
}
void __reg3
fhandler_serial::raw_read (void *ptr, size_t& ulen)
{
OVERLAPPED ov = { 0 };
DWORD io_err;
COMSTAT st;
DWORD bytes_to_read, read_bytes;
ssize_t tot = 0;
if (ulen > SSIZE_MAX)
ulen = SSIZE_MAX;
if (ulen == 0)
return;
/* If MIN > 0 in blocking mode, we have to read at least VMIN chars,
otherwise we're in polling mode and there's no minimum chars. */
ssize_t minchars = (!is_nonblocking () && vmin_) ? MIN (vmin_, ulen) : 0;
debug_printf ("ulen %ld, vmin_ %u, vtime_ %u", ulen, vmin_, vtime_);
ov.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
do
{
/* First check if chars are already in the inbound queue. */
if (!ClearCommError (get_handle (), &io_err, &st))
goto err;
/* FIXME: In case of I/O error, do we really want to bail out or is it
better just trying to pull through? */
if (io_err)
{
termios_printf ("error detected %x", io_err);
SetLastError (ERROR_IO_DEVICE);
goto err;
}
/* ReadFile only handles up to DWORD bytes. */
bytes_to_read = MIN (ulen, UINT32_MAX);
if (is_nonblocking ())
{
/* In O_NONBLOCK mode we just care for the number of chars already
in the inbound queue. */
if (!st.cbInQue)
break;
bytes_to_read = MIN (st.cbInQue, bytes_to_read);
}
else
{
/* If the number of chars in the inbound queue is sufficent
(minchars defines the minimum), set bytes_to_read accordingly
and don't wait. */
if (st.cbInQue && (ssize_t) st.cbInQue >= minchars)
bytes_to_read = MIN (st.cbInQue, bytes_to_read);
}
ResetEvent (ov.hEvent);
if (!ReadFile (get_handle (), ptr, bytes_to_read, &read_bytes, &ov))
{
if (GetLastError () != ERROR_IO_PENDING)
goto err;
if (is_nonblocking ())
{
CancelIo (get_handle ());
if (!GetOverlappedResult (get_handle (), &ov, &read_bytes,
TRUE))
goto err;
}
else
{
switch (cygwait (ov.hEvent))
{
default: /* Handle an error case from cygwait basically like
a cancel condition and see if we got "something" */
CancelIo (get_handle ());
fallthrough;
case WAIT_OBJECT_0:
if (!GetOverlappedResult (get_handle (), &ov, &read_bytes,
TRUE))
goto err;
debug_printf ("got %u bytes from ReadFile", read_bytes);
break;
case WAIT_SIGNALED:
CancelIo (get_handle ());
if (!GetOverlappedResult (get_handle (), &ov, &read_bytes,
TRUE))
goto err;
/* Only if no bytes read, return with EINTR. */
if (!tot && !read_bytes)
{
tot = -1;
set_sig_errno (EINTR);
debug_printf ("signal received, set EINTR");
}
else
debug_printf ("signal received but ignored");
goto out;
case WAIT_CANCELED:
CancelIo (get_handle ());
GetOverlappedResult (get_handle (), &ov, &read_bytes, TRUE);
debug_printf ("thread canceled");
pthread::static_cancel_self ();
/*NOTREACHED*/
}
}
}
tot += read_bytes;
ptr = (void *) ((caddr_t) ptr + read_bytes);
ulen -= read_bytes;
minchars -= read_bytes;
debug_printf ("vtime_ %u, vmin_ %u, read_bytes %u, tot %D",
vtime_, vmin_, read_bytes, tot);
continue;
err:
debug_printf ("err %E");
if (GetLastError () != ERROR_OPERATION_ABORTED)
{
if (tot == 0)
{
tot = -1;
__seterrno ();
}
break;
}
}
/* ALL of these are required to loop:
Still room in user space buffer
AND still a minchars requirement (implies blocking mode)
AND vtime_ is not set. */
while (ulen > 0 && minchars > 0 && vtime_ == 0);
out:
CloseHandle (ov.hEvent);
ulen = (size_t) tot;
if (is_nonblocking () && ulen == 0)
{
ulen = (size_t) -1;
set_errno (EAGAIN);
}
}
/* Cover function to WriteFile to provide Posix interface and semantics
(as much as possible). */
ssize_t __reg3
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 ())
{
switch (cygwait (write_status.hEvent))
{
case WAIT_OBJECT_0:
break;
case WAIT_SIGNALED:
PurgeComm (get_handle (), PURGE_TXABORT);
set_sig_errno (EINTR);
ForceCloseHandle (write_status.hEvent);
return -1;
case WAIT_CANCELED:
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;
syscall_printf ("fhandler_serial::open (%s, %y, 0%o)",
get_name (), flags, mode);
if (!fhandler_base::open (flags, mode))
return 0;
res = 1;
SetCommMask (get_handle (), EV_RXCHAR);
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, %y, 0%o)",
res, get_name (), flags, mode);
return res;
}
/* 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 *buf)
{
int res = 0;
# define ibuf ((int) (intptr_t) buf)
# define ipbuf (*(int *) buf)
DWORD ev;
COMSTAT st;
if (!ClearCommError (get_handle (), &ev, &st))
{
__seterrno ();
res = -1;
}
else
switch (cmd)
{
case TCFLSH:
res = tcflush (ibuf);
break;
case TIOCMGET:
DWORD modem_lines;
if (!GetCommModemStatus (get_handle (), &modem_lines))
{
__seterrno ();
res = -1;
}
else
{
ipbuf = 0;
if (modem_lines & MS_CTS_ON)
ipbuf |= TIOCM_CTS;
if (modem_lines & MS_DSR_ON)
ipbuf |= TIOCM_DSR;
if (modem_lines & MS_RING_ON)
ipbuf |= TIOCM_RI;
if (modem_lines & MS_RLSD_ON)
ipbuf |= TIOCM_CD;
DWORD cb;
DWORD mcr;
if (!DeviceIoControl (get_handle (), IOCTL_SERIAL_GET_DTRRTS,
NULL, 0, &mcr, 4, &cb, 0) || cb != 4)
ipbuf |= rts | dtr;
else
{
if (mcr & 2)
ipbuf |= TIOCM_RTS;
if (mcr & 1)
ipbuf |= TIOCM_DTR;
}
}
break;
case TIOCMSET:
if (switch_modem_lines (ipbuf, ~ipbuf))
res = -1;
break;
case TIOCMBIS:
if (switch_modem_lines (ipbuf, 0))
res = -1;
break;
case TIOCMBIC:
if (switch_modem_lines (0, ipbuf))
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:
ipbuf = st.cbInQue;
break;
case TIOCGWINSZ:
((struct winsize *) buf)->ws_row = 0;
((struct winsize *) buf)->ws_col = 0;
break;
case FIONREAD:
set_errno (ENOTSUP);
res = -1;
break;
default:
res = fhandler_base::ioctl (cmd, buf);
break;
}
termios_printf ("%d = ioctl(%x, %p)", res, cmd, buf);
# undef ibuf
# undef ipbuf
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;
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 %u", 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 %u",
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];
vmin_ = t->c_cc[VMIN];
}
debug_printf ("vtime %u, vmin %u", vtime_, vmin_);
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_ * 100;
to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD;
}
else if ((vmin_ > 0) && (vtime_ > 0))
{
/* time applies to the interval time for this case */
to.ReadIntervalTimeout = vtime_ * 100;
}
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 %u, ReadIntervalTimeout %u, "
"ReadTotalTimeoutMultiplier %u", 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 %u", 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 %u", 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_;
t->c_cc[VMIN] = vmin_;
debug_printf ("vmin_ %u, vtime_ %u", vmin_, vtime_);
return 0;
}