From 89e3bbd2a3bc02f1506465abad9d7206d54f89de Mon Sep 17 00:00:00 2001 From: Will Xyen Date: Sat, 21 Sep 2019 12:57:05 -0400 Subject: [PATCH] hooklib: Allow proper passthrough of serial calls to real hardware (#26) Only enabled / tested on IIDX CB and SDVX5 The point of this, is to allow rs232 hooks for only the descriptors that we hook. This is because the emulation here is incomplete. Although the IOCTL's do end up being passed to the real hardware, the game rejects some of the responses and hangs. By short circuiting out, and calling the real Comm functions instead, it works. And hence, card-reader passthrough works on IIDX25+/SDVX5+ finally without eamio-real --- src/main/hooklib/rs232.c | 117 ++++++++++++++++++++++++++++++++++ src/main/hooklib/rs232.h | 15 +++++ src/main/iidxhook-util/acio.c | 3 + src/main/iidxhook8/bio2.c | 3 + src/main/iidxhook8/dllmain.c | 1 + 5 files changed, 139 insertions(+) diff --git a/src/main/hooklib/rs232.c b/src/main/hooklib/rs232.c index ed72280..3ad5148 100644 --- a/src/main/hooklib/rs232.c +++ b/src/main/hooklib/rs232.c @@ -4,6 +4,7 @@ #include #include +#include #include #include "hook/iohook.h" @@ -11,6 +12,7 @@ #include "util/hr.h" #include "util/log.h" +#include "util/array.h" /* RS232 API hooks */ @@ -29,55 +31,130 @@ static BOOL STDCALL my_SetupComm(HANDLE fd, uint32_t in_q, uint32_t out_q); static BOOL STDCALL my_SetCommBreak(HANDLE fd); static BOOL STDCALL my_ClearCommBreak(HANDLE fd); +static BOOL (STDCALL *real_ClearCommError)( + HANDLE fd, + uint32_t *errors, + COMSTAT *status); + +static BOOL (STDCALL *real_EscapeCommFunction)(HANDLE fd, uint32_t func); +static BOOL (STDCALL *real_GetCommState)(HANDLE fd, DCB *dcb); +static BOOL (STDCALL *real_PurgeComm)(HANDLE fd, uint32_t flags); +static BOOL (STDCALL *real_SetCommMask)(HANDLE fd, uint32_t mask); +static BOOL (STDCALL *real_SetCommState)(HANDLE fd, const DCB *dcb); +static BOOL (STDCALL *real_SetCommTimeouts)(HANDLE fd, COMMTIMEOUTS *timeouts); +static BOOL (STDCALL *real_SetupComm)(HANDLE fd, uint32_t in_q, uint32_t out_q); +static BOOL (STDCALL *real_SetCommBreak)(HANDLE fd); +static BOOL (STDCALL *real_ClearCommBreak)(HANDLE fd); + static struct hook_symbol rs232_syms[] = { { .name = "ClearCommError", .patch = my_ClearCommError, + .link = (void*) &real_ClearCommError, }, { .name = "EscapeCommFunction", .patch = my_EscapeCommFunction, + .link = (void*) &real_EscapeCommFunction, }, { .name = "GetCommState", .patch = my_GetCommState, + .link = (void*) &real_GetCommState, }, { .name = "PurgeComm", .patch = my_PurgeComm, + .link = (void*) &real_PurgeComm, }, { .name = "SetCommMask", .patch = my_SetCommMask, + .link = (void*) &real_SetCommMask, }, { .name = "SetCommState", .patch = my_SetCommState, + .link = (void*) &real_SetCommState, }, { .name = "SetCommTimeouts", .patch = my_SetCommTimeouts, + .link = (void*) &real_SetCommTimeouts, }, { .name = "SetupComm", .patch = my_SetupComm, + .link = (void*) &real_SetupComm, }, { .name = "SetCommBreak", .patch = my_SetCommBreak, + .link = (void*) &real_SetCommBreak, }, { .name = "ClearCommBreak", .patch = my_ClearCommBreak, + .link = (void*) &real_ClearCommBreak, }, }; +static struct array hooked_port_fds; +static bool rs232_limit_hooks; +static CRITICAL_SECTION hooked_port_fds_cs; + +/** + * Some notes: + * + * The point of this, is to allow rs232 hooks for only the descriptors that we hook. + * This is because the emulation here is incomplete. + * Although the IOCTL's do end up being passed to the real hardware, the game rejects some of the responses and hangs. + * By short circuiting out, and calling the real Comm functions instead, it works. + * And hence, card-reader passthrough works on IIDX25+/SDVX5+ finally without eamio-real + * + */ + void rs232_hook_init(void) { + array_init(&hooked_port_fds); + InitializeCriticalSection(&hooked_port_fds_cs); + hook_table_apply(NULL, "kernel32.dll", rs232_syms, lengthof(rs232_syms)); log_info("IO Hook RS232 ioctl subsystem initialized"); } +void rs232_hook_limit_hooks(void) +{ + rs232_limit_hooks = true; +} + +static BOOL rs232_check_fd(HANDLE fd) { + HANDLE check; + + if (rs232_limit_hooks){ + EnterCriticalSection(&hooked_port_fds_cs); + for (size_t i = 0 ; i < hooked_port_fds.nitems ; i++) { + check = *array_item(HANDLE, &hooked_port_fds, i); + if (check == fd) { + LeaveCriticalSection(&hooked_port_fds_cs); + return TRUE; + } + } + + LeaveCriticalSection(&hooked_port_fds_cs); + return FALSE; + } + + return TRUE; +} + +void rs232_hook_add_fd(HANDLE fd) +{ + EnterCriticalSection(&hooked_port_fds_cs); + *array_append(HANDLE, &hooked_port_fds) = fd; + LeaveCriticalSection(&hooked_port_fds_cs); +} + static BOOL STDCALL my_ClearCommError( HANDLE fd, uint32_t *errors, @@ -87,6 +164,10 @@ static BOOL STDCALL my_ClearCommError( SERIAL_STATUS llstatus; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_ClearCommError(fd, errors, status); + } + memset(&irp, 0, sizeof(irp)); irp.op = IRP_OP_IOCTL; irp.fd = fd; @@ -180,6 +261,10 @@ static BOOL STDCALL my_EscapeCommFunction(HANDLE fd, uint32_t cmd) uint32_t ioctl; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_EscapeCommFunction(fd, cmd); + } + switch (cmd) { case CLRBREAK: ioctl = IOCTL_SERIAL_SET_BREAK_OFF; break; case CLRDTR: ioctl = IOCTL_SERIAL_CLR_DTR; break; @@ -221,6 +306,10 @@ static BOOL STDCALL my_GetCommState(HANDLE fd, DCB *dcb) SERIAL_LINE_CONTROL line_control; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_GetCommState(fd, dcb); + } + /* * Validate params */ @@ -393,6 +482,10 @@ static BOOL STDCALL my_PurgeComm(HANDLE fd, uint32_t flags) struct irp irp; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_PurgeComm(fd, flags); + } + memset(&irp, 0, sizeof(irp)); irp.op = IRP_OP_IOCTL; irp.fd = fd; @@ -415,6 +508,10 @@ static BOOL STDCALL my_SetCommMask(HANDLE fd, uint32_t mask) struct irp irp; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_SetCommMask(fd, mask); + } + memset(&irp, 0, sizeof(irp)); irp.op = IRP_OP_IOCTL; irp.fd = fd; @@ -440,6 +537,10 @@ static BOOL STDCALL my_SetCommState(HANDLE fd, const DCB *dcb) SERIAL_LINE_CONTROL line_control; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_SetCommState(fd, dcb); + } + if (dcb == NULL) { log_warning("%s: DCB pointer is NULL", __func__); SetLastError(ERROR_INVALID_PARAMETER); @@ -616,6 +717,10 @@ static BOOL STDCALL my_SetCommTimeouts(HANDLE fd, COMMTIMEOUTS *src) SERIAL_TIMEOUTS dest; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_SetCommTimeouts(fd, src); + } + dest.ReadIntervalTimeout = src->ReadIntervalTimeout; dest.ReadTotalTimeoutMultiplier = src->ReadTotalTimeoutMultiplier; dest.ReadTotalTimeoutConstant = src->ReadTotalTimeoutConstant; @@ -644,6 +749,10 @@ static BOOL STDCALL my_SetupComm(HANDLE fd, uint32_t in_q, uint32_t out_q) SERIAL_QUEUE_SIZE qs; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_SetupComm(fd, in_q, out_q); + } + qs.InSize = in_q; qs.OutSize = out_q; @@ -668,6 +777,10 @@ static BOOL STDCALL my_SetCommBreak(HANDLE fd) struct irp irp; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_SetCommBreak(fd); + } + memset(&irp, 0, sizeof(irp)); irp.op = IRP_OP_IOCTL; irp.fd = fd; @@ -687,6 +800,10 @@ static BOOL STDCALL my_ClearCommBreak(HANDLE fd) struct irp irp; HRESULT hr; + if (!rs232_check_fd(fd)) { + return real_ClearCommBreak(fd); + } + memset(&irp, 0, sizeof(irp)); irp.op = IRP_OP_IOCTL; irp.fd = fd; diff --git a/src/main/hooklib/rs232.h b/src/main/hooklib/rs232.h index f0beddf..a32ea15 100644 --- a/src/main/hooklib/rs232.h +++ b/src/main/hooklib/rs232.h @@ -1,6 +1,21 @@ #ifndef HOOKLIB_RS232_H #define HOOKLIB_RS232_H +#include + void rs232_hook_init(void); +/** + * Allows limiting the rs232 to only the specified fd's + */ + +void rs232_hook_limit_hooks(void); + +/** + * Adds the specified fd to the list of descriptors to hook + * + * @param fd descriptor to hook + */ +void rs232_hook_add_fd(HANDLE fd); + #endif diff --git a/src/main/iidxhook-util/acio.c b/src/main/iidxhook-util/acio.c index a1bc8c2..0ca919c 100644 --- a/src/main/iidxhook-util/acio.c +++ b/src/main/iidxhook-util/acio.c @@ -14,6 +14,7 @@ #include "acioemu/icca.h" #include "hook/iohook.h" +#include "hooklib/rs232.h" #include "iidxhook-util/acio.h" @@ -41,6 +42,8 @@ void iidxhook_util_acio_init(bool legacy_mode) ac_io_emu_icca_init(&iidxhook_util_acio_emu_icca[i], &iidxhook_util_acio_emu, i); } + + rs232_hook_add_fd(iidxhook_util_acio_emu.fd); } void iidxhook_util_acio_fini(void) diff --git a/src/main/iidxhook8/bio2.c b/src/main/iidxhook8/bio2.c index 82c1149..29a3a10 100644 --- a/src/main/iidxhook8/bio2.c +++ b/src/main/iidxhook8/bio2.c @@ -13,6 +13,7 @@ #include "acioemu/emu.h" #include "hook/iohook.h" +#include "hooklib/rs232.h" #include "iidxhook8/bi2a.h" @@ -31,6 +32,8 @@ void bio2_port_init(bool disable_poll_limiter) // they use the same framing ac_io_emu_init(&bio2_emu, L"COM4"); bio2_emu_bi2a_init(&bio2_emu, disable_poll_limiter); + + rs232_hook_add_fd(bio2_emu.fd); } void bio2_port_fini(void) diff --git a/src/main/iidxhook8/dllmain.c b/src/main/iidxhook8/dllmain.c index 47178d7..5e90d3b 100644 --- a/src/main/iidxhook8/dllmain.c +++ b/src/main/iidxhook8/dllmain.c @@ -126,6 +126,7 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) API implementations with real IO devices */ iohook_init(iidxhook_handlers, lengthof(iidxhook_handlers)); rs232_hook_init(); + rs232_hook_limit_hooks(); if (!iidxhook8_config_io.disable_bio2_emu) { bio2_port_init(iidxhook8_config_io.disable_poll_limiter);