Update jbio for testmenu support and add jbio-p4io

This commit is contained in:
Will Toohey 2021-04-04 16:41:45 +10:00
parent 6ce4cce500
commit 2a03690772
34 changed files with 1214 additions and 172 deletions

View File

@ -137,6 +137,7 @@ include src/main/inject/Module.mk
include src/main/jbhook/Module.mk include src/main/jbhook/Module.mk
include src/main/jbhook1/Module.mk include src/main/jbhook1/Module.mk
include src/main/jbio-magicbox/Module.mk include src/main/jbio-magicbox/Module.mk
include src/main/jbio-p4io/Module.mk
include src/main/jbio/Module.mk include src/main/jbio/Module.mk
include src/main/jbiotest/Module.mk include src/main/jbiotest/Module.mk
include src/main/launcher/Module.mk include src/main/launcher/Module.mk
@ -144,6 +145,7 @@ include src/main/mempatch-hook/Module.mk
include src/main/mm/Module.mk include src/main/mm/Module.mk
include src/main/p3io/Module.mk include src/main/p3io/Module.mk
include src/main/p3ioemu/Module.mk include src/main/p3ioemu/Module.mk
include src/main/p4iodrv/Module.mk
include src/main/p4ioemu/Module.mk include src/main/p4ioemu/Module.mk
include src/main/pcbidgen/Module.mk include src/main/pcbidgen/Module.mk
include src/main/sdvxhook/Module.mk include src/main/sdvxhook/Module.mk
@ -444,6 +446,15 @@ $(zipdir)/jb-08.zip: \
$(V)echo ... $@ $(V)echo ... $@
$(V)zip -j $@ $^ $(V)zip -j $@ $^
$(zipdir)/jb-hwio.zip: \
build/bin/indep-32/aciomgr.dll \
build/bin/indep-32/eamio-icca.dll \
build/bin/indep-32/jbio-magicbox.dll \
build/bin/indep-32/jbio-p4io.dll \
| $(zipdir)/
$(V)echo ... $@
$(V)zip -j $@ $^
$(zipdir)/sdvx-01-to-04.zip: \ $(zipdir)/sdvx-01-to-04.zip: \
build/bin/avs2_1508-32/launcher.exe \ build/bin/avs2_1508-32/launcher.exe \
build/bin/avs2_1508-32/sdvxhook.dll \ build/bin/avs2_1508-32/sdvxhook.dll \
@ -609,6 +620,7 @@ $(BUILDDIR)/bemanitools.zip: \
$(zipdir)/jb-01.zip \ $(zipdir)/jb-01.zip \
$(zipdir)/jb-05-to-07.zip \ $(zipdir)/jb-05-to-07.zip \
$(zipdir)/jb-08.zip \ $(zipdir)/jb-08.zip \
$(zipdir)/jb-hwio.zip \
$(zipdir)/sdvx-01-to-04.zip \ $(zipdir)/sdvx-01-to-04.zip \
$(zipdir)/sdvx-05.zip \ $(zipdir)/sdvx-05.zip \
$(zipdir)/sdvx-05-cn.zip \ $(zipdir)/sdvx-05-cn.zip \

15
doc/jbhook/jbio-p4io.md Normal file
View File

@ -0,0 +1,15 @@
This library talks to a P4IO (panel) and H44B through ACIO (lights) of a jubeat cab and implements the jbio API of BT5.
Thus, it allows you to use a modern jubeat cab with *any* version of jubeat that is supported by BT5.
# Setup
* Rename `jbio-p4io.dll` to `jbio.dll`.
* Make sure `jbio.dll` is in the same folder as both `jbhook.dll`, `aciomgr.dll` and `jubeat.dll`
* Ensure that your `gamestart.bat` actually injects the appropriate jbhook dll
for example:
```
launcher -K jbhook.dll jubeat.dll ...*
```
or that the application otherwise uses `jbio.dll`
* If you have P4IO but no H44B, lights will be disabled
* You can change the port and baudrate of the H44B node by editing the jbio-h44b.conf that should be created by default

View File

@ -5,6 +5,7 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include "acio/h44b.h"
#include "acio/hdxs.h" #include "acio/hdxs.h"
#include "acio/icca.h" #include "acio/icca.h"
#include "acio/kfca.h" #include "acio/kfca.h"
@ -69,6 +70,8 @@ struct ac_io_message {
uint8_t param; uint8_t param;
struct ac_io_version version; struct ac_io_version version;
struct ac_io_h44b_output h44b_output;
struct ac_io_icca_misc icca_misc; struct ac_io_icca_misc icca_misc;
struct ac_io_icca_state icca_state; struct ac_io_icca_state icca_state;

View File

@ -14,6 +14,6 @@ struct ac_io_h44b_output {
uint8_t right_rgb[3]; uint8_t right_rgb[3];
uint8_t title_rgb[3]; uint8_t title_rgb[3];
uint8_t woofer_rgb[3]; uint8_t woofer_rgb[3];
}; } __attribute__((__packed__));
#endif #endif

View File

@ -1,9 +1,10 @@
libs += aciodrv libs += aciodrv
libs_aciodrv := \ libs_aciodrv := \
src_aciodrv := \ src_aciodrv := \
device.c \ device.c \
h44b.c \
icca.c \ icca.c \
kfca.c \ kfca.c \
port.c \ port.c \

42
src/main/aciodrv/h44b.c Normal file
View File

@ -0,0 +1,42 @@
#define LOG_MODULE "aciodrv-h44b"
#include <stdio.h>
#include <string.h>
#include "acio/h44b.h"
#include "aciodrv/device.h"
#include "util/log.h"
bool aciodrv_h44b_init(
struct aciodrv_device_ctx *device,
uint8_t node_id)
{
return true;
}
bool aciodrv_h44b_lights(
struct aciodrv_device_ctx *device,
uint8_t node_id,
const struct ac_io_h44b_output *lights)
{
struct ac_io_message msg;
log_assert(device);
msg.addr = node_id + 1;
msg.cmd.code = ac_io_u16(AC_IO_H44B_CMD_SET_OUTPUTS);
msg.cmd.nbytes = sizeof(*lights);
msg.cmd.h44b_output = *lights;
if (!aciodrv_send_and_recv(
device,
&msg,
offsetof(struct ac_io_message, cmd.raw) + 1)) {
log_warning("Polling of node %d failed", node_id + 1);
return false;
}
return true;
}

32
src/main/aciodrv/h44b.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef ACIODRV_H44B_H
#define ACIODRV_H44B_H
#include "aciodrv/device.h"
#include "acio/h44b.h"
/**
* Initialize an H44B node.
*
* @param device Context of opened device
* @param node_id Id of the node to initialize (0 based).
* @return True if successful, false on error.
* @note This module is supposed to be used in combination with the common
* device driver foundation.
* @see driver.h
*/
bool aciodrv_h44b_init(struct aciodrv_device_ctx *device, uint8_t node_id);
/**
* Set the H44B LEDs
*
* @param device Context of opened device
* @param node_id Id of the node to query (0 based).
* @param lights Packed lights struct to send
* @return True on success, false on error.
*/
bool aciodrv_h44b_lights(
struct aciodrv_device_ctx *device,
uint8_t node_id,
const struct ac_io_h44b_output *lights);
#endif

View File

@ -61,7 +61,7 @@ void ac_io_emu_h44b_dispatch_request(
req->cmd.raw[i * 3 + 2]); req->cmd.raw[i * 3 + 2]);
} }
jb_io_write_outputs(); jb_io_write_lights();
ac_io_emu_h44b_send_status(h44b, req, 0x00); ac_io_emu_h44b_send_status(h44b, req, 0x00);

View File

