Cygwin: fix TIOCMBIS/TIOCMBIC not working when using usbser.sys

In winsup/cygwin/fhandler/serial.cc, the function
fhandler_serial::switch_modem_lines() is called when TIOCMBIS/TIOCMBIC
are used in an ioctl() call.

This function uses EscapeCommFunction() for setting and resetting RTS
and DTR signals of a serial port.  Unfortunately, this function does not
work on USB CDC devices.

This is not a true bug of CYGWIN but an issue of the usbser.sys driver,
from Windows 2000 to the latest Windows 11.  Both 32bit and 64bit
versions of the operating system are affected.  Actually, I tested
EscapeCommFunction() also when using a real UART, based on the
traditional 16550 driver and it works fine.  Using thirdy party CDC
drivers, like the one provided by FTDI for their USB bridge chips,
probably also works.

However, it is also possible to drive the RTS/DTR signals by writing
their state with SetCommState(), which proved to be working fine all
types of connection.  This is also a better solution for handling these
signals since RTS/DTR can be set at the same time rather than having two
separate calls with a visible delay between them.
This commit is contained in:
Carlo Bramini 2022-11-11 11:15:27 +01:00 committed by Corinna Vinschen
parent 5c79aa4b22
commit 2dab880c96
1 changed files with 28 additions and 39 deletions

View File

@ -391,50 +391,39 @@ fhandler_serial::tcflow (int action)
int int
fhandler_serial::switch_modem_lines (int set, int clr) fhandler_serial::switch_modem_lines (int set, int clr)
{ {
int res = 0; if ((set & (TIOCM_RTS | TIOCM_DTR)) || (clr & (TIOCM_RTS | TIOCM_DTR)))
{
DCB dcb;
if (set & TIOCM_RTS) memset(&dcb,0,sizeof(dcb));
{ dcb.DCBlength = sizeof(dcb);
if (EscapeCommFunction (get_handle (), SETRTS))
rts = TIOCM_RTS; if (!GetCommState(get_handle(), &dcb))
{
__seterrno ();
return -1;
}
if (set & TIOCM_RTS)
dcb.fRtsControl = RTS_CONTROL_ENABLE;
else else
{ if (clr & TIOCM_RTS)
__seterrno (); dcb.fRtsControl = RTS_CONTROL_DISABLE;
res = -1;
} if (set & TIOCM_DTR)
} dcb.fRtsControl = DTR_CONTROL_ENABLE;
else if (clr & TIOCM_RTS)
{
if (EscapeCommFunction (get_handle (), CLRRTS))
rts = 0;
else else
{ if (clr & TIOCM_DTR)
__seterrno (); dcb.fRtsControl = DTR_CONTROL_DISABLE;
res = -1;
} if (!SetCommState(get_handle(), &dcb))
} {
if (set & TIOCM_DTR) __seterrno ();
{ return -1;
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; return 0;
} }
/* ioctl: */ /* ioctl: */