diff --git a/common/board/led15070.c b/common/board/led15070.c index 1cd9be5..6644ef9 100644 --- a/common/board/led15070.c +++ b/common/board/led15070.c @@ -244,6 +244,12 @@ static HRESULT led15070_handle_irp_locked(int board, struct irp *irp) } } + if (irp->op == IRP_OP_READ) { + if (irp->ovl != NULL && boarduart->readable.pos == 0) { + Sleep(1); + } + } + hr = uart_handle_irp(boarduart, irp); if (FAILED(hr) || irp->op != IRP_OP_WRITE) { diff --git a/games/mai2hook/touch.c b/games/mai2hook/touch.c index 305d005..7e0e615 100644 --- a/games/mai2hook/touch.c +++ b/games/mai2hook/touch.c @@ -1,5 +1,8 @@ +#include + #include #include +#include #include "hooklib/fdshark.h" #include "hooklib/reg.h" @@ -10,6 +13,59 @@ #include "util/dprintf.h" #include "util/dump.h" +static HRESULT touch_handle_irp(struct irp *irp); +static HRESULT touch_handle_irp_locked( + struct irp *irp, + struct uart *uart); +static HRESULT touch_handle_read(struct irp *irp, struct uart *uart); +static HRESULT touch_handle_ioctl(struct irp *irp, struct uart *uart); +static void touch_complete_pending_read(struct uart *uart); +static void touch_release_pending_read(struct uart *uart); +static void touch_clear_auto_scan(struct uart *uart); +static bool touch_read_available(struct uart *uart); +static size_t touch_current_depth(struct uart *uart); +static void touch_shift_read(struct uart *uart, struct iobuf *read); +static HRESULT touch_enqueue( + struct uart *uart, + CONDITION_VARIABLE *cv, + const void *bytes, + size_t nbytes); +static HRESULT touch_enqueue_reply( + struct uart *uart, + CONDITION_VARIABLE *cv, + uint8_t side, + uint8_t sensor, + uint8_t command, + uint8_t value); +static void touch_auto_scan(const uint8_t player, const uint8_t state[7]); + +struct touch_pending_read { + OVERLAPPED *ovl; + uint8_t *bytes; + size_t nbytes; +}; + +enum { + touch_pending_reads_capacity = 4096, +}; + +struct touch_pending_reads { + struct touch_pending_read entries[touch_pending_reads_capacity]; + size_t head; + size_t count; +}; + +struct touch_auto_scan_state { + uint8_t frame[9]; + size_t pos; + bool valid; +}; + +enum { + touch_status_pending = 0x00000103UL, + touch_status_success = 0x00000000UL, +}; + static HRESULT read_reg_touch_1p(void *bytes, uint32_t *nbytes) { @@ -53,17 +109,38 @@ const char *sensor_to_str(uint8_t sensor) } static CRITICAL_SECTION touch_1p_lock; +static CONDITION_VARIABLE touch_1p_cv; +static struct touch_pending_reads touch_1p_pending_reads; +static struct touch_auto_scan_state touch_1p_auto_scan; static struct uart touch_1p_uart; static uint8_t touch_1p_written_bytes[64]; -static uint8_t touch_1p_readable_bytes[64]; +static uint8_t touch_1p_readable_bytes[1024]; static bool touch_1p_status = false; static CRITICAL_SECTION touch_2p_lock; +static CONDITION_VARIABLE touch_2p_cv; +static struct touch_pending_reads touch_2p_pending_reads; +static struct touch_auto_scan_state touch_2p_auto_scan; static struct uart touch_2p_uart; static uint8_t touch_2p_written_bytes[64]; -static uint8_t touch_2p_readable_bytes[64]; +static uint8_t touch_2p_readable_bytes[1024]; static bool touch_2p_status = false; +static struct touch_pending_reads *touch_get_pending_reads(struct uart *uart) +{ + return uart->port_no == 3 ? &touch_1p_pending_reads : &touch_2p_pending_reads; +} + +static bool *touch_get_status_flag(struct uart *uart) +{ + return uart->port_no == 3 ? &touch_1p_status : &touch_2p_status; +} + +static struct touch_auto_scan_state *touch_get_auto_scan(struct uart *uart) +{ + return uart->port_no == 3 ? &touch_1p_auto_scan : &touch_2p_auto_scan; +} + HRESULT touch_hook_init(const struct touch_config *cfg) { assert(cfg != NULL); @@ -85,6 +162,7 @@ HRESULT touch_hook_init(const struct touch_config *cfg) dprintf("Mai2 touch 1P: Init.\n"); InitializeCriticalSection(&touch_1p_lock); + InitializeConditionVariable(&touch_1p_cv); uart_init(&touch_1p_uart, 3); touch_1p_uart.written.bytes = touch_1p_written_bytes; touch_1p_uart.written.nbytes = sizeof(touch_1p_written_bytes); @@ -97,6 +175,7 @@ HRESULT touch_hook_init(const struct touch_config *cfg) dprintf("Mai2 touch 2P: Init.\n"); InitializeCriticalSection(&touch_2p_lock); + InitializeConditionVariable(&touch_2p_cv); uart_init(&touch_2p_uart, 4); touch_2p_uart.written.bytes = touch_2p_written_bytes; touch_2p_uart.written.nbytes = sizeof(touch_2p_written_bytes); @@ -133,12 +212,16 @@ static HRESULT touch_handle_irp(struct irp *irp) return hr; } -static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart) +static HRESULT touch_handle_irp_locked( + struct irp *irp, + struct uart *uart) { HRESULT hr; if (irp->op == IRP_OP_OPEN) { + touch_clear_auto_scan(uart); + touch_release_pending_read(uart); dprintf("Mai2 touch port %d: Starting backend\n", uart->port_no); hr = mai2_dll.touch_init(touch_auto_scan); @@ -149,10 +232,23 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart) } } + if (irp->op == IRP_OP_READ) { + return touch_handle_read(irp, uart); + } + + if (irp->op == IRP_OP_IOCTL) { + return touch_handle_ioctl(irp, uart); + } + hr = uart_handle_irp(uart, irp); if (FAILED(hr) || irp->op != IRP_OP_WRITE) { + if (irp->op == IRP_OP_CLOSE) { + *touch_get_status_flag(uart) = false; + touch_clear_auto_scan(uart); + touch_release_pending_read(uart); + } return hr; } @@ -160,9 +256,18 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart) dprintf("Mai2 touch port %d WRITE:\n", uart->port_no); dump_iobuf(&uart->written); #endif + + if (uart->written.pos < 6) { + dprintf("Mai2 touch port %d: Short write (%u bytes)\n", + uart->port_no, + (unsigned int) uart->written.pos); + uart->written.pos = 0; + + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + uint8_t port_no = uart->port_no; uint8_t *src = uart->written.bytes; - uint8_t *dest = uart->readable.bytes; switch (src[3]) { @@ -175,18 +280,16 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart) assert(mai2_dll.touch_update != NULL); if (port_no == 3) { - EnterCriticalSection(&touch_1p_lock); touch_1p_status = false; mai2_dll.touch_update(touch_1p_status, touch_2p_status); - LeaveCriticalSection(&touch_1p_lock); } else { - EnterCriticalSection(&touch_2p_lock); touch_2p_status = false; mai2_dll.touch_update(touch_1p_status, touch_2p_status); - LeaveCriticalSection(&touch_2p_lock); } + touch_clear_auto_scan(uart); + touch_release_pending_read(uart); break; case commandSTAT: // Exit Conditioning mode and resume sending touch data. @@ -194,17 +297,13 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart) assert(mai2_dll.touch_update != NULL); if (port_no == 3) { - EnterCriticalSection(&touch_1p_lock); touch_1p_status = true; mai2_dll.touch_update(touch_1p_status, touch_2p_status); - LeaveCriticalSection(&touch_1p_lock); } else { - EnterCriticalSection(&touch_2p_lock); touch_2p_status = true; mai2_dll.touch_update(touch_1p_status, touch_2p_status); - LeaveCriticalSection(&touch_2p_lock); } break; @@ -212,28 +311,40 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart) #if defined(LOG_MAI2_TOUCH) dprintf("Mai2 touch side %c: set sensor %s ratio to %d\n", src[1], sensor_to_str(src[2]), src[4]); #endif - dest[0] = res_start; - dest[1] = src[1]; // L,R - dest[2] = src[2]; // sensor - dest[3] = commandRatio; - dest[4] = src[4]; // Ratio - dest[5] = res_end; - uart->readable.pos = 6; - // The Ratio is fixed at 0x72 and does not need to be sent to mai2io for processing. + hr = touch_enqueue_reply( + uart, + uart == &touch_1p_uart ? &touch_1p_cv : &touch_2p_cv, + src[1], + src[2], + commandRatio, + src[4]); break; case commandSens: #if defined(LOG_MAI2_TOUCH) dprintf("Mai2 touch side %c: set sensor %s sensitivity to %d\n", src[1], sensor_to_str(src[2]), src[4]); #endif - dest[0] = res_start; - dest[1] = src[1]; // L,R - dest[2] = src[2]; // sensor - dest[3] = commandSens; - dest[4] = src[4]; // Sensitivity - dest[5] = res_end; - uart->readable.pos = 6; - mai2_dll.touch_set_sens(dest); + assert(mai2_dll.touch_set_sens != NULL); + hr = touch_enqueue_reply( + uart, + uart == &touch_1p_uart ? &touch_1p_cv : &touch_2p_cv, + src[1], + src[2], + commandSens, + src[4]); + + if (SUCCEEDED(hr)) { + uint8_t sens_bytes[6]; + + sens_bytes[0] = res_start; + sens_bytes[1] = src[1]; + sens_bytes[2] = src[2]; + sens_bytes[3] = commandSens; + sens_bytes[4] = src[4]; + sens_bytes[5] = res_end; + mai2_dll.touch_set_sens(sens_bytes); + } + break; default: @@ -249,11 +360,276 @@ static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart) return hr; } +static HRESULT touch_handle_read(struct irp *irp, struct uart *uart) +{ + struct touch_pending_reads *pending_reads; + struct touch_pending_read *pending; + bool *status; + size_t tail; + + pending_reads = touch_get_pending_reads(uart); + status = touch_get_status_flag(uart); + + if (!touch_read_available(uart)) { + if (irp->ovl != NULL && + *status && + pending_reads->count < touch_pending_reads_capacity) { + tail = (pending_reads->head + pending_reads->count) % + touch_pending_reads_capacity; + pending = &pending_reads->entries[tail]; + pending_reads->count++; + + pending->ovl = irp->ovl; + pending->bytes = irp->read.bytes; + pending->nbytes = irp->read.nbytes; + pending->ovl->Internal = touch_status_pending; + pending->ovl->InternalHigh = 0; + + if (pending->ovl->hEvent != NULL) { + ResetEvent(pending->ovl->hEvent); + } + + return HRESULT_FROM_WIN32(ERROR_IO_PENDING); + } + return E_PENDING; + } + + touch_shift_read(uart, &irp->read); + + return S_OK; +} + +static HRESULT touch_handle_ioctl(struct irp *irp, struct uart *uart) +{ + if (irp->ioctl == IOCTL_SERIAL_GET_COMMSTATUS) { + uart->status.AmountInInQueue = (ULONG) touch_current_depth(uart); + uart->status.AmountInOutQueue = uart->written.pos; + + return iobuf_write(&irp->read, &uart->status, sizeof(uart->status)); + } + + return uart_handle_irp(uart, irp); +} + +static void touch_complete_pending_read(struct uart *uart) +{ + struct touch_pending_reads *pending_reads; + struct touch_pending_read *pending; + struct iobuf read; + OVERLAPPED *ovl; + HANDLE event; + + pending_reads = touch_get_pending_reads(uart); + + if (pending_reads->count == 0 || !touch_read_available(uart)) { + return; + } + + pending = &pending_reads->entries[pending_reads->head]; + + read.bytes = pending->bytes; + read.nbytes = pending->nbytes; + read.pos = 0; + + touch_shift_read(uart, &read); + + ovl = pending->ovl; + memset(pending, 0, sizeof(*pending)); + pending_reads->head = (pending_reads->head + 1) % + touch_pending_reads_capacity; + pending_reads->count--; + + ovl->InternalHigh = (ULONG_PTR) read.pos; + + event = ovl->hEvent; + MemoryBarrier(); + ovl->Internal = touch_status_success; + + if (event != NULL) { + SetEvent(event); + } +} + +static void touch_release_pending_read(struct uart *uart) +{ + struct touch_pending_reads *pending_reads; + struct touch_pending_read *pending; + OVERLAPPED *ovl; + HANDLE event; + size_t i; + + pending_reads = touch_get_pending_reads(uart); + + for (i = 0; i < pending_reads->count; i++) { + pending = &pending_reads->entries[ + (pending_reads->head + i) % touch_pending_reads_capacity]; + + if (pending->ovl == NULL) { + continue; + } + + ovl = pending->ovl; + ovl->InternalHigh = 0; + event = ovl->hEvent; + MemoryBarrier(); + ovl->Internal = touch_status_success; + + if (event != NULL) { + SetEvent(event); + } + } + + memset(pending_reads, 0, sizeof(*pending_reads)); +} + +static void touch_clear_auto_scan(struct uart *uart) +{ + struct touch_auto_scan_state *auto_scan; + + auto_scan = touch_get_auto_scan(uart); + auto_scan->pos = 0; + auto_scan->valid = false; +} + +static bool touch_read_available(struct uart *uart) +{ + return uart->readable.pos > 0 || touch_get_auto_scan(uart)->valid; +} + +static size_t touch_current_depth(struct uart *uart) +{ + size_t depth; + struct touch_auto_scan_state *auto_scan; + + depth = uart->readable.pos; + auto_scan = touch_get_auto_scan(uart); + + if (auto_scan->valid) { + depth += sizeof(auto_scan->frame) - auto_scan->pos; + } + + return depth; +} + +static void touch_shift_read(struct uart *uart, struct iobuf *read) +{ + struct touch_auto_scan_state *auto_scan; + size_t read_avail; + size_t frame_avail; + size_t chunksz; + + if (uart->readable.pos > 0) { + iobuf_shift(read, &uart->readable); + return; + } + + auto_scan = touch_get_auto_scan(uart); + + if (!auto_scan->valid) { + return; + } + + read_avail = read->nbytes - read->pos; + frame_avail = sizeof(auto_scan->frame) - auto_scan->pos; + chunksz = read_avail < frame_avail ? read_avail : frame_avail; + + memcpy(&read->bytes[read->pos], + &auto_scan->frame[auto_scan->pos], + chunksz); + read->pos += chunksz; + auto_scan->pos += chunksz; + + if (auto_scan->pos == sizeof(auto_scan->frame)) { + auto_scan->pos = 0; + auto_scan->valid = false; + } +} + +static HRESULT touch_enqueue( + struct uart *uart, + CONDITION_VARIABLE *cv, + const void *bytes, + size_t nbytes) +{ + HRESULT hr; + + hr = iobuf_write(&uart->readable, bytes, nbytes); + + if (FAILED(hr)) { + dprintf("Mai2 touch port %d: RX queue overflow (%u/%u bytes)\n", + uart->port_no, + (unsigned int) uart->readable.pos, + (unsigned int) uart->readable.nbytes); + } else { + while (touch_read_available(uart) && + touch_get_pending_reads(uart)->count > 0) { + touch_complete_pending_read(uart); + } + WakeConditionVariable(cv); + } + + return hr; +} + +static HRESULT touch_enqueue_reply( + struct uart *uart, + CONDITION_VARIABLE *cv, + uint8_t side, + uint8_t sensor, + uint8_t command, + uint8_t value) +{ + uint8_t reply[6]; + + reply[0] = res_start; + reply[1] = side; + reply[2] = sensor; + reply[3] = command; + reply[4] = value; + reply[5] = res_end; + + return touch_enqueue(uart, cv, reply, sizeof(reply)); +} + static void touch_auto_scan(const uint8_t player, const uint8_t state[7]) { - struct uart *touch_uart = player == 1 ? &touch_1p_uart : &touch_2p_uart; - touch_uart->readable.bytes[0] = res_start; - memcpy(&touch_uart->readable.bytes[1], state, 7); - touch_uart->readable.bytes[8] = res_end; - touch_uart->readable.pos = 9; + struct touch_auto_scan_state *auto_scan; + struct uart *touch_uart; + CRITICAL_SECTION *touch_lock; + CONDITION_VARIABLE *touch_cv; + uint8_t frame[9]; + + if (player == 1) { + touch_uart = &touch_1p_uart; + touch_lock = &touch_1p_lock; + touch_cv = &touch_1p_cv; + } else { + touch_uart = &touch_2p_uart; + touch_lock = &touch_2p_lock; + touch_cv = &touch_2p_cv; + } + + if (touch_uart->readable.bytes == NULL) { + return; + } + + frame[0] = res_start; + memcpy(&frame[1], state, 7); + frame[8] = res_end; + + EnterCriticalSection(touch_lock); + if (!*touch_get_status_flag(touch_uart)) { + LeaveCriticalSection(touch_lock); + return; + } + auto_scan = touch_get_auto_scan(touch_uart); + memcpy(auto_scan->frame, frame, sizeof(frame)); + auto_scan->pos = 0; + auto_scan->valid = true; + while (touch_read_available(touch_uart) && + touch_get_pending_reads(touch_uart)->count > 0) { + touch_complete_pending_read(touch_uart); + } + WakeConditionVariable(touch_cv); + LeaveCriticalSection(touch_lock); } diff --git a/games/mai2hook/touch.h b/games/mai2hook/touch.h index f6fc72a..23d67ec 100644 --- a/games/mai2hook/touch.h +++ b/games/mai2hook/touch.h @@ -29,10 +29,3 @@ extern const char *sensor_map[34]; const char *sensor_to_str(uint8_t sensor); HRESULT touch_hook_init(const struct touch_config *cfg); -static HRESULT touch_handle_irp(struct irp *irp); -static HRESULT touch_handle_irp_locked(struct irp *irp, struct uart *uart); - -/* Called in mai2io to send touch data. - Similar to chuni slider_res_auto_scan, but the host does not require periodic updates. - Touch data is sent only when there is a change. */ -static void touch_auto_scan(const uint8_t player, const uint8_t state[7]); diff --git a/games/mai2io/mai2io.c b/games/mai2io/mai2io.c index 6a15bdf..6729538 100644 --- a/games/mai2io/mai2io.c +++ b/games/mai2io/mai2io.c @@ -2,6 +2,7 @@ #include #include +#include #include "mai2hook/touch.h" #include "mai2io/config.h" @@ -160,34 +161,30 @@ void mai2_io_touch_set_sens(uint8_t *bytes) { } void mai2_io_touch_update(bool player1, bool player2) { - if (mai2_io_cfg.debug_input_1p) { - if (player1 && mai2_io_touch_1p_thread == NULL) { - mai2_io_touch_1p_thread = (HANDLE)_beginthreadex( - NULL, 0, mai2_io_touch_1p_thread_proc, _callback, 0, NULL); - } else if (!player1 && mai2_io_touch_1p_thread != NULL) { - mai2_io_touch_1p_stop_flag = true; + if (player1 && mai2_io_touch_1p_thread == NULL) { + mai2_io_touch_1p_thread = (HANDLE)_beginthreadex( + NULL, 0, mai2_io_touch_1p_thread_proc, _callback, 0, NULL); + } else if (!player1 && mai2_io_touch_1p_thread != NULL) { + mai2_io_touch_1p_stop_flag = true; - WaitForSingleObject(mai2_io_touch_1p_thread, INFINITE); - CloseHandle(mai2_io_touch_1p_thread); - mai2_io_touch_1p_thread = NULL; + WaitForSingleObject(mai2_io_touch_1p_thread, INFINITE); + CloseHandle(mai2_io_touch_1p_thread); + mai2_io_touch_1p_thread = NULL; - mai2_io_touch_1p_stop_flag = false; - } + mai2_io_touch_1p_stop_flag = false; } - if (mai2_io_cfg.debug_input_2p) { - if (player2 && mai2_io_touch_2p_thread == NULL) { - mai2_io_touch_2p_thread = (HANDLE)_beginthreadex( - NULL, 0, mai2_io_touch_2p_thread_proc, _callback, 0, NULL); - } else if (!player2 && mai2_io_touch_2p_thread != NULL) { - mai2_io_touch_2p_stop_flag = true; + if (player2 && mai2_io_touch_2p_thread == NULL) { + mai2_io_touch_2p_thread = (HANDLE)_beginthreadex( + NULL, 0, mai2_io_touch_2p_thread_proc, _callback, 0, NULL); + } else if (!player2 && mai2_io_touch_2p_thread != NULL) { + mai2_io_touch_2p_stop_flag = true; - WaitForSingleObject(mai2_io_touch_2p_thread, INFINITE); - CloseHandle(mai2_io_touch_2p_thread); - mai2_io_touch_2p_thread = NULL; + WaitForSingleObject(mai2_io_touch_2p_thread, INFINITE); + CloseHandle(mai2_io_touch_2p_thread); + mai2_io_touch_2p_thread = NULL; - mai2_io_touch_2p_stop_flag = false; - } + mai2_io_touch_2p_stop_flag = false; } } @@ -197,14 +194,18 @@ static unsigned int __stdcall mai2_io_touch_1p_thread_proc(void *ctx) { while (!mai2_io_touch_1p_stop_flag) { uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0}; - for (int i = 0; i < 34; i++) { - if (GetAsyncKeyState(mai2_io_cfg.vk_1p_touch[i])) { - int byteIndex = i / 5; - int bitIndex = i % 5; - state[byteIndex] |= (1 << bitIndex); + if (mai2_io_cfg.debug_input_1p) { + for (int i = 0; i < 34; i++) { + if (GetAsyncKeyState(mai2_io_cfg.vk_1p_touch[i])) { + int byteIndex = i / 5; + int bitIndex = i % 5; + state[byteIndex] |= (1 << bitIndex); + } } } + callback(1, state); + Sleep(1); } return 0; @@ -216,14 +217,18 @@ static unsigned int __stdcall mai2_io_touch_2p_thread_proc(void *ctx) { while (!mai2_io_touch_2p_stop_flag) { uint8_t state[7] = {0, 0, 0, 0, 0, 0, 0}; - for (int i = 0; i < 34; i++) { - if (GetAsyncKeyState(mai2_io_cfg.vk_2p_touch[i])) { - int byteIndex = i / 5; - int bitIndex = i % 5; - state[byteIndex] |= (1 << bitIndex); + if (mai2_io_cfg.debug_input_2p) { + for (int i = 0; i < 34; i++) { + if (GetAsyncKeyState(mai2_io_cfg.vk_2p_touch[i])) { + int byteIndex = i / 5; + int bitIndex = i % 5; + state[byteIndex] |= (1 << bitIndex); + } } } + callback(2, state); + Sleep(1); } return 0; diff --git a/games/mai2io/mai2io.h b/games/mai2io/mai2io.h index 297f35c..e061bbf 100644 --- a/games/mai2io/mai2io.h +++ b/games/mai2io/mai2io.h @@ -120,7 +120,8 @@ void mai2_io_touch_set_sens(uint8_t *bytes); * * This function determines whether the game is ready to accept touch input based on the states of player 1 and player 2. * If the game is ready, it creates or stops the corresponding threads to handle touch data for each player. - * Whether or not threads are created for each player is controlled by `mai2_io_cfg.debug_input_1p` and `mai2_io_cfg.debug_input_2p` configuration. + * The `debug_input_1p` and `debug_input_2p` settings only control whether keyboard input is mapped into touch bits. + * When a player is active, the thread continues to send idle all-zero frames even with debug input disabled. * * @param player1 If `true`, indicates the game is ready to accept touch data from player 1, `false` means the game is not ready. * @param player2 If `true`, indicates the game is ready to accept touch data from player 2, `false` means the game is not ready.