@ -169,6 +169,10 @@ static void ac_io_emu_iccb_send_state(
/* state update */ /* state update */
if (!eam_io_poll(iccb->unit_no)) {
log_warning("Polling eamio failed");
}
sensor = eam_io_get_sensor_state(iccb->unit_no); sensor = eam_io_get_sensor_state(iccb->unit_no);
if (sensor != iccb->last_sensor) { if (sensor != iccb->last_sensor) {

View File

@ -29,6 +29,17 @@ enum jb_io_panel_bit {
JB_IO_PANEL_16 = 0x0F, JB_IO_PANEL_16 = 0x0F,
}; };
/* input "single button mode" mappings. Allows you to check each corner of each
button to determine any flaky inputs
*/
enum jb_io_panel_mode {
JB_IO_PANEL_MODE_ALL = 0, // any of the four corners will trigger a panel
JB_IO_PANEL_MODE_TOP_LEFT = 1,
JB_IO_PANEL_MODE_TOP_RIGHT = 2,
JB_IO_PANEL_MODE_BOTTOM_RIGHT = 3,
JB_IO_PANEL_MODE_BOTTOM_LEFT = 4,
};
/* Bit mappings for "system" inputs */ /* Bit mappings for "system" inputs */
enum jb_io_sys_bit { enum jb_io_sys_bit {
JB_IO_SYS_TEST = 0x00, JB_IO_SYS_TEST = 0x00,
@ -73,17 +84,36 @@ bool jb_io_init(
void jb_io_fini(void); void jb_io_fini(void);
/* TODO doc */ /* Read input state */
bool jb_io_read_inputs(void); bool jb_io_read_inputs(void);
bool jb_io_write_outputs(void); /* Get state of coin, test, service inputs */
uint8_t jb_io_get_sys_inputs(void); uint8_t jb_io_get_sys_inputs(void);
/* Get panel button state. Will return either any button being pressed, or a
particular panel corner depending on previous call to jb_io_set_panel_mode */
uint16_t jb_io_get_panel_inputs(void); uint16_t jb_io_get_panel_inputs(void);
/* Set state of a PWM (dimmable) light */
void jb_io_set_rgb_led( void jb_io_set_rgb_led(
enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b); enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b);
/* Transmit the light state to the IOPCB */
bool jb_io_write_lights(void);
/* Select operating mode for the panel. Should be immediately sent to the IOPCB */
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode);
/* Open or close the coin chute - true will redirect all coins to the return
slot. Required for jb_io_set_panel_mode to operate correctly on p4io.
Should be immediately sent to the IOPCB */
bool jb_io_set_coin_blocker(bool blocked);
#endif #endif

View File

@ -42,9 +42,10 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
log_info("--- Begin jbhook dll_entry_init ---"); log_info("--- Begin jbhook dll_entry_init ---");
if (!options.disable_p4ioemu) { iohook_push_handler(p4ioemu_dispatch_irp);
p4ioemu_init(jbhook_io_init()); iohook_push_handler(ac_io_port_dispatch_irp);
if (!options.disable_p4ioemu) {
log_info("Starting up jubeat IO backend"); log_info("Starting up jubeat IO backend");
jb_io_set_loggers( jb_io_set_loggers(
@ -56,11 +57,12 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
if (!jb_io_ok) { if (!jb_io_ok) {
goto fail; goto fail;
} }
hook_setupapi_init(&p4ioemu_setupapi_data);
p4ioemu_init(jbhook_io_init());
} }
if (!options.disable_cardemu) { if (!options.disable_cardemu) {
ac_io_port_init();
log_info("Starting up card reader backend"); log_info("Starting up card reader backend");
eam_io_set_loggers( eam_io_set_loggers(
@ -72,6 +74,9 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
if (!eam_io_ok) { if (!eam_io_ok) {
goto fail; goto fail;
} }
rs232_hook_init();
ac_io_port_init();
} }
log_info("--- End jbhook dll_entry_init ---"); log_info("--- End jbhook dll_entry_init ---");
@ -128,21 +133,10 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
app_hook_init(my_dll_entry_init, my_dll_entry_main); app_hook_init(my_dll_entry_init, my_dll_entry_main);
iohook_push_handler(p4ioemu_dispatch_irp);
iohook_push_handler(ac_io_port_dispatch_irp);
if (!options.disable_adapteremu) { if (!options.disable_adapteremu) {
adapter_hook_init(); adapter_hook_init();
} }
if (!options.disable_cardemu) {
rs232_hook_init();
}
if (!options.disable_p4ioemu) {
hook_setupapi_init(&p4ioemu_setupapi_data);
}
gfx_hook_init(); gfx_hook_init();
jbhook_eamuse_hook_init(); jbhook_eamuse_hook_init();

View File

@ -8,16 +8,10 @@
#include "jbhook/io.h" #include "jbhook/io.h"
#include "p4io/cmd.h"
#include "util/log.h" #include "util/log.h"
enum jbhook_io_p4io_command {
JUHOOK_IO_P4IO_CMD_OUTPUTS = 0x18,
};
struct jbhook_io_p4io_outputs {
uint32_t outputs;
};
static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes); static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes);
static uint32_t jbhook_command_handle( static uint32_t jbhook_command_handle(
uint8_t cmd, uint8_t cmd,
@ -91,6 +85,7 @@ static const uint32_t jbhook_io_panel_mappings[] = {
static const uint32_t jbhook_io_sys_button_mappings[] = { static const uint32_t jbhook_io_sys_button_mappings[] = {
(1 << 28), (1 << 28),
(1 << 25), (1 << 25),
(1 << 24),
}; };
static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes) static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes)
@ -116,7 +111,7 @@ static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes)
} }
} }
for (uint8_t i = 0; i < 2; i++) { for (uint8_t i = 0; i < 3; i++) {
if (buttons & (1 << i)) { if (buttons & (1 << i)) {
*inputs |= jbhook_io_sys_button_mappings[i]; *inputs |= jbhook_io_sys_button_mappings[i];
} }
@ -131,19 +126,56 @@ static uint32_t jbhook_command_handle(
uint32_t resp_max_len) uint32_t resp_max_len)
{ {
switch (cmd) { switch (cmd) {
case JUHOOK_IO_P4IO_CMD_OUTPUTS: { case P4IO_CMD_COINSTOCK: {
// const struct jbhook_io_p4io_outputs* req = // on is 0x00, off is either 0x10 or 0x20 depending on whether it's
// (const struct jbhook_io_p4io_outputs*) payload; // during gameplay (0x10) or test menu (0x20). Both seem to have the
// same effect
// log_misc("JUHOOK_IO_P4IO_CMD_OUTPUTS: 0x%X", req->outputs); jb_io_set_coin_blocker(*(uint8_t*)payload == 0x00);
/* coin blocker: off 0x20, on 0x00 */
// this actually returns the coinstock, don't care for it
memset(resp, 0, 4); memset(resp, 0, 4);
return 4; return 4;
} }
case P4IO_CMD_SET_PORTOUT: {
const struct p4io_req_panel_mode *req =
(const struct p4io_req_panel_mode *) payload;
// always fallback to ALL if input is unknown
enum jb_io_panel_mode mode = JB_IO_PANEL_MODE_ALL;
if(req->is_single) {
switch(req->mode) {
case 0x0001:
mode = JB_IO_PANEL_MODE_TOP_LEFT;
break;
case 0x0000:
mode = JB_IO_PANEL_MODE_TOP_RIGHT;
break;
case 0x0101:
mode = JB_IO_PANEL_MODE_BOTTOM_LEFT;
break;
case 0x0100:
mode = JB_IO_PANEL_MODE_BOTTOM_RIGHT;
break;
default:
mode = JB_IO_PANEL_MODE_ALL;
break;
}
}
jb_io_set_panel_mode(mode);
memset(resp, 0, 1);
return 1;
}
default: default:
return 0xFFFFFFFF; return 0xFFFFFFFF;
} }

View File

@ -8,4 +8,6 @@ EXPORTS
jb_io_read_inputs jb_io_read_inputs
jb_io_set_loggers jb_io_set_loggers
jb_io_set_rgb_led jb_io_set_rgb_led
jb_io_write_outputs jb_io_set_coin_blocker
jb_io_set_panel_mode
jb_io_write_lights

View File

@ -119,7 +119,7 @@ bool jb_io_read_inputs(void)
return true; return true;
} }
bool jb_io_write_outputs(void) bool jb_io_write_lights(void)
{ {
return true; return true;
} }
@ -139,3 +139,13 @@ void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b)
// I mean I guess there's reactive LEDs on the sides? I'm not going to the // I mean I guess there's reactive LEDs on the sides? I'm not going to the
// effort to work out if they're controllable or not // effort to work out if they're controllable or not
} }
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode)
{
return true;
}
bool jb_io_set_coin_blocker(bool blocked)
{
return true;
}

