Files
djhackersdev_bemanitools/src/main/acioemu/emu.c
T
icex2 e4a221e15b refactor: Entire code base, thread and log usage
Boils down to:
- Include headers
- Reduce boiler plate with helpers
- Swap out explicit usages with core API layer
  and ensure the right API is configured beforehand
2024-02-25 09:30:53 +01:00

211 lines
4.9 KiB
C

#include <windows.h> /* Usermode API */
// clang-format off
// Don't format because the order is important here
#include <ntdef.h>
#include <devioctl.h>
#include <ntddser.h>
// clang-format on
#include <stdbool.h>
#include <stdint.h>
#include <wchar.h>
#include "acioemu/emu.h"
#include "acioemu/pipe.h"
#include "core/log.h"
#include "hook/iohook.h"
#include "util/str.h"
static HRESULT ac_io_emu_open(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_close(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_read(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_write(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_ioctl(struct ac_io_emu *emu, struct irp *irp);
void ac_io_emu_init(struct ac_io_emu *emu, const wchar_t *filename)
{
log_assert(emu != NULL);
log_assert(filename != NULL);
HRESULT hr;
memset(emu, 0, sizeof(*emu));
hr = iohook_open_nul_fd(&emu->fd);
if (hr != S_OK) {
log_fatal("Opening nul fd failed: %08lx", hr);
}
emu->wfilename = wstr_dup(filename);
wstr_narrow(filename, &emu->filename);
ac_io_in_init(&emu->in);
ac_io_out_init(&emu->out);
}
void ac_io_emu_fini(struct ac_io_emu *emu)
{
log_assert(emu != NULL);
free(emu->filename);
free(emu->wfilename);
if (emu->fd != NULL) {
CloseHandle(emu->fd);
}
memset(emu, 0, sizeof(*emu));
}
bool ac_io_emu_match_irp(const struct ac_io_emu *emu, const struct irp *irp)
{
log_assert(emu != NULL);
log_assert(irp != NULL);
if (irp->op == IRP_OP_OPEN) {
return wstr_eq(emu->wfilename, irp->open_filename);
} else {
return irp->fd == emu->fd;
}
}
HRESULT
ac_io_emu_dispatch_irp(struct ac_io_emu *emu, struct irp *irp)
{
log_assert(irp != NULL);
switch (irp->op) {
case IRP_OP_OPEN:
return ac_io_emu_open(emu, irp);
case IRP_OP_CLOSE:
return ac_io_emu_close(emu, irp);
case IRP_OP_READ:
return ac_io_emu_read(emu, irp);
case IRP_OP_WRITE:
return ac_io_emu_write(emu, irp);
case IRP_OP_IOCTL:
return ac_io_emu_ioctl(emu, irp);
case IRP_OP_FSYNC:
return S_FALSE;
default:
return E_NOTIMPL;
}
}
static HRESULT ac_io_emu_open(struct ac_io_emu *emu, struct irp *irp)
{
irp->fd = emu->fd;
log_info("%s: ACIO port opened", emu->filename);
return S_FALSE;
}
static HRESULT ac_io_emu_close(struct ac_io_emu *emu, struct irp *irp)
{
log_info("%s: ACIO port closed", emu->filename);
return S_FALSE;
}
static HRESULT ac_io_emu_read(struct ac_io_emu *emu, struct irp *irp)
{
ac_io_in_drain(&emu->in, &irp->read);
return S_FALSE;
}
static HRESULT ac_io_emu_write(struct ac_io_emu *emu, struct irp *irp)
{
const struct ac_io_message *msg;
for (;;) {
ac_io_out_supply(&emu->out, &irp->write);
if (!ac_io_out_have_message(&emu->out)) {
break;
}
msg = ac_io_out_get_message(&emu->out);
if (msg != NULL) {
break;
}
// this supplies a single SOF byte per byte read
ac_io_in_supply(&emu->in, NULL, 0);
ac_io_out_consume_message(&emu->out);
}
return ac_io_out_have_message(&emu->out) ? S_OK : S_FALSE;
}
static HRESULT ac_io_emu_ioctl(struct ac_io_emu *emu, struct irp *irp)
{
SERIAL_STATUS *status;
log_assert(irp != NULL);
switch (irp->ioctl) {
case IOCTL_SERIAL_GET_COMMSTATUS:
if (irp->read.bytes == NULL) {
log_warning(
"IOCTL_SERIAL_GET_COMMSTATUS: Output buffer is NULL");
return E_INVALIDARG;
}
if (irp->read.nbytes < sizeof(*status)) {
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Buffer is too small");
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
status = (SERIAL_STATUS *) irp->read.bytes;
status->Errors = 0;
status->AmountInInQueue = ac_io_in_is_msg_pending(&emu->in);
irp->read.pos = sizeof(*status);
return S_FALSE;
default:
return S_FALSE;
}
}
const struct ac_io_message *ac_io_emu_request_peek(const struct ac_io_emu *emu)
{
log_assert(emu != NULL);
return ac_io_out_get_message(&emu->out);
}
void ac_io_emu_request_pop(struct ac_io_emu *emu)
{
log_assert(emu != NULL);
ac_io_out_consume_message(&emu->out);
}
void ac_io_emu_response_push(
struct ac_io_emu *emu, const struct ac_io_message *resp, uint64_t delay_us)
{
log_assert(emu != NULL);
log_assert(resp != NULL);
ac_io_in_supply(&emu->in, resp, delay_us);
}
void ac_io_emu_response_push_thunk(
struct ac_io_emu *emu, ac_io_in_thunk_t thunk, void *ctx, uint64_t delay_us)
{
log_assert(emu != NULL);
log_assert(thunk != NULL);
ac_io_in_supply_thunk(&emu->in, thunk, ctx, delay_us);
}