P3IO and EXTIO drivers, ddrio-p3io implementation #255
@@ -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 \
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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 \
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
exes += ddriotest \
|
||||
|
||||
libs_ddriotest := \
|
||||
ddrio \
|
||||
util \
|
||||
|
||||
src_ddriotest := \
|
||||
main.c \
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
libs += extio
|
||||
|
||||
src_extio := \
|
||||
cmd.c \
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "cmd.h"
|
||||
|
||||
uint8_t extio_cmd_checksum(const struct extio_cmd_write *write)
|
||||
{
|
||||
const uint8_t *data;
|
||||
uint32_t checksum;
|
||||
|
||||
data = (const uint8_t *) write;
|
||||
|
||||
checksum = 0;
|
||||
checksum += data[0];
|
||||
checksum += data[1];
|
||||
checksum += data[2];
|
||||
|
||||
return (uint8_t) (checksum & 0x7F);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
libs += extiodrv
|
||||
|
||||
src_extiodrv := \
|
||||
device.c \
|
||||
extio.c \
|
||||
@@ -0,0 +1,182 @@
|
||||
#define LOG_MODULE "extiodrv-device"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
HRESULT extiodrv_device_open(const char *port, HANDLE *handle)
|
||||
{
|
||||
HRESULT hr;
|
||||
COMMTIMEOUTS ct;
|
||||
DCB dcb;
|
||||
|
||||
log_assert(port);
|
||||
log_assert(handle);
|
||||
|
||||
log_info("Opening extio on %s", port);
|
||||
|
||||
*handle = CreateFile(
|
||||
port,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (*handle == INVALID_HANDLE_VALUE) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("Failed to open %s: %lX", port, hr);
|
||||
|
||||
goto early_fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(*handle, EV_RXCHAR)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetCommMask failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(*handle, 0x4000u, 0x4000u)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetupComm failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!PurgeComm(
|
||||
*handle,
|
||||
PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("PurgeComm failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ct.ReadTotalTimeoutConstant = 0;
|
||||
ct.WriteTotalTimeoutConstant = 0;
|
||||
ct.ReadIntervalTimeout = -1;
|
||||
ct.ReadTotalTimeoutMultiplier = 10;
|
||||
ct.WriteTotalTimeoutMultiplier = 100;
|
||||
|
||||
if (!SetCommTimeouts(*handle, &ct)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetCommTimeouts failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.DCBlength = sizeof(dcb);
|
||||
|
||||
if (!GetCommState(*handle, &dcb)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("GetCommState failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dcb.BaudRate = 38400;
|
||||
dcb.fBinary = TRUE;
|
||||
dcb.fParity = FALSE;
|
||||
dcb.fDtrControl = DTR_CONTROL_ENABLE;
|
||||
dcb.fDsrSensitivity = FALSE;
|
||||
dcb.fOutX = FALSE;
|
||||
dcb.fInX = FALSE;
|
||||
dcb.fErrorChar = FALSE;
|
||||
dcb.fNull = FALSE;
|
||||
dcb.fRtsControl = RTS_CONTROL_ENABLE;
|
||||
dcb.fAbortOnError = FALSE;
|
||||
dcb.ByteSize = 8;
|
||||
dcb.Parity = NOPARITY;
|
||||
dcb.StopBits = ONESTOPBIT;
|
||||
dcb.XonChar = 17;
|
||||
dcb.XoffChar = 19;
|
||||
dcb.XonLim = 100;
|
||||
dcb.XoffLim = 100;
|
||||
|
||||
if (!SetCommState(*handle, &dcb)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("SetCommState failed: %lX", hr);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_info("[%p] Opened extio device on %s", *handle, port);
|
||||
|
||||
return S_OK;
|
||||
|
||||
fail:
|
||||
CloseHandle(*handle);
|
||||
*handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
early_fail:
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT extiodrv_device_close(HANDLE *handle)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
log_assert(handle);
|
||||
|
||||
if (CloseHandle(*handle) == FALSE) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hr;
|
||||
}
|
||||
|
||||
*handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT extiodrv_device_read(
|
||||
HANDLE handle, void *bytes, size_t nbytes, size_t *read_bytes)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD dw_read_bytes;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(bytes);
|
||||
log_assert(read_bytes);
|
||||
|
||||
if (!ClearCommError(handle, NULL, NULL)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("[%p] ClearCommError failed: %lX", handle, hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (!ReadFile(handle, bytes, nbytes, &dw_read_bytes, NULL)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("[%p] ReadFile failed: %lX", handle, hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
*read_bytes = dw_read_bytes;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT extiodrv_device_write(
|
||||
HANDLE handle, const void *bytes, size_t nbytes, size_t *written_bytes)
|
||||
{
|
||||
HRESULT hr;
|
||||
DWORD dw_written_bytes;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(bytes);
|
||||
log_assert(written_bytes);
|
||||
|
||||
if (!WriteFile(handle, bytes, nbytes, &dw_written_bytes, NULL)) {
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
log_warning("[%p] WriteFile failed: %lX", handle, hr);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
*written_bytes = dw_written_bytes;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
exes += extiotest \
|
||||
|
||||
libs_extiotest := \
|
||||
extiodrv \
|
||||
extio \
|
||||
util \
|
||||
|
||||
src_extiotest := \
|
||||
main.c \
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 \
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
libs += p3iodrv
|
||||
|
||||
libs_p4iodrv := \
|
||||
p3io \
|
||||
util \
|
||||
|
||||
src_p3iodrv := \
|
||||
ddr.c \
|
||||
device.c \
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
#define LOG_MODULE "p3iodrv-ddr"
|
||||
|
||||
#include "p3io/cmd.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#include "ddr.h"
|
||||
#include "device.h"
|
||||
|
||||
static uint8_t p3iodrv_ddr_seq_counter;
|
||||
|
||||
static uint8_t p3iodrv_ddr_get_and_update_seq_counter()
|
||||
{
|
||||
return p3iodrv_ddr_seq_counter++ & 0xF;
|
||||
}
|
||||
|
||||
static HRESULT p3iodrv_ddr_check_resp(
|
||||
const union p3io_resp_any *resp,
|
||||
uint8_t expected_size,
|
||||
const union p3io_req_any *corresponding_req)
|
||||
{
|
||||
uint8_t actual_size;
|
||||
|
||||
log_assert(resp);
|
||||
|
||||
actual_size = p3io_get_full_resp_size(resp);
|
||||
|
||||
if (actual_size != expected_size) {
|
||||
log_warning(
|
||||
"Incorrect response size, actual %d != expected %d",
|
||||
actual_size,
|
||||
expected_size);
|
||||
return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
|
||||
}
|
||||
|
||||
if (resp->hdr.seq_no != corresponding_req->hdr.seq_no) {
|
||||
log_warning(
|
||||
"Incorrect sequence num in response, actual %d != expected %d",
|
||||
resp->hdr.seq_no,
|
||||
corresponding_req->hdr.seq_no);
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
}
|
||||
|
||||
if (resp->hdr.cmd != corresponding_req->hdr.cmd) {
|
||||
log_warning(
|
||||
"Incorrect command in response, actual 0x%d != expected 0x%d",
|
||||
resp->hdr.cmd,
|
||||
corresponding_req->hdr.cmd);
|
||||
return HRESULT_FROM_WIN32(ERROR_BAD_COMMAND);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT p3iodrv_ddr_init(HANDLE handle)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t seq_cnt;
|
||||
union p3io_req_any req;
|
||||
union p3io_resp_any resp;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
|
||||
|
||||
p3io_req_hdr_init(&req.hdr, seq_cnt, P3IO_CMD_INIT, sizeof(req.init));
|
||||
|
||||
hr = p3iodrv_device_transfer(handle, &req, &resp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.init), &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (resp.init.status != 0) {
|
||||
log_warning("Initialization failed");
|
||||
return HRESULT_FROM_WIN32(ERROR_GEN_FAILURE);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT p3iodrv_ddr_get_version(
|
||||
HANDLE handle,
|
||||
char str[4],
|
||||
uint32_t *major,
|
||||
uint32_t *minor,
|
||||
uint32_t *patch)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t seq_cnt;
|
||||
union p3io_req_any req;
|
||||
union p3io_resp_any resp;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(major);
|
||||
log_assert(minor);
|
||||
log_assert(patch);
|
||||
|
||||
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
|
||||
|
||||
p3io_req_hdr_init(
|
||||
&req.hdr, seq_cnt, P3IO_CMD_GET_VERSION, sizeof(req.version));
|
||||
|
||||
hr = p3iodrv_device_transfer(handle, &req, &resp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.version), &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
memcpy(str, resp.version.str, sizeof(char[4]));
|
||||
*major = resp.version.major;
|
||||
*minor = resp.version.minor;
|
||||
*patch = resp.version.patch;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT p3iodrv_ddr_set_watchdog(HANDLE handle, bool enable)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t seq_cnt;
|
||||
union p3io_req_any req;
|
||||
union p3io_resp_any resp;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
|
||||
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
|
||||
|
||||
p3io_req_hdr_init(
|
||||
&req.hdr, seq_cnt, P3IO_CMD_SET_WATCHDOG, sizeof(req.watchdog));
|
||||
|
||||
req.watchdog.enable = enable ? 1 : 0;
|
||||
|
||||
hr = p3iodrv_device_transfer(handle, &req, &resp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.watchdog), &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT p3iodrv_ddr_get_dipsw(HANDLE handle, uint8_t *state)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t seq_cnt;
|
||||
union p3io_req_any req;
|
||||
union p3io_resp_any resp;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(state);
|
||||
|
||||
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
|
||||
|
||||
p3io_req_hdr_init(
|
||||
&req.hdr,
|
||||
seq_cnt,
|
||||
P3IO_CMD_GET_CAB_TYPE_OR_DIPSW,
|
||||
sizeof(req.cab_type_or_dipsw));
|
||||
|
||||
req.cab_type_or_dipsw.cab_type_or_dipsw = P3IO_DIP_SW_SELECTOR;
|
||||
|
||||
hr = p3iodrv_device_transfer(handle, &req, &resp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.cab_type_or_dipsw), &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
*state = resp.cab_type_or_dipsw.state;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT p3iodrv_ddr_get_cab_type(HANDLE handle, enum p3io_cab_type *type)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t seq_cnt;
|
||||
union p3io_req_any req;
|
||||
union p3io_resp_any resp;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(type);
|
||||
|
||||
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
|
||||
|
||||
p3io_req_hdr_init(
|
||||
&req.hdr,
|
||||
seq_cnt,
|
||||
P3IO_CMD_GET_CAB_TYPE_OR_DIPSW,
|
||||
sizeof(req.cab_type_or_dipsw));
|
||||
|
||||
req.cab_type_or_dipsw.cab_type_or_dipsw = P3IO_CAB_TYPE_SELECTOR;
|
||||
|
||||
hr = p3iodrv_device_transfer(handle, &req, &resp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.cab_type_or_dipsw), &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
*type = resp.cab_type_or_dipsw.state;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT p3iodrv_ddr_get_video_freq(HANDLE handle, enum p3io_video_freq *freq)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t seq_cnt;
|
||||
union p3io_req_any req;
|
||||
union p3io_resp_any resp;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(freq);
|
||||
|
||||
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
|
||||
|
||||
p3io_req_hdr_init(
|
||||
&req.hdr, seq_cnt, P3IO_CMD_GET_VIDEO_FREQ, sizeof(req.video_freq));
|
||||
|
||||
// Must be set to 5 in order to return the right values. There might be more
|
||||
// to this, e.g. this being some kind of selector where to read from the
|
||||
// JAMMA harness (?) but this is good for now to get what we want to know
|
||||
// for DDR
|
||||
req.video_freq.unknown_05 = 5;
|
||||
|
||||
hr = p3iodrv_device_transfer(handle, &req, &resp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.video_freq), &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
*freq = (enum p3io_video_freq) resp.video_freq.video_freq;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT p3iodrv_ddr_get_jamma(HANDLE handle, struct p3io_ddr_jamma *jamma)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint32_t *jamma_raw;
|
||||
|
||||
jamma_raw = (uint32_t *) jamma;
|
||||
|
||||
hr = p3iodrv_device_read_jamma(handle, jamma_raw);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Inputs are active low for p1, p2 and operator bits
|
||||
jamma_raw[0] ^= 0xFFFFFF00;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT
|
||||
p3iodrv_ddr_set_outputs(HANDLE handle, const struct p3io_ddr_output *state)
|
||||
{
|
||||
HRESULT hr;
|
||||
uint8_t seq_cnt;
|
||||
union p3io_req_any req;
|
||||
union p3io_resp_any resp;
|
||||
|
||||
log_assert(handle != INVALID_HANDLE_VALUE);
|
||||
log_assert(state);
|
||||
|
||||
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
|
||||
|
||||
p3io_req_hdr_init(
|
||||
&req.hdr, seq_cnt, P3IO_CMD_SET_OUTPUTS, sizeof(req.set_outputs));
|
||||
|
||||
memcpy(&req.set_outputs.outputs, state, sizeof(req.set_outputs.outputs));
|
||||
|
||||
// Always set by the game like this
|
||||
req.set_outputs.unk_FF = 0xFF;
|
||||
|
||||
hr = p3iodrv_device_transfer(handle, &req, &resp);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.set_outputs), &req);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user