View File

@ -0,0 +1,16 @@
dlls += jbio-p4io
ldflags_jbio-p4io := \
-lsetupapi
src_jbio-p4io := \
config-h44b.c \
h44b.c \
jbio.c \
libs_jbio-p4io := \
aciodrv \
aciomgr \
cconfig \
p4iodrv \
util \

View File

@ -0,0 +1,55 @@
#include "cconfig/cconfig-util.h"
#include "jbio-p4io/config-h44b.h"
#include "util/log.h"
#define JBIO_CONFIG_H44B_PORT_KEY "h44b.port"
#define JBIO_CONFIG_H44B_BAUD_KEY "h44b.baud"
#define JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE "COM2"
#define JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE 57600
void jbio_config_h44b_init(struct cconfig *config)
{
cconfig_util_set_str(
config,
JBIO_CONFIG_H44B_PORT_KEY,
JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE,
"H44B serial port");
cconfig_util_set_int(
config,
JBIO_CONFIG_H44B_BAUD_KEY,
JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE,
"H44B bus baudrate (real devices expect 57600)");
}
void jbio_config_h44b_get(
struct h44b_config *config_h44b, struct cconfig *config)
{
if (!cconfig_util_get_str(
config,
JBIO_CONFIG_H44B_PORT_KEY,
config_h44b->port,
sizeof(config_h44b->port) - 1,
JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE)) {
log_warning(
"Invalid value for key '%s' specified, fallback "
"to default '%s'",
JBIO_CONFIG_H44B_PORT_KEY,
JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE);
}
if (!cconfig_util_get_int(
config,
JBIO_CONFIG_H44B_BAUD_KEY,
&config_h44b->baud,
JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE)) {
log_warning(
"Invalid value for key '%s' specified, fallback "
"to default '%d'",
JBIO_CONFIG_H44B_BAUD_KEY,
JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE);
}
}

View File

@ -0,0 +1,18 @@
#ifndef JBIO_CONFIG_H44B_H
#define JBIO_CONFIG_H44B_H
#include <windows.h>
#include "cconfig/cconfig.h"
struct h44b_config {
char port[64];
int32_t baud;
};
void jbio_config_h44b_init(struct cconfig *config);
void jbio_config_h44b_get(
struct h44b_config *config_h44b, struct cconfig *config);
#endif

99
src/main/jbio-p4io/h44b.c Normal file
View File

@ -0,0 +1,99 @@
#define LOG_MODULE "jbio-h44b"
#include <stdatomic.h>
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include "aciodrv/h44b.h"
#include "aciomgr/manager.h"
#include "util/log.h"
static int16_t h44b_node_id;
static atomic_bool running;
static struct aciomgr_port_dispatcher *acio_manager_ctx;
bool jb_io_h44b_init(const char *port, int32_t baud) {
acio_manager_ctx = aciomgr_port_init(port, baud);
if (acio_manager_ctx == NULL) {
log_info("Opening acio device on [%s] failed", port);
return false;
}
log_info("Opening acio device successful");
uint8_t node_count = aciomgr_get_node_count(acio_manager_ctx);
log_info("Enumerated %d nodes", node_count);
h44b_node_id = -1;
for (uint8_t i = 0; i < node_count; i++) {
char product[4];
aciomgr_get_node_product_ident(acio_manager_ctx, i, product);
log_info(
"> %d: %c%c%c%c",
i,
product[0],
product[1],
product[2],
product[3]);
if (!memcmp(product, "H44B", 4)) {
if (h44b_node_id != -1) {
log_warning("Multiple H44B found! Using highest node id.");
}
h44b_node_id = i;
}
}
if (h44b_node_id != -1) {
log_warning("Using H44B on node: %d", h44b_node_id);
bool init_result = aciodrv_h44b_init(
aciomgr_port_checkout(acio_manager_ctx),
h44b_node_id);
aciomgr_port_checkin(acio_manager_ctx);
if (!init_result) {
log_warning("Unable to start H44B on node: %d", h44b_node_id);
return false;
}
running = true;
log_warning("jbio-h44b now running");
} else {
log_warning("No H44B device found");
}
return running;
}
bool jb_io_h44b_fini(void) {
return true;
}
bool jb_io_h44b_write_lights(struct ac_io_h44b_output *lights) {
if (!running) {
return false;
}
bool amp_result = aciodrv_h44b_lights(
aciomgr_port_checkout(acio_manager_ctx),
h44b_node_id,
lights);
aciomgr_port_checkin(acio_manager_ctx);
if (!amp_result) {
return false;
}
return true;
}

View File

@ -0,0 +1,7 @@
#include <stdbool.h>
#include "acio/h44b.h"
bool jb_io_h44b_init(const char *port, int32_t baud);
bool jb_io_h44b_fini(void);
bool jb_io_h44b_write_lights(struct ac_io_h44b_output *lights);

View File

@ -0,0 +1,13 @@
LIBRARY jbio
EXPORTS
jb_io_fini
jb_io_get_panel_inputs
jb_io_get_sys_inputs
jb_io_init
jb_io_read_inputs
jb_io_set_loggers
jb_io_set_rgb_led
jb_io_set_panel_mode
jb_io_set_coin_blocker
jb_io_write_lights

206
src/main/jbio-p4io/jbio.c Normal file
View File

