From 19c86c3f7ecdaa8dd8a981fb08c175f96b8690f2 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Thu, 19 Jun 2025 21:39:09 -0400 Subject: [PATCH 1/2] A few cleanups in serial passthrough --- src/device/serial_passthrough.c | 29 +++++++++++++++----------- src/include/86box/serial_passthrough.h | 15 ++++++++----- src/qt/win_serial_passthrough.c | 21 ++++++++++--------- src/unix/unix_serial_passthrough.c | 8 +++---- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/src/device/serial_passthrough.c b/src/device/serial_passthrough.c index 25db29096..479c1dc24 100644 --- a/src/device/serial_passthrough.c +++ b/src/device/serial_passthrough.c @@ -13,7 +13,7 @@ * Jasmine Iwanek * * Copyright 2021 Andreas J. Reichel. - * Copyright 2021-2022 Jasmine Iwanek. + * Copyright 2021-2025 Jasmine Iwanek. */ #include @@ -222,10 +222,15 @@ serial_passthrough_dev_init(const device_t *info) } const char *serpt_mode_names[SERPT_MODES_MAX] = { - [SERPT_MODE_VCON] = "vcon", - [SERPT_MODE_TCPSRV] = "tcpsrv", - [SERPT_MODE_TCPCLNT] = "tcpclnt", - [SERPT_MODE_HOSTSER] = "hostser", +#ifdef _WIN32 + [SERPT_MODE_NPIPE_SRV] = "npipesrv", + [SERPT_MODE_NPIPE_CLNT] = "npipeclnt", +#else + [SERPT_MODE_VCON] = "vcon", +#endif + [SERPT_MODE_TCP_SRV] = "tcpsrv", + [SERPT_MODE_TCP_CLNT] = "tcpclnt", + [SERPT_MODE_HOSTSER] = "hostser", }; // clang-format off @@ -240,19 +245,19 @@ static const device_config_t serial_passthrough_config[] = { .spinner = { 0 }, .selection = { #ifdef _WIN32 - { .description = "Named Pipe (Server)", .value = SERPT_MODE_VCON }, + { .description = "Named Pipe (Server)", .value = SERPT_MODE_NPIPE_SRV }, #if 0 /* TODO */ - { .description = "Named Pipe (Client)", .value = SERPT_MODE_VCON }, + { .description = "Named Pipe (Client)", .value = SERPT_MODE_NPIPE_CLNT }, #endif #else /* _WIN32 */ - { .description = "Pseudo Terminal/Virtual Console", .value = SERPT_MODE_VCON }, + { .description = "Pseudo Terminal/Virtual Console", .value = SERPT_MODE_VCON }, #endif /* _WIN32 */ #if 0 /* TODO */ - { .description = "TCP Server", .value = SERPT_MODE_TCPSRV }, - { .description = "TCP Client", .value = SERPT_MODE_TCPCLNT }, + { .description = "TCP Server", .value = SERPT_MODE_TCP_SRV }, + { .description = "TCP Client", .value = SERPT_MODE_TCP_CLNT }, #endif - { .description = "Host Serial Passthrough", .value = SERPT_MODE_HOSTSER }, - { .description = "" } + { .description = "Host Serial Passthrough", .value = SERPT_MODE_HOSTSER }, + { .description = "" } }, .bios = { { 0 } } }, diff --git a/src/include/86box/serial_passthrough.h b/src/include/86box/serial_passthrough.h index c5454194a..a5fa0013a 100644 --- a/src/include/86box/serial_passthrough.h +++ b/src/include/86box/serial_passthrough.h @@ -13,7 +13,7 @@ * Jasmine Iwanek * * Copyright 2021 Andreas J. Reichel. - * Copyright 2021-2022 Jasmine Iwanek. + * Copyright 2021-2025 Jasmine Iwanek. */ #ifndef SERIAL_PASSTHROUGH_H @@ -28,10 +28,15 @@ #include <86box/serial.h> enum serial_passthrough_mode { - SERPT_MODE_VCON, /*Named Pipe (Server) / Pseudo Terminal/Virtual Console */ - SERPT_MODE_TCPSRV, /* TCP Server (TODO) */ - SERPT_MODE_TCPCLNT, /* TCP Client (TODO) */ - SERPT_MODE_HOSTSER, /* Host Serial Passthrough */ +#ifdef _WIN32 + SERPT_MODE_NPIPE_SRV, /* Named Pipe (Server) */ + SERPT_MODE_NPIPE_CLNT, /* Named Pipe (Client) */ +#else + SERPT_MODE_VCON, /* Pseudo Terminal/Virtual Console */ +#endif + SERPT_MODE_TCP_SRV, /* TCP Server (TODO) */ + SERPT_MODE_TCP_CLNT, /* TCP Client (TODO) */ + SERPT_MODE_HOSTSER, /* Host Serial Passthrough */ SERPT_MODES_MAX, }; diff --git a/src/qt/win_serial_passthrough.c b/src/qt/win_serial_passthrough.c index c1802ce73..e17d70f16 100644 --- a/src/qt/win_serial_passthrough.c +++ b/src/qt/win_serial_passthrough.c @@ -13,7 +13,7 @@ * Jasmine Iwanek * * Copyright 2021 Andreas J. Reichel - * Copyright 2021-2023 Jasmine Iwanek + * Copyright 2021-2025 Jasmine Iwanek */ #define _XOPEN_SOURCE 500 @@ -46,9 +46,9 @@ plat_serpt_close(void *priv) fclose(dev->master_fd); #endif FlushFileBuffers((HANDLE) dev->master_fd); - if (dev->mode == SERPT_MODE_VCON) + if (dev->mode == SERPT_MODE_NPIPE_SRV) DisconnectNamedPipe((HANDLE) dev->master_fd); - if (dev->mode == SERPT_MODE_HOSTSER) { + else if (dev->mode == SERPT_MODE_HOSTSER) { SetCommState((HANDLE) dev->master_fd, (DCB *) dev->backend_priv); free(dev->backend_priv); } @@ -133,7 +133,8 @@ plat_serpt_write(void *priv, uint8_t data) serial_passthrough_t *dev = (serial_passthrough_t *) priv; switch (dev->mode) { - case SERPT_MODE_VCON: + case SERPT_MODE_NPIPE_SRV: + case SERPT_MODE_NPIPE_CLNT: case SERPT_MODE_HOSTSER: plat_serpt_write_vcon(dev, data); break; @@ -157,7 +158,8 @@ plat_serpt_read(void *priv, uint8_t *data) int res = 0; switch (dev->mode) { - case SERPT_MODE_VCON: + case SERPT_MODE_NPIPE_SRV: + case SERPT_MODE_NPIPE_CLNT: case SERPT_MODE_HOSTSER: res = plat_serpt_read_vcon(dev, data); break; @@ -222,15 +224,14 @@ plat_serpt_open_device(void *priv) serial_passthrough_t *dev = (serial_passthrough_t *) priv; switch (dev->mode) { - case SERPT_MODE_VCON: - if (open_pseudo_terminal(dev)) { + case SERPT_MODE_NPIPE_SRV: + if (open_pseudo_terminal(dev)) return 0; - } break; case SERPT_MODE_HOSTSER: - if (open_host_serial_port(dev)) { + if (open_host_serial_port(dev)) return 0; - } + break; default: break; } diff --git a/src/unix/unix_serial_passthrough.c b/src/unix/unix_serial_passthrough.c index 9929f3298..873c706b9 100644 --- a/src/unix/unix_serial_passthrough.c +++ b/src/unix/unix_serial_passthrough.c @@ -13,7 +13,7 @@ * Jasmine Iwanek * * Copyright 2021 Andreas J. Reichel. - * Copyright 2021-2022 Jasmine Iwanek. + * Copyright 2021-2025 Jasmine Iwanek. */ #ifndef __APPLE__ @@ -310,14 +310,12 @@ plat_serpt_open_device(void *priv) switch (dev->mode) { case SERPT_MODE_VCON: - if (!open_pseudo_terminal(dev)) { + if (!open_pseudo_terminal(dev)) return 1; - } break; case SERPT_MODE_HOSTSER: - if (!open_host_serial_port(dev)) { + if (!open_host_serial_port(dev)) return 1; - } break; default: break; From e8c6fcdd4eaf70a6d7c9ee3448ed1f7718d52f20 Mon Sep 17 00:00:00 2001 From: Jasmine Iwanek Date: Thu, 19 Jun 2025 21:55:54 -0400 Subject: [PATCH 2/2] Add support for Client Named Pipe on Windows --- src/device/serial_passthrough.c | 2 -- src/qt/win_serial_passthrough.c | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/src/device/serial_passthrough.c b/src/device/serial_passthrough.c index 479c1dc24..426bfbc7b 100644 --- a/src/device/serial_passthrough.c +++ b/src/device/serial_passthrough.c @@ -246,9 +246,7 @@ static const device_config_t serial_passthrough_config[] = { .selection = { #ifdef _WIN32 { .description = "Named Pipe (Server)", .value = SERPT_MODE_NPIPE_SRV }, -#if 0 /* TODO */ { .description = "Named Pipe (Client)", .value = SERPT_MODE_NPIPE_CLNT }, -#endif #else /* _WIN32 */ { .description = "Pseudo Terminal/Virtual Console", .value = SERPT_MODE_VCON }, #endif /* _WIN32 */ diff --git a/src/qt/win_serial_passthrough.c b/src/qt/win_serial_passthrough.c index e17d70f16..725dc096f 100644 --- a/src/qt/win_serial_passthrough.c +++ b/src/qt/win_serial_passthrough.c @@ -189,6 +189,38 @@ open_pseudo_terminal(serial_passthrough_t *dev) return 1; } +static int +connect_named_pipe_client(serial_passthrough_t *dev) +{ + char ascii_pipe_name[1024] = { 0 }; + strncpy(ascii_pipe_name, dev->named_pipe, sizeof(ascii_pipe_name) - 1); + + HANDLE hPipe = CreateFileA( + ascii_pipe_name, // pipe name + GENERIC_READ | GENERIC_WRITE, + 0, // no sharing + NULL, // default security attributes + OPEN_EXISTING, // open existing pipe + 0, // default attributes + NULL); // no template file + + if (hPipe == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + wchar_t errorMsg[1024] = { 0 }; + wchar_t finalMsg[1024] = { 0 }; + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMsg, 1024, NULL); + swprintf(finalMsg, 1024, L"Named Pipe (client, named_pipe=\"%hs\", port=COM%d): %ls\n", ascii_pipe_name, dev->port + 1, errorMsg); + ui_msgbox(MBX_ERROR | MBX_FATAL, finalMsg); + return 0; + } + + DWORD mode = PIPE_READMODE_BYTE | PIPE_NOWAIT; + SetNamedPipeHandleState(hPipe, &mode, NULL, NULL); + dev->master_fd = (intptr_t) hPipe; + pclog("Named Pipe client connected to %s\n", ascii_pipe_name); + return 1; +} + static int open_host_serial_port(serial_passthrough_t *dev) { @@ -228,6 +260,10 @@ plat_serpt_open_device(void *priv) if (open_pseudo_terminal(dev)) return 0; break; + case SERPT_MODE_NPIPE_CLNT: + if (connect_named_pipe_client(dev)) + return 0; + break; case SERPT_MODE_HOSTSER: if (open_host_serial_port(dev)) return 0;