mirror of
https://github.com/djhackersdev/bemanitools.git
synced 2026-09-15 11:55:10 -05:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
54e7d783ef | ||
|
|
4ea1397af2 | ||
|
|
fb593df4a1 | ||
|
|
f0c33663e9 | ||
|
|
87ee37d059 | ||
|
|
5688fb7319 | ||
|
|
d529ee484f | ||
|
|
7c43fb7163 | ||
|
|
652d2ef633 | ||
|
|
78884aebad | ||
|
|
549f00bfb3 | ||
|
|
aab45f145f | ||
|
|
6171f0dc60 | ||
|
|
2f05760f4e | ||
|
|
7b5e4772ea | ||
|
|
e266aed1f9 | ||
|
|
0b0e7faef0 | ||
|
|
53866130cb | ||
|
|
dbb8c45a9f | ||
|
|
8ed7d6db71 | ||
|
|
4c746df6a8 | ||
|
|
7d485e7c7e | ||
|
|
deb6e5cf77 | ||
|
|
2a03690772 | ||
|
|
6ce4cce500 | ||
|
|
c6e59bae9b | ||
|
|
31e15ec5ea | ||
|
|
9cef7c03d0 | ||
|
|
f9b37c7a72 | ||
|
|
609c19b0ca | ||
|
|
75b27d5992 | ||
|
|
965df98093 | ||
|
|
0c06d224cc | ||
|
|
fb3d2ea312 | ||
|
|
089253a537 | ||
|
|
a4436747de | ||
|
|
43b11c8778 | ||
|
|
dd7fd860b5 | ||
|
|
18842a1dd3 | ||
|
|
18f6d9ef70 | ||
|
|
592967e191 | ||
|
|
6614cc801e | ||
|
|
8e1151ac02 | ||
|
|
4b60cb6c6c | ||
|
|
770bae4169 | ||
|
|
49d0bc638b | ||
|
|
837affc66c |
@@ -12,6 +12,8 @@
|
||||
necessary -->
|
||||
|
||||
## Steps to reproduce
|
||||
<!-- Try running the game with a default configuration as well. If that also causes issues, please point that out here in the report. This might speed up the debugging process.-->
|
||||
|
||||
<!--- Provide a detailed step by step description how to reproduce this issue -->
|
||||
1.
|
||||
2.
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# Release history
|
||||
## 5.35
|
||||
* Jubeat: Add jbio-p4io
|
||||
* Nostalgia: Add nostio-panb
|
||||
* Misc: Refactor aciodrv to support multiple devices using the same port (with aciomgr)
|
||||
* Misc: Fix d3d9ex windowed hook not behaving correctly (size/framed)
|
||||
* Misc: Fix docs not being included with release
|
||||
* Various bugfixes
|
||||
|
||||
## 5.34
|
||||
* IIDX: Support IO and card reader feature switches to disable emulation in iidxhook4-7
|
||||
|
||||
29
Module.mk
29
Module.mk
@@ -72,12 +72,14 @@ cflags += \
|
||||
# that is still ABI-compatible with the real build its target links against.
|
||||
|
||||
avsvers_32 := 1700 1603 1601 1508 1403 1304 1101 803 0
|
||||
avsvers_64 := 1700 1603 1601 1508
|
||||
avsvers_64 := 1700 1603 1601 1509 1508
|
||||
|
||||
imps += avs avs-ea3
|
||||
|
||||
include src/main/aciodrv/Module.mk
|
||||
include src/main/aciodrv-proc/Module.mk
|
||||
include src/main/acioemu/Module.mk
|
||||
include src/main/aciomgr/Module.mk
|
||||
include src/main/aciotest/Module.mk
|
||||
include src/main/asio/Module.mk
|
||||
include src/main/bio2drv/Module.mk
|
||||
@@ -136,6 +138,7 @@ include src/main/inject/Module.mk
|
||||
include src/main/jbhook/Module.mk
|
||||
include src/main/jbhook1/Module.mk
|
||||
include src/main/jbio-magicbox/Module.mk
|
||||
include src/main/jbio-p4io/Module.mk
|
||||
include src/main/jbio/Module.mk
|
||||
include src/main/jbiotest/Module.mk
|
||||
include src/main/launcher/Module.mk
|
||||
@@ -143,6 +146,7 @@ include src/main/mempatch-hook/Module.mk
|
||||
include src/main/mm/Module.mk
|
||||
include src/main/p3io/Module.mk
|
||||
include src/main/p3ioemu/Module.mk
|
||||
include src/main/p4iodrv/Module.mk
|
||||
include src/main/p4ioemu/Module.mk
|
||||
include src/main/pcbidgen/Module.mk
|
||||
include src/main/sdvxhook/Module.mk
|
||||
@@ -378,6 +382,7 @@ $(zipdir)/iidx-27.zip: \
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
$(zipdir)/iidx-hwio-x86.zip: \
|
||||
build/bin/indep-32/aciomgr.dll \
|
||||
build/bin/indep-32/eamio-icca.dll \
|
||||
build/bin/indep-32/iidxio-bio2.dll \
|
||||
build/bin/indep-32/iidxio-ezusb.dll \
|
||||
@@ -390,6 +395,7 @@ $(zipdir)/iidx-hwio-x86.zip: \
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
$(zipdir)/iidx-hwio-x64.zip: \
|
||||
build/bin/indep-64/aciomgr.dll \
|
||||
build/bin/indep-64/eamio-icca.dll \
|
||||
build/bin/indep-64/iidxio-bio2.dll \
|
||||
build/bin/indep-64/iidxio-ezusb.dll \
|
||||
@@ -441,6 +447,17 @@ $(zipdir)/jb-08.zip: \
|
||||
$(V)echo ... $@
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
$(zipdir)/jb-hwio.zip: \
|
||||
build/bin/indep-32/aciomgr.dll \
|
||||
build/bin/indep-32/eamio-icca.dll \
|
||||
build/bin/indep-32/jbio-magicbox.dll \
|
||||
build/bin/indep-32/jbio-p4io.dll \
|
||||
dist/jb/jbio-h44b.conf \
|
||||
dist/jb/eamio-icc.conf \
|
||||
| $(zipdir)/
|
||||
$(V)echo ... $@
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
$(zipdir)/sdvx-01-to-04.zip: \
|
||||
build/bin/avs2_1508-32/launcher.exe \
|
||||
build/bin/avs2_1508-32/sdvxhook.dll \
|
||||
@@ -483,6 +500,8 @@ $(zipdir)/sdvx-05-cn.zip: \
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
$(zipdir)/sdvx-hwio-x86.zip: \
|
||||
build/bin/indep-32/aciomgr.dll \
|
||||
build/bin/indep-32/eamio-icca.dll \
|
||||
build/bin/indep-32/sdvxio-kfca.dll \
|
||||
build/bin/indep-32/sdvxio-bio2.dll \
|
||||
build/bin/indep-32/vigem-sdvxio.exe \
|
||||
@@ -491,6 +510,8 @@ $(zipdir)/sdvx-hwio-x86.zip: \
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
$(zipdir)/sdvx-hwio-x64.zip: \
|
||||
build/bin/indep-64/aciomgr.dll \
|
||||
build/bin/indep-64/eamio-icca.dll \
|
||||
build/bin/indep-64/sdvxio-kfca.dll \
|
||||
build/bin/indep-64/sdvxio-bio2.dll \
|
||||
build/bin/indep-64/vigem-sdvxio.exe \
|
||||
@@ -549,10 +570,7 @@ $(zipdir)/bst.zip: \
|
||||
$(V)zip -j $@ $^
|
||||
|
||||
$(zipdir)/doc.zip: \
|
||||
doc/iidxhook \
|
||||
doc/sdvxhook \
|
||||
doc/jbhook \
|
||||
doc/tools \
|
||||
doc/ \
|
||||
| $(zipdir)/
|
||||
$(V)echo ... $@
|
||||
$(V)zip -r $@ $^
|
||||
@@ -602,6 +620,7 @@ $(BUILDDIR)/bemanitools.zip: \
|
||||
$(zipdir)/jb-01.zip \
|
||||
$(zipdir)/jb-05-to-07.zip \
|
||||
$(zipdir)/jb-08.zip \
|
||||
$(zipdir)/jb-hwio.zip \
|
||||
$(zipdir)/sdvx-01-to-04.zip \
|
||||
$(zipdir)/sdvx-05.zip \
|
||||
$(zipdir)/sdvx-05-cn.zip \
|
||||
|
||||
32
README.md
32
README.md
@@ -1,5 +1,5 @@
|
||||
# Bemanitools 5
|
||||
[](https://dev.s-ul.eu/djhackers/bemanitools/commits/master)
|
||||
[](https://dev.s-ul.net/djhackers/bemanitools/commits/master)
|
||||
|
||||
Version: 5.35
|
||||
|
||||
@@ -73,7 +73,7 @@ The tl;dr version and golden rules of the sections in the document:
|
||||
Play your favorite non-bemani arcade games with any hardware, e.g. real cabinet hardware, that
|
||||
implements BT5 APIs.
|
||||
|
||||
See the dedicated [readme](docs/vigem/README.md) for details.
|
||||
See the dedicated [readme](doc/vigem/README.md) for details.
|
||||
|
||||
## Supported platforms
|
||||
Our main platforms are currently Windows XP and Windows 7 which are also the target platforms on the original hardware
|
||||
@@ -110,6 +110,34 @@ important aspects you should know before you get started with development.
|
||||
### API
|
||||
Please refer to the [API documentation](doc/api.md).
|
||||
|
||||
## Release process
|
||||
Steps for the release process:
|
||||
1. Update the corresponding section in the [changelog](CHANGELOG.md) with bullet points reflecting
|
||||
major features merged into master since the previous release
|
||||
1. Make sure you have the latest `master` state pulled to your local copy
|
||||
1. Tag the release with the next release number which also be found at the
|
||||
[top of the readme](#bemanitools-5) as it always reflects the current version beinged worked on
|
||||
on the `master` branch , e.g. 5.35: `git tag 5.35`
|
||||
1. Push the tag to upstream: `git push origin 5.35`
|
||||
1. The [build pipeline](https://dev.s-ul.net/djhackers/bemanitools/-/pipelines) should start
|
||||
automatically once the tag is pushed including the steps `build` and `upload-release`
|
||||
1. Once completed successfully, the release is uploaded
|
||||
1. Take the changelog of the published version and notify the pigs in the stall about it:
|
||||
1. New post in thread
|
||||
```
|
||||
<insert version here> released: <direct link to published version>
|
||||
|
||||
Changelog:
|
||||
<paste changelog here>
|
||||
```
|
||||
1. Update the OP title by bumping the version number in it
|
||||
1. Update the OP post by extending it accordingly (see previous entries)
|
||||
|
||||
1. Bump the version number at the [top of this readme](#bemanitools-5) and add a new empty section
|
||||
in the [changelog](CHANGELOG.md) with the new version number as title. Commit changes and push to
|
||||
`master` branch
|
||||
1. Continue developing and merging MRs until you decide its time for another release
|
||||
|
||||
## License
|
||||
Source code license is the Unlicense; you are permitted to do with this as thou wilt. For details, please refer to the
|
||||
[LICENSE file](LICENSE) included with the source code.
|
||||
|
||||
8
dist/iidx/iidxhook-27.conf
vendored
8
dist/iidx/iidxhook-27.conf
vendored
@@ -34,7 +34,7 @@ gfx.framed=true
|
||||
# Run the game windowed
|
||||
gfx.windowed=false
|
||||
|
||||
# Confine mouse coursor to window
|
||||
# Confine mouse cursor to window
|
||||
gfx.confined=false
|
||||
|
||||
# Windowed width, -1 for default size
|
||||
@@ -43,6 +43,12 @@ gfx.window_width=-1
|
||||
# Windowed height, -1 for default size
|
||||
gfx.window_height=-1
|
||||
|
||||
# Windowed X, -1 for default X position
|
||||
gfx.window_x=-1
|
||||
|
||||
# Windowed Y, -1 for default Y position
|
||||
gfx.window_y=-1
|
||||
|
||||
# Forced refresh rate, -1 to not force any (try 59 or 60 if monitor check fails to lock on high refresh rate monitors)
|
||||
gfx.forced_refresh_rate=-1
|
||||
|
||||
|
||||
5
dist/jb/eamio-icc.conf
vendored
Normal file
5
dist/jb/eamio-icc.conf
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# ICCA serial port
|
||||
icc.port=COM2
|
||||
|
||||
# ICCA bus baudrate (real devices expect 57600)
|
||||
icc.baud=57600
|
||||
5
dist/jb/jbio-h44b.conf
vendored
Normal file
5
dist/jb/jbio-h44b.conf
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# H44B serial port
|
||||
h44b.port=COM2
|
||||
|
||||
# H44B bus baudrate (real devices expect 57600)
|
||||
h44b.baud=57600
|
||||
8
dist/sdvx5/sdvxhook2.conf
vendored
8
dist/sdvx5/sdvxhook2.conf
vendored
@@ -19,7 +19,7 @@ gfx.framed=true
|
||||
# Run the game windowed
|
||||
gfx.windowed=false
|
||||
|
||||
# Confine mouse coursor to window
|
||||
# Confine mouse cursor to window
|
||||
gfx.confined=false
|
||||
|
||||
# Windowed width, -1 for default size
|
||||
@@ -28,6 +28,12 @@ gfx.window_width=1080
|
||||
# Windowed height, -1 for default size
|
||||
gfx.window_height=1920
|
||||
|
||||
# Windowed X, -1 for default X position
|
||||
gfx.window_x=-1
|
||||
|
||||
# Windowed Y, -1 for default Y position
|
||||
gfx.window_y=-1
|
||||
|
||||
# Forced refresh rate, -1 to not force any (try 59 or 60 if monitor check fails to lock on high refresh rate monitors)
|
||||
gfx.forced_refresh_rate=-1
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
# BIO2 sub IO turntable bypass cable
|
||||
If you upgraded a C02/IO2 wired cabinet, the turntables are connected to the
|
||||
If you upgraded a C02 wired cabinet, the turntables are connected to the
|
||||
sub IO via a serial line that goes to another sub IO behind the front panel
|
||||
next to the 16seg display. This introduces additional latency as the input
|
||||
data needs to pass through multiple systems/PCBs until it reaches the game.
|
||||
|
||||
This can be reduced by using a so-called "bypass" cable, that connects the
|
||||
turntables directly to the BIO2 sub IO board reducing latency significantly.
|
||||
turntables directly to the BIO2 sub IO board by emulating the wiring of a
|
||||
D01 cabinet, reducing latency significantly.
|
||||
|
||||
This cable could be ordered with any IIDX upgrade kit including the BIO sub IO
|
||||
board but can also be easily created from scratch.
|
||||
This cable could be ordered with the IIDX Rootage upgrade kit but can also
|
||||
be easily created from scratch.
|
||||
|
||||
## Parts
|
||||
* Some wires
|
||||
@@ -51,4 +52,4 @@ Pins of the two connectors need to be connected like this:
|
||||
| 5 | 5 |
|
||||
| 6 | 4 |
|
||||
| 7 | 8 |
|
||||
| 8 | 8 |
|
||||
| 8 | 8 |
|
||||
|
||||
18
doc/jbhook/jbio-p4io.md
Normal file
18
doc/jbhook/jbio-p4io.md
Normal file
@@ -0,0 +1,18 @@
|
||||
This library talks to a P4IO (panel) and H44B through ACIO (lights) of a jubeat cab and implements the jbio API of BT5.
|
||||
Thus, it allows you to use a modern jubeat cab with *any* version of jubeat that is supported by BT5.
|
||||
|
||||
# Setup
|
||||
* Rename `jbio-p4io.dll` to `jbio.dll`.
|
||||
* Make sure `jbio.dll` is in the same folder as both `jbhook.dll`, `aciomgr.dll` and `jubeat.dll`
|
||||
* If you want IC card support via eamio-icca, ensure you also rename `eamio-icca.dll` to `eamio.dll`
|
||||
* Ensure that your `gamestart.bat` actually injects the appropriate jbhook dll
|
||||
for example:
|
||||
```
|
||||
launcher -K jbhook.dll jubeat.dll ...*
|
||||
```
|
||||
or that the application otherwise uses `jbio.dll`
|
||||
|
||||
* If you have P4IO but no H44B, lights will be disabled
|
||||
* You can change the port and baudrate of the H44B node by editing the `jbio-h44b.conf` that should be created by default
|
||||
* If you are using IC card support, you can edit `eamio-icc.conf` to change the port and baudrate
|
||||
* If you are playing on a real cab, the port and baud of H44B and ICCA should be identical
|
||||
@@ -58,5 +58,5 @@ Turntables are either in absolute or relative mode depending on how it's defined
|
||||
* Make sure that you follow the instructions exactly from the release page
|
||||
([Prerequisites for Windows 7](https://github.com/ViGEm/ViGEmBus/wiki/Prerequisites-for-Windows-7))
|
||||
* If you get an error while trying to install KB3033929, re-enable windows update
|
||||
* Make sure to ewfmgr C: -commit and reboot after installing the drivers (this only needs to be
|
||||
done once)
|
||||
* If you can't find some of these files, see bemanitools-supplements
|
||||
* Make sure to ewfmgr C: -commit and reboot after installing the drivers (this only needs to be done once)
|
||||
|
||||
@@ -19,6 +19,8 @@ Thus, it allows you to use a real cab with *any* game that supports xinput.
|
||||
* VOL LR are mapped to L thumbstick X/Y (either in absolute or relative mode depending on the config)
|
||||
|
||||
# Additional Notes For Cabinets (Running on embedded Windows 7):
|
||||
* Make sure that you follow the instructions exactly from the release page ([Prerequisites for Windows 7](https://github.com/ViGEm/ViGEmBus/wiki/Prerequisites-for-Windows-7))
|
||||
* Make sure that you follow the instructions exactly from the release page
|
||||
([Prerequisites for Windows 7](https://github.com/ViGEm/ViGEmBus/wiki/Prerequisites-for-Windows-7))
|
||||
* If you get an error while trying to install KB3033929, re-enable windows update
|
||||
* If you can't find some of these files, see bemanitools-supplements
|
||||
* Make sure to ewfmgr C: -commit and reboot after installing the drivers (this only needs to be done once)
|
||||
|
||||
7
src/imports/import_64_1509_avs-ea3.def
Normal file
7
src/imports/import_64_1509_avs-ea3.def
Normal file
@@ -0,0 +1,7 @@
|
||||
LIBRARY libavs-win64-ea3
|
||||
|
||||
EXPORTS
|
||||
ea3_boot @8 NONAME
|
||||
ea3_get_boot_status @11 NONAME
|
||||
ea3_get_pp_status @10 NONAME
|
||||
ea3_shutdown @9 NONAME
|
||||
32
src/imports/import_64_1509_avs.def
Normal file
32
src/imports/import_64_1509_avs.def
Normal file
@@ -0,0 +1,32 @@
|
||||
LIBRARY libavs-win64
|
||||
|
||||
EXPORTS
|
||||
avs_boot @285 NONAME
|
||||
avs_fs_close @65 NONAME
|
||||
avs_fs_lseek @59 NONAME
|
||||
avs_fs_lseek64 @60 NONAME
|
||||
avs_fs_open @58 NONAME
|
||||
avs_fs_read @61 NONAME
|
||||
avs_net_ctrl @98 NONAME
|
||||
avs_shutdown @286 NONAME
|
||||
avs_thread_create @6 NONAME
|
||||
avs_thread_destroy @8 NONAME
|
||||
avs_thread_exit @12 NONAME
|
||||
avs_thread_join @13 NONAME
|
||||
log_body_fatal @357 NONAME
|
||||
log_body_info @359 NONAME
|
||||
log_body_misc @360 NONAME
|
||||
log_body_warning @358 NONAME
|
||||
property_create @127 NONAME
|
||||
property_desc_to_buffer @129 NONAME
|
||||
property_destroy @128 NONAME
|
||||
property_insert_read @131 NONAME
|
||||
property_node_create @145 NONAME
|
||||
property_node_refer @158 NONAME
|
||||
property_node_remove @146 NONAME
|
||||
property_psmap_export @162 NONAME
|
||||
property_psmap_import @161 NONAME
|
||||
property_read_query_memsize @159 NONAME
|
||||
property_search @144 NONAME
|
||||
std_getenv @207 NONAME
|
||||
std_setenv @208 NONAME
|
||||
@@ -5,9 +5,11 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "acio/h44b.h"
|
||||
#include "acio/hdxs.h"
|
||||
#include "acio/icca.h"
|
||||
#include "acio/kfca.h"
|
||||
#include "acio/panb.h"
|
||||
|
||||
#define AC_IO_SOF 0xAA
|
||||
#define AC_IO_ESCAPE 0xFF
|
||||
@@ -37,6 +39,7 @@ enum ac_io_node_type {
|
||||
AC_IO_NODE_TYPE_KFCA = 0x09060000,
|
||||
AC_IO_NODE_TYPE_BI2A = 0x0d060000,
|
||||
AC_IO_NODE_TYPE_RVOL = 0x09060001,
|
||||
AC_IO_NODE_TYPE_PANB = 0x090E0000,
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
@@ -69,12 +72,17 @@ struct ac_io_message {
|
||||
uint8_t param;
|
||||
struct ac_io_version version;
|
||||
|
||||
struct ac_io_h44b_output h44b_output;
|
||||
|
||||
struct ac_io_icca_misc icca_misc;
|
||||
struct ac_io_icca_state icca_state;
|
||||
|
||||
struct ac_io_kfca_poll_in kfca_poll_in;
|
||||
struct ac_io_kfca_poll_out kfca_poll_out;
|
||||
|
||||
struct ac_io_panb_poll_in panb_poll_in;
|
||||
struct ac_io_panb_poll_out panb_poll_out;
|
||||
|
||||
struct ac_io_hdxs_output hdxs_output;
|
||||
};
|
||||
} cmd;
|
||||
|
||||
@@ -14,6 +14,6 @@ struct ac_io_h44b_output {
|
||||
uint8_t right_rgb[3];
|
||||
uint8_t title_rgb[3];
|
||||
uint8_t woofer_rgb[3];
|
||||
};
|
||||
} __attribute__((__packed__));
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -29,6 +29,14 @@ enum ac_io_icca_sensor_state {
|
||||
AC_IO_ICCA_SENSOR_MASK_BACK_ON = (1 << 5)
|
||||
};
|
||||
|
||||
enum ac_io_icca_status_code {
|
||||
AC_IO_ICCA_STATUS_FAULT = 0x00,
|
||||
AC_IO_ICCA_STATUS_IDLE = 0x01,
|
||||
AC_IO_ICCA_STATUS_GOT_UID = 0x02,
|
||||
AC_IO_ICCA_STATUS_IDLE_NEW = 0x04,
|
||||
AC_IO_ICCA_STATUS_BUSY_NEW = 0x01,
|
||||
};
|
||||
|
||||
enum ac_io_icca_keypad_mask {
|
||||
AC_IO_ICCA_KEYPAD_MASK_EMPTY = (1 << 0),
|
||||
AC_IO_ICCA_KEYPAD_MASK_3 = (1 << 1),
|
||||
|
||||
42
src/main/acio/panb.h
Normal file
42
src/main/acio/panb.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef AC_IO_PANB_H
|
||||
#define AC_IO_PANB_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define AC_IO_CMD_PANB_POLL_REPLY 0x0110
|
||||
#define AC_IO_CMD_PANB_SEND_LAMP 0x0111
|
||||
#define AC_IO_CMD_PANB_START_AUTO_INPUT 0x0115
|
||||
|
||||
#define AC_IO_PANB_NUM_NODES 4
|
||||
#define AC_IO_PANB_MAX_KEYS (7*AC_IO_PANB_NUM_NODES)
|
||||
#define AC_IO_PANB_MAX_KEYPAIRS (AC_IO_PANB_MAX_KEYS/2)
|
||||
|
||||
struct ac_io_panb_keypair {
|
||||
uint8_t key2 : 4;
|
||||
uint8_t key1 : 4;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ac_io_panb_poll_in {
|
||||
/* last received command sequence number (start_auto_input / send_lamp) */
|
||||
uint8_t sub_seq1;
|
||||
/* auto-increment sequence number for autopoll reports */
|
||||
uint8_t sub_seq2;
|
||||
struct ac_io_panb_keypair keypair[AC_IO_PANB_MAX_KEYPAIRS];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
struct ac_io_panb_color {
|
||||
uint8_t red;
|
||||
uint8_t green;
|
||||
uint8_t blue;
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct ac_io_panb_poll_out {
|
||||
struct ac_io_panb_color key[AC_IO_PANB_MAX_KEYS];
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
#endif
|
||||
7
src/main/aciodrv-proc/Module.mk
Normal file
7
src/main/aciodrv-proc/Module.mk
Normal file
@@ -0,0 +1,7 @@
|
||||
libs += aciodrv-proc
|
||||
|
||||
libs_aciodrv-proc := \
|
||||
|
||||
src_aciodrv-proc := \
|
||||
panb.c \
|
||||
|
||||
90
src/main/aciodrv-proc/panb.c
Normal file
90
src/main/aciodrv-proc/panb.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#define LOG_MODULE "aciodrv-proc-panb"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
#include "aciodrv/panb.h"
|
||||
|
||||
#include "util/thread.h"
|
||||
#include "util/log.h"
|
||||
|
||||
static int auto_poll_proc(void *auto_poll_param);
|
||||
static int auto_poll_threadid;
|
||||
|
||||
static CRITICAL_SECTION keypair_lock;
|
||||
static struct ac_io_panb_keypair _keypair[AC_IO_PANB_MAX_KEYPAIRS];
|
||||
|
||||
static CRITICAL_SECTION auto_poll_stop_lock;
|
||||
static bool auto_poll_stop;
|
||||
|
||||
static int auto_poll_proc(void * param)
|
||||
{
|
||||
struct aciodrv_device_ctx * device = (struct aciodrv_device_ctx *) param;
|
||||
struct ac_io_panb_poll_in poll_in;
|
||||
bool stop;
|
||||
|
||||
do {
|
||||
aciodrv_panb_recv_poll(device, &poll_in);
|
||||
|
||||
EnterCriticalSection(&keypair_lock);
|
||||
memcpy(_keypair, poll_in.keypair, AC_IO_PANB_MAX_KEYPAIRS);
|
||||
LeaveCriticalSection(&keypair_lock);
|
||||
|
||||
EnterCriticalSection(&auto_poll_stop_lock);
|
||||
stop = auto_poll_stop;
|
||||
LeaveCriticalSection(&auto_poll_stop_lock);
|
||||
} while (!stop);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool aciodrv_proc_panb_init(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
log_assert(device);
|
||||
|
||||
if (!aciodrv_panb_start_auto_input(device, 0, AC_IO_PANB_NUM_NODES)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto_poll_stop = false;
|
||||
InitializeCriticalSection(&keypair_lock);
|
||||
InitializeCriticalSection(&auto_poll_stop_lock);
|
||||
auto_poll_threadid = thread_create(auto_poll_proc, (void *)device, 0x4000, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_proc_panb_get_state(uint8_t *button_state)
|
||||
{
|
||||
struct ac_io_panb_keypair keypair[AC_IO_PANB_MAX_KEYPAIRS];
|
||||
|
||||
EnterCriticalSection(&keypair_lock);
|
||||
memcpy(keypair, _keypair, AC_IO_PANB_MAX_KEYPAIRS);
|
||||
LeaveCriticalSection(&keypair_lock);
|
||||
|
||||
/* splice the keypairs into separate button values */
|
||||
for (int i=0; i<AC_IO_PANB_MAX_KEYPAIRS; i++) {
|
||||
uint8_t but1 = keypair[i].key1;
|
||||
uint8_t but2 = keypair[i].key2;
|
||||
button_state[2*i] = but1;
|
||||
button_state[2*i+1] = but2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void aciodrv_proc_panb_fini(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
EnterCriticalSection(&auto_poll_stop_lock);
|
||||
auto_poll_stop = true;
|
||||
LeaveCriticalSection(&auto_poll_stop_lock);
|
||||
|
||||
thread_join(auto_poll_threadid, NULL);
|
||||
thread_destroy(auto_poll_threadid);
|
||||
|
||||
DeleteCriticalSection(&keypair_lock);
|
||||
DeleteCriticalSection(&auto_poll_stop_lock);
|
||||
|
||||
/* reset is the only way to disable the auto polling on device side */
|
||||
aciodrv_device_reset(device);
|
||||
}
|
||||
32
src/main/aciodrv-proc/panb.h
Normal file
32
src/main/aciodrv-proc/panb.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef ACIODRV_PROC_PANB_H
|
||||
#define ACIODRV_PROC_PANB_H
|
||||
|
||||
#include "aciodrv/panb.h"
|
||||
|
||||
/**
|
||||
* Initialize a PANB device. This will take care of setting up the auto-poll and processing
|
||||
* the poll messages in a separate thread (this is necessary as failing to process messages
|
||||
* fast enough will cause the device to malfunction).
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @return True if successful, false on error.
|
||||
* @note This function spawns a thread. Caller must call aciodrv_proc_panb_fini to properly
|
||||
* terminate.
|
||||
*/
|
||||
bool aciodrv_proc_panb_init(struct aciodrv_device_ctx *device);
|
||||
|
||||
/**
|
||||
* Retrieve latest known button state from the PANB device.
|
||||
*
|
||||
* @param button_state 28 cell array to store button state
|
||||
* (mandatory, upon calling the array contains values between 0 to 15 indicating the keys velocity).
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
bool aciodrv_proc_panb_get_state(uint8_t *button_state);
|
||||
|
||||
/**
|
||||
* Properly terminate the thread and reset the device (this is the only way to stop the auto polling).
|
||||
*/
|
||||
void aciodrv_proc_panb_fini(struct aciodrv_device_ctx *device);
|
||||
|
||||
#endif
|
||||
@@ -1,10 +1,12 @@
|
||||
libs += aciodrv
|
||||
|
||||
libs_aciodrv := \
|
||||
|
||||
|
||||
src_aciodrv := \
|
||||
device.c \
|
||||
h44b.c \
|
||||
icca.c \
|
||||
kfca.c \
|
||||
panb.c \
|
||||
port.c \
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#define LOG_MODULE "aciodrv-device"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
@@ -8,34 +9,64 @@
|
||||
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
/* Enable to dump all data to the logger */
|
||||
//#define AC_IO_MSG_LOG
|
||||
|
||||
static uint8_t aciodrv_device_msg_counter = 1;
|
||||
static uint8_t aciodrv_device_node_count;
|
||||
static char aviodrv_device_node_products[16][4];
|
||||
#define ACIO_MAX_NODES_PER_PORT 16
|
||||
|
||||
static bool aciodrv_device_init(void)
|
||||
struct aciodrv_device_ctx {
|
||||
HANDLE fd;
|
||||
struct aciodrv_device_node_version node_versions[ACIO_MAX_NODES_PER_PORT];
|
||||
uint8_t msg_counter;
|
||||
uint8_t node_count;
|
||||
};
|
||||
|
||||
bool aciodrv_device_reset(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
uint8_t reset_seq[525] = {0};
|
||||
|
||||
/* init/reset the device by sending 525 NULL bytes */
|
||||
log_info("Resetting device");
|
||||
if (aciodrv_port_write(device->fd, reset_seq, sizeof(reset_seq)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* signal to device for a reset */
|
||||
EscapeCommFunction(device->fd, SETBREAK);
|
||||
Sleep(1450);
|
||||
EscapeCommFunction(device->fd, CLRBREAK);
|
||||
Sleep(1200);
|
||||
|
||||
/* ignore the next 100 reads (of whatever length) */
|
||||
for (size_t i = 0; i < 100; ++i) {
|
||||
aciodrv_port_read(device->fd, reset_seq, sizeof(reset_seq));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool aciodrv_device_sof_flush(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
uint8_t init_seq[1] = {AC_IO_SOF};
|
||||
uint8_t read_buff[1] = {0x00};
|
||||
uint8_t read_buff[1] = {0};
|
||||
|
||||
/* init/reset the device by sending 0xAA until 0xAA is returned */
|
||||
/* wait for reset to finish */
|
||||
int read = 0;
|
||||
do {
|
||||
if (aciodrv_port_write(init_seq, sizeof(init_seq)) <= 0) {
|
||||
if (aciodrv_port_write(device->fd, init_seq, sizeof(init_seq)) <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
read = aciodrv_port_read(read_buff, sizeof(read_buff));
|
||||
read = aciodrv_port_read(device->fd, read_buff, sizeof(read_buff));
|
||||
} while ((read <= 0) || (read_buff[0] != init_seq[0]));
|
||||
|
||||
if (read > 0) {
|
||||
log_warning("Obtained SOF, clearing out buffer now");
|
||||
log_info("Obtained SOF, clearing out buffer now");
|
||||
/* empty buffer by reading all data */
|
||||
while (read > 0) {
|
||||
read = aciodrv_port_read(init_seq, sizeof(init_seq));
|
||||
read = aciodrv_port_read(device->fd, init_seq, sizeof(init_seq));
|
||||
}
|
||||
log_warning("Cleared buffer, init done");
|
||||
|
||||
@@ -46,30 +77,39 @@ static bool aciodrv_device_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool aciodrv_device_init(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
if (!aciodrv_device_reset(device)) {
|
||||
log_warning("Reset failed");
|
||||
return false;
|
||||
}
|
||||
return aciodrv_device_sof_flush(device);
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
static void
|
||||
aciodrv_device_log_buffer(const char *msg, const uint8_t *buffer, int length)
|
||||
aciodrv_device_log_buffer(struct aciodrv_device_ctx *device, const char *msg, const uint8_t *buffer, int length)
|
||||
{
|
||||
char str[4096];
|
||||
|
||||
hex_encode_uc((const void *) buffer, length, str, sizeof(str));
|
||||
log_misc("%s, length %d: %s", msg, length, str);
|
||||
log_misc("[%p] %s, length %d: %s", device->fd, msg, length, str);
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool aciodrv_device_send(const uint8_t *buffer, int length)
|
||||
static bool aciodrv_device_send(struct aciodrv_device_ctx *device, const uint8_t *buffer, int length)
|
||||
{
|
||||
uint8_t send_buf[512];
|
||||
int send_buf_pos = 0;
|
||||
uint8_t checksum = 0;
|
||||
|
||||
if (length > sizeof(send_buf)) {
|
||||
log_warning("Send buffer overflow");
|
||||
log_warning("[%p] Send buffer overflow", device->fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Send (1)", buffer, length);
|
||||
aciodrv_device_log_buffer(device, "Send (1)", buffer, length);
|
||||
#endif
|
||||
|
||||
send_buf[send_buf_pos++] = AC_IO_SOF;
|
||||
@@ -95,18 +135,18 @@ static bool aciodrv_device_send(const uint8_t *buffer, int length)
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Send (2)", send_buf, send_buf_pos);
|
||||
aciodrv_device_log_buffer(device, "Send (2)", send_buf, send_buf_pos);
|
||||
#endif
|
||||
|
||||
if (aciodrv_port_write(send_buf, send_buf_pos) != send_buf_pos) {
|
||||
log_warning("Sending data with length %d failed", send_buf_pos);
|
||||
if (aciodrv_port_write(device->fd, send_buf, send_buf_pos) != send_buf_pos) {
|
||||
log_warning("[%p] Sending data with length %d failed", device->fd, send_buf_pos);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int aciodrv_device_receive(uint8_t *buffer, int size)
|
||||
static int aciodrv_device_receive(struct aciodrv_device_ctx *device, uint8_t *buffer, int max_resp_size)
|
||||
{
|
||||
uint8_t recv_buf[512];
|
||||
int recv_size = 0;
|
||||
@@ -118,16 +158,14 @@ static int aciodrv_device_receive(uint8_t *buffer, int size)
|
||||
of 0xAAs before we get a valid message. */
|
||||
recv_buf[0] = AC_IO_SOF;
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf, 1);
|
||||
read = aciodrv_port_read(device->fd, recv_buf, 1);
|
||||
} while (recv_buf[0] == AC_IO_SOF);
|
||||
|
||||
if (read > 0) {
|
||||
size += 1;
|
||||
|
||||
if (read > 0) {
|
||||
/* recv_buf[0] is already the first byte of the message.
|
||||
now read until nothing's left */
|
||||
recv_size++;
|
||||
size--;
|
||||
|
||||
/* important: we have to know how much data we expect
|
||||
and have to read until we reach the requested amount.
|
||||
@@ -135,9 +173,10 @@ static int aciodrv_device_receive(uint8_t *buffer, int size)
|
||||
need to handle escaping (which relies on an up to
|
||||
date recv_buf[recv_size]) we loop until we get a
|
||||
non-zero read. */
|
||||
while (size > 0) {
|
||||
size_t expected_size = offsetof(struct ac_io_message, cmd.nbytes) + 1;
|
||||
while (recv_size < expected_size) {
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf + recv_size, 1);
|
||||
read = aciodrv_port_read(device->fd, recv_buf + recv_size, 1);
|
||||
} while (read == 0);
|
||||
|
||||
if (read < 0) {
|
||||
@@ -150,7 +189,8 @@ static int aciodrv_device_receive(uint8_t *buffer, int size)
|
||||
/* next byte is our real data
|
||||
overwrite escape byte */
|
||||
do {
|
||||
read = aciodrv_port_read(recv_buf + recv_size, 1);
|
||||
read = aciodrv_port_read(
|
||||
device->fd, recv_buf + recv_size, 1);
|
||||
} while (read == 0);
|
||||
|
||||
if (read < 0) {
|
||||
@@ -161,15 +201,23 @@ static int aciodrv_device_receive(uint8_t *buffer, int size)
|
||||
}
|
||||
|
||||
recv_size += read;
|
||||
size -= read;
|
||||
|
||||
if (recv_size > offsetof(struct ac_io_message, cmd.nbytes)) {
|
||||
// header + data + checksum
|
||||
expected_size = offsetof(struct ac_io_message, cmd.raw) + ((struct ac_io_message*)recv_buf)->cmd.nbytes + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Recv (1)", recv_buf, recv_size);
|
||||
log_warning("Expected %d got %d", recv_buf[4], recv_size - 6);
|
||||
aciodrv_device_log_buffer(device, "Recv (1)", recv_buf, recv_size);
|
||||
log_warning("Expected %d got %d", max_resp_size - 6, recv_buf[4]);
|
||||
#endif
|
||||
|
||||
/* recv_size - 1: omit checksum for checksum calc */
|
||||
if ((recv_size - 1) > max_resp_size) {
|
||||
log_warning("[%p] Expected %d got %d", device->fd, max_resp_size - 6, recv_buf[4]);
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < recv_size - 1; i++) {
|
||||
checksum += recv_buf[i];
|
||||
buffer[i] = recv_buf[i];
|
||||
@@ -178,12 +226,13 @@ static int aciodrv_device_receive(uint8_t *buffer, int size)
|
||||
result_size = recv_size - 1;
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
aciodrv_device_log_buffer("Recv (2)", buffer, result_size);
|
||||
aciodrv_device_log_buffer(device, "Recv (2)", buffer, result_size);
|
||||
#endif
|
||||
|
||||
if (checksum != recv_buf[recv_size - 1]) {
|
||||
log_warning(
|
||||
"Invalid message checksum: %02X != %02X",
|
||||
"[%p] Invalid message checksum: %02X != %02X",
|
||||
device->fd,
|
||||
checksum,
|
||||
recv_buf[recv_size - 1]);
|
||||
return -1;
|
||||
@@ -195,7 +244,7 @@ static int aciodrv_device_receive(uint8_t *buffer, int size)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint8_t aciodrv_device_enum_nodes(void)
|
||||
static uint8_t aciodrv_device_enum_nodes(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
@@ -205,7 +254,9 @@ static uint8_t aciodrv_device_enum_nodes(void)
|
||||
msg.cmd.count = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Enumerating nodes failed");
|
||||
return 0;
|
||||
}
|
||||
@@ -215,7 +266,7 @@ static uint8_t aciodrv_device_enum_nodes(void)
|
||||
return msg.cmd.count;
|
||||
}
|
||||
|
||||
static bool aciodrv_device_get_version(uint8_t node_id, char product[4])
|
||||
static bool aciodrv_device_get_version(struct aciodrv_device_ctx *device, uint8_t node_id, struct aciodrv_device_node_version *version)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
@@ -224,6 +275,7 @@ static bool aciodrv_device_get_version(uint8_t node_id, char product[4])
|
||||
msg.cmd.nbytes = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) +
|
||||
sizeof(struct ac_io_version))) {
|
||||
@@ -247,12 +299,15 @@ static bool aciodrv_device_get_version(uint8_t node_id, char product[4])
|
||||
msg.cmd.version.date,
|
||||
msg.cmd.version.time);
|
||||
|
||||
memcpy(product, msg.cmd.version.product_code, 4);
|
||||
memcpy(version->product, msg.cmd.version.product_code, ACIO_NODE_PRODUCT_CODE_LEN);
|
||||
version->major = msg.cmd.version.major;
|
||||
version->minor = msg.cmd.version.minor;
|
||||
version->revision = msg.cmd.version.revision;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool aciodrv_device_start_node(uint8_t node_id)
|
||||
static bool aciodrv_device_start_node(struct aciodrv_device_ctx *device, uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
@@ -261,7 +316,9 @@ static bool aciodrv_device_start_node(uint8_t node_id)
|
||||
msg.cmd.nbytes = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Starting node %d failed", node_id);
|
||||
return false;
|
||||
}
|
||||
@@ -270,80 +327,127 @@ static bool aciodrv_device_start_node(uint8_t node_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_device_open(const char *port, int baud)
|
||||
// deprecated name
|
||||
struct aciodrv_device_ctx *aciodrv_device_open(const char *port_path, int baud)
|
||||
{
|
||||
if (!aciodrv_port_open(port, baud)) {
|
||||
return aciodrv_device_open_path(port_path, baud);
|
||||
}
|
||||
|
||||
struct aciodrv_device_ctx *aciodrv_device_open_path(const char *port_path, int baud)
|
||||
{
|
||||
HANDLE port = aciodrv_port_open(port_path, baud);
|
||||
|
||||
if (!port) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct aciodrv_device_ctx *device = xmalloc(sizeof(struct aciodrv_device_ctx));
|
||||
|
||||
memset(device, 0, sizeof(struct aciodrv_device_ctx));
|
||||
device->fd = port;
|
||||
device->msg_counter = 1;
|
||||
|
||||
if (!aciodrv_device_init(device)) {
|
||||
aciodrv_device_close(device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device->node_count = aciodrv_device_enum_nodes(device);
|
||||
|
||||
if (device->node_count == 0) {
|
||||
aciodrv_device_close(device);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aciodrv_device_init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aciodrv_device_node_count = aciodrv_device_enum_nodes();
|
||||
if (aciodrv_device_node_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aciodrv_device_node_count; i++) {
|
||||
for (uint8_t i = 0; i < device->node_count; i++) {
|
||||
if (!aciodrv_device_get_version(
|
||||
i + 1, aviodrv_device_node_products[i])) {
|
||||
return false;
|
||||
device, i + 1, &device->node_versions[i])) {
|
||||
aciodrv_device_close(device);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < aciodrv_device_node_count; i++) {
|
||||
if (!aciodrv_device_start_node(i + 1)) {
|
||||
return false;
|
||||
for (uint8_t i = 0; i < device->node_count; i++) {
|
||||
if (!aciodrv_device_start_node(device, i + 1)) {
|
||||
aciodrv_device_close(device);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
log_info("Opening ACIO device on [%p]", device->fd);
|
||||
return device;
|
||||
}
|
||||
|
||||
uint8_t aciodrv_device_get_node_count(void)
|
||||
uint8_t aciodrv_device_get_node_count(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
return aciodrv_device_node_count;
|
||||
return device->node_count;
|
||||
}
|
||||
|
||||
bool aciodrv_device_get_node_product_ident(uint8_t node_id, char product[4])
|
||||
bool aciodrv_device_get_node_product_ident(struct aciodrv_device_ctx *device, uint8_t node_id, char product[ACIO_NODE_PRODUCT_CODE_LEN])
|
||||
{
|
||||
if (aciodrv_device_node_count == 0 || node_id > aciodrv_device_node_count) {
|
||||
if (device->node_count == 0 || node_id > device->node_count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(product, aviodrv_device_node_products[node_id], 4);
|
||||
memcpy(product, device->node_versions[node_id].product, ACIO_NODE_PRODUCT_CODE_LEN);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_send_and_recv(struct ac_io_message *msg, int resp_size)
|
||||
const struct aciodrv_device_node_version *aciodrv_device_get_node_product_version(struct aciodrv_device_ctx *device, uint8_t node_id)
|
||||
{
|
||||
msg->cmd.seq_no = aciodrv_device_msg_counter++;
|
||||
if (device->node_count == 0 || node_id > device->node_count) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &device->node_versions[node_id];
|
||||
}
|
||||
|
||||
bool aciodrv_send(struct aciodrv_device_ctx *device, struct ac_io_message *msg)
|
||||
{
|
||||
msg->cmd.seq_no = device->msg_counter++;
|
||||
int send_size = offsetof(struct ac_io_message, cmd.raw) + msg->cmd.nbytes;
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
log_info(
|
||||
"Beginning send on %d: %04x (%d b)",
|
||||
"[%p] Beginning send on %d: %04x (%d b)",
|
||||
device->fd,
|
||||
msg->addr,
|
||||
msg->cmd.code,
|
||||
send_size);
|
||||
#endif
|
||||
if (aciodrv_device_send((uint8_t *) msg, send_size) <= 0) {
|
||||
if (aciodrv_device_send(device, (uint8_t *) msg, send_size) <= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size)
|
||||
{
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
log_info("[%p] Beginning recv: (%d b)", device->fd, max_resp_size);
|
||||
#endif
|
||||
if (aciodrv_device_receive(device, (uint8_t *) msg, max_resp_size) <= 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_send_and_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size)
|
||||
{
|
||||
if (!aciodrv_send(device, msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t req_code = msg->cmd.code;
|
||||
|
||||
#ifdef AC_IO_MSG_LOG
|
||||
log_info("Beginning recv: (%d b)", resp_size);
|
||||
#endif
|
||||
if (aciodrv_device_receive((uint8_t *) msg, resp_size) <= 0) {
|
||||
if (!aciodrv_recv(device, msg, max_resp_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (req_code != msg->cmd.code) {
|
||||
log_warning(
|
||||
"Received invalid response %04X for request %04X",
|
||||
"[%p] Received invalid response %04X for request %04X",
|
||||
device->fd,
|
||||
msg->cmd.code,
|
||||
req_code);
|
||||
return false;
|
||||
@@ -352,7 +456,13 @@ bool aciodrv_send_and_recv(struct ac_io_message *msg, int resp_size)
|
||||
return true;
|
||||
}
|
||||
|
||||
void aciodrv_device_close(void)
|
||||
void aciodrv_device_close(struct aciodrv_device_ctx *device)
|
||||
{
|
||||
aciodrv_port_close();
|
||||
log_assert(device);
|
||||
|
||||
log_info("Closing ACIO on [%p]", device->fd);
|
||||
|
||||
aciodrv_port_close(device->fd);
|
||||
|
||||
free(device);
|
||||
}
|
||||
|
||||
@@ -5,48 +5,109 @@
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
#define ACIO_NODE_PRODUCT_CODE_LEN 4
|
||||
|
||||
struct aciodrv_device_ctx;
|
||||
|
||||
struct aciodrv_device_node_version {
|
||||
char product[ACIO_NODE_PRODUCT_CODE_LEN];
|
||||
uint8_t major;
|
||||
uint8_t minor;
|
||||
uint8_t revision;
|
||||
};
|
||||
|
||||
/**
|
||||
* Open an ACIO device connected to a serial port.
|
||||
*
|
||||
* @param port Port the device is connected to (e.g. "COM1")
|
||||
* @param baud Baud rate for communication (e.g. 57600 for ICCA)
|
||||
* @return True if opening the port and resetting the device was successful,
|
||||
* false on error.
|
||||
* @return opened device context, NULL on error
|
||||
*/
|
||||
bool aciodrv_device_open(const char *port, int baud);
|
||||
struct aciodrv_device_ctx *aciodrv_device_open(const char *port_path, int baud)
|
||||
#ifdef __GNUC__
|
||||
__attribute__((deprecated("Use aciomgr instead if device is shareable, else aciodrv_device_open_path")))
|
||||
#endif
|
||||
;
|
||||
|
||||
struct aciodrv_device_ctx *aciodrv_device_open_path(const char *port_path, int baud);
|
||||
|
||||
/**
|
||||
* Get the node count on the opened device.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @return Total num of nodes enumerated on the ACIO device.
|
||||
*/
|
||||
uint8_t aciodrv_device_get_node_count(void);
|
||||
uint8_t aciodrv_device_get_node_count(struct aciodrv_device_ctx *device);
|
||||
|
||||
/**
|
||||
* Get the product identifier of an enumerated node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node. Needs to be in range of the total node count.
|
||||
* @param product Buffer to return the product id to.
|
||||
* @return True on success, false on error. If True the variable product
|
||||
* contains the identifier of the queried node.
|
||||
*/
|
||||
bool aciodrv_device_get_node_product_ident(uint8_t node_id, char product[4]);
|
||||
bool aciodrv_device_get_node_product_ident(struct aciodrv_device_ctx *device, uint8_t node_id, char product[ACIO_NODE_PRODUCT_CODE_LEN]);
|
||||
|
||||
/**
|
||||
* Get the product version of an enumerated node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node. Needs to be in range of the total node count.
|
||||
* @return Pointer to the version struct
|
||||
*/
|
||||
const struct aciodrv_device_node_version *aciodrv_device_get_node_product_version(struct aciodrv_device_ctx *device, uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Send a message to the ACIO bus and receive an answer.
|
||||
* Use this to implement the protocol for each type of device that can be
|
||||
* part of the bus.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param msg Msg to send to the bus. Make sure that the buffer backing
|
||||
* this message is big enough to receive the response as well.
|
||||
* @param resp_size Size of the expecting response.
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
bool aciodrv_send_and_recv(struct ac_io_message *msg, int resp_size);
|
||||
bool aciodrv_send_and_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size);
|
||||
|
||||
/**
|
||||
* Send a message to the ACIO bus.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param msg Msg to send to the bus.
|
||||
* @return True on success, false on error.
|
||||
* @note Prefer the use of aciodrv_send_and_recv when possible. This is for commands which don't trigger a reply.
|
||||
*/
|
||||
bool aciodrv_send(struct aciodrv_device_ctx *device, struct ac_io_message *msg);
|
||||
|
||||
/**
|
||||
* Read a message from the ACIO bus.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param msg Msg to send to the bus. Make sure that the buffer
|
||||
* is big enough to receive the response.
|
||||
* @return True on success, false on error.
|
||||
* @note Prefer the use of aciodrv_send_and_recv when possible. This is for unsollicited incoming messages.
|
||||
*/
|
||||
bool aciodrv_recv(struct aciodrv_device_ctx *device, struct ac_io_message *msg, int max_resp_size);
|
||||
|
||||
/**
|
||||
* Reset an opened device.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @return Total num of nodes enumerated on the ACIO device.
|
||||
*/
|
||||
bool aciodrv_device_reset(struct aciodrv_device_ctx *device);
|
||||
|
||||
/**
|
||||
* Close the previously opened ACIO device.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
*/
|
||||
void aciodrv_device_close(void);
|
||||
void aciodrv_device_close(struct aciodrv_device_ctx *device);
|
||||
|
||||
#endif
|
||||
45
src/main/aciodrv/h44b.c
Normal file
45
src/main/aciodrv/h44b.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#define LOG_MODULE "aciodrv-h44b"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "acio/h44b.h"
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
bool aciodrv_h44b_init(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
// unlike input devices like KFCA, H44B has no watchdog or special init code
|
||||
// requirements - shared ACIO node initialisation is enough
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_h44b_lights(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct ac_io_h44b_output *lights)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
log_assert(lights);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_H44B_CMD_SET_OUTPUTS);
|
||||
msg.cmd.nbytes = sizeof(*lights);
|
||||
msg.cmd.h44b_output = *lights;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Polling of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
32
src/main/aciodrv/h44b.h
Normal file
32
src/main/aciodrv/h44b.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef ACIODRV_H44B_H
|
||||
#define ACIODRV_H44B_H
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
#include "acio/h44b.h"
|
||||
|
||||
/**
|
||||
* Initialize an H44B node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to initialize (0 based).
|
||||
* @return True if successful, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_h44b_init(struct aciodrv_device_ctx *device, uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Set the H44B LEDs
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param lights Packed lights struct to send
|
||||
* @return True on success, false on error.
|
||||
*/
|
||||
bool aciodrv_h44b_lights(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct ac_io_h44b_output *lights);
|
||||
|
||||
#endif
|
||||
@@ -3,20 +3,27 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
#include "aciodrv/icca.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static bool aciodrv_icca_queue_loop_start(uint8_t node_id)
|
||||
static bool aciodrv_icca_queue_loop_start(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_QUEUE_LOOP_START);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.status = 0;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Starting queue loop failed");
|
||||
return false;
|
||||
}
|
||||
@@ -27,9 +34,11 @@ static bool aciodrv_icca_queue_loop_start(uint8_t node_id)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_init(uint8_t node_id)
|
||||
bool aciodrv_icca_init(struct aciodrv_device_ctx *device, uint8_t node_id)
|
||||
{
|
||||
if (!aciodrv_icca_queue_loop_start(node_id + 1)) {
|
||||
log_assert(device);
|
||||
|
||||
if (!aciodrv_icca_queue_loop_start(device, node_id + 1)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -37,10 +46,15 @@ bool aciodrv_icca_init(uint8_t node_id)
|
||||
}
|
||||
|
||||
bool aciodrv_icca_set_state(
|
||||
uint8_t node_id, int slot_state, struct ac_io_icca_state *state)
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
int slot_state,
|
||||
struct ac_io_icca_state *state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_SET_SLOT_STATE);
|
||||
msg.cmd.nbytes = 2;
|
||||
@@ -49,7 +63,9 @@ bool aciodrv_icca_set_state(
|
||||
msg.cmd.raw[1] = slot_state;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + msg.cmd.raw[0])) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + msg.cmd.raw[0])) {
|
||||
log_warning("Setting state of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
@@ -61,10 +77,15 @@ bool aciodrv_icca_set_state(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state *state)
|
||||
bool aciodrv_icca_get_state(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
struct ac_io_icca_state *state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_POLL);
|
||||
msg.cmd.nbytes = 1;
|
||||
@@ -72,7 +93,9 @@ bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state *state)
|
||||
msg.cmd.count = sizeof(struct ac_io_icca_state);
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + msg.cmd.count)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + msg.cmd.count)) {
|
||||
log_warning("Getting state of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
@@ -84,10 +107,15 @@ bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state *state)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state *state)
|
||||
bool aciodrv_icca_read_card(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
struct ac_io_icca_state *state)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_ENGAGE);
|
||||
msg.cmd.nbytes = 1;
|
||||
@@ -95,7 +123,9 @@ bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state *state)
|
||||
msg.cmd.count = sizeof(struct ac_io_icca_state);
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + msg.cmd.count)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + msg.cmd.count)) {
|
||||
log_warning("Reading card of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
@@ -105,4 +135,53 @@ bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state *state)
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool aciodrv_icca_is_slotted(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
const struct aciodrv_device_node_version *version;
|
||||
version = aciodrv_device_get_node_product_version(device, node_id);
|
||||
|
||||
// current heuristic is to check if version >= 1.5
|
||||
if (version) {
|
||||
if (version->major == 1) {
|
||||
if (version->minor >= 5) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_icca_poll_felica(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_ICCA_CMD_POLL_FELICA);
|
||||
msg.cmd.nbytes = 4;
|
||||
/* buffer size of data we expect */
|
||||
msg.cmd.count = 1;
|
||||
|
||||
// additional data, not sure
|
||||
msg.cmd.raw[1] = 0x03;
|
||||
msg.cmd.raw[2] = 0xFF;
|
||||
msg.cmd.raw[3] = 0xFF;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + msg.cmd.count)) {
|
||||
log_warning("Reading card of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -6,17 +6,19 @@
|
||||
/**
|
||||
* Initialize an ICCA node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to initialize (0 based).
|
||||
* @return True if successful, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_init(uint8_t node_id);
|
||||
bool aciodrv_icca_init(struct aciodrv_device_ctx *device, uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Set the state of on ICCA node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to set the state for (0 based).
|
||||
* @param slot_state State of the slot (refer to corresponding enum).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
@@ -27,11 +29,15 @@ bool aciodrv_icca_init(uint8_t node_id);
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_set_state(
|
||||
uint8_t node_id, int slot_state, struct ac_io_icca_state *state);
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
int slot_state,
|
||||
struct ac_io_icca_state *state);
|
||||
|
||||
/**
|
||||
* Get the current state of an ICCA node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
@@ -40,7 +46,10 @@ bool aciodrv_icca_set_state(
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state *state);
|
||||
bool aciodrv_icca_get_state(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
struct ac_io_icca_state *state);
|
||||
|
||||
/**
|
||||
* Trigger a card read action on the ICCA reader. Make sure to call this
|
||||
@@ -48,6 +57,7 @@ bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state *state);
|
||||
* to get the most recent card data. Make sure to re-get the state after
|
||||
* a read call. The state returned here might not be up to date for some reason.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
@@ -56,6 +66,46 @@ bool aciodrv_icca_get_state(uint8_t node_id, struct ac_io_icca_state *state);
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_read_card(uint8_t node_id, struct ac_io_icca_state *state);
|
||||
bool aciodrv_icca_read_card(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
struct ac_io_icca_state *state);
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Uses some heruistics based on the product ident to determine if the detected
|
||||
* device is a slotted (true) or wavepass reader (false).
|
||||
*
|
||||
* This function will also return false if the provided node_id is invalid.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @return True on slotted, false on wavepass.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_is_slotted(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Polls the felica chip on wavepass readers. This will cause the state of the
|
||||
* reader to be AC_IO_ICCA_STATUS_BUSY_NEW for a few polls, before returning
|
||||
* either: AC_IO_ICCA_STATUS_IDLE_NEW or AC_IO_ICCA_STATUS_GOT_UID.
|
||||
*
|
||||
* The user should take care to call this every so often to actually get new
|
||||
* felica UIDs, instead of just old NFC idents. The game seems to do it every
|
||||
* 5 or so polls after the last AC_IO_ICCA_STATUS_BUSY_NEW poll.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @return True on success, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_icca_poll_felica(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,37 +7,35 @@
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static bool aciodrv_kfca_watchdog_start(uint8_t node_id)
|
||||
static bool aciodrv_kfca_watchdog_start(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
// exit early and don't actually call watchdog
|
||||
// the watchdog call actually returns different sized packets depending on
|
||||
// the state this results in an issue during packet processing (see: #68)
|
||||
return true;
|
||||
|
||||
/*
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_WATCHDOG);
|
||||
msg.cmd.nbytes = 2;
|
||||
msg.cmd.nbytes = 2;
|
||||
|
||||
// uint16_t: 6000
|
||||
msg.cmd.raw[0] = 23;
|
||||
msg.cmd.raw[1] = 112;
|
||||
|
||||
if (!aciodrv_send_and_recv(&msg, offsetof(struct ac_io_message, cmd.raw) +
|
||||
2)) { log_warning("Starting watchdog failed"); return false;
|
||||
if (!aciodrv_send_and_recv(
|
||||
device, &msg, offsetof(struct ac_io_message, cmd.raw) + 2)) {
|
||||
log_warning("Starting watchdog failed"); return false;
|
||||
}
|
||||
|
||||
log_warning("Started watchdog of node %d, status: %d",
|
||||
node_id, msg.cmd.status);
|
||||
log_warning("Started watchdog of node %d, sz: %d, status: %d",
|
||||
node_id, msg.cmd.nbytes, msg.cmd.status);
|
||||
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
bool aciodrv_kfca_amp(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
uint8_t primary,
|
||||
uint8_t headphone,
|
||||
@@ -46,6 +44,8 @@ bool aciodrv_kfca_amp(
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_AMP_CONTROL);
|
||||
msg.cmd.nbytes = 4;
|
||||
@@ -56,7 +56,9 @@ bool aciodrv_kfca_amp(
|
||||
msg.cmd.raw[3] = subwoofer;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Setting AMP failed");
|
||||
return false;
|
||||
}
|
||||
@@ -66,13 +68,17 @@ bool aciodrv_kfca_amp(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_kfca_init(uint8_t node_id)
|
||||
bool aciodrv_kfca_init(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
if (!aciodrv_kfca_watchdog_start(node_id)) {
|
||||
log_assert(device);
|
||||
|
||||
if (!aciodrv_kfca_watchdog_start(device, node_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aciodrv_kfca_amp(node_id, 0, 0, 0, 0)) {
|
||||
if (!aciodrv_kfca_amp(device, node_id, 0, 0, 0, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -80,12 +86,15 @@ bool aciodrv_kfca_init(uint8_t node_id)
|
||||
}
|
||||
|
||||
bool aciodrv_kfca_poll(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct ac_io_kfca_poll_out *pout,
|
||||
struct ac_io_kfca_poll_in *pin)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_POLL);
|
||||
msg.cmd.nbytes = sizeof(*pout);
|
||||
@@ -93,7 +102,9 @@ bool aciodrv_kfca_poll(
|
||||
msg.cmd.kfca_poll_out = *pout;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + sizeof(*pin))) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + sizeof(*pin))) {
|
||||
log_warning("Polling of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -6,17 +6,19 @@
|
||||
/**
|
||||
* Initialize an KFCA node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to initialize (0 based).
|
||||
* @return True if successful, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_kfca_init(uint8_t node_id);
|
||||
bool aciodrv_kfca_init(struct aciodrv_device_ctx *device, uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Poll the KFCA io board
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
@@ -26,6 +28,7 @@ bool aciodrv_kfca_init(uint8_t node_id);
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_kfca_poll(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct ac_io_kfca_poll_out *pout,
|
||||
struct ac_io_kfca_poll_in *pin);
|
||||
@@ -33,6 +36,7 @@ bool aciodrv_kfca_poll(
|
||||
/**
|
||||
* Set the KFCA digital amp level
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param primary primary volume (96-0)
|
||||
* @param headphone headphone volume (96-0)
|
||||
@@ -42,6 +46,7 @@ bool aciodrv_kfca_poll(
|
||||
* @note Note 96 (or 100?) is lowest volume level, 0 is highest
|
||||
*/
|
||||
bool aciodrv_kfca_amp(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
uint8_t primary,
|
||||
uint8_t headphone,
|
||||
|
||||
80
src/main/aciodrv/panb.c
Normal file
80
src/main/aciodrv/panb.c
Normal file
@@ -0,0 +1,80 @@
|
||||
#define LOG_MODULE "aciodrv-panb"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
#include "aciodrv/panb.h"
|
||||
|
||||
#include "util/thread.h"
|
||||
#include "util/log.h"
|
||||
|
||||
bool aciodrv_panb_start_auto_input(struct aciodrv_device_ctx *device, uint8_t node_id, uint8_t node_count)
|
||||
{
|
||||
log_assert(device);
|
||||
struct ac_io_message msg;
|
||||
|
||||
/* only the first node is handling the commands */
|
||||
if (node_id != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_PANB_START_AUTO_INPUT);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.count = node_count;
|
||||
|
||||
if (!aciodrv_send(device, &msg)) {
|
||||
log_warning("Starting auto input failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Started auto input for %d nodes", node_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_panb_recv_poll(struct aciodrv_device_ctx *device, struct ac_io_panb_poll_in *poll_in)
|
||||
{
|
||||
log_assert(device);
|
||||
struct ac_io_message msg;
|
||||
struct ac_io_panb_poll_in *poll_res = (struct ac_io_panb_poll_in *) &msg.cmd.raw;
|
||||
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_PANB_POLL_REPLY);
|
||||
msg.cmd.nbytes = sizeof(struct ac_io_panb_poll_in);
|
||||
|
||||
if (!aciodrv_recv(device,
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + msg.cmd.nbytes + 1)) {
|
||||
log_warning("Getting state failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (poll_in != NULL){
|
||||
memcpy(poll_in, poll_res, sizeof(struct ac_io_panb_poll_in));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool aciodrv_panb_send_lamp(struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id, struct ac_io_panb_poll_out *state)
|
||||
{
|
||||
log_assert(device);
|
||||
struct ac_io_message msg;
|
||||
|
||||
/* only the first node is handling the commands */
|
||||
if (node_id != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_PANB_SEND_LAMP);
|
||||
msg.cmd.nbytes = 0x54;
|
||||
memcpy(&msg.cmd.raw, state, sizeof(struct ac_io_panb_poll_out));
|
||||
|
||||
if (!aciodrv_send(device, &msg)) {
|
||||
log_warning("Sending lamp state failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
56
src/main/aciodrv/panb.h
Normal file
56
src/main/aciodrv/panb.h
Normal file
@@ -0,0 +1,56 @@
|
||||
#ifndef ACIODRV_PANB_H
|
||||
#define ACIODRV_PANB_H
|
||||
|
||||
#include "acio/panb.h"
|
||||
|
||||
/**
|
||||
* Send the AC_IO_CMD_PANB_START_AUTO_INPUT command on a PANB device.
|
||||
*
|
||||
* @param device Context of opened device.
|
||||
* @param node_id node_id Id of the node to query (0 based).
|
||||
* @param node_count The number of nodes to poll.
|
||||
* @return True if successful, false on error.
|
||||
* @note node_id should be 0, PANB internally manages the other nodes.
|
||||
* @note Upon calling, the device will stop replying to commands and just keep sending
|
||||
* AC_IO_CMD_PANB_POLL_REPLY messages indefinitely. Failure to retrieve them fast enough
|
||||
* will cause the device to malfunction. It is thus advised to make use of the aciodrv-proc
|
||||
* module to spawn a thread that will handle these messages and provide an easy access to the
|
||||
* latest input state.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_panb_start_auto_input(struct aciodrv_device_ctx *device, uint8_t node_id, uint8_t node_count);
|
||||
|
||||
/**
|
||||
* Retrieve a AC_IO_CMD_PANB_POLL_REPLY message from a PANB device. This assumes that there
|
||||
* is such message incoming (ie. that start_auto_input has been called prior).
|
||||
*
|
||||
* @param device Context of opened device.
|
||||
* @param poll_in Buffer to hold the received message, or NULL.
|
||||
* @return True if successful, false on error.
|
||||
* @note node_id should be 0, PANB internally manages the other nodes.
|
||||
* @note Failure to retrieve the incoming messages fast enough will cause the device to malfunction.
|
||||
* It is thus advised to make use of the aciodrv-proc module to spawn a thread that will handle these
|
||||
* messages and provide an easy access to the latest input state.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_panb_recv_poll(struct aciodrv_device_ctx *device, struct ac_io_panb_poll_in *poll_in);
|
||||
|
||||
/**
|
||||
* Light lamps on a PANB device.
|
||||
*
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a lamp state struct
|
||||
* (mandatory).
|
||||
* @return True on success, false on error.
|
||||
* @note node_id should be 0, PANB internally manages the other nodes.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool aciodrv_panb_send_lamp(struct aciodrv_device_ctx *device, uint8_t node_id, struct ac_io_panb_poll_out *state);
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#define LOG_MODULE "aciodrv-port"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -7,17 +8,15 @@
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static HANDLE aciodrv_port_fd;
|
||||
|
||||
bool aciodrv_port_open(const char *port, int baud)
|
||||
HANDLE aciodrv_port_open(const char *port_path, int baud)
|
||||
{
|
||||
COMMTIMEOUTS ct;
|
||||
DCB dcb;
|
||||
|
||||
log_info("Opening ACIO on %s at %d baud", port, baud);
|
||||
log_info("Opening ACIO on %s at %d baud", port_path, baud);
|
||||
|
||||
aciodrv_port_fd = CreateFile(
|
||||
port,
|
||||
HANDLE port_fd = CreateFile(
|
||||
port_path,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
@@ -25,26 +24,26 @@ bool aciodrv_port_open(const char *port, int baud)
|
||||
FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
|
||||
if (aciodrv_port_fd == INVALID_HANDLE_VALUE) {
|
||||
log_warning("Failed to open %s", port);
|
||||
if (port_fd == INVALID_HANDLE_VALUE) {
|
||||
log_warning("Failed to open %s", port_path);
|
||||
|
||||
goto early_fail;
|
||||
}
|
||||
|
||||
if (!SetCommMask(aciodrv_port_fd, EV_RXCHAR)) {
|
||||
if (!SetCommMask(port_fd, EV_RXCHAR)) {
|
||||
log_warning("SetCommMask failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!SetupComm(aciodrv_port_fd, 0x1000, 0x1000)) {
|
||||
if (!SetupComm(port_fd, 0x1000, 0x1000)) {
|
||||
log_warning("SetupComm failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!PurgeComm(
|
||||
aciodrv_port_fd,
|
||||
port_fd,
|
||||
PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR)) {
|
||||
log_warning("PurgeComm failed");
|
||||
|
||||
@@ -57,7 +56,7 @@ bool aciodrv_port_open(const char *port, int baud)
|
||||
ct.ReadTotalTimeoutMultiplier = 0;
|
||||
ct.WriteTotalTimeoutMultiplier = 0;
|
||||
|
||||
if (!SetCommTimeouts(aciodrv_port_fd, &ct)) {
|
||||
if (!SetCommTimeouts(port_fd, &ct)) {
|
||||
log_warning("SetCommTimeouts failed");
|
||||
|
||||
goto fail;
|
||||
@@ -65,7 +64,7 @@ bool aciodrv_port_open(const char *port, int baud)
|
||||
|
||||
dcb.DCBlength = sizeof(dcb);
|
||||
|
||||
if (!GetCommState(aciodrv_port_fd, &dcb)) {
|
||||
if (!GetCommState(port_fd, &dcb)) {
|
||||
log_warning("GetCommState failed");
|
||||
|
||||
goto fail;
|
||||
@@ -90,46 +89,47 @@ bool aciodrv_port_open(const char *port, int baud)
|
||||
dcb.XonLim = 100;
|
||||
dcb.XoffLim = 100;
|
||||
|
||||
if (!SetCommState(aciodrv_port_fd, &dcb)) {
|
||||
if (!SetCommState(port_fd, &dcb)) {
|
||||
log_warning("SetCommState failed");
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!EscapeCommFunction(aciodrv_port_fd, SETDTR)) {
|
||||
if (!EscapeCommFunction(port_fd, SETDTR)) {
|
||||
log_warning("SETDTR failed: err = %lu", GetLastError());
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
log_info("Opened ACIO device on %s", port);
|
||||
log_info("[%p] Opened ACIO device on %s", port_fd, port_path);
|
||||
|
||||
return true;
|
||||
return port_fd;
|
||||
|
||||
fail:
|
||||
CloseHandle(aciodrv_port_fd);
|
||||
CloseHandle(port_fd);
|
||||
|
||||
early_fail:
|
||||
aciodrv_port_fd = NULL;
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int aciodrv_port_read(void *bytes, int nbytes)
|
||||
int aciodrv_port_read(HANDLE port_fd, void *bytes, int nbytes)
|
||||
{
|
||||
DWORD nread;
|
||||
|
||||
if (aciodrv_port_fd == NULL) {
|
||||
log_assert(bytes);
|
||||
|
||||
if (port_fd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ClearCommError(aciodrv_port_fd, NULL, NULL)) {
|
||||
log_warning("ClearCommError failed");
|
||||
if (!ClearCommError(port_fd, NULL, NULL)) {
|
||||
log_warning("[%p] ClearCommError failed", port_fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ReadFile(aciodrv_port_fd, bytes, nbytes, &nread, NULL)) {
|
||||
log_warning("ReadFile failed: err = %lu", GetLastError());
|
||||
if (!ReadFile(port_fd, bytes, nbytes, &nread, NULL)) {
|
||||
log_warning("[%p] ReadFile failed: err = %lu", port_fd, GetLastError());
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -137,16 +137,18 @@ int aciodrv_port_read(void *bytes, int nbytes)
|
||||
return nread;
|
||||
}
|
||||
|
||||
int aciodrv_port_write(const void *bytes, int nbytes)
|
||||
int aciodrv_port_write(HANDLE port_fd, const void *bytes, int nbytes)
|
||||
{
|
||||
DWORD nwrit;
|
||||
|
||||
if (aciodrv_port_fd == NULL) {
|
||||
log_assert(bytes);
|
||||
|
||||
if (port_fd == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!WriteFile(aciodrv_port_fd, bytes, nbytes, &nwrit, NULL)) {
|
||||
log_warning("WriteFile failed: err = %lu", GetLastError());
|
||||
if (!WriteFile(port_fd, bytes, nbytes, &nwrit, NULL)) {
|
||||
log_warning("[%p] WriteFile failed: err = %lu", port_fd, GetLastError());
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -154,9 +156,9 @@ int aciodrv_port_write(const void *bytes, int nbytes)
|
||||
return nwrit;
|
||||
}
|
||||
|
||||
void aciodrv_port_close(void)
|
||||
void aciodrv_port_close(HANDLE port_fd)
|
||||
{
|
||||
if (aciodrv_port_fd != NULL) {
|
||||
CloseHandle(aciodrv_port_fd);
|
||||
if (port_fd != NULL) {
|
||||
CloseHandle(port_fd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,39 +4,45 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
/**
|
||||
* Open a serial port for communication with a ACIO device.
|
||||
*
|
||||
* @param port Port the device is connected to (e.g. "COM1")
|
||||
* @param baud Baud rate for communication (e.g. 57600 for ICCA)
|
||||
* @return True if opening the com port was successful, false on error.
|
||||
* @return HANDLE of the port, NULL on error
|
||||
* @note This will open and setup the com port, only.
|
||||
*/
|
||||
bool aciodrv_port_open(const char *port, int baud);
|
||||
HANDLE aciodrv_port_open(const char *port_path, int baud);
|
||||
|
||||
/**
|
||||
* Read data from the opened com port.
|
||||
*
|
||||
* @param port_fd HANDLE of opened serial port
|
||||
* @param bytes Pointer to an allocated buffer to read the data into.
|
||||
* @param nbytes Number of bytes to read. Has to be less or equal the allocated
|
||||
* buffer size.
|
||||
* @return Number of bytes read on success or -1 on error.
|
||||
*/
|
||||
int aciodrv_port_read(void *bytes, int nbytes);
|
||||
int aciodrv_port_read(HANDLE port_fd, void *bytes, int nbytes);
|
||||
|
||||
/**
|
||||
* Write data to the opened com port.
|
||||
*
|
||||
* @param port_fd HANDLE of opened serial port
|
||||
* @param bytes Pointer to an allocated buffer with data to write.
|
||||
* @param nbytes Number of bytes to write. Has to be equal or less the size
|
||||
* of the allocated buffer.
|
||||
* @return Number of bytes written on success or -1 on error.
|
||||
*/
|
||||
int aciodrv_port_write(const void *bytes, int nbytes);
|
||||
int aciodrv_port_write(HANDLE port_fd, const void *bytes, int nbytes);
|
||||
|
||||
/**
|
||||
* Close the previously opened com port.
|
||||
*
|
||||
* @param port_fd HANDLE of opened serial port
|
||||
*/
|
||||
void aciodrv_port_close(void);
|
||||
void aciodrv_port_close(HANDLE port_fd);
|
||||
|
||||
#endif
|
||||
@@ -134,6 +134,7 @@ static HRESULT ac_io_emu_write(struct ac_io_emu *emu, struct irp *irp)
|
||||
break;
|
||||
}
|
||||
|
||||
// this supplies a single SOF byte per byte read
|
||||
ac_io_in_supply(&emu->in, NULL, 0);
|
||||
ac_io_out_consume_message(&emu->out);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ void ac_io_emu_h44b_dispatch_request(
|
||||
req->cmd.raw[i * 3 + 2]);
|
||||
}
|
||||
|
||||
jb_io_write_outputs();
|
||||
jb_io_write_lights();
|
||||
|
||||
ac_io_emu_h44b_send_status(h44b, req, 0x00);
|
||||
|
||||
|
||||
@@ -29,13 +29,6 @@ enum ac_io_icca_flag {
|
||||
AC_IO_ICCA_FLAG_SOLENOID = 0x40
|
||||
};
|
||||
|
||||
enum ac_io_icca_status_code {
|
||||
AC_IO_ICCA_STATUS_FAULT = 0x00,
|
||||
AC_IO_ICCA_STATUS_IDLE = 0x01,
|
||||
AC_IO_ICCA_STATUS_GOT_UID = 0x02,
|
||||
AC_IO_ICCA_STATUS_IDLE_NEW = 0x04
|
||||
};
|
||||
|
||||
static void ac_io_emu_icca_cmd_send_version(
|
||||
struct ac_io_emu_icca *icca, const struct ac_io_message *req);
|
||||
|
||||
|
||||
@@ -169,6 +169,10 @@ static void ac_io_emu_iccb_send_state(
|
||||
|
||||
/* state update */
|
||||
|
||||
if (!eam_io_poll(iccb->unit_no)) {
|
||||
log_warning("Polling eamio failed");
|
||||
}
|
||||
|
||||
sensor = eam_io_get_sensor_state(iccb->unit_no);
|
||||
|
||||
if (sensor != iccb->last_sensor) {
|
||||
|
||||
9
src/main/aciomgr/Module.mk
Normal file
9
src/main/aciomgr/Module.mk
Normal file
@@ -0,0 +1,9 @@
|
||||
dlls += aciomgr
|
||||
|
||||
libs_aciomgr := \
|
||||
aciodrv \
|
||||
util \
|
||||
|
||||
src_aciomgr := \
|
||||
dllmain.c \
|
||||
manager.c \
|
||||
12
src/main/aciomgr/aciomgr.def
Normal file
12
src/main/aciomgr/aciomgr.def
Normal file
@@ -0,0 +1,12 @@
|
||||
LIBRARY aciomgr
|
||||
|
||||
EXPORTS
|
||||
aciomgr_set_loggers
|
||||
aciomgr_port_init
|
||||
aciomgr_port_fini
|
||||
aciomgr_get_node_count
|
||||
aciomgr_get_node_product_ident
|
||||
aciomgr_port_submit_packet
|
||||
aciomgr_port_checkout
|
||||
aciomgr_port_checkin
|
||||
DllMain@12 @1 NONAME
|
||||
16
src/main/aciomgr/dllmain.c
Normal file
16
src/main/aciomgr/dllmain.c
Normal file
@@ -0,0 +1,16 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "manager-init.h"
|
||||
|
||||
BOOL WINAPI DllMain(HMODULE self, DWORD reason, void *ctx)
|
||||
{
|
||||
if (reason == DLL_PROCESS_ATTACH) {
|
||||
_aciomgr_init();
|
||||
}
|
||||
|
||||
if (reason == DLL_PROCESS_DETACH) {
|
||||
_aciomgr_fini();
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
8
src/main/aciomgr/manager-init.h
Normal file
8
src/main/aciomgr/manager-init.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef ACIOMGR_MANAGER_INIT_H
|
||||
#define ACIOMGR_MANAGER_INIT_H
|
||||
|
||||
// internal, used by DllMain
|
||||
void _aciomgr_init();
|
||||
void _aciomgr_fini();
|
||||
|
||||
#endif
|
||||
210
src/main/aciomgr/manager.c
Normal file
210
src/main/aciomgr/manager.c
Normal file
@@ -0,0 +1,210 @@
|
||||
#define LOG_MODULE "aciomgr-manager"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdatomic.h>
|
||||
|
||||
#include "aciomgr/manager-init.h"
|
||||
|
||||
#include "aciomgr/manager.h"
|
||||
|
||||
#include "acio/acio.h"
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
#include "util/array.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#define MAX_PORT_PATH_LENGTH 256
|
||||
|
||||
struct aciomgr_port_dispatcher {
|
||||
CRITICAL_SECTION cs;
|
||||
atomic_size_t references;
|
||||
struct aciodrv_device_ctx *device;
|
||||
char path[MAX_PORT_PATH_LENGTH];
|
||||
int baud;
|
||||
bool has_failure;
|
||||
};
|
||||
|
||||
static void _aciomgr_setup_port_dispatcher(
|
||||
struct aciomgr_port_dispatcher *dispatcher, const char *path, int baud);
|
||||
static void
|
||||
_aciomgr_destroy_port_dispatcher(struct aciomgr_port_dispatcher *dispatcher);
|
||||
|
||||
// DLL-globals
|
||||
static atomic_bool running;
|
||||
static CRITICAL_SECTION mgr_cs;
|
||||
static struct array active_ports;
|
||||
|
||||
static void _aciomgr_setup_port_dispatcher(
|
||||
struct aciomgr_port_dispatcher *dispatcher, const char *path, int baud)
|
||||
{
|
||||
InitializeCriticalSection(&dispatcher->cs);
|
||||
|
||||
strcpy(dispatcher->path, path);
|
||||
dispatcher->baud = baud;
|
||||
|
||||
dispatcher->device = aciodrv_device_open_path(path, baud);
|
||||
dispatcher->has_failure = false;
|
||||
|
||||
if (dispatcher->device == NULL) {
|
||||
log_info("Opening ACIO device on %s failed", path);
|
||||
dispatcher->has_failure = true;
|
||||
}
|
||||
|
||||
dispatcher->references = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
_aciomgr_destroy_port_dispatcher(struct aciomgr_port_dispatcher *dispatcher)
|
||||
{
|
||||
aciodrv_device_close(dispatcher->device);
|
||||
DeleteCriticalSection(&dispatcher->cs);
|
||||
}
|
||||
|
||||
void _aciomgr_init()
|
||||
{
|
||||
if (running) {
|
||||
log_warning("_aciomgr_init called when already running");
|
||||
_aciomgr_fini();
|
||||
}
|
||||
InitializeCriticalSection(&mgr_cs);
|
||||
array_init(&active_ports);
|
||||
|
||||
running = true;
|
||||
}
|
||||
|
||||
void _aciomgr_fini()
|
||||
{
|
||||
if (!running) {
|
||||
log_warning("_aciomgr_fini called when not running");
|
||||
}
|
||||
|
||||
running = false;
|
||||
DeleteCriticalSection(&mgr_cs);
|
||||
array_fini(&active_ports);
|
||||
}
|
||||
|
||||
void aciomgr_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);
|
||||
}
|
||||
|
||||
struct aciomgr_port_dispatcher *aciomgr_port_init(const char *path, int baud)
|
||||
{
|
||||
if (!running) {
|
||||
log_warning("aciomgr_port_init: called when not running");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (strlen(path) >= MAX_PORT_PATH_LENGTH) {
|
||||
log_warning("aciomgr_port_init: path too long");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct aciomgr_port_dispatcher *entry;
|
||||
|
||||
EnterCriticalSection(&mgr_cs);
|
||||
|
||||
for (size_t i = 0; i < active_ports.nitems; i++) {
|
||||
entry = array_item(struct aciomgr_port_dispatcher, &active_ports, i);
|
||||
|
||||
if (strcmp(entry->path, path) == 0) {
|
||||
// found
|
||||
if (entry->baud != baud) {
|
||||
log_warning(
|
||||
"Device at %s opened with baud of %d, but requested %d",
|
||||
path,
|
||||
entry->baud,
|
||||
baud);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
entry = array_append(struct aciomgr_port_dispatcher, &active_ports);
|
||||
_aciomgr_setup_port_dispatcher(entry, path, baud);
|
||||
|
||||
done:
|
||||
if (!entry->has_failure) {
|
||||
entry->references++;
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&mgr_cs);
|
||||
|
||||
if (entry->has_failure) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void aciomgr_port_fini(struct aciomgr_port_dispatcher *dispatcher)
|
||||
{
|
||||
if (!running) {
|
||||
log_warning("aciomgr_port_fini: called when not running");
|
||||
return;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&mgr_cs);
|
||||
dispatcher->references--;
|
||||
LeaveCriticalSection(&mgr_cs);
|
||||
|
||||
if (dispatcher->references == 0) {
|
||||
_aciomgr_destroy_port_dispatcher(dispatcher);
|
||||
}
|
||||
}
|
||||
|
||||
// this function don't require the lock
|
||||
uint8_t aciomgr_get_node_count(struct aciomgr_port_dispatcher *dispatcher)
|
||||
{
|
||||
return aciodrv_device_get_node_count(dispatcher->device);
|
||||
}
|
||||
|
||||
// this function don't require the lock
|
||||
bool aciomgr_get_node_product_ident(
|
||||
struct aciomgr_port_dispatcher *dispatcher,
|
||||
uint8_t node_id,
|
||||
char product[ACIOMGR_NODE_PRODUCT_CODE_LEN])
|
||||
{
|
||||
return aciodrv_device_get_node_product_ident(
|
||||
dispatcher->device, node_id, product);
|
||||
}
|
||||
|
||||
bool aciomgr_port_submit_packet(
|
||||
struct aciomgr_port_dispatcher *dispatcher,
|
||||
struct ac_io_message *msg,
|
||||
int max_resp_size)
|
||||
{
|
||||
// CS's although lightweight, may still be a burden, short circuit
|
||||
if (dispatcher->references > 1) {
|
||||
EnterCriticalSection(&dispatcher->cs);
|
||||
bool response =
|
||||
aciodrv_send_and_recv(dispatcher->device, msg, max_resp_size);
|
||||
LeaveCriticalSection(&dispatcher->cs);
|
||||
return response;
|
||||
}
|
||||
|
||||
return aciodrv_send_and_recv(dispatcher->device, msg, max_resp_size);
|
||||
}
|
||||
|
||||
struct aciodrv_device_ctx *
|
||||
aciomgr_port_checkout(struct aciomgr_port_dispatcher *dispatcher)
|
||||
{
|
||||
if (dispatcher->references > 1) {
|
||||
EnterCriticalSection(&dispatcher->cs);
|
||||
}
|
||||
|
||||
return dispatcher->device;
|
||||
}
|
||||
|
||||
void aciomgr_port_checkin(struct aciomgr_port_dispatcher *dispatcher)
|
||||
{
|
||||
if (dispatcher->references > 1) {
|
||||
LeaveCriticalSection(&dispatcher->cs);
|
||||
}
|
||||
}
|
||||
86
src/main/aciomgr/manager.h
Normal file
86
src/main/aciomgr/manager.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef ACIOMGR_MANAGER_H
|
||||
#define ACIOMGR_MANAGER_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "bemanitools/glue.h"
|
||||
|
||||
#define ACIOMGR_NODE_PRODUCT_CODE_LEN 4
|
||||
|
||||
struct ac_io_message;
|
||||
struct aciodrv_device_ctx;
|
||||
struct aciomgr_port_dispatcher;
|
||||
|
||||
/**
|
||||
* The first function that will be called on your DLL. You will be supplied
|
||||
* with four function pointers that may be used to log messages to the game's
|
||||
* log file. See comments in glue.h for further information.
|
||||
*/
|
||||
void aciomgr_set_loggers(
|
||||
log_formatter_t misc,
|
||||
log_formatter_t info,
|
||||
log_formatter_t warning,
|
||||
log_formatter_t fatal);
|
||||
|
||||
/**
|
||||
* Init and open, or return the existing handle to an acio port
|
||||
*
|
||||
* @param path Port the device is connected to (e.g. "COM1")
|
||||
* @return opened port dispatcher context, NULL on error
|
||||
*/
|
||||
struct aciomgr_port_dispatcher *aciomgr_port_init(const char *path, int baud);
|
||||
|
||||
void aciomgr_port_fini(struct aciomgr_port_dispatcher *dispatcher);
|
||||
|
||||
/**
|
||||
* Get the node count on the opened device.
|
||||
*
|
||||
* @param dispatcher dispatcher context from aciomgr_port_init
|
||||
* @return Total num of nodes enumerated on the ACIO device.
|
||||
*/
|
||||
uint8_t aciomgr_get_node_count(struct aciomgr_port_dispatcher *dispatcher);
|
||||
|
||||
/**
|
||||
* Get the product identifier of an enumerated node.
|
||||
*
|
||||
* @param dispatcher dispatcher context from aciomgr_port_init
|
||||
* @param node_id Id of the node. Needs to be in range of the total node count.
|
||||
* @param product Buffer to return the product id to.
|
||||
* @return True on success, false on error. If True the variable product
|
||||
* contains the identifier of the queried node.
|
||||
*/
|
||||
bool aciomgr_get_node_product_ident(
|
||||
struct aciomgr_port_dispatcher *dispatcher,
|
||||
uint8_t node_id,
|
||||
char product[ACIOMGR_NODE_PRODUCT_CODE_LEN]);
|
||||
|
||||
/**
|
||||
* Submit and wait for the response of the specified packet
|
||||
*
|
||||
* @param dispatcher dispatcher context from aciomgr_port_init
|
||||
* @param msg pointer to the message buffer to read from / write to
|
||||
* @param max_resp_size pointer to the max size response we expect
|
||||
* @return false on error
|
||||
*/
|
||||
bool aciomgr_port_submit_packet(
|
||||
struct aciomgr_port_dispatcher *dispatcher,
|
||||
struct ac_io_message *msg,
|
||||
int max_resp_size);
|
||||
|
||||
/**
|
||||
* Checkout the device handler for this port dispatcher on this thread
|
||||
*
|
||||
* @param dispatcher dispatcher context from aciomgr_port_init
|
||||
* @return the device context
|
||||
*/
|
||||
struct aciodrv_device_ctx *
|
||||
aciomgr_port_checkout(struct aciomgr_port_dispatcher *dispatcher);
|
||||
|
||||
/**
|
||||
* Checkin the device handler that this thread holds
|
||||
*
|
||||
* @param dispatcher dispatcher context from aciomgr_port_init
|
||||
*/
|
||||
void aciomgr_port_checkin(struct aciomgr_port_dispatcher *dispatcher);
|
||||
|
||||
#endif
|
||||
@@ -3,10 +3,12 @@ exes += aciotest
|
||||
libs_aciotest := \
|
||||
bio2drv \
|
||||
aciodrv \
|
||||
aciodrv-proc \
|
||||
util \
|
||||
|
||||
src_aciotest := \
|
||||
icca.c \
|
||||
kfca.c \
|
||||
bi2a-sdvx.c \
|
||||
panb.c \
|
||||
main.c \
|
||||
|
||||
@@ -8,22 +8,24 @@
|
||||
|
||||
#include "bio2drv/bi2a-sdvx.h"
|
||||
|
||||
bool aciotest_bi2a_sdvx_handler_init(uint8_t node_id, void **ctx)
|
||||
bool aciotest_bi2a_sdvx_handler_init(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx)
|
||||
{
|
||||
*ctx = malloc(sizeof(uint32_t));
|
||||
*((uint32_t *) *ctx) = 0;
|
||||
|
||||
return bio2drv_bi2a_sdvx_init(node_id);
|
||||
return bio2drv_bi2a_sdvx_init(device, node_id);
|
||||
}
|
||||
|
||||
bool aciotest_bi2a_sdvx_handler_update(uint8_t node_id, void *ctx)
|
||||
bool aciotest_bi2a_sdvx_handler_update(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx)
|
||||
{
|
||||
struct bi2a_sdvx_state_in pin;
|
||||
struct bi2a_sdvx_state_out pout;
|
||||
|
||||
memset(&pout, 0, sizeof(pout));
|
||||
|
||||
if (!bio2drv_bi2a_sdvx_poll(node_id, &pout, &pin)) {
|
||||
if (!bio2drv_bi2a_sdvx_poll(device, node_id, &pout, &pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool aciotest_bi2a_sdvx_handler_init(uint8_t node_id, void **ctx);
|
||||
bool aciotest_bi2a_sdvx_handler_update(uint8_t node_id, void *ctx);
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
bool aciotest_bi2a_sdvx_handler_init(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx);
|
||||
bool aciotest_bi2a_sdvx_handler_update(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef ACIOTEST_HANDLER_H
|
||||
#define ACIOTEST_HANDLER_H
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
static const uint8_t aciotest_handler_max = 16;
|
||||
|
||||
/**
|
||||
@@ -8,8 +10,10 @@ static const uint8_t aciotest_handler_max = 16;
|
||||
*/
|
||||
struct aciotest_handler_node_handler {
|
||||
void *ctx;
|
||||
bool (*init)(uint8_t node_id, void **ctx);
|
||||
bool (*update)(uint8_t node_id, void *ctx);
|
||||
bool (*init)(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx);
|
||||
bool (*update)(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -5,29 +5,52 @@
|
||||
|
||||
#include "aciodrv/icca.h"
|
||||
|
||||
bool aciotest_icca_handler_init(uint8_t node_id, void **ctx)
|
||||
{
|
||||
*ctx = malloc(sizeof(uint32_t));
|
||||
*((uint32_t *) *ctx) = 0;
|
||||
|
||||
return aciodrv_icca_init(node_id);
|
||||
#define IDLE_RESPONSES_BETWEEN_FELICA_POLLS 5
|
||||
|
||||
struct icca_handler_ctx {
|
||||
bool init;
|
||||
bool slotted_reader;
|
||||
uint8_t last_poll;
|
||||
};
|
||||
|
||||
bool aciotest_icca_handler_init(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx)
|
||||
{
|
||||
*ctx = malloc(sizeof(struct icca_handler_ctx));
|
||||
|
||||
struct icca_handler_ctx *icca_ctx = (struct icca_handler_ctx*)*ctx;
|
||||
icca_ctx->init = false;
|
||||
|
||||
icca_ctx->slotted_reader = true;
|
||||
|
||||
icca_ctx->last_poll = 0;
|
||||
|
||||
icca_ctx->slotted_reader = aciodrv_icca_is_slotted(device, node_id);
|
||||
|
||||
return aciodrv_icca_init(device, node_id);
|
||||
}
|
||||
|
||||
bool aciotest_icca_handler_update(uint8_t node_id, void *ctx)
|
||||
bool aciotest_icca_handler_update(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx)
|
||||
{
|
||||
if (*((uint32_t *) ctx) == 0) {
|
||||
*((uint32_t *) ctx) = 1;
|
||||
struct icca_handler_ctx *icca_ctx = (struct icca_handler_ctx*)ctx;
|
||||
|
||||
/* eject cards that were left in the reader */
|
||||
if (!aciodrv_icca_set_state(
|
||||
node_id, AC_IO_ICCA_SLOT_STATE_EJECT, NULL)) {
|
||||
return false;
|
||||
if (icca_ctx->init == false) {
|
||||
icca_ctx->init = true;
|
||||
|
||||
if (icca_ctx->slotted_reader) {
|
||||
/* eject cards that were left in the reader */
|
||||
if (!aciodrv_icca_set_state(
|
||||
device, node_id, AC_IO_ICCA_SLOT_STATE_EJECT, NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ac_io_icca_state state;
|
||||
|
||||
if (!aciodrv_icca_get_state(node_id, &state)) {
|
||||
if (!aciodrv_icca_get_state(device, node_id, &state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -58,33 +81,48 @@ bool aciotest_icca_handler_update(uint8_t node_id, void *ctx)
|
||||
state.key_events[0],
|
||||
state.key_events[1]);
|
||||
|
||||
/* eject card with "empty" key */
|
||||
if (state.key_state & AC_IO_ICCA_KEYPAD_MASK_EMPTY) {
|
||||
if (!aciodrv_icca_set_state(
|
||||
node_id, AC_IO_ICCA_SLOT_STATE_EJECT, NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* allow new card to be inserted when slot is clear */
|
||||
if (!(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_BACK_ON) &&
|
||||
!(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_FRONT_ON)) {
|
||||
if (!aciodrv_icca_set_state(
|
||||
node_id, AC_IO_ICCA_SLOT_STATE_OPEN, NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* lock the card when fully inserted */
|
||||
if ((state.sensor_state & AC_IO_ICCA_SENSOR_MASK_BACK_ON) &&
|
||||
(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_FRONT_ON)) {
|
||||
if (!aciodrv_icca_set_state(
|
||||
node_id, AC_IO_ICCA_SLOT_STATE_CLOSE, NULL)) {
|
||||
return false;
|
||||
if (icca_ctx->slotted_reader) {
|
||||
/* eject card with "empty" key */
|
||||
if (state.key_state & AC_IO_ICCA_KEYPAD_MASK_EMPTY) {
|
||||
if (!aciodrv_icca_set_state(
|
||||
device, node_id, AC_IO_ICCA_SLOT_STATE_EJECT, NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aciodrv_icca_read_card(node_id, NULL)) {
|
||||
return false;
|
||||
/* allow new card to be inserted when slot is clear */
|
||||
if (!(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_BACK_ON) &&
|
||||
!(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_FRONT_ON)) {
|
||||
if (!aciodrv_icca_set_state(
|
||||
device, node_id, AC_IO_ICCA_SLOT_STATE_OPEN, NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* lock the card when fully inserted */
|
||||
if ((state.sensor_state & AC_IO_ICCA_SENSOR_MASK_BACK_ON) &&
|
||||
(state.sensor_state & AC_IO_ICCA_SENSOR_MASK_FRONT_ON)) {
|
||||
if (!aciodrv_icca_set_state(
|
||||
device, node_id, AC_IO_ICCA_SLOT_STATE_CLOSE, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!aciodrv_icca_read_card(device, node_id, NULL)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// wavepass reader, see eamio-icca for why this is done this way
|
||||
if (state.status_code != AC_IO_ICCA_STATUS_BUSY_NEW) {
|
||||
++icca_ctx->last_poll;
|
||||
}
|
||||
|
||||
if (icca_ctx->last_poll >= IDLE_RESPONSES_BETWEEN_FELICA_POLLS) {
|
||||
if (!aciodrv_icca_poll_felica(device, node_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
icca_ctx->last_poll = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool aciotest_icca_handler_init(uint8_t node_id, void **ctx);
|
||||
bool aciotest_icca_handler_update(uint8_t node_id, void *ctx);
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
bool aciotest_icca_handler_init(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx);
|
||||
bool aciotest_icca_handler_update(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx);
|
||||
|
||||
#endif
|
||||
@@ -8,15 +8,17 @@
|
||||
|
||||
#include "aciodrv/kfca.h"
|
||||
|
||||
bool aciotest_kfca_handler_init(uint8_t node_id, void **ctx)
|
||||
bool aciotest_kfca_handler_init(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx)
|
||||
{
|
||||
*ctx = malloc(sizeof(uint32_t));
|
||||
*((uint32_t *) *ctx) = 0;
|
||||
|
||||
return aciodrv_kfca_init(node_id);
|
||||
return aciodrv_kfca_init(device, node_id);
|
||||
}
|
||||
|
||||
bool aciotest_kfca_handler_update(uint8_t node_id, void *ctx)
|
||||
bool aciotest_kfca_handler_update(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx)
|
||||
{
|
||||
struct ac_io_kfca_poll_in pin;
|
||||
struct ac_io_kfca_poll_out pout;
|
||||
@@ -28,7 +30,7 @@ bool aciotest_kfca_handler_update(uint8_t node_id, void *ctx)
|
||||
pout.gpio |= 1 << gpio_test_pin;
|
||||
pout.gpio = ac_io_u32(pout.gpio);
|
||||
|
||||
if (!aciodrv_kfca_poll(node_id, &pout, &pin)) {
|
||||
if (!aciodrv_kfca_poll(device, node_id, &pout, &pin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
bool aciotest_kfca_handler_init(uint8_t node_id, void **ctx);
|
||||
bool aciotest_kfca_handler_update(uint8_t node_id, void *ctx);
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
bool aciotest_kfca_handler_init(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx);
|
||||
bool aciotest_kfca_handler_update(
|
||||
struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "aciotest/handler.h"
|
||||
#include "aciotest/icca.h"
|
||||
#include "aciotest/kfca.h"
|
||||
#include "aciotest/panb.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
@@ -37,6 +38,13 @@ static bool aciotest_assign_handler(
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp(product, "PANB", 4)) {
|
||||
handler->init = aciotest_panb_handler_init;
|
||||
handler->update = aciotest_panb_handler_update;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp(product, "BI2A", 4)) {
|
||||
if (bi2a_mode == 0) {
|
||||
handler->init = aciotest_bi2a_sdvx_handler_init;
|
||||
@@ -70,14 +78,17 @@ int main(int argc, char **argv)
|
||||
|
||||
log_to_writer(log_writer_stdout, NULL);
|
||||
|
||||
if (!aciodrv_device_open(argv[1], atoi(argv[2]))) {
|
||||
|
||||
struct aciodrv_device_ctx *device = aciodrv_device_open_path(argv[1], atoi(argv[2]));
|
||||
|
||||
if (!device) {
|
||||
printf("Opening acio device failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("Opening acio device successful\n");
|
||||
|
||||
uint8_t node_count = aciodrv_device_get_node_count();
|
||||
uint8_t node_count = aciodrv_device_get_node_count(device);
|
||||
printf("Enumerated %d nodes\n", node_count);
|
||||
|
||||
struct aciotest_handler_node_handler handler[aciotest_handler_max];
|
||||
@@ -88,7 +99,7 @@ int main(int argc, char **argv)
|
||||
|
||||
for (uint8_t i = 0; i < node_count; i++) {
|
||||
char product[4];
|
||||
aciodrv_device_get_node_product_ident(i, product);
|
||||
aciodrv_device_get_node_product_ident(device, i, product);
|
||||
printf(
|
||||
"> %d: %c%c%c%c\n",
|
||||
i + 1,
|
||||
@@ -110,7 +121,7 @@ int main(int argc, char **argv)
|
||||
|
||||
for (uint8_t i = 0; i < aciotest_handler_max; i++) {
|
||||
if (handler[i].init != NULL) {
|
||||
if (!handler[i].init(i, &handler[i].ctx)) {
|
||||
if (!handler[i].init(device, i, &handler[i].ctx)) {
|
||||
printf("ERROR: Initializing node %d failed\n", i);
|
||||
handler[i].update = NULL;
|
||||
}
|
||||
@@ -129,7 +140,7 @@ int main(int argc, char **argv)
|
||||
|
||||
for (uint8_t i = 0; i < aciotest_handler_max; i++) {
|
||||
if (handler[i].update != NULL) {
|
||||
if (!handler[i].update(i, handler[i].ctx)) {
|
||||
if (!handler[i].update(device, i, handler[i].ctx)) {
|
||||
printf("ERROR: Updating node %d, removed from loop\n", i);
|
||||
handler[i].update = NULL;
|
||||
Sleep(5000);
|
||||
|
||||
76
src/main/aciotest/panb.c
Normal file
76
src/main/aciotest/panb.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "aciotest/panb.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "aciodrv/panb.h"
|
||||
#include "aciodrv-proc/panb.h"
|
||||
|
||||
struct panb_handler_ctx {
|
||||
bool running;
|
||||
};
|
||||
|
||||
bool aciotest_panb_handler_init(struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx)
|
||||
{
|
||||
if (node_id != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
*ctx = malloc(sizeof(struct panb_handler_ctx));
|
||||
struct panb_handler_ctx *panb_ctx = (struct panb_handler_ctx*)*ctx;
|
||||
panb_ctx->running = true;
|
||||
|
||||
return aciodrv_proc_panb_init(device);
|
||||
}
|
||||
|
||||
bool aciotest_panb_handler_update(struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx)
|
||||
{
|
||||
uint8_t button[AC_IO_PANB_MAX_KEYS];
|
||||
struct ac_io_panb_poll_out state;
|
||||
struct panb_handler_ctx *panb_ctx = (struct panb_handler_ctx *) ctx;
|
||||
|
||||
if (node_id != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!panb_ctx->running) {
|
||||
printf(">>> PANB:\nDevice has been closed. Press Ctrl+C to exit.");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!aciodrv_proc_panb_get_state(button)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
printf(">>> PANB:\nPress first and last keys to close device\n");
|
||||
|
||||
for (int i=0; i<AC_IO_PANB_MAX_KEYS; i++) {
|
||||
printf("%01X ", button[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
/* I added a key combo to properly close the module.
|
||||
* Leaving the PANB device in autopolling state without ever
|
||||
* reading packets from the serial interface eventually
|
||||
* makes it fall into a state where it doesn't send anything anymore
|
||||
* and won't even respond to a reset, thus requiring a power cycle.
|
||||
*/
|
||||
if (button[0] && button[AC_IO_PANB_MAX_KEYS-1]) {
|
||||
aciodrv_proc_panb_fini(device);
|
||||
panb_ctx->running = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* light the panel */
|
||||
for (int i=0; i<AC_IO_PANB_MAX_KEYS; i++) {
|
||||
state.key[i].green = (button[i])? 0x7F : 0;
|
||||
state.key[i].red = 0;
|
||||
state.key[i].blue = (button[i])? 0x23 : 0;
|
||||
}
|
||||
|
||||
if (!aciodrv_panb_send_lamp(device, node_id, &state)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
12
src/main/aciotest/panb.h
Normal file
12
src/main/aciotest/panb.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef ACIOTEST_PANB_H
|
||||
#define ACIOTEST_PANB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
bool aciotest_panb_handler_init(struct aciodrv_device_ctx *device, uint8_t node_id, void **ctx);
|
||||
bool aciotest_panb_handler_update(struct aciodrv_device_ctx *device, uint8_t node_id, void *ctx);
|
||||
|
||||
#endif
|
||||
@@ -29,6 +29,17 @@ enum jb_io_panel_bit {
|
||||
JB_IO_PANEL_16 = 0x0F,
|
||||
};
|
||||
|
||||
/* input "single button mode" mappings. Allows you to check each corner of each
|
||||
button to determine any flaky inputs
|
||||
*/
|
||||
enum jb_io_panel_mode {
|
||||
JB_IO_PANEL_MODE_ALL = 0, // any of the four corners will trigger a panel
|
||||
JB_IO_PANEL_MODE_TOP_LEFT = 1,
|
||||
JB_IO_PANEL_MODE_TOP_RIGHT = 2,
|
||||
JB_IO_PANEL_MODE_BOTTOM_RIGHT = 3,
|
||||
JB_IO_PANEL_MODE_BOTTOM_LEFT = 4,
|
||||
};
|
||||
|
||||
/* Bit mappings for "system" inputs */
|
||||
enum jb_io_sys_bit {
|
||||
JB_IO_SYS_TEST = 0x00,
|
||||
@@ -73,17 +84,36 @@ bool jb_io_init(
|
||||
|
||||
void jb_io_fini(void);
|
||||
|
||||
/* TODO doc */
|
||||
/* Read input state */
|
||||
|
||||
bool jb_io_read_inputs(void);
|
||||
|
||||
bool jb_io_write_outputs(void);
|
||||
/* Get state of coin, test, service inputs */
|
||||
|
||||
uint8_t jb_io_get_sys_inputs(void);
|
||||
|
||||
/* Get panel button state. Will return either any button being pressed, or a
|
||||
particular panel corner depending on previous call to jb_io_set_panel_mode */
|
||||
|
||||
uint16_t jb_io_get_panel_inputs(void);
|
||||
|
||||
/* Set state of a PWM (dimmable) light */
|
||||
|
||||
void jb_io_set_rgb_led(
|
||||
enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b);
|
||||
|
||||
/* Transmit the light state to the IOPCB */
|
||||
|
||||
bool jb_io_write_lights(void);
|
||||
|
||||
/* Select operating mode for the panel. Should be immediately sent to the IOPCB */
|
||||
|
||||
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode);
|
||||
|
||||
/* Open or close the coin chute - true will redirect all coins to the return
|
||||
slot. Required for jb_io_set_panel_mode to operate correctly on p4io.
|
||||
Should be immediately sent to the IOPCB */
|
||||
|
||||
bool jb_io_set_coin_blocker(bool blocked);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,17 +14,23 @@
|
||||
// in all inputs and outputs (over sub IO) other than 14 keys to not work.
|
||||
static const uint8_t _BIO2DR_BI2A_IIDX_INIT_DATA = 0x2D;
|
||||
|
||||
static bool bio2drv_bi2a_iidx_init_io(uint8_t node_id)
|
||||
static bool bio2drv_bi2a_iidx_init_io(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(BIO2_BI2A_CMD_INIT);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.param = _BIO2DR_BI2A_IIDX_INIT_DATA;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Init node failed");
|
||||
return 0;
|
||||
}
|
||||
@@ -34,8 +40,12 @@ static bool bio2drv_bi2a_iidx_init_io(uint8_t node_id)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool bio2drv_bi2a_iidx_watchdog_start(uint8_t node_id)
|
||||
static bool bio2drv_bi2a_iidx_watchdog_start(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id)
|
||||
{
|
||||
log_assert(device);
|
||||
|
||||
// exit early and don't actually call watchdog
|
||||
// the watchdog call actually returns different sized packets depending on
|
||||
// the state this results in an issue during packet processing (see: #68)
|
||||
@@ -47,14 +57,13 @@ static bool bio2drv_bi2a_iidx_watchdog_start(uint8_t node_id)
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_WATCHDOG);
|
||||
msg.cmd.nbytes = 2;
|
||||
msg.cmd.nbytes = 2;
|
||||
|
||||
// uint16_t: 6000
|
||||
msg.cmd.raw[0] = 23;
|
||||
msg.cmd.raw[1] = 112;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 2
|
||||
device, &msg, offsetof(struct ac_io_message, cmd.raw) + 2
|
||||
)) {
|
||||
log_warning("Starting watchdog failed"); return false;
|
||||
}
|
||||
@@ -66,13 +75,15 @@ static bool bio2drv_bi2a_iidx_watchdog_start(uint8_t node_id)
|
||||
*/
|
||||
}
|
||||
|
||||
bool bio2drv_bi2a_iidx_init(uint8_t node_id)
|
||||
bool bio2drv_bi2a_iidx_init(struct aciodrv_device_ctx *device, uint8_t node_id)
|
||||
{
|
||||
if (!bio2drv_bi2a_iidx_init_io(node_id)) {
|
||||
log_assert(device);
|
||||
|
||||
if (!bio2drv_bi2a_iidx_init_io(device, node_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bio2drv_bi2a_iidx_watchdog_start(node_id)) {
|
||||
if (!bio2drv_bi2a_iidx_watchdog_start(device, node_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -80,12 +91,15 @@ bool bio2drv_bi2a_iidx_init(uint8_t node_id)
|
||||
}
|
||||
|
||||
bool bio2drv_bi2a_iidx_poll(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct bi2a_iidx_state_out *pout,
|
||||
struct bi2a_iidx_state_in *pin)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
// log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(BIO2_BI2A_CMD_POLL);
|
||||
msg.cmd.nbytes = sizeof(*pout);
|
||||
@@ -93,7 +107,9 @@ bool bio2drv_bi2a_iidx_poll(
|
||||
*(struct bi2a_iidx_state_out *) msg.cmd.raw = *pout;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + sizeof(*pin))) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + sizeof(*pin))) {
|
||||
log_warning("Polling of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
#ifndef BIO2DRV_BI2A_IIDX_H
|
||||
#define BIO2DRV_BI2A_IIDX_H
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "bio2/bi2a-iidx.h"
|
||||
|
||||
/**
|
||||
* Initialize a BI2A node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to initialize (0 based).
|
||||
* @return True if successful, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool bio2drv_bi2a_iidx_init(uint8_t node_id);
|
||||
bool bio2drv_bi2a_iidx_init(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Poll the BI2A board
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
@@ -26,6 +32,7 @@ bool bio2drv_bi2a_iidx_init(uint8_t node_id);
|
||||
* @see driver.h
|
||||
*/
|
||||
bool bio2drv_bi2a_iidx_poll(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct bi2a_iidx_state_out *pout,
|
||||
struct bi2a_iidx_state_in *pin);
|
||||
|
||||
@@ -12,17 +12,21 @@
|
||||
static const uint8_t _BIO2DR_BI2A_SDVX_INIT_DATA = 0x3B;
|
||||
|
||||
// this is probably InitIO
|
||||
static bool bio2drv_bi2a_sdvx_init_io(uint8_t node_id)
|
||||
static bool bio2drv_bi2a_sdvx_init_io(struct aciodrv_device_ctx *device, uint8_t node_id)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(BIO2_BI2A_CMD_INIT);
|
||||
msg.cmd.nbytes = 1;
|
||||
msg.cmd.param = _BIO2DR_BI2A_SDVX_INIT_DATA;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Init node failed");
|
||||
return 0;
|
||||
}
|
||||
@@ -32,8 +36,10 @@ static bool bio2drv_bi2a_sdvx_init_io(uint8_t node_id)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static bool bio2drv_bi2a_sdvx_watchdog_start(uint8_t node_id)
|
||||
static bool bio2drv_bi2a_sdvx_watchdog_start(struct aciodrv_device_ctx *device, uint8_t node_id)
|
||||
{
|
||||
log_assert(device);
|
||||
|
||||
// exit early and don't actually call watchdog
|
||||
// the watchdog call actually returns different sized packets depending on
|
||||
// the state this results in an issue during packet processing (see: #68)
|
||||
@@ -52,7 +58,7 @@ static bool bio2drv_bi2a_sdvx_watchdog_start(uint8_t node_id)
|
||||
msg.cmd.raw[1] = 112;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 2
|
||||
device, &msg, offsetof(struct ac_io_message, cmd.raw) + 2
|
||||
)) {
|
||||
log_warning("Starting watchdog failed"); return false;
|
||||
}
|
||||
@@ -65,6 +71,7 @@ static bool bio2drv_bi2a_sdvx_watchdog_start(uint8_t node_id)
|
||||
}
|
||||
|
||||
bool bio2drv_bi2a_sdvx_amp(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
uint8_t unused_1,
|
||||
uint8_t unused_2,
|
||||
@@ -73,6 +80,8 @@ bool bio2drv_bi2a_sdvx_amp(
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_AMP_CONTROL);
|
||||
msg.cmd.nbytes = 4;
|
||||
@@ -86,7 +95,9 @@ bool bio2drv_bi2a_sdvx_amp(
|
||||
msg.cmd.raw[3] = right;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + 1)) {
|
||||
log_warning("Setting AMP failed");
|
||||
return false;
|
||||
}
|
||||
@@ -96,17 +107,19 @@ bool bio2drv_bi2a_sdvx_amp(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool bio2drv_bi2a_sdvx_init(uint8_t node_id)
|
||||
bool bio2drv_bi2a_sdvx_init(struct aciodrv_device_ctx *device, uint8_t node_id)
|
||||
{
|
||||
if (!bio2drv_bi2a_sdvx_init_io(node_id)) {
|
||||
log_assert(device);
|
||||
|
||||
if (!bio2drv_bi2a_sdvx_init_io(device, node_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bio2drv_bi2a_sdvx_watchdog_start(node_id)) {
|
||||
if (!bio2drv_bi2a_sdvx_watchdog_start(device, node_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bio2drv_bi2a_sdvx_amp(node_id, 0, 0, 0, 0)) {
|
||||
if (!bio2drv_bi2a_sdvx_amp(device, node_id, 0, 0, 0, 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -114,12 +127,15 @@ bool bio2drv_bi2a_sdvx_init(uint8_t node_id)
|
||||
}
|
||||
|
||||
bool bio2drv_bi2a_sdvx_poll(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct bi2a_sdvx_state_out *pout,
|
||||
struct bi2a_sdvx_state_in *pin)
|
||||
{
|
||||
struct ac_io_message msg;
|
||||
|
||||
// log_assert(device);
|
||||
|
||||
msg.addr = node_id + 1;
|
||||
msg.cmd.code = ac_io_u16(AC_IO_CMD_KFCA_POLL);
|
||||
msg.cmd.nbytes = sizeof(*pout);
|
||||
@@ -127,7 +143,9 @@ bool bio2drv_bi2a_sdvx_poll(
|
||||
*(struct bi2a_sdvx_state_out *) msg.cmd.raw = *pout;
|
||||
|
||||
if (!aciodrv_send_and_recv(
|
||||
&msg, offsetof(struct ac_io_message, cmd.raw) + sizeof(*pin))) {
|
||||
device,
|
||||
&msg,
|
||||
offsetof(struct ac_io_message, cmd.raw) + sizeof(*pin))) {
|
||||
log_warning("Polling of node %d failed", node_id + 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
#ifndef BIO2DRV_BI2A_H
|
||||
#define BIO2DRV_BI2A_H
|
||||
#ifndef BIO2DRV_BI2A_SDVX_H
|
||||
#define BIO2DRV_BI2A_SDVX_H
|
||||
|
||||
#include "aciodrv/device.h"
|
||||
|
||||
#include "bio2/bi2a-sdvx.h"
|
||||
|
||||
/**
|
||||
* Initialize a BI2A node.
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to initialize (0 based).
|
||||
* @return True if successful, false on error.
|
||||
* @note This module is supposed to be used in combination with the common
|
||||
* device driver foundation.
|
||||
* @see driver.h
|
||||
*/
|
||||
bool bio2drv_bi2a_sdvx_init(uint8_t node_id);
|
||||
bool bio2drv_bi2a_sdvx_init(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id);
|
||||
|
||||
/**
|
||||
* Poll the board
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param state Pointer to a state struct to return the current state to
|
||||
* (optional, NULL for none).
|
||||
@@ -26,6 +32,7 @@ bool bio2drv_bi2a_sdvx_init(uint8_t node_id);
|
||||
* @see driver.h
|
||||
*/
|
||||
bool bio2drv_bi2a_sdvx_poll(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
const struct bi2a_sdvx_state_out *pout,
|
||||
struct bi2a_sdvx_state_in *pin);
|
||||
@@ -33,6 +40,7 @@ bool bio2drv_bi2a_sdvx_poll(
|
||||
/**
|
||||
* Set the BIO2 BI2A SDVX digital amp level
|
||||
*
|
||||
* @param device Context of opened device
|
||||
* @param node_id Id of the node to query (0 based).
|
||||
* @param primary primary volume (96-0)
|
||||
* @param headphone headphone volume (96-0)
|
||||
@@ -42,6 +50,7 @@ bool bio2drv_bi2a_sdvx_poll(
|
||||
* @note Note 96 is lowest volume level, 0 is highest
|
||||
*/
|
||||
bool bio2drv_bi2a_sdvx_amp(
|
||||
struct aciodrv_device_ctx *device,
|
||||
uint8_t node_id,
|
||||
uint8_t unused_1,
|
||||
uint8_t unused_2,
|
||||
|
||||
@@ -341,14 +341,14 @@ static HRESULT my_MFEnumDeviceSources(
|
||||
|
||||
for (UINT32 i = 0; i < nsrcs; ++i) {
|
||||
api = (*pppSourceActivate)[i];
|
||||
|
||||
|
||||
ret = com_proxy_wrap(&api_proxy, api, sizeof(*api->lpVtbl));
|
||||
|
||||
|
||||
if (ret != S_OK) {
|
||||
log_warning("Wrapping com proxy failed: %08lx", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
real_GetAllocatedString = api_vtbl->GetAllocatedString;
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#define D3D9EXHOOK_CONFIG_GFX_CONFINED_KEY "gfx.confined"
|
||||
#define D3D9EXHOOK_CONFIG_GFX_WINDOW_WIDTH_KEY "gfx.window_width"
|
||||
#define D3D9EXHOOK_CONFIG_GFX_WINDOW_HEIGHT_KEY "gfx.window_height"
|
||||
#define D3D9EXHOOK_CONFIG_GFX_WINDOW_X_KEY "gfx.window_x"
|
||||
#define D3D9EXHOOK_CONFIG_GFX_WINDOW_Y_KEY "gfx.window_y"
|
||||
#define D3D9EXHOOK_CONFIG_GFX_FORCED_REFRESHRATE_KEY "gfx.forced_refresh_rate"
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEVICE_ADAPTER_KEY "gfx.device_adapter"
|
||||
#define D3D9EXHOOK_CONFIG_GFX_FORCE_ORIENTATION_KEY "gfx.force_orientation"
|
||||
@@ -24,6 +26,8 @@
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_CONFINED_VALUE false
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_WIDTH_VALUE -1
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_HEIGHT_VALUE -1
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_X_VALUE -1
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_Y_VALUE -1
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_FORCED_RR_VALUE -1
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_DEVICE_ADAPTER_VALUE -1
|
||||
#define D3D9EXHOOK_CONFIG_GFX_DEFAULT_FORCE_ORIENTATION_VALUE -1
|
||||
@@ -62,6 +66,18 @@ void d3d9exhook_config_gfx_init(struct cconfig *config)
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_HEIGHT_VALUE,
|
||||
"Windowed height, -1 for default size");
|
||||
|
||||
cconfig_util_set_int(
|
||||
config,
|
||||
D3D9EXHOOK_CONFIG_GFX_WINDOW_X_KEY,
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_X_VALUE,
|
||||
"Windowed X, -1 for default X position");
|
||||
|
||||
cconfig_util_set_int(
|
||||
config,
|
||||
D3D9EXHOOK_CONFIG_GFX_WINDOW_Y_KEY,
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_Y_VALUE,
|
||||
"Windowed Y, -1 for default Y position");
|
||||
|
||||
cconfig_util_set_int(
|
||||
config,
|
||||
D3D9EXHOOK_CONFIG_GFX_FORCED_REFRESHRATE_KEY,
|
||||
@@ -161,6 +177,30 @@ void d3d9exhook_config_gfx_get(
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_HEIGHT_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(
|
||||
config,
|
||||
D3D9EXHOOK_CONFIG_GFX_WINDOW_X_KEY,
|
||||
&config_gfx->window_x,
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_X_VALUE)) {
|
||||
log_warning(
|
||||
"Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'",
|
||||
D3D9EXHOOK_CONFIG_GFX_WINDOW_X_KEY,
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_X_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(
|
||||
config,
|
||||
D3D9EXHOOK_CONFIG_GFX_WINDOW_Y_KEY,
|
||||
&config_gfx->window_y,
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_Y_VALUE)) {
|
||||
log_warning(
|
||||
"Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'",
|
||||
D3D9EXHOOK_CONFIG_GFX_WINDOW_Y_KEY,
|
||||
D3D9EXHOOK_CONFIG_GFX_DEFAULT_WINDOW_Y_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(
|
||||
config,
|
||||
D3D9EXHOOK_CONFIG_GFX_FORCED_REFRESHRATE_KEY,
|
||||
|
||||
@@ -15,6 +15,8 @@ struct d3d9exhook_config_gfx {
|
||||
bool confined;
|
||||
int32_t window_width;
|
||||
int32_t window_height;
|
||||
int32_t window_x;
|
||||
int32_t window_y;
|
||||
int32_t forced_refresh_rate;
|
||||
int32_t device_adapter;
|
||||
int32_t force_orientation;
|
||||
|
||||
@@ -45,6 +45,18 @@ static HRESULT STDCALL my_CreateDeviceEx(
|
||||
D3DDISPLAYMODEEX *fdm,
|
||||
IDirect3DDevice9Ex **pdev);
|
||||
|
||||
static HRESULT STDCALL my_EnumAdapterModes(
|
||||
IDirect3D9Ex *self,
|
||||
UINT Adapter,
|
||||
D3DFORMAT Format,
|
||||
UINT Mode,
|
||||
D3DDISPLAYMODE *pMode);
|
||||
|
||||
static UINT STDCALL my_GetAdapterModeCount(
|
||||
IDirect3D9Ex *self,
|
||||
UINT Adapter,
|
||||
D3DFORMAT Format);
|
||||
|
||||
static HRESULT STDCALL my_Direct3DCreate9Ex(UINT sdk_ver, IDirect3D9Ex **api);
|
||||
|
||||
static WNDPROC real_WndProc;
|
||||
@@ -88,12 +100,25 @@ static bool d3d9ex_confined;
|
||||
static int32_t d3d9ex_force_refresh_rate = -1;
|
||||
static int32_t d3d9ex_window_width = -1;
|
||||
static int32_t d3d9ex_window_height = -1;
|
||||
static int32_t d3d9ex_window_x = -1;
|
||||
static int32_t d3d9ex_window_y = -1;
|
||||
static bool d3d9ex_window_framed;
|
||||
static int32_t d3d9ex_device_adapter = -1;
|
||||
static int32_t d3d9ex_force_orientation = -1;
|
||||
static int32_t d3d9ex_force_screen_res_width = -1;
|
||||
static int32_t d3d9ex_force_screen_res_height = -1;
|
||||
|
||||
static bool check_d3d9ex_force_enum()
|
||||
{
|
||||
if (d3d9ex_force_refresh_rate != -1 &&
|
||||
d3d9ex_force_screen_res_width != -1 &&
|
||||
d3d9ex_force_screen_res_height != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
static const struct hook_symbol d3d9ex_hook_syms[] = {
|
||||
@@ -130,11 +155,16 @@ static HWND STDCALL my_CreateWindowExA(
|
||||
HINSTANCE hInstance,
|
||||
LPVOID lpParam)
|
||||
{
|
||||
if (d3d9ex_windowed && d3d9ex_window_framed) {
|
||||
/* use a different style */
|
||||
dwStyle |= WS_OVERLAPPEDWINDOW;
|
||||
/* also show mouse cursor */
|
||||
ShowCursor(TRUE);
|
||||
if (d3d9ex_windowed) {
|
||||
if (d3d9ex_window_framed) {
|
||||
/* use a different style */
|
||||
dwStyle |= WS_OVERLAPPEDWINDOW;
|
||||
/* also show mouse cursor */
|
||||
ShowCursor(TRUE);
|
||||
} else {
|
||||
dwStyle = dwStyle & ~WS_OVERLAPPEDWINDOW;
|
||||
ShowCursor(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
HWND hwnd = real_CreateWindowExA(
|
||||
@@ -157,11 +187,7 @@ static HWND STDCALL my_CreateWindowExA(
|
||||
static BOOL STDCALL
|
||||
my_MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint)
|
||||
{
|
||||
if (d3d9ex_windowed && d3d9ex_window_framed) {
|
||||
/* we have to adjust the window size, because the window needs to be a
|
||||
slightly bigger than the rendering resolution (window caption and
|
||||
stuff is included in the window size) */
|
||||
|
||||
if (d3d9ex_windowed) {
|
||||
if (d3d9ex_window_width != -1 && d3d9ex_window_height != -1) {
|
||||
log_misc(
|
||||
"Overriding window size from %dx%d with %dx%d",
|
||||
@@ -173,14 +199,31 @@ my_MoveWindow(HWND hWnd, int X, int Y, int nWidth, int nHeight, BOOL bRepaint)
|
||||
nWidth = d3d9ex_window_width;
|
||||
nHeight = d3d9ex_window_height;
|
||||
}
|
||||
if (d3d9ex_window_x != -1 && d3d9ex_window_y != -1) {
|
||||
log_misc(
|
||||
"Overriding window position from %dx%d with %dx%d",
|
||||
X,
|
||||
Y,
|
||||
d3d9ex_window_x,
|
||||
d3d9ex_window_y);
|
||||
|
||||
WINDOWPOS wp;
|
||||
calc_win_size_with_framed(hWnd, X, Y, nWidth, nHeight, &wp);
|
||||
SetWindowPos(hWnd, 0, wp.x, wp.y, wp.cx, wp.cy, 0);
|
||||
X = wp.x;
|
||||
Y = wp.y;
|
||||
nWidth = wp.cx;
|
||||
nHeight = wp.cy;
|
||||
X = d3d9ex_window_x;
|
||||
Y = d3d9ex_window_y;
|
||||
}
|
||||
|
||||
if (d3d9ex_window_framed) {
|
||||
/* we have to adjust the window size, because the window needs to
|
||||
be a slightly bigger than the rendering resolution (window caption
|
||||
and stuff is included in the window size) */
|
||||
|
||||
WINDOWPOS wp;
|
||||
calc_win_size_with_framed(hWnd, X, Y, nWidth, nHeight, &wp);
|
||||
SetWindowPos(hWnd, 0, wp.x, wp.y, wp.cx, wp.cy, 0);
|
||||
X = wp.x;
|
||||
Y = wp.y;
|
||||
nWidth = wp.cx;
|
||||
nHeight = wp.cy;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL result = real_MoveWindow(hWnd, X, Y, nWidth, nHeight, bRepaint);
|
||||
@@ -392,6 +435,54 @@ static HRESULT STDCALL my_CreateDeviceEx(
|
||||
return hr;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_EnumAdapterModes(
|
||||
IDirect3D9Ex *self,
|
||||
UINT Adapter,
|
||||
D3DFORMAT Format,
|
||||
UINT Mode,
|
||||
D3DDISPLAYMODE *pMode)
|
||||
{
|
||||
IDirect3D9Ex *real = com_proxy_downcast(self)->real;
|
||||
HRESULT hr;
|
||||
|
||||
if (d3d9ex_device_adapter>= 0) {
|
||||
Adapter = d3d9ex_device_adapter;
|
||||
}
|
||||
|
||||
hr = IDirect3D9Ex_EnumAdapterModes(real, Adapter, Format, Mode, pMode);
|
||||
|
||||
if (check_d3d9ex_force_enum()) {
|
||||
log_info("EnumAdapterModes: Forcing mode");
|
||||
pMode->Width = d3d9ex_force_screen_res_width;
|
||||
pMode->Height = d3d9ex_force_screen_res_height;
|
||||
pMode->RefreshRate = d3d9ex_force_refresh_rate;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
static UINT STDCALL my_GetAdapterModeCount(
|
||||
IDirect3D9Ex *self,
|
||||
UINT Adapter,
|
||||
D3DFORMAT Format)
|
||||
{
|
||||
IDirect3D9Ex *real = com_proxy_downcast(self)->real;
|
||||
UINT res;
|
||||
|
||||
if (d3d9ex_device_adapter>= 0) {
|
||||
Adapter = d3d9ex_device_adapter;
|
||||
}
|
||||
|
||||
res = IDirect3D9Ex_GetAdapterModeCount(real, Adapter, Format);
|
||||
|
||||
if (check_d3d9ex_force_enum()) {
|
||||
log_info("GetAdapterModeCount: Forcing single return");
|
||||
res = 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static HRESULT STDCALL my_Direct3DCreate9Ex(UINT sdk_ver, IDirect3D9Ex **api)
|
||||
{
|
||||
HRESULT hr;
|
||||
@@ -414,6 +505,8 @@ static HRESULT STDCALL my_Direct3DCreate9Ex(UINT sdk_ver, IDirect3D9Ex **api)
|
||||
api_vtbl = api_proxy->vptr;
|
||||
|
||||
api_vtbl->CreateDeviceEx = my_CreateDeviceEx;
|
||||
api_vtbl->EnumAdapterModes = my_EnumAdapterModes;
|
||||
api_vtbl->GetAdapterModeCount = my_GetAdapterModeCount;
|
||||
|
||||
*api = (IDirect3D9Ex *) api_proxy;
|
||||
|
||||
@@ -451,6 +544,8 @@ void d3d9ex_configure(struct d3d9exhook_config_gfx *gfx_config)
|
||||
d3d9ex_window_framed = gfx_config->framed;
|
||||
d3d9ex_window_width = gfx_config->window_width;
|
||||
d3d9ex_window_height = gfx_config->window_height;
|
||||
d3d9ex_window_x = gfx_config->window_x;
|
||||
d3d9ex_window_y = gfx_config->window_y;
|
||||
|
||||
d3d9ex_force_refresh_rate = gfx_config->forced_refresh_rate;
|
||||
d3d9ex_device_adapter = gfx_config->device_adapter;
|
||||
|
||||
@@ -3,7 +3,10 @@ dlls += \
|
||||
|
||||
libs_eamio-icca := \
|
||||
aciodrv \
|
||||
aciomgr \
|
||||
cconfig \
|
||||
util \
|
||||
|
||||
src_eamio-icca := \
|
||||
config-icc.c \
|
||||
eamio-icca.c \
|
||||
|
||||
55
src/main/eamio-icca/config-icc.c
Normal file
55
src/main/eamio-icca/config-icc.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "cconfig/cconfig-util.h"
|
||||
|
||||
#include "eamio-icca/config-icc.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define EAMIO_ICCA_CONFIG_ICC_PORT_KEY "icc.port"
|
||||
#define EAMIO_ICCA_CONFIG_ICC_BAUD_KEY "icc.baud"
|
||||
|
||||
#define EAMIO_ICCA_CONFIG_ICC_DEFAULT_PORT_VALUE "COM1"
|
||||
#define EAMIO_ICCA_CONFIG_ICC_DEFAULT_BAUD_VALUE 57600
|
||||
|
||||
void eamio_icca_config_icc_init(struct cconfig *config)
|
||||
{
|
||||
cconfig_util_set_str(
|
||||
config,
|
||||
EAMIO_ICCA_CONFIG_ICC_PORT_KEY,
|
||||
EAMIO_ICCA_CONFIG_ICC_DEFAULT_PORT_VALUE,
|
||||
"ICCA serial port");
|
||||
|
||||
cconfig_util_set_int(
|
||||
config,
|
||||
EAMIO_ICCA_CONFIG_ICC_BAUD_KEY,
|
||||
EAMIO_ICCA_CONFIG_ICC_DEFAULT_BAUD_VALUE,
|
||||
"ICCA bus baudrate (real devices expect 57600)");
|
||||
}
|
||||
|
||||
void eamio_icca_config_icc_get(
|
||||
struct icc_config *config_icc, struct cconfig *config)
|
||||
{
|
||||
if (!cconfig_util_get_str(
|
||||
config,
|
||||
EAMIO_ICCA_CONFIG_ICC_PORT_KEY,
|
||||
config_icc->port,
|
||||
sizeof(config_icc->port) - 1,
|
||||
EAMIO_ICCA_CONFIG_ICC_DEFAULT_PORT_VALUE)) {
|
||||
log_warning(
|
||||
"Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'",
|
||||
EAMIO_ICCA_CONFIG_ICC_PORT_KEY,
|
||||
EAMIO_ICCA_CONFIG_ICC_DEFAULT_PORT_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(
|
||||
config,
|
||||
EAMIO_ICCA_CONFIG_ICC_BAUD_KEY,
|
||||
&config_icc->baud,
|
||||
EAMIO_ICCA_CONFIG_ICC_DEFAULT_BAUD_VALUE)) {
|
||||
log_warning(
|
||||
"Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'",
|
||||
EAMIO_ICCA_CONFIG_ICC_BAUD_KEY,
|
||||
EAMIO_ICCA_CONFIG_ICC_DEFAULT_BAUD_VALUE);
|
||||
}
|
||||
}
|
||||
18
src/main/eamio-icca/config-icc.h
Normal file
18
src/main/eamio-icca/config-icc.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef EAMIO_ICCA_CONFIG_ICC_H
|
||||
#define EAMIO_ICCA_CONFIG_ICC_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
struct icc_config {
|
||||
char port[64];
|
||||
int32_t baud;
|
||||
};
|
||||
|
||||
void eamio_icca_config_icc_init(struct cconfig *config);
|
||||
|
||||
void eamio_icca_config_icc_get(
|
||||
struct icc_config *config_icc, struct cconfig *config);
|
||||
|
||||
#endif
|
||||
@@ -8,13 +8,21 @@
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "aciomgr/manager.h"
|
||||
#include "aciodrv/device.h"
|
||||
#include "aciodrv/icca.h"
|
||||
|
||||
#include "bemanitools/eamio.h"
|
||||
|
||||
#include "cconfig/cconfig-main.h"
|
||||
#include "eamio-icca/config-icc.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define IDLE_RESPONSES_BETWEEN_FELICA_POLLS 5
|
||||
#define NUMBER_OF_EMULATED_READERS 2
|
||||
#define INVALID_NODE_ID -1
|
||||
|
||||
static const uint8_t eam_io_keypad_mappings[16] = {EAM_IO_KEYPAD_DECIMAL,
|
||||
EAM_IO_KEYPAD_3,
|
||||
EAM_IO_KEYPAD_6,
|
||||
@@ -32,7 +40,16 @@ static const uint8_t eam_io_keypad_mappings[16] = {EAM_IO_KEYPAD_DECIMAL,
|
||||
EAM_IO_KEYPAD_5,
|
||||
EAM_IO_KEYPAD_8};
|
||||
|
||||
static struct ac_io_icca_state eam_io_icca_state[2];
|
||||
static struct ac_io_icca_state eam_io_icca_state[NUMBER_OF_EMULATED_READERS];
|
||||
|
||||
static struct aciomgr_port_dispatcher *acio_manager_ctx;
|
||||
|
||||
static int32_t icca_node_id[NUMBER_OF_EMULATED_READERS];
|
||||
|
||||
static bool icca_is_slotted[NUMBER_OF_EMULATED_READERS];
|
||||
|
||||
static int32_t icca_poll_counter[NUMBER_OF_EMULATED_READERS];
|
||||
static uint8_t icca_last_nonbusy_state[NUMBER_OF_EMULATED_READERS];
|
||||
|
||||
void eam_io_set_loggers(
|
||||
log_formatter_t misc,
|
||||
@@ -40,30 +57,113 @@ void eam_io_set_loggers(
|
||||
log_formatter_t warning,
|
||||
log_formatter_t fatal)
|
||||
{
|
||||
aciomgr_set_loggers(misc, info, warning, fatal);
|
||||
|
||||
log_to_external(misc, info, warning, fatal);
|
||||
}
|
||||
|
||||
// all of these are referred to internally as ICCA
|
||||
static bool check_if_icca(int node_id)
|
||||
{
|
||||
char product[ACIOMGR_NODE_PRODUCT_CODE_LEN];
|
||||
aciomgr_get_node_product_ident(acio_manager_ctx, node_id, product);
|
||||
|
||||
if (!memcmp(product, "ICCA", ACIOMGR_NODE_PRODUCT_CODE_LEN)) {
|
||||
return true;
|
||||
}
|
||||
if (!memcmp(product, "ICCB", ACIOMGR_NODE_PRODUCT_CODE_LEN)) {
|
||||
return true;
|
||||
}
|
||||
if (!memcmp(product, "ICCC", ACIOMGR_NODE_PRODUCT_CODE_LEN)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool eam_io_init(
|
||||
thread_create_t create, thread_join_t join, thread_destroy_t destroy)
|
||||
{
|
||||
if (!aciodrv_device_open("COM1", 57600)) {
|
||||
log_warning("Opening acio device on COM1 failed");
|
||||
struct cconfig *config;
|
||||
struct icc_config config_icc;
|
||||
|
||||
config = cconfig_init();
|
||||
|
||||
eamio_icca_config_icc_init(config);
|
||||
|
||||
if (!cconfig_main_config_init(
|
||||
config,
|
||||
"--icc-config",
|
||||
"eamio-icc.conf",
|
||||
"--help",
|
||||
"-h",
|
||||
"eamio-icca",
|
||||
CCONFIG_CMD_USAGE_OUT_STDOUT)) {
|
||||
cconfig_finit(config);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
eamio_icca_config_icc_get(&config_icc, config);
|
||||
|
||||
cconfig_finit(config);
|
||||
|
||||
acio_manager_ctx = aciomgr_port_init(config_icc.port, config_icc.baud);
|
||||
|
||||
if (acio_manager_ctx == NULL) {
|
||||
log_warning("Opening acio device on %s failed", config_icc.port);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
if (!aciodrv_icca_init(i)) {
|
||||
log_warning("Initializing icca %d failed", i);
|
||||
return false;
|
||||
struct aciodrv_device_ctx *device = aciomgr_port_checkout(acio_manager_ctx);
|
||||
|
||||
for (uint8_t i = 0; i < NUMBER_OF_EMULATED_READERS; i++) {
|
||||
icca_node_id[i] = INVALID_NODE_ID;
|
||||
|
||||
for (uint8_t nid = 0; nid < aciomgr_get_node_count(acio_manager_ctx); ++nid) {
|
||||
if (check_if_icca(nid)) {
|
||||
bool existing_reader = false;
|
||||
|
||||
for (uint8_t j = 0; j < i; j++) {
|
||||
if (nid == icca_node_id[j]) {
|
||||
existing_reader = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (existing_reader) {
|
||||
continue;
|
||||
}
|
||||
|
||||
icca_node_id[i] = nid;
|
||||
icca_poll_counter[i] = 0;
|
||||
icca_is_slotted[i] = aciodrv_icca_is_slotted(device, nid);
|
||||
icca_last_nonbusy_state[i] = 0;
|
||||
|
||||
log_misc("ICC reader %d is_slotted: %d", nid, icca_is_slotted[i]);
|
||||
|
||||
if (!aciodrv_icca_init(device, icca_node_id[i])) {
|
||||
log_warning("Initializing icca %d failed", i);
|
||||
icca_node_id[i] = INVALID_NODE_ID;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (icca_node_id[0] == INVALID_NODE_ID) {
|
||||
log_warning("No ICC readers detected");
|
||||
return false;
|
||||
}
|
||||
|
||||
aciomgr_port_checkin(acio_manager_ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
void eam_io_fini(void)
|
||||
{
|
||||
aciodrv_device_close();
|
||||
aciomgr_port_fini(acio_manager_ctx);
|
||||
}
|
||||
|
||||
uint16_t eam_io_get_keypad_state(uint8_t unit_no)
|
||||
@@ -85,13 +185,31 @@ uint8_t eam_io_get_sensor_state(uint8_t unit_no)
|
||||
{
|
||||
uint8_t sensors = 0;
|
||||
|
||||
if ((eam_io_icca_state[unit_no].sensor_state &
|
||||
AC_IO_ICCA_SENSOR_MASK_BACK_ON) > 0) {
|
||||
sensors |= (1 << EAM_IO_SENSOR_BACK);
|
||||
}
|
||||
if ((eam_io_icca_state[unit_no].sensor_state &
|
||||
AC_IO_ICCA_SENSOR_MASK_FRONT_ON) > 0) {
|
||||
sensors |= (1 << EAM_IO_SENSOR_FRONT);
|
||||
if (icca_is_slotted[unit_no]) {
|
||||
if ((eam_io_icca_state[unit_no].sensor_state &
|
||||
AC_IO_ICCA_SENSOR_MASK_BACK_ON) > 0) {
|
||||
sensors |= (1 << EAM_IO_SENSOR_BACK);
|
||||
}
|
||||
if ((eam_io_icca_state[unit_no].sensor_state &
|
||||
AC_IO_ICCA_SENSOR_MASK_FRONT_ON) > 0) {
|
||||
sensors |= (1 << EAM_IO_SENSOR_FRONT);
|
||||
}
|
||||
} else {
|
||||
// wavepass readers always report (EAM_IO_SENSOR_BACK + EAM_IO_SENSOR_FRONT) + type
|
||||
// but because we can't report status_code back directly
|
||||
// and libacio actually just ignores the sensor_state other then the type
|
||||
// we just return this state like we're a slotted reader so the emulation takes card of it
|
||||
if (eam_io_icca_state[unit_no].status_code == AC_IO_ICCA_STATUS_GOT_UID) {
|
||||
sensors |= (1 << EAM_IO_SENSOR_BACK);
|
||||
sensors |= (1 << EAM_IO_SENSOR_FRONT);
|
||||
}
|
||||
|
||||
// continue reporting last state during busy
|
||||
if (eam_io_icca_state[unit_no].status_code == AC_IO_ICCA_STATUS_BUSY_NEW) {
|
||||
sensors = icca_last_nonbusy_state[unit_no];
|
||||
} else {
|
||||
icca_last_nonbusy_state[unit_no] = sensors;
|
||||
}
|
||||
}
|
||||
|
||||
return sensors;
|
||||
@@ -109,33 +227,78 @@ uint8_t eam_io_read_card(uint8_t unit_no, uint8_t *card_id, uint8_t nbytes)
|
||||
|
||||
bool eam_io_card_slot_cmd(uint8_t unit_no, uint8_t cmd)
|
||||
{
|
||||
// this node is not setup, just return "success""
|
||||
if (icca_node_id[unit_no] == INVALID_NODE_ID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ignore these for wavepass
|
||||
if (!icca_is_slotted[unit_no]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct aciodrv_device_ctx *device = aciomgr_port_checkout(acio_manager_ctx);
|
||||
|
||||
bool response = false;
|
||||
switch (cmd) {
|
||||
case EAM_IO_CARD_SLOT_CMD_CLOSE:
|
||||
return aciodrv_icca_set_state(
|
||||
unit_no, AC_IO_ICCA_SLOT_STATE_CLOSE, NULL);
|
||||
response = aciodrv_icca_set_state(
|
||||
device, icca_node_id[unit_no], AC_IO_ICCA_SLOT_STATE_CLOSE, NULL);
|
||||
|
||||
case EAM_IO_CARD_SLOT_CMD_OPEN:
|
||||
return aciodrv_icca_set_state(
|
||||
unit_no, AC_IO_ICCA_SLOT_STATE_OPEN, NULL);
|
||||
response = aciodrv_icca_set_state(
|
||||
device, icca_node_id[unit_no], AC_IO_ICCA_SLOT_STATE_OPEN, NULL);
|
||||
|
||||
case EAM_IO_CARD_SLOT_CMD_EJECT:
|
||||
return aciodrv_icca_set_state(
|
||||
unit_no, AC_IO_ICCA_SLOT_STATE_EJECT, NULL);
|
||||
response = aciodrv_icca_set_state(
|
||||
device, icca_node_id[unit_no], AC_IO_ICCA_SLOT_STATE_EJECT, NULL);
|
||||
|
||||
case EAM_IO_CARD_SLOT_CMD_READ:
|
||||
return aciodrv_icca_read_card(unit_no, NULL) &&
|
||||
aciodrv_icca_get_state(unit_no, &eam_io_icca_state[unit_no]);
|
||||
response = aciodrv_icca_read_card(device, icca_node_id[unit_no], NULL) &&
|
||||
aciodrv_icca_get_state(
|
||||
device, icca_node_id[unit_no], &eam_io_icca_state[unit_no]);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
aciomgr_port_checkin(acio_manager_ctx);
|
||||
|
||||
return false;
|
||||
return response;
|
||||
}
|
||||
|
||||
bool eam_io_poll(uint8_t unit_no)
|
||||
{
|
||||
return aciodrv_icca_get_state(unit_no, &eam_io_icca_state[unit_no]);
|
||||
// this node is not setup, just return "success""
|
||||
if (icca_node_id[unit_no] == INVALID_NODE_ID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool response = aciodrv_icca_get_state(
|
||||
aciomgr_port_checkout(acio_manager_ctx),
|
||||
icca_node_id[unit_no],
|
||||
&eam_io_icca_state[unit_no]);
|
||||
aciomgr_port_checkin(acio_manager_ctx);
|
||||
|
||||
if (response && !icca_is_slotted[unit_no]) {
|
||||
// we handle wavepass a bit differently to handle polling felica
|
||||
if (eam_io_icca_state[unit_no].status_code != AC_IO_ICCA_STATUS_BUSY_NEW) {
|
||||
++icca_poll_counter[unit_no];
|
||||
}
|
||||
|
||||
// we must manually call this every few polls to actually update the felica state
|
||||
// we don't do it every poll, since card polling isn't that time sensitive of an operation
|
||||
// libacio does it every 5ish polls after the last AC_IO_ICCA_STATUS_BUSY_NEW message
|
||||
if (icca_poll_counter[unit_no] >= IDLE_RESPONSES_BETWEEN_FELICA_POLLS) {
|
||||
response = aciodrv_icca_poll_felica(
|
||||
aciomgr_port_checkout(acio_manager_ctx),
|
||||
icca_node_id[unit_no]);
|
||||
aciomgr_port_checkin(acio_manager_ctx);
|
||||
|
||||
icca_poll_counter[unit_no] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
const struct eam_io_config_api *eam_io_get_config_api(void)
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
log_to_writer(log_writer_stdout, NULL);
|
||||
|
||||
eam_io_set_loggers(
|
||||
log_impl_misc, log_impl_info, log_impl_warning, log_impl_fatal);
|
||||
|
||||
|
||||
@@ -25,16 +25,11 @@ struct file_entry {
|
||||
static struct array hooked_files;
|
||||
static CRITICAL_SECTION hooked_files_cs;
|
||||
|
||||
|
||||
BOOL my_GetFileInformationByHandle(
|
||||
HANDLE hFile,
|
||||
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
||||
);
|
||||
HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation);
|
||||
|
||||
BOOL (*real_GetFileInformationByHandle)(
|
||||
HANDLE hFile,
|
||||
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
||||
);
|
||||
BOOL (*real_GetFileInformationByHandle)
|
||||
(HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation);
|
||||
|
||||
static const struct hook_symbol memfile_hook_kernel32_syms[] = {
|
||||
{.name = "GetFileInformationByHandle",
|
||||
@@ -42,7 +37,6 @@ static const struct hook_symbol memfile_hook_kernel32_syms[] = {
|
||||
.link = (void **) &real_GetFileInformationByHandle},
|
||||
};
|
||||
|
||||
|
||||
void memfile_hook_init(void)
|
||||
{
|
||||
array_init(&hooked_files);
|
||||
@@ -82,7 +76,10 @@ void memfile_hook_fini(void)
|
||||
}
|
||||
|
||||
void memfile_hook_add_fd(
|
||||
const char *path, enum memfile_hook_path_mode path_mode, const void *data, uint32_t sz)
|
||||
const char *path,
|
||||
enum memfile_hook_path_mode path_mode,
|
||||
const void *data,
|
||||
uint32_t sz)
|
||||
{
|
||||
log_assert(path != NULL);
|
||||
|
||||
@@ -110,7 +107,11 @@ void memfile_hook_add_fd(
|
||||
|
||||
LeaveCriticalSection(&hooked_files_cs);
|
||||
|
||||
log_misc("memfile_hook_add_fd: path %s, mode %d, data size %d", path, path_mode, sz);
|
||||
log_misc(
|
||||
"memfile_hook_add_fd: path %s, mode %d, data size %d",
|
||||
path,
|
||||
path_mode,
|
||||
sz);
|
||||
}
|
||||
|
||||
static struct file_entry *memfile_hook_match_irp(const struct irp *irp)
|
||||
@@ -155,7 +156,10 @@ static HRESULT memfile_hook_irp_read(struct file_entry *entry, struct irp *irp)
|
||||
size_t nread = min(entry->sz - entry->pos, irp->read.nbytes);
|
||||
|
||||
if (nread > 0) {
|
||||
memcpy(irp->read.bytes + irp->read.pos, (uint8_t*)entry->data + entry->pos, nread);
|
||||
memcpy(
|
||||
irp->read.bytes + irp->read.pos,
|
||||
(uint8_t *) entry->data + entry->pos,
|
||||
nread);
|
||||
|
||||
entry->pos += nread;
|
||||
irp->read.pos += nread;
|
||||
@@ -248,9 +252,7 @@ HRESULT memfile_hook_dispatch_irp(struct irp *irp)
|
||||
|
||||
// this isn't a standard iohook IRP atm.
|
||||
BOOL my_GetFileInformationByHandle(
|
||||
HANDLE hFile,
|
||||
LPBY_HANDLE_FILE_INFORMATION lpFileInformation
|
||||
)
|
||||
HANDLE hFile, LPBY_HANDLE_FILE_INFORMATION lpFileInformation)
|
||||
{
|
||||
struct file_entry *entry;
|
||||
for (size_t i = 0; i < hooked_files.nitems; i++) {
|
||||
|
||||
@@ -32,7 +32,10 @@ enum memfile_hook_path_mode {
|
||||
* @param sz size of file
|
||||
*/
|
||||
void memfile_hook_add_fd(
|
||||
const char *path, enum memfile_hook_path_mode path_mode, const void *data, uint32_t sz);
|
||||
const char *path,
|
||||
enum memfile_hook_path_mode path_mode,
|
||||
const void *data,
|
||||
uint32_t sz);
|
||||
|
||||
/**
|
||||
* iohook dispatch function. Needs to be installed.
|
||||
|
||||
@@ -38,6 +38,8 @@ static struct bi2a_iidx_state_in pin_cur;
|
||||
static struct bi2a_iidx_state_out pout_staging;
|
||||
static struct bi2a_iidx_state_out pout_ready;
|
||||
|
||||
static struct aciodrv_device_ctx *bio2_device_ctx;
|
||||
|
||||
static bool _bio2_iidx_io_poll(
|
||||
const struct bi2a_iidx_state_out *pout, struct bi2a_iidx_state_in *pin)
|
||||
{
|
||||
@@ -47,7 +49,7 @@ static bool _bio2_iidx_io_poll(
|
||||
|
||||
processing_io = true;
|
||||
|
||||
if (!bio2drv_bi2a_iidx_poll(bio2_node_id, pout, pin)) {
|
||||
if (!bio2drv_bi2a_iidx_poll(bio2_device_ctx, bio2_node_id, pout, pin)) {
|
||||
processing_io = false;
|
||||
return false;
|
||||
}
|
||||
@@ -114,14 +116,17 @@ bool iidx_io_init(
|
||||
}
|
||||
}
|
||||
|
||||
if (!aciodrv_device_open(selected_port, config_bio2.baud)) {
|
||||
// BIO2's cannot share a bus with anything else, so use device directly
|
||||
bio2_device_ctx = aciodrv_device_open_path(selected_port, config_bio2.baud);
|
||||
|
||||
if (bio2_device_ctx == NULL) {
|
||||
log_info("Opening BIO2 device on [%s] failed", selected_port);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_info("Opening BIO2 device on [%s] successful", selected_port);
|
||||
|
||||
uint8_t node_count = aciodrv_device_get_node_count();
|
||||
uint8_t node_count = aciodrv_device_get_node_count(bio2_device_ctx);
|
||||
log_info("Enumerated %d nodes", node_count);
|
||||
|
||||
bio2_node_id = -1;
|
||||
@@ -129,7 +134,7 @@ bool iidx_io_init(
|
||||
for (uint8_t i = 0; i < node_count; i++) {
|
||||
char product[4];
|
||||
|
||||
aciodrv_device_get_node_product_ident(i, product);
|
||||
aciodrv_device_get_node_product_ident(bio2_device_ctx, i, product);
|
||||
|
||||
log_info(
|
||||
"> %d: %c%c%c%c\n",
|
||||
@@ -150,7 +155,7 @@ bool iidx_io_init(
|
||||
if (bio2_node_id != -1) {
|
||||
log_warning("Using BI2A on node: %d", bio2_node_id);
|
||||
|
||||
if (!bio2drv_bi2a_iidx_init(bio2_node_id)) {
|
||||
if (!bio2drv_bi2a_iidx_init(bio2_device_ctx, bio2_node_id)) {
|
||||
log_warning("Unable to start BI2A on node: %d", bio2_node_id);
|
||||
return false;
|
||||
}
|
||||
@@ -182,7 +187,7 @@ void iidx_io_fini(void)
|
||||
Sleep(1);
|
||||
}
|
||||
|
||||
aciodrv_device_close();
|
||||
aciodrv_device_close(bio2_device_ctx);
|
||||
}
|
||||
|
||||
void iidx_io_ep1_set_deck_lights(uint16_t deck_lights)
|
||||
|
||||
@@ -222,7 +222,7 @@ static bool debugger_create_process(
|
||||
|
||||
if (!ok) {
|
||||
log_warning(
|
||||
"ERRPR: Failed to launch hooked EXE: %08x", (unsigned int) GetLastError());
|
||||
"ERROR: Failed to launch hooked EXE: %08x", (unsigned int) GetLastError());
|
||||
|
||||
free(cmd_line);
|
||||
|
||||
@@ -592,4 +592,4 @@ void debugger_finit(bool failure)
|
||||
|
||||
CloseHandle(pi.hThread);
|
||||
CloseHandle(pi.hProcess);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,10 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
|
||||
|
||||
log_info("--- Begin jbhook dll_entry_init ---");
|
||||
|
||||
if (!options.disable_p4ioemu) {
|
||||
p4ioemu_init(jbhook_io_init());
|
||||
iohook_push_handler(p4ioemu_dispatch_irp);
|
||||
iohook_push_handler(ac_io_port_dispatch_irp);
|
||||
|
||||
if (!options.disable_p4ioemu) {
|
||||
log_info("Starting up jubeat IO backend");
|
||||
|
||||
jb_io_set_loggers(
|
||||
@@ -56,11 +57,12 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
|
||||
if (!jb_io_ok) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hook_setupapi_init(&p4ioemu_setupapi_data);
|
||||
p4ioemu_init(jbhook_io_init());
|
||||
}
|
||||
|
||||
if (!options.disable_cardemu) {
|
||||
ac_io_port_init();
|
||||
|
||||
log_info("Starting up card reader backend");
|
||||
|
||||
eam_io_set_loggers(
|
||||
@@ -72,6 +74,9 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *param)
|
||||
if (!eam_io_ok) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rs232_hook_init();
|
||||
ac_io_port_init();
|
||||
}
|
||||
|
||||
log_info("--- End jbhook dll_entry_init ---");
|
||||
@@ -128,21 +133,10 @@ BOOL WINAPI DllMain(HMODULE mod, DWORD reason, void *ctx)
|
||||
|
||||
app_hook_init(my_dll_entry_init, my_dll_entry_main);
|
||||
|
||||
iohook_push_handler(p4ioemu_dispatch_irp);
|
||||
iohook_push_handler(ac_io_port_dispatch_irp);
|
||||
|
||||
if (!options.disable_adapteremu) {
|
||||
adapter_hook_init();
|
||||
}
|
||||
|
||||
if (!options.disable_cardemu) {
|
||||
rs232_hook_init();
|
||||
}
|
||||
|
||||
if (!options.disable_p4ioemu) {
|
||||
hook_setupapi_init(&p4ioemu_setupapi_data);
|
||||
}
|
||||
|
||||
gfx_hook_init();
|
||||
jbhook_eamuse_hook_init();
|
||||
|
||||
|
||||
@@ -8,16 +8,10 @@
|
||||
|
||||
#include "jbhook/io.h"
|
||||
|
||||
#include "p4io/cmd.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
enum jbhook_io_p4io_command {
|
||||
JUHOOK_IO_P4IO_CMD_OUTPUTS = 0x18,
|
||||
};
|
||||
|
||||
struct jbhook_io_p4io_outputs {
|
||||
uint32_t outputs;
|
||||
};
|
||||
|
||||
static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes);
|
||||
static uint32_t jbhook_command_handle(
|
||||
uint8_t cmd,
|
||||
@@ -91,6 +85,7 @@ static const uint32_t jbhook_io_panel_mappings[] = {
|
||||
static const uint32_t jbhook_io_sys_button_mappings[] = {
|
||||
(1 << 28),
|
||||
(1 << 25),
|
||||
(1 << 24),
|
||||
};
|
||||
|
||||
static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes)
|
||||
@@ -116,7 +111,7 @@ static void jbhook_io_jamma2_read(void *resp, uint32_t nbytes)
|
||||
}
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
for (uint8_t i = 0; i < 3; i++) {
|
||||
if (buttons & (1 << i)) {
|
||||
*inputs |= jbhook_io_sys_button_mappings[i];
|
||||
}
|
||||
@@ -131,19 +126,56 @@ static uint32_t jbhook_command_handle(
|
||||
uint32_t resp_max_len)
|
||||
{
|
||||
switch (cmd) {
|
||||
case JUHOOK_IO_P4IO_CMD_OUTPUTS: {
|
||||
// const struct jbhook_io_p4io_outputs* req =
|
||||
// (const struct jbhook_io_p4io_outputs*) payload;
|
||||
|
||||
// log_misc("JUHOOK_IO_P4IO_CMD_OUTPUTS: 0x%X", req->outputs);
|
||||
|
||||
/* coin blocker: off 0x20, on 0x00 */
|
||||
case P4IO_CMD_COINSTOCK: {
|
||||
// on is 0x00, off is either 0x10 or 0x20 depending on whether it's
|
||||
// during gameplay (0x10) or test menu (0x20). Both seem to have the
|
||||
// same effect
|
||||
jb_io_set_coin_blocker(*(uint8_t*)payload == 0x00);
|
||||
|
||||
// this actually returns the coinstock, don't care for it
|
||||
memset(resp, 0, 4);
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
case P4IO_CMD_SET_PORTOUT: {
|
||||
const struct p4io_req_panel_mode *req =
|
||||
(const struct p4io_req_panel_mode *) payload;
|
||||
|
||||
// always fallback to ALL if input is unknown
|
||||
enum jb_io_panel_mode mode = JB_IO_PANEL_MODE_ALL;
|
||||
|
||||
if(req->is_single) {
|
||||
switch(req->mode) {
|
||||
case 0x0001:
|
||||
mode = JB_IO_PANEL_MODE_TOP_LEFT;
|
||||
break;
|
||||
|
||||
case 0x0000:
|
||||
mode = JB_IO_PANEL_MODE_TOP_RIGHT;
|
||||
break;
|
||||
|
||||
case 0x0101:
|
||||
mode = JB_IO_PANEL_MODE_BOTTOM_LEFT;
|
||||
break;
|
||||
|
||||
case 0x0100:
|
||||
mode = JB_IO_PANEL_MODE_BOTTOM_RIGHT;
|
||||
break;
|
||||
|
||||
default:
|
||||
mode = JB_IO_PANEL_MODE_ALL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jb_io_set_panel_mode(mode);
|
||||
|
||||
memset(resp, 0, 1);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
return 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
@@ -8,4 +8,6 @@ EXPORTS
|
||||
jb_io_read_inputs
|
||||
jb_io_set_loggers
|
||||
jb_io_set_rgb_led
|
||||
jb_io_write_outputs
|
||||
jb_io_set_coin_blocker
|
||||
jb_io_set_panel_mode
|
||||
jb_io_write_lights
|
||||
|
||||
@@ -119,7 +119,7 @@ bool jb_io_read_inputs(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jb_io_write_outputs(void)
|
||||
bool jb_io_write_lights(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -139,3 +139,14 @@ void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b)
|
||||
// I mean I guess there's reactive LEDs on the sides? I'm not going to the
|
||||
// effort to work out if they're controllable or not
|
||||
}
|
||||
|
||||
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode)
|
||||
{
|
||||
// panel always returns merged input state, no corner support
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jb_io_set_coin_blocker(bool blocked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
16
src/main/jbio-p4io/Module.mk
Normal file
16
src/main/jbio-p4io/Module.mk
Normal file
@@ -0,0 +1,16 @@
|
||||
dlls += jbio-p4io
|
||||
|
||||
ldflags_jbio-p4io := \
|
||||
-lsetupapi
|
||||
|
||||
src_jbio-p4io := \
|
||||
config-h44b.c \
|
||||
h44b.c \
|
||||
jbio.c \
|
||||
|
||||
libs_jbio-p4io := \
|
||||
aciodrv \
|
||||
aciomgr \
|
||||
cconfig \
|
||||
p4iodrv \
|
||||
util \
|
||||
55
src/main/jbio-p4io/config-h44b.c
Normal file
55
src/main/jbio-p4io/config-h44b.c
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "cconfig/cconfig-util.h"
|
||||
|
||||
#include "jbio-p4io/config-h44b.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
#define JBIO_CONFIG_H44B_PORT_KEY "h44b.port"
|
||||
#define JBIO_CONFIG_H44B_BAUD_KEY "h44b.baud"
|
||||
|
||||
#define JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE "COM2"
|
||||
#define JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE 57600
|
||||
|
||||
void jbio_config_h44b_init(struct cconfig *config)
|
||||
{
|
||||
cconfig_util_set_str(
|
||||
config,
|
||||
JBIO_CONFIG_H44B_PORT_KEY,
|
||||
JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE,
|
||||
"H44B serial port");
|
||||
|
||||
cconfig_util_set_int(
|
||||
config,
|
||||
JBIO_CONFIG_H44B_BAUD_KEY,
|
||||
JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE,
|
||||
"H44B bus baudrate (real devices expect 57600)");
|
||||
}
|
||||
|
||||
void jbio_config_h44b_get(
|
||||
struct h44b_config *config_h44b, struct cconfig *config)
|
||||
{
|
||||
if (!cconfig_util_get_str(
|
||||
config,
|
||||
JBIO_CONFIG_H44B_PORT_KEY,
|
||||
config_h44b->port,
|
||||
sizeof(config_h44b->port) - 1,
|
||||
JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE)) {
|
||||
log_warning(
|
||||
"Invalid value for key '%s' specified, fallback "
|
||||
"to default '%s'",
|
||||
JBIO_CONFIG_H44B_PORT_KEY,
|
||||
JBIO_CONFIG_H44B_DEFAULT_PORT_VALUE);
|
||||
}
|
||||
|
||||
if (!cconfig_util_get_int(
|
||||
config,
|
||||
JBIO_CONFIG_H44B_BAUD_KEY,
|
||||
&config_h44b->baud,
|
||||
JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE)) {
|
||||
log_warning(
|
||||
"Invalid value for key '%s' specified, fallback "
|
||||
"to default '%d'",
|
||||
JBIO_CONFIG_H44B_BAUD_KEY,
|
||||
JBIO_CONFIG_H44B_DEFAULT_BAUD_VALUE);
|
||||
}
|
||||
}
|
||||
18
src/main/jbio-p4io/config-h44b.h
Normal file
18
src/main/jbio-p4io/config-h44b.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef JBIO_CONFIG_H44B_H
|
||||
#define JBIO_CONFIG_H44B_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "cconfig/cconfig.h"
|
||||
|
||||
struct h44b_config {
|
||||
char port[64];
|
||||
int32_t baud;
|
||||
};
|
||||
|
||||
void jbio_config_h44b_init(struct cconfig *config);
|
||||
|
||||
void jbio_config_h44b_get(
|
||||
struct h44b_config *config_h44b, struct cconfig *config);
|
||||
|
||||
#endif
|
||||
101
src/main/jbio-p4io/h44b.c
Normal file
101
src/main/jbio-p4io/h44b.c
Normal file
@@ -0,0 +1,101 @@
|
||||
#define LOG_MODULE "jbio-h44b"
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "aciodrv/h44b.h"
|
||||
|
||||
#include "aciomgr/manager.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static int16_t h44b_node_id;
|
||||
|
||||
static atomic_bool running;
|
||||
|
||||
static struct aciomgr_port_dispatcher *acio_manager_ctx;
|
||||
|
||||
bool jb_io_h44b_init(const char *port, int32_t baud) {
|
||||
acio_manager_ctx = aciomgr_port_init(port, baud);
|
||||
|
||||
if (acio_manager_ctx == NULL) {
|
||||
log_info("Opening acio device on [%s] failed", port);
|
||||
return false;
|
||||
}
|
||||
|
||||
log_info("Opening acio device successful");
|
||||
|
||||
uint8_t node_count = aciomgr_get_node_count(acio_manager_ctx);
|
||||
log_info("Enumerated %d nodes", node_count);
|
||||
|
||||
h44b_node_id = -1;
|
||||
|
||||
for (uint8_t i = 0; i < node_count; i++) {
|
||||
char product[4];
|
||||
aciomgr_get_node_product_ident(acio_manager_ctx, i, product);
|
||||
log_info(
|
||||
"> %d: %c%c%c%c",
|
||||
i,
|
||||
product[0],
|
||||
product[1],
|
||||
product[2],
|
||||
product[3]);
|
||||
|
||||
if (!memcmp(product, "H44B", 4)) {
|
||||
if (h44b_node_id != -1) {
|
||||
log_warning("Multiple H44B found! Using highest node id.");
|
||||
}
|
||||
h44b_node_id = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (h44b_node_id != -1) {
|
||||
log_warning("Using H44B on node: %d", h44b_node_id);
|
||||
|
||||
bool init_result = aciodrv_h44b_init(
|
||||
aciomgr_port_checkout(acio_manager_ctx),
|
||||
h44b_node_id);
|
||||
aciomgr_port_checkin(acio_manager_ctx);
|
||||
|
||||
if (!init_result) {
|
||||
log_warning("Unable to start H44B on node: %d", h44b_node_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
running = true;
|
||||
log_warning("jbio-h44b now running");
|
||||
} else {
|
||||
log_warning("No H44B device found");
|
||||
}
|
||||
|
||||
return running;
|
||||
|
||||
}
|
||||
|
||||
bool jb_io_h44b_fini(void) {
|
||||
aciomgr_port_fini(acio_manager_ctx);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jb_io_h44b_write_lights(struct ac_io_h44b_output *lights) {
|
||||
if (!running) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool amp_result = aciodrv_h44b_lights(
|
||||
aciomgr_port_checkout(acio_manager_ctx),
|
||||
h44b_node_id,
|
||||
lights);
|
||||
aciomgr_port_checkin(acio_manager_ctx);
|
||||
|
||||
if (!amp_result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
7
src/main/jbio-p4io/h44b.h
Normal file
7
src/main/jbio-p4io/h44b.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "acio/h44b.h"
|
||||
|
||||
bool jb_io_h44b_init(const char *port, int32_t baud);
|
||||
bool jb_io_h44b_fini(void);
|
||||
bool jb_io_h44b_write_lights(struct ac_io_h44b_output *lights);
|
||||
13
src/main/jbio-p4io/jbio-p4io.def
Normal file
13
src/main/jbio-p4io/jbio-p4io.def
Normal file
@@ -0,0 +1,13 @@
|
||||
LIBRARY jbio
|
||||
|
||||
EXPORTS
|
||||
jb_io_fini
|
||||
jb_io_get_panel_inputs
|
||||
jb_io_get_sys_inputs
|
||||
jb_io_init
|
||||
jb_io_read_inputs
|
||||
jb_io_set_loggers
|
||||
jb_io_set_rgb_led
|
||||
jb_io_set_panel_mode
|
||||
jb_io_set_coin_blocker
|
||||
jb_io_write_lights
|
||||
229
src/main/jbio-p4io/jbio.c
Normal file
229
src/main/jbio-p4io/jbio.c
Normal file
@@ -0,0 +1,229 @@
|
||||
// clang-format off
|
||||
// Don't format because the order is important here
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <stdlib.h>
|
||||
// clang-format on
|
||||
|
||||
#include "aciomgr/manager.h"
|
||||
|
||||
#include "cconfig/cconfig-main.h"
|
||||
|
||||
#include "bemanitools/jbio.h"
|
||||
|
||||
#include "jbio-p4io/config-h44b.h"
|
||||
#include "jbio-p4io/h44b.h"
|
||||
|
||||
#include "p4iodrv/device.h"
|
||||
|
||||
#include "util/log.h"
|
||||
|
||||
static struct p4iodrv_ctx *p4io_ctx;
|
||||
static uint16_t jb_io_panels;
|
||||
static uint8_t jb_io_sys_buttons;
|
||||
|
||||
static bool lights_present;
|
||||
static struct ac_io_h44b_output jb_io_lights;
|
||||
static struct ac_io_h44b_output jb_io_new_lights;
|
||||
|
||||
static bool coin_blocked;
|
||||
|
||||
void jb_io_set_loggers(
|
||||
log_formatter_t misc,
|
||||
log_formatter_t info,
|
||||
log_formatter_t warning,
|
||||
log_formatter_t fatal)
|
||||
{
|
||||
aciomgr_set_loggers(misc, info, warning, fatal);
|
||||
|
||||
log_to_external(misc, info, warning, fatal);
|
||||
}
|
||||
|
||||
bool jb_io_init(
|
||||
thread_create_t thread_create,
|
||||
thread_join_t thread_join,
|
||||
thread_destroy_t thread_destroy)
|
||||
{
|
||||
struct cconfig *config;
|
||||
struct h44b_config config_h44b;
|
||||
|
||||
config = cconfig_init();
|
||||
|
||||
jbio_config_h44b_init(config);
|
||||
|
||||
if (!cconfig_main_config_init(
|
||||
config,
|
||||
"--h44b-config",
|
||||
"jbio-h44b.conf",
|
||||
"--help",
|
||||
"-h",
|
||||
"jbio-h44b",
|
||||
CCONFIG_CMD_USAGE_OUT_STDOUT)) {
|
||||
cconfig_finit(config);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
jbio_config_h44b_get(&config_h44b, config);
|
||||
|
||||
cconfig_finit(config);
|
||||
|
||||
p4io_ctx = p4iodrv_open();
|
||||
if(!p4io_ctx) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// some people use p4io just for inputs and have no lights. Soft fail when
|
||||
// h44b is not able to be connected instead of returning false
|
||||
lights_present = jb_io_h44b_init(config_h44b.port, config_h44b.baud);
|
||||
if(!lights_present) {
|
||||
log_warning("Could not connect to H44B, lights disabled");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void jb_io_fini(void)
|
||||
{
|
||||
p4iodrv_close(p4io_ctx);
|
||||
}
|
||||
|
||||
static const uint32_t jb_io_panel_mappings[] = {
|
||||
(1 << 5),
|
||||
(1 << 1),
|
||||
(1 << 13),
|
||||
(1 << 9),
|
||||
(1 << 6),
|
||||
(1 << 2),
|
||||
(1 << 14),
|
||||
(1 << 10),
|
||||
(1 << 7),
|
||||
(1 << 3),
|
||||
(1 << 15),
|
||||
(1 << 11),
|
||||
(1 << 16),
|
||||
(1 << 4),
|
||||
(1 << 20),
|
||||
(1 << 12),
|
||||
};
|
||||
|
||||
static const uint32_t jb_io_sys_button_mappings[] = {
|
||||
(1 << 28),
|
||||
(1 << 25),
|
||||
};
|
||||
|
||||
bool jb_io_read_inputs(void)
|
||||
{
|
||||
uint32_t jamma[4];
|
||||
if(!p4iodrv_read_jamma(p4io_ctx, jamma)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jb_io_panels = 0;
|
||||
jb_io_sys_buttons = 0;
|
||||
|
||||
// panel is active low
|
||||
uint32_t panel_in = ~jamma[0];
|
||||
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
if (panel_in & jb_io_panel_mappings[i]) {
|
||||
jb_io_panels |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
// sys is active high
|
||||
for (uint8_t i = 0; i < 2; i++) {
|
||||
if (jamma[0] & jb_io_sys_button_mappings[i]) {
|
||||
jb_io_sys_buttons |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jb_io_write_lights(void)
|
||||
{
|
||||
if(lights_present && memcmp(&jb_io_lights, &jb_io_new_lights, sizeof(jb_io_lights))) {
|
||||
memcpy(&jb_io_lights, &jb_io_new_lights, sizeof(jb_io_lights));
|
||||
return jb_io_h44b_write_lights(&jb_io_lights);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t jb_io_get_sys_inputs(void)
|
||||
{
|
||||
return jb_io_sys_buttons;
|
||||
}
|
||||
|
||||
uint16_t jb_io_get_panel_inputs(void)
|
||||
{
|
||||
return jb_io_panels;
|
||||
}
|
||||
|
||||
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode)
|
||||
{
|
||||
struct p4io_req_panel_mode panel_mode = {0};
|
||||
|
||||
panel_mode.is_single = 1;
|
||||
|
||||
switch(mode) {
|
||||
case JB_IO_PANEL_MODE_ALL:
|
||||
panel_mode.is_single = 0;
|
||||
panel_mode.mode = 0;
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_TOP_LEFT:
|
||||
panel_mode.mode = 0x0001;
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_TOP_RIGHT:
|
||||
panel_mode.mode = 0x0000;
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_BOTTOM_LEFT:
|
||||
panel_mode.mode = 0x0101;
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_BOTTOM_RIGHT:
|
||||
panel_mode.mode = 0x0100;
|
||||
break;
|
||||
}
|
||||
|
||||
return p4iodrv_cmd_portout(p4io_ctx, (uint8_t*)&panel_mode);
|
||||
}
|
||||
|
||||
void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
uint8_t *light;
|
||||
|
||||
switch(unit) {
|
||||
case JB_IO_RGB_LED_FRONT:
|
||||
light = jb_io_new_lights.front_rgb;
|
||||
break;
|
||||
case JB_IO_RGB_LED_TOP:
|
||||
light = jb_io_new_lights.top_rgb;
|
||||
break;
|
||||
case JB_IO_RGB_LED_LEFT:
|
||||
light = jb_io_new_lights.left_rgb;
|
||||
break;
|
||||
case JB_IO_RGB_LED_RIGHT:
|
||||
light = jb_io_new_lights.right_rgb;
|
||||
break;
|
||||
case JB_IO_RGB_LED_TITLE:
|
||||
light = jb_io_new_lights.title_rgb;
|
||||
break;
|
||||
case JB_IO_RGB_LED_WOOFER:
|
||||
light = jb_io_new_lights.woofer_rgb;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
light[0] = r;
|
||||
light[1] = g;
|
||||
light[2] = b;
|
||||
}
|
||||
|
||||
bool jb_io_set_coin_blocker(bool blocked) {
|
||||
coin_blocked = blocked;
|
||||
|
||||
uint8_t coin[4] = {0};
|
||||
coin[0] = coin_blocked ? 0x00 : 0x20;
|
||||
return p4iodrv_cmd_coinstock(p4io_ctx, coin);
|
||||
}
|
||||
@@ -102,7 +102,7 @@ bool jb_io_read_inputs(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jb_io_write_outputs(void)
|
||||
bool jb_io_write_lights(void)
|
||||
{
|
||||
/* The generic input stack currently initiates lighting sends and input
|
||||
reads simultaneously, though this might change later. Perform all of our
|
||||
@@ -122,9 +122,20 @@ uint16_t jb_io_get_panel_inputs(void)
|
||||
return jb_io_panels;
|
||||
}
|
||||
|
||||
bool jb_io_set_panel_mode(enum jb_io_panel_mode mode)
|
||||
{
|
||||
// geninput only uses 1 switch per panel, so ignore alternate modes
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jb_io_set_coin_blocker(bool blocked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void jb_io_set_rgb_led(enum jb_io_rgb_led unit, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
mapper_write_light(unit * 3, r);
|
||||
mapper_write_light(unit * 3 + 1, g);
|
||||
mapper_write_light(unit * 3 + 2, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,7 @@ EXPORTS
|
||||
jb_io_init
|
||||
jb_io_read_inputs
|
||||
jb_io_set_loggers
|
||||
jb_io_set_panel_mode
|
||||
jb_io_set_rgb_led
|
||||
jb_io_write_outputs
|
||||
jb_io_set_coin_blocker
|
||||
jb_io_write_lights
|
||||
|
||||
@@ -51,8 +51,13 @@ int main(int argc, char **argv)
|
||||
rgb_t lights[6] = {0};
|
||||
|
||||
bool loop = true;
|
||||
uint8_t cnt = 0;
|
||||
uint16_t cnt = 0;
|
||||
enum jbio_light_mode light_mode = LIGHTS_OFF;
|
||||
enum jb_io_panel_mode panel_mode = JB_IO_PANEL_MODE_ALL;
|
||||
|
||||
bool panel_corners_animate = false;
|
||||
char *all_text;
|
||||
char top_left, top_right, bottom_left, bottom_right;
|
||||
|
||||
while (loop) {
|
||||
if (!jb_io_read_inputs()) {
|
||||
@@ -60,6 +65,39 @@ int main(int argc, char **argv)
|
||||
return -2;
|
||||
}
|
||||
|
||||
if(panel_corners_animate && (cnt % 50) == 0) {
|
||||
panel_mode++;
|
||||
panel_mode %= 5;
|
||||
|
||||
// skip the all state
|
||||
if(panel_mode == JB_IO_PANEL_MODE_ALL) {
|
||||
panel_mode++;
|
||||
}
|
||||
|
||||
jb_io_set_panel_mode(panel_mode);
|
||||
}
|
||||
|
||||
all_text = " ";
|
||||
top_left = top_right = bottom_left = bottom_right = ' ';
|
||||
switch(panel_mode) {
|
||||
case JB_IO_PANEL_MODE_ALL:
|
||||
all_text = "ALL";
|
||||
top_left = top_right = bottom_left = bottom_right = '*';
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_TOP_LEFT:
|
||||
top_left = '*';
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_TOP_RIGHT:
|
||||
top_right = '*';
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_BOTTOM_LEFT:
|
||||
bottom_left = '*';
|
||||
break;
|
||||
case JB_IO_PANEL_MODE_BOTTOM_RIGHT:
|
||||
bottom_right = '*';
|
||||
break;
|
||||
}
|
||||
|
||||
/* get inputs */
|
||||
input_sys = jb_io_get_sys_inputs();
|
||||
input_panel = jb_io_get_panel_inputs();
|
||||
@@ -85,12 +123,12 @@ int main(int argc, char **argv)
|
||||
"\n"
|
||||
" Test: %d Service: %d Coin: %d\n"
|
||||
" .___.___.___.___.\n"
|
||||
" | %d | %d | %d | %d |\n"
|
||||
" |---|---|---|---|\n"
|
||||
" | %d | %d | %d | %d |\n"
|
||||
" |---|---|---|---|\n"
|
||||
" | %d | %d | %d | %d |\n"
|
||||
" |---|---|---|---|\n"
|
||||
" | %d | %d | %d | %d | Corner\n"
|
||||
" |---|---|---|---| .___.\n"
|
||||
" | %d | %d | %d | %d | |%c %c|\n"
|
||||
" |---|---|---|---| |%s|\n"
|
||||
" | %d | %d | %d | %d | |%c %c|\n"
|
||||
" |---|---|---|---| `---`\n"
|
||||
" | %d | %d | %d | %d |\n"
|
||||
" `---`---`---`---`\n",
|
||||
cnt,
|
||||
@@ -126,10 +164,19 @@ int main(int argc, char **argv)
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_06),
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_07),
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_08),
|
||||
|
||||
top_left,
|
||||
top_right,
|
||||
all_text,
|
||||
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_09),
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_10),
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_11),
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_12),
|
||||
|
||||
bottom_left,
|
||||
bottom_right,
|
||||
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_13),
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_14),
|
||||
IS_BIT_SET(input_panel, JB_IO_PANEL_15),
|
||||
@@ -165,7 +212,7 @@ int main(int argc, char **argv)
|
||||
jb_io_set_rgb_led(i, lights[i].r, lights[i].g, lights[i].b);
|
||||
}
|
||||
|
||||
if (!jb_io_write_outputs()) {
|
||||
if (!jb_io_write_lights()) {
|
||||
printf("ERROR: Writing outputs failed\n");
|
||||
return -4;
|
||||
}
|
||||
@@ -185,6 +232,7 @@ int main(int argc, char **argv)
|
||||
" 2: Set all lights on (cleared by pressing any panel)\n"
|
||||
" 3: Tie each R/G/B to inputs (panels + test + service)\n"
|
||||
" 4: Set all lights off\n"
|
||||
" 5: Toggle switch corner test mode\n"
|
||||
"Waiting for input: ");
|
||||
char c = getchar();
|
||||
|
||||
@@ -195,7 +243,7 @@ int main(int argc, char **argv)
|
||||
jb_io_set_rgb_led(i, 0, 0, 0);
|
||||
}
|
||||
|
||||
if (!jb_io_write_outputs()) {
|
||||
if (!jb_io_write_lights()) {
|
||||
printf("ERROR: Writing outputs failed\n");
|
||||
return -4;
|
||||
}
|
||||
@@ -219,6 +267,15 @@ int main(int argc, char **argv)
|
||||
break;
|
||||
}
|
||||
|
||||
case '5': {
|
||||
panel_corners_animate = !panel_corners_animate;
|
||||
if(!panel_corners_animate) {
|
||||
panel_mode = JB_IO_PANEL_MODE_ALL;
|
||||
jb_io_set_panel_mode(panel_mode);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case '0':
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -64,7 +64,7 @@ struct property *boot_property_load(const char *filename)
|
||||
nbytes = property_read_query_memsize(boot_property_fread, f_keyhole, 0, 0);
|
||||
|
||||
if (nbytes < 0) {
|
||||
log_fatal("%s: Error parsing configuration file", filename);
|
||||
log_fatal("%s: Error querying configuration file", filename);
|
||||
}
|
||||
|
||||
buffer = xmalloc(nbytes);
|
||||
@@ -76,7 +76,7 @@ struct property *boot_property_load(const char *filename)
|
||||
rewind(f);
|
||||
|
||||
if (!property_insert_read(prop, 0, boot_property_fread, f_keyhole)) {
|
||||
log_fatal("%s: Error parsing configuration file", filename);
|
||||
log_fatal("%s: Error reading configuration file", filename);
|
||||
}
|
||||
|
||||
TlsFree(f_keyhole);
|
||||
|
||||
@@ -35,7 +35,7 @@ static void *STDCALL my_GetProcAddress(HMODULE dll, const char *name);
|
||||
|
||||
static void bt_get_ikey_status(struct ikey_status *ik);
|
||||
|
||||
#if AVS_VERSION >= 1600
|
||||
#if AVS_VERSION >= 1509
|
||||
static void bt_get_fucked(bt_get_fucked_t callback, uint32_t ctx);
|
||||
#endif
|
||||
|
||||
@@ -48,7 +48,7 @@ static HMODULE(STDCALL *real_GetModuleHandleW)(const wchar_t *name);
|
||||
static void *(STDCALL *real_GetProcAddress)(HMODULE dll, const char *name);
|
||||
|
||||
static struct stub stub_list[] = {
|
||||
#if AVS_VERSION >= 1600
|
||||
#if AVS_VERSION >= 1509
|
||||
{"k_bt0001", bt_get_ikey_status},
|
||||
{"k_bt0002", bt_get_fucked},
|
||||
#else
|
||||
@@ -134,7 +134,7 @@ static void bt_get_ikey_status(struct ikey_status *ik)
|
||||
ik[1].cert_valid_end = -1;
|
||||
}
|
||||
|
||||
#if AVS_VERSION >= 1600
|
||||
#if AVS_VERSION >= 1509
|
||||
static void bt_get_fucked(bt_get_fucked_t callback, uint32_t ctx)
|
||||
{
|
||||
log_info(">>> k_bt0002");
|
||||
|
||||
67
src/main/p4io/cmd.h
Normal file
67
src/main/p4io/cmd.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#ifndef P4IO_CMD_H
|
||||
#define P4IO_CMD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define P4IO_SOF 0xAA
|
||||
|
||||
enum p4io_command {
|
||||
P4IO_CMD_INIT = 0x00,
|
||||
P4IO_CMD_GET_DEVICE_INFO = 0x01,
|
||||
P4IO_CMD_SET_PORTOUT = 0x12,
|
||||
P4IO_CMD_COINSTOCK = 0x18,
|
||||
P4IO_CMD_RESET_WTD = 0x1C,
|
||||
P4IO_CMD_SCI_MNG_OPEN = 0x20,
|
||||
P4IO_CMD_SCI_UPDATE = 0x21,
|
||||
/* SCI = serial communication interface */
|
||||
P4IO_CMD_SCI_MNG_BREAK = 0x24,
|
||||
/* Read round plug id over one-wire */
|
||||
P4IO_CMD_DALLAS_READ_ID = 0x40,
|
||||
/* Read round plug mem over one-wire */
|
||||
P4IO_CMD_DALLAS_READ_MEM = 0x41
|
||||
};
|
||||
|
||||
enum p4io_roundplug_type {
|
||||
P4IO_ROUNDPLUG_BLACK = 0,
|
||||
P4IO_ROUNDPLUG_WHITE = 1,
|
||||
};
|
||||
|
||||
#define P4IO_CMD_HEADER_LEN 4
|
||||
#define P4IO_MAX_PAYLOAD 60
|
||||
|
||||
struct p4io_cmd_header {
|
||||
uint8_t sof;
|
||||
uint8_t cmd;
|
||||
uint8_t seq_num;
|
||||
uint8_t payload_len;
|
||||
};
|
||||
|
||||
struct p4io_cmd_package {
|
||||
struct p4io_cmd_header header;
|
||||
uint8_t payload[P4IO_MAX_PAYLOAD];
|
||||
};
|
||||
|
||||
struct p4io_req_read_roundplug {
|
||||
// see enum p4io_roundplug_type
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
struct p4io_req_panel_mode {
|
||||
uint16_t mode;
|
||||
uint8_t is_single;
|
||||
uint8_t padding[13];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct p4io_resp_device_info {
|
||||
uint32_t type;
|
||||
uint8_t padding;
|
||||
uint8_t version_major;
|
||||
uint8_t version_minor;
|
||||
uint8_t version_revision;
|
||||
char product_code[4];
|
||||
char build_date[16];
|
||||
char build_time[16];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
13
src/main/p4io/guid.h
Normal file
13
src/main/p4io/guid.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef P4IO_GUID_H
|
||||
#define P4IO_GUID_H
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
static const GUID p4io_guid = {
|
||||
0x8B7250A5,
|
||||
0x4F61,
|
||||
0x46C9,
|
||||
{0x84, 0x3A, 0xE6, 0x68, 0x06, 0x47, 0x6A, 0x20}
|
||||
};
|
||||
|
||||
#endif
|
||||
42
src/main/p4io/ioctl.h
Normal file
42
src/main/p4io/ioctl.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#ifndef P4IO_IOCTL_H
|
||||
#define P4IO_IOCTL_H
|
||||
|
||||
/* can't seem to #include the requisite DDK headers from usermode code,
|
||||
so we have to redefine these macros here */
|
||||
|
||||
#ifndef CTL_CODE
|
||||
#define CTL_CODE(DeviceType, Function, Method, Access) \
|
||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||
#endif
|
||||
|
||||
#ifndef METHOD_BUFFERED
|
||||
#define METHOD_BUFFERED 0
|
||||
#endif
|
||||
|
||||
#ifndef FILE_ANY_ACCESS
|
||||
#define FILE_ANY_ACCESS 0x00
|
||||
#endif
|
||||
|
||||
#ifndef FILE_DEVICE_UNKNOWN
|
||||
#define FILE_DEVICE_UNKNOWN 0x22
|
||||
#endif
|
||||
|
||||
#define P4IO_FUNCTION_READ_JAMMA_2 0x801
|
||||
#define P4IO_FUNCTION_GET_DEVICE_NAME 0x803
|
||||
|
||||
// 0x22200Cu sent to bulk handle
|
||||
#define P4IO_IOCTL_GET_DEVICE_NAME \
|
||||
CTL_CODE( \
|
||||
FILE_DEVICE_UNKNOWN, \
|
||||
P4IO_FUNCTION_GET_DEVICE_NAME, \
|
||||
METHOD_BUFFERED, \
|
||||
FILE_ANY_ACCESS)
|
||||
// 0x222004 sent to int handle
|
||||
#define P4IO_IOCTL_READ_JAMMA_2 \
|
||||
CTL_CODE( \
|
||||
FILE_DEVICE_UNKNOWN, \
|
||||
P4IO_FUNCTION_READ_JAMMA_2, \
|
||||
METHOD_BUFFERED, \
|
||||
FILE_ANY_ACCESS)
|
||||
|
||||
#endif
|
||||
6
src/main/p4iodrv/Module.mk
Normal file
6
src/main/p4iodrv/Module.mk
Normal file
@@ -0,0 +1,6 @@
|
||||
libs += p4iodrv
|
||||
|
||||
src_p4iodrv := \
|
||||
device.c \
|
||||
usb.c \
|
||||
|
||||
133
src/main/p4iodrv/device.c
Normal file
133
src/main/p4iodrv/device.c
Normal file
@@ -0,0 +1,133 @@
|
||||
#define LOG_MODULE "p4iodrv-device"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include "p4io/cmd.h"
|
||||
|
||||
#include "p4iodrv/device.h"
|
||||
#include "p4iodrv/usb.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/mem.h"
|
||||
|
||||
struct p4iodrv_ctx {
|
||||
HANDLE jamma_handle;
|
||||
HANDLE bulk_handle;
|
||||
uint8_t seq_no;
|
||||
};
|
||||
|
||||
static void p4io_cmd_init(struct p4iodrv_ctx *ctx);
|
||||
static bool p4io_print_version(struct p4iodrv_ctx *ctx);
|
||||
|
||||
static bool p4io_transfer(
|
||||
struct p4iodrv_ctx *ctx,
|
||||
uint8_t cmd,
|
||||
const void *req_payload,
|
||||
size_t req_payload_len,
|
||||
void *resp_payload,
|
||||
size_t resp_payload_len
|
||||
) {
|
||||
size_t transferred_response_payload = resp_payload_len;
|
||||
bool ret = p4io_usb_transfer(ctx->bulk_handle, cmd, ctx->seq_no,
|
||||
req_payload, req_payload_len,
|
||||
resp_payload, &transferred_response_payload);
|
||||
|
||||
ctx->seq_no++;
|
||||
|
||||
if(!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(resp_payload_len && transferred_response_payload != resp_payload_len) {
|
||||
log_warning("Asked for %u bytes got %u", (unsigned)resp_payload_len,
|
||||
(unsigned)transferred_response_payload);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct p4iodrv_ctx *p4iodrv_open(void) {
|
||||
struct p4iodrv_ctx *ctx = xcalloc(sizeof(struct p4iodrv_ctx));
|
||||
|
||||
// jamma is read as fast as possible in its own thread, so having them be
|
||||
// separate files is absolutely essential to avoid the bulk handle being
|
||||
// starved
|
||||
ctx->jamma_handle = p4io_usb_open();
|
||||
ctx->bulk_handle = p4io_usb_open();
|
||||
ctx->seq_no = 0;
|
||||
|
||||
if(ctx->jamma_handle == INVALID_HANDLE_VALUE || ctx->bulk_handle == INVALID_HANDLE_VALUE) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p4io_cmd_init(ctx);
|
||||
|
||||
if(!p4io_print_version(ctx)) {
|
||||
free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
void p4iodrv_close(struct p4iodrv_ctx *ctx) {
|
||||
p4io_usb_close(ctx->jamma_handle);
|
||||
p4io_usb_close(ctx->bulk_handle);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
bool p4iodrv_read_jamma(struct p4iodrv_ctx *ctx, uint32_t jamma[4]) {
|
||||
return p4io_usb_read_jamma(ctx->jamma_handle, jamma);
|
||||
}
|
||||
|
||||
// send something you don't expect a response for
|
||||
static bool p4io_send(struct p4iodrv_ctx *ctx, uint8_t cmd) {
|
||||
return p4io_transfer(ctx, cmd, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
// Real IO does not check the return value, so neither do we.
|
||||
// Even if we were to check the return value here, the reset command returns a 0
|
||||
// byte response - not even an "OK" status, 0 bytes in total. That technically
|
||||
// should be raising an error, but because reset is a special case it's ignored
|
||||
static void p4io_cmd_init(struct p4iodrv_ctx *ctx) {
|
||||
p4io_send(ctx, P4IO_CMD_INIT);
|
||||
}
|
||||
|
||||
static bool p4io_print_version(struct p4iodrv_ctx *ctx) {
|
||||
char p4io_name[128];
|
||||
|
||||
if(!p4io_usb_read_device_name(ctx->bulk_handle, p4io_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct p4io_resp_device_info dev_info;
|
||||
if(!p4iodrv_cmd_device_info(ctx, &dev_info)) {
|
||||
log_warning("p4io get_device_info failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
dev_info.build_date[15] = '\0';
|
||||
dev_info.build_time[15] = '\0';
|
||||
|
||||
log_info("p4io name: %s", p4io_name);
|
||||
log_info("p4io type: %08X", dev_info.type);
|
||||
log_info("p4io version: %d.%d.%d", dev_info.version_major, dev_info.version_minor, dev_info.version_revision);
|
||||
log_info("p4io product: %.4s", dev_info.product_code);
|
||||
log_info("p4io build date: %.16s", dev_info.build_date);
|
||||
log_info("p4io build time: %.16s", dev_info.build_time);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool p4iodrv_cmd_device_info(struct p4iodrv_ctx *ctx, struct p4io_resp_device_info *info) {
|
||||
return p4io_transfer(ctx, P4IO_CMD_GET_DEVICE_INFO, NULL, 0, info, sizeof(*info));
|
||||
}
|
||||
|
||||
bool p4iodrv_cmd_portout(struct p4iodrv_ctx *ctx, const uint8_t buffer[16]) {
|
||||
return p4io_transfer(ctx, P4IO_CMD_SET_PORTOUT, buffer, 16, NULL, 0);
|
||||
}
|
||||
|
||||
bool p4iodrv_cmd_coinstock(struct p4iodrv_ctx *ctx, const uint8_t buffer[4]) {
|
||||
return p4io_transfer(ctx, P4IO_CMD_COINSTOCK, buffer, 4, NULL, 0);
|
||||
}
|
||||
18
src/main/p4iodrv/device.h
Normal file
18
src/main/p4iodrv/device.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef P4IODRV_DEVICE_H
|
||||
#define P4IODRV_DEVICE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "p4io/cmd.h"
|
||||
|
||||
struct p4iodrv_ctx;
|
||||
|
||||
struct p4iodrv_ctx *p4iodrv_open(void);
|
||||
void p4iodrv_close(struct p4iodrv_ctx *ctx);
|
||||
bool p4iodrv_cmd_device_info(struct p4iodrv_ctx *ctx, struct p4io_resp_device_info *info);
|
||||
bool p4iodrv_cmd_portout(struct p4iodrv_ctx *ctx, const uint8_t buffer[16]);
|
||||
bool p4iodrv_cmd_coinstock(struct p4iodrv_ctx *ctx, const uint8_t buffer[4]);
|
||||
bool p4iodrv_read_jamma(struct p4iodrv_ctx *ctx, uint32_t jamma[4]);
|
||||
|
||||
#endif
|
||||
162
src/main/p4iodrv/usb.c
Normal file
162
src/main/p4iodrv/usb.c
Normal file
@@ -0,0 +1,162 @@
|
||||
#define LOG_MODULE "p4iodrv-usb"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
// clang-format off
|
||||
// Don't format because the order is important here
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
// clang-format on
|
||||
|
||||
#include "p4io/cmd.h"
|
||||
#include "p4io/guid.h"
|
||||
#include "p4io/ioctl.h"
|
||||
|
||||
#include "p4iodrv/usb.h"
|
||||
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
HANDLE p4io_usb_open(void) {
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
wchar_t p4io_filename[MAX_PATH]; // game uses 1024, but it shouldn't be that long
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA_W detail_data = NULL;
|
||||
HDEVINFO dev_info_set;
|
||||
|
||||
dev_info_set = SetupDiGetClassDevsW(&p4io_guid, NULL, NULL, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
|
||||
|
||||
if(dev_info_set == INVALID_HANDLE_VALUE) {
|
||||
log_warning("SetupDiGetClassDevs fail, is p4io device connected and driver installed?");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
SP_DEVICE_INTERFACE_DATA interface_data;
|
||||
interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
if(!SetupDiEnumDeviceInterfaces(dev_info_set, NULL, &p4io_guid, 0, &interface_data)) {
|
||||
log_warning("SetupDiEnumDeviceInterfaces fail");
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
DWORD required_size;
|
||||
SetupDiGetDeviceInterfaceDetailW(dev_info_set, &interface_data, NULL, 0, &required_size, NULL);
|
||||
|
||||
detail_data = malloc(required_size);
|
||||
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
|
||||
|
||||
if(!SetupDiGetDeviceInterfaceDetailW(dev_info_set, &interface_data, detail_data, required_size, NULL, NULL)) {
|
||||
log_warning("SetupDiGetDeviceInterfaceDetailW fail");
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
wstr_cpy(p4io_filename, MAX_PATH, detail_data->DevicePath);
|
||||
wstr_cat(p4io_filename, MAX_PATH, L"\\p4io");
|
||||
|
||||
log_info("p4io found at path %ls", p4io_filename);
|
||||
|
||||
handle = CreateFileW(p4io_filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
|
||||
if(handle == INVALID_HANDLE_VALUE) {
|
||||
log_warning("CreateFileW fail");
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
CLEANUP:
|
||||
free(detail_data);
|
||||
SetupDiDestroyDeviceInfoList(dev_info_set);
|
||||
|
||||
return handle;
|
||||
}
|
||||
void p4io_usb_close(HANDLE p4io_handle) {
|
||||
if(p4io_handle != INVALID_HANDLE_VALUE) {
|
||||
CloseHandle(p4io_handle);
|
||||
}
|
||||
p4io_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
bool p4io_usb_read_jamma(HANDLE interrupt_handle, uint32_t jamma[4]) {
|
||||
DWORD bytes_returned;
|
||||
if(!DeviceIoControl(interrupt_handle, P4IO_IOCTL_READ_JAMMA_2, NULL, 0, jamma, sizeof(uint32_t[4]), &bytes_returned, NULL)) {
|
||||
log_warning("jamma read failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool p4io_usb_read_device_name(HANDLE bulk_handle, char name[128]) {
|
||||
DWORD bytes_returned;
|
||||
if(!DeviceIoControl(bulk_handle, P4IO_IOCTL_GET_DEVICE_NAME, NULL, 0, name, 128, &bytes_returned, NULL)) {
|
||||
log_warning("p4io does not support get_name cmd");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool p4io_usb_transfer(
|
||||
HANDLE bulk_handle,
|
||||
uint8_t cmd,
|
||||
uint8_t seq_no,
|
||||
const void *req_payload,
|
||||
size_t req_payload_len,
|
||||
void *resp_payload,
|
||||
size_t *resp_payload_len
|
||||
) {
|
||||
DWORD bytes_requested;
|
||||
DWORD bytes_xferred;
|
||||
struct p4io_cmd_package cmd_buf;
|
||||
|
||||
if(bulk_handle == INVALID_HANDLE_VALUE) {
|
||||
log_warning("p4io not open");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(req_payload_len > sizeof(cmd_buf.payload)) {
|
||||
log_warning("request too big");
|
||||
return false;
|
||||
}
|
||||
|
||||
cmd_buf.header.sof = P4IO_SOF;
|
||||
cmd_buf.header.cmd = cmd;
|
||||
cmd_buf.header.seq_num = seq_no;
|
||||
cmd_buf.header.payload_len = req_payload_len;
|
||||
memcpy(cmd_buf.payload, req_payload, req_payload_len);
|
||||
|
||||
bytes_requested = P4IO_CMD_HEADER_LEN + req_payload_len;
|
||||
|
||||
if(!WriteFile(bulk_handle, &cmd_buf, bytes_requested, &bytes_xferred, 0)) {
|
||||
log_warning("WriteFile failed");
|
||||
return false;
|
||||
}
|
||||
if(bytes_xferred != bytes_requested) {
|
||||
log_warning("WriteFile didn't finish");
|
||||
return false;
|
||||
}
|
||||
|
||||
// must be 65 bytes or requests can stall - only 64 will ever be returned
|
||||
if(!ReadFile(bulk_handle, &cmd_buf, 65, &bytes_xferred, 0)) {
|
||||
log_warning("ReadFile (%u) failed", (unsigned)bytes_requested);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(bytes_xferred >= sizeof(struct p4io_cmd_header)) {
|
||||
memcpy(resp_payload, cmd_buf.payload, *resp_payload_len);
|
||||
*resp_payload_len = cmd_buf.header.payload_len;
|
||||
|
||||
if(cmd_buf.header.sof != P4IO_SOF) {
|
||||
log_warning("Response bad header");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(seq_no != cmd_buf.header.seq_num) {
|
||||
log_warning("seq_num mismatch (ours %d != theirs %d)", seq_no, cmd_buf.header.seq_num);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
*resp_payload_len = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
22
src/main/p4iodrv/usb.h
Normal file
22
src/main/p4iodrv/usb.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef P4IODRV_USB_H
|
||||
#define P4IODRV_USB_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <windows.h>
|
||||
|
||||
HANDLE p4io_usb_open(void);
|
||||
void p4io_usb_close(HANDLE p4io_handle);
|
||||
bool p4io_usb_read_jamma(HANDLE interrupt_handle, uint32_t jamma[4]);
|
||||
bool p4io_usb_read_device_name(HANDLE bulk_handle, char name[128]);
|
||||
bool p4io_usb_transfer(
|
||||
HANDLE bulk_handle,
|
||||
uint8_t cmd,
|
||||
uint8_t seq_no,
|
||||
const void *req_payload,
|
||||
size_t req_payload_len,
|
||||
void *resp_payload,
|
||||
size_t *resp_payload_len
|
||||
);
|
||||
|
||||
#endif
|
||||
@@ -7,78 +7,14 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "hook/iohook.h"
|
||||
#include "p4io/cmd.h"
|
||||
#include "p4io/ioctl.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/log.h"
|
||||
#include "util/str.h"
|
||||
|
||||
//#define P4IOEMU_DEBUG_DUMP
|
||||
|
||||
/* can't seem to #include the requisite DDK headers from usermode code,
|
||||
so we have to redefine these macros here */
|
||||
|
||||
#define CTL_CODE(DeviceType, Function, Method, Access) \
|
||||
(((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))
|
||||
|
||||
#define METHOD_BUFFERED 0
|
||||
|
||||
#define FILE_ANY_ACCESS 0x00
|
||||
|
||||
#define FILE_DEVICE_UNKNOWN 0x22
|
||||
|
||||
#define P4IO_FUNCTION_READ_JAMMA_2 0x801
|
||||
#define P4IO_FUNCTION_GET_DEVICE_NAME 0x803
|
||||
|
||||
#define IOCTL_P4IO_GET_DEVICE_NAME \
|
||||
CTL_CODE( \
|
||||
FILE_DEVICE_UNKNOWN, \
|
||||
P4IO_FUNCTION_GET_DEVICE_NAME, \
|
||||
METHOD_BUFFERED, \
|
||||
FILE_ANY_ACCESS)
|
||||
#define IOCTL_P4IO_READ_JAMMA_2 \
|
||||
CTL_CODE( \
|
||||
FILE_DEVICE_UNKNOWN, \
|
||||
P4IO_FUNCTION_READ_JAMMA_2, \
|
||||
METHOD_BUFFERED, \
|
||||
FILE_ANY_ACCESS)
|
||||
|
||||
enum p4ioemu_p4io_command {
|
||||
P4IOEMU_P4IO_CMD_INIT = 0x00,
|
||||
P4IOEMU_P4IO_CMD_GET_DEVICE_INFO = 0x01,
|
||||
P4IOEMU_P4IO_CMD_UNKNOWN = 0x12,
|
||||
P4IOEMU_P4IO_CMD_RESET_WTD = 0x1C,
|
||||
P4IOEMU_P4IO_CMD_SCI_MNG_OPEN = 0x20,
|
||||
P4IOEMU_P4IO_CMD_SCI_UPDATE = 0x21,
|
||||
/* SCI = serial communication interface */
|
||||
P4IOEMU_P4IO_CMD_SCI_MNG_BREAK = 0x24,
|
||||
/* Read round plug id over one-wire */
|
||||
P4IOEMU_P4IO_CMD_DALLAS_READ_ID = 0x40,
|
||||
/* Read round plug mem over one-wire */
|
||||
P4IOEMU_P4IO_CMD_DALLAS_READ_MEM = 0x41
|
||||
};
|
||||
|
||||
struct p4ioemu_p4io_cmd_package {
|
||||
uint8_t header_AA;
|
||||
uint8_t cmd;
|
||||
uint8_t seq_num;
|
||||
uint8_t payload_len;
|
||||
};
|
||||
|
||||
struct p4ioemu_p4io_device_info_resp {
|
||||
uint32_t type;
|
||||
uint8_t padding;
|
||||
uint8_t version_major;
|
||||
uint8_t version_minor;
|
||||
uint8_t version_revision;
|
||||
char product_code[4];
|
||||
char build_date[16];
|
||||
char build_time[16];
|
||||
};
|
||||
|
||||
struct p4ioemu_p4io_read_roundplug_req {
|
||||
/* 0 = black, 1 = white */
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
static const struct p4ioemu_device_msg_hook *p4ioemu_device_msg_hook;
|
||||
|
||||
static HANDLE p4ioemu_p4io_fd;
|
||||
@@ -95,7 +31,7 @@ static uint32_t p4ioemu_p4io_command_handle(
|
||||
uint32_t resp_max_len)
|
||||
{
|
||||
switch (cmd) {
|
||||
case P4IOEMU_P4IO_CMD_INIT: {
|
||||
case P4IO_CMD_INIT: {
|
||||
log_misc("P4IOEMU_P4IO_CMD_INIT");
|
||||
|
||||
/* no data to send to host */
|
||||
@@ -104,11 +40,11 @@ static uint32_t p4ioemu_p4io_command_handle(
|
||||
return 0;
|
||||
}
|
||||
|
||||
case P4IOEMU_P4IO_CMD_GET_DEVICE_INFO: {
|
||||
case P4IO_CMD_GET_DEVICE_INFO: {
|
||||
log_misc("P4IOEMU_P4IO_CMD_GET_DEVICE_INFO");
|
||||
|
||||
struct p4ioemu_p4io_device_info_resp *info =
|
||||
(struct p4ioemu_p4io_device_info_resp *) resp;
|
||||
struct p4io_resp_device_info *info =
|
||||
(struct p4io_resp_device_info *) resp;
|
||||
|
||||
info->type = 0x37133713;
|
||||
info->version_major = 5;
|
||||
@@ -118,12 +54,12 @@ static uint32_t p4ioemu_p4io_command_handle(
|
||||
memcpy(info->build_date, "build_date", 11);
|
||||
memcpy(info->build_time, "build_time", 11);
|
||||
|
||||
return sizeof(struct p4ioemu_p4io_device_info_resp);
|
||||
return sizeof(struct p4io_resp_device_info);
|
||||
}
|
||||
|
||||
case P4IOEMU_P4IO_CMD_DALLAS_READ_ID: {
|
||||
const struct p4ioemu_p4io_read_roundplug_req *req =
|
||||
(const struct p4ioemu_p4io_read_roundplug_req *) payload;
|
||||
case P4IO_CMD_DALLAS_READ_ID: {
|
||||
const struct p4io_req_read_roundplug *req =
|
||||
(const struct p4io_req_read_roundplug *) payload;
|
||||
|
||||
log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_ID: %d", req->type);
|
||||
|
||||
@@ -136,9 +72,9 @@ static uint32_t p4ioemu_p4io_command_handle(
|
||||
return 8;
|
||||
}
|
||||
|
||||
case P4IOEMU_P4IO_CMD_DALLAS_READ_MEM: {
|
||||
const struct p4ioemu_p4io_read_roundplug_req *req =
|
||||
(const struct p4ioemu_p4io_read_roundplug_req *) payload;
|
||||
case P4IO_CMD_DALLAS_READ_MEM: {
|
||||
const struct p4io_req_read_roundplug *req =
|
||||
(const struct p4io_req_read_roundplug *) payload;
|
||||
|
||||
log_misc("P4IOEMU_P4IO_CMD_DALLAS_READ_MEM: %d", req->type);
|
||||
|
||||
@@ -152,13 +88,7 @@ static uint32_t p4ioemu_p4io_command_handle(
|
||||
return 32;
|
||||
}
|
||||
|
||||
case P4IOEMU_P4IO_CMD_UNKNOWN: {
|
||||
log_misc("P4IOEMU_P4IO_CMD_UNKNOWN: %d", payload_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case P4IOEMU_P4IO_CMD_SCI_UPDATE: {
|
||||
case P4IO_CMD_SCI_UPDATE: {
|
||||
// log_misc("P4IOEMU_P4IO_CMD_SCI_UPDATE");
|
||||
|
||||
// TODO we need a game which uses it
|
||||
@@ -167,7 +97,7 @@ static uint32_t p4ioemu_p4io_command_handle(
|
||||
return 0;
|
||||
}
|
||||
|
||||
case P4IOEMU_P4IO_CMD_RESET_WTD: {
|
||||
case P4IO_CMD_RESET_WTD: {
|
||||
// log_misc("P4IOEMU_P4IO_CMD_RESET_WTD");
|
||||
|
||||
return 0;
|
||||
@@ -188,20 +118,18 @@ static void p4ioemu_p4io_dump_buffer(const void *buffer, uint32_t len)
|
||||
|
||||
static HRESULT p4ioemu_p4io_bulk_read(void *resp, uint32_t nbytes)
|
||||
{
|
||||
struct p4ioemu_p4io_cmd_package *package;
|
||||
void *payload;
|
||||
struct p4io_cmd_package *package;
|
||||
uint32_t max_payload_len;
|
||||
uint32_t payload_len;
|
||||
|
||||
if (nbytes < sizeof(struct p4ioemu_p4io_cmd_package)) {
|
||||
log_warning("Buffer for bulk read endpoint to short: %d", nbytes);
|
||||
if (nbytes < sizeof(struct p4io_cmd_header)) {
|
||||
log_warning("Buffer for bulk read endpoint too short: %d", nbytes);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
package = (struct p4ioemu_p4io_cmd_package *) resp;
|
||||
payload = resp + sizeof(struct p4ioemu_p4io_cmd_package);
|
||||
max_payload_len = nbytes - sizeof(struct p4ioemu_p4io_cmd_package);
|
||||
package = (struct p4io_cmd_package *) resp;
|
||||
max_payload_len = nbytes - sizeof(struct p4io_cmd_header);
|
||||
|
||||
if (max_payload_len < p4ioemu_p4io_cmd_buffer_resp_len) {
|
||||
log_warning(
|
||||
@@ -213,46 +141,44 @@ static HRESULT p4ioemu_p4io_bulk_read(void *resp, uint32_t nbytes)
|
||||
payload_len = p4ioemu_p4io_cmd_buffer_resp_len;
|
||||
}
|
||||
|
||||
package->header_AA = 0xAA;
|
||||
package->cmd = p4ioemu_p4io_last_cmd;
|
||||
package->seq_num = p4ioemu_p4io_last_seq_num;
|
||||
package->payload_len = payload_len;
|
||||
package->header.sof = P4IO_SOF;
|
||||
package->header.cmd = p4ioemu_p4io_last_cmd;
|
||||
package->header.seq_num = p4ioemu_p4io_last_seq_num;
|
||||
package->header.payload_len = payload_len;
|
||||
|
||||
memcpy(payload, p4ioemu_p4io_cmd_read_buffer, payload_len);
|
||||
memcpy(package->payload, p4ioemu_p4io_cmd_read_buffer, payload_len);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT p4ioemu_p4io_bulk_write(const void *req, uint32_t nbytes)
|
||||
{
|
||||
const struct p4ioemu_p4io_cmd_package *package;
|
||||
const void *payload;
|
||||
const struct p4io_cmd_package *package;
|
||||
|
||||
if (nbytes < sizeof(struct p4ioemu_p4io_cmd_package)) {
|
||||
log_warning("Command on bulk write endpoint to short: %d", nbytes);
|
||||
if (nbytes < sizeof(struct p4io_cmd_header)) {
|
||||
log_warning("Command on bulk write endpoint too short: %d", nbytes);
|
||||
p4ioemu_p4io_dump_buffer(req, nbytes);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
package = (struct p4ioemu_p4io_cmd_package *) req;
|
||||
payload = req + sizeof(struct p4ioemu_p4io_cmd_package);
|
||||
package = (struct p4io_cmd_package *) req;
|
||||
|
||||
if (package->header_AA != 0xAA) {
|
||||
log_warning("Command on bulk endpoint to short: %d", nbytes);
|
||||
if (package->header.sof != P4IO_SOF) {
|
||||
log_warning("Command on bulk endpoint too short: %d", nbytes);
|
||||
p4ioemu_p4io_dump_buffer(req, nbytes);
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
p4ioemu_p4io_last_cmd = package->cmd;
|
||||
p4ioemu_p4io_last_seq_num = package->seq_num;
|
||||
p4ioemu_p4io_last_cmd = package->header.cmd;
|
||||
p4ioemu_p4io_last_seq_num = package->header.seq_num;
|
||||
|
||||
/* handle commands that are common p4io ones first */
|
||||
p4ioemu_p4io_cmd_buffer_resp_len = p4ioemu_p4io_command_handle(
|
||||
package->cmd,
|
||||
payload,
|
||||
package->payload_len,
|
||||
package->header.cmd,
|
||||
package->payload,
|
||||
package->header.payload_len,
|
||||
p4ioemu_p4io_cmd_read_buffer,
|
||||
sizeof(p4ioemu_p4io_cmd_read_buffer));
|
||||
|
||||
@@ -261,16 +187,16 @@ static HRESULT p4ioemu_p4io_bulk_write(const void *req, uint32_t nbytes)
|
||||
if (p4ioemu_device_msg_hook->command_handle) {
|
||||
p4ioemu_p4io_cmd_buffer_resp_len =
|
||||
p4ioemu_device_msg_hook->command_handle(
|
||||
package->cmd,
|
||||
payload,
|
||||
package->payload_len,
|
||||
package->header.cmd,
|
||||
package->payload,
|
||||
package->header.payload_len,
|
||||
p4ioemu_p4io_cmd_read_buffer,
|
||||
sizeof(p4ioemu_p4io_cmd_read_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
if (p4ioemu_p4io_cmd_buffer_resp_len == 0xFFFFFFFF) {
|
||||
log_warning("Unhandled cmd 0x%X", package->cmd);
|
||||
log_warning("Unhandled cmd 0x%X", package->header.cmd);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
@@ -413,7 +339,7 @@ static HRESULT p4ioemu_device_ioctl(struct irp *irp)
|
||||
|
||||
/* Cases are listed in order of first receipt */
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_P4IO_GET_DEVICE_NAME: {
|
||||
case P4IO_IOCTL_GET_DEVICE_NAME: {
|
||||
const char dev_name[] = "kactools p4ioemu";
|
||||
|
||||
if (irp->read.nbytes < strlen(dev_name)) {
|
||||
@@ -427,7 +353,7 @@ static HRESULT p4ioemu_device_ioctl(struct irp *irp)
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
case IOCTL_P4IO_READ_JAMMA_2: {
|
||||
case P4IO_IOCTL_READ_JAMMA_2: {
|
||||
p4ioemu_device_msg_hook->jamma2_read(
|
||||
irp->read.bytes, irp->read.nbytes);
|
||||
irp->read.pos = irp->read.nbytes;
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
#include "p4ioemu/setupapi.h"
|
||||
|
||||
#include "p4io/guid.h"
|
||||
|
||||
const struct hook_setupapi_data p4ioemu_setupapi_data = {
|
||||
.device_guid = {0x8B7250A5,
|
||||
0x4F61,
|
||||
0x46C9,
|
||||
{0x84, 0x3A, 0xE6, 0x68, 0x06, 0x47, 0x6A, 0x20}},
|
||||
.device_guid = p4io_guid,
|
||||
.device_desc = NULL,
|
||||
.device_path = "\\p4io",
|
||||
};
|
||||
};
|
||||
|
||||
@@ -48,6 +48,14 @@ static bool my_dll_entry_init(char *sidcode, struct property_node *config)
|
||||
|
||||
ok = eam_io_init(avs_thread_create, avs_thread_join, avs_thread_destroy);
|
||||
|
||||
/* Set up IO emulation hooks _after_ IO API setup to allow
|
||||
API implementations with real IO devices */
|
||||
iohook_push_handler(ac_io_bus_dispatch_irp);
|
||||
iohook_push_handler(lcd_dispatch_irp);
|
||||
|
||||
rs232_hook_init();
|
||||
lcd_init();
|
||||
|
||||
if (!ok) {
|
||||
goto eam_io_fail;
|
||||
}
|
||||
@@ -119,13 +127,7 @@ BOOL WINAPI DllMain(HMODULE self, DWORD reason, void *ctx)
|
||||
|
||||
app_hook_init(my_dll_entry_init, my_dll_entry_main);
|
||||
|
||||
iohook_push_handler(ac_io_bus_dispatch_irp);
|
||||
iohook_push_handler(lcd_dispatch_irp);
|
||||
|
||||
rs232_hook_init();
|
||||
|
||||
gfx_init();
|
||||
lcd_init();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user