@ -0,0 +1,206 @@
// clang-format off
// Don't format because the order is important here
#include <windows.h>
#include <setupapi.h>
#include <stdlib.h>
// clang-format on
#include "aciomgr/manager.h"
#include "cconfig/cconfig-main.h"
#include "bemanitools/jbio.h"
#include "jbio-p4io/config-h44b.h"
#include "jbio-p4io/h44b.h"
#include "p4iodrv/device.h"
#include "util/log.h"
static struct p4iodrv_ctx *p4io_ctx;
static uint16_t jb_io_panels;
static uint8_t jb_io_sys_buttons;
static bool lights_present;
static struct ac_io_h44b_output jb_io_lights;
static struct ac_io_h44b_output jb_io_new_lights;
static bool coin_blocked;
void jb_io_set_loggers(
log_formatter_t misc,
log_formatter_t info,
log_formatter_t warning,
log_formatter_t fatal)
{
aciomgr_set_loggers(misc, info, warning, fatal);
log_to_external(misc, info, warning, fatal);
}
bool jb_io_init(
thread_create_t thread_create,
thread_join_t thread_join,
thread_destroy_t thread_destroy)
{
struct cconfig *config;
struct h44b_config config_h44b;
config = cconfig_init();
jbio_config_h44b_init(config);
if (!cconfig_main_config_init(
config,
"--h44b-config",
"jbio-h44b.conf",
"--help",
"-h",
"jbio-h44b",
CCONFIG_CMD_USAGE_OUT_STDOUT)) {
cconfig_finit(config);
exit(EXIT_FAILURE);
}
jbio_config_h44b_get(&config_h44b, config);
cconfig_finit(config);
p4io_ctx = p4iodrv_open();
if(!p4io_ctx) {
return false;
}
// some people use p4io just for inputs and have no lights. Soft fail when
// h44b is not able to be connected instead of returning false
lights_present = jb_io_h44b_init(config_h44b.port, config_h44b.baud);
if(!lights_present) {
log_warning("Could not connect to H44B, lights disabled");
}
return true;
}
void jb_io_fini(void)
{
p4iodrv_close(p4io_ctx);
}
static const uint32_t jb_io_panel_mappings[] = {
(1 << 5),
(1 << 1),
(1 << 13),
(1 << 9),
(1 << 6),
(1 << 2),
(1 << 14),
(1 << 10),
(1 << 7),
(1 << 3),
(1 << 15),
(1 << 11),
(1 << 16),
(1 << 4),
(1 << 20),
(1 << 12),
};
static const uint32_t jb_io_sys_button_mappings[] = {
(1 << 28),
(1 << 25),
};
bool jb_io_read_inputs(void)
{
uint32_t jamma[4];
if(!p4iodrv_read_jamma(p4io_ctx, jamma)) {
return false;
}
jb_io_panels = 0;
jb_io_sys_buttons = 0;
// panel is active low
uint32_t panel_in = ~jamma[0];
for (uint8_t i = 0; i < 16; i++) {
if (panel_in & jb_io_panel_mappings[i]) {
jb_io_panels |= 1 << i;
}
}
// sys is active high
for (uint8_t i = 0; i < 2; i++) {
if (jamma[0] & jb_io_sys_button_mappings[i]) {
jb_io_sys_buttons |= 1 << i;
}
}
return true;
}
bool jb_io_write_lights(void)
{
if(lights_present && memcmp(&jb_io_lights, &jb_io_new_lights, sizeof(jb_io_lights))) {
memcpy(&jb_io_lights, &jb_io_new_lights, sizeof(jb_io_lights));
return jb_io_h44b_write_lights(&jb_io_lights);
}
return true;
}
uint8_t jb_io_get_sys_inputs(void)
{
return jb_io_sys_buttons;
}
uint16_t jb_io_get_panel_inputs(void)
{
return jb_io_panels;
}
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode)
{
struct p4io_req_panel_mode panel_mode = {0};
panel_mode.is_single = 1;
switch(mode) {
case JB_IO_PANEL_MODE_ALL:
panel_mode.is_single = 0;
panel_mode.mode = 0;
break;
case JB_IO_PANEL_MODE_TOP_LEFT:
panel_mode.mode = 0x0001;
break;
case JB_IO_PANEL_MODE_TOP_RIGHT:
panel_mode.mode = 0x0000;
break;
case JB_IO_PANEL_MODE_BOTTOM_LEFT:
panel_mode.mode = 0x0101;
break;
case JB_IO_PANEL_MODE_BOTTOM_RIGHT:
panel_mode.mode = 0x0100;
break;
}
return p4iodrv_cmd_portout(p4io_ctx, (uint8_t*)&panel_mode);
}
void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b)
{
// enum jb_io_rgb_led matches the ACIO message order
uint8_t *raw_lights = (uint8_t*)&jb_io_new_lights;
raw_lights[unit * 3] = r;
raw_lights[unit * 3 + 1] = g;
raw_lights[unit * 3 + 2] = b;
}
bool jb_io_set_coin_blocker(bool blocked) {
coin_blocked = blocked;
uint8_t coin[4] = {0};
coin[0] = coin_blocked ? 0x00 : 0x20;
return p4iodrv_cmd_coinstock(p4io_ctx, coin);
}

View File

@ -102,7 +102,7 @@ bool jb_io_read_inputs(void)
return true; return true;
} }
bool jb_io_write_outputs(void) bool jb_io_write_lights(void)
{ {
/* The generic input stack currently initiates lighting sends and input /* The generic input stack currently initiates lighting sends and input
reads simultaneously, though this might change later. Perform all of our reads simultaneously, though this might change later. Perform all of our
@ -122,9 +122,20 @@ uint16_t jb_io_get_panel_inputs(void)
return jb_io_panels; return jb_io_panels;
} }
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode)
{
// geninput only uses 1 switch per panel, so ignore alternate modes
return true;
}
bool jb_io_set_coin_blocker(bool blocked)
{
return true;
}
void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b) void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b)
{ {
mapper_write_light(unit * 3, r); mapper_write_light(unit * 3, r);
mapper_write_light(unit * 3 + 1, g); mapper_write_light(unit * 3 + 1, g);
mapper_write_light(unit * 3 + 2, b); mapper_write_light(unit * 3 + 2, b);
} }

View File

@ -7,5 +7,7 @@ EXPORTS
jb_io_init jb_io_init
jb_io_read_inputs jb_io_read_inputs
jb_io_set_loggers jb_io_set_loggers
jb_io_set_panel_mode
jb_io_set_rgb_led jb_io_set_rgb_led
jb_io_write_outputs jb_io_set_coin_blocker
jb_io_write_lights

View File

@ -51,8 +51,13 @@ int main(int argc, char **argv)
rgb_t lights[6] = {0}; rgb_t lights[6] = {0};
bool loop = true; bool loop = true;
uint8_t cnt = 0; uint16_t cnt = 0;
enum jbio_light_mode light_mode = LIGHTS_OFF; enum jbio_light_mode light_mode = LIGHTS_OFF;
enum jb_io_panel_mode panel_mode = JB_IO_PANEL_MODE_ALL;
bool panel_corners_animate = false;
char *all_text;
char top_left, top_right, bottom_left, bottom_right;
while (loop) { while (loop) {
if (!jb_io_read_inputs()) { if (!jb_io_read_inputs()) {
@ -60,6 +65,39 @@ int main(int argc, char **argv)
return -2; return -2;
} }
if(panel_corners_animate && (cnt % 50) == 0) {
panel_mode++;
panel_mode %= 5;
// skip the all state
if(panel_mode == JB_IO_PANEL_MODE_ALL) {
panel_mode++;
}
jb_io_set_panel_mode(panel_mode);
}
all_text = " ";
top_left = top_right = bottom_left = bottom_right = ' ';
switch(panel_mode) {
case JB_IO_PANEL_MODE_ALL:
all_text = "ALL";
top_left = top_right = bottom_left = bottom_right = '*';
break;
case JB_IO_PANEL_MODE_TOP_LEFT:
top_left = '*';
break;
case JB_IO_PANEL_MODE_TOP_RIGHT:
top_right = '*';
break;
case JB_IO_PANEL_MODE_BOTTOM_LEFT:
bottom_left = '*';
break;
case JB_IO_PANEL_MODE_BOTTOM_RIGHT:
bottom_right = '*';
break;
}
/* get inputs */ /* get inputs */
input_sys = jb_io_get_sys_inputs(); input_sys = jb_io_get_sys_inputs();
input_panel = jb_io_get_panel_inputs(); input_panel = jb_io_get_panel_inputs();
@ -85,12 +123,12 @@ int main(int argc, char **argv)
"\n" "\n"
" Test: %d Service: %d Coin: %d\n" " Test: %d Service: %d Coin: %d\n"
" .___.___.___.___.\n" " .___.___.___.___.\n"
" | %d | %d | %d | %d |\n" " | %d | %d | %d | %d | Corner\n"
" |---|---|---|---|\n" " |---|---|---|---| .___.\n"
" | %d | %d | %d | %d |\n" " | %d | %d | %d | %d | |%c %c|\n"
" |---|---|---|---|\n" " |---|---|---|---| |%s|\n"
" | %d | %d | %d | %d |\n" " | %d | %d | %d | %d | |%c %c|\n"
" |---|---|---|---|\n" " |---|---|---|---| `---`\n"
" | %d | %d | %d | %d |\n" " | %d | %d | %d | %d |\n"
" `---`---`---`---`\n", " `---`---`---`---`\n",
cnt, cnt,
@ -126,10 +164,19 @@ int main(int argc, char **argv)
IS_BIT_SET(input_panel, JB_IO_PANEL_06), IS_BIT_SET(input_panel, JB_IO_PANEL_06),
IS_BIT_SET(input_panel, JB_IO_PANEL_07), IS_BIT_SET(input_panel, JB_IO_PANEL_07),
IS_BIT_SET(input_panel, JB_IO_PANEL_08), IS_BIT_SET(input_panel, JB_IO_PANEL_08),
top_left,
top_right,
all_text,
IS_BIT_SET(input_panel, JB_IO_PANEL_09), IS_BIT_SET(input_panel, JB_IO_PANEL_09),
IS_BIT_SET(input_panel, JB_IO_PANEL_10), IS_BIT_SET(input_panel, JB_IO_PANEL_10),
IS_BIT_SET(input_panel, JB_IO_PANEL_11), IS_BIT_SET(input_panel, JB_IO_PANEL_11),
IS_BIT_SET(input_panel, JB_IO_PANEL_12), IS_BIT_SET(input_panel, JB_IO_PANEL_12),
bottom_left,
bottom_right,
IS_BIT_SET(input_panel, JB_IO_PANEL_13), IS_BIT_SET(input_panel, JB_IO_PANEL_13),
IS_BIT_SET(input_panel, JB_IO_PANEL_14), IS_BIT_SET(input_panel, JB_IO_PANEL_14),
IS_BIT_SET(input_panel, JB_IO_PANEL_15), IS_BIT_SET(input_panel, JB_IO_PANEL_15),
@ -165,7 +212,7 @@ int main(int argc, char **argv)
jb_io_set_rgb_led(i, lights[i].r, lights[i].g, lights[i].b); jb_io_set_rgb_led(i, lights[i].r, lights[i].g, lights[i].b);
} }
if (!jb_io_write_outputs()) { if (!jb_io_write_lights()) {
printf("ERROR: Writing outputs failed\n"); printf("ERROR: Writing outputs failed\n");
return -4; return -4;
} }
@ -185,6 +232,7 @@ int main(int argc, char **argv)
" 2: Set all lights on (cleared by pressing any panel)\n" " 2: Set all lights on (cleared by pressing any panel)\n"
" 3: Tie each R/G/B to inputs (panels + test + service)\n" " 3: Tie each R/G/B to inputs (panels + test + service)\n"
" 4: Set all lights off\n" " 4: Set all lights off\n"
" 5: Toggle switch corner test mode\n"
"Waiting for input: "); "Waiting for input: ");
char c = getchar(); char c = getchar();
@ -195,7 +243,7 @@ int main(int argc, char **argv)
jb_io_set_rgb_led(i, 0, 0, 0); jb_io_set_rgb_led(i, 0, 0, 0);
} }
if (!jb_io_write_outputs()) { if (!jb_io_write_lights()) {
printf("ERROR: Writing outputs failed\n"); printf("ERROR: Writing outputs failed\n");
return -4; return -4;
} }
@ -219,6 +267,15 @@ int main(int argc, char **argv)
break; break;
} }
case '5': {
panel_corners_animate = !panel_corners_animate;
if(!panel_corners_animate) {
panel_mode = JB_IO_PANEL_MODE_ALL;
jb_io_set_panel_mode(panel_mode);
}
break;
}
case '0': case '0':
default: default:
break; break;

