From 178d4afec8db1ceee7d26f821ba5f7ac42733648 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 16:47:13 +0200 Subject: [PATCH 01/20] feat(util/log): Expose proper log level definition with enum Internally, this was already used but it lacked a clean public interface to set different log levels from the outside. Useful for various command line tooling to implement verbosity levels, e.g. -v, -vv, -vvv --- src/main/util/log.c | 28 +++++++++++++--------------- src/main/util/log.h | 9 ++++++++- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/main/util/log.c b/src/main/util/log.c index f1f697e..7da1d63 100644 --- a/src/main/util/log.c +++ b/src/main/util/log.c @@ -10,14 +10,14 @@ static log_writer_t log_writer; static void *log_writer_ctx; -static unsigned int log_level; +static enum log_level log_level; static void log_builtin_fatal(const char *module, const char *fmt, ...); static void log_builtin_info(const char *module, const char *fmt, ...); static void log_builtin_misc(const char *module, const char *fmt, ...); static void log_builtin_warning(const char *module, const char *fmt, ...); static void log_builtin_format( - unsigned int msg_level, const char *module, const char *fmt, va_list ap); + enum log_level msg_level, const char *module, const char *fmt, va_list ap); #define IMPLEMENT_SINK(name, msg_level) \ static void name(const char *module, const char *fmt, ...) \ @@ -29,16 +29,16 @@ static void log_builtin_format( va_end(ap); \ } -IMPLEMENT_SINK(log_builtin_info, 2) -IMPLEMENT_SINK(log_builtin_misc, 3) -IMPLEMENT_SINK(log_builtin_warning, 1) +IMPLEMENT_SINK(log_builtin_info, LOG_LEVEL_INFO) +IMPLEMENT_SINK(log_builtin_misc, LOG_LEVEL_MISC) +IMPLEMENT_SINK(log_builtin_warning, LOG_LEVEL_WARNING) static void log_builtin_fatal(const char *module, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - log_builtin_format(0, module, fmt, ap); + log_builtin_format(LOG_LEVEL_FATAL, module, fmt, ap); va_end(ap); DebugBreak(); @@ -46,7 +46,7 @@ static void log_builtin_fatal(const char *module, const char *fmt, ...) } static void log_builtin_format( - unsigned int msg_level, const char *module, const char *fmt, va_list ap) + enum log_level msg_level, const char *module, const char *fmt, va_list ap) { static const char chars[] = "FWIM"; @@ -55,13 +55,11 @@ static void log_builtin_format( char msg[65536]; int result; - str_vformat(msg, sizeof(msg), fmt, ap); - result = str_format( - line, sizeof(line), "%c:%s: %s\n", chars[msg_level], module, msg); - - // TODO move this up because there is no reason to format and do all the - // above if disabled if (msg_level <= log_level) { + str_vformat(msg, sizeof(msg), fmt, ap); + result = str_format( + line, sizeof(line), "%c:%s: %s\n", chars[msg_level], module, msg); + log_writer(log_writer_ctx, line, result); } } @@ -98,7 +96,7 @@ void log_to_writer(log_writer_t writer, void *ctx) } } -void log_set_level(unsigned int new_level) +void log_set_level(enum log_level new_level) { log_level = new_level; } @@ -133,4 +131,4 @@ log_formatter_t log_impl_info = log_builtin_info; log_formatter_t log_impl_warning = log_builtin_warning; log_formatter_t log_impl_fatal = log_builtin_fatal; static log_writer_t log_writer = log_writer_null; -static unsigned int log_level = 4; +static enum log_level log_level = LOG_LEVEL_MISC; diff --git a/src/main/util/log.h b/src/main/util/log.h index a60cdb7..47603fa 100644 --- a/src/main/util/log.h +++ b/src/main/util/log.h @@ -59,6 +59,13 @@ extern log_formatter_t log_impl_info; extern log_formatter_t log_impl_warning; extern log_formatter_t log_impl_fatal; +enum log_level { + LOG_LEVEL_FATAL = 0, + LOG_LEVEL_WARNING = 1, + LOG_LEVEL_INFO = 2, + LOG_LEVEL_MISC = 3, +}; + void log_assert_body(const char *file, int line, const char *function); void log_to_external( log_formatter_t misc, @@ -67,7 +74,7 @@ void log_to_external( log_formatter_t fatal); void log_to_writer(log_writer_t writer, void *ctx); -void log_set_level(unsigned int new_level); +void log_set_level(enum log_level new_level); /* I tried to make this API match the function signature of the AVS log writer callback, but then the signature changed and the explicit line breaks -- 2.54.0 From b803084cc83fb29b989cb6b339b24b66a3d23851 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 16:48:27 +0200 Subject: [PATCH 02/20] feat(util/iobuf): Improve log output Output the full buffer and the buffer up to the currently set position. Makes debugging a lot easier if you know the position set is ok to only look at the output up to there. Otherwise, having the full view is also important to check if data got truncated or checking for odd looking "garbage" data. --- src/main/util/iobuf.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/main/util/iobuf.c b/src/main/util/iobuf.c index c2d0ad1..45884ea 100644 --- a/src/main/util/iobuf.c +++ b/src/main/util/iobuf.c @@ -14,11 +14,18 @@ void iobuf_log(struct iobuf *buffer, const char *tag) str = xmalloc(str_len); log_misc( - "[%s] (%d %d)", tag, (uint32_t) buffer->nbytes, (uint32_t) buffer->pos); + "[%s] (nbytes %d, pos %d)", + tag, + (uint32_t) buffer->nbytes, + (uint32_t) buffer->pos); hex_encode_uc(buffer->bytes, buffer->nbytes, str, str_len); - log_misc("[%s]: %s", tag, str); + log_misc("full [%s]: %s", tag, str); + + hex_encode_uc(buffer->bytes, buffer->pos, str, str_len); + + log_misc("pos [%s]: %s", tag, str); free(str); } @@ -32,11 +39,18 @@ void iobuf_log_const(struct const_iobuf *buffer, const char *tag) str = xmalloc(str_len); log_misc( - "[%s] (%d %d)", tag, (uint32_t) buffer->nbytes, (uint32_t) buffer->pos); + "[%s] (nbytes %d, pos %d)", + tag, + (uint32_t) buffer->nbytes, + (uint32_t) buffer->pos); hex_encode_uc(buffer->bytes, buffer->nbytes, str, str_len); - log_misc("[%s]: %s", tag, str); + log_misc("full [%s]: %s", tag, str); + + hex_encode_uc(buffer->bytes, buffer->pos, str, str_len); + + log_misc("pos [%s]: %s", tag, str); free(str); } -- 2.54.0 From a3f3599cf247ad320bdec838558ca254cbe9cd01 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 16:50:35 +0200 Subject: [PATCH 03/20] refactor(p3io/frame): Lift magic numbers to macro Aligns the code with other ACIO code where the init and escape chars have proper macros to improve readability --- src/main/p3io/frame.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/p3io/frame.c b/src/main/p3io/frame.c index df00016..bc50dd4 100644 --- a/src/main/p3io/frame.c +++ b/src/main/p3io/frame.c @@ -8,6 +8,9 @@ #include "util/iobuf.h" #include "util/log.h" +#define P3IO_FRAME_SOF 0xAA +#define P3IO_FRAME_ESCAPE 0xFF + HRESULT p3io_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes) { @@ -24,17 +27,17 @@ p3io_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes) goto trunc; } - dest->bytes[dest->pos++] = 0xAA; + dest->bytes[dest->pos++] = P3IO_FRAME_SOF; for (i = 0; i < nbytes; i++) { b = bytes[i]; - if (b == 0xAA || b == 0xFF) { + if (b == P3IO_FRAME_SOF || b == P3IO_FRAME_ESCAPE) { if (dest->pos + 1 >= dest->nbytes) { goto trunc; } - dest->bytes[dest->pos++] = 0xFF; + dest->bytes[dest->pos++] = P3IO_FRAME_ESCAPE; dest->bytes[dest->pos++] = ~b; } else { if (dest->pos >= dest->nbytes) { @@ -64,7 +67,7 @@ p3io_frame_decode(struct iobuf *dest, struct const_iobuf *src) log_assert(dest != NULL); log_assert(src != NULL); - if (src->pos >= src->nbytes || src->bytes[src->pos] != 0xAA) { + if (src->pos >= src->nbytes || src->bytes[src->pos] != P3IO_FRAME_SOF) { return E_FAIL; } @@ -78,9 +81,9 @@ p3io_frame_decode(struct iobuf *dest, struct const_iobuf *src) b = src->bytes[src->pos++]; - if (b == 0xAA) { + if (b == P3IO_FRAME_SOF) { return E_FAIL; - } else if (b == 0xFF) { + } else if (b == P3IO_FRAME_ESCAPE) { if (escape) { return E_FAIL; } -- 2.54.0 From b12d6e6b004cea87e4032835ed88eca303d641dd Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 16:59:36 +0200 Subject: [PATCH 04/20] feat(p3io/cmd): Overhaul based on real P3IO DDR hardware tests Using a real DDR P3IO (Dragon PCB), the overall P3IO command structures improved significantly. Added further notes about unknown/unclear commands that were not derivable from the DDR 18 game binary either. Note that this module is also used by jubeat to some extend. The initial design apparently assumed a greater overlap in commands. This now seems less likely as many commands appear to be DDR specific. Refactoring/splitting this is a major effort that is not being addressed here. --- src/main/p3io/cmd.h | 210 +++++++++++++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 42 deletions(-) diff --git a/src/main/p3io/cmd.h b/src/main/p3io/cmd.h index 498b6e2..d70b192 100644 --- a/src/main/p3io/cmd.h +++ b/src/main/p3io/cmd.h @@ -5,15 +5,24 @@ #include "p3io/frame.h" +// Max message size of bulk endpoint for p3io command messaging +#define P3IO_MAX_MESSAGE_SIZE 256 + enum { P3IO_CMD_GET_VERSION = 0x01, + // Appears to be some alternative "initialize" (set mode) command call on + // DDR but currently unclear how this is being used. So far, it causes the + // p3io (on a SD cab) to get stuck/crash when issued. + P3IO_CMD_INIT2 = 0x03, P3IO_CMD_SET_WATCHDOG = 0x05, P3IO_CMD_POWEROFF = 0x22, P3IO_CMD_SET_OUTPUTS = 0x24, P3IO_CMD_READ_PLUG = 0x25, P3IO_CMD_GET_CAB_TYPE_OR_DIPSW = 0x27, P3IO_CMD_GET_VIDEO_FREQ = 0x29, - P3IO_CMD_SET_MODE = 0x2F, + // Unknown command that is part of DDR's IO setup routine + P3IO_CMD_UNKNOWN_2B = 0x2B, + P3IO_CMD_INIT = 0x2F, P3IO_CMD_GET_COINSTOCK = 0x31, P3IO_CMD_SET_COINCOUNTER = 0x32, P3IO_CMD_RS232_OPEN_CLOSE = 0x38, @@ -21,58 +30,119 @@ enum { P3IO_CMD_RS232_READ = 0x3B, }; -enum { +enum p3io_rs232_cmd { P3IO_RS232_CMD_OPEN = 0x00, P3IO_RS232_CMD_CLOSE = 0xFF, }; -enum { +enum p3io_rs232_baud_rate { P3IO_RS232_BAUD_19200 = 0x02, P3IO_RS232_BAUD_38400 = 0x03, P3IO_RS232_BAUD_57600 = 0x04, }; +/** + * Selector for the P3IO_CMD_GET_CAB_TYPE_OR_DIPSW command + */ +enum p3io_cab_type_dip_sw_selector { + P3IO_DIP_SW_SELECTOR = 0, + P3IO_CAB_TYPE_SELECTOR = 1, +}; + +/** + * Enum for available video frequencies to select from. Depending on the + * game, this feature is used or unused. + * + * Set by a jumper wire on the Jamma harness. + */ +enum p3io_video_freq { + P3IO_VIDEO_FREQ_15KHZ = 0xFF, + P3IO_VIDEO_FREQ_31KHZ = 0x7F, +}; + +/** + * Enum for available cabinet/display types to select from. Depending on the + * game, this feature is used or unused. + * + * The p3io detects the monitor type through its monitor passthrough. If the + * monitor provides an EDID, it assumes it's an HD monitor. Otherwise, it + * assumes SD because CRT monitors likely do not provide EDIDs. + */ +enum p3io_cab_type { + P3IO_CAB_TYPE_SD = 0, + P3IO_CAB_TYPE_HD = 1, +}; + #pragma pack(push, 1) +/** + * p3io command message header of any message (request and response) + */ struct p3io_hdr { uint8_t nbytes; uint8_t seq_no; + uint8_t cmd; }; -struct p3io_req_u8 { +// ----------------------------------------------------------------------------- + +struct p3io_req_version { struct p3io_hdr hdr; - uint8_t cmd; - uint8_t u8; }; -struct p3io_req_get_cab_type_or_dipsw { +struct p3io_req_init2 { struct p3io_hdr hdr; - uint8_t cmd; - uint8_t cab_type_or_dipsw; + uint8_t unknown_AA; }; -struct p3io_req_set_coin_counter { +struct p3io_req_watchdog { struct p3io_hdr hdr; - uint8_t cmd; - uint8_t coin_counter[2]; + uint8_t enable; }; struct p3io_req_set_outputs { struct p3io_hdr hdr; - uint8_t cmd; - uint8_t unk_00; + uint8_t unk_FF; uint32_t outputs; }; struct p3io_req_read_plug { struct p3io_hdr hdr; - uint8_t cmd; uint8_t flags; }; +struct p3io_req_get_cab_type_or_dipsw { + struct p3io_hdr hdr; + uint8_t cab_type_or_dipsw; +}; + +struct p3io_req_get_video_freq { + struct p3io_hdr hdr; + uint8_t unknown_05; +}; + +struct p3io_req_unknown_2b { + struct p3io_hdr hdr; + // either 0 or 1 set by game (boolean value) + // set to 0 for p3io dragon (SD cab?) + uint8_t unknown; +}; + +struct p3io_req_init { + struct p3io_hdr hdr; +}; + +struct p3io_req_coin_stock { + struct p3io_hdr hdr; +}; + +struct p3io_req_set_coin_counter { + struct p3io_hdr hdr; + uint8_t coin_counter[2]; +}; + struct p3io_req_rs232_open_close { struct p3io_hdr hdr; - uint8_t cmd; uint8_t port_no; uint8_t subcmd; uint8_t baud_code; @@ -80,91 +150,147 @@ struct p3io_req_rs232_open_close { struct p3io_req_rs232_read { struct p3io_hdr hdr; - uint8_t cmd; uint8_t port_no; uint8_t nbytes; }; struct p3io_req_rs232_write { struct p3io_hdr hdr; - uint8_t cmd; uint8_t port_no; uint8_t nbytes; uint8_t bytes[128]; }; +struct p3io_req_raw { + uint8_t data[P3IO_MAX_MESSAGE_SIZE]; +}; + +// Structs sorted in command ID order union p3io_req_any { struct p3io_hdr hdr; - struct p3io_req_u8 u8; - struct p3io_req_get_cab_type_or_dipsw cab_type_or_dipsw; - struct p3io_req_set_coin_counter set_coin_counter; + struct p3io_req_version version; + struct p3io_req_init2 init2; + struct p3io_req_watchdog watchdog; struct p3io_req_set_outputs set_outputs; struct p3io_req_read_plug read_plug; + struct p3io_req_get_cab_type_or_dipsw cab_type_or_dipsw; + struct p3io_req_get_video_freq video_freq; + struct p3io_req_unknown_2b unknown_2b; + struct p3io_req_init init; + struct p3io_req_coin_stock coin_stock; + struct p3io_req_set_coin_counter set_coin_counter; struct p3io_req_rs232_open_close rs232_open_close; struct p3io_req_rs232_read rs232_read; struct p3io_req_rs232_write rs232_write; - uint8_t raw[128]; + struct p3io_req_raw raw; }; -struct p3io_resp_u8 { +// ----------------------------------------------------------------------------- + +struct p3io_resp_version { + struct p3io_hdr hdr; + char str[4]; + uint8_t major; + uint8_t minor; + uint8_t patch; +}; + +struct p3io_resp_init2 { struct p3io_hdr hdr; uint8_t status; - uint8_t u8; +}; + +struct p3io_resp_watchdog { + struct p3io_hdr hdr; + uint8_t state; +}; + +struct p3io_resp_set_outputs { + struct p3io_hdr hdr; + // Apparently always 0xFF on real hardware + uint8_t unkn_FF; + uint8_t unkn; +}; + +struct p3io_resp_read_plug { + struct p3io_hdr hdr; + uint8_t present; + uint8_t rom[8]; + uint8_t eeprom[32]; }; struct p3io_resp_get_cab_type_or_dipsw { + struct p3io_hdr hdr; + uint8_t state; +}; + +struct p3io_resp_get_video_freq { + struct p3io_hdr hdr; + uint8_t video_freq; +}; + +struct p3io_resp_unknown_2b { + struct p3io_hdr hdr; + uint8_t unknown; +}; + +struct p3io_resp_init { + struct p3io_hdr hdr; + uint8_t status; +}; + +struct p3io_resp_set_coin_counter { struct p3io_hdr hdr; uint8_t status; }; struct p3io_resp_coin_stock { struct p3io_hdr hdr; - uint8_t status; uint8_t error; uint16_t slots[2]; }; -struct p3io_resp_read_plug { +struct p3io_resp_rs232_open_close { struct p3io_hdr hdr; uint8_t status; - uint8_t present; - uint8_t rom[8]; - uint8_t eeprom[32]; }; struct p3io_resp_rs232_read { struct p3io_hdr hdr; - uint8_t status; uint8_t nbytes; uint8_t bytes[126]; }; struct p3io_resp_rs232_write { struct p3io_hdr hdr; - uint8_t status; uint8_t nbytes; }; -struct p3io_resp_version { - struct p3io_hdr hdr; - uint8_t status; - char str[4]; - uint32_t major; - uint32_t minor; - uint32_t patch; +struct p3io_resp_raw { + uint8_t data[P3IO_MAX_MESSAGE_SIZE]; }; union p3io_resp_any { struct p3io_hdr hdr; - struct p3io_resp_u8 u8; - struct p3io_resp_get_cab_type_or_dipsw cab_type_or_dipsw; - struct p3io_resp_coin_stock coin_stock; + struct p3io_resp_version version; + struct p3io_resp_init2 init2; + struct p3io_resp_watchdog watchdog; + struct p3io_resp_set_outputs set_outputs; struct p3io_resp_read_plug read_plug; + struct p3io_resp_get_cab_type_or_dipsw cab_type_or_dipsw; + struct p3io_resp_get_video_freq video_freq; + struct p3io_resp_unknown_2b unknown_2b; + struct p3io_resp_init init; + struct p3io_resp_coin_stock coin_stock; + struct p3io_resp_set_coin_counter set_coin_counter; + struct p3io_resp_rs232_open_close rs232_open_close; struct p3io_resp_rs232_read rs232_read; struct p3io_resp_rs232_write rs232_write; - struct p3io_resp_version version; + struct p3io_resp_raw raw; }; +// ----------------------------------------------------------------------------- + #pragma pack(pop) uint8_t p3io_req_cmd(const union p3io_req_any *src); -- 2.54.0 From 93752f66a31a4b11e1fb98e1b7538c452043d6c0 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:03:34 +0200 Subject: [PATCH 05/20] refactor(p3io/cmd): Add and improve p3io command helper functions With the overhaul of commands, some helpers needed tweaks while new ones were added to abstract p3io command details with a common interface, e.g. dealing with the command length field. --- src/main/p3io/cmd.c | 48 +++++++++++++++++++++++++++++++++------------ src/main/p3io/cmd.h | 11 ++++++++--- 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/main/p3io/cmd.c b/src/main/p3io/cmd.c index 6a0a7ff..bfb4e82 100644 --- a/src/main/p3io/cmd.c +++ b/src/main/p3io/cmd.c @@ -4,25 +4,49 @@ #include "util/log.h" -uint8_t p3io_req_cmd(const union p3io_req_any *src) +uint8_t p3io_get_full_req_size(const union p3io_req_any *req) { - log_assert(src != NULL); + /* Length byte in this packet format counts everything from the length + byte onwards. The length byte itself occurs at the start of the frame. */ - /* In requests, the command byte is the first byte after the header. */ - - return src->raw[sizeof(struct p3io_hdr)]; + return req->hdr.nbytes + 1; } -void p3io_resp_init( - struct p3io_hdr *dest, size_t nbytes, const struct p3io_hdr *req) +uint8_t p3io_get_full_resp_size(const union p3io_resp_any *resp) { - log_assert(dest != NULL); - log_assert(req != NULL); - log_assert(nbytes < 0x100); + /* Length byte in this packet format counts everything from the length + byte onwards. The length byte itself occurs at the start of the frame. */ + + return resp->hdr.nbytes + 1; +} + +void p3io_req_hdr_init( + struct p3io_hdr *req_hdr, uint8_t seq_no, uint8_t cmd, size_t size) +{ + log_assert(req_hdr != NULL); + log_assert(size < P3IO_MAX_MESSAGE_SIZE); + + memset(req_hdr, 0, sizeof(struct p3io_hdr)); /* Length byte in this packet format counts everything from the length byte onwards. The length byte itself occurs at the start of the frame. */ - dest->nbytes = nbytes - 1; - dest->seq_no = req->seq_no; + req_hdr->nbytes = size - 1; + req_hdr->seq_no = seq_no; + req_hdr->cmd = cmd; +} + +void p3io_resp_hdr_init( + struct p3io_hdr *resp_hdr, size_t nbytes, const struct p3io_hdr *req_hdr) +{ + log_assert(resp_hdr != NULL); + log_assert(req_hdr != NULL); + log_assert(nbytes < P3IO_MAX_MESSAGE_SIZE); + + /* Length byte in this packet format counts everything from the length + byte onwards. The length byte itself occurs at the start of the frame. */ + + resp_hdr->nbytes = nbytes - 1; + resp_hdr->seq_no = req_hdr->seq_no; + resp_hdr->cmd = req_hdr->cmd; } diff --git a/src/main/p3io/cmd.h b/src/main/p3io/cmd.h index d70b192..5359ec4 100644 --- a/src/main/p3io/cmd.h +++ b/src/main/p3io/cmd.h @@ -293,9 +293,14 @@ union p3io_resp_any { #pragma pack(pop) -uint8_t p3io_req_cmd(const union p3io_req_any *src); +uint8_t p3io_get_full_req_size(const union p3io_req_any *req); -void p3io_resp_init( - struct p3io_hdr *dest, size_t nbytes, const struct p3io_hdr *req); +uint8_t p3io_get_full_resp_size(const union p3io_resp_any *resp); + +void p3io_req_hdr_init( + struct p3io_hdr *hdr, uint8_t seq_no, uint8_t cmd, size_t size); + +void p3io_resp_hdr_init( + struct p3io_hdr *resp_hdr, size_t nbytes, const struct p3io_hdr *req_hdr); #endif -- 2.54.0 From 0013efa31abc9fc42efbd5316367956723b0ca0b Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:07:07 +0200 Subject: [PATCH 06/20] refactor(p3ioemu/uart): Adjust to command structure changes --- src/main/p3ioemu/uart.c | 22 +++++++++++++++------- src/main/p3ioemu/uart.h | 3 ++- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/p3ioemu/uart.c b/src/main/p3ioemu/uart.c index 68717b5..a4a9f73 100644 --- a/src/main/p3ioemu/uart.c +++ b/src/main/p3ioemu/uart.c @@ -38,7 +38,8 @@ void p3io_uart_set_path(size_t uart_no, const wchar_t *path) } void p3io_uart_cmd_open_close( - const struct p3io_req_rs232_open_close *req, struct p3io_resp_u8 *resp) + const struct p3io_req_rs232_open_close *req, + struct p3io_resp_rs232_open_close *resp) { const wchar_t *path; uint32_t baud_rate; @@ -104,9 +105,9 @@ void p3io_uart_cmd_open_close( } end: - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); - resp->status = 0; - resp->u8 = FAILED(hr); + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); + + resp->status = FAILED(hr); } void p3io_uart_cmd_read( @@ -140,10 +141,13 @@ void p3io_uart_cmd_read( end: /* Variable-length response, init the header manually */ + if (FAILED(hr)) { + log_warning("Reading failed: %lX", hr); + } resp->hdr.nbytes = iobuf.pos + 3; resp->hdr.seq_no = req->hdr.seq_no; - resp->status = FAILED(hr); + resp->hdr.cmd = req->hdr.cmd; resp->nbytes = iobuf.pos; } @@ -167,8 +171,12 @@ void p3io_uart_cmd_write( hr = p3io_uart_write(p3io_uart_fds[req->port_no], &iobuf); end: - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); - resp->status = FAILED(hr); + if (FAILED(hr)) { + log_warning("Writing failed: %lX", hr); + } + + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); + resp->nbytes = iobuf.pos; } diff --git a/src/main/p3ioemu/uart.h b/src/main/p3ioemu/uart.h index e0d417b..36e201b 100644 --- a/src/main/p3ioemu/uart.h +++ b/src/main/p3ioemu/uart.h @@ -6,7 +6,8 @@ void p3io_uart_set_path(size_t uart_no, const wchar_t *path); void p3io_uart_cmd_open_close( - const struct p3io_req_rs232_open_close *req, struct p3io_resp_u8 *resp); + const struct p3io_req_rs232_open_close *req, + struct p3io_resp_rs232_open_close *resp); void p3io_uart_cmd_read( const struct p3io_req_rs232_read *req, struct p3io_resp_rs232_read *resp); -- 2.54.0 From d52a7e0993467d57c318e40e9a483a574bfb9041 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:07:51 +0200 Subject: [PATCH 07/20] refactor(p3ioemu/emu): Adjust to command structure changes Various structures were improved, some slightly changed even. Behavior/functionality expected to be identical. --- src/main/p3ioemu/emu.c | 182 +++++++++++++++++++---------------------- src/main/p3ioemu/emu.h | 18 +--- 2 files changed, 87 insertions(+), 113 deletions(-) diff --git a/src/main/p3ioemu/emu.c b/src/main/p3ioemu/emu.c index 4b42e59..c72f9d8 100644 --- a/src/main/p3ioemu/emu.c +++ b/src/main/p3ioemu/emu.c @@ -35,35 +35,28 @@ static HRESULT p3io_emu_handle_write(struct irp *irp); static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req); static void p3io_cmd_get_version( - const struct p3io_hdr *req, struct p3io_resp_version *resp); - -static void -p3io_cmd_set_watchdog(const struct p3io_req_u8 *req, struct p3io_resp_u8 *resp); - + const struct p3io_req_version *req, struct p3io_resp_version *resp); +static void p3io_cmd_set_watchdog( + const struct p3io_req_watchdog *req, struct p3io_resp_watchdog *resp); static void p3io_cmd_set_outputs( - const struct p3io_req_set_outputs *req, struct p3io_resp_u8 *resp); - + const struct p3io_req_set_outputs *req, struct p3io_resp_set_outputs *resp); static void p3io_cmd_read_plug( const struct p3io_req_read_plug *req, struct p3io_resp_read_plug *resp); - static void p3io_cmd_get_cab_type_or_dipsw( const struct p3io_req_get_cab_type_or_dipsw *req, struct p3io_resp_get_cab_type_or_dipsw *resp); - static void p3io_cmd_get_video_freq( - const struct p3io_req_u8 *req, struct p3io_resp_u8 *resp); - + const struct p3io_req_get_video_freq *req, + struct p3io_resp_get_video_freq *resp); static void -p3io_cmd_set_mode(const struct p3io_req_u8 *req, struct p3io_resp_u8 *resp); - +p3io_cmd_init(const struct p3io_req_init *req, struct p3io_resp_init *resp); static void p3io_cmd_get_coinstock( - const struct p3io_req_u8 *req, struct p3io_resp_coin_stock *resp); - + const struct p3io_req_coin_stock *req, struct p3io_resp_coin_stock *resp); static void p3io_cmd_set_coin_counter( - const struct p3io_req_set_coin_counter *req, struct p3io_resp_u8 *resp); - + const struct p3io_req_set_coin_counter *req, + struct p3io_resp_set_coin_counter *resp); static void -p3io_cmd_unknown(const union p3io_req_any *req, struct p3io_resp_u8 *resp); +p3io_cmd_unknown(const union p3io_req_any *req, struct p3io_resp_raw *resp); void p3io_emu_init(const struct p3io_ops *ops, void *ctx) { @@ -218,7 +211,7 @@ static HRESULT p3io_emu_handle_write(struct irp *irp) memset(&req, 0, sizeof(req)); - deframe.bytes = req.raw; + deframe.bytes = req.raw.data; deframe.nbytes = sizeof(req.raw); deframe.pos = 0; @@ -242,23 +235,20 @@ static HRESULT p3io_emu_handle_write(struct irp *irp) static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req) { union p3io_resp_any resp; - uint8_t cmd; - cmd = p3io_req_cmd(req); - - switch (cmd) { + switch (req->hdr.cmd) { case P3IO_CMD_GET_VERSION: - p3io_cmd_get_version(&req->hdr, &resp.version); + p3io_cmd_get_version(&req->version, &resp.version); break; case P3IO_CMD_SET_WATCHDOG: - p3io_cmd_set_watchdog(&req->u8, &resp.u8); + p3io_cmd_set_watchdog(&req->watchdog, &resp.watchdog); break; case P3IO_CMD_SET_OUTPUTS: - p3io_cmd_set_outputs(&req->set_outputs, &resp.u8); + p3io_cmd_set_outputs(&req->set_outputs, &resp.set_outputs); break; @@ -274,27 +264,29 @@ static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req) break; case P3IO_CMD_GET_VIDEO_FREQ: - p3io_cmd_get_video_freq(&req->u8, &resp.u8); + p3io_cmd_get_video_freq(&req->video_freq, &resp.video_freq); break; - case P3IO_CMD_SET_MODE: - p3io_cmd_set_mode(&req->u8, &resp.u8); + case P3IO_CMD_INIT: + p3io_cmd_init(&req->init, &resp.init); break; case P3IO_CMD_GET_COINSTOCK: - p3io_cmd_get_coinstock(&req->u8, &resp.coin_stock); + p3io_cmd_get_coinstock(&req->coin_stock, &resp.coin_stock); break; case P3IO_CMD_SET_COINCOUNTER: - p3io_cmd_set_coin_counter(&req->set_coin_counter, &resp.u8); + p3io_cmd_set_coin_counter( + &req->set_coin_counter, &resp.set_coin_counter); break; case P3IO_CMD_RS232_OPEN_CLOSE: - p3io_uart_cmd_open_close(&req->rs232_open_close, &resp.u8); + p3io_uart_cmd_open_close( + &req->rs232_open_close, &resp.rs232_open_close); break; @@ -309,7 +301,7 @@ static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req) break; default: - p3io_cmd_unknown(req, &resp.u8); + p3io_cmd_unknown(req, &resp.raw); break; } @@ -319,34 +311,34 @@ static HRESULT p3io_cmd_dispatch(const union p3io_req_any *req) return S_OK; } -static void -p3io_cmd_get_version(const struct p3io_hdr *req, struct p3io_resp_version *resp) +static void p3io_cmd_get_version( + const struct p3io_req_version *req, struct p3io_resp_version *resp) { - log_misc("%s", __func__); + log_misc("Getting version"); + + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); - p3io_resp_init(&resp->hdr, sizeof(resp), req); - resp->status = 0; resp->str[0] = 'H'; resp->str[1] = 'D'; resp->str[2] = 'X'; resp->str[3] = '\0'; resp->major = 1; resp->minor = 2; - resp->patch = 3; + // resp->patch = 3; } -static void -p3io_cmd_set_watchdog(const struct p3io_req_u8 *req, struct p3io_resp_u8 *resp) +static void p3io_cmd_set_watchdog( + const struct p3io_req_watchdog *req, struct p3io_resp_watchdog *resp) { - log_misc("%s(%02x)", __func__, req->u8); + log_misc("Setting watchdog: %d", req->enable); - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); - resp->status = 0; - resp->u8 = 0; + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); + + resp->state = 0; } static void p3io_cmd_set_outputs( - const struct p3io_req_set_outputs *req, struct p3io_resp_u8 *resp) + const struct p3io_req_set_outputs *req, struct p3io_resp_set_outputs *resp) { uint32_t outputs; HRESULT hr; @@ -358,9 +350,13 @@ static void p3io_cmd_set_outputs( hr = S_OK; } - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); - resp->status = FAILED(hr); - resp->u8 = 0; + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + } + + resp->unkn_FF = 0xFF; } static void p3io_cmd_read_plug( @@ -368,9 +364,10 @@ static void p3io_cmd_read_plug( { HRESULT hr; - log_misc("%s(%02x)", __func__, req->flags); + log_misc("Reading plug: %02x", req->flags); + + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); resp->present = 1; /* @@ -427,8 +424,6 @@ static void p3io_cmd_read_plug( resp->eeprom[3], resp->eeprom[4], resp->eeprom[5]); - - resp->status = FAILED(hr); } static void p3io_cmd_get_cab_type_or_dipsw( @@ -439,22 +434,22 @@ static void p3io_cmd_get_cab_type_or_dipsw( uint8_t dipsw; enum p3io_cab_type type; - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); hr = S_OK; - if (req->cab_type_or_dipsw == 0) { + if (req->cab_type_or_dipsw == P3IO_CAB_TYPE_SELECTOR) { if (p3io_ops->get_cab_type) { hr = p3io_ops->get_cab_type(p3io_ops_ctx, &type); if (hr == S_OK) { switch (type) { case P3IO_CAB_TYPE_SD: - resp->status = 1; + resp->state = P3IO_CAB_TYPE_SD; break; case P3IO_CAB_TYPE_HD: - resp->status = 2; + resp->state = P3IO_CAB_TYPE_HD; break; default: @@ -462,59 +457,49 @@ static void p3io_cmd_get_cab_type_or_dipsw( break; } } else { - resp->status = 0; + resp->state = 0; } - } else { - resp->status = 0; } - } else if (req->cab_type_or_dipsw == 1) { + } else if (req->cab_type_or_dipsw == P3IO_DIP_SW_SELECTOR) { if (p3io_ops->get_dipsw) { hr = p3io_ops->get_dipsw(p3io_ops_ctx, &dipsw); if (hr == S_OK) { - resp->status = dipsw; + resp->state = dipsw; } else { - resp->status = 0; + resp->state = 0; } - } else { - resp->status = 0; } } else { log_warning( "Unknown value for cab type or dipsw: %d", req->cab_type_or_dipsw); - resp->status = 0; } } static void p3io_cmd_get_video_freq( - const struct p3io_req_u8 *req, struct p3io_resp_u8 *resp) + const struct p3io_req_get_video_freq *req, + struct p3io_resp_get_video_freq *resp) { HRESULT hr; - enum p3io_video_freq freq; - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); if (p3io_ops->get_video_freq != NULL) { - hr = p3io_ops->get_video_freq(p3io_ops_ctx, &freq); + hr = p3io_ops->get_video_freq( + p3io_ops_ctx, (enum p3io_video_freq *) &resp->video_freq); } else { hr = S_OK; - freq = P3IO_VIDEO_FREQ_31KHZ; + resp->video_freq = P3IO_VIDEO_FREQ_31KHZ; } if (hr == S_OK) { - switch (freq) { + switch (resp->video_freq) { case P3IO_VIDEO_FREQ_15KHZ: log_misc("%s: Returning 15 kHz", __func__); - resp->status = 0; - resp->u8 = 0x00; - break; case P3IO_VIDEO_FREQ_31KHZ: log_misc("%s: Returning 31 kHz", __func__); - resp->status = 0; - resp->u8 = 0x80; - break; default: @@ -523,23 +508,21 @@ static void p3io_cmd_get_video_freq( } } else { log_misc("%s: Returning error! (hr=%x)", __func__, (int) hr); - resp->status = 1; - resp->u8 = 0x00; } } static void -p3io_cmd_set_mode(const struct p3io_req_u8 *req, struct p3io_resp_u8 *resp) +p3io_cmd_init(const struct p3io_req_init *req, struct p3io_resp_init *resp) { - log_misc("%s(%02x)", __func__, req->u8); + log_misc("Init"); + + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); resp->status = 0; - resp->u8 = 0; } static void p3io_cmd_get_coinstock( - const struct p3io_req_u8 *req, struct p3io_resp_coin_stock *resp) + const struct p3io_req_coin_stock *req, struct p3io_resp_coin_stock *resp) { uint16_t slots[2]; HRESULT hr; @@ -552,30 +535,37 @@ static void p3io_cmd_get_coinstock( hr = S_OK; } - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); - resp->status = 0; + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); + resp->error = FAILED(hr); resp->slots[0] = _byteswap_ushort(slots[0]); resp->slots[1] = _byteswap_ushort(slots[1]); } static void p3io_cmd_set_coin_counter( - const struct p3io_req_set_coin_counter *req, struct p3io_resp_u8 *resp) + const struct p3io_req_set_coin_counter *req, + struct p3io_resp_set_coin_counter *resp) { log_misc( - "%s(%02x %02x)", __func__, req->coin_counter[0], req->coin_counter[1]); + "Setting coin counter: %02x %02x", + req->coin_counter[0], + req->coin_counter[1]); + + p3io_resp_hdr_init(&resp->hdr, sizeof(*resp), &req->hdr); - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); resp->status = 0; - resp->u8 = 0; } static void -p3io_cmd_unknown(const union p3io_req_any *req, struct p3io_resp_u8 *resp) +p3io_cmd_unknown(const union p3io_req_any *req, struct p3io_resp_raw *resp) { - log_warning("Unsupported P3IO command: %02x", p3io_req_cmd(req)); + log_warning("Unsupported P3IO command: %02x", req->hdr.cmd); - p3io_resp_init(&resp->hdr, sizeof(*resp), &req->hdr); - resp->status = 1; - resp->u8 = 0; + p3io_resp_hdr_init( + (struct p3io_hdr *) &resp->data, sizeof(*resp), &req->hdr); + + // Not always applicable/correct as there are several commands not + // responding with any data, but fine for the majority of (unsupported) + // commands + resp->data[sizeof(struct p3io_hdr) + 0] = 0; } diff --git a/src/main/p3ioemu/emu.h b/src/main/p3ioemu/emu.h index a013986..4f6f054 100644 --- a/src/main/p3ioemu/emu.h +++ b/src/main/p3ioemu/emu.h @@ -8,23 +8,7 @@ #include "hook/iohook.h" -/** - * Enum for available video frequencies to select from. Depending on the - * game, this feature is used or unused. - */ -enum p3io_video_freq { - P3IO_VIDEO_FREQ_15KHZ = 0, - P3IO_VIDEO_FREQ_31KHZ = 1, -}; - -/** - * Enum for available cabinet/display types to select from. Depending on the - * game, this feature is used or unused. - */ -enum p3io_cab_type { - P3IO_CAB_TYPE_SD = 0, - P3IO_CAB_TYPE_HD = 1, -}; +#include "p3io/cmd.h" /** * P3IO operation dispatching table. Many operations are optional and not used -- 2.54.0 From 367c9ca0d35f763cb82ac0e59c1fdea5432db1d2 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:09:12 +0200 Subject: [PATCH 08/20] refactor(unicorntail): Adjust to p3io command structure changes --- src/main/unicorntail/p3io.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/unicorntail/p3io.c b/src/main/unicorntail/p3io.c index f28103c..fd0a85c 100644 --- a/src/main/unicorntail/p3io.c +++ b/src/main/unicorntail/p3io.c @@ -136,7 +136,7 @@ static HRESULT p3io_handle_write(struct irp *irp) struct iobuf desc; HRESULT hr; - desc.bytes = req.raw; + desc.bytes = req.raw.data; desc.nbytes = sizeof(req); desc.pos = 0; @@ -146,10 +146,11 @@ static HRESULT p3io_handle_write(struct irp *irp) return hr; } - switch (p3io_req_cmd(&req)) { + switch (req.hdr.cmd) { case P3IO_CMD_RS232_OPEN_CLOSE: EnterCriticalSection(&p3io_cmd_lock); - p3io_uart_cmd_open_close(&req.rs232_open_close, &resp.u8); + p3io_uart_cmd_open_close( + &req.rs232_open_close, &resp.rs232_open_close); break; -- 2.54.0 From bb62d5241d73082449ab925e5e398b7b3b56b8c5 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:10:38 +0200 Subject: [PATCH 09/20] feat(p3io/ddr): Add data structures for DDR P3IO data Reverse engineered using a real P3IO DDR IO board and the DDR 16 game binary for additional reference --- src/main/p3io/ddr.h | 81 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/main/p3io/ddr.h diff --git a/src/main/p3io/ddr.h b/src/main/p3io/ddr.h new file mode 100644 index 0000000..85c1693 --- /dev/null +++ b/src/main/p3io/ddr.h @@ -0,0 +1,81 @@ +#ifndef P3IO_DDR_H +#define P3IO_DDR_H + +#include + +#pragma pack(push, 1) + +struct p3io_ddr_player { + uint8_t menu_start : 1; + uint8_t pad_up : 1; + uint8_t pad_down : 1; + uint8_t pad_left : 1; + uint8_t pad_right : 1; + uint8_t unknown : 1; + uint8_t menu_left : 1; + uint8_t menu_right : 1; +}; + +struct p3io_ddr_operator { + // Yes, this also contains the HD cab menu up and down buttons + // which are not "operator" inputs. I suppose they just had to go + // somewhere ¯\_(ツ)_/¯ + uint8_t p1_menu_up : 1; + uint8_t p1_menu_down : 1; + uint8_t p2_menu_up : 1; + uint8_t p2_menu_down : 1; + uint8_t test : 1; + uint8_t coin : 1; + uint8_t service : 1; + uint8_t unknown : 1; +}; + +struct p3io_ddr_jamma { + uint8_t unknown_80; + + struct p3io_ddr_player p1; + struct p3io_ddr_player p2; + struct p3io_ddr_operator operator; + + uint32_t unused; + uint32_t unused2; +}; + +struct p3io_ddr_cabinet_light { + uint8_t p1_menu_buttons : 1; + uint8_t p2_menu_buttons : 1; + uint8_t unknown : 2; + uint8_t top_p1_lower : 1; + uint8_t top_p1_upper : 1; + uint8_t top_p2_lower : 1; + uint8_t top_p2_upper : 1; +}; + +struct p3io_ddr_output { + uint8_t unused_00[3]; + struct p3io_ddr_cabinet_light cabinet; +}; + +_Static_assert( + sizeof(struct p3io_ddr_player) == 1, + "struct p3io_ddr_player is the wrong size"); + +_Static_assert( + sizeof(struct p3io_ddr_operator) == 1, + "struct p3io_ddr_operator is the wrong size"); + +_Static_assert( + sizeof(struct p3io_ddr_jamma) == sizeof(uint32_t) * 3, + "struct p3io_ddr_jamma is the wrong size"); + +_Static_assert( + sizeof(struct p3io_ddr_cabinet_light) == 1, + "struct p3io_ddr_cabinet_light is the wrong size"); + +_Static_assert( + sizeof(struct p3io_ddr_output) == sizeof(uint32_t), + "struct p3io_ddr_output is the wrong size"); + +#pragma pack(pop) + +#endif \ No newline at end of file -- 2.54.0 From 166f1f183abeb73f7027ba2fbd5466f7a82934bd Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:13:03 +0200 Subject: [PATCH 10/20] feat(p3iodrv): Add DDR compatible P3IO driver Initial version that supports the most important features to operate all inputs and outputs of a DDR SD cabinet. --- Module.mk | 1 + src/main/p3iodrv/Module.mk | 10 + src/main/p3iodrv/ddr.c | 326 ++++++++++++++++++++++++++++ src/main/p3iodrv/ddr.h | 115 ++++++++++ src/main/p3iodrv/device.c | 429 +++++++++++++++++++++++++++++++++++++ src/main/p3iodrv/device.h | 105 +++++++++ 6 files changed, 986 insertions(+) create mode 100644 src/main/p3iodrv/Module.mk create mode 100644 src/main/p3iodrv/ddr.c create mode 100644 src/main/p3iodrv/ddr.h create mode 100644 src/main/p3iodrv/device.c create mode 100644 src/main/p3iodrv/device.h diff --git a/Module.mk b/Module.mk index 235160d..c87fd20 100644 --- a/Module.mk +++ b/Module.mk @@ -166,6 +166,7 @@ include src/main/launcher/Module.mk include src/main/mempatch-hook/Module.mk include src/main/mm/Module.mk include src/main/p3io/Module.mk +include src/main/p3iodrv/Module.mk include src/main/p3ioemu/Module.mk include src/main/p4iodrv/Module.mk include src/main/p4ioemu/Module.mk diff --git a/src/main/p3iodrv/Module.mk b/src/main/p3iodrv/Module.mk new file mode 100644 index 0000000..7116bc2 --- /dev/null +++ b/src/main/p3iodrv/Module.mk @@ -0,0 +1,10 @@ +libs += p3iodrv + +libs_p4iodrv := \ + p3io \ + util \ + +src_p3iodrv := \ + ddr.c \ + device.c \ + diff --git a/src/main/p3iodrv/ddr.c b/src/main/p3iodrv/ddr.c new file mode 100644 index 0000000..2cc4526 --- /dev/null +++ b/src/main/p3iodrv/ddr.c @@ -0,0 +1,326 @@ +#define LOG_MODULE "p3iodrv-ddr" + +#include "p3io/cmd.h" + +#include "util/log.h" + +#include "ddr.h" +#include "device.h" + +static uint8_t p3iodrv_ddr_seq_counter; + +static uint8_t p3iodrv_ddr_get_and_update_seq_counter() +{ + return p3iodrv_ddr_seq_counter++ & 0xF; +} + +static HRESULT p3iodrv_ddr_check_resp( + const union p3io_resp_any *resp, + uint8_t expected_size, + const union p3io_req_any *corresponding_req) +{ + uint8_t actual_size; + + log_assert(resp); + + actual_size = p3io_get_full_resp_size(resp); + + if (actual_size != expected_size) { + log_warning( + "Incorrect response size, actual %d != expected %d", + actual_size, + expected_size); + return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); + } + + if (resp->hdr.seq_no != corresponding_req->hdr.seq_no) { + log_warning( + "Incorrect sequence num in response, actual %d != expected %d", + resp->hdr.seq_no, + corresponding_req->hdr.seq_no); + return HRESULT_FROM_WIN32(ERROR_INVALID_DATA); + } + + if (resp->hdr.cmd != corresponding_req->hdr.cmd) { + log_warning( + "Incorrect command in response, actual 0x%d != expected 0x%d", + resp->hdr.cmd, + corresponding_req->hdr.cmd); + return HRESULT_FROM_WIN32(ERROR_BAD_COMMAND); + } + + return S_OK; +} + +HRESULT p3iodrv_ddr_init(HANDLE handle) +{ + HRESULT hr; + uint8_t seq_cnt; + union p3io_req_any req; + union p3io_resp_any resp; + + log_assert(handle != INVALID_HANDLE_VALUE); + + seq_cnt = p3iodrv_ddr_get_and_update_seq_counter(); + + p3io_req_hdr_init(&req.hdr, seq_cnt, P3IO_CMD_INIT, sizeof(req.init)); + + hr = p3iodrv_device_transfer(handle, &req, &resp); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.init), &req); + + if (FAILED(hr)) { + return hr; + } + + if (resp.init.status != 0) { + log_warning("Initialization failed"); + return HRESULT_FROM_WIN32(ERROR_GEN_FAILURE); + } + + return S_OK; +} + +HRESULT p3iodrv_ddr_get_version( + HANDLE handle, + char str[4], + uint32_t *major, + uint32_t *minor, + uint32_t *patch) +{ + HRESULT hr; + uint8_t seq_cnt; + union p3io_req_any req; + union p3io_resp_any resp; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(major); + log_assert(minor); + log_assert(patch); + + seq_cnt = p3iodrv_ddr_get_and_update_seq_counter(); + + p3io_req_hdr_init( + &req.hdr, seq_cnt, P3IO_CMD_GET_VERSION, sizeof(req.version)); + + hr = p3iodrv_device_transfer(handle, &req, &resp); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.version), &req); + + if (FAILED(hr)) { + return hr; + } + + memcpy(str, resp.version.str, sizeof(char[4])); + *major = resp.version.major; + *minor = resp.version.minor; + *patch = resp.version.patch; + + return S_OK; +} + +HRESULT p3iodrv_ddr_set_watchdog(HANDLE handle, bool enable) +{ + HRESULT hr; + uint8_t seq_cnt; + union p3io_req_any req; + union p3io_resp_any resp; + + log_assert(handle != INVALID_HANDLE_VALUE); + + seq_cnt = p3iodrv_ddr_get_and_update_seq_counter(); + + p3io_req_hdr_init( + &req.hdr, seq_cnt, P3IO_CMD_SET_WATCHDOG, sizeof(req.watchdog)); + + req.watchdog.enable = enable ? 1 : 0; + + hr = p3iodrv_device_transfer(handle, &req, &resp); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.watchdog), &req); + + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +HRESULT p3iodrv_ddr_get_dipsw(HANDLE handle, uint8_t *state) +{ + HRESULT hr; + uint8_t seq_cnt; + union p3io_req_any req; + union p3io_resp_any resp; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(state); + + seq_cnt = p3iodrv_ddr_get_and_update_seq_counter(); + + p3io_req_hdr_init( + &req.hdr, + seq_cnt, + P3IO_CMD_GET_CAB_TYPE_OR_DIPSW, + sizeof(req.cab_type_or_dipsw)); + + req.cab_type_or_dipsw.cab_type_or_dipsw = P3IO_DIP_SW_SELECTOR; + + hr = p3iodrv_device_transfer(handle, &req, &resp); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.cab_type_or_dipsw), &req); + + if (FAILED(hr)) { + return hr; + } + + *state = resp.cab_type_or_dipsw.state; + + return S_OK; +} + +HRESULT p3iodrv_ddr_get_cab_type(HANDLE handle, enum p3io_cab_type *type) +{ + HRESULT hr; + uint8_t seq_cnt; + union p3io_req_any req; + union p3io_resp_any resp; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(type); + + seq_cnt = p3iodrv_ddr_get_and_update_seq_counter(); + + p3io_req_hdr_init( + &req.hdr, + seq_cnt, + P3IO_CMD_GET_CAB_TYPE_OR_DIPSW, + sizeof(req.cab_type_or_dipsw)); + + req.cab_type_or_dipsw.cab_type_or_dipsw = P3IO_CAB_TYPE_SELECTOR; + + hr = p3iodrv_device_transfer(handle, &req, &resp); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.cab_type_or_dipsw), &req); + + if (FAILED(hr)) { + return hr; + } + + *type = resp.cab_type_or_dipsw.state; + + return S_OK; +} + +HRESULT p3iodrv_ddr_get_video_freq(HANDLE handle, enum p3io_video_freq *freq) +{ + HRESULT hr; + uint8_t seq_cnt; + union p3io_req_any req; + union p3io_resp_any resp; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(freq); + + seq_cnt = p3iodrv_ddr_get_and_update_seq_counter(); + + p3io_req_hdr_init( + &req.hdr, seq_cnt, P3IO_CMD_GET_VIDEO_FREQ, sizeof(req.video_freq)); + + // Must be set to 5 in order to return the right values. There might be more + // to this, e.g. this being some kind of selector where to read from the + // JAMMA harness (?) but this is good for now to get what we want to know + // for DDR + req.video_freq.unknown_05 = 5; + + hr = p3iodrv_device_transfer(handle, &req, &resp); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.video_freq), &req); + + if (FAILED(hr)) { + return hr; + } + + *freq = (enum p3io_video_freq) resp.video_freq.video_freq; + + return S_OK; +} + +HRESULT p3iodrv_ddr_get_jamma(HANDLE handle, struct p3io_ddr_jamma *jamma) +{ + HRESULT hr; + uint32_t *jamma_raw; + + jamma_raw = (uint32_t *) jamma; + + hr = p3iodrv_device_read_jamma(handle, jamma_raw); + + if (FAILED(hr)) { + return hr; + } + + // Inputs are active low for p1, p2 and operator bits + jamma_raw[0] ^= 0xFFFFFF00; + + return S_OK; +} + +HRESULT +p3iodrv_ddr_set_outputs(HANDLE handle, const struct p3io_ddr_output *state) +{ + HRESULT hr; + uint8_t seq_cnt; + union p3io_req_any req; + union p3io_resp_any resp; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(state); + + seq_cnt = p3iodrv_ddr_get_and_update_seq_counter(); + + p3io_req_hdr_init( + &req.hdr, seq_cnt, P3IO_CMD_SET_OUTPUTS, sizeof(req.set_outputs)); + + memcpy(&req.set_outputs.outputs, state, sizeof(req.set_outputs.outputs)); + + // Always set by the game like this + req.set_outputs.unk_FF = 0xFF; + + hr = p3iodrv_device_transfer(handle, &req, &resp); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.set_outputs), &req); + + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} \ No newline at end of file diff --git a/src/main/p3iodrv/ddr.h b/src/main/p3iodrv/ddr.h new file mode 100644 index 0000000..2f8987d --- /dev/null +++ b/src/main/p3iodrv/ddr.h @@ -0,0 +1,115 @@ +#ifndef P3IODRV_DDR_H +#define P3IODRV_DDR_H + +#include +#include +#include + +#include "p3io/cmd.h" +#include "p3io/ddr.h" + +#define P3IODRV_DDR_VERSION_STR_LEN 4 + +/** + * Initialize the p3io device for operation. + * + * This call is also somewhat referred to as "set mode" in the game, though it + * is part of a larger setup routine. + * + * If the p3io device is not initialized, the menu buttons (left, right, start) + * on (a SD) cabinet will periodically blink. Once this function is called, this + * blinking stops indicating the p3io device is initialized. + * + * Other p3io commands might work (partially) even if this function is not + * called. However, it is recommended to call this first after every p3io + * device reset. + * + * @param handle A handle to an opened p3io device. + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT p3iodrv_ddr_init(HANDLE handle); + +/** + * Get the DDR specific version information from the p3io device. + * + * @param handle A handle to an opened p3io device. + * @param str Pointer to a buffer to write the version string to + * @param major Pointer to a uint32 to write the major version number to + * @param minor Pointer to a uint32 to write the minor version number to + * @param patch Poitner to a uint32 to ewrite the patch version number to + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT p3iodrv_ddr_get_version( + HANDLE handle, + char str[P3IODRV_DDR_VERSION_STR_LEN], + uint32_t *major, + uint32_t *minor, + uint32_t *patch); + +/** + * Enable/disable the device side watchdog. The watchdog will reset the device + * if it is not reset periodically. + * + * If you stop sending commands to the p3io, it will trigger after about 5-7 + * seconds of not receiving anything. You can notice the watchdog triggered when + * the menu buttons start to blink again which indicates the device is not + * initialized (anymore). + * + * The watchdog seems to expect that you periodically send the "get version" + * command to reset it and keep the p3io alive (p3iodrv_ddr_get_version). + * + * @param handle A handle to an opened p3io device. + * @param enable true to enable the watchdog, false to disable + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT p3iodrv_ddr_set_watchdog(HANDLE handle, bool enable); + +/** + * Get the current state of the dip switches from the p3io. + * + * @param handle A handle to an opened p3io device. + * @param state Pointer to a uint8 to write the resulting dip switch state to + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT p3iodrv_ddr_get_dipsw(HANDLE handle, uint8_t *state); + +/** + * Get the detected cabinet type from the p3io. + * + * @param handle A handle to an opened p3io device. + * @param type Pointer to a enum p3io_cab_type to write the resulting state to + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT p3iodrv_ddr_get_cab_type(HANDLE handle, enum p3io_cab_type *type); + +/** + * Get the detected video frequency of the monitor from the p3io. + * + * @param handle A handle to an opened p3io device. + * @param type Pointer to a enum p3io_video_freq to write the resulting state to + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT p3iodrv_ddr_get_video_freq(HANDLE handle, enum p3io_video_freq *freq); + +/** + * Get the current state of the JAMMA edge/input from the p3io. + * + * @param handle A handle to an opened p3io device. + * @param type Pointer to a struct p3io_ddr_jamma to write the resulting state + * to + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT p3iodrv_ddr_get_jamma(HANDLE handle, struct p3io_ddr_jamma *jamma); + +/** + * Set (some cabinet) light output state on the p3io. + * + * @param handle A handle to an opened p3io device. + * @param type Pointer to a struct p3io_ddr_output with the light output data to + * set on the p3io + * @result S_OK on success, any other HRESULT value on error + */ +HRESULT +p3iodrv_ddr_set_outputs(HANDLE handle, const struct p3io_ddr_output *state); + +#endif \ No newline at end of file diff --git a/src/main/p3iodrv/device.c b/src/main/p3iodrv/device.c new file mode 100644 index 0000000..dbbb04a --- /dev/null +++ b/src/main/p3iodrv/device.c @@ -0,0 +1,429 @@ +#define LOG_MODULE "p3iodrv-device" + +#include +#include +#include + +// clang-format off +// Don't format because the order is important here +#include +#include +// clang-format on + +#include "p3io/cmd.h" +#include "p3io/guid.h" +#include "p3io/ioctl.h" + +#include "p3iodrv/device.h" + +#include "util/log.h" +#include "util/str.h" + +#define P3IO_DEVICE_FILENMAME "\\p3io" + +static HRESULT _p3iodrv_write_file_iobuf(HANDLE handle, struct iobuf *buffer) +{ + HRESULT res; + DWORD bytes_processed; + + if (!WriteFile( + handle, buffer->bytes, buffer->pos, &bytes_processed, NULL)) { + res = HRESULT_FROM_WIN32(GetLastError()); + log_warning("WriteFile failed: %lX", res); + return res; + } + + log_misc("Written length: %ld", bytes_processed); + + if (bytes_processed != buffer->pos) { + log_warning( + "WriteFile didn't finish: %ld != %Id", + bytes_processed, + buffer->pos); + return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); + } + + return S_OK; +} + +static HRESULT _p3iodrv_read_file_iobuf(HANDLE handle, struct iobuf *buffer) +{ + HRESULT hr; + DWORD bytes_processed; + + if (!ReadFile( + handle, + buffer->bytes + buffer->pos, + buffer->nbytes - buffer->pos, + &bytes_processed, + NULL)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("ReadFile failed: %lX", hr); + return hr; + } + + log_misc("Read length: %ld", bytes_processed); + + buffer->pos += bytes_processed; + + return S_OK; +} + +static HRESULT +_p3iodrv_write_message(HANDLE handle, const union p3io_req_any *req) +{ + HRESULT hr; + uint8_t req_framed_bytes[P3IO_MAX_MESSAGE_SIZE]; + + struct iobuf req_deframed; + struct iobuf req_framed; + + memset(req_framed_bytes, 0, sizeof(req_framed_bytes)); + + // Used for logging the buffer, only + req_deframed.bytes = (uint8_t *) req; + req_deframed.nbytes = sizeof(union p3io_req_any); + req_deframed.pos = req->hdr.nbytes + 1; + + req_framed.bytes = req_framed_bytes; + req_framed.nbytes = sizeof(req_framed_bytes); + req_framed.pos = 0; + + iobuf_log(&req_deframed, "p3iodrv-device request deframed"); + + hr = p3io_frame_encode(&req_framed, req, req->hdr.nbytes + 1); + + if (FAILED(hr)) { + log_warning("Encoding request payload failed: %lX", hr); + return hr; + } + + iobuf_log(&req_framed, "p3iodrv-device request framed"); + + hr = _p3iodrv_write_file_iobuf(handle, &req_framed); + + if (FAILED(hr)) { + return hr; + } + + return S_OK; +} + +/** + * Read a full response message and not just a part of it. + * + * Because someone decided to implement a general "streaming bytes" solution + * with framing (ACIO) on top of a streaming byte solution with framing (USB), + * there is no guarantee that single reads are complete. This even applies to + * reads that are less then the max USB endpoint size. + * + * When polling to read data too fast in succession to writes, e.g. on a p3io + * command which first writes a request, then reads a response, the p3io + * hardware might not be fast enough to put all bytes of the full response into + * the device hardware side buffer. It appears that the device hardware/the + * firmware pushes out chunks of/single bytes instead of preparing full messages + * that are framed to the available USB buffer size. + * + * Therefore, messages can get fragmented and require multiple read calls to + * ensure the read buffer is fully flushed on the device side and received on + * the host. The following solution does exactly that it "reads until read + * everything". The following heuristics are applied to determine when we think + * a response message is fully received: + * + * 1. Read until you get parts of a header to determine how long the entire ACIO + * message is supposed to be. This must assume that the device will always put + * the correct amount of bytes onto the wire at some point. If it stopps doing + * that, there is no way to possibility to detect and take action on that. + * 2. Read until you get the complete ACIO paket based on the ACIO paket length + * field. + */ +static HRESULT +_p3iodrv_read_full_response_message(HANDLE handle, union p3io_resp_any *resp) +{ + HRESULT hr; + uint8_t resp_framed_bytes[P3IO_MAX_MESSAGE_SIZE]; + + struct iobuf resp_framed; + struct iobuf resp_framed_flip_read; + struct iobuf resp_deframed; + + memset(resp_framed_bytes, 0, sizeof(resp_framed_bytes)); + + resp_framed.bytes = resp_framed_bytes; + resp_framed.nbytes = sizeof(resp_framed_bytes); + resp_framed.pos = 0; + + log_misc("Receiving response"); + + // Read as long as we do not consider the message to be complete + while (true) { + hr = _p3iodrv_read_file_iobuf(handle, &resp_framed); + + if (FAILED(hr)) { + return hr; + } + + // Read at least 0xAA + header size, otherwise keep reading + if (resp_framed.pos < 1 + sizeof(struct p3io_hdr)) { + log_misc( + "Read truncated message, length %ld less than header, read " + "again", + (DWORD) resp_framed.pos); + continue; + } + + log_misc("Response received, length: %ld", (DWORD) resp_framed.pos); + + iobuf_log(&resp_framed, "p3iodrv-device response framed"); + + // Potential pre-mature deframing because we cannot be sure we already + // received the entire frame + + // Use a flipped buffer to read from the framed response + resp_framed_flip_read.bytes = resp_framed.bytes; + resp_framed_flip_read.nbytes = resp_framed.pos; + resp_framed_flip_read.pos = 0; + + // Init for de-framing + resp_deframed.bytes = resp->raw.data; + resp_deframed.nbytes = sizeof(resp->raw); + resp_deframed.pos = 0; + + hr = p3io_frame_decode( + &resp_deframed, (struct const_iobuf *) &resp_framed_flip_read); + + iobuf_log(&resp_deframed, "p3iodrv-device response deframed"); + + // Verify if the de-framed message is actually complete + // +1 for the length byte + if (resp->hdr.nbytes + 1 > resp_deframed.pos) { + log_warning( + "Truncated de-framed message, length %ld less than size on " + "header %ld, read again", + (DWORD) resp_deframed.pos, + (DWORD) resp->hdr.nbytes); + continue; + } + + // Eror check decoding only after verification that frame is completed + // Otherwise, this leads to failed decodings on truncated pakets that + // require another read + if (FAILED(hr)) { + log_warning("Decoding response payload failed: %lX", hr); + return hr; + } + + log_misc( + "Recieved complete response, length de-framed %ld", + (DWORD) resp_deframed.pos); + + break; + } + + return S_OK; +} + +HRESULT p3iodrv_device_scan(char path[MAX_PATH]) +{ + HRESULT res; + PSP_DEVICE_INTERFACE_DETAIL_DATA_A detail_data; + HDEVINFO devinfo; + DWORD required_size; + SP_DEVICE_INTERFACE_DATA interface_data; + DWORD err; + + detail_data = NULL; + + devinfo = SetupDiGetClassDevsA( + &p3io_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT); + + if (devinfo == INVALID_HANDLE_VALUE) { + // No device with GUID found + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + + if (!SetupDiEnumDeviceInterfaces( + devinfo, NULL, &p3io_guid, 0, &interface_data)) { + res = HRESULT_FROM_WIN32(GetLastError()); + log_warning("SetupDiEnumDeviceInterfaces failed: %lX", res); + SetupDiDestroyDeviceInfoList(devinfo); + return res; + } + + if (!SetupDiGetDeviceInterfaceDetailA( + devinfo, &interface_data, NULL, 0, &required_size, NULL)) { + err = GetLastError(); + res = HRESULT_FROM_WIN32(err); + + if (err != ERROR_INSUFFICIENT_BUFFER) { + log_warning("SetupDiGetDeviceInterfaceDetailA failed: %lX", res); + SetupDiDestroyDeviceInfoList(devinfo); + return res; + } + } + + detail_data = malloc(required_size); + detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A); + + if (!SetupDiGetDeviceInterfaceDetailA( + devinfo, &interface_data, detail_data, required_size, NULL, NULL)) { + res = HRESULT_FROM_WIN32(GetLastError()); + log_warning("SetupDiGetDeviceInterfaceDetailA failed: %lX", res); + free(detail_data); + SetupDiDestroyDeviceInfoList(devinfo); + return res; + } + + str_cpy(path, MAX_PATH, detail_data->DevicePath); + str_cat(path, MAX_PATH, P3IO_DEVICE_FILENMAME); + + free(detail_data); + SetupDiDestroyDeviceInfoList(devinfo); + + return S_OK; +} + +HRESULT p3iodrv_device_open(const char *path, HANDLE *handle) +{ + HRESULT res; + + log_assert(path); + log_assert(handle); + + *handle = CreateFileA( + path, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL); + + if (*handle == INVALID_HANDLE_VALUE) { + res = HRESULT_FROM_WIN32(GetLastError()); + log_warning("CreateFileA failed: %lX", res); + return res; + } + + return S_OK; +} + +HRESULT p3iodrv_device_close(HANDLE *handle) +{ + HRESULT res; + + log_assert(handle); + log_assert(*handle != INVALID_HANDLE_VALUE); + + if (CloseHandle(*handle) == FALSE) { + res = HRESULT_FROM_WIN32(GetLastError()); + log_warning("Closing failed: %lX", res); + return res; + } + + *handle = INVALID_HANDLE_VALUE; + + return S_OK; +} + +HRESULT p3iodrv_device_read_version( + HANDLE handle, char version[P3IODRV_VERSION_MAX_LEN]) +{ + HRESULT res; + DWORD bytes_returned; + + log_assert(handle != INVALID_HANDLE_VALUE); + + memset(version, 0, sizeof(char[P3IODRV_VERSION_MAX_LEN])); + + if (!DeviceIoControl( + handle, + P3IO_IOCTL_GET_VERSION, + NULL, + 0, + version, + sizeof(char[P3IODRV_VERSION_MAX_LEN]), + &bytes_returned, + NULL)) { + res = HRESULT_FROM_WIN32(GetLastError()); + log_warning("DeviceIoControl failed: %lX", res); + return res; + } + + if (bytes_returned < 1) { + log_warning( + "DeviceIoControl returned size invalid: %ld", bytes_returned); + return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); + } + + return S_OK; +} + +HRESULT +p3iodrv_device_read_jamma(HANDLE handle, uint32_t jamma[P3IO_DRV_JAMMA_MAX_LEN]) +{ + HRESULT res; + DWORD bytes_returned; + + log_assert(handle != INVALID_HANDLE_VALUE); + + if (!DeviceIoControl( + handle, + P3IO_IOCTL_READ_JAMMA, + NULL, + 0, + jamma, + sizeof(uint32_t[P3IO_DRV_JAMMA_MAX_LEN]), + &bytes_returned, + NULL)) { + res = HRESULT_FROM_WIN32(GetLastError()); + log_warning("ioctl read jamma failed: %lX", res); + return res; + } + + if (bytes_returned != sizeof(uint32_t[P3IO_DRV_JAMMA_MAX_LEN])) { + log_warning( + "DeviceIoControl returned size invalid: %ld", bytes_returned); + return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); + } + + return S_OK; +} + +HRESULT p3iodrv_device_transfer( + HANDLE handle, const union p3io_req_any *req, union p3io_resp_any *resp) +{ + HRESULT hr; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(req); + log_assert(resp); + + log_misc( + "TRANSFER START, req nbytes %d, seq_no %d, cmd 0x%X", + req->hdr.nbytes, + req->hdr.seq_no, + req->hdr.cmd); + + hr = _p3iodrv_write_message(handle, req); + + if (FAILED(hr)) { + return hr; + } + + hr = _p3iodrv_read_full_response_message(handle, resp); + + if (FAILED(hr)) { + return hr; + } + + log_misc( + "TRANSFER FINISHED, resp nbytes %d, seq_no %d, cmd 0x%X", + resp->hdr.nbytes, + resp->hdr.seq_no, + resp->hdr.cmd); + + return S_OK; +} \ No newline at end of file diff --git a/src/main/p3iodrv/device.h b/src/main/p3iodrv/device.h new file mode 100644 index 0000000..f8ea144 --- /dev/null +++ b/src/main/p3iodrv/device.h @@ -0,0 +1,105 @@ +#ifndef P3IODRV_DEVICE_H +#define P3IODRV_DEVICE_H + +#include +#include +#include + +#include "p3io/cmd.h" + +#include "util/iobuf.h" + +#define P3IODRV_VERSION_MAX_LEN 128 +#define P3IO_DRV_JAMMA_MAX_LEN 3 + +/** + * Scan for a connected p3io device. Currently, this does not support multiple + * p3io devices and only returns the "first one" found. + * + * @param path Buffer of size MAX_PATH this function writes the full device path + * to when a device was found + * @return S_OK when a device was found, ERROR_FILE_NOT_FOUND if none was found + * or any other HRESULT error on any errors while scanning occured. + */ +HRESULT p3iodrv_device_scan(char path[MAX_PATH]); + +/** + * Open a p3io device by the given device path. + * + * @param path Pointer to a buffer with the full device path of the p3io device + * to open + * @param handle Pointer to a handle variable to return the opened device handle + * to when opening was successful + * @return S_OK if opening was successful, ERROR_FILE_NOT_FOUND if the device + * with the given path was not found, or any other HRESULT error when + * trying to open the device. + */ +HRESULT p3iodrv_device_open(const char *path, HANDLE *handle); + +/** + * Close an open p3io device by its handle + * + * @param handle Pointer to the handle to close. The variable will be set to + * INVALID_HANDLE_VALUE on success + * @return S_OK if closing was successful, or any other HRESULT value on errors + */ +HRESULT p3iodrv_device_close(HANDLE *handle); + +/** + * Read the version of the p3io device. + * + * This call is not guaranteed to be supported with all p3io drivers, e.g. we + * know this is supported by the 64-bit p3io driver but apparently not by the + * 32-bit one. + * + * @param handle A valid handle to an opened p3io device + * @param version Pointer to a buffer to write the resulting version string to + * @return S_OK if the operation was successful, or any other HRESULT value on + * errors + */ +HRESULT p3iodrv_device_read_version( + HANDLE handle, char version[P3IODRV_VERSION_MAX_LEN]); + +/** + * Read input data from the JAMMA edge. As this is supported by different games + * and a core functionality of the p3io, the data is not further specified + * here. This function is optimised for low latency inputs by continously + * polling it on the host. + * + * @param handle A valid handle to an opened p3io device + * @param jamma Buffer to write the read input data to on success + * @return S_OK if the operation was successful, or any other HRESULT value on + * errors + */ +HRESULT p3iodrv_device_read_jamma( + HANDLE handle, uint32_t jamma[P3IO_DRV_JAMMA_MAX_LEN]); + +/** + * Execute a generic data transfer. + * + * This is _the_ generic messaging interface of the p3io to exchange data with + * the device. Depending on the type of p3io and firmware, different command + * codes support different functionality. + * + * Note that this interface is not optimized for low latency data transfers + * and is a fully blocking "endpoint". The caller of this function is blocked + * entirely until the host request is fully processed and the device response is + * fully received. + * + * Implementation detail: It implements (sort of?) the ACIO protocol on top of + * the USB as a transport layer. Encoding and framing is being handling by this + * function transparently, so the caller does not have to worry about that + * anymore. + * + * @param handle A valid handle to an opened p3io device + * @param req Pointer to a buffer/struct with a p3io formatted request + * @param resp Pointer to a buffer to receive the device's response to. Ensure + * this is large enough for the expceted response matching the + * issued type of request. + * @return S_OK if the transfer was successful and a response was received and + * written to the buffer, or any other HRESULT value on errors + */ +HRESULT p3iodrv_device_transfer( + HANDLE handle, const union p3io_req_any *req, union p3io_resp_any *resp); + +#endif \ No newline at end of file -- 2.54.0 From 5ed972c16c27b868c9823677f14f34aa1a009dff Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:14:47 +0200 Subject: [PATCH 11/20] feat(extio): Add EXTIO command data structures Reverse engineered from a real EXTIO device, open-io project as further reference --- src/main/extio/Module.mk | 4 ++++ src/main/extio/cmd.c | 16 +++++++++++++ src/main/extio/cmd.h | 49 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 src/main/extio/Module.mk create mode 100644 src/main/extio/cmd.c create mode 100644 src/main/extio/cmd.h diff --git a/src/main/extio/Module.mk b/src/main/extio/Module.mk new file mode 100644 index 0000000..9845d60 --- /dev/null +++ b/src/main/extio/Module.mk @@ -0,0 +1,4 @@ +libs += extio + +src_extio := \ + cmd.c \ diff --git a/src/main/extio/cmd.c b/src/main/extio/cmd.c new file mode 100644 index 0000000..e392fae --- /dev/null +++ b/src/main/extio/cmd.c @@ -0,0 +1,16 @@ +#include "cmd.h" + +uint8_t extio_cmd_checksum(const struct extio_cmd_write *write) +{ + const uint8_t *data; + uint32_t checksum; + + data = (const uint8_t *) write; + + checksum = 0; + checksum += data[0]; + checksum += data[1]; + checksum += data[2]; + + return (uint8_t) (checksum & 0x7F); +} \ No newline at end of file diff --git a/src/main/extio/cmd.h b/src/main/extio/cmd.h new file mode 100644 index 0000000..e764705 --- /dev/null +++ b/src/main/extio/cmd.h @@ -0,0 +1,49 @@ +#ifndef EXTIO_CMD_H +#define EXTIO_CMD_H + +#include + +#define EXTIO_PAD_LIGHT_MAX_PLAYERS 2 + +enum extio_status { + EXTIO_STATUS_OK = 0x11, +}; + +#pragma pack(push, 1) + +struct extio_cmd_pad_light { + uint8_t unknown : 3; + uint8_t right : 1; + uint8_t left : 1; + uint8_t down : 1; + uint8_t up : 1; + uint8_t unknown_80 : 1; +}; + +struct extio_cmd_write { + struct extio_cmd_pad_light pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + + uint8_t unknown3 : 3; + uint8_t sensor_read_mode : 3; + uint8_t neons : 1; + uint8_t unknown4 : 1; + + uint8_t checksum; +}; + +struct extio_cmd_read { + uint8_t status; +}; + +_Static_assert( + sizeof(struct extio_cmd_write) == 4, + "struct extio_cmd_write is the wrong size"); +_Static_assert( + sizeof(struct extio_cmd_read) == 1, + "struct extio_cmd_read is the wrong size"); + +#pragma pack(pop) + +uint8_t extio_cmd_checksum(const struct extio_cmd_write *write); + +#endif \ No newline at end of file -- 2.54.0 From 9f8a0dae3a530c2674d7b9130a2f75c4b025c0ac Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:15:40 +0200 Subject: [PATCH 12/20] feat(extiodrv): Add (DDR) EXTIO driver Initial version that supports the most important features to operate a DDR SD cabinet with all inputs and outputs working. Note that the individual sensor cycling modes are currently broken. The driver will always set the sensor read mode to all for now. --- src/main/extiodrv/Module.mk | 5 + src/main/extiodrv/device.c | 182 ++++++++++++++++++++++++++++++++++++ src/main/extiodrv/device.h | 57 +++++++++++ src/main/extiodrv/extio.c | 110 ++++++++++++++++++++++ src/main/extiodrv/extio.h | 45 +++++++++ 5 files changed, 399 insertions(+) create mode 100644 src/main/extiodrv/Module.mk create mode 100644 src/main/extiodrv/device.c create mode 100644 src/main/extiodrv/device.h create mode 100644 src/main/extiodrv/extio.c create mode 100644 src/main/extiodrv/extio.h diff --git a/src/main/extiodrv/Module.mk b/src/main/extiodrv/Module.mk new file mode 100644 index 0000000..5b4eacf --- /dev/null +++ b/src/main/extiodrv/Module.mk @@ -0,0 +1,5 @@ +libs += extiodrv + +src_extiodrv := \ + device.c \ + extio.c \ diff --git a/src/main/extiodrv/device.c b/src/main/extiodrv/device.c new file mode 100644 index 0000000..ba14fa7 --- /dev/null +++ b/src/main/extiodrv/device.c @@ -0,0 +1,182 @@ +#define LOG_MODULE "extiodrv-device" + +#include "device.h" + +#include "util/log.h" + +HRESULT extiodrv_device_open(const char *port, HANDLE *handle) +{ + HRESULT hr; + COMMTIMEOUTS ct; + DCB dcb; + + log_assert(port); + log_assert(handle); + + log_info("Opening extio on %s", port); + + *handle = CreateFile( + port, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + OPEN_EXISTING, + FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL, + NULL); + + if (*handle == INVALID_HANDLE_VALUE) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("Failed to open %s: %lX", port, hr); + + goto early_fail; + } + + if (!SetCommMask(*handle, EV_RXCHAR)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("SetCommMask failed: %lX", hr); + + goto fail; + } + + if (!SetupComm(*handle, 0x4000u, 0x4000u)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("SetupComm failed: %lX", hr); + + goto fail; + } + + if (!PurgeComm( + *handle, + PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("PurgeComm failed: %lX", hr); + + goto fail; + } + + ct.ReadTotalTimeoutConstant = 0; + ct.WriteTotalTimeoutConstant = 0; + ct.ReadIntervalTimeout = -1; + ct.ReadTotalTimeoutMultiplier = 10; + ct.WriteTotalTimeoutMultiplier = 100; + + if (!SetCommTimeouts(*handle, &ct)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("SetCommTimeouts failed: %lX", hr); + + goto fail; + } + + dcb.DCBlength = sizeof(dcb); + + if (!GetCommState(*handle, &dcb)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("GetCommState failed: %lX", hr); + + goto fail; + } + + dcb.BaudRate = 38400; + dcb.fBinary = TRUE; + dcb.fParity = FALSE; + dcb.fDtrControl = DTR_CONTROL_ENABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fOutX = FALSE; + dcb.fInX = FALSE; + dcb.fErrorChar = FALSE; + dcb.fNull = FALSE; + dcb.fRtsControl = RTS_CONTROL_ENABLE; + dcb.fAbortOnError = FALSE; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + dcb.XonChar = 17; + dcb.XoffChar = 19; + dcb.XonLim = 100; + dcb.XoffLim = 100; + + if (!SetCommState(*handle, &dcb)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("SetCommState failed: %lX", hr); + + goto fail; + } + + log_info("[%p] Opened extio device on %s", *handle, port); + + return S_OK; + +fail: + CloseHandle(*handle); + *handle = INVALID_HANDLE_VALUE; + +early_fail: + return hr; +} + +HRESULT extiodrv_device_close(HANDLE *handle) +{ + HRESULT hr; + + log_assert(handle); + + if (CloseHandle(*handle) == FALSE) { + hr = HRESULT_FROM_WIN32(GetLastError()); + return hr; + } + + *handle = INVALID_HANDLE_VALUE; + + return S_OK; +} + +HRESULT extiodrv_device_read( + HANDLE handle, void *bytes, size_t nbytes, size_t *read_bytes) +{ + HRESULT hr; + DWORD dw_read_bytes; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(bytes); + log_assert(read_bytes); + + if (!ClearCommError(handle, NULL, NULL)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("[%p] ClearCommError failed: %lX", handle, hr); + + return hr; + } + + if (!ReadFile(handle, bytes, nbytes, &dw_read_bytes, NULL)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("[%p] ReadFile failed: %lX", handle, hr); + + return hr; + } + + *read_bytes = dw_read_bytes; + + return S_OK; +} + +HRESULT extiodrv_device_write( + HANDLE handle, const void *bytes, size_t nbytes, size_t *written_bytes) +{ + HRESULT hr; + DWORD dw_written_bytes; + + log_assert(handle != INVALID_HANDLE_VALUE); + log_assert(bytes); + log_assert(written_bytes); + + if (!WriteFile(handle, bytes, nbytes, &dw_written_bytes, NULL)) { + hr = HRESULT_FROM_WIN32(GetLastError()); + log_warning("[%p] WriteFile failed: %lX", handle, hr); + + return hr; + } + + *written_bytes = dw_written_bytes; + + return S_OK; +} \ No newline at end of file diff --git a/src/main/extiodrv/device.h b/src/main/extiodrv/device.h new file mode 100644 index 0000000..4121295 --- /dev/null +++ b/src/main/extiodrv/device.h @@ -0,0 +1,57 @@ +#ifndef EXTIODRV_DEVICE_H +#define EXTIODRV_DEVICE_H + +#include + +#include +#include + +/** + * Open an EXTIO device connected to a com port. + * + * @param port Com port the device is connected to, e.g. "COM1" + * @param handle Pointer to a HANDLE variable to return the handle to when + * succesfully opened + * @return S_OK on success with the handle returned in handle, other HRESULT + * value on error. + */ +HRESULT extiodrv_device_open(const char *port, HANDLE *handle); + +/** + * Close an opened EXTIO device + * + * @param handle Pointer to a handle variable that stores a valid and opened + * EXTIO handle. + * @return S_OK on success and the handle value is set to INVALID_HANDLE_VALUE, + * other HRESULT value on error. + */ +HRESULT extiodrv_device_close(HANDLE *handle); + +/** + * Read data from the EXTIO device. This call blocks until data is available. + * + * @param handle A valid handle to an opened EXTIO device. + * @param bytes Pointer to a buffer to read the data into + * @param nbytes (Maximum) number of bytes to read + * @param read_bytes Pointer to a variable to store the resulting read size to + * @return S_OK on success with read_bytes populated with the number of bytes + * read, other HRESULT value on error. + */ +HRESULT extiodrv_device_read( + HANDLE handle, void *bytes, size_t nbytes, size_t *read_bytes); + +/** + * WRite data to the EXTIO device. This call blocks until the data is written. + * + * @param handle A valid handle to an opened EXTIO device. + * @param bytes Pointer to a buffer with data to write to the device + * @param nbytes (Maximum) number of bytes to write + * @param written_bytes Pointer to a variable to store the resulting write size + * to + * @return S_OK on success with written_bytes populated with the number of bytes + * written, other HRESULT value on error. + */ +HRESULT extiodrv_device_write( + HANDLE handle, const void *bytes, size_t nbytes, size_t *written_bytes); + +#endif \ No newline at end of file diff --git a/src/main/extiodrv/extio.c b/src/main/extiodrv/extio.c new file mode 100644 index 0000000..8c50de2 --- /dev/null +++ b/src/main/extiodrv/extio.c @@ -0,0 +1,110 @@ +#define LOG_MODULE "extiodrv-extio" + +#include "extio.h" + +#include "util/log.h" + +// static uint8_t _extiodrv_extio_sensor_read_mode_map[5] = { +// 1, // all +// 2, // up +// 3, // down +// 4, // left +// 5, // right +// }; + +HRESULT extiodrv_extio_transfer( + HANDLE handle, + enum extiodrv_extio_sensor_read_mode sensor_read_mode, + const struct extiodrv_extio_pad_lights + pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS], + bool neons) +{ + HRESULT hr; + struct extio_cmd_write write; + struct extio_cmd_read read; + size_t bytes_written; + size_t bytes_read; + const uint8_t *raw; + + log_assert(handle != INVALID_HANDLE_VALUE); + + memset(&write, 0, sizeof(write)); + + // TODO this doesn't seem to work, yet + // write.sensor_read_mode = + // 0;//_extiodrv_extio_sensor_read_mode_map[sensor_read_mode]; 0 = all 1 = + // mess? 2 = mess? 3 = mess? 4 = mess? 5 = mess? 6 = right sensor only 7 = + // right sensor only 8 = right sensor only + write.sensor_read_mode = 0; + + for (uint8_t i = 0; i < EXTIO_PAD_LIGHT_MAX_PLAYERS; i++) { + write.pad_lights[i].up = pad_lights[i].up ? 1 : 0; + write.pad_lights[i].down = pad_lights[i].down ? 1 : 0; + write.pad_lights[i].left = pad_lights[i].left ? 1 : 0; + write.pad_lights[i].right = pad_lights[i].right ? 1 : 0; + } + + write.neons = neons ? 1 : 0; + + // This MUST be set but only on the p1 packet + // Not setting it or also setting it on the p2 packet results in the EXTIO + // not responding at all + write.pad_lights[0].unknown_80 = 1; + + write.checksum = extio_cmd_checksum(&write); + + raw = (uint8_t *) &write; + + log_misc("Raw write paket: %X %X %X %X", raw[0], raw[1], raw[2], raw[3]); + + hr = extiodrv_device_write(handle, &write, sizeof(write), &bytes_written); + + if (FAILED(hr)) { + log_warning("Writing extio device failed"); + return hr; + } + + if (bytes_written != sizeof(write)) { + log_warning( + "Writing extio device failed, expected bytes %d != actual bytes %d", + (uint32_t) sizeof(write), + (uint32_t) bytes_written); + return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); + } + + // Read loop to keep reading until a full message is read + // There are various occasions, especially if polling from the host side is + // faster than the device can put the response to the wire. this can result + // in empty reads + while (true) { + hr = extiodrv_device_read(handle, &read, sizeof(read), &bytes_read); + + if (FAILED(hr)) { + log_warning("Reading extio device failed"); + return hr; + } + + if (bytes_read < sizeof(read)) { + log_misc("Empty read, retry read"); + continue; + } + + if (bytes_read != sizeof(read)) { + log_warning( + "Reading extio device failed, expected bytes %d != actual " + "bytes %d", + (uint32_t) sizeof(read), + (uint32_t) bytes_read); + return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH); + } + + break; + } + + if (read.status != EXTIO_STATUS_OK) { + log_warning("Status not ok: 0x%X", read.status); + return HRESULT_FROM_WIN32(ERROR_GEN_FAILURE); + } + + return S_OK; +} \ No newline at end of file diff --git a/src/main/extiodrv/extio.h b/src/main/extiodrv/extio.h new file mode 100644 index 0000000..bc48c82 --- /dev/null +++ b/src/main/extiodrv/extio.h @@ -0,0 +1,45 @@ +#ifndef EXTIODRV_EXTIO_H +#define EXTIODRV_EXTIO_H + +#include + +#include + +#include "extio/cmd.h" + +#include "device.h" + +enum extiodrv_extio_sensor_read_mode { + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL = 0, + EXTIODRV_EXTIO_SENSOR_READ_MODE_UP = 1, + EXTIODRV_EXTIO_SENSOR_READ_MODE_DOWN = 2, + EXTIODRV_EXTIO_SENSOR_READ_MODE_LEFT = 3, + EXTIODRV_EXTIO_SENSOR_READ_MODE_RIGHT = 4 +}; + +struct extiodrv_extio_pad_lights { + bool up; + bool down; + bool left; + bool right; +}; + +/** + * Execute a data transfer (write + read) to the EXTIO device + * + * @param handle A valid and opened handle to an EXTIO device + * @param sensor_read_mode The sensor read mode to set on the EXTIO device. + * This only needs to be set once that input reads return the configured + * sensor read setting. + * @param pad_lights Pad light ouptut state to set + * @param neons Neon output state to set + * @return S_OK on success, other HRESULT value on error + */ +HRESULT extiodrv_extio_transfer( + HANDLE handle, + enum extiodrv_extio_sensor_read_mode sensor_read_mode, + const struct extiodrv_extio_pad_lights + pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS], + bool neons); + +#endif \ No newline at end of file -- 2.54.0 From 52ef70fe1d25213c72142f24f2f27546b0420d9b Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:16:48 +0200 Subject: [PATCH 13/20] feat(extiotest): Add testing tool for real EXTIO devices A small but helpful tool to run a quick functional test of a real EXTIO device connected to the target machine. --- src/main/extiotest/Module.mk | 9 ++++++ src/main/extiotest/main.c | 55 ++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 src/main/extiotest/Module.mk create mode 100644 src/main/extiotest/main.c diff --git a/src/main/extiotest/Module.mk b/src/main/extiotest/Module.mk new file mode 100644 index 0000000..73ec1b0 --- /dev/null +++ b/src/main/extiotest/Module.mk @@ -0,0 +1,9 @@ +exes += extiotest \ + +libs_extiotest := \ + extiodrv \ + extio \ + util \ + +src_extiotest := \ + main.c \ diff --git a/src/main/extiotest/main.c b/src/main/extiotest/main.c new file mode 100644 index 0000000..3be152b --- /dev/null +++ b/src/main/extiotest/main.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include + +#include + +#include "extiodrv/extio.h" + +#include "util/log.h" + +int main(int argc, char **argv) +{ + HRESULT hr; + const char *port; + HANDLE handle; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + + if (argc < 2) { + fprintf(stderr, "Basic functional test of EXTIO\n"); + fprintf(stderr, "Usage: %s COM_PORT\n", argv[0]); + fprintf(stderr, " COM_PORT: For example COM1\n"); + } + + log_to_writer(log_writer_stderr, NULL); + log_set_level(LOG_LEVEL_MISC); + + port = argv[1]; + + hr = extiodrv_device_open(port, &handle); + + if (FAILED(hr)) { + fprintf(stderr, "Opening extio at port '%s' failed: %lX\n", port, hr); + return -1; + } + + memset(&pad_lights, 0, sizeof(pad_lights)); + + hr = extiodrv_extio_transfer( + handle, EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, pad_lights, false); + + if (FAILED(hr)) { + fprintf(stderr, "Extio transfer failed: %lX\n", hr); + return -1; + } + + hr = extiodrv_device_close(&handle); + + if (FAILED(hr)) { + fprintf(stderr, "Closing extio failed: %lX", hr); + return -1; + } + + return 0; +} \ No newline at end of file -- 2.54.0 From 1de4da49e98b0276498bef23420c07c8ddb26a11 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:17:58 +0200 Subject: [PATCH 14/20] chore(module.mk): Add extio related sub-projects --- Module.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Module.mk b/Module.mk index c87fd20..36339ea 100644 --- a/Module.mk +++ b/Module.mk @@ -111,6 +111,9 @@ include src/main/dinput/Module.mk include src/main/eamio-icca/Module.mk include src/main/eamio/Module.mk include src/main/eamiotest/Module.mk +include src/main/extio/Module.mk +include src/main/extiodrv/Module.mk +include src/main/extiotest/Module.mk include src/main/ezusb-emu/Module.mk include src/main/ezusb-iidx-16seg-emu/Module.mk include src/main/ezusb-iidx-emu/Module.mk -- 2.54.0 From 93f564d958c287a9445b0b0adf09bfbdd4a7b2ac Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:18:22 +0200 Subject: [PATCH 15/20] feat(p3io-ddr-tool): CLI tool to test and debug a real DDR P3IO Extensive tool with subcommands to trigger the various P3IO commands and run the P3IO + EXTIO DDR hardware combo with a hardware test menu. --- Module.mk | 1 + src/main/p3io-ddr-tool/Module.mk | 16 + src/main/p3io-ddr-tool/main.c | 774 +++++++++++++++++++++++++++++ src/main/p3io-ddr-tool/mode-test.c | 406 +++++++++++++++ src/main/p3io-ddr-tool/mode-test.h | 11 + 5 files changed, 1208 insertions(+) create mode 100644 src/main/p3io-ddr-tool/Module.mk create mode 100644 src/main/p3io-ddr-tool/main.c create mode 100644 src/main/p3io-ddr-tool/mode-test.c create mode 100644 src/main/p3io-ddr-tool/mode-test.h diff --git a/Module.mk b/Module.mk index 36339ea..31741fa 100644 --- a/Module.mk +++ b/Module.mk @@ -171,6 +171,7 @@ include src/main/mm/Module.mk include src/main/p3io/Module.mk include src/main/p3iodrv/Module.mk include src/main/p3ioemu/Module.mk +include src/main/p3io-ddr-tool/Module.mk include src/main/p4iodrv/Module.mk include src/main/p4ioemu/Module.mk include src/main/popnhook-util/Module.mk diff --git a/src/main/p3io-ddr-tool/Module.mk b/src/main/p3io-ddr-tool/Module.mk new file mode 100644 index 0000000..2c76b42 --- /dev/null +++ b/src/main/p3io-ddr-tool/Module.mk @@ -0,0 +1,16 @@ +exes += p3io-ddr-tool \ + +ldflags_p3io-ddr-tool := \ + -lsetupapi \ + +libs_p3io-ddr-tool := \ + extiodrv \ + extio \ + p3iodrv \ + p3io \ + hook \ + util \ + +src_p3io-ddr-tool := \ + main.c \ + mode-test.c \ diff --git a/src/main/p3io-ddr-tool/main.c b/src/main/p3io-ddr-tool/main.c new file mode 100644 index 0000000..939e1e7 --- /dev/null +++ b/src/main/p3io-ddr-tool/main.c @@ -0,0 +1,774 @@ +#define LOG_MODULE "p3io-ddr-tool" + +#include + +#include +#include +#include +#include + +#include "extiodrv/extio.h" + +#include "p3io/ddr.h" +#include "p3iodrv/ddr.h" +#include "p3iodrv/device.h" + +#include "util/log.h" +#include "util/thread.h" + +#include "mode-test.h" + +enum mode { + MODE_INVALID = 0, + MODE_SCAN = 1, + MODE_VERP3IO = 2, + MODE_INIT = 3, + MODE_VERDDR = 4, + MODE_WATCHDOGON = 5, + MODE_WATCHDOGOFF = 6, + MODE_CABTYPE = 7, + MODE_DIPSW = 8, + MODE_VIDEOFREQ = 9, + MODE_TEST = 10, + MODE_LIGHTOFF = 11, + MODE_POLL = 12, + MODE_SENSORES = 13, + MODE_TOTAL_COUNT = 14, +}; + +typedef bool (*mode_proc)(HANDLE handle); + +static bool _mode_invalid(HANDLE handle); +static bool _mode_scan(HANDLE handle); +static bool _mode_verp3io(HANDLE handle); +static bool _mode_init(HANDLE handle); +static bool _mode_verddr(HANDLE handle); +static bool _mode_watchdogon(HANDLE handle); +static bool _mode_watchdogoff(HANDLE handle); +static bool _mode_cabtype(HANDLE handle); +static bool _mode_dipsw(HANDLE handle); +static bool _mode_videofreq(HANDLE handle); +static bool _mode_test(HANDLE handle); +static bool _mode_lightoff(HANDLE handle); +static bool _mode_poll(HANDLE handle); +static bool _mode_sensores(HANDLE handle); + +static mode_proc _mode_procs[MODE_TOTAL_COUNT] = { + _mode_invalid, + _mode_scan, + _mode_verp3io, + _mode_init, + _mode_verddr, + _mode_watchdogon, + _mode_watchdogoff, + _mode_cabtype, + _mode_dipsw, + _mode_videofreq, + _mode_test, + _mode_lightoff, + _mode_poll, + _mode_sensores, +}; + +static enum log_level _log_level = LOG_LEVEL_FATAL; +static bool _extio_enabled = true; +static const char *_p3io_device_path = ""; +static const char *_extio_com_port = "COM1"; +static enum mode _mode = MODE_INVALID; + +static bool _scan_and_open(HANDLE *handle) +{ + HRESULT hr; + char path[MAX_PATH]; + + log_info("Scanning for p3io..."); + + hr = p3iodrv_device_scan(path); + + if (FAILED(hr)) { + log_warning("Cannot find a connected p3io: %lX", hr); + return false; + } + + log_info("Opening p3io: %s", path); + + hr = p3iodrv_device_open(path, handle); + + if (FAILED(hr)) { + log_warning("Opening p3io failed: %lX", hr); + return false; + } + + return true; +} + +static bool _close(HANDLE handle) +{ + HRESULT hr; + + log_info("Closing p3io..."); + + hr = p3iodrv_device_close(handle); + + if (FAILED(hr)) { + log_warning("Closing p3io failed: %lX", hr); + return false; + } + + return true; +} + +static bool _process_cmd_args(int argc, char **argv) +{ + char *mode; + + if (argc < 2) { + fprintf(stderr, "DDR P3IO and EXTIO CLI testing tool\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Usage: [ARG ...] MODE\n"); + fprintf(stderr, "Arguments:\n"); + fprintf( + stderr, + " -v Enable additional logging, log level 'warning'\n"); + fprintf( + stderr, + " -vv Enable additional logging, log level 'info'\n"); + fprintf( + stderr, + " -vvv Enable additional logging, log level 'misc' (very " + "verbose)\n"); + fprintf( + stderr, + " -noextio Run the tool without an EXTIO. This also impacts " + "various functionality, e.g. sensor polling, light outputs\n"); + fprintf( + stderr, + " -p3io Explicit device path pointing to a P3IO device to " + "open. If not provided, the device will be automatically scanned " + "for.\n"); + fprintf( + stderr, + " -extio Explicitly set the COM port the EXTIO is connected " + "to, e.g. COM4. If not provided COM1 is used.\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Modes:\n"); + fprintf( + stderr, + " scan: Scan for a connected P3IO device. Outputs the full device " + "path to stdout if found.\n"); + fprintf( + stderr, + " verp3io: Get and print the P3IO version information to " + "stdout.\n"); + fprintf(stderr, " init: Initialize the P3IO (\"DDR mode\").\n"); + fprintf( + stderr, + " verddr: Get and print the DDR P3IO specific information to " + "stdout.\n"); + fprintf(stderr, " watchdogon: Turn the watchdog on.\n"); + fprintf(stderr, " watchdogoff: Turn the watchdog off.\n"); + fprintf( + stderr, + " cabtype: Get and print the cabinet type information to " + "stdout.\n"); + fprintf( + stderr, " dipsw: Get and print the DIP switch state to stdout.\n"); + fprintf( + stderr, + " videofreq: Get and print the detected video frequency to " + "stdout.\n"); + fprintf( + stderr, + " test: Run interactive test mode, executes a polling read/write " + "loop to drive the IO and displays the current input states.\n"); + fprintf(stderr, " lightoff: Turn all the lights off\n"); + fprintf( + stderr, + " poll: Run a simple polling loop that polls data from the JAMMA " + "endpoint with input state output.\n"); + fprintf( + stderr, + " sensores: Run cycling individual sensores in a simple polling " + "loop with input state ouptut.\n"); + + return false; + } + + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "-v")) { + _log_level = LOG_LEVEL_WARNING; + } else if (!strcmp(argv[i], "-vv")) { + _log_level = LOG_LEVEL_INFO; + } else if (!strcmp(argv[i], "-vvv")) { + _log_level = LOG_LEVEL_MISC; + } else if (!strcmp(argv[i], "-noextio")) { + _extio_enabled = false; + } else if (!strcmp(argv[i], "-p3io")) { + if (i + 1 < argc) { + _p3io_device_path = argv[++i]; + } else { + fprintf(stderr, "Missing value for -p3io argument\n"); + } + } else if (!strcmp(argv[i], "-extio")) { + if (i + 1 < argc) { + _extio_com_port = argv[++i]; + } else { + fprintf(stderr, "Missing value for -extio argument\n"); + } + } + } + + mode = argv[argc - 1]; + + if (!strcmp(mode, "scan")) { + _mode = MODE_SCAN; + } else if (!strcmp(mode, "verp3io")) { + _mode = MODE_VERP3IO; + } else if (!strcmp(mode, "init")) { + _mode = MODE_INIT; + } else if (!strcmp(mode, "verddr")) { + _mode = MODE_VERDDR; + } else if (!strcmp(mode, "watchdogon")) { + _mode = MODE_WATCHDOGON; + } else if (!strcmp(mode, "watchdogoff")) { + _mode = MODE_WATCHDOGOFF; + } else if (!strcmp(mode, "cabtype")) { + _mode = MODE_CABTYPE; + } else if (!strcmp(mode, "dipsw")) { + _mode = MODE_DIPSW; + } else if (!strcmp(mode, "videofreq")) { + _mode = MODE_VIDEOFREQ; + } else if (!strcmp(mode, "test")) { + _mode = MODE_TEST; + } else if (!strcmp(mode, "lightoff")) { + _mode = MODE_LIGHTOFF; + } else if (!strcmp(mode, "poll")) { + _mode = MODE_POLL; + } else if (!strcmp(mode, "sensores")) { + _mode = MODE_SENSORES; + } else { + fprintf(stderr, "Invalid mode '%s'", mode); + return false; + } + + return true; +} + +static void _init_logging() +{ + log_to_writer(log_writer_stderr, NULL); + log_set_level(_log_level); +} + +static bool _mode_invalid(HANDLE handle) +{ + fprintf(stderr, "Invalid mode"); + return false; +} + +static bool _mode_scan(HANDLE handle) +{ + HRESULT hr; + char path[MAX_PATH]; + + log_info("Scanning for p3io..."); + + hr = p3iodrv_device_scan(path); + + if (FAILED(hr)) { + log_warning("Cannot find a connected p3io: %lX", hr); + return false; + } + + log_info("Found p3io: %s", path); + + printf("%s\n", path); + + return true; +} + +static bool _mode_verp3io(HANDLE handle) +{ + HRESULT hr; + char version[P3IODRV_VERSION_MAX_LEN]; + + memset(version, 0, P3IODRV_VERSION_MAX_LEN); + + hr = p3iodrv_device_read_version(handle, version); + + if (FAILED(hr)) { + log_warning("Getting version failed: %lX", hr); + return _close(handle); + } + + log_info("Version (P3IO): %s", version); + + printf("%s\n", version); + + return true; +} + +static bool _mode_init(HANDLE handle) +{ + HRESULT hr; + + hr = p3iodrv_ddr_init(handle); + + if (FAILED(hr)) { + log_warning("Init failed: %lX", hr); + return false; + } + + log_info("Initialized"); + + return true; +} + +static bool _mode_verddr(HANDLE handle) +{ + HRESULT hr; + char str[4]; + uint32_t major; + uint32_t minor; + uint32_t patch; + + hr = p3iodrv_ddr_get_version(handle, str, &major, &minor, &patch); + + if (FAILED(hr)) { + log_warning("Getting DDR version failed: %lX", hr); + return false; + } + + log_info("Version (DDR): %s %d.%d.%d", str, major, minor, patch); + + printf("%s %d.%d.%d\n", str, major, minor, patch); + + return true; +} + +static bool _mode_watchdogon(HANDLE handle) +{ + HRESULT hr; + + hr = p3iodrv_ddr_set_watchdog(handle, true); + + if (FAILED(hr)) { + log_warning("Enabling watchdog failed: %lX", hr); + return false; + } + + log_info("Watchdog on"); + + return true; +} + +static bool _mode_watchdogoff(HANDLE handle) +{ + HRESULT hr; + + hr = p3iodrv_ddr_set_watchdog(handle, false); + + if (FAILED(hr)) { + log_warning("Disabling watchdog failed: %lX", hr); + return false; + } + + log_info("Watchdog off"); + + return true; +} + +static bool _mode_cabtype(HANDLE handle) +{ + HRESULT hr; + enum p3io_cab_type type; + const char *type_str; + + hr = p3iodrv_ddr_get_cab_type(handle, &type); + + if (FAILED(hr)) { + log_warning("Getting cab type failed: %lX", hr); + return false; + } + + log_info("Cabinet type: %d", type); + + switch (type) { + case P3IO_CAB_TYPE_SD: + type_str = "sd"; + break; + + case P3IO_CAB_TYPE_HD: + type_str = "hd"; + break; + + default: + type_str = "unknown"; + break; + } + + printf("%s\n", type_str); + + return true; +} + +static bool _mode_dipsw(HANDLE handle) +{ + HRESULT hr; + uint8_t dip_sw; + + hr = p3iodrv_ddr_get_dipsw(handle, &dip_sw); + + if (FAILED(hr)) { + log_warning("Getting dip switches failed: %lX", hr); + return false; + } + + log_info("Dip switches: 0x%X", dip_sw); + + printf("0x%X\n", dip_sw); + + return true; +} + +static bool _mode_videofreq(HANDLE handle) +{ + HRESULT hr; + enum p3io_video_freq video_freq; + const char *video_freq_str; + + hr = p3iodrv_ddr_get_video_freq(handle, &video_freq); + + if (FAILED(hr)) { + log_warning("Getting video freq failed: %lX", hr); + return false; + } + + log_info("Video freq: 0x%X", video_freq); + + switch (video_freq) { + case P3IO_VIDEO_FREQ_15KHZ: + video_freq_str = "15khz"; + break; + + case P3IO_VIDEO_FREQ_31KHZ: + video_freq_str = "31khz"; + break; + + default: + video_freq_str = "unknown"; + break; + } + + printf("%s\n", video_freq_str); + + return true; +} + +static bool _mode_test(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + bool result; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + result = mode_test_proc(handle_p3io, handle_extio); + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + result = false; + } + } + + return result; +} + +static bool _mode_lightoff(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; + + memset(&output, 0, sizeof(output)); + memset(&pad_lights, 0, sizeof(pad_lights)); + neons = false; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (_extio_enabled) { + hr = extiodrv_extio_transfer( + handle_extio, + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, + pad_lights, + neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + } + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + return false; + } + } + + return true; +} + +static bool _mode_poll(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + + struct p3io_ddr_jamma jamma; + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; + + uint32_t cnt; + + memset(&output, 0, sizeof(output)); + memset(&pad_lights, 0, sizeof(pad_lights)); + neons = false; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + fprintf( + stderr, + ">>> Press enter to start endless polling loop. Press Escape to exit " + "to exit polling loop <<<\n"); + + if (getchar() != '\n') { + return true; + } + + cnt = 0; + + while ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) == 0) { + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (_extio_enabled) { + hr = extiodrv_extio_transfer( + handle_extio, + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, + pad_lights, + neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &jamma); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + + printf( + "%d: %08X %08X %08X\n", + cnt, + *((uint32_t *) &jamma) & 0xFFFFFF00, + jamma.unused, + jamma.unused2); + + cnt++; + } + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + return false; + } + } + + return true; +} + +static bool _mode_sensores(HANDLE handle_p3io) +{ + HRESULT hr; + HANDLE handle_extio; + + struct p3io_ddr_jamma jamma; + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; + + uint32_t cnt; + uint8_t sensor_read; + + memset(&output, 0, sizeof(output)); + memset(&pad_lights, 0, sizeof(pad_lights)); + neons = false; + + if (_extio_enabled) { + hr = extiodrv_device_open(_extio_com_port, &handle_extio); + + if (FAILED(hr)) { + log_warning( + "Failed opening EXTIO on com port '%s': %lX", + _extio_com_port, + hr); + return false; + } + } else { + handle_extio = INVALID_HANDLE_VALUE; + } + + fprintf( + stderr, + ">>> Press enter to start endless sensor cycling loop. Press Escape to " + "exit to exit sensor cycling loop <<<\n"); + + if (getchar() != '\n') { + return true; + } + + cnt = 0; + sensor_read = 0; + + while ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) == 0) { + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (_extio_enabled) { + hr = extiodrv_extio_transfer( + handle_extio, sensor_read + 1, pad_lights, neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &jamma); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + + printf( + "%d (%d): %08X %08X %08X\n", + cnt, + sensor_read, + *((uint32_t *) &jamma) & 0xFFFFFF00, + jamma.unused, + jamma.unused2); + + cnt++; + sensor_read++; + + if (sensor_read > 3) { + sensor_read = 0; + } + } + + if (_extio_enabled) { + hr = extiodrv_device_close(&handle_extio); + + if (FAILED(hr)) { + log_warning("Failed closing EXTIO: %lX", hr); + return false; + } + } + + return true; +} + +int main(int argc, char **argv) +{ + HANDLE handle; + bool result; + + if (!_process_cmd_args(argc, argv)) { + return -1; + } + + _init_logging(); + + if (_mode != MODE_SCAN) { + if (!_scan_and_open(&handle)) { + result = false; + } else { + result = _mode_procs[_mode](handle); + + if (!_close(&handle)) { + result = false; + } + } + } else { + result = _mode_procs[_mode](INVALID_HANDLE_VALUE); + } + + return result ? 0 : -1; +} \ No newline at end of file diff --git a/src/main/p3io-ddr-tool/mode-test.c b/src/main/p3io-ddr-tool/mode-test.c new file mode 100644 index 0000000..791e6d2 --- /dev/null +++ b/src/main/p3io-ddr-tool/mode-test.c @@ -0,0 +1,406 @@ +#define LOG_MODULE "mode-test" + +#include +#include + +#include "extiodrv/extio.h" + +#include "p3io/ddr.h" +#include "p3iodrv/ddr.h" +#include "p3iodrv/device.h" + +#include "util/log.h" + +#include "mode-test.h" + +struct mode_test_output_state { + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; +}; + +struct mode_test_input_state { + struct p3io_ddr_jamma jamma[4]; +}; + +static bool _check_input_state_change( + const struct mode_test_input_state *cur_state, + const struct mode_test_input_state *prev_state) +{ + return !memcmp(cur_state, prev_state, sizeof(struct mode_test_input_state)); +} + +static void _input_state_swap( + struct mode_test_input_state **cur_state, + struct mode_test_input_state **prev_state) +{ + struct mode_test_input_state *tmp; + + tmp = *cur_state; + *cur_state = *prev_state; + *prev_state = tmp; +} + +static bool _update_io_frame( + HANDLE handle_p3io, + HANDLE handle_extio, + bool polling_mode, + struct mode_test_input_state *input, + const struct mode_test_output_state *output) +{ + HRESULT hr; + + hr = p3iodrv_ddr_set_outputs(handle_p3io, &output->output); + + if (FAILED(hr)) { + log_warning("Setting outputs failed: %lX", hr); + return false; + } + + if (polling_mode) { + hr = extiodrv_extio_transfer( + handle_extio, + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, + output->pad_lights, + output->neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &input->jamma[0]); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + } else { + for (uint8_t i = 0; i < 4; i++) { + hr = extiodrv_extio_transfer( + handle_extio, i + 1, output->pad_lights, output->neons); + + if (FAILED(hr)) { + log_warning("extio transfer failed: %lX", hr); + return false; + } + + hr = p3iodrv_ddr_get_jamma(handle_p3io, &input->jamma[i]); + + if (FAILED(hr)) { + log_warning("Reading jamma failed: %lX", hr); + return false; + } + } + } + + return true; +} + +static void _set_outputs_on_inputs( + const struct mode_test_input_state *input, + struct mode_test_output_state *output) +{ + memset(&output->pad_lights, 0, sizeof(output->pad_lights)); + + output->output.cabinet.p1_menu_buttons = 0; + output->output.cabinet.p2_menu_buttons = 0; + + for (uint8_t i = 0; i < 4; i++) { + output->pad_lights[0].up |= input->jamma[i].p1.pad_up ? 1 : 0; + output->pad_lights[0].down |= input->jamma[i].p1.pad_down ? 1 : 0; + output->pad_lights[0].left |= input->jamma[i].p1.pad_left ? 1 : 0; + output->pad_lights[0].right |= input->jamma[i].p1.pad_right ? 1 : 0; + + output->pad_lights[1].up |= input->jamma[i].p2.pad_up ? 1 : 0; + output->pad_lights[1].down |= input->jamma[i].p2.pad_down ? 1 : 0; + output->pad_lights[1].left |= input->jamma[i].p2.pad_left ? 1 : 0; + output->pad_lights[1].right |= input->jamma[i].p2.pad_right ? 1 : 0; + + output->output.cabinet.p1_menu_buttons |= + (input->jamma[i].p1.menu_left || input->jamma[i].p1.menu_right || + input->jamma[i].p1.menu_start) ? + 1 : + 0; + output->output.cabinet.p2_menu_buttons |= + (input->jamma[i].p2.menu_left || input->jamma[i].p2.menu_right || + input->jamma[i].p2.menu_start) ? + 1 : + 0; + } +} + +static void _render_main( + uint8_t cnt, bool polling_mode, const struct mode_test_input_state *state) +{ + printf("Counter: %d\n", cnt); + printf("Polling mode: %d\n", polling_mode); + printf("Player 1 Player 2\n"); + printf("\n"); + printf("Pad\n"); + + if (polling_mode) { + printf( + "Up: %d Up: %d\n", + state->jamma[0].p1.pad_up, + state->jamma[0].p2.pad_up); + printf( + "Down: %d Down: %d\n", + state->jamma[0].p1.pad_down, + state->jamma[0].p2.pad_down); + printf( + "Left: %d Left: %d\n", + state->jamma[0].p1.pad_left, + state->jamma[0].p2.pad_left); + printf( + "Right: %d Right: %d\n", + state->jamma[0].p1.pad_right, + state->jamma[0].p2.pad_right); + } else { + printf( + "Up: %c%c%c%c Up: %c%c%c%c\n", + state->jamma[0].p1.pad_up ? 'U' : '_', + state->jamma[1].p1.pad_up ? 'D' : '_', + state->jamma[2].p1.pad_up ? 'L' : '_', + state->jamma[3].p1.pad_up ? 'R' : '_', + state->jamma[0].p2.pad_up ? 'U' : '_', + state->jamma[1].p2.pad_up ? 'D' : '_', + state->jamma[2].p2.pad_up ? 'L' : '_', + state->jamma[3].p2.pad_up) ? + 'R' : + '_'; + printf( + "Down: %c%c%c%c Down: %c%c%c%c\n", + state->jamma[0].p1.pad_down ? 'U' : '_', + state->jamma[1].p1.pad_down ? 'D' : '_', + state->jamma[2].p1.pad_down ? 'L' : '_', + state->jamma[3].p1.pad_down ? 'R' : '_', + state->jamma[0].p2.pad_down ? 'U' : '_', + state->jamma[1].p2.pad_down ? 'D' : '_', + state->jamma[2].p2.pad_down ? 'L' : '_', + state->jamma[3].p2.pad_down ? 'R' : '_'); + printf( + "Left: %c%c%c%c Left: %c%c%c%c\n", + state->jamma[0].p1.pad_left ? 'U' : '_', + state->jamma[1].p1.pad_left ? 'D' : '_', + state->jamma[2].p1.pad_left ? 'L' : '_', + state->jamma[3].p1.pad_left ? 'R' : '_', + state->jamma[0].p2.pad_left ? 'U' : '_', + state->jamma[1].p2.pad_left ? 'D' : '_', + state->jamma[2].p2.pad_left ? 'L' : '_', + state->jamma[3].p2.pad_left ? 'R' : '_'); + printf( + "Right: %c%c%c%c Right: %c%c%c%c\n", + state->jamma[0].p1.pad_right ? 'U' : '_', + state->jamma[1].p1.pad_right ? 'D' : '_', + state->jamma[2].p1.pad_right ? 'L' : '_', + state->jamma[3].p1.pad_right ? 'R' : '_', + state->jamma[0].p2.pad_right ? 'U' : '_', + state->jamma[1].p2.pad_right ? 'D' : '_', + state->jamma[2].p2.pad_right ? 'L' : '_', + state->jamma[3].p2.pad_right ? 'R' : '_'); + } + + printf("\n"); + printf("Menu\n"); + printf( + "Start: %d Start: %d\n", + state->jamma[0].p1.menu_start, + state->jamma[0].p2.menu_start); + printf( + "Up: %d Up: %d\n", + state->jamma[0].operator.p1_menu_up, + state->jamma[0].operator.p2_menu_up); + printf( + "Down: %d Down: %d\n", + state->jamma[0].operator.p1_menu_down, + state->jamma[0].operator.p2_menu_down); + printf( + "Left: %d Left: %d\n", + state->jamma[0].p1.menu_left, + state->jamma[0].p2.menu_left); + printf( + "Right: %d Right: %d\n", + state->jamma[0].p1.menu_right, + state->jamma[0].p2.menu_right); + printf("\n"); + printf("Operator\n"); + printf( + "Test: %d Service: %d Coin: %d\n", + state->jamma[0].operator.test, + state->jamma[0].operator.service, + state->jamma[0].operator.coin); +} + +static void +_render_menu(bool *continue_loop, struct mode_test_output_state *output) +{ + printf( + "Menu options:\n" + " 0: Exit menu and continue loop\n" + " 1: Exit\n" + " 2: Set neon state\n" + " 3: Top lamp state\n" + " 4: Set all lights on\n" + " 5: Set all lights off\n" + "Waiting for input: "); + + char c = getchar(); + + // Keyboard input debounce + Sleep(10); + + switch (c) { + case '1': { + *continue_loop = false; + break; + } + + case '2': { + int state; + int n; + + printf("Enter neon state (0/1): "); + + n = scanf("%d", &state); + + if (n > 0) { + output->neons = state > 0; + } + + break; + } + + case '3': { + char buf[4]; + int n; + + printf("Enter top lamp state, chain of 0/1s: "); + + n = scanf("%8s", buf); + + if (n > 0) { + output->output.cabinet.top_p1_upper = buf[0] == '1'; + output->output.cabinet.top_p1_lower = buf[1] == '1'; + output->output.cabinet.top_p2_upper = buf[2] == '1'; + output->output.cabinet.top_p2_lower = buf[3] == '1'; + } + + break; + } + + case '4': { + output->output.cabinet.top_p1_upper = 1; + output->output.cabinet.top_p1_lower = 1; + output->output.cabinet.top_p2_upper = 1; + output->output.cabinet.top_p2_lower = 1; + + output->output.cabinet.p1_menu_buttons = 1; + output->output.cabinet.p2_menu_buttons = 1; + + output->neons = true; + + output->pad_lights[0].up = true; + output->pad_lights[0].down = true; + output->pad_lights[0].left = true; + output->pad_lights[0].right = true; + + output->pad_lights[1].up = true; + output->pad_lights[1].down = true; + output->pad_lights[1].left = true; + output->pad_lights[1].right = true; + + break; + } + + case '5': { + output->output.cabinet.top_p1_upper = 0; + output->output.cabinet.top_p1_lower = 0; + output->output.cabinet.top_p2_upper = 0; + output->output.cabinet.top_p2_lower = 0; + + output->output.cabinet.p1_menu_buttons = 0; + output->output.cabinet.p2_menu_buttons = 0; + + output->neons = false; + + output->pad_lights[0].up = false; + output->pad_lights[0].down = false; + output->pad_lights[0].left = false; + output->pad_lights[0].right = false; + + output->pad_lights[1].up = false; + output->pad_lights[1].down = false; + output->pad_lights[1].left = false; + output->pad_lights[1].right = false; + + break; + } + + case '0': + default: + break; + } +} + +bool mode_test_proc(HANDLE handle_p3io, HANDLE handle_extio) +{ + struct mode_test_input_state state[2]; + struct mode_test_input_state *cur_input_state; + struct mode_test_input_state *prev_input_state; + struct mode_test_output_state output_state; + + bool polling_mode; + bool loop; + uint8_t cnt; + + memset(&state, 0, sizeof(state)); + cur_input_state = &state[0]; + prev_input_state = &state[1]; + memset(&output_state, 0, sizeof(output_state)); + + polling_mode = true; + loop = true; + cnt = 0; + + fprintf(stderr, ">>> Press enter to start running test mode <<<\n"); + + if (getchar() != '\n') { + return true; + } + + while (loop) { + if ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) != 0) { + system("cls"); + + _render_menu(&loop, &output_state); + } else { + _input_state_swap(&cur_input_state, &prev_input_state); + + memset(cur_input_state, 0, sizeof(struct mode_test_input_state)); + + if (!_update_io_frame( + handle_p3io, + handle_extio, + polling_mode, + cur_input_state, + &output_state)) { + return false; + } + + _set_outputs_on_inputs(cur_input_state, &output_state); + + if (_check_input_state_change(cur_input_state, prev_input_state)) { + system("cls"); + + _render_main(cnt, polling_mode, cur_input_state); + } + } + + /* avoid CPU banging */ + Sleep(1); + ++cnt; + } + + return true; +} \ No newline at end of file diff --git a/src/main/p3io-ddr-tool/mode-test.h b/src/main/p3io-ddr-tool/mode-test.h new file mode 100644 index 0000000..884ad66 --- /dev/null +++ b/src/main/p3io-ddr-tool/mode-test.h @@ -0,0 +1,11 @@ +#ifndef P3IO_DDR_TOOL_MODE_TEST_H +#define P3IO_DDR_TOOL_MODE_TEST_H + +#include + +#include +#include + +bool mode_test_proc(HANDLE handle_p3io, HANDLE handle_extio); + +#endif \ No newline at end of file -- 2.54.0 From c4b4ef685a2266b01deaacfef4fb4384246d9ff6 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:20:22 +0200 Subject: [PATCH 16/20] feat(ddrio-p3io): Add ddrio API implementation for DDR P3IO Also includes EXTIO. Run a SD style cabinet with bemanitools ddrio API. --- Module.mk | 1 + src/main/ddrio-p3io/Module.mk | 16 ++ src/main/ddrio-p3io/config.c | 35 +++ src/main/ddrio-p3io/config.h | 17 ++ src/main/ddrio-p3io/ddrio-p3io.def | 11 + src/main/ddrio-p3io/ddrio.c | 427 +++++++++++++++++++++++++++++ 6 files changed, 507 insertions(+) create mode 100644 src/main/ddrio-p3io/Module.mk create mode 100644 src/main/ddrio-p3io/config.c create mode 100644 src/main/ddrio-p3io/config.h create mode 100644 src/main/ddrio-p3io/ddrio-p3io.def create mode 100644 src/main/ddrio-p3io/ddrio.c diff --git a/Module.mk b/Module.mk index 31741fa..173246d 100644 --- a/Module.mk +++ b/Module.mk @@ -104,6 +104,7 @@ include src/main/d3d9exhook/Module.mk include src/main/ddrhook-util/Module.mk include src/main/ddrhook1/Module.mk include src/main/ddrhook2/Module.mk +include src/main/ddrio-p3io/Module.mk include src/main/ddrio-mm/Module.mk include src/main/ddrio-smx/Module.mk include src/main/ddrio/Module.mk diff --git a/src/main/ddrio-p3io/Module.mk b/src/main/ddrio-p3io/Module.mk new file mode 100644 index 0000000..43e0fd0 --- /dev/null +++ b/src/main/ddrio-p3io/Module.mk @@ -0,0 +1,16 @@ +dlls += ddrio-p3io + +ldflags_ddrio-p3io:= \ + -lsetupapi \ + +libs_ddrio-p3io := \ + cconfig \ + extio \ + extiodrv \ + p3io \ + p3iodrv \ + util \ + +src_ddrio-p3io := \ + config.c \ + ddrio.c \ diff --git a/src/main/ddrio-p3io/config.c b/src/main/ddrio-p3io/config.c new file mode 100644 index 0000000..c45254e --- /dev/null +++ b/src/main/ddrio-p3io/config.c @@ -0,0 +1,35 @@ +#include "cconfig/cconfig-util.h" + +#include "util/log.h" + +#include "config.h" + +#define DDRIO_P3IO_CONFIG_EXTIO_PORT_KEY "ddrio.p3io.extio_port" + +#define DDRIO_P3IO_CONFIG_EXTIO_PORT_VALUE "COM1" + +void ddrio_p3io_config_init(struct cconfig *config) +{ + cconfig_util_set_str( + config, + DDRIO_P3IO_CONFIG_EXTIO_PORT_KEY, + DDRIO_P3IO_CONFIG_EXTIO_PORT_VALUE, + "COM port the EXTIO is connected to, default COM1"); +} + +void ddrio_p3io_config_get( + struct ddrio_p3io_config *config_ddrio_p3io, struct cconfig *config) +{ + if (!cconfig_util_get_str( + config, + DDRIO_P3IO_CONFIG_EXTIO_PORT_KEY, + config_ddrio_p3io->extio_port, + sizeof(config_ddrio_p3io->extio_port) - 1, + DDRIO_P3IO_CONFIG_EXTIO_PORT_VALUE)) { + log_warning( + "Invalid value for key '%s' specified, fallback " + "to default '%s'", + DDRIO_P3IO_CONFIG_EXTIO_PORT_KEY, + DDRIO_P3IO_CONFIG_EXTIO_PORT_VALUE); + } +} diff --git a/src/main/ddrio-p3io/config.h b/src/main/ddrio-p3io/config.h new file mode 100644 index 0000000..784782f --- /dev/null +++ b/src/main/ddrio-p3io/config.h @@ -0,0 +1,17 @@ +#ifndef DDRIO_P3IO_CONFIG_H +#define DDRIO_P3IO_CONFIG_H + +#include + +#include "cconfig/cconfig.h" + +struct ddrio_p3io_config { + char extio_port[12]; +}; + +void ddrio_p3io_config_init(struct cconfig *config); + +void ddrio_p3io_config_get( + struct ddrio_p3io_config *config_ddrio_p3io, struct cconfig *config); + +#endif \ No newline at end of file diff --git a/src/main/ddrio-p3io/ddrio-p3io.def b/src/main/ddrio-p3io/ddrio-p3io.def new file mode 100644 index 0000000..f6e9fbb --- /dev/null +++ b/src/main/ddrio-p3io/ddrio-p3io.def @@ -0,0 +1,11 @@ +LIBRARY ddrio-p3io + +EXPORTS + ddr_io_set_loggers + ddr_io_fini + ddr_io_init + ddr_io_read_pad + ddr_io_set_lights_extio + ddr_io_set_lights_p3io + ddr_io_set_lights_hdxs_panel + ddr_io_set_lights_hdxs_rgb diff --git a/src/main/ddrio-p3io/ddrio.c b/src/main/ddrio-p3io/ddrio.c new file mode 100644 index 0000000..a38ce09 --- /dev/null +++ b/src/main/ddrio-p3io/ddrio.c @@ -0,0 +1,427 @@ +#define LOG_MODULE "ddrio-p3io" + +#include + +#include +#include +#include + +#include "bemanitools/ddrio.h" + +#include "cconfig/cconfig-main.h" + +#include "extiodrv/device.h" +#include "extiodrv/extio.h" + +#include "p3iodrv/ddr.h" +#include "p3iodrv/device.h" + +#include "util/log.h" +#include "util/thread.h" + +#include "config.h" + +static struct ddrio_p3io_config _ddr_io_p3io_config; +static HANDLE _ddr_io_p3io_handle; +static HANDLE _ddr_io_extio_handle; + +static HRESULT _ddr_io_p3io_scan_and_open(HANDLE *handle) +{ + HRESULT hr; + char path[MAX_PATH]; + + log_info("Scanning for p3io..."); + + hr = p3iodrv_device_scan(path); + + if (FAILED(hr)) { + log_warning("Cannot find a connected p3io: %lX", hr); + return hr; + } + + log_info("Opening p3io: %s", path); + + hr = p3iodrv_device_open(path, handle); + + if (FAILED(hr)) { + log_warning("Opening p3io failed: %lX", hr); + return hr; + } + + return hr; +} + +static HRESULT _ddr_io_p3io_print_version(HANDLE handle) +{ + HRESULT hr; + char version[P3IODRV_VERSION_MAX_LEN]; + + memset(version, 0, P3IODRV_VERSION_MAX_LEN); + + hr = p3iodrv_device_read_version(handle, version); + + if (FAILED(hr)) { + log_warning("Getting p3io version failed: %lX", hr); + return hr; + } + + log_info("P3IO version: %s", version); + + return hr; +} + +static HRESULT _ddr_io_ddr_p3io_print_version(HANDLE handle) +{ + HRESULT hr; + char str[4]; + uint32_t major; + uint32_t minor; + uint32_t patch; + + hr = p3iodrv_ddr_get_version(handle, str, &major, &minor, &patch); + + if (FAILED(hr)) { + log_warning("Getting version failed: %lX", hr); + return hr; + } + + log_info("DDR version: %s %d.%d.%d", str, major, minor, patch); + + return hr; +} + +static HRESULT _ddr_io_ddr_p3io_print_cabinet_type(HANDLE handle) +{ + HRESULT hr; + enum p3io_cab_type type; + + hr = p3iodrv_ddr_get_cab_type(handle, &type); + + if (FAILED(hr)) { + log_warning("Getting cab type failed: %lX", hr); + return hr; + } + + log_info("Cabinet type: %d", type); + + return hr; +} + +static HRESULT _ddr_io_ddr_p3io_print_dip_switches(HANDLE handle) +{ + HRESULT hr; + uint8_t dip_sw; + + hr = p3iodrv_ddr_get_dipsw(handle, &dip_sw); + + if (FAILED(hr)) { + log_warning("Getting dip switches failed"); + return hr; + } + + log_info("Dip switches: 0x%X", dip_sw); + + return hr; +} + +static HRESULT _ddr_io_ddr_p3io_print_video_freq(HANDLE handle) +{ + HRESULT hr; + enum p3io_video_freq video_freq; + + hr = p3iodrv_ddr_get_video_freq(handle, &video_freq); + + if (FAILED(hr)) { + log_warning("Getting video freq failed"); + return hr; + } + + log_info("Video freq: 0x%X", video_freq); + + return hr; +} + +static HRESULT _ddr_io_ddr_p3io_print_debug_info(HANDLE handle) +{ + return _ddr_io_p3io_print_version(handle) && + _ddr_io_ddr_p3io_print_version(handle) && + _ddr_io_ddr_p3io_print_cabinet_type(handle) && + _ddr_io_ddr_p3io_print_dip_switches(handle) && + _ddr_io_ddr_p3io_print_video_freq(handle); +} + +static HRESULT _ddr_io_p3io_init(HANDLE *handle) +{ + HRESULT hr; + + log_info("Initializing DDR P3IO..."); + + hr = _ddr_io_p3io_scan_and_open(handle); + + if (FAILED(hr)) { + return hr; + } + + hr = p3iodrv_ddr_init(*handle); + + if (FAILED(hr)) { + return hr; + } + + hr = _ddr_io_ddr_p3io_print_debug_info(*handle); + + if (FAILED(hr)) { + return hr; + } + + return hr; +} + +static HRESULT _ddr_io_p3io_close(HANDLE *handle) +{ + HRESULT hr; + + log_info("Closing p3io..."); + + hr = p3iodrv_device_close(handle); + + if (FAILED(hr)) { + log_warning("Closing p3io failed: %lX", hr); + return hr; + } + + return hr; +} + +static HRESULT _ddr_io_extio_init(HANDLE *handle) +{ + log_info("Initializing EXTIO..."); + + return extiodrv_device_open(_ddr_io_p3io_config.extio_port, handle); +} + +static HRESULT _ddr_io_extio_close(HANDLE *handle) +{ + log_info("Closing EXTIO..."); + + return extiodrv_device_close(handle); +} + +static HRESULT _ddr_io_flush_and_reset(HANDLE p3io_handle, HANDLE extio_handle) +{ + HRESULT hr; + struct p3io_ddr_jamma jamma; + struct p3io_ddr_output output; + struct extiodrv_extio_pad_lights pad_lights[2]; + bool neons; + + log_assert(p3io_handle != INVALID_HANDLE_VALUE); + log_assert(extio_handle != INVALID_HANDLE_VALUE); + + log_info("Flushing inputs and outputs"); + + memset(&output, 0, sizeof(output)); + memset(&pad_lights, 0, sizeof(pad_lights)); + neons = false; + + hr = p3iodrv_ddr_get_jamma(p3io_handle, &jamma); + + if (FAILED(hr)) { + log_warning("Reading jamma failed"); + return hr; + } + + hr = p3iodrv_ddr_set_outputs(p3io_handle, &output); + + if (FAILED(hr)) { + log_warning("Settings outputs failed"); + return hr; + } + + hr = extiodrv_extio_transfer( + extio_handle, EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, pad_lights, neons); + + if (FAILED(hr)) { + log_warning("EXTIO transfer failed"); + return hr; + } + + return hr; +} + +void ddr_io_set_loggers( + log_formatter_t misc, + log_formatter_t info, + log_formatter_t warning, + log_formatter_t fatal) +{ + log_to_external(misc, info, warning, fatal); +} + +static void _ddr_io_config_init(struct ddrio_p3io_config *config_ddrio_p3io) +{ + struct cconfig *config; + + config = cconfig_init(); + + ddrio_p3io_config_init(config); + + if (!cconfig_main_config_init( + config, + "--ddrio-p3io-config", + "ddrio-p3io.conf", + "--help", + "-h", + "ddrio-p3io", + CCONFIG_CMD_USAGE_OUT_STDOUT)) { + cconfig_finit(config); + exit(EXIT_FAILURE); + } + + ddrio_p3io_config_get(config_ddrio_p3io, config); + + cconfig_finit(config); +} + +bool ddr_io_init( + thread_create_t thread_create, + thread_join_t thread_join, + thread_destroy_t thread_destroy) +{ + HRESULT hr; + + _ddr_io_config_init(&_ddr_io_p3io_config); + + hr = _ddr_io_p3io_init(&_ddr_io_p3io_handle); + + if (FAILED(hr)) { + return false; + } + + hr = _ddr_io_extio_init(&_ddr_io_extio_handle); + + if (FAILED(hr)) { + return false; + } + + hr = _ddr_io_flush_and_reset(_ddr_io_p3io_handle, _ddr_io_extio_handle); + + if (FAILED(hr)) { + return false; + } + + return true; +} + +uint32_t ddr_io_read_pad(void) +{ + HRESULT hr; + struct p3io_ddr_jamma jamma; + + hr = p3iodrv_ddr_get_jamma(_ddr_io_p3io_handle, &jamma); + + if (FAILED(hr)) { + log_warning("Reading jamma failed"); + return 0; + } + + return _byteswap_ulong(*((uint32_t *) &jamma)); +} + +void ddr_io_set_lights_extio(uint32_t extio_lights) +{ + HRESULT hr; + struct extiodrv_extio_pad_lights pad_lights[EXTIO_PAD_LIGHT_MAX_PLAYERS]; + bool neons; + + pad_lights[0].up = (extio_lights & (1 << LIGHT_P1_UP)) > 0; + pad_lights[0].down = (extio_lights & (1 << LIGHT_P1_DOWN)) > 0; + pad_lights[0].left = (extio_lights & (1 << LIGHT_P1_LEFT)) > 0; + pad_lights[0].right = (extio_lights & (1 << LIGHT_P1_RIGHT)) > 0; + + pad_lights[1].up = (extio_lights & (1 << LIGHT_P2_UP)) > 0; + pad_lights[1].down = (extio_lights & (1 << LIGHT_P2_DOWN)) > 0; + pad_lights[1].left = (extio_lights & (1 << LIGHT_P2_LEFT)) > 0; + pad_lights[1].right = (extio_lights & (1 << LIGHT_P2_RIGHT)) > 0; + + neons = (extio_lights & (1 << LIGHT_NEONS)) > 0; + + hr = extiodrv_extio_transfer( + _ddr_io_extio_handle, + EXTIODRV_EXTIO_SENSOR_READ_MODE_ALL, + pad_lights, + neons); + + if (FAILED(hr)) { + log_warning("EXTIO transfer failed: %lX", hr); + } +} + +void ddr_io_set_lights_p3io(uint32_t p3io_lights) +{ + HRESULT hr; + struct p3io_ddr_output output; + + output.cabinet.top_p1_lower = + (p3io_lights & (1 << LIGHT_P1_LOWER_LAMP)) > 0 ? 1 : 0; + output.cabinet.top_p1_upper = + (p3io_lights & (1 << LIGHT_P1_UPPER_LAMP)) > 0 ? 1 : 0; + output.cabinet.top_p2_lower = + (p3io_lights & (1 << LIGHT_P2_LOWER_LAMP)) > 0 ? 1 : 0; + output.cabinet.top_p2_upper = + (p3io_lights & (1 << LIGHT_P2_UPPER_LAMP)) > 0 ? 1 : 0; + + output.cabinet.p1_menu_buttons = + (p3io_lights & (1 << LIGHT_P1_MENU)) > 0 ? 1 : 0; + output.cabinet.p2_menu_buttons = + (p3io_lights & (1 << LIGHT_P2_MENU)) > 0 ? 1 : 0; + + hr = p3iodrv_ddr_set_outputs(_ddr_io_p3io_handle, &output); + + if (FAILED(hr)) { + log_warning("Settings outputs failed: %lX", hr); + } +} + +void ddr_io_set_lights_hdxs_panel(uint32_t lights) +{ + // Unused + + (void) lights; +} + +void ddr_io_set_lights_hdxs_rgb(uint8_t idx, uint8_t r, uint8_t g, uint8_t b) +{ + // Unused + + (void) idx; + (void) r; + (void) g; + (void) b; +} + +void ddr_io_fini(void) +{ + HRESULT hr; + + hr = _ddr_io_flush_and_reset(_ddr_io_p3io_handle, _ddr_io_extio_handle); + + if (FAILED(hr)) { + log_warning("Flusing IO failed: %lX", hr); + return; + } + + hr = _ddr_io_p3io_close(&_ddr_io_p3io_handle); + + if (FAILED(hr)) { + log_warning("Closing P3IO failed: %lX", hr); + // continue + } + + hr = _ddr_io_extio_close(&_ddr_io_extio_handle); + + if (FAILED(hr)) { + log_warning("Closing EXTIO failed: %lX", hr); + // continue + } +} -- 2.54.0 From e20f450f0c62d49d00dda9ef6a671db3565587b9 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:21:25 +0200 Subject: [PATCH 17/20] doc(dev/journal): Add notes when working on the P3IO driver --- .../2023-05-28-ddr-p3io-driver-work.md | 194 ++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 doc/dev/journal/2023-05-28-ddr-p3io-driver-work.md diff --git a/doc/dev/journal/2023-05-28-ddr-p3io-driver-work.md b/doc/dev/journal/2023-05-28-ddr-p3io-driver-work.md new file mode 100644 index 0000000..98a67d8 --- /dev/null +++ b/doc/dev/journal/2023-05-28-ddr-p3io-driver-work.md @@ -0,0 +1,194 @@ +# DDR p3io driver work, various notes + +Date: 2023-05-28 +Author: icex2 + +Notes about my work on writing a DDR P3io driver. + +## Python IO board hardware breakout interface + +PCB breakout interfaces/connectors on the side of the boards. + +### P2IO DDR + +Descriptions assume cabinet hardware of an upgraded DDR "black SD cabinet" + +* `LINE OUT 1`: Primary audio out to amplifier +* `LINE OUT 2`: N.C. +* `RGB`: Video out to monitor, supports 15khz/31khz based on hardware switch for video freq/mode selection +* `COM1`: Virtual com port that connects to the EXTIO +* `COM2`: Virtual com port that connects to a pair of ICCA card readers +* `LAN`: Network connection +* `PWR`: 100V power in +* `ANALOG`: N.C. +* `PORT 1`: Lights output for cabinet lights, e.g. menu button lights, top header lights +* `PORT 2`: N.C. +* `DIPSW` + * `1`: ??? + * `2`: On = force all sensor polling mode and allow running the game without an EXTIO + * `3`: ??? + * `4`: On = force 15 khz monitor output +* `PLUG 1`: Black dongle +* `PLUG 2`: White dongle + +### P3IO GF/DM + +Descriptions assume usage of a P3IO from a GF/DM in a "chimera style PCB build" on an upgraded +DDR "black SD cabinet" + +* `PWR`: 100V power in +* `12V-OUT`: +12V out for external devices +* `PORT 1`: Lights output for cabinet lights, e.g. menu button lights, top header lights +* `PORT 2`: N.C. +* `COM1`: To EXTIO +* `COM2`: To card readers +* `RGB`: Video out to monitor, supports 15khz/31khz based on hardware switch for video freq/mode selection +* `LINE OUT 1`: Primary audio out to amplifier +* `LINE OUT 2`: N.C. +* `DIPSW` + * `1`: + * `2`: + * `3`: + * `4`: On = force 15 khz monitor output +* `PLUG 1`: Roundplug black dongle +* `PLUG 2`: Roundplug white dongle + +### P3IO DDR(X) + +Descriptions assume cabinet hardware of an upgraded DDR "black SD cabinet" + +* `PWR`: 100V power in +* `USB`: USB memory card readers on cabinet +* `PORT 1`: Lights output for cabinet lights, e.g. menu button lights, top header lights +* `COM3-4`: Virtual COM ports + * COM3 (VCOM1): Pins 1,3,5 on connector -> To card readers + * COM4 (VCOM0): Pins 2,4,6 on connector -> N.C. +* `PLUG`: Breakout to security round plugs (black and white dongles) +* `COM1`: To EXTIO + * Pinout (pins left to right) + * 1: TXD1 + * 2: RXD1 + * 3: N.C. + * 4: N.C. + * 5: GND +* `COM2`: N/A (light spires on black HD cabinet) + * Pinout (pins left to right) + * 1: TXD2 + * 2: RXD2 + * 3: GND +* `RGB`: Video out to monitor, supports 15khz/31khz based on hardware switch for video freq/mode selection +* `LINE OUT1`: Primary audio out to amplifier +* `LINE OUT2`: N.C. +* `LAN`: Network +* `DIP SW` + * `1`: + * `2`: + * `3`: + * `4`: On = force 15 khz monitor output + +## Python IO boards and differences + +### P3IO DDR(X) + +* Connects to USB and actually enumerates as a USB device and not a virtual COM port +* COM ports 1-4 on breakout of the PCB are being passed through as actual COM ports to the operating system + +### P3IO GF/DM + +* Connects to USB and actually enumerates as a USB device and not a virtual COM port +* COM ports 1-2 on breakout of the PCB are just virtual COM ports +* These do not show up as COM ports on the operating system +* The game drives the COM ports through the main P3IO protocol with additional P3IO commands + to open, read, write and close these virtual COM ports + +### P2IO DDR + +* Connects to USB and actually enumerates as a USB device and not a virtual COM port +* COM ports 1-2 on breakout of the PCB are just virtual COM ports +* These do not show up as COM ports on the operating system +* The game drives the COM ports through the main P3IO protocol with additional P3IO commands + to open, read, write and close these virtual COM ports + + +## Pinout card reader P1 -> P2 mini din8 male to mini din8 male + +Port 1 2 and 3: + +* Pin 3: TX +* Pin 4: GND +* Pin 5 : RX + +Pin 3 and 5 need to be reversed inbetween readers +They are all the same, so your cable needs to bridge them over. +A standard male to male mini din 8 cable does not do that. + +### Pinout card reader stock cable s-sub9 to round pin9 + +```text +dsub-9 female -> mini-din8 male +2 (TXD) -> 3 +3 (RXD) -> 5 +5 (GND) -> 4 +``` + +## ADE board and com port assignments + +Default or incorrectly configured? + +* `COM1` -> on mainboard +* `COM2` -> COM2 on P3IO breakout +* `COM3` -> ??? +* `COM4` -> COM1 on P3IO breakout + +## P3IO command init sequence on DDR 18 + +From the ddrio-python23 library + +```text +.data:1002D5D0 g_init_pakets db 0AAh, 2, 0, 1, 29h dup(0); field_0.field_0 +.data:1002D5D0 ; DATA XREF: initialize_and_send_pakets+5↑o +.data:1002D5D0 db 0AAh, 2, 0, 2Fh, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 3, 1, 27h, 1, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2 dup(2), 31h, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2, 3, 1, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 3, 4, 27h, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 5, 25h, 10h, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 6, 25h, 10h, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 7, 25h, 10h, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 8, 25h, 10h, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 9, 25h, 10h, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 0Ah, 25h, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 0Bh, 25h, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 0Ch, 25h, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 0Dh, 25h, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2Bh, 0Eh, 25h, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 3, 0Fh, 5, 29h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 3, 0, 2Bh, 1, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 3, 1, 29h, 5, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 3, 2, 5, 30h, 28h dup(0); field_0.field_0 +.data:1002D5D0 db 0AAh, 2 dup(3), 27h, 29h dup(0); field_0.field_0 +`` + +```text +// 01: get version <- this is part of a "flush" of old data? +// 2F: set mode +// 27: get cab type or dispsw +// 31: get coinstock +// 1: get version +// 27: get cab type or dipsw +// 25: read plug +// 25: read plug +// 25: read plug +// 25: read plug +// 25: read plug +// 25: read plug +// 25: read plug +// 25: read plug +// 25: read plug +// 25: read plug +// 5: set watchdog +// 2b: unknown +// 29: get video freq +// 5: set watchdog +// 27: get cab type or dipsw +``` \ No newline at end of file -- 2.54.0 From b2b93b21df7621e2a0b18a162369788ec84cd4bd Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 18:21:36 +0200 Subject: [PATCH 18/20] feat(ddriotest): Add driotest tool to test ddrio API implementations --- Module.mk | 3 + src/main/ddriotest/Module.mk | 8 + src/main/ddriotest/main.c | 303 +++++++++++++++++++++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 src/main/ddriotest/Module.mk create mode 100644 src/main/ddriotest/main.c diff --git a/Module.mk b/Module.mk index 173246d..73a24af 100644 --- a/Module.mk +++ b/Module.mk @@ -107,6 +107,7 @@ include src/main/ddrhook2/Module.mk include src/main/ddrio-p3io/Module.mk include src/main/ddrio-mm/Module.mk include src/main/ddrio-smx/Module.mk +include src/main/ddriotest/Module.mk include src/main/ddrio/Module.mk include src/main/dinput/Module.mk include src/main/eamio-icca/Module.mk @@ -213,6 +214,7 @@ $(zipdir)/: $(zipdir)/tools.zip: \ build/bin/indep-32/aciotest.exe \ + build/bin/indep-32/ddriotest.exe \ build/bin/indep-32/eamiotest.exe \ build/bin/indep-32/ezusb-iidx-fpga-flash.exe \ build/bin/indep-32/ezusb-iidx-sram-flash.exe \ @@ -234,6 +236,7 @@ $(zipdir)/tools.zip: \ $(zipdir)/tools-x64.zip: \ build/bin/indep-64/aciotest.exe \ + build/bin/indep-64/ddriotest.exe \ build/bin/indep-64/eamiotest.exe \ build/bin/indep-64/iidxiotest.exe \ build/bin/indep-64/iidx-bio2-exit-hook.dll \ diff --git a/src/main/ddriotest/Module.mk b/src/main/ddriotest/Module.mk new file mode 100644 index 0000000..569d973 --- /dev/null +++ b/src/main/ddriotest/Module.mk @@ -0,0 +1,8 @@ +exes += ddriotest \ + +libs_ddriotest := \ + ddrio \ + util \ + +src_ddriotest := \ + main.c \ diff --git a/src/main/ddriotest/main.c b/src/main/ddriotest/main.c new file mode 100644 index 0000000..e03bc0c --- /dev/null +++ b/src/main/ddriotest/main.c @@ -0,0 +1,303 @@ +#include +#include +#include +#include + +#include + +#include "bemanitools/ddrio.h" + +#include "util/log.h" +#include "util/thread.h" + +int main(int argc, char **argv) +{ + enum log_level log_level; + + log_level = LOG_LEVEL_FATAL; + + for (int i = 0; i < argc; i++) { + if (!strcmp(argv[i], "-v")) { + log_level = LOG_LEVEL_WARNING; + } else if (!strcmp(argv[i], "-vv")) { + log_level = LOG_LEVEL_INFO; + } else if (!strcmp(argv[i], "-vvv")) { + log_level = LOG_LEVEL_MISC; + } + } + + log_to_writer(log_writer_stderr, NULL); + log_set_level(log_level); + + ddr_io_set_loggers( + log_impl_misc, log_impl_info, log_impl_warning, log_impl_fatal); + + if (!ddr_io_init(crt_thread_create, crt_thread_join, crt_thread_destroy)) { + fprintf(stderr, "Initializing ddrio failed\n"); + return -1; + } + + fprintf( + stderr, + ">>> Initializing ddrio successful, press enter to continue <<<\n"); + + if (getchar() != '\n') { + return 0; + } + + // inputs + uint32_t pad = 0; + + // outputs + uint32_t extio_lights = 0; + uint32_t p3io_lights = 0; + + bool loop = true; + uint8_t cnt = 0; + + while (loop) { + ddr_io_set_lights_extio(extio_lights); + ddr_io_set_lights_p3io(p3io_lights); + + pad = ddr_io_read_pad(); + + system("cls"); + + printf("Counter: %d\n", cnt); + printf("Player 1 Player 2\n"); + printf("\n"); + printf("Pad\n"); + + printf( + "Up: %d Up: %d\n", + (pad & (1 << DDR_P1_UP)) > 0, + (pad & (1 << DDR_P2_UP)) > 0); + printf( + "Down: %d Down: %d\n", + (pad & (1 << DDR_P1_DOWN)) > 0, + (pad & (1 << DDR_P2_DOWN)) > 0); + printf( + "Left: %d Left: %d\n", + (pad & (1 << DDR_P1_LEFT)) > 0, + (pad & (1 << DDR_P2_LEFT)) > 0); + printf( + "Right: %d Right: %d\n", + (pad & (1 << DDR_P1_RIGHT)) > 0, + (pad & (1 << DDR_P2_RIGHT)) > 0); + printf("\n"); + printf("Menu\n"); + printf( + "Start: %d Start: %d\n", + (pad & (1 << DDR_P1_START)) > 0, + (pad & (1 << DDR_P2_START)) > 0); + printf( + "Up: %d Up: %d\n", + (pad & (1 << DDR_P1_MENU_UP)) > 0, + (pad & (1 << DDR_P2_MENU_UP)) > 0); + printf( + "Down: %d Down: %d\n", + (pad & (1 << DDR_P1_MENU_DOWN)) > 0, + (pad & (1 << DDR_P2_MENU_DOWN)) > 0); + printf( + "Left: %d Left: %d\n", + (pad & (1 << DDR_P1_MENU_LEFT)) > 0, + (pad & (1 << DDR_P2_MENU_LEFT)) > 0); + printf( + "Right: %d Right: %d\n", + (pad & (1 << DDR_P1_MENU_RIGHT)) > 0, + (pad & (1 << DDR_P2_MENU_RIGHT)) > 0); + printf("\n"); + printf("Operator\n"); + printf( + "Test: %d Service: %d Coin: %d\n", + (pad & (1 << DDR_TEST)) > 0, + (pad & (1 << DDR_SERVICE)) > 0, + (pad & (1 << DDR_COIN)) > 0); + + if ((pad & (1 << DDR_P1_UP)) > 0) { + extio_lights |= (1 << LIGHT_P1_UP); + } else { + extio_lights &= ~(1 << LIGHT_P1_UP); + } + + if ((pad & (1 << DDR_P1_DOWN)) > 0) { + extio_lights |= (1 << LIGHT_P1_DOWN); + } else { + extio_lights &= ~(1 << LIGHT_P1_DOWN); + } + + if ((pad & (1 << DDR_P1_LEFT)) > 0) { + extio_lights |= (1 << LIGHT_P1_LEFT); + } else { + extio_lights &= ~(1 << LIGHT_P1_LEFT); + } + + if ((pad & (1 << DDR_P1_RIGHT)) > 0) { + extio_lights |= (1 << LIGHT_P1_RIGHT); + } else { + extio_lights &= ~(1 << LIGHT_P1_RIGHT); + } + + if ((pad & (1 << DDR_P2_UP)) > 0) { + extio_lights |= (1 << LIGHT_P2_UP); + } else { + extio_lights &= ~(1 << LIGHT_P2_UP); + } + + if ((pad & (1 << DDR_P2_DOWN)) > 0) { + extio_lights |= (1 << LIGHT_P2_DOWN); + } else { + extio_lights &= ~(1 << LIGHT_P2_DOWN); + } + + if ((pad & (1 << DDR_P2_LEFT)) > 0) { + extio_lights |= (1 << LIGHT_P2_LEFT); + } else { + extio_lights &= ~(1 << LIGHT_P2_LEFT); + } + + if ((pad & (1 << DDR_P2_RIGHT)) > 0) { + extio_lights |= (1 << LIGHT_P2_RIGHT); + } else { + extio_lights &= ~(1 << LIGHT_P2_RIGHT); + } + + if ((pad & (1 << DDR_P1_START)) > 0 || + (pad & (1 << DDR_P1_MENU_UP)) > 0 || + (pad & (1 << DDR_P1_MENU_DOWN)) > 0 || + (pad & (1 << DDR_P1_MENU_LEFT)) > 0 || + (pad & (1 << DDR_P1_MENU_RIGHT)) > 0) { + p3io_lights |= (1 << LIGHT_P1_MENU); + } else { + p3io_lights &= ~(1 << LIGHT_P1_MENU); + } + + if ((pad & (1 << DDR_P2_START)) > 0 || + (pad & (1 << DDR_P2_MENU_UP)) > 0 || + (pad & (1 << DDR_P2_MENU_DOWN)) > 0 || + (pad & (1 << DDR_P2_MENU_LEFT)) > 0 || + (pad & (1 << DDR_P2_MENU_RIGHT)) > 0) { + p3io_lights |= (1 << LIGHT_P2_MENU); + } else { + p3io_lights &= ~(1 << LIGHT_P2_MENU); + } + + /* avoid CPU banging */ + Sleep(1); + ++cnt; + + /* process menu */ + if ((GetAsyncKeyState(VK_ESCAPE) & 0x8000) != 0) { + system("cls"); + Sleep(5); + printf( + "Menu options:\n" + " 0: Exit menu and continue loop\n" + " 1: Exit\n" + " 2: Set neon state\n" + " 3: Top lamp state\n" + " 4: Set all lights on\n" + " 5: Set all lights off\n" + "Waiting for input: "); + + char c = getchar(); + + switch (c) { + case '1': { + loop = false; + break; + } + + case '2': { + int state; + int n; + + printf("Enter neon state (0/1): "); + + n = scanf("%d", &state); + + if (n > 0) { + extio_lights |= (1 << LIGHT_NEONS); + } else { + extio_lights &= ~(1 << LIGHT_NEONS); + } + + break; + } + + case '3': { + char buf[4]; + int n; + + printf("Enter top lamp state, chain of 0/1s: "); + + n = scanf("%8s", buf); + + if (n > 0) { + if (buf[0] == '1') { + p3io_lights |= (1 << LIGHT_P1_UPPER_LAMP); + } else { + p3io_lights &= ~(1 << LIGHT_P1_UPPER_LAMP); + } + + if (buf[1] == '1') { + p3io_lights |= (1 << LIGHT_P1_LOWER_LAMP); + } else { + p3io_lights &= ~(1 << LIGHT_P1_LOWER_LAMP); + } + + if (buf[2] == '1') { + p3io_lights |= (1 << LIGHT_P2_UPPER_LAMP); + } else { + p3io_lights &= ~(1 << LIGHT_P2_UPPER_LAMP); + } + + if (buf[3] == '1') { + p3io_lights |= (1 << LIGHT_P2_LOWER_LAMP); + } else { + p3io_lights &= ~(1 << LIGHT_P2_LOWER_LAMP); + } + } + + break; + } + + case '4': { + extio_lights |= (1 << LIGHT_NEONS); + + p3io_lights |= (1 << LIGHT_P1_MENU); + p3io_lights |= (1 << LIGHT_P2_MENU); + p3io_lights |= (1 << LIGHT_P1_UPPER_LAMP); + p3io_lights |= (1 << LIGHT_P1_LOWER_LAMP); + p3io_lights |= (1 << LIGHT_P2_UPPER_LAMP); + p3io_lights |= (1 << LIGHT_P2_LOWER_LAMP); + + break; + } + + case '5': { + extio_lights &= ~(1 << LIGHT_NEONS); + + p3io_lights &= ~(1 << LIGHT_P1_MENU); + p3io_lights &= ~(1 << LIGHT_P2_MENU); + p3io_lights &= ~(1 << LIGHT_P1_UPPER_LAMP); + p3io_lights &= ~(1 << LIGHT_P1_LOWER_LAMP); + p3io_lights &= ~(1 << LIGHT_P2_UPPER_LAMP); + p3io_lights &= ~(1 << LIGHT_P2_LOWER_LAMP); + + break; + } + + case '0': + default: + break; + } + } + } + + system("cls"); + + ddr_io_fini(); + + return 0; +} \ No newline at end of file -- 2.54.0 From 4edcb36205fdc0039decb355bf6e5fed774cf6ff Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 18:21:56 +0200 Subject: [PATCH 19/20] chore(module.mk): Add various ddr related IO tools --- Module.mk | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/Module.mk b/Module.mk index 73a24af..2c708e5 100644 --- a/Module.mk +++ b/Module.mk @@ -627,18 +627,6 @@ $(zipdir)/sdvx-hwio-x64.zip: \ $(V)echo ... $@ $(V)zip -j $@ $^ -$(zipdir)/ddr-hwio-x86.zip: \ - build/bin/indep-32/vigem-ddrio.exe \ - | $(zipdir)/ - $(V)echo ... $@ - $(V)zip -j $@ $^ - -$(zipdir)/ddr-hwio-x64.zip: \ - build/bin/indep-64/vigem-ddrio.exe \ - | $(zipdir)/ - $(V)echo ... $@ - $(V)zip -j $@ $^ - $(zipdir)/ddr-11.zip: \ build/bin/indep-32/inject.exe \ build/bin/avs2_803-32/ddrhook1.dll \ @@ -715,8 +703,6 @@ $(zipdir)/ddr-14-to-16.zip: \ build/bin/avs2_1508-32/unicorntail.dll \ build/bin/indep-32/config.exe \ build/bin/indep-32/ddrio.dll \ - build/bin/indep-32/ddrio-mm.dll \ - build/bin/indep-32/ddrio-smx.dll \ build/bin/indep-32/eamio.dll \ build/bin/indep-32/geninput.dll \ dist/ddr/config.bat \ @@ -733,8 +719,6 @@ $(zipdir)/ddr-16-x64.zip: \ build/bin/avs2_1603-64/unicorntail.dll \ build/bin/indep-64/config.exe \ build/bin/indep-64/ddrio.dll \ - build/bin/indep-64/ddrio-mm.dll \ - build/bin/indep-64/ddrio-smx.dll \ build/bin/indep-64/eamio.dll \ build/bin/indep-64/geninput.dll \ dist/ddr/config.bat \ @@ -743,6 +727,28 @@ $(zipdir)/ddr-16-x64.zip: \ $(V)echo ... $@ $(V)zip -j $@ $^ +$(zipdir)/ddr-hwio-x86.zip: \ + build/bin/indep-32/ddrio-p3io.dll \ + build/bin/indep-32/ddrio-mm.dll \ + build/bin/indep-32/ddrio-smx.dll \ + build/bin/indep-32/extiotest.exe \ + build/bin/indep-32/p3io-ddr-tool.exe \ + build/bin/indep-32/vigem-ddrio.exe \ + | $(zipdir)/ + $(V)echo ... $@ + $(V)zip -j $@ $^ + +$(zipdir)/ddr-hwio-x64.zip: \ + build/bin/indep-64/ddrio-p3io.dll \ + build/bin/indep-64/ddrio-mm.dll \ + build/bin/indep-64/ddrio-smx.dll \ + build/bin/indep-64/extiotest.exe \ + build/bin/indep-64/p3io-ddr-tool.exe \ + build/bin/indep-64/vigem-ddrio.exe \ + | $(zipdir)/ + $(V)echo ... $@ + $(V)zip -j $@ $^ + $(zipdir)/bst.zip: \ build/bin/avs2_1603-64/bsthook.dll \ build/bin/avs2_1603-64/launcher.exe \ -- 2.54.0 From 6cd02fb5c88a2b35570e9e2dc581a25b636fcbc3 Mon Sep 17 00:00:00 2001 From: icex2 Date: Sun, 11 Jun 2023 17:35:14 +0200 Subject: [PATCH 20/20] doc: Update documentation for various tools and ddrio added --- README.md | 4 ++++ doc/api.md | 3 ++- doc/ddrhook/ddrio-p3io.md | 27 +++++++++++++++++++++++++++ doc/tools/README.md | 5 +++++ doc/tools/ddriotest.md | 3 +++ doc/tools/p3io-ddr-tool.md | 2 ++ 6 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 doc/ddrhook/ddrio-p3io.md create mode 100644 doc/tools/ddriotest.md create mode 100644 doc/tools/p3io-ddr-tool.md diff --git a/README.md b/README.md index 77b357f..62a54d8 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,13 @@ The following games are supported with their corresponding hook-libraries. * [iidx-bio2-exit-hook](doc/tools/iidx-bio2-exit-hook.md): For IIDX with BIO2 IO * [iidx-ezusb2-exit-hook](doc/tools/iidx-ezusb-exit-hook.md): For IIDX with ezusb FX2 IO * Bemanitools API testing: Tools for testing bemanitools API implementations + * [ddriotest](doc/tools/ddriotest.md): For [ddrio API](doc/api.md#io-boards) * [eamiotest](doc/tools/eamiotest.md): For [eamio API](doc/api.md#eamuse-readers) * [iidxiotest](doc/tools/iidxiotest.md): For [iidxio API](doc/api.md#io-boards) * [jbiotest](doc/tools/jbiotest.md): For [jbio API](doc/api.md#io-boards) +* DDR IO testing: Tools for testing hardware of a real DDR cabinet + * [p3io-ddr-tool](doc/tools/p3io-ddr-tool.md) + * [extiotest](doc/tools/extiotest.md) * [aciotest](doc/tools/aciotest.md): Command line tool to quickly test ACIO devices * config: UI input/output configuration tool when using the default bemanitools API (geninput) * ir-beat-patch-9/10: Patch the IR beat phase on IIDX 9 and 10 diff --git a/doc/api.md b/doc/api.md index 63a5048..3c85111 100644 --- a/doc/api.md +++ b/doc/api.md @@ -22,8 +22,9 @@ The following implementations are already shipped with BT5. * bstio.dll (default): Keyboard, joystick and mouse input * Dance Dance Revolution * ddrio.dll (default): Keyboard, joystick and mouse input + * [ddrio-p3io.dll](ddrhook/ddrio-p3io.md): DDR P3IO (Dragon PCB) + EXTIO hardware * ddrio-mm.dll: Minimaid hardware - * ddrio-smx.dll: StepManiaX platforms + * [ddrio-smx.dll](ddrhook/ddrio-smx.md): StepManiaX platforms * Beatmania IIDX * iidxio.dll (default): Keyboard, joystick and mouse input * [iidxio-bio2.dll](iidxhook/iidxio-bio2.md): BIO2 driver diff --git a/doc/ddrhook/ddrio-p3io.md b/doc/ddrhook/ddrio-p3io.md new file mode 100644 index 0000000..59be0b2 --- /dev/null +++ b/doc/ddrhook/ddrio-p3io.md @@ -0,0 +1,27 @@ +# ddrio API implementation with DDR P3IO (Dragon) and EXTIO + +This implementation of BT5's ddrio API allows you to use the native DDR P3IO +of a "Dragon PCB" plus the EXTIO with anything the ddrio API supports. + +This is not required to run the actual games supporting the hardware natively. +However, there are various 3rd party applications using the ddrio API where you +might benefit from using actual SD cabinet hardware, e.g. +[vigem-ddrio](../vigem/README.md). + +## Setup + +For hooks, but likely applicable to 3rd party applications (consolidate their +manuals). + +* Driver: You must have the P3IO driver intalled on your system + * Driver from [bemanitools-supplements](https://github.com/djhackersdev/bemanitools-supplement/blob/master/ddr/p3io/README.md) +* Have `ddrio-p3io.dll` in the same folder as your `ddrhookX.dll` +* Rename `ddrio-p3io.dll` to `ddrio.dll` +* Ensure that your `gamestart.bat` actually injects the appropriate ddrhook dll, for example: +```bat +inject ddrhook1.dll ddr.exe ...* +``` +or +```bat +launcher -K ddrhook2.dll arkmdxp3.dll ...* +``` \ No newline at end of file diff --git a/doc/tools/README.md b/doc/tools/README.md index 8168f44..a8cd655 100644 --- a/doc/tools/README.md +++ b/doc/tools/README.md @@ -6,6 +6,7 @@ BT5's supported games, but provides additional features for users and developers Various command line tools for quick and easy testing of BT5 API implementations witohut having to run any target games. +* [ddriotest](ddriotest.md): `ddrio` API * [eamiotest](eamiotest.md): `eamio` API * [iidxiotest](iidxiotest.md): `iidxio` API * [jbiotest](jbiotest.md): `jbio` API @@ -15,6 +16,10 @@ to run any target games. * [aciotest](aciotest.md): Command line tool for quick and easy testing of ACIO devices without having to run a game. +### P3IO DDR testing tool +* [p3io-ddr-tool](p3io-ddr-tool.md): Extensive command line tool to test and debug a real P3IO DDR + (Dragon PCB) IO board + EXTIO + ### Ezusb * [ezusb-iidx-fpga-flash](ezusb-iidx-fpga-flash.md): Tool for flashing the FPGA on ezusb 1 boards. Required if you want to run games without native ezusb support using BT5's iidxio API. diff --git a/doc/tools/ddriotest.md b/doc/tools/ddriotest.md new file mode 100644 index 0000000..3c620c7 --- /dev/null +++ b/doc/tools/ddriotest.md @@ -0,0 +1,3 @@ +Testing tool for development of ddrio libraries used for emulating the main io +hardware on bemanitools for DanceDanceRevoluion. Just place ddriotest.exe and your +custom ddrio.dll in the same folder and run ddriotest.exe. \ No newline at end of file diff --git a/doc/tools/p3io-ddr-tool.md b/doc/tools/p3io-ddr-tool.md new file mode 100644 index 0000000..eb6054f --- /dev/null +++ b/doc/tools/p3io-ddr-tool.md @@ -0,0 +1,2 @@ +Test your real DDR P3IO (Dragon) + EXTIO hardware connected to your machine +using this tool. Just execute it and follow the usage instructions. \ No newline at end of file -- 2.54.0