Files
djhackersdev_bemanitools/src/main/geninput/io-thread.c
T

206 lines
4.9 KiB
C

#include <windows.h>
#include <stdlib.h>
#include "geninput/dev-list.h"
#include "geninput/hid-generic.h"
#include "geninput/hid-mgr.h"
#include "geninput/hid.h"
#include "geninput/io-thread.h"
#include "geninput/pacdrive.h"
#include "util/defs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/thread.h"
enum io_thread_cmd {
IO_THREAD_CMD_STOP,
IO_THREAD_CMD_ADD_DEVICE,
};
struct io_thread_msg {
enum io_thread_cmd cmd;
const char *dev_node;
HANDLE barrier;
};
static HANDLE io_thread_cp;
static int io_thread_id;
static void io_thread_proc_add_device(const char *dev_node);
static void io_thread_proc_init(void);
static void io_thread_proc_main_loop(void);
static void io_thread_proc_fini(void);
static int io_thread_proc(void *param);
static void io_thread_proc_add_device(const char *dev_node)
{
struct hid_stub *stub;
struct hid_fd *hid_fd;
uintptr_t ctx;
hid_mgr_lock();
stub = hid_mgr_get_named_stub(dev_node);
if (!hid_stub_is_attached(stub)) {
ctx = (uintptr_t) stub;
if (pac_open(&hid_fd, dev_node, io_thread_cp, ctx) ||
hid_generic_open(&hid_fd, dev_node, io_thread_cp, ctx)) {
hid_stub_attach(stub, (struct hid *) hid_fd);
}
}
hid_mgr_unlock();
}
static void io_thread_proc_init(void)
{
const char *dev_node;
struct dev_list devs;
io_thread_cp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
dev_list_init(&devs, &hid_guid);
while (dev_list_next(&devs)) {
dev_node = dev_list_get_dev_node(&devs);
io_thread_proc_add_device(dev_node);
}
dev_list_fini(&devs);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
log_info(
"USB I/O thread ready, thread id = %d",
(unsigned int) GetCurrentThreadId());
}
static void io_thread_proc_main_loop(void)
{
uintptr_t ctx;
OVERLAPPED *p_ovl;
DWORD nbytes;
struct hid_stub *stub;
struct io_thread_msg *msg;
BOOL ok;
for (;;) {
ok = GetQueuedCompletionStatus(
io_thread_cp, &nbytes, (ULONG_PTR *) &ctx, &p_ovl, 0);
if (p_ovl != NULL) {
/* An I/O completed (depending on the value of `ok' either
successfuly or not), either way dispatch the completion event */
stub = (struct hid_stub *) ctx;
hid_mgr_lock();
if (!ok) {
log_warning(
"Async IO returned failure: %08x",
(unsigned int) GetLastError());
hid_stub_detach(stub);
} else {
ok = hid_stub_handle_completion(stub, p_ovl, nbytes);
}
hid_mgr_unlock();
} else if (ok) {
/* No OVERLAPPED*, so this is a command message queued from a
different thread. */
msg = (struct io_thread_msg *) ctx;
if (msg->cmd == IO_THREAD_CMD_STOP) {
break;
} else if (msg->cmd == IO_THREAD_CMD_ADD_DEVICE) {
io_thread_proc_add_device(msg->dev_node);
SetEvent(msg->barrier);
}
} else if (GetLastError() == WAIT_TIMEOUT) {
/* Failure, but a benign one: timeout due to no event.
I don't exactly know why, but we need to back off here for a bit
or we end up soaking the CPU (in kernel mode...). It's probably
a bemanitools bug instead of an NT deficiency but I'll have to
track down the root cause later. */
Sleep(1);
} else {
/* Otherwise idk what's going on */
log_warning(
"Spurious wakeup in I/O mux: %08x",
(unsigned int) GetLastError());
}
}
}
static void io_thread_proc_fini(void)
{
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
CloseHandle(io_thread_cp);
log_misc("USB I/O thread shutting down");
}
static int io_thread_proc(void *param)
{
HANDLE barrier;
barrier = param;
io_thread_proc_init();
SetEvent(barrier);
io_thread_proc_main_loop();
io_thread_proc_fini();
return 0;
}
void io_thread_init(void)
{
HANDLE barrier;
barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
io_thread_id = thread_create(io_thread_proc, barrier, 16384, 0);
WaitForSingleObject(barrier, INFINITE);
CloseHandle(barrier);
}
void io_thread_add_device(const char *dev_node)
{
struct io_thread_msg msg;
msg.cmd = IO_THREAD_CMD_ADD_DEVICE;
msg.dev_node = dev_node;
msg.barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
PostQueuedCompletionStatus(io_thread_cp, 0, (uintptr_t) &msg, NULL);
WaitForSingleObject(msg.barrier, INFINITE);
CloseHandle(msg.barrier);
}
void io_thread_fini(void)
{
struct io_thread_msg msg;
msg.cmd = IO_THREAD_CMD_STOP;
PostQueuedCompletionStatus(io_thread_cp, 0, (uintptr_t) &msg, NULL);
thread_join(io_thread_id, NULL);
thread_destroy(io_thread_id);
}