60
src/main/p4io/cmd.h Normal file
View File

@ -0,0 +1,60 @@
#ifndef P4IO_CMD_H
#define P4IO_CMD_H
#include <stdint.h>
enum p4ioemu_p4io_command {
P4IO_CMD_INIT = 0x00,
P4IO_CMD_GET_DEVICE_INFO = 0x01,
P4IO_CMD_SET_PORTOUT = 0x12,
P4IO_CMD_COINSTOCK = 0x18,
P4IO_CMD_RESET_WTD = 0x1C,
P4IO_CMD_SCI_MNG_OPEN = 0x20,
P4IO_CMD_SCI_UPDATE = 0x21,
/* SCI = serial communication interface */
P4IO_CMD_SCI_MNG_BREAK = 0x24,
/* Read round plug id over one-wire */
P4IO_CMD_DALLAS_READ_ID = 0x40,
/* Read round plug mem over one-wire */
P4IO_CMD_DALLAS_READ_MEM = 0x41
};
#define P4IO_CMD_HEADER_LEN 4
#define P4IO_MAX_PAYLOAD 60
struct p4io_cmd_header {
uint8_t AA;
uint8_t cmd;
uint8_t seq_num;
uint8_t payload_len;
};
struct p4io_cmd_package {
struct p4io_cmd_header header;
uint8_t payload[P4IO_MAX_PAYLOAD];
};
struct p4io_req_read_roundplug {
/* 0 = black, 1 = white */
uint8_t type;
};
struct p4io_req_panel_mode {
uint16_t mode;
uint8_t is_single;
uint8_t padding[13];
} __attribute__((__packed__));
struct p4io_resp_device_info {
uint32_t type;
uint8_t padding;
uint8_t version_major;
uint8_t version_minor;
uint8_t version_revision;
char product_code[4];
char build_date[16];
char build_time[16];
};
#endif

13
src/main/p4io/guid.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef P4IO_GUID_H
#define P4IO_GUID_H
#include <windows.h>
static const GUID p4io_guid = {
0x8B7250A5,
0x4F61,
0x46C9,
{0x84, 0x3A, 0xE6, 0x68, 0x06, 0x47, 0x6A, 0x20}
};
#endif

42
src/main/p4io/ioctl.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef P4IO_IOCTL_H
#define P4IO_IOCTL_H
/* can't seem to #include the requisite DDK headers from usermode code,
so we have to redefine these macros here */
#ifndef CTL_CODE
#define CTL_CODE(DeviceType, Function, Method, Access) \
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#endif
#ifndef METHOD_BUFFERED
#define METHOD_BUFFERED 0
#endif
#ifndef FILE_ANY_ACCESS
#define FILE_ANY_ACCESS 0x00
#endif
#ifndef FILE_DEVICE_UNKNOWN
#define FILE_DEVICE_UNKNOWN 0x22
#endif
#define P4IO_FUNCTION_READ_JAMMA_2 0x801
#define P4IO_FUNCTION_GET_DEVICE_NAME 0x803
// 0x22200Cu sent to bulk handle
#define P4IO_IOCTL_GET_DEVICE_NAME \
CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
P4IO_FUNCTION_GET_DEVICE_NAME, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
// 0x222004 sent to int handle
#define P4IO_IOCTL_READ_JAMMA_2 \
CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
P4IO_FUNCTION_READ_JAMMA_2, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#endif

View File

@ -0,0 +1,6 @@
libs += p4iodrv
src_p4iodrv := \
device.c \
usb.c \

122
src/main/p4iodrv/device.c Normal file
View File

