P3IO and EXTIO drivers, ddrio-p3io implementation #255

Merged
icex2 merged 20 commits from p3iodrv into master 2023-06-25 14:24:08 +03:00
46 changed files with 4258 additions and 224 deletions
+31 -16
View File
@@ -104,13 +104,18 @@ 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/ddriotest/Module.mk
include src/main/ddrio/Module.mk
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
@@ -166,7 +171,9 @@ 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/p3io-ddr-tool/Module.mk
include src/main/p4iodrv/Module.mk
include src/main/p4ioemu/Module.mk
include src/main/popnhook-util/Module.mk
@@ -207,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 \
@@ -228,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 \
@@ -618,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 \
@@ -706,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 \
@@ -724,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 \
@@ -734,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 \
+4
View File
@@ -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
+2 -1
View File
@@ -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
+27
View File
@@ -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 ...*
```
@@ -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
```
+5
View File
@@ -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.
+3
View File
@@ -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.
+2
View File
@@ -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.
+16
View File
@@ -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 \
+35
View File
@@ -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);
}
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef DDRIO_P3IO_CONFIG_H
#define DDRIO_P3IO_CONFIG_H
#include <windows.h>
#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
+11
View File
@@ -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
+427
View File
@@ -0,0 +1,427 @@
#define LOG_MODULE "ddrio-p3io"
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#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
}
}
+8
View File
@@ -0,0 +1,8 @@
exes += ddriotest \
libs_ddriotest := \
ddrio \
util \
src_ddriotest := \
main.c \
+303
View File
@@ -0,0 +1,303 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#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;
}
+4
View File
@@ -0,0 +1,4 @@
libs += extio
src_extio := \
cmd.c \
+16
View File
@@ -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);
}
+49
View File
@@ -0,0 +1,49 @@
#ifndef EXTIO_CMD_H
#define EXTIO_CMD_H
#include <stdint.h>
#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
+5
View File
@@ -0,0 +1,5 @@
libs += extiodrv
src_extiodrv := \
device.c \
extio.c \
+182
View File
@@ -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;
}
+57
View File
@@ -0,0 +1,57 @@
#ifndef EXTIODRV_DEVICE_H
#define EXTIODRV_DEVICE_H
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
/**
* 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
+110
View File
@@ -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;
}
+45
View File
@@ -0,0 +1,45 @@
#ifndef EXTIODRV_EXTIO_H
#define EXTIODRV_EXTIO_H
#include <windows.h>
#include <stdbool.h>
#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
+9
View File
@@ -0,0 +1,9 @@
exes += extiotest \
libs_extiotest := \
extiodrv \
extio \
util \
src_extiotest := \
main.c \
+55
View File
@@ -0,0 +1,55 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#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;
}
+16
View File
@@ -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 \
+774
View File
@@ -0,0 +1,774 @@
#define LOG_MODULE "p3io-ddr-tool"
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#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;
}
+406
View File
@@ -0,0 +1,406 @@
#define LOG_MODULE "mode-test"
#include <stdio.h>
#include <stdlib.h>
#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;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef P3IO_DDR_TOOL_MODE_TEST_H
#define P3IO_DDR_TOOL_MODE_TEST_H
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
bool mode_test_proc(HANDLE handle_p3io, HANDLE handle_extio);
#endif
+36 -12
View File
@@ -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;
}
+176 -45
View File
@@ -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,96 +150,157 @@ 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);
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
+81
View File
@@ -0,0 +1,81 @@
#ifndef P3IO_DDR_H
#define P3IO_DDR_H
#include <stdint.h>
#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
+9 -6
View File
@@ -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;
}
+10
View File
@@ -0,0 +1,10 @@
libs += p3iodrv
libs_p4iodrv := \
p3io \
util \
src_p3iodrv := \
ddr.c \
device.c \
+326
View File
@@ -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;
}
+115
View File
@@ -0,0 +1,115 @@
#ifndef P3IODRV_DDR_H
#define P3IODRV_DDR_H
#include <stdbool.h>
#include <stddef.h>
#include <windows.h>
#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
+429
View File
@@ -0,0 +1,429 @@
#define LOG_MODULE "p3iodrv-device"
#include <inttypes.h>
#include <stdint.h>
#include <stdlib.h>
// clang-format off
// Don't format because the order is important here
#include <windows.h>
#include <setupapi.h>
// 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;
}
+105
View File
@@ -0,0 +1,105 @@
#ifndef P3IODRV_DEVICE_H
#define P3IODRV_DEVICE_H
#include <stdbool.h>
#include <stddef.h>
#include <windows.h>
#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
+86 -96
View File
@@ -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;
}
+1 -17
View File
@@ -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
+15 -7
View File
@@ -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;
}
+2 -1
View File
@@ -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);
+4 -3
View File
@@ -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;
+18 -4
View File
@@ -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);
}
+13 -15
View File
@@ -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;
+8 -1
View File
@@ -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