135 lines
3.0 KiB
C
135 lines
3.0 KiB
C
#define WIN32_NO_STATUS
|
|
#include <windows.h>
|
|
#undef WIN32_NO_STATUS
|
|
#include <winternl.h>
|
|
|
|
#include <ntstatus.h>
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
|
|
#include "idmac/jvs.h"
|
|
|
|
#include "hook/iobuf.h"
|
|
#include "hook/iohook.h"
|
|
#include "hooklib/uart.h"
|
|
|
|
#include "hooklib/setupapi.h"
|
|
|
|
#include "jvs/jvs-bus.h"
|
|
|
|
#include "util/dprintf.h"
|
|
#include "util/dump.h"
|
|
#include "util/str.h"
|
|
|
|
enum {
|
|
JVS_IOCTL_HELLO = 0x80006004,
|
|
JVS_IOCTL_SENSE = 0x8000600C,
|
|
JVS_IOCTL_TRANSACT = 0x8000E008,
|
|
};
|
|
|
|
static HRESULT jvs_handle_irp(struct irp *irp);
|
|
static HRESULT jvs_handle_irp_locked(struct irp *irp);
|
|
|
|
static CRITICAL_SECTION jvs_lock;
|
|
static struct uart jvs_uart;
|
|
static uint8_t jvs_written_bytes[516];
|
|
static uint8_t jvs_readable_bytes[516];
|
|
static struct jvs_node *jvs_root;
|
|
static jvs_provider_t jvs_provider;
|
|
|
|
HRESULT jvs_hook_init(const struct jvs_config *cfg, jvs_provider_t provider)
|
|
{
|
|
HRESULT hr;
|
|
|
|
assert(cfg != NULL);
|
|
|
|
if (!cfg->enable) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
InitializeCriticalSection(&jvs_lock);
|
|
|
|
uart_init(&jvs_uart, 2);
|
|
jvs_uart.written.bytes = jvs_written_bytes;
|
|
jvs_uart.written.nbytes = sizeof(jvs_written_bytes);
|
|
jvs_uart.readable.bytes = jvs_readable_bytes;
|
|
jvs_uart.readable.nbytes = sizeof(jvs_readable_bytes);
|
|
|
|
jvs_provider = provider;
|
|
|
|
return iohook_push_handler(jvs_handle_irp);
|
|
}
|
|
|
|
static HRESULT jvs_handle_irp(struct irp *irp)
|
|
{
|
|
HRESULT hr;
|
|
|
|
assert(irp != NULL);
|
|
|
|
if (!uart_match_irp(&jvs_uart, irp)) {
|
|
return iohook_invoke_next(irp);
|
|
}
|
|
|
|
EnterCriticalSection(&jvs_lock);
|
|
hr = jvs_handle_irp_locked(irp);
|
|
LeaveCriticalSection(&jvs_lock);
|
|
|
|
return hr;
|
|
}
|
|
|
|
static HRESULT jvs_handle_irp_locked(struct irp *irp)
|
|
{
|
|
#if 0
|
|
dprintf("\nJVS Port: Outbound frame:\n");
|
|
dump_const_iobuf(&irp->write);
|
|
#endif
|
|
|
|
struct iobuf req_iobuf;
|
|
HRESULT hr;
|
|
struct jvs_node *root;
|
|
|
|
if (irp->op == IRP_OP_OPEN) {
|
|
dprintf("iDmac JVS: Starting backend\n");
|
|
if (jvs_provider != NULL) {
|
|
hr = jvs_provider(&root);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
jvs_root = root;
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = uart_handle_irp(&jvs_uart, irp);
|
|
|
|
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
|
|
return hr;
|
|
}
|
|
|
|
jvs_bus_transact(jvs_root, irp->write.bytes, irp->write.nbytes, &irp->read);
|
|
|
|
#if 1
|
|
dprintf("JVS Port: Inbound frame:\n");
|
|
dump_iobuf(&irp->read);
|
|
dprintf("\n");
|
|
#endif
|
|
|
|
if (irp->read.pos == 0) {
|
|
/* The un-acked JVS reset command must return ERROR_NO_DATA_DETECTED,
|
|
and this error must always be returned asynchronously. And since
|
|
async I/O comes from the NT kernel, we have to return that win32
|
|
error as the equivalent NTSTATUS. */
|
|
|
|
if (irp->ovl == NULL || irp->ovl->hEvent == NULL) {
|
|
return HRESULT_FROM_WIN32(ERROR_NO_DATA_DETECTED);
|
|
}
|
|
|
|
irp->ovl->Internal = STATUS_NO_DATA_DETECTED;
|
|
SetEvent(irp->ovl->hEvent);
|
|
|
|
return HRESULT_FROM_WIN32(ERROR_IO_PENDING);
|
|
} else {
|
|
return S_OK;
|
|
}
|
|
}
|