@ -0,0 +1,122 @@
#define LOG_MODULE "p4iodrv-device"
#include <windows.h>
#include "p4io/cmd.h"
#include "p4iodrv/device.h"
#include "p4iodrv/usb.h"
#include "util/log.h"
#include "util/mem.h"
struct p4iodrv_ctx {
HANDLE jamma_handle;
HANDLE bulk_handle;
uint8_t seq_no;
};
static void p4io_cmd_init(struct p4iodrv_ctx *ctx);
static bool p4io_print_version(struct p4iodrv_ctx *ctx);
static bool p4io_transfer(
struct p4iodrv_ctx *ctx,
uint8_t cmd,
const void *req_payload,
size_t req_payload_len,
void *resp_payload,
size_t resp_payload_len
) {
bool ret = p4io_usb_transfer(ctx->bulk_handle, cmd, ctx->seq_no,
req_payload, req_payload_len,
resp_payload, &resp_payload_len);
ctx->seq_no++;
return ret;
}
struct p4iodrv_ctx *p4iodrv_open(void) {
struct p4iodrv_ctx *ctx = xcalloc(sizeof(struct p4iodrv_ctx));
// jamma is read as fast as possible in its own thread, so having them be
// separate files is absolutely essential to avoid the bulk handle being
// starved
ctx->jamma_handle = p4io_usb_open();
ctx->bulk_handle = p4io_usb_open();
ctx->seq_no = 0;
if(ctx->jamma_handle == INVALID_HANDLE_VALUE || ctx->bulk_handle == INVALID_HANDLE_VALUE) {
free(ctx);
return NULL;
}
p4io_cmd_init(ctx);
if(!p4io_print_version(ctx)) {
free(ctx);
return NULL;
}
return ctx;
}
void p4iodrv_close(struct p4iodrv_ctx *ctx) {
p4io_usb_close(ctx->jamma_handle);
p4io_usb_close(ctx->bulk_handle);
free(ctx);
}
bool p4iodrv_read_jamma(struct p4iodrv_ctx *ctx, uint32_t jamma[4]) {
return p4io_usb_read_jamma(ctx->jamma_handle, jamma);
}
// send something you don't expect a response for
static bool p4io_send(struct p4iodrv_ctx *ctx, uint8_t cmd) {
uint8_t dummy[P4IO_MAX_PAYLOAD];
return p4io_transfer(ctx, cmd, NULL, 0, dummy, sizeof(dummy));
}
// real IO does not check the return value, so neither do we
static void p4io_cmd_init(struct p4iodrv_ctx *ctx) {
p4io_send(ctx, P4IO_CMD_INIT);
}
static bool p4io_print_version(struct p4iodrv_ctx *ctx) {
char p4io_name[128];
if(!p4io_usb_read_device_name(ctx->bulk_handle, p4io_name)) {
return false;
}
struct p4io_resp_device_info dev_info;
if(!p4iodrv_cmd_device_info(ctx, &dev_info)) {
log_warning("p4io get_device_info failed");
return false;
}
dev_info.build_date[15] = '\0';
dev_info.build_time[15] = '\0';
log_info("p4io name: %s", p4io_name);
log_info("p4io type: %08X", dev_info.type);
log_info("p4io version: %d.%d.%d", dev_info.version_major, dev_info.version_minor, dev_info.version_revision);
log_info("p4io product: %.4s", dev_info.product_code);
log_info("p4io build date: %.16s", dev_info.build_date);
log_info("p4io build time: %.16s", dev_info.build_time);
return true;
}
bool p4iodrv_cmd_device_info(struct p4iodrv_ctx *ctx, struct p4io_resp_device_info *info) {
return p4io_transfer(ctx, P4IO_CMD_GET_DEVICE_INFO, NULL, 0, info, sizeof(*info));
}
bool p4iodrv_cmd_portout(struct p4iodrv_ctx *ctx, const uint8_t buffer[16]) {
uint8_t dummy[P4IO_MAX_PAYLOAD];
return p4io_transfer(ctx, P4IO_CMD_SET_PORTOUT, buffer, 16, dummy, sizeof(dummy));
}
bool p4iodrv_cmd_coinstock(struct p4iodrv_ctx *ctx, const uint8_t buffer[4]) {
uint8_t dummy[P4IO_MAX_PAYLOAD];
return p4io_transfer(ctx, P4IO_CMD_COINSTOCK, buffer, 4, dummy, sizeof(dummy));
}

18
src/main/p4iodrv/device.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef P4IODRV_DEVICE_H
#define P4IODRV_DEVICE_H
#include <stdbool.h>
#include <stdint.h>
#include "p4io/cmd.h"
struct p4iodrv_ctx;
struct p4iodrv_ctx *p4iodrv_open(void);
void p4iodrv_close(struct p4iodrv_ctx *ctx);
bool p4iodrv_cmd_device_info(struct p4iodrv_ctx *ctx, struct p4io_resp_device_info *info);
bool p4iodrv_cmd_portout(struct p4iodrv_ctx *ctx, const uint8_t buffer[16]);
bool p4iodrv_cmd_coinstock(struct p4iodrv_ctx *ctx, const uint8_t buffer[4]);
bool p4iodrv_read_jamma(struct p4iodrv_ctx *ctx, uint32_t jamma[4]);
#endif

173
src/main/p4iodrv/usb.c Normal file
View File

