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.
-- v6: ntdll/unix: Use termios2 for serial when possible.
From: Kirill Zhumarin kirill.zhumarin@gmail.com
--- configure.ac | 1 + dlls/ntdll/unix/serial.c | 158 ++++++++++++++++++++++++++++++++++++++- include/config.h.in | 3 + 3 files changed, 159 insertions(+), 3 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..d48c49c87f6 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,18 @@ 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; +#else struct termios port; int speed;
@@ -192,12 +208,22 @@ static NTSTATUS get_baud_rate(int fd, SERIAL_BAUD_RATE* sbr) ERR("unknown speed %x\n", speed); return STATUS_INVALID_PARAMETER; } +#endif return STATUS_SUCCESS; }
static NTSTATUS get_hand_flow(int fd, SERIAL_HANDFLOW* shf) { int stat = 0; +#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 ); + } +#else struct termios port;
if (tcgetattr(fd, &port) == -1) @@ -205,6 +231,7 @@ static NTSTATUS get_hand_flow(int fd, SERIAL_HANDFLOW* shf) 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,6 +277,15 @@ 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; + + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else struct termios port;
if (tcgetattr(fd, &port) == -1) @@ -257,6 +293,7 @@ static NTSTATUS get_line_control(int fd, SERIAL_LINE_CONTROL* slc) ERR("tcgetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif
#ifdef CMSPAR switch (port.c_cflag & (PARENB | PARODD | CMSPAR)) @@ -356,6 +393,15 @@ 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; + + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else struct termios port;
if (tcgetattr(fd, &port) == -1) @@ -363,6 +409,8 @@ static NTSTATUS get_special_chars(int fd, SERIAL_CHARS* sc) 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 +492,45 @@ 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, TCIFLUSH); + if (flags & PURGE_TXCLEAR) ioctl(fd, TCFLSH, TCOFLUSH); + if (flags & PURGE_RXCLEAR) ioctl(fd, TCFLSH, TCIFLUSH); +#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 ); + } +#else struct termios port;
if (tcgetattr(fd, &port) == -1) @@ -564,6 +642,7 @@ static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr) ERR("tcsetattr error '%s'\n", strerror(errno)); return errno_to_status( errno ); } +#endif return STATUS_SUCCESS; }
@@ -583,17 +662,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 +723,44 @@ 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) { - struct termios port; unsigned bytesize, stopbits; +#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 ); + } +#else + struct termios port;
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 +855,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,6 +879,15 @@ 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; + + if (ioctl(fd, TCGETS2, &port) == -1) + { + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } +#else struct termios port;
if (tcgetattr(fd, &port) == -1) @@ -768,6 +895,7 @@ static NTSTATUS set_special_chars(int fd, const SERIAL_CHARS* sc) 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 +904,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,10 +925,18 @@ 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 ); + } +#else if (tcflow(fd, TCOOFF)) { return errno_to_status( errno ); } +#endif return STATUS_SUCCESS; }
@@ -801,10 +945,18 @@ 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 ); + } +#else if (tcflow(fd, TCOON)) { return errno_to_status( errno ); } +#endif return STATUS_SUCCESS; }
@@ -1364,7 +1516,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, I fixed it.
Looks like a pipeline problem.... ``` script.c:1223: Test failed: L"/index.html?xhr.js: [iframes 2] unexpected order: 0,1,echo,sync_xhr(pre-send),sync_xhr(DONE),sync_xhr,blank(DONE)" ```
Now all tests passed.
another option would be to manually redefine the linux termios2 structure in serial.c (as, say, struct linux_termios2) (and potentially some other bits, BOTHER, LINUX_NCCS...) and not #include termbits.h so that we avoid any conflict for struct termios between linux & (g)libc and trigger setting / getting speed only if TCSET2 and/or TCGET2 are defined
I think this variant is more hacky and less reliable (in the case when `asm/termbits.h` is changed). I intentionally avoided this in the first version of MR by putting this ambiguous code into `serial_termios2.c`.
But, I can implement this variant if there is a final decision.
P.S. I'm ready to do any of the variants. I'm just waiting for your opinion.
@infyquest @epo Hi! Are you going to merge this?
@kirill.zhumarin Could you help to build Wineskin Server Engine with patch from this merge request?
On Sun Apr 28 12:51:39 2024 +0000, Sergey Belov wrote:
@infyquest @epo Hi! Are you going to merge this?
@julliard is responsible for the final review and merging the patches.