Termios2 is a modern Linux feature thats allows usage of any custom baudrate instead of hardcoded values from the 1970's. Also, this patch improves compatibility with the win32 api, because windows allow any custom baudrates by design.
Example software for which this patch is needed: http://www.vi-soft.com.ua/index_e.htm
This software for reading/writing/patch fullflash of Siemens mobile phones, where serial speed is important. V-Klay uses 1600000 baudrate and perfectly works on Linux with termios2 and Prolific PL2303.
-- v5: ntdll/unix: Use termios2 for serial when possible.
From: Kirill Zhumarin kirill.zhumarin@gmail.com
--- configure.ac | 1 + dlls/ntdll/unix/serial.c | 172 ++++++++++++++++++++++++++++++++++++++- include/config.h.in | 3 + 3 files changed, 174 insertions(+), 2 deletions(-)
diff --git a/configure.ac b/configure.ac index 64083de89c4..1a7361763a5 100644 --- a/configure.ac +++ b/configure.ac @@ -388,6 +388,7 @@ AC_CHECK_HEADERS(\ OpenCL/opencl.h \ arpa/inet.h \ arpa/nameser.h \ + asm/termbits.h \ asm/types.h \ asm/user.h \ elf.h \ diff --git a/dlls/ntdll/unix/serial.c b/dlls/ntdll/unix/serial.c index 372103b4df8..712e39b3430 100644 --- a/dlls/ntdll/unix/serial.c +++ b/dlls/ntdll/unix/serial.c @@ -31,7 +31,11 @@ #include <stdlib.h> #include <stdarg.h> #include <stdio.h> -#include <termios.h> +#ifdef HAVE_ASM_TERMBITS_H +# include <asm/termbits.h> +#else +# include <termios.h> +#endif #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> @@ -119,6 +123,20 @@ static const char* iocode2str(UINT ioc)
static NTSTATUS get_baud_rate(int fd, SERIAL_BAUD_RATE* sbr) { +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; + + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } + + /* TERMIOS2 supports separate input and output baudrate, but wine supports only the common baudrate. */ + sbr->BaudRate = port.c_ospeed; + + return STATUS_SUCCESS; +#else struct termios port; int speed;
@@ -193,18 +211,31 @@ static NTSTATUS get_baud_rate(int fd, SERIAL_BAUD_RATE* sbr) return STATUS_INVALID_PARAMETER; } return STATUS_SUCCESS; +#endif }
static NTSTATUS get_hand_flow(int fd, SERIAL_HANDFLOW* shf) { int stat = 0; +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; +#else struct termios port; +#endif
+#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcgetattr(fd, &port) == -1) { ERR("tcgetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif /* termios does not support DTR/DSR flow control */ shf->ControlHandShake = 0; shf->FlowReplace = 0; @@ -250,13 +281,25 @@ static NTSTATUS get_hand_flow(int fd, SERIAL_HANDFLOW* shf)
static NTSTATUS get_line_control(int fd, SERIAL_LINE_CONTROL* slc) { +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; +#else struct termios port; +#endif
+#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcgetattr(fd, &port) == -1) { ERR("tcgetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif
#ifdef CMSPAR switch (port.c_cflag & (PARENB | PARODD | CMSPAR)) @@ -356,13 +399,26 @@ static NTSTATUS get_properties(int fd, SERIAL_COMMPROP *prop)
static NTSTATUS get_special_chars(int fd, SERIAL_CHARS* sc) { +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; +#else struct termios port; +#endif
+#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcgetattr(fd, &port) == -1) { ERR("tcgetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif + sc->EofChar = port.c_cc[VEOF]; sc->ErrorChar = 0xFF; sc->BreakChar = 0; /* FIXME */ @@ -444,15 +500,47 @@ static NTSTATUS purge(int fd, DWORD flags) ** Perhaps if we had our own internal queues, one flushes them ** and the other flushes the kernel's buffers. */ +#ifdef HAVE_ASM_TERMBITS_H + if (flags & PURGE_TXABORT) ioctl(fd, TCFLSH, TCOFLUSH); + if (flags & PURGE_RXABORT) ioctl(fd, TCFLSH, TCOFLUSH); + if (flags & PURGE_TXCLEAR) ioctl(fd, TCFLSH, TCOFLUSH); + if (flags & PURGE_RXCLEAR) ioctl(fd, TCFLSH, TCOFLUSH); +#else if (flags & PURGE_TXABORT) tcflush(fd, TCOFLUSH); if (flags & PURGE_RXABORT) tcflush(fd, TCIFLUSH); if (flags & PURGE_TXCLEAR) tcflush(fd, TCOFLUSH); if (flags & PURGE_RXCLEAR) tcflush(fd, TCIFLUSH); +#endif return STATUS_SUCCESS; }
static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr) { +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; + + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } + + port.c_cflag &= ~CBAUD; + port.c_cflag |= BOTHER; + port.c_ospeed = sbr->BaudRate; + + port.c_cflag &= ~(CBAUD << IBSHIFT); + port.c_cflag |= BOTHER << IBSHIFT; + port.c_ispeed = sbr->BaudRate; + + if (ioctl(fd, TCSETS2, &port) == -1) + { + ERR("ioctl TCSETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } + + return STATUS_SUCCESS; +#else struct termios port;
if (tcgetattr(fd, &port) == -1) @@ -565,6 +653,7 @@ static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr) return errno_to_status( errno ); } return STATUS_SUCCESS; +#endif }
static int whack_modem(int fd, unsigned int andy, unsigned int orrie) @@ -583,17 +672,29 @@ static int whack_modem(int fd, unsigned int andy, unsigned int orrie)
static NTSTATUS set_handflow(int fd, const SERIAL_HANDFLOW* shf) { +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; +#else struct termios port; +#endif
if ((shf->FlowReplace & (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE)) == (SERIAL_RTS_CONTROL | SERIAL_RTS_HANDSHAKE)) return STATUS_NOT_SUPPORTED;
+#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcgetattr(fd, &port) == -1) { ERR("tcgetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif
#ifdef CRTSCTS if ((shf->ControlHandShake & SERIAL_CTS_HANDSHAKE) || @@ -632,25 +733,46 @@ static NTSTATUS set_handflow(int fd, const SERIAL_HANDFLOW* shf) port.c_iflag |= IXON; else port.c_iflag &= ~IXON; + +#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCSETS2, &port) == -1) + { + ERR("ioctl TCSETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcsetattr(fd, TCSANOW, &port) == -1) { ERR("tcsetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif
return STATUS_SUCCESS; }
static NTSTATUS set_line_control(int fd, const SERIAL_LINE_CONTROL* slc) { +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; +#else struct termios port; +#endif unsigned bytesize, stopbits;
+#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcgetattr(fd, &port) == -1) { ERR("tcgetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif
#ifdef IMAXBEL port.c_iflag &= ~(ISTRIP|BRKINT|IGNCR|ICRNL|INLCR|PARMRK|IMAXBEL); @@ -745,11 +867,19 @@ static NTSTATUS set_line_control(int fd, const SERIAL_LINE_CONTROL* slc) return STATUS_NOT_SUPPORTED; } /* otherwise it hangs with pending input*/ +#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCSETS2, &port) == -1) + { + ERR("ioctl TCSETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcsetattr(fd, TCSANOW, &port) == -1) { ERR("tcsetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif return STATUS_SUCCESS; }
@@ -761,13 +891,25 @@ static NTSTATUS set_queue_size(int fd, const SERIAL_QUEUE_SIZE* sqs)
static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc) { +#ifdef HAVE_ASM_TERMBITS_H + struct termios2 port; +#else struct termios port; +#endif
+#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcgetattr(fd, &port) == -1) { ERR("tcgetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif
port.c_cc[VEOF ] = sc->EofChar; /* FIXME: sc->ErrorChar is not supported */ @@ -776,11 +918,19 @@ static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc) port.c_cc[VSTART] = sc->XonChar; port.c_cc[VSTOP ] = sc->XoffChar;
+#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCSETS2, &port) == -1) + { + ERR("ioctl TCSETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else if (tcsetattr(fd, TCSANOW, &port) == -1) { ERR("tcsetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif return STATUS_SUCCESS; }
@@ -789,11 +939,20 @@ static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc) */ static NTSTATUS set_XOff(int fd) { +#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCXONC, TCOOFF) == -1) + { + ERR("ioctl TCXONC error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } + return STATUS_SUCCESS; +#else if (tcflow(fd, TCOOFF)) { return errno_to_status( errno ); } return STATUS_SUCCESS; +#endif }
/* @@ -801,11 +960,20 @@ static NTSTATUS set_XOff(int fd) */ static NTSTATUS set_XOn(int fd) { +#ifdef HAVE_ASM_TERMBITS_H + if (ioctl(fd, TCXONC, TCOON) == -1) + { + ERR("ioctl TCXONC error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } + return STATUS_SUCCESS; +#else if (tcflow(fd, TCOON)) { return errno_to_status( errno ); } return STATUS_SUCCESS; +#endif }
/* serial_irq_info @@ -1364,7 +1532,7 @@ NTSTATUS serial_DeviceIoControl( HANDLE device, HANDLE event, PIO_APC_ROUTINE ap
NTSTATUS serial_FlushBuffersFile( int fd ) { -#ifdef HAVE_TCDRAIN +#if defined(HAVE_TCDRAIN) && !defined(HAVE_ASM_TERMBITS_H) while (tcdrain( fd ) == -1) { if (errno != EINTR) return errno_to_status( errno ); diff --git a/include/config.h.in b/include/config.h.in index a910b2c85f1..ee92b4a5024 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -12,6 +12,9 @@ /* Define to 1 if you have the <arpa/nameser.h> header file. */ #undef HAVE_ARPA_NAMESER_H
+/* Define to 1 if you have the <asm/termbits.h> header file. */ +#undef HAVE_ASM_TERMBITS_H + /* Define to 1 if you have the <asm/types.h> header file. */ #undef HAVE_ASM_TYPES_H
Thanks, you are right, this variant is better. I made a new variant with all the requested changes.
`asm/termbits.h` don't provide some important functions:
- I use `TCFLSH` ioctl instead of `tcflush`, as in glibc: https://github.com/bminor/glibc/blob/d370155b9ab73d0c45bf968b7a9c823f4697a50... - I use `TCXONC` ioctl instead of `tcflow`, as in glibc: https://github.com/bminor/glibc/blob/d370155b9ab73d0c45bf968b7a9c823f4697a50... - I use `TCSBRK` ioctl instead of `tcdrain`, as in glibc: https://github.com/bminor/glibc/blob/d370155b9ab73d0c45bf968b7a9c823f4697a50...
Termios2 is a Linux-only thing and I assume that directly using Linux-syscalls in this case is okay.
In other cases, I just replace tcgetattr/tcsetattr/termios to TCGETS2/TCSETS2/termios2, because structures are compatible by fields.
1. tcflush implementation is wrong for HAVE_ASM_TERMBITS_H, please check and use appropriate flags for PURGE_RXABORT & PURGE_RXCLEAR. 2. In some cases both termios and termios2 cases return SUCCESS, you can put it out of the ifdef's 3. Also in some functions you need not keep the separate ifdefs for declaring the variables and actual functionality, you can put them in in one ifdef. for eg: get_hand_flow, get_line_control, get_special_chars, set_line_control, set_special_chars