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.
-- v4: ntdll/unix: Use termios2 for serial when possible.
From: Kirill Zhumarin kirill.zhumarin@gmail.com
--- configure.ac | 1 + dlls/ntdll/Makefile.in | 1 + dlls/ntdll/unix/serial.c | 12 ++++ dlls/ntdll/unix/serial_termios2.c | 109 ++++++++++++++++++++++++++++++ dlls/ntdll/unix/unix_private.h | 4 ++ include/config.h.in | 3 + 6 files changed, 130 insertions(+) create mode 100644 dlls/ntdll/unix/serial_termios2.c
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/Makefile.in b/dlls/ntdll/Makefile.in index b519bcd655f..d6fa26ea1d3 100644 --- a/dlls/ntdll/Makefile.in +++ b/dlls/ntdll/Makefile.in @@ -55,6 +55,7 @@ SOURCES = \ unix/registry.c \ unix/security.c \ unix/serial.c \ + unix/serial_termios2.c \ unix/server.c \ unix/signal_arm.c \ unix/signal_arm64.c \ diff --git a/dlls/ntdll/unix/serial.c b/dlls/ntdll/unix/serial.c index 372103b4df8..73c1e176c4a 100644 --- a/dlls/ntdll/unix/serial.c +++ b/dlls/ntdll/unix/serial.c @@ -121,6 +121,12 @@ static NTSTATUS get_baud_rate(int fd, SERIAL_BAUD_RATE* sbr) { struct termios port; int speed; + int status; + + /* If termios2 is supported, first try to use it. Otherwise, use termios as a fallback. */ + status = serial_get_baud_rate_termios2(fd, &sbr->BaudRate); + if (status != STATUS_NOT_SUPPORTED) + return status;
if (tcgetattr(fd, &port) == -1) { @@ -454,6 +460,12 @@ static NTSTATUS purge(int fd, DWORD flags) static NTSTATUS set_baud_rate(int fd, const SERIAL_BAUD_RATE* sbr) { struct termios port; + int status; + + /* If termios2 is supported, first try to use it. Otherwise, use termios as a fallback. */ + status = serial_set_baud_rate_termios2(fd, sbr->BaudRate); + if (status != STATUS_NOT_SUPPORTED) + return status;
if (tcgetattr(fd, &port) == -1) { diff --git a/dlls/ntdll/unix/serial_termios2.c b/dlls/ntdll/unix/serial_termios2.c new file mode 100644 index 00000000000..45f48bc4f2f --- /dev/null +++ b/dlls/ntdll/unix/serial_termios2.c @@ -0,0 +1,109 @@ +/* + * Linux termios2 support + * + * Copyright 2024 Kirill Zhumarin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#if 0 +#pragma makedep unix +#endif + +#include "config.h" + +#include <errno.h> +#include <sys/ioctl.h> + +#ifdef HAVE_ASM_TERMBITS_H +#include <asm/termbits.h> +#endif + +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winternl.h" +#include "winioctl.h" +#include "ddk/ntddser.h" +#include "wine/server.h" +#include "unix_private.h" +#include "wine/debug.h" +#include "unix_private.h" + +#if defined(HAVE_ASM_TERMBITS_H) && defined(TCGETS2) + +WINE_DEFAULT_DEBUG_CHANNEL(comm); + +NTSTATUS serial_set_baud_rate_termios2(int fd, ULONG baudrate) +{ + struct termios2 port; + + if (ioctl(fd, TCGETS2, &port) == -1) + { + /* This is an old kernel, TCGETS2 is not present. */ + if (errno == EINVAL) + return STATUS_NOT_SUPPORTED; + + ERR("ioctl TCGETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } + + port.c_cflag &= ~CBAUD; + port.c_cflag |= BOTHER; + port.c_ospeed = baudrate; + + port.c_cflag &= ~(CBAUD << IBSHIFT); + port.c_cflag |= BOTHER << IBSHIFT; + port.c_ispeed = baudrate; + + if (ioctl(fd, TCSETS2, &port) == -1) + { + ERR("ioctl TCSETS2 error '%s'\n", strerror(errno)); + return errno_to_status( errno ); + } + + return STATUS_SUCCESS; +} + +NTSTATUS serial_get_baud_rate_termios2(int fd, ULONG* baudrate) +{ + struct termios2 port; + + if (ioctl(fd, TCGETS2, &port) == -1) + { + /* This is an old kernel, TCGETS2 is not present. */ + if (errno == EINVAL) + return STATUS_NOT_SUPPORTED; + + 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. */ + *baudrate = port.c_ospeed; + + return STATUS_SUCCESS; +} +#else +NTSTATUS serial_set_baud_rate_termios2(int fd, ULONG baudrate) +{ + return STATUS_NOT_SUPPORTED; +} + +NTSTATUS serial_get_baud_rate_termios2(int fd, ULONG* baudrate) +{ + return STATUS_NOT_SUPPORTED; +} +#endif diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index b249570d421..1823299b8b9 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -308,6 +308,10 @@ extern NTSTATUS serial_DeviceIoControl( HANDLE device, HANDLE event, PIO_APC_ROU IO_STATUS_BLOCK *io, UINT code, void *in_buffer, UINT in_size, void *out_buffer, UINT out_size ); extern NTSTATUS serial_FlushBuffersFile( int fd ); + +extern NTSTATUS serial_set_baud_rate_termios2( int fd, ULONG baudrate ); +extern NTSTATUS serial_get_baud_rate_termios2( int fd, ULONG *baudrate ); + extern NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io, UINT code, void *in_buffer, UINT in_size, void *out_buffer, UINT out_size ); extern NTSTATUS sock_read( HANDLE handle, int fd, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, 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 for the feedback!
For some unknown and strange reasons, `asm/termbits.h` and `termios.h` are not compatible due to the same #define's. I think dedicated `serial_termios2.c` is the only way to support termios2 in the existing code.
I made the following changes:
- All changes merged into a single commit. - Changes to `./configure` removed, checking for `asm/termios.h` now in `configure.ac`. - Fallback logic is rewritten. No more double or false-positive errors.
I believe it would be better to rewrite the entire serial.c to switch to termios2 when available, rather than add a new file.