Bemanitools v5.26 release
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
libs += p3ioemu
|
||||
|
||||
src_p3ioemu := \
|
||||
devmgr.c \
|
||||
emu.c \
|
||||
uart.c \
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/table.h"
|
||||
|
||||
#include "p3io/guid.h"
|
||||
|
||||
#include "p3ioemu/devmgr.h"
|
||||
|
||||
#include "util/defs.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
/* Link pointers */
|
||||
|
||||
static HDEVINFO (WINAPI * next_SetupDiGetClassDevsW)(
|
||||
const GUID *class_guid,
|
||||
const wchar_t *enumerator,
|
||||
HWND hwnd,
|
||||
DWORD flags);
|
||||
|
||||
static HDEVINFO (WINAPI * next_SetupDiGetClassDevsA)(
|
||||
const GUID *class_guid,
|
||||
const char *enumerator,
|
||||
HWND hwnd,
|
||||
DWORD flags);
|
||||
|
||||
static BOOL (WINAPI *next_SetupDiEnumDeviceInterfaces)(
|
||||
HDEVINFO dev_info,
|
||||
SP_DEVINFO_DATA *info_data,
|
||||
const GUID *iface_guid,
|
||||
DWORD index,
|
||||
SP_DEVICE_INTERFACE_DATA *ifd);
|
||||
|
||||
static BOOL (WINAPI *next_SetupDiGetDeviceInterfaceDetailW)(
|
||||
HDEVINFO dev_info,
|
||||
SP_DEVICE_INTERFACE_DATA *ifd,
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail,
|
||||
DWORD size,
|
||||
DWORD *required_size,
|
||||
SP_DEVINFO_DATA *info_data);
|
||||
|
||||
static BOOL (WINAPI *next_SetupDiDestroyDeviceInfoList)(HDEVINFO dev_info);
|
||||
|
||||
/* API hooks */
|
||||
|
||||
static HDEVINFO WINAPI my_SetupDiGetClassDevsW(
|
||||
const GUID *class_guid,
|
||||
const wchar_t *enumerator,
|
||||
HWND hwnd,
|
||||
DWORD flags);
|
||||
|
||||
static HDEVINFO WINAPI my_SetupDiGetClassDevsA(
|
||||
const GUID *class_guid,
|
||||
const char *enumerator,
|
||||
HWND hwnd,
|
||||
DWORD flags);
|
||||
|
||||
static BOOL WINAPI my_SetupDiEnumDeviceInterfaces(
|
||||
HDEVINFO dev_info,
|
||||
SP_DEVINFO_DATA *info_data,
|
||||
const GUID *iface_guid,
|
||||
DWORD index,
|
||||
SP_DEVICE_INTERFACE_DATA *ifd);
|
||||
|
||||
static BOOL WINAPI my_SetupDiGetDeviceInterfaceDetailW(
|
||||
HDEVINFO dev_info,
|
||||
SP_DEVICE_INTERFACE_DATA *ifd,
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail,
|
||||
DWORD size,
|
||||
DWORD *required_size,
|
||||
SP_DEVINFO_DATA *info_data);
|
||||
|
||||
static BOOL WINAPI my_SetupDiDestroyDeviceInfoList(HDEVINFO dev_info);
|
||||
|
||||
static const struct hook_symbol p3io_setupapi_syms[] = {
|
||||
{
|
||||
.name = "SetupDiGetClassDevsW",
|
||||
.patch = my_SetupDiGetClassDevsW,
|
||||
.link = (void **) &next_SetupDiGetClassDevsW,
|
||||
}, {
|
||||
.name = "SetupDiGetClassDevsA",
|
||||
.patch = my_SetupDiGetClassDevsA,
|
||||
.link = (void **) &next_SetupDiGetClassDevsA,
|
||||
}, {
|
||||
#if 0
|
||||
.name = "SetupDiEnumDeviceInfo",
|
||||
.patch = my_SetupDiEnumDeviceInfo,
|
||||
.link = (void **) &next_SetupDiEnumDeviceInfo,
|
||||
}, {
|
||||
#endif
|
||||
.name = "SetupDiEnumDeviceInterfaces",
|
||||
.patch = my_SetupDiEnumDeviceInterfaces,
|
||||
.link = (void **) &next_SetupDiEnumDeviceInterfaces,
|
||||
}, {
|
||||
.name = "SetupDiGetDeviceInterfaceDetailW",
|
||||
.patch = my_SetupDiGetDeviceInterfaceDetailW,
|
||||
.link = (void **) &next_SetupDiGetDeviceInterfaceDetailW,
|
||||
}, {
|
||||
.name = "SetupDiDestroyDeviceInfoList",
|
||||
.patch = my_SetupDiDestroyDeviceInfoList,
|
||||
.link = (void **) &next_SetupDiDestroyDeviceInfoList,
|
||||
},
|
||||
};
|
||||
|
||||
/* p3iolib appends \p3io to whatever path is returned by SETUPAPI */
|
||||
|
||||
static const wchar_t p3io_path_prefix[] = L"$p3io";
|
||||
static const wchar_t p3io_path[] = L"$p3io\\p3io";
|
||||
|
||||
static HDEVINFO p3io_hdevinfo;
|
||||
|
||||
static HDEVINFO WINAPI my_SetupDiGetClassDevsA(
|
||||
const GUID *class_guid,
|
||||
const char *enumerator,
|
||||
HWND hwnd,
|
||||
DWORD flags)
|
||||
{
|
||||
HDEVINFO result;
|
||||
|
||||
result = next_SetupDiGetClassDevsA(class_guid, enumerator, hwnd, flags);
|
||||
|
||||
if (result != INVALID_HANDLE_VALUE && IsEqualGUID(class_guid, &p3io_guid)) {
|
||||
p3io_hdevinfo = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static HDEVINFO WINAPI my_SetupDiGetClassDevsW(
|
||||
const GUID *class_guid,
|
||||
const wchar_t *enumerator,
|
||||
HWND hwnd,
|
||||
DWORD flags)
|
||||
{
|
||||
HDEVINFO result;
|
||||
|
||||
result = next_SetupDiGetClassDevsW(class_guid, enumerator, hwnd, flags);
|
||||
|
||||
if (result != INVALID_HANDLE_VALUE && IsEqualGUID(class_guid, &p3io_guid)) {
|
||||
p3io_hdevinfo = result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetupDiEnumDeviceInterfaces(
|
||||
HDEVINFO dev_info,
|
||||
SP_DEVINFO_DATA *info_data,
|
||||
const GUID *iface_guid,
|
||||
DWORD index,
|
||||
SP_DEVICE_INTERFACE_DATA *ifd)
|
||||
{
|
||||
if (dev_info != p3io_hdevinfo) {
|
||||
return next_SetupDiEnumDeviceInterfaces(
|
||||
dev_info,
|
||||
info_data,
|
||||
iface_guid,
|
||||
index,
|
||||
ifd);
|
||||
}
|
||||
|
||||
/* Not implemented */
|
||||
|
||||
log_assert(info_data == NULL);
|
||||
|
||||
/* Class GUID is not the same thing as interface GUID but whatever. p3iolib
|
||||
treats them as the same thing. */
|
||||
|
||||
if (index > 0) {
|
||||
SetLastError(ERROR_NO_MORE_ITEMS);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ifd == NULL || ifd->cbSize != sizeof(*ifd)) {
|
||||
SetLastError(ERROR_INVALID_USER_BUFFER);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy(&ifd->InterfaceClassGuid, &p3io_guid, sizeof(GUID));
|
||||
ifd->Flags = SPINT_ACTIVE | SPINT_DEFAULT;
|
||||
ifd->Reserved = 0;
|
||||
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetupDiGetDeviceInterfaceDetailW(
|
||||
HDEVINFO dev_info,
|
||||
SP_DEVICE_INTERFACE_DATA *ifd,
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_W *detail,
|
||||
DWORD size,
|
||||
DWORD *required_size,
|
||||
SP_DEVINFO_DATA *info_data)
|
||||
{
|
||||
if (dev_info != p3io_hdevinfo) {
|
||||
return next_SetupDiGetDeviceInterfaceDetailW(
|
||||
dev_info,
|
||||
ifd,
|
||||
detail,
|
||||
size,
|
||||
required_size,
|
||||
info_data);
|
||||
}
|
||||
|
||||
/* Not implemented */
|
||||
|
||||
log_assert(info_data == NULL);
|
||||
|
||||
if (required_size != NULL) {
|
||||
*required_size = sizeof(*detail) + sizeof(p3io_path_prefix);
|
||||
}
|
||||
|
||||
if (detail != NULL) {
|
||||
if (size < sizeof(*detail) + sizeof(p3io_path_prefix)) {
|
||||
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (detail->cbSize != sizeof(*detail)) {
|
||||
SetLastError(ERROR_INVALID_USER_BUFFER);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
memcpy( detail->DevicePath,
|
||||
p3io_path_prefix,
|
||||
sizeof(p3io_path_prefix));
|
||||
}
|
||||
|
||||
SetLastError(ERROR_SUCCESS);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL WINAPI my_SetupDiDestroyDeviceInfoList(HDEVINFO dev_info)
|
||||
{
|
||||
if (dev_info == p3io_hdevinfo) {
|
||||
p3io_hdevinfo = NULL;
|
||||
}
|
||||
|
||||
return next_SetupDiDestroyDeviceInfoList(dev_info);
|
||||
}
|
||||
|
||||
void p3io_setupapi_insert_hooks(HMODULE target)
|
||||
{
|
||||
hook_table_apply(
|
||||
target,
|
||||
"setupapi.dll",
|
||||
p3io_setupapi_syms,
|
||||
lengthof(p3io_setupapi_syms));
|
||||
|
||||
log_info("Inserted P3IO setupapi hooks into %p", target);
|
||||
}
|
||||
|
||||
bool p3io_setupapi_match_path(const wchar_t *path)
|
||||
{
|
||||
return wstr_eq(path, p3io_path);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef P3IO_DEVMGR_H
|
||||
#define P3IO_DEVMGR_H
|
||||
|
||||
/* Not called setupapi.h so that we don't collide with the real setupapi.h */
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
void p3io_setupapi_insert_hooks(HMODULE target);
|
||||
bool p3io_setupapi_match_path(const wchar_t *path);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,554 @@
|
||||
#define LOG_MODULE "p3ioemu"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "p3io/cmd.h"
|
||||
#include "p3io/frame.h"
|
||||
#include "p3io/ioctl.h"
|
||||
|
||||
#include "p3ioemu/devmgr.h"
|
||||
#include "p3ioemu/emu.h"
|
||||
#include "p3ioemu/uart.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static HANDLE p3io_emu_fd;
|
||||
static uint8_t p3io_emu_resp_bytes[256];
|
||||
static struct iobuf p3io_emu_resp;
|
||||
static const struct p3io_ops *p3io_ops;
|
||||
static void *p3io_ops_ctx;
|
||||
|
||||
static HRESULT p3io_emu_handle_open(struct irp *irp);
|
||||
static HRESULT p3io_emu_handle_close(struct irp *irp);
|
||||
static HRESULT p3io_emu_handle_ioctl(struct irp *irp);
|
||||
static HRESULT p3io_emu_handle_read(struct irp *irp);
|
||||
static HRESULT p3io_emu_handle_write(struct irp *irp);
|
||||
|
||||
static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req);
|
||||
|
||||
static void p3io_cmd_get_version(
|
||||
const struct p3io_hdr *req,
|
||||
struct p3io_resp_version *resp);
|
||||
|
||||
static void p3io_cmd_set_watchdog(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_u8 *resp);
|
||||
|
||||
static void p3io_cmd_set_outputs(
|
||||
const struct p3io_req_set_outputs *req,
|
||||
struct p3io_resp_u8 *resp);
|
||||
|
||||
static void p3io_cmd_read_plug(
|
||||
const struct p3io_req_read_plug *req,
|
||||
struct p3io_resp_read_plug *resp);
|
||||
|
||||
static void p3io_cmd_get_cab_type_or_dipsw(
|
||||
const struct p3io_req_get_cab_type_or_dipsw *req,
|
||||
struct p3io_resp_get_cab_type_or_dipsw *resp);
|
||||
|
||||
static void p3io_cmd_get_video_freq(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_u8 *resp);
|
||||
|
||||
static void p3io_cmd_set_mode(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_u8 *resp);
|
||||
|
||||
static void p3io_cmd_get_coinstock(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_coin_stock *resp);
|
||||
|
||||
static void p3io_cmd_set_coin_counter(
|
||||
const struct p3io_req_set_coin_counter *req,
|
||||
struct p3io_resp_u8 *resp);
|
||||
|
||||
static void p3io_cmd_unknown(
|
||||
const union p3io_req_any *req,
|
||||
struct p3io_resp_u8 *resp);
|
||||
|
||||
void p3io_emu_init(const struct p3io_ops *ops, void *ctx)
|
||||
{
|
||||
log_assert(p3io_emu_fd == NULL);
|
||||
log_assert(ops != NULL);
|
||||
|
||||
p3io_emu_resp.bytes = p3io_emu_resp_bytes;
|
||||
p3io_emu_resp.nbytes = sizeof(p3io_emu_resp_bytes);
|
||||
p3io_emu_resp.pos = 0;
|
||||
|
||||
p3io_ops = ops;
|
||||
p3io_ops_ctx = ctx;
|
||||
|
||||
p3io_emu_fd = iohook_open_dummy_fd();
|
||||
}
|
||||
|
||||
void p3io_emu_fini(void)
|
||||
{
|
||||
if (p3io_emu_fd == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
CloseHandle(p3io_emu_fd);
|
||||
p3io_emu_fd = NULL;
|
||||
|
||||
if (p3io_ops->close != NULL) {
|
||||
p3io_ops->close(p3io_ops_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT p3io_emu_dispatch_irp(struct irp *irp)
|
||||
{
|
||||
log_assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != p3io_emu_fd) {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return p3io_emu_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return p3io_emu_handle_close(irp);
|
||||
case IRP_OP_READ: return p3io_emu_handle_read(irp);
|
||||
case IRP_OP_WRITE: return p3io_emu_handle_write(irp);
|
||||
case IRP_OP_IOCTL: return p3io_emu_handle_ioctl(irp);
|
||||
default: return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT p3io_emu_handle_open(struct irp *irp)
|
||||
{
|
||||
if (!p3io_setupapi_match_path(irp->open_filename)) {
|
||||
return irp_invoke_next(irp);
|
||||
}
|
||||
|
||||
log_info("P3IO device opened");
|
||||
irp->fd = p3io_emu_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_emu_handle_close(struct irp *irp)
|
||||
{
|
||||
log_info("P3IO device closed");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_emu_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
uint32_t *dest;
|
||||
uint32_t pad;
|
||||
HRESULT hr;
|
||||
|
||||
if (irp->ioctl != P3IO_IOCTL_READ_JAMMA) {
|
||||
log_warning("Unknown ioctl: %x", irp->ioctl);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
if (irp->read.nbytes < sizeof(uint32_t)) {
|
||||
log_warning("Insufficient ioctl response buffer space");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
pad = 0;
|
||||
|
||||
if (p3io_ops->read_jamma != NULL) {
|
||||
hr = p3io_ops->read_jamma(p3io_ops_ctx, &pad);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
dest = (uint32_t *) irp->read.bytes;
|
||||
*dest = ~_byteswap_ulong(pad);
|
||||
irp->read.pos = sizeof(pad);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_emu_handle_read(struct irp *irp)
|
||||
{
|
||||
struct const_iobuf tmp;
|
||||
|
||||
iobuf_flip(&tmp, &p3io_emu_resp);
|
||||
p3io_emu_resp.pos = 0;
|
||||
|
||||
return iobuf_move(&irp->read, &tmp);
|
||||
}
|
||||
|
||||
static HRESULT p3io_emu_handle_write(struct irp *irp)
|
||||
{
|
||||
union p3io_req_any req;
|
||||
struct iobuf deframe;
|
||||
HRESULT hr;
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
deframe.bytes = req.raw;
|
||||
deframe.nbytes = sizeof(req.raw);
|
||||
deframe.pos = 0;
|
||||
|
||||
hr = p3io_frame_decode(&deframe, &irp->write);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (deframe.pos < sizeof(req.hdr)) {
|
||||
log_warning("Received truncated P3IO frame");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
p3io_emu_resp.pos = 0;
|
||||
|
||||
return p3io_cmd_dispatch(&req);
|
||||
}
|
||||
|
||||
static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req)
|
||||
{
|
||||
union p3io_resp_any resp;
|
||||
uint8_t cmd;
|
||||
|
||||
cmd = p3io_req_cmd(req);
|
||||
|
||||
switch (cmd) {
|
||||
case P3IO_CMD_GET_VERSION:
|
||||
p3io_cmd_get_version(&req->hdr, &resp.version);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_SET_WATCHDOG:
|
||||
p3io_cmd_set_watchdog(&req->u8, &resp.u8);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_SET_OUTPUTS:
|
||||
p3io_cmd_set_outputs(&req->set_outputs, &resp.u8);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_READ_PLUG:
|
||||
p3io_cmd_read_plug(&req->read_plug, &resp.read_plug);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_GET_CAB_TYPE_OR_DIPSW:
|
||||
p3io_cmd_get_cab_type_or_dipsw(&req->cab_type_or_dipsw,
|
||||
&resp.cab_type_or_dipsw);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_GET_VIDEO_FREQ:
|
||||
p3io_cmd_get_video_freq(&req->u8, &resp.u8);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_SET_MODE:
|
||||
p3io_cmd_set_mode(&req->u8, &resp.u8);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_GET_COINSTOCK:
|
||||
p3io_cmd_get_coinstock(&req->u8, &resp.coin_stock);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_SET_COINCOUNTER:
|
||||
p3io_cmd_set_coin_counter(&req->set_coin_counter, &resp.u8);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_RS232_OPEN_CLOSE:
|
||||
p3io_uart_cmd_open_close(&req->rs232_open_close, &resp.u8);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_RS232_READ:
|
||||
p3io_uart_cmd_read(&req->rs232_read, &resp.rs232_read);
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_CMD_RS232_WRITE:
|
||||
p3io_uart_cmd_write(&req->rs232_write, &resp.rs232_write);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
p3io_cmd_unknown(req, &resp.u8);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
p3io_frame_encode(&p3io_emu_resp, &resp, resp.hdr.nbytes + 1);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void p3io_cmd_get_version(
|
||||
const struct p3io_hdr *req,
|
||||
struct p3io_resp_version *resp)
|
||||
{
|
||||
log_misc("%s", __func__);
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(resp), req);
|
||||
resp->status = 0;
|
||||
resp->str[0] = 'H';
|
||||
resp->str[1] = 'D';
|
||||
resp->str[2] = 'X';
|
||||
resp->str[3] = '\0';
|
||||
resp->major = 1;
|
||||
resp->minor = 2;
|
||||
resp->patch = 3;
|
||||
}
|
||||
|
||||
static void p3io_cmd_set_watchdog(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_u8 *resp)
|
||||
{
|
||||
log_misc("%s(%02x)", __func__, req->u8);
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
resp->status = 0;
|
||||
resp->u8 = 0;
|
||||
}
|
||||
|
||||
static void p3io_cmd_set_outputs(
|
||||
const struct p3io_req_set_outputs *req,
|
||||
struct p3io_resp_u8 *resp)
|
||||
{
|
||||
uint32_t outputs;
|
||||
HRESULT hr;
|
||||
|
||||
if (p3io_ops->set_outputs != NULL) {
|
||||
outputs = _byteswap_ulong(req->outputs);
|
||||
hr = p3io_ops->set_outputs(p3io_ops_ctx, outputs);
|
||||
} else {
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
resp->status = FAILED(hr);
|
||||
resp->u8 = 0;
|
||||
}
|
||||
|
||||
static void p3io_cmd_read_plug(
|
||||
const struct p3io_req_read_plug *req,
|
||||
struct p3io_resp_read_plug *resp)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
log_misc("%s(%02x)", __func__, req->flags);
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
resp->present = 1;
|
||||
|
||||
/*
|
||||
* Some notes about the flags available here:
|
||||
* 0x10: black plug rom read
|
||||
* 0x12: black plug eeprom read
|
||||
* 0x00: white plug rom read
|
||||
* 0x02: white plug eeprom read
|
||||
*
|
||||
* The structure of the buffer is the same for all flags set and the game
|
||||
* just grabs whatever it currently needs from the buffer. i.e. if we
|
||||
* send everything on every read (rom + eeprom) always, the game doesn't
|
||||
* care
|
||||
*/
|
||||
|
||||
if (req->flags & 0x10) {
|
||||
if (p3io_ops->get_roundplug) {
|
||||
hr = p3io_ops->get_roundplug(p3io_ops_ctx, 0, resp->rom,
|
||||
resp->eeprom);
|
||||
} else {
|
||||
log_warning("Reading black roundplug but no function available");
|
||||
memset(resp->rom, 0, 8);
|
||||
memset(resp->eeprom, 0, 32);
|
||||
hr = S_OK;
|
||||
}
|
||||
} else {
|
||||
if (p3io_ops->get_roundplug) {
|
||||
hr = p3io_ops->get_roundplug(p3io_ops_ctx, 1, resp->rom,
|
||||
resp->eeprom);
|
||||
} else {
|
||||
log_warning("Reading white roundplug but no function available");
|
||||
memset(resp->rom, 0, 8);
|
||||
memset(resp->eeprom, 0, 32);
|
||||
hr = S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
log_misc("Reading %s roundplug, success 0x%lX, rom %02X%02X%02X%02X%02X%02X"
|
||||
"%02X%02X, eeprom sig %X%X%X%X%X%X",
|
||||
req->flags & 0x10 ? "black" : "white", hr, resp->rom[0], resp->rom[1],
|
||||
resp->rom[2], resp->rom[3], resp->rom[4], resp->rom[5], resp->rom[6],
|
||||
resp->rom[7], resp->eeprom[0], resp->eeprom[1], resp->eeprom[2],
|
||||
resp->eeprom[3], resp->eeprom[4], resp->eeprom[5]);
|
||||
|
||||
resp->status = FAILED(hr);
|
||||
}
|
||||
|
||||
static void p3io_cmd_get_cab_type_or_dipsw(
|
||||
const struct p3io_req_get_cab_type_or_dipsw *req,
|
||||
struct p3io_resp_get_cab_type_or_dipsw *resp)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t dipsw;
|
||||
enum p3io_cab_type type;
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
|
||||
hr = S_OK;
|
||||
|
||||
if (req->cab_type_or_dipsw == 0) {
|
||||
if (p3io_ops->get_cab_type) {
|
||||
hr = p3io_ops->get_cab_type(p3io_ops_ctx, &type);
|
||||
|
||||
if (hr == S_OK) {
|
||||
switch (type) {
|
||||
case P3IO_CAB_TYPE_SD:
|
||||
log_misc("%s: Returning cab type SD", __func__);
|
||||
resp->status = 1;
|
||||
break;
|
||||
|
||||
case P3IO_CAB_TYPE_HD:
|
||||
log_misc("%s: Returning cab type HD", __func__);
|
||||
resp->status = 2;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_assert(false);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
resp->status = 0;
|
||||
}
|
||||
} else {
|
||||
resp->status = 0;
|
||||
}
|
||||
} else if (req->cab_type_or_dipsw == 1) {
|
||||
if (p3io_ops->get_dipsw) {
|
||||
hr = p3io_ops->get_dipsw(p3io_ops_ctx, &dipsw);
|
||||
|
||||
if (hr == S_OK) {
|
||||
resp->status = dipsw;
|
||||
} else {
|
||||
resp->status = 0;
|
||||
}
|
||||
} else {
|
||||
resp->status = 0;
|
||||
}
|
||||
} else {
|
||||
log_warning("Unknown value for cab type or dipsw: %d",
|
||||
req->cab_type_or_dipsw);
|
||||
resp->status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void p3io_cmd_get_video_freq(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_u8 *resp)
|
||||
{
|
||||
HRESULT hr;
|
||||
enum p3io_video_freq freq;
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
|
||||
if (p3io_ops->get_video_freq != NULL) {
|
||||
hr = p3io_ops->get_video_freq(p3io_ops_ctx, &freq);
|
||||
} else {
|
||||
hr = S_OK;
|
||||
freq = P3IO_VIDEO_FREQ_31KHZ;
|
||||
}
|
||||
|
||||
if (hr == S_OK) {
|
||||
switch (freq) {
|
||||
case P3IO_VIDEO_FREQ_15KHZ:
|
||||
log_misc("%s: Returning 15 kHz", __func__);
|
||||
resp->status = 0;
|
||||
resp->u8 = 0x00;
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_VIDEO_FREQ_31KHZ:
|
||||
log_misc("%s: Returning 31 kHz", __func__);
|
||||
resp->status = 0;
|
||||
resp->u8 = 0x80;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_assert(false);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log_misc("%s: Returning error! (hr=%x)", __func__, (int) hr);
|
||||
resp->status = 1;
|
||||
resp->u8 = 0x00;
|
||||
}
|
||||
}
|
||||
|
||||
static void p3io_cmd_set_mode(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_u8 *resp)
|
||||
{
|
||||
log_misc("%s(%02x)", __func__, req->u8);
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
resp->status = 0;
|
||||
resp->u8 = 0;
|
||||
}
|
||||
|
||||
static void p3io_cmd_get_coinstock(
|
||||
const struct p3io_req_u8 *req,
|
||||
struct p3io_resp_coin_stock *resp)
|
||||
{
|
||||
uint16_t slots[2];
|
||||
HRESULT hr;
|
||||
|
||||
memset(slots, 0, sizeof(slots));
|
||||
|
||||
if (p3io_ops->get_coinstock != NULL) {
|
||||
hr = p3io_ops->get_coinstock(p3io_ops_ctx, slots, lengthof(slots));
|
||||
} else {
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
resp->status = 0;
|
||||
resp->error = FAILED(hr);
|
||||
resp->slots[0] = _byteswap_ushort(slots[0]);
|
||||
resp->slots[1] = _byteswap_ushort(slots[1]);
|
||||
}
|
||||
|
||||
static void p3io_cmd_set_coin_counter(
|
||||
const struct p3io_req_set_coin_counter *req,
|
||||
struct p3io_resp_u8 *resp)
|
||||
{
|
||||
log_misc("%s(%02x %02x)", __func__, req->coin_counter[0],
|
||||
req->coin_counter[1]);
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
resp->status = 0;
|
||||
resp->u8 = 0;
|
||||
}
|
||||
|
||||
static void p3io_cmd_unknown(
|
||||
const union p3io_req_any *req,
|
||||
struct p3io_resp_u8 *resp)
|
||||
{
|
||||
log_warning("Unsupported P3IO command: %02x", p3io_req_cmd(req));
|
||||
|
||||
p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr);
|
||||
resp->status = 1;
|
||||
resp->u8 = 0;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef P3IO_EMU_H
|
||||
#define P3IO_EMU_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
/**
|
||||
* Enum for available video frequencies to select from. Depending on the
|
||||
* game, this feature is used or unused.
|
||||
*/
|
||||
enum p3io_video_freq {
|
||||
P3IO_VIDEO_FREQ_15KHZ = 0,
|
||||
P3IO_VIDEO_FREQ_31KHZ = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum for available cabinet/display types to select from. Depending on the
|
||||
* game, this feature is used or unused.
|
||||
*/
|
||||
enum p3io_cab_type {
|
||||
P3IO_CAB_TYPE_SD = 0,
|
||||
P3IO_CAB_TYPE_HD = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* P3IO operation dispatching table. Many operations are optional and not used
|
||||
* by every game that's running on that type of hardware which means you can
|
||||
* ommit hooking up implementations for them.
|
||||
*/
|
||||
struct p3io_ops {
|
||||
void (*close)(void *ctx);
|
||||
HRESULT (*read_jamma)(void *ctx, uint32_t *state);
|
||||
HRESULT (*set_outputs)(void *ctx, uint32_t state);
|
||||
HRESULT (*get_dipsw)(void *ctx, uint8_t *state);
|
||||
HRESULT (*get_cab_type)(void *ctx, enum p3io_cab_type *type);
|
||||
HRESULT (*get_video_freq)(void *ctx, enum p3io_video_freq *freq);
|
||||
HRESULT (*get_coinstock)(void *ctx, uint16_t *slots, size_t nslots);
|
||||
HRESULT (*get_roundplug)(void *ctx, uint8_t plug_id, uint8_t* rom,
|
||||
uint8_t* eeprom);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the p3io emulation backend.
|
||||
*
|
||||
* @param ops Dispatch table with operation hooks for your target game.
|
||||
* @param ctx A context which is passed along with every call to a function of
|
||||
* your provided dispatch table.
|
||||
*/
|
||||
void p3io_emu_init(const struct p3io_ops *ops, void *ctx);
|
||||
|
||||
/**
|
||||
* Shutdown the p3io emulation backend.
|
||||
*/
|
||||
void p3io_emu_fini(void);
|
||||
|
||||
/**
|
||||
* p3io emulation dispatch irp call. This needs to be hooked to the iohook
|
||||
* module in order to drive the emulation backend with detoured system calls.
|
||||
*/
|
||||
HRESULT p3io_emu_dispatch_irp(struct irp *irp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,445 @@
|
||||
#define LOG_MODULE "p3ioemu-uart"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <ntdef.h>
|
||||
#include <devioctl.h>
|
||||
#include <ntddser.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "p3io/cmd.h"
|
||||
|
||||
#include "p3ioemu/uart.h"
|
||||
|
||||
#include "util/iobuf.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static HRESULT p3io_uart_open(
|
||||
const wchar_t *path,
|
||||
uint32_t baud_rate,
|
||||
HANDLE *fd);
|
||||
static HRESULT p3io_uart_close(HANDLE fd);
|
||||
static HRESULT p3io_uart_read(HANDLE fd, struct iobuf *iobuf);
|
||||
static HRESULT p3io_uart_write(HANDLE fd, struct const_iobuf *iobuf);
|
||||
|
||||
static const wchar_t *p3io_uart_paths[2];
|
||||
static HANDLE p3io_uart_fds[2];
|
||||
|
||||
static const uint32_t p3io_uart_baud_codes[] = {
|
||||
0, 0, 19200, 38400, 57600
|
||||
};
|
||||
|
||||
void p3io_uart_set_path(size_t uart_no, const wchar_t *path)
|
||||
{
|
||||
log_assert(uart_no <= 1);
|
||||
|
||||
p3io_uart_paths[uart_no] = path;
|
||||
}
|
||||
|
||||
void p3io_uart_cmd_open_close(
|
||||
const struct p3io_req_rs232_open_close *req,
|
||||
struct p3io_resp_u8 *resp)
|
||||
{
|
||||
const wchar_t *path;
|
||||
uint32_t baud_rate;
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(req != NULL);
|
||||
log_assert(resp != NULL);
|
||||
|
||||
if (req->port_no > 1) {
|
||||
log_warning("Invalid UART number %i", req->port_no);
|
||||
hr = E_INVALIDARG;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
switch (req->subcmd) {
|
||||
case P3IO_RS232_CMD_OPEN:
|
||||
log_info("Opening remote RS232 port #%d", req->port_no);
|
||||
|
||||
if (req->baud_code < lengthof(p3io_uart_baud_codes)) {
|
||||
baud_rate = p3io_uart_baud_codes[req->baud_code];
|
||||
} else {
|
||||
baud_rate = 0;
|
||||
}
|
||||
|
||||
if (baud_rate == 0) {
|
||||
log_warning("Invalid baud rate code: %02x", req->baud_code);
|
||||
hr = E_FAIL;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
path = p3io_uart_paths[req->port_no];
|
||||
|
||||
if (path == NULL) {
|
||||
log_warning("UART #%i: No downstream connection", req->port_no);
|
||||
hr = E_FAIL;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
hr = p3io_uart_open(path, baud_rate, &p3io_uart_fds[req->port_no]);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("p3io_uart_open() failed: %x", (int) hr);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case P3IO_RS232_CMD_CLOSE:
|
||||
log_info("Closing remote RS232 port #%d", req->port_no);
|
||||
|
||||
hr = p3io_uart_close(p3io_uart_fds[req->port_no]);
|
||||
p3io_uart_fds[req->port_no] = NULL;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warning("Unknown subcommand %02x", req->subcmd);
|
||||
hr = E_FAIL;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
p3io_resp_init(&resp->hdr, sizeof(resp), &req->hdr);
|
||||
resp->status = 0;
|
||||
resp->u8 = FAILED(hr);
|
||||
}
|
||||
|
||||
void p3io_uart_cmd_read(
|
||||
const struct p3io_req_rs232_read *req,
|
||||
struct p3io_resp_rs232_read *resp)
|
||||
{
|
||||
struct iobuf iobuf;
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(req != NULL);
|
||||
log_assert(resp != NULL);
|
||||
|
||||
iobuf.bytes = resp->bytes;
|
||||
iobuf.nbytes = req->nbytes;
|
||||
iobuf.pos = 0;
|
||||
|
||||
if (req->port_no > 1) {
|
||||
log_warning("Invalid UART number %i", req->port_no);
|
||||
hr = E_INVALIDARG;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (req->nbytes > sizeof(resp->bytes)) {
|
||||
log_warning("Excessive read %i", req->nbytes);
|
||||
hr = E_INVALIDARG;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
hr = p3io_uart_read(p3io_uart_fds[req->port_no], &iobuf);
|
||||
|
||||
end:
|
||||
/* Variable-length response, init the header manually */
|
||||
|
||||
resp->hdr.nbytes = iobuf.pos + 3;
|
||||
resp->hdr.seq_no = req->hdr.seq_no;
|
||||
resp->status = FAILED(hr);
|
||||
resp->nbytes = iobuf.pos;
|
||||
}
|
||||
|
||||
void p3io_uart_cmd_write(
|
||||
const struct p3io_req_rs232_write *req,
|
||||
struct p3io_resp_rs232_write *resp)
|
||||
{
|
||||
struct const_iobuf iobuf;
|
||||
HRESULT hr;
|
||||
|
||||
iobuf.bytes = req->bytes;
|
||||
iobuf.nbytes = req->nbytes;
|
||||
iobuf.pos = 0;
|
||||
|
||||
if (req->port_no > 1) {
|
||||
log_warning("Invalid UART number %i", req->port_no);
|
||||
hr = E_INVALIDARG;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
hr = p3io_uart_write(p3io_uart_fds[req->port_no], &iobuf);
|
||||
|
||||
end:
|
||||
p3io_resp_init(&resp->hdr, sizeof(resp), &req->hdr);
|
||||
resp->status = FAILED(hr);
|
||||
resp->nbytes = iobuf.pos;
|
||||
}
|
||||
|
||||
static HRESULT p3io_uart_open(
|
||||
const wchar_t *path,
|
||||
uint32_t baud_rate,
|
||||
HANDLE *fd)
|
||||
{
|
||||
struct irp irp;
|
||||
uint32_t comm_mask;
|
||||
uint32_t flags;
|
||||
SERIAL_QUEUE_SIZE qs;
|
||||
SERIAL_TIMEOUTS timeouts;
|
||||
SERIAL_LINE_CONTROL lc;
|
||||
SERIAL_BAUD_RATE baud;
|
||||
SERIAL_HANDFLOW handflow;
|
||||
HRESULT hr;
|
||||
|
||||
if (*fd != NULL) {
|
||||
log_warning("Port is already open");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_OPEN;
|
||||
irp.open_filename = path;
|
||||
irp.open_access = GENERIC_READ | GENERIC_WRITE;
|
||||
irp.open_share = 0;
|
||||
irp.open_sa = NULL;
|
||||
irp.open_creation = OPEN_EXISTING;
|
||||
irp.open_flags = 0;
|
||||
irp.open_tmpl = NULL;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*fd = irp.fd;
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = *fd;
|
||||
irp.ioctl = IOCTL_SERIAL_SET_WAIT_MASK;
|
||||
irp.write.bytes = (const void *) &comm_mask;
|
||||
irp.write.nbytes = sizeof(comm_mask);
|
||||
comm_mask = EV_RXCHAR;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = *fd;
|
||||
irp.ioctl = IOCTL_SERIAL_SET_QUEUE_SIZE;
|
||||
irp.write.bytes = (const void *) &qs;
|
||||
irp.write.nbytes = sizeof(qs);
|
||||
qs.InSize = 0x4000;
|
||||
qs.OutSize = 0x4000;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = *fd;
|
||||
irp.ioctl = IOCTL_SERIAL_PURGE;
|
||||
irp.write.bytes = (const void *) &flags;
|
||||
irp.write.nbytes = sizeof(flags);
|
||||
flags = PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = *fd;
|
||||
irp.ioctl = IOCTL_SERIAL_SET_TIMEOUTS;
|
||||
irp.write.bytes = (const void *) &timeouts;
|
||||
irp.write.nbytes = sizeof(timeouts);
|
||||
timeouts.ReadIntervalTimeout = -1;
|
||||
timeouts.ReadTotalTimeoutMultiplier = 10;
|
||||
timeouts.ReadTotalTimeoutConstant = 0;
|
||||
timeouts.WriteTotalTimeoutMultiplier = 100;
|
||||
timeouts.WriteTotalTimeoutConstant = 0;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = *fd;
|
||||
irp.ioctl = IOCTL_SERIAL_SET_LINE_CONTROL;
|
||||
irp.write.bytes = (const void *) &lc;
|
||||
irp.write.nbytes = sizeof(lc);
|
||||
lc.WordLength = 8;
|
||||
lc.Parity = NO_PARITY;
|
||||
lc.StopBits = STOP_BIT_1;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = *fd;
|
||||
irp.ioctl = IOCTL_SERIAL_SET_BAUD_RATE;
|
||||
irp.write.bytes = (const void *) &baud;
|
||||
irp.write.nbytes = sizeof(baud);
|
||||
baud.BaudRate = baud_rate;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = *fd;
|
||||
irp.ioctl = IOCTL_SERIAL_SET_HANDFLOW;
|
||||
irp.write.bytes = (const void *) &handflow;
|
||||
irp.write.nbytes = sizeof(handflow);
|
||||
handflow.ControlHandShake = 0;
|
||||
handflow.FlowReplace = 0;
|
||||
handflow.XonLimit = 0;
|
||||
handflow.XoffLimit = 0;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
if (*fd != NULL) {
|
||||
irp.op = IRP_OP_CLOSE;
|
||||
irp.fd = *fd;
|
||||
|
||||
irp_invoke_next(&irp);
|
||||
|
||||
*fd = NULL;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT p3io_uart_close(HANDLE fd)
|
||||
{
|
||||
struct irp irp;
|
||||
HRESULT hr;
|
||||
|
||||
if (fd != NULL) {
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_CLOSE;
|
||||
irp.fd = fd;
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("Error closing port: %x", (int) hr);
|
||||
}
|
||||
} else {
|
||||
log_warning("Port is already closed");
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT p3io_uart_read(HANDLE fd, struct iobuf *iobuf)
|
||||
{
|
||||
struct irp irp;
|
||||
SERIAL_STATUS status;
|
||||
HRESULT hr;
|
||||
|
||||
if (fd == NULL) {
|
||||
log_warning("Read from unopened port");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
/* Peek RX buffer */
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_IOCTL;
|
||||
irp.fd = fd;
|
||||
irp.ioctl = IOCTL_SERIAL_GET_COMMSTATUS;
|
||||
irp.read.bytes = (uint8_t *) &status;
|
||||
irp.read.nbytes = sizeof(status);
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("UART FIFO peek failed: %x", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Return immediately if no data available */
|
||||
|
||||
if (status.AmountInInQueue == 0) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Issue read */
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_READ;
|
||||
irp.fd = fd;
|
||||
memcpy(&irp.read, iobuf, sizeof(*iobuf));
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
log_warning("Read error: %x", (int) hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
memcpy(iobuf, &irp.read, sizeof(*iobuf));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p3io_uart_write(HANDLE fd, struct const_iobuf *iobuf)
|
||||
{
|
||||
struct irp irp;
|
||||
HRESULT hr;
|
||||
|
||||
if (fd == NULL) {
|
||||
log_warning("Write to unopened port");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
memset(&irp, 0, sizeof(irp));
|
||||
irp.op = IRP_OP_WRITE;
|
||||
irp.fd = fd;
|
||||
memcpy(&irp.write, iobuf, sizeof(*iobuf));
|
||||
|
||||
hr = irp_invoke_next(&irp);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
memcpy(iobuf, &irp.write, sizeof(*iobuf));
|
||||
} else {
|
||||
log_warning("Write error: %x", (int) hr);
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef P3IO_UART_H
|
||||
#define P3IO_UART_H
|
||||
|
||||
#include "p3io/cmd.h"
|
||||
|
||||
void p3io_uart_set_path(size_t uart_no, const wchar_t *path);
|
||||
|
||||
void p3io_uart_cmd_open_close(
|
||||
const struct p3io_req_rs232_open_close *req,
|
||||
struct p3io_resp_u8 *resp);
|
||||
|
||||
void p3io_uart_cmd_read(
|
||||
const struct p3io_req_rs232_read *req,
|
||||
struct p3io_resp_rs232_read *resp);
|
||||
|
||||
void p3io_uart_cmd_write(
|
||||
const struct p3io_req_rs232_write *req,
|
||||
struct p3io_resp_rs232_write *resp);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user