@ -0,0 +1,173 @@
#define LOG_MODULE "p4iodrv-usb"
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
// clang-format off
// Don't format because the order is important here
#include <windows.h>
#include <setupapi.h>
// clang-format on
#include "p4io/cmd.h"
#include "p4io/guid.h"
#include "p4io/ioctl.h"
#include "p4iodrv/usb.h"
#include "util/log.h"
#include "util/str.h"
HANDLE p4io_usb_open(void) {
HANDLE handle = INVALID_HANDLE_VALUE;
wchar_t p4io_filename[MAX_PATH]; // game uses 1024, but it shouldn't be that long
PSP_DEVICE_INTERFACE_DETAIL_DATA_W detail_data = NULL;
HDEVINFO dev_info_set;
dev_info_set = SetupDiGetClassDevsW(&p4io_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if(dev_info_set == INVALID_HANDLE_VALUE) {
log_warning("SetupDiGetClassDevs fail, is p4io device connected and driver installed?");
return INVALID_HANDLE_VALUE;
}
SP_DEVICE_INTERFACE_DATA interface_data;
interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if(!SetupDiEnumDeviceInterfaces(dev_info_set, NULL, &p4io_guid, 0, &interface_data)) {
log_warning("SetupDiEnumDeviceInterfaces fail");
goto CLEANUP;
}
DWORD required_size;
SetupDiGetDeviceInterfaceDetailW(dev_info_set, &interface_data, NULL, 0, &required_size, NULL);
detail_data = malloc(required_size);
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
if(!SetupDiGetDeviceInterfaceDetailW(dev_info_set, &interface_data, detail_data, required_size, NULL, NULL)) {
log_warning("SetupDiGetDeviceInterfaceDetailW fail");
goto CLEANUP;
}
wstr_cpy(p4io_filename, MAX_PATH, detail_data->DevicePath);
wstr_cat(p4io_filename, MAX_PATH, L"\\p4io");
log_info("p4io found at path %ls", p4io_filename);
handle = CreateFileW(p4io_filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if(handle == INVALID_HANDLE_VALUE) {
log_warning("CreateFileW fail");
goto CLEANUP;
}
CLEANUP:
free(detail_data);
SetupDiDestroyDeviceInfoList(dev_info_set);
return handle;
}
void p4io_usb_close(HANDLE p4io_handle) {
if(p4io_handle != INVALID_HANDLE_VALUE) {
CloseHandle(p4io_handle);
}
p4io_handle = INVALID_HANDLE_VALUE;
}
bool p4io_usb_read_jamma(HANDLE jamma_handle, uint32_t jamma[4]) {
DWORD bytes_returned;
if(!DeviceIoControl(jamma_handle, P4IO_IOCTL_READ_JAMMA_2, NULL, 0, jamma, sizeof(uint32_t[4]), &bytes_returned, NULL)) {
log_warning("jamma read failed");
return false;
}
return true;
}
bool p4io_usb_read_device_name(HANDLE bulk_handle, char name[128]) {
DWORD bytes_returned;
if(!DeviceIoControl(bulk_handle, P4IO_IOCTL_GET_DEVICE_NAME, NULL, 0, name, 128, &bytes_returned, NULL)) {
log_warning("p4io does not support get_name cmd");
return false;
}
return true;
}
bool p4io_usb_transfer(
HANDLE bulk_handle,
uint8_t cmd,
uint8_t seq_no,
const void *req_payload,
size_t req_payload_len,
void *resp_payload,
size_t *resp_payload_len
) {
DWORD bytes_requested;
DWORD bytes_xferred;
struct p4io_cmd_package cmd_buf;
if(bulk_handle == INVALID_HANDLE_VALUE) {
log_warning("p4io not open");
return false;
}
if(req_payload_len > sizeof(cmd_buf.payload)) {
log_warning("request too big");
return false;
}
cmd_buf.header.AA = 0xAA;
cmd_buf.header.cmd = cmd;
cmd_buf.header.seq_num = seq_no;
cmd_buf.header.payload_len = req_payload_len;
memcpy(cmd_buf.payload, req_payload, req_payload_len);
bytes_requested = P4IO_CMD_HEADER_LEN + req_payload_len;
if(!WriteFile(bulk_handle, &cmd_buf, bytes_requested, &bytes_xferred, 0)) {
log_warning("WriteFile failed");
return false;
}
if(bytes_xferred != bytes_requested) {
log_warning("WriteFile didn't finish");
return false;
}
bytes_requested = P4IO_CMD_HEADER_LEN + *resp_payload_len;
// must do this or requests can stall
if(bytes_requested == 64) {
bytes_requested = 65;
}
if(!ReadFile(bulk_handle, &cmd_buf, bytes_requested, &bytes_xferred, 0)) {
log_warning("ReadFile failed");
return false;
}
if(bytes_xferred > 0) {
if(resp_payload_len) {
if(*resp_payload_len < cmd_buf.header.payload_len) {
log_warning("Response buffer too short");
return false;
}
memcpy(resp_payload, cmd_buf.payload, *resp_payload_len);
*resp_payload_len = cmd_buf.header.payload_len;
}
if(cmd_buf.header.AA != 0xAA) {
log_warning("Response bad header");
return false;
}
if(seq_no != cmd_buf.header.seq_num) {
log_warning("seq_num mismatch (ours %d != theirs %d)", seq_no, cmd_buf.header.seq_num);
return false;
}
} else if(resp_payload_len) {
*resp_payload_len = 0;
}
return true;
}

22
src/main/p4iodrv/usb.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef P4IODRV_USB_H
#define P4IODRV_USB_H
#include <stdbool.h>
#include <stddef.h>
#include <windows.h>
HANDLE p4io_usb_open(void);
void p4io_usb_close(HANDLE p4io_handle);
bool p4io_usb_read_jamma(HANDLE jamma_handle, uint32_t jamma[4]);
bool p4io_usb_read_device_name(HANDLE bulk_handle, char name[128]);
bool p4io_usb_transfer(
HANDLE bulk_handle,
uint8_t cmd,
uint8_t seq_no,
const void *req_payload,
size_t req_payload_len,
void *resp_payload,
size_t *resp_payload_len
);
#endif

View File

@ -7,78 +7,14 @@
#include <windows.h> #include <windows.h>
#include "hook/iohook.h" #include "hook/iohook.h"
#include "p4io/cmd.h"
#include "p4io/ioctl.h"
#include "util/hex.h" #include "util/hex.h"
#include "util/log.h" #include "util/log.h"
#include "util/str.h" #include "util/str.h"
//#define P4IOEMU_DEBUG_DUMP //#define P4IOEMU_DEBUG_DUMP
/* can't seem to #include the requisite DDK headers from usermode code,
so we have to redefine these macros here */
#define CTL_CODE(DeviceType, Function, Method, Access) \
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
#define METHOD_BUFFERED 0
#define FILE_ANY_ACCESS 0x00
#define FILE_DEVICE_UNKNOWN 0x22
#define P4IO_FUNCTION_READ_JAMMA_2 0x801
#define P4IO_FUNCTION_GET_DEVICE_NAME 0x803
#define IOCTL_P4IO_GET_DEVICE_NAME \
CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
P4IO_FUNCTION_GET_DEVICE_NAME, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
#define IOCTL_P4IO_READ_JAMMA_2 \
CTL_CODE( \
FILE_DEVICE_UNKNOWN, \
P4IO_FUNCTION_READ_JAMMA_2, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
enum p4ioemu_p4io_command {
P4IOEMU_P4IO_CMD_INIT = 0x00,
P4IOEMU_P4IO_CMD_GET_DEVICE_INFO = 0x01,
P4IOEMU_P4IO_CMD_UNKNOWN = 0x12,
P4IOEMU_P4IO_CMD_RESET_WTD = 0x1C,
P4IOEMU_P4IO_CMD_SCI_MNG_OPEN = 0x20,
P4IOEMU_P4IO_CMD_SCI_UPDATE = 0x21,
/* SCI = serial communication interface */
P4IOEMU_P4IO_CMD_SCI_MNG_BREAK = 0x24,
/* Read round plug id over one-wire */
P4IOEMU_P4IO_CMD_DALLAS_READ_ID = 0x40,
/* Read round plug mem over one-wire */
P4IOEMU_P4IO_CMD_DALLAS_READ_MEM = 0x41
};
struct p4ioemu_p4io_cmd_package {
uint8_t header_AA;
uint8_t cmd;
uint8_t seq_num;
uint8_t payload_len;
};
struct p4ioemu_p4io_device_info_resp {
uint32_t type;
uint8_t padding;
uint8_t version_major;
uint8_t version_minor;
uint8_t version_revision;
char product_code[4];
char build_date[16];
char build_time[16];
};
struct p4ioemu_p4io_read_roundplug_req {
/* 0 = black, 1 = white */
uint8_t type;
};
static const struct p4ioemu_device_msg_hook *p4ioemu_device_msg_hook; static const struct p4ioemu_device_msg_hook *p4ioemu_device_msg_hook;
static HANDLE p4ioemu_p4io_fd; static HANDLE p4ioemu_p4io_fd;
@ -95,7 +31,7 @@ static uint32_t p4ioemu_p4io_command_handle(
uint32_t resp_max_len) uint32_t resp_max_len)
{ {
switch (cmd) { switch (cmd) {
case P4IOEMU_P4IO_CMD_INIT: { case P4IO_CMD_INIT: {
log_misc("P4IOEMU_P4IO_CMD_INIT"); log_misc("P4IOEMU_P4IO_CMD_INIT");
/* no data to send to host */ /* no data to send to host */
@ -104,11 +40,11 @@ static uint32_t p4ioemu_p4io_command_handle(
return 0; return 0;
} }
case P4IOEMU_P4IO_CMD_GET_DEVICE_INFO: { case P4IO_CMD_GET_DEVICE_INFO: {
log_misc("P4IOEMU_P4IO_CMD_GET_DEVICE_INFO"); log_misc("P4IOEMU_P4IO_CMD_GET_DEVICE_INFO");
struct p4ioemu_p4io_device_info_resp *info = struct p4io_resp_device_info *info =
(struct p4ioemu_p4io_device_info_resp *) resp; (struct p4io_resp_device_info *) resp;
info->type = 0x37133713; info->type = 0x37133713;
info->version_major = 5; info->version_major = 5;
@ -118,12 +54,12 @@ static uint32_t p4ioemu_p4io_command_handle(
memcpy(info->build_date, "build_date", 11); memcpy(info->build_date, "build_date", 11);
memcpy(info->build_time, "build_time", 11); memcpy(info->build_time, "build_time", 11);
return sizeof(struct p4ioemu_p4io_device_info_resp); return sizeof(struct p4io_resp_device_info);
} }
case P4IOEMU_P4IO_CMD_DALLAS_READ_ID: { case P4IO_CMD_DALLAS_READ_ID: {
const struct p4ioemu_p4io_read_roundplug_req *req = const struct p4io_req_read_roundplug *req =
(const struct p4ioemu_p4io_read_roundplug_req *) payload; (const struct p4io_req_read_roundplug *) payload;
log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_ID: %d", req->type); log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_ID: %d", req->type);
@ -136,9 +72,9 @@ static uint32_t p4ioemu_p4io_command_handle(
return 8; return 8;
} }
case P4IOEMU_P4IO_CMD_DALLAS_READ_MEM: { case P4IO_CMD_DALLAS_READ_MEM: {
const struct p4ioemu_p4io_read_roundplug_req *req = const struct p4io_req_read_roundplug *req =
(const struct p4ioemu_p4io_read_roundplug_req *) payload; (const struct p4io_req_read_roundplug *) payload;
log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_MEM: %d", req->type); log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_MEM: %d", req->type);
@ -152,13 +88,7 @@ static uint32_t p4ioemu_p4io_command_handle(
return 32; return 32;
} }
case P4IOEMU_P4IO_CMD_UNKNOWN: { case P4IO_CMD_SCI_UPDATE: {
log_misc("P4IOEMU_P4IO_CMD_UNKNOWN: %d", payload_len);
return 0;
}
case P4IOEMU_P4IO_CMD_SCI_UPDATE: {
// log_misc("P4IOEMU_P4IO_CMD_SCI_UPDATE"); // log_misc("P4IOEMU_P4IO_CMD_SCI_UPDATE");
// TODO we need a game which uses it // TODO we need a game which uses it
@ -167,7 +97,7 @@ static uint32_t p4ioemu_p4io_command_handle(
return 0; return 0;
} }
case P4IOEMU_P4IO_CMD_RESET_WTD: { case P4IO_CMD_RESET_WTD: {
// log_misc("P4IOEMU_P4IO_CMD_RESET_WTD"); // log_misc("P4IOEMU_P4IO_CMD_RESET_WTD");
return 0; return 0;
@ -188,20 +118,18 @@ static void p4ioemu_p4io_dump_buffer(const void *buffer, uint32_t len)
static HRESULT p4ioemu_p4io_bulk_read(void *resp, uint32_t nbytes) static HRESULT p4ioemu_p4io_bulk_read(void *resp, uint32_t nbytes)
{ {
struct p4ioemu_p4io_cmd_package *package; struct p4io_cmd_package *package;
void *payload;
uint32_t max_payload_len; uint32_t max_payload_len;
uint32_t payload_len; uint32_t payload_len;
if (nbytes < sizeof(struct p4ioemu_p4io_cmd_package)) { if (nbytes < sizeof(struct p4io_cmd_header)) {
log_warning("Buffer for bulk read endpoint to short: %d", nbytes); log_warning("Buffer for bulk read endpoint too short: %d", nbytes);
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
} }
package = (struct p4ioemu_p4io_cmd_package *) resp; package = (struct p4io_cmd_package *) resp;
payload = resp + sizeof(struct p4ioemu_p4io_cmd_package); max_payload_len = nbytes - sizeof(struct p4io_cmd_header);
max_payload_len = nbytes - sizeof(struct p4ioemu_p4io_cmd_package);
if (max_payload_len < p4ioemu_p4io_cmd_buffer_resp_len) { if (max_payload_len < p4ioemu_p4io_cmd_buffer_resp_len) {
log_warning( log_warning(
@ -213,46 +141,44 @@ static HRESULT p4ioemu_p4io_bulk_read(void *resp, uint32_t nbytes)
payload_len = p4ioemu_p4io_cmd_buffer_resp_len; payload_len = p4ioemu_p4io_cmd_buffer_resp_len;
} }
package->header_AA = 0xAA; package->header.AA = 0xAA;
package->cmd = p4ioemu_p4io_last_cmd; package->header.cmd = p4ioemu_p4io_last_cmd;
package->seq_num = p4ioemu_p4io_last_seq_num; package->header.seq_num = p4ioemu_p4io_last_seq_num;
package->payload_len = payload_len; package->header.payload_len = payload_len;
memcpy(payload, p4ioemu_p4io_cmd_read_buffer, payload_len); memcpy(package->payload, p4ioemu_p4io_cmd_read_buffer, payload_len);
return S_OK; return S_OK;
} }
static HRESULT p4ioemu_p4io_bulk_write(const void *req, uint32_t nbytes) static HRESULT p4ioemu_p4io_bulk_write(const void *req, uint32_t nbytes)
{ {
const struct p4ioemu_p4io_cmd_package *package; const struct p4io_cmd_package *package;
const void *payload;
if (nbytes < sizeof(struct p4ioemu_p4io_cmd_package)) { if (nbytes < sizeof(struct p4io_cmd_header)) {
log_warning("Command on bulk write endpoint to short: %d", nbytes); log_warning("Command on bulk write endpoint too short: %d", nbytes);
p4ioemu_p4io_dump_buffer(req, nbytes); p4ioemu_p4io_dump_buffer(req, nbytes);
return E_INVALIDARG; return E_INVALIDARG;
} }
package = (struct p4ioemu_p4io_cmd_package *) req; package = (struct p4io_cmd_package *) req;
payload = req + sizeof(struct p4ioemu_p4io_cmd_package);
if (package->header_AA != 0xAA) { if (package->header.AA != 0xAA) {
log_warning("Command on bulk endpoint to short: %d", nbytes); log_warning("Command on bulk endpoint too short: %d", nbytes);
p4ioemu_p4io_dump_buffer(req, nbytes); p4ioemu_p4io_dump_buffer(req, nbytes);
return E_INVALIDARG; return E_INVALIDARG;
} }
p4ioemu_p4io_last_cmd = package->cmd; p4ioemu_p4io_last_cmd = package->header.cmd;
p4ioemu_p4io_last_seq_num = package->seq_num; p4ioemu_p4io_last_seq_num = package->header.seq_num;
/* handle commands that are common p4io ones first */ /* handle commands that are common p4io ones first */
p4ioemu_p4io_cmd_buffer_resp_len = p4ioemu_p4io_command_handle( p4ioemu_p4io_cmd_buffer_resp_len = p4ioemu_p4io_command_handle(
package->cmd, package->header.cmd,
payload, package->payload,
package->payload_len, package->header.payload_len,
p4ioemu_p4io_cmd_read_buffer, p4ioemu_p4io_cmd_read_buffer,
sizeof(p4ioemu_p4io_cmd_read_buffer)); sizeof(p4ioemu_p4io_cmd_read_buffer));
@ -261,16 +187,16 @@ static HRESULT p4ioemu_p4io_bulk_write(const void *req, uint32_t nbytes)
if (p4ioemu_device_msg_hook->command_handle) { if (p4ioemu_device_msg_hook->command_handle) {
p4ioemu_p4io_cmd_buffer_resp_len = p4ioemu_p4io_cmd_buffer_resp_len =
p4ioemu_device_msg_hook->command_handle( p4ioemu_device_msg_hook->command_handle(
package->cmd, package->header.cmd,
payload, package->payload,
package->payload_len, package->header.payload_len,
p4ioemu_p4io_cmd_read_buffer, p4ioemu_p4io_cmd_read_buffer,
sizeof(p4ioemu_p4io_cmd_read_buffer)); sizeof(p4ioemu_p4io_cmd_read_buffer));
} }
} }
if (p4ioemu_p4io_cmd_buffer_resp_len == 0xFFFFFFFF) { if (p4ioemu_p4io_cmd_buffer_resp_len == 0xFFFFFFFF) {
log_warning("Unhandled cmd 0x%X", package->cmd); log_warning("Unhandled cmd 0x%X", package->header.cmd);
return E_NOTIMPL; return E_NOTIMPL;
} }
@ -413,7 +339,7 @@ static HRESULT p4ioemu_device_ioctl(struct irp *irp)
/* Cases are listed in order of first receipt */ /* Cases are listed in order of first receipt */
switch (irp->ioctl) { switch (irp->ioctl) {
case IOCTL_P4IO_GET_DEVICE_NAME: { case P4IO_IOCTL_GET_DEVICE_NAME: {
const char dev_name[] = "kactools p4ioemu"; const char dev_name[] = "kactools p4ioemu";
if (irp->read.nbytes < strlen(dev_name)) { if (irp->read.nbytes < strlen(dev_name)) {
@ -427,7 +353,7 @@ static HRESULT p4ioemu_device_ioctl(struct irp *irp)
return S_OK; return S_OK;
} }
case IOCTL_P4IO_READ_JAMMA_2: { case P4IO_IOCTL_READ_JAMMA_2: {
p4ioemu_device_msg_hook->jamma2_read( p4ioemu_device_msg_hook->jamma2_read(
irp->read.bytes, irp->read.nbytes); irp->read.bytes, irp->read.nbytes);
irp->read.pos = irp->read.nbytes; irp->read.pos = irp->read.nbytes;

View File

@ -2,11 +2,10 @@
#include "p4ioemu/setupapi.h" #include "p4ioemu/setupapi.h"
#include "p4io/guid.h"
const struct hook_setupapi_data p4ioemu_setupapi_data = { const struct hook_setupapi_data p4ioemu_setupapi_data = {
.device_guid = {0x8B7250A5, .device_guid = p4io_guid,
0x4F61,
0x46C9,
{0x84, 0x3A, 0xE6, 0x68, 0x06, 0x47, 0x6A, 0x20}},
.device_desc = NULL, .device_desc = NULL,
.device_path = "\\p4io", .device_path = "\\p4io",
}; };