From 2a03690772f00bf2363c4c341430c5fd63c19c96 Mon Sep 17 00:00:00 2001 From: Will Toohey Date: Sun, 4 Apr 2021 16:41:45 +1000 Subject: [PATCH] Update jbio for testmenu support and add jbio-p4io --- Module.mk | 12 ++ doc/jbhook/jbio-p4io.md | 15 ++ src/main/acio/acio.h | 3 + src/main/acio/h44b.h | 4 +- src/main/aciodrv/Module.mk | 3 +- src/main/aciodrv/h44b.c | 42 +++++ src/main/aciodrv/h44b.h | 32 ++++ src/main/acioemu/h44b.c | 2 +- src/main/acioemu/iccb.c | 4 + src/main/bemanitools/jbio.h | 34 +++- src/main/jbhook/dllmain.c | 24 +-- src/main/jbhook/io.c | 64 +++++-- src/main/jbio-magicbox/jbio-magicbox.def | 4 +- src/main/jbio-magicbox/jbio.c | 12 +- src/main/jbio-p4io/Module.mk | 16 ++ src/main/jbio-p4io/config-h44b.c | 55 ++++++ src/main/jbio-p4io/config-h44b.h | 18 ++ src/main/jbio-p4io/h44b.c | 99 +++++++++++ src/main/jbio-p4io/h44b.h | 7 + src/main/jbio-p4io/jbio-p4io.def | 13 ++ src/main/jbio-p4io/jbio.c | 206 +++++++++++++++++++++++ src/main/jbio/jbio.c | 15 +- src/main/jbio/jbio.def | 4 +- src/main/jbiotest/main.c | 75 ++++++++- src/main/p4io/cmd.h | 60 +++++++ src/main/p4io/guid.h | 13 ++ src/main/p4io/ioctl.h | 42 +++++ src/main/p4iodrv/Module.mk | 6 + src/main/p4iodrv/device.c | 122 ++++++++++++++ src/main/p4iodrv/device.h | 18 ++ src/main/p4iodrv/usb.c | 173 +++++++++++++++++++ src/main/p4iodrv/usb.h | 22 +++ src/main/p4ioemu/device.c | 158 +++++------------ src/main/p4ioemu/setupapi.c | 9 +- 34 files changed, 1214 insertions(+), 172 deletions(-) create mode 100644 doc/jbhook/jbio-p4io.md create mode 100644 src/main/aciodrv/h44b.c create mode 100644 src/main/aciodrv/h44b.h create mode 100644 src/main/jbio-p4io/Module.mk create mode 100644 src/main/jbio-p4io/config-h44b.c create mode 100644 src/main/jbio-p4io/config-h44b.h create mode 100644 src/main/jbio-p4io/h44b.c create mode 100644 src/main/jbio-p4io/h44b.h create mode 100644 src/main/jbio-p4io/jbio-p4io.def create mode 100644 src/main/jbio-p4io/jbio.c create mode 100644 src/main/p4io/cmd.h create mode 100644 src/main/p4io/guid.h create mode 100644 src/main/p4io/ioctl.h create mode 100644 src/main/p4iodrv/Module.mk create mode 100644 src/main/p4iodrv/device.c create mode 100644 src/main/p4iodrv/device.h create mode 100644 src/main/p4iodrv/usb.c create mode 100644 src/main/p4iodrv/usb.h diff --git a/Module.mk b/Module.mk index 4522190..b8a1dd4 100644 --- a/Module.mk +++ b/Module.mk @@ -137,6 +137,7 @@ include src/main/inject/Module.mk include src/main/jbhook/Module.mk include src/main/jbhook1/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/jbiotest/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/p3io/Module.mk include src/main/p3ioemu/Module.mk +include src/main/p4iodrv/Module.mk include src/main/p4ioemu/Module.mk include src/main/pcbidgen/Module.mk include src/main/sdvxhook/Module.mk @@ -444,6 +446,15 @@ $(zipdir)/jb-08.zip: \ $(V)echo ... $@ $(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: \ build/bin/avs2_1508-32/launcher.exe \ build/bin/avs2_1508-32/sdvxhook.dll \ @@ -609,6 +620,7 @@ $(BUILDDIR)/bemanitools.zip: \ $(zipdir)/jb-01.zip \ $(zipdir)/jb-05-to-07.zip \ $(zipdir)/jb-08.zip \ + $(zipdir)/jb-hwio.zip \ $(zipdir)/sdvx-01-to-04.zip \ $(zipdir)/sdvx-05.zip \ $(zipdir)/sdvx-05-cn.zip \ diff --git a/doc/jbhook/jbio-p4io.md b/doc/jbhook/jbio-p4io.md new file mode 100644 index 0000000..6380306 --- /dev/null +++ b/doc/jbhook/jbio-p4io.md @@ -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 diff --git a/src/main/acio/acio.h b/src/main/acio/acio.h index b8608ef..6a3d395 100644 --- a/src/main/acio/acio.h +++ b/src/main/acio/acio.h @@ -5,6 +5,7 @@ #include #include +#include "acio/h44b.h" #include "acio/hdxs.h" #include "acio/icca.h" #include "acio/kfca.h" @@ -69,6 +70,8 @@ struct ac_io_message { uint8_t param; struct ac_io_version version; + struct ac_io_h44b_output h44b_output; + struct ac_io_icca_misc icca_misc; struct ac_io_icca_state icca_state; diff --git a/src/main/acio/h44b.h b/src/main/acio/h44b.h index 0135944..2960786 100644 --- a/src/main/acio/h44b.h +++ b/src/main/acio/h44b.h @@ -14,6 +14,6 @@ struct ac_io_h44b_output { uint8_t right_rgb[3]; uint8_t title_rgb[3]; uint8_t woofer_rgb[3]; -}; +} __attribute__((__packed__)); -#endif \ No newline at end of file +#endif diff --git a/src/main/aciodrv/Module.mk b/src/main/aciodrv/Module.mk index bb30a47..970e199 100644 --- a/src/main/aciodrv/Module.mk +++ b/src/main/aciodrv/Module.mk @@ -1,9 +1,10 @@ libs += aciodrv libs_aciodrv := \ - + src_aciodrv := \ device.c \ + h44b.c \ icca.c \ kfca.c \ port.c \ diff --git a/src/main/aciodrv/h44b.c b/src/main/aciodrv/h44b.c new file mode 100644 index 0000000..5d68bbf --- /dev/null +++ b/src/main/aciodrv/h44b.c @@ -0,0 +1,42 @@ +#define LOG_MODULE "aciodrv-h44b" + +#include +#include + +#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; +} diff --git a/src/main/aciodrv/h44b.h b/src/main/aciodrv/h44b.h new file mode 100644 index 0000000..33879bd --- /dev/null +++ b/src/main/aciodrv/h44b.h @@ -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 diff --git a/src/main/acioemu/h44b.c b/src/main/acioemu/h44b.c index 3e3240d..dde446e 100644 --- a/src/main/acioemu/h44b.c +++ b/src/main/acioemu/h44b.c @@ -61,7 +61,7 @@ void ac_io_emu_h44b_dispatch_request( req->cmd.raw[i * 3 + 2]); } - jb_io_write_outputs(); + jb_io_write_lights(); ac_io_emu_h44b_send_status(h44b, req, 0x00); diff --git a/src/main/acioemu/iccb.c b/src/main/acioemu/iccb.c index 811a110..d4cfb33 100644 --- a/src/main/acioemu/iccb.c +++ b/src/main/acioemu/iccb.c @@ -169,6 +169,10 @@ static void ac_io_emu_iccb_send_state( /* state update */ + if (!eam_io_poll(iccb->unit_no)) { + log_warning("Polling eamio failed"); + } + sensor = eam_io_get_sensor_state(iccb->unit_no); if (sensor != iccb->last_sensor) { diff --git a/src/main/bemanitools/jbio.h b/src/main/bemanitools/jbio.h index 806d70e..d6b7f5a 100644 --- a/src/main/bemanitools/jbio.h +++ b/src/main/bemanitools/jbio.h @@ -29,6 +29,17 @@ enum jb_io_panel_bit { 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 */ enum jb_io_sys_bit { JB_IO_SYS_TEST = 0x00, @@ -73,17 +84,36 @@ bool jb_io_init( void jb_io_fini(void); -/* TODO doc */ +/* Read input state */ 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); +/* 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); +/* Set state of a PWM (dimmable) light */ + void jb_io_set_rgb_led( 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 diff --git a/src/main/jbhook/dllmain.c b/src/main/jbhook/dllmain.c index 2caa4ea..0136bf2 100644 --- a/src/main/jbhook/dllmain.c +++ b/src/main/jbhook/dllmain.c @@ -42,9 +42,10 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) log_info("--- Begin jbhook dll_entry_init ---"); - if (!options.disable_p4ioemu) { - p4ioemu_init(jbhook_io_init()); + iohook_push_handler(p4ioemu_dispatch_irp); + iohook_push_handler(ac_io_port_dispatch_irp); + if (!options.disable_p4ioemu) { log_info("Starting up jubeat IO backend"); jb_io_set_loggers( @@ -56,11 +57,12 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) if (!jb_io_ok) { goto fail; } + + hook_setupapi_init(&p4ioemu_setupapi_data); + p4ioemu_init(jbhook_io_init()); } if (!options.disable_cardemu) { - ac_io_port_init(); - log_info("Starting up card reader backend"); eam_io_set_loggers( @@ -72,6 +74,9 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param) if (!eam_io_ok) { goto fail; } + + rs232_hook_init(); + ac_io_port_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); - iohook_push_handler(p4ioemu_dispatch_irp); - iohook_push_handler(ac_io_port_dispatch_irp); - if (!options.disable_adapteremu) { adapter_hook_init(); } - if (!options.disable_cardemu) { - rs232_hook_init(); - } - - if (!options.disable_p4ioemu) { - hook_setupapi_init(&p4ioemu_setupapi_data); - } - gfx_hook_init(); jbhook_eamuse_hook_init(); diff --git a/src/main/jbhook/io.c b/src/main/jbhook/io.c index 65a4b36..dafdbe9 100644 --- a/src/main/jbhook/io.c +++ b/src/main/jbhook/io.c @@ -8,16 +8,10 @@ #include "jbhook/io.h" +#include "p4io/cmd.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 uint32_t jbhook_command_handle( uint8_t cmd, @@ -91,6 +85,7 @@ static const uint32_t jbhook_io_panel_mappings[] = { static const uint32_t jbhook_io_sys_button_mappings[] = { (1 << 28), (1 << 25), + (1 << 24), }; 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)) { *inputs |= jbhook_io_sys_button_mappings[i]; } @@ -131,19 +126,56 @@ static uint32_t jbhook_command_handle( uint32_t resp_max_len) { switch (cmd) { - case JUHOOK_IO_P4IO_CMD_OUTPUTS: { - // const struct jbhook_io_p4io_outputs* req = - // (const struct jbhook_io_p4io_outputs*) payload; - - // log_misc("JUHOOK_IO_P4IO_CMD_OUTPUTS: 0x%X", req->outputs); - - /* coin blocker: off 0x20, on 0x00 */ + case P4IO_CMD_COINSTOCK: { + // on is 0x00, off is either 0x10 or 0x20 depending on whether it's + // during gameplay (0x10) or test menu (0x20). Both seem to have the + // same effect + jb_io_set_coin_blocker(*(uint8_t*)payload == 0x00); + // this actually returns the coinstock, don't care for it memset(resp, 0, 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: return 0xFFFFFFFF; } diff --git a/src/main/jbio-magicbox/jbio-magicbox.def b/src/main/jbio-magicbox/jbio-magicbox.def index a23c2a1..4920f40 100644 --- a/src/main/jbio-magicbox/jbio-magicbox.def +++ b/src/main/jbio-magicbox/jbio-magicbox.def @@ -8,4 +8,6 @@ EXPORTS jb_io_read_inputs jb_io_set_loggers jb_io_set_rgb_led - jb_io_write_outputs + jb_io_set_coin_blocker + jb_io_set_panel_mode + jb_io_write_lights diff --git a/src/main/jbio-magicbox/jbio.c b/src/main/jbio-magicbox/jbio.c index c156120..f18e28c 100644 --- a/src/main/jbio-magicbox/jbio.c +++ b/src/main/jbio-magicbox/jbio.c @@ -119,7 +119,7 @@ bool jb_io_read_inputs(void) return true; } -bool jb_io_write_outputs(void) +bool jb_io_write_lights(void) { 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 // 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; +} diff --git a/src/main/jbio-p4io/Module.mk b/src/main/jbio-p4io/Module.mk new file mode 100644 index 0000000..b31604d --- /dev/null +++ b/src/main/jbio-p4io/Module.mk @@ -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 \ diff --git a/src/main/jbio-p4io/config-h44b.c b/src/main/jbio-p4io/config-h44b.c new file mode 100644 index 0000000..91d37cb --- /dev/null +++ b/src/main/jbio-p4io/config-h44b.c @@ -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); + } +} diff --git a/src/main/jbio-p4io/config-h44b.h b/src/main/jbio-p4io/config-h44b.h new file mode 100644 index 0000000..cdc33f9 --- /dev/null +++ b/src/main/jbio-p4io/config-h44b.h @@ -0,0 +1,18 @@ +#ifndef JBIO_CONFIG_H44B_H +#define JBIO_CONFIG_H44B_H + +#include + +#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 diff --git a/src/main/jbio-p4io/h44b.c b/src/main/jbio-p4io/h44b.c new file mode 100644 index 0000000..2f54e41 --- /dev/null +++ b/src/main/jbio-p4io/h44b.c @@ -0,0 +1,99 @@ +#define LOG_MODULE "jbio-h44b" + +#include +#include +#include +#include +#include + +#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; +} + diff --git a/src/main/jbio-p4io/h44b.h b/src/main/jbio-p4io/h44b.h new file mode 100644 index 0000000..0f83ae8 --- /dev/null +++ b/src/main/jbio-p4io/h44b.h @@ -0,0 +1,7 @@ +#include + +#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); diff --git a/src/main/jbio-p4io/jbio-p4io.def b/src/main/jbio-p4io/jbio-p4io.def new file mode 100644 index 0000000..e923ef7 --- /dev/null +++ b/src/main/jbio-p4io/jbio-p4io.def @@ -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 diff --git a/src/main/jbio-p4io/jbio.c b/src/main/jbio-p4io/jbio.c new file mode 100644 index 0000000..e3c42c1 --- /dev/null +++ b/src/main/jbio-p4io/jbio.c @@ -0,0 +1,206 @@ +// clang-format off +// Don't format because the order is important here +#include +#include +#include +// 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); +} diff --git a/src/main/jbio/jbio.c b/src/main/jbio/jbio.c index 1069106..26cb4bd 100644 --- a/src/main/jbio/jbio.c +++ b/src/main/jbio/jbio.c @@ -102,7 +102,7 @@ bool jb_io_read_inputs(void) return true; } -bool jb_io_write_outputs(void) +bool jb_io_write_lights(void) { /* The generic input stack currently initiates lighting sends and input 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; } +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) { mapper_write_light(unit * 3, r); mapper_write_light(unit * 3 + 1, g); mapper_write_light(unit * 3 + 2, b); -} \ No newline at end of file +} diff --git a/src/main/jbio/jbio.def b/src/main/jbio/jbio.def index 4b8f183..aecc228 100644 --- a/src/main/jbio/jbio.def +++ b/src/main/jbio/jbio.def @@ -7,5 +7,7 @@ EXPORTS jb_io_init jb_io_read_inputs jb_io_set_loggers + jb_io_set_panel_mode jb_io_set_rgb_led - jb_io_write_outputs \ No newline at end of file + jb_io_set_coin_blocker + jb_io_write_lights diff --git a/src/main/jbiotest/main.c b/src/main/jbiotest/main.c index 8252c47..0a24151 100644 --- a/src/main/jbiotest/main.c +++ b/src/main/jbiotest/main.c @@ -51,8 +51,13 @@ int main(int argc, char **argv) rgb_t lights[6] = {0}; bool loop = true; - uint8_t cnt = 0; + uint16_t cnt = 0; 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) { if (!jb_io_read_inputs()) { @@ -60,6 +65,39 @@ int main(int argc, char **argv) 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 */ input_sys = jb_io_get_sys_inputs(); input_panel = jb_io_get_panel_inputs(); @@ -85,12 +123,12 @@ int main(int argc, char **argv) "\n" " Test: %d Service: %d Coin: %d\n" " .___.___.___.___.\n" - " | %d | %d | %d | %d |\n" - " |---|---|---|---|\n" - " | %d | %d | %d | %d |\n" - " |---|---|---|---|\n" - " | %d | %d | %d | %d |\n" - " |---|---|---|---|\n" + " | %d | %d | %d | %d | Corner\n" + " |---|---|---|---| .___.\n" + " | %d | %d | %d | %d | |%c %c|\n" + " |---|---|---|---| |%s|\n" + " | %d | %d | %d | %d | |%c %c|\n" + " |---|---|---|---| `---`\n" " | %d | %d | %d | %d |\n" " `---`---`---`---`\n", 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_07), 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_10), IS_BIT_SET(input_panel, JB_IO_PANEL_11), 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_14), 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); } - if (!jb_io_write_outputs()) { + if (!jb_io_write_lights()) { printf("ERROR: Writing outputs failed\n"); return -4; } @@ -185,6 +232,7 @@ int main(int argc, char **argv) " 2: Set all lights on (cleared by pressing any panel)\n" " 3: Tie each R/G/B to inputs (panels + test + service)\n" " 4: Set all lights off\n" + " 5: Toggle switch corner test mode\n" "Waiting for input: "); char c = getchar(); @@ -195,7 +243,7 @@ int main(int argc, char **argv) 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"); return -4; } @@ -219,6 +267,15 @@ int main(int argc, char **argv) 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': default: break; diff --git a/src/main/p4io/cmd.h b/src/main/p4io/cmd.h new file mode 100644 index 0000000..b5c354a --- /dev/null +++ b/src/main/p4io/cmd.h @@ -0,0 +1,60 @@ +#ifndef P4IO_CMD_H +#define P4IO_CMD_H + +#include + +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 diff --git a/src/main/p4io/guid.h b/src/main/p4io/guid.h new file mode 100644 index 0000000..abe19dd --- /dev/null +++ b/src/main/p4io/guid.h @@ -0,0 +1,13 @@ +#ifndef P4IO_GUID_H +#define P4IO_GUID_H + +#include + +static const GUID p4io_guid = { + 0x8B7250A5, + 0x4F61, + 0x46C9, + {0x84, 0x3A, 0xE6, 0x68, 0x06, 0x47, 0x6A, 0x20} +}; + +#endif diff --git a/src/main/p4io/ioctl.h b/src/main/p4io/ioctl.h new file mode 100644 index 0000000..76ff3d6 --- /dev/null +++ b/src/main/p4io/ioctl.h @@ -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 diff --git a/src/main/p4iodrv/Module.mk b/src/main/p4iodrv/Module.mk new file mode 100644 index 0000000..de744c6 --- /dev/null +++ b/src/main/p4iodrv/Module.mk @@ -0,0 +1,6 @@ +libs += p4iodrv + +src_p4iodrv := \ + device.c \ + usb.c \ + diff --git a/src/main/p4iodrv/device.c b/src/main/p4iodrv/device.c new file mode 100644 index 0000000..cdf7737 --- /dev/null +++ b/src/main/p4iodrv/device.c @@ -0,0 +1,122 @@ +#define LOG_MODULE "p4iodrv-device" + +#include + +#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)); +} diff --git a/src/main/p4iodrv/device.h b/src/main/p4iodrv/device.h new file mode 100644 index 0000000..7b688f7 --- /dev/null +++ b/src/main/p4iodrv/device.h @@ -0,0 +1,18 @@ +#ifndef P4IODRV_DEVICE_H +#define P4IODRV_DEVICE_H + +#include +#include + +#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 diff --git a/src/main/p4iodrv/usb.c b/src/main/p4iodrv/usb.c new file mode 100644 index 0000000..f7d2af2 --- /dev/null +++ b/src/main/p4iodrv/usb.c @@ -0,0 +1,173 @@ +#define LOG_MODULE "p4iodrv-usb" + +#include +#include +#include + +// clang-format off +// Don't format because the order is important here +#include +#include +// 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; +} diff --git a/src/main/p4iodrv/usb.h b/src/main/p4iodrv/usb.h new file mode 100644 index 0000000..fe42892 --- /dev/null +++ b/src/main/p4iodrv/usb.h @@ -0,0 +1,22 @@ +#ifndef P4IODRV_USB_H +#define P4IODRV_USB_H + +#include +#include +#include + +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 diff --git a/src/main/p4ioemu/device.c b/src/main/p4ioemu/device.c index b514f30..a3684ed 100644 --- a/src/main/p4ioemu/device.c +++ b/src/main/p4ioemu/device.c @@ -7,78 +7,14 @@ #include #include "hook/iohook.h" +#include "p4io/cmd.h" +#include "p4io/ioctl.h" #include "util/hex.h" #include "util/log.h" #include "util/str.h" //#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 HANDLE p4ioemu_p4io_fd; @@ -95,7 +31,7 @@ static uint32_t p4ioemu_p4io_command_handle( uint32_t resp_max_len) { switch (cmd) { - case P4IOEMU_P4IO_CMD_INIT: { + case P4IO_CMD_INIT: { log_misc("P4IOEMU_P4IO_CMD_INIT"); /* no data to send to host */ @@ -104,11 +40,11 @@ static uint32_t p4ioemu_p4io_command_handle( return 0; } - case P4IOEMU_P4IO_CMD_GET_DEVICE_INFO: { + case P4IO_CMD_GET_DEVICE_INFO: { log_misc("P4IOEMU_P4IO_CMD_GET_DEVICE_INFO"); - struct p4ioemu_p4io_device_info_resp *info = - (struct p4ioemu_p4io_device_info_resp *) resp; + struct p4io_resp_device_info *info = + (struct p4io_resp_device_info *) resp; info->type = 0x37133713; 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_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: { - const struct p4ioemu_p4io_read_roundplug_req *req = - (const struct p4ioemu_p4io_read_roundplug_req *) payload; + case P4IO_CMD_DALLAS_READ_ID: { + const struct p4io_req_read_roundplug *req = + (const struct p4io_req_read_roundplug *) payload; log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_ID: %d", req->type); @@ -136,9 +72,9 @@ static uint32_t p4ioemu_p4io_command_handle( return 8; } - case P4IOEMU_P4IO_CMD_DALLAS_READ_MEM: { - const struct p4ioemu_p4io_read_roundplug_req *req = - (const struct p4ioemu_p4io_read_roundplug_req *) payload; + case P4IO_CMD_DALLAS_READ_MEM: { + const struct p4io_req_read_roundplug *req = + (const struct p4io_req_read_roundplug *) payload; log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_MEM: %d", req->type); @@ -152,13 +88,7 @@ static uint32_t p4ioemu_p4io_command_handle( return 32; } - case P4IOEMU_P4IO_CMD_UNKNOWN: { - log_misc("P4IOEMU_P4IO_CMD_UNKNOWN: %d", payload_len); - - return 0; - } - - case P4IOEMU_P4IO_CMD_SCI_UPDATE: { + case P4IO_CMD_SCI_UPDATE: { // log_misc("P4IOEMU_P4IO_CMD_SCI_UPDATE"); // TODO we need a game which uses it @@ -167,7 +97,7 @@ static uint32_t p4ioemu_p4io_command_handle( return 0; } - case P4IOEMU_P4IO_CMD_RESET_WTD: { + case P4IO_CMD_RESET_WTD: { // log_misc("P4IOEMU_P4IO_CMD_RESET_WTD"); 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) { - struct p4ioemu_p4io_cmd_package *package; - void *payload; + struct p4io_cmd_package *package; uint32_t max_payload_len; uint32_t payload_len; - if (nbytes < sizeof(struct p4ioemu_p4io_cmd_package)) { - log_warning("Buffer for bulk read endpoint to short: %d", nbytes); + if (nbytes < sizeof(struct p4io_cmd_header)) { + log_warning("Buffer for bulk read endpoint too short: %d", nbytes); return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } - package = (struct p4ioemu_p4io_cmd_package *) resp; - payload = resp + sizeof(struct p4ioemu_p4io_cmd_package); - max_payload_len = nbytes - sizeof(struct p4ioemu_p4io_cmd_package); + package = (struct p4io_cmd_package *) resp; + max_payload_len = nbytes - sizeof(struct p4io_cmd_header); if (max_payload_len < p4ioemu_p4io_cmd_buffer_resp_len) { 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; } - package->header_AA = 0xAA; - package->cmd = p4ioemu_p4io_last_cmd; - package->seq_num = p4ioemu_p4io_last_seq_num; - package->payload_len = payload_len; + package->header.AA = 0xAA; + package->header.cmd = p4ioemu_p4io_last_cmd; + package->header.seq_num = p4ioemu_p4io_last_seq_num; + 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; } static HRESULT p4ioemu_p4io_bulk_write(const void *req, uint32_t nbytes) { - const struct p4ioemu_p4io_cmd_package *package; - const void *payload; + const struct p4io_cmd_package *package; - if (nbytes < sizeof(struct p4ioemu_p4io_cmd_package)) { - log_warning("Command on bulk write endpoint to short: %d", nbytes); + if (nbytes < sizeof(struct p4io_cmd_header)) { + log_warning("Command on bulk write endpoint too short: %d", nbytes); p4ioemu_p4io_dump_buffer(req, nbytes); return E_INVALIDARG; } - package = (struct p4ioemu_p4io_cmd_package *) req; - payload = req + sizeof(struct p4ioemu_p4io_cmd_package); + package = (struct p4io_cmd_package *) req; - if (package->header_AA != 0xAA) { - log_warning("Command on bulk endpoint to short: %d", nbytes); + if (package->header.AA != 0xAA) { + log_warning("Command on bulk endpoint too short: %d", nbytes); p4ioemu_p4io_dump_buffer(req, nbytes); return E_INVALIDARG; } - p4ioemu_p4io_last_cmd = package->cmd; - p4ioemu_p4io_last_seq_num = package->seq_num; + p4ioemu_p4io_last_cmd = package->header.cmd; + p4ioemu_p4io_last_seq_num = package->header.seq_num; /* handle commands that are common p4io ones first */ p4ioemu_p4io_cmd_buffer_resp_len = p4ioemu_p4io_command_handle( - package->cmd, - payload, - package->payload_len, + package->header.cmd, + package->payload, + package->header.payload_len, 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) { p4ioemu_p4io_cmd_buffer_resp_len = p4ioemu_device_msg_hook->command_handle( - package->cmd, - payload, - package->payload_len, + package->header.cmd, + package->payload, + package->header.payload_len, p4ioemu_p4io_cmd_read_buffer, sizeof(p4ioemu_p4io_cmd_read_buffer)); } } 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; } @@ -413,7 +339,7 @@ static HRESULT p4ioemu_device_ioctl(struct irp *irp) /* Cases are listed in order of first receipt */ switch (irp->ioctl) { - case IOCTL_P4IO_GET_DEVICE_NAME: { + case P4IO_IOCTL_GET_DEVICE_NAME: { const char dev_name[] = "kactools p4ioemu"; if (irp->read.nbytes < strlen(dev_name)) { @@ -427,7 +353,7 @@ static HRESULT p4ioemu_device_ioctl(struct irp *irp) return S_OK; } - case IOCTL_P4IO_READ_JAMMA_2: { + case P4IO_IOCTL_READ_JAMMA_2: { p4ioemu_device_msg_hook->jamma2_read( irp->read.bytes, irp->read.nbytes); irp->read.pos = irp->read.nbytes; diff --git a/src/main/p4ioemu/setupapi.c b/src/main/p4ioemu/setupapi.c index 7642e9e..2d6daf2 100644 --- a/src/main/p4ioemu/setupapi.c +++ b/src/main/p4ioemu/setupapi.c @@ -2,11 +2,10 @@ #include "p4ioemu/setupapi.h" +#include "p4io/guid.h" + const struct hook_setupapi_data p4ioemu_setupapi_data = { - .device_guid = {0x8B7250A5, - 0x4F61, - 0x46C9, - {0x84, 0x3A, 0xE6, 0x68, 0x06, 0x47, 0x6A, 0x20}}, + .device_guid = p4io_guid, .device_desc = NULL, .device_path = "\\p4io", -}; \ No newline at end of file +};