Files
djhackersdev_bemanitools/src/main/p3iodrv/ddr.c
T
icex2 c22ec4fcff feat(p3iodrv): Add DDR compatible P3IO driver
Initial version that supports the most important
features to operate all inputs and outputs of a
DDR SD cabinet.
2023-06-25 13:24:07 +02:00

326 lines
7.2 KiB
C

#define LOG_MODULE "p3iodrv-ddr"
#include "p3io/cmd.h"
#include "util/log.h"
#include "ddr.h"
#include "device.h"
static uint8_t p3iodrv_ddr_seq_counter;
static uint8_t p3iodrv_ddr_get_and_update_seq_counter()
{
return p3iodrv_ddr_seq_counter++ & 0xF;
}
static HRESULT p3iodrv_ddr_check_resp(
const union p3io_resp_any *resp,
uint8_t expected_size,
const union p3io_req_any *corresponding_req)
{
uint8_t actual_size;
log_assert(resp);
actual_size = p3io_get_full_resp_size(resp);
if (actual_size != expected_size) {
log_warning(
"Incorrect response size, actual %d != expected %d",
actual_size,
expected_size);
return HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
}
if (resp->hdr.seq_no != corresponding_req->hdr.seq_no) {
log_warning(
"Incorrect sequence num in response, actual %d != expected %d",
resp->hdr.seq_no,
corresponding_req->hdr.seq_no);
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
}
if (resp->hdr.cmd != corresponding_req->hdr.cmd) {
log_warning(
"Incorrect command in response, actual 0x%d != expected 0x%d",
resp->hdr.cmd,
corresponding_req->hdr.cmd);
return HRESULT_FROM_WIN32(ERROR_BAD_COMMAND);
}
return S_OK;
}
HRESULT p3iodrv_ddr_init(HANDLE handle)
{
HRESULT hr;
uint8_t seq_cnt;
union p3io_req_any req;
union p3io_resp_any resp;
log_assert(handle != INVALID_HANDLE_VALUE);
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
p3io_req_hdr_init(&req.hdr, seq_cnt, P3IO_CMD_INIT, sizeof(req.init));
hr = p3iodrv_device_transfer(handle, &req, &resp);
if (FAILED(hr)) {
return hr;
}
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.init), &req);
if (FAILED(hr)) {
return hr;
}
if (resp.init.status != 0) {
log_warning("Initialization failed");
return HRESULT_FROM_WIN32(ERROR_GEN_FAILURE);
}
return S_OK;
}
HRESULT p3iodrv_ddr_get_version(
HANDLE handle,
char str[4],
uint32_t *major,
uint32_t *minor,
uint32_t *patch)
{
HRESULT hr;
uint8_t seq_cnt;
union p3io_req_any req;
union p3io_resp_any resp;
log_assert(handle != INVALID_HANDLE_VALUE);
log_assert(major);
log_assert(minor);
log_assert(patch);
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
p3io_req_hdr_init(
&req.hdr, seq_cnt, P3IO_CMD_GET_VERSION, sizeof(req.version));
hr = p3iodrv_device_transfer(handle, &req, &resp);
if (FAILED(hr)) {
return hr;
}
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.version), &req);
if (FAILED(hr)) {
return hr;
}
memcpy(str, resp.version.str, sizeof(char[4]));
*major = resp.version.major;
*minor = resp.version.minor;
*patch = resp.version.patch;
return S_OK;
}
HRESULT p3iodrv_ddr_set_watchdog(HANDLE handle, bool enable)
{
HRESULT hr;
uint8_t seq_cnt;
union p3io_req_any req;
union p3io_resp_any resp;
log_assert(handle != INVALID_HANDLE_VALUE);
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
p3io_req_hdr_init(
&req.hdr, seq_cnt, P3IO_CMD_SET_WATCHDOG, sizeof(req.watchdog));
req.watchdog.enable = enable ? 1 : 0;
hr = p3iodrv_device_transfer(handle, &req, &resp);
if (FAILED(hr)) {
return hr;
}
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.watchdog), &req);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
HRESULT p3iodrv_ddr_get_dipsw(HANDLE handle, uint8_t *state)
{
HRESULT hr;
uint8_t seq_cnt;
union p3io_req_any req;
union p3io_resp_any resp;
log_assert(handle != INVALID_HANDLE_VALUE);
log_assert(state);
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
p3io_req_hdr_init(
&req.hdr,
seq_cnt,
P3IO_CMD_GET_CAB_TYPE_OR_DIPSW,
sizeof(req.cab_type_or_dipsw));
req.cab_type_or_dipsw.cab_type_or_dipsw = P3IO_DIP_SW_SELECTOR;
hr = p3iodrv_device_transfer(handle, &req, &resp);
if (FAILED(hr)) {
return hr;
}
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.cab_type_or_dipsw), &req);
if (FAILED(hr)) {
return hr;
}
*state = resp.cab_type_or_dipsw.state;
return S_OK;
}
HRESULT p3iodrv_ddr_get_cab_type(HANDLE handle, enum p3io_cab_type *type)
{
HRESULT hr;
uint8_t seq_cnt;
union p3io_req_any req;
union p3io_resp_any resp;
log_assert(handle != INVALID_HANDLE_VALUE);
log_assert(type);
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
p3io_req_hdr_init(
&req.hdr,
seq_cnt,
P3IO_CMD_GET_CAB_TYPE_OR_DIPSW,
sizeof(req.cab_type_or_dipsw));
req.cab_type_or_dipsw.cab_type_or_dipsw = P3IO_CAB_TYPE_SELECTOR;
hr = p3iodrv_device_transfer(handle, &req, &resp);
if (FAILED(hr)) {
return hr;
}
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.cab_type_or_dipsw), &req);
if (FAILED(hr)) {
return hr;
}
*type = resp.cab_type_or_dipsw.state;
return S_OK;
}
HRESULT p3iodrv_ddr_get_video_freq(HANDLE handle, enum p3io_video_freq *freq)
{
HRESULT hr;
uint8_t seq_cnt;
union p3io_req_any req;
union p3io_resp_any resp;
log_assert(handle != INVALID_HANDLE_VALUE);
log_assert(freq);
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
p3io_req_hdr_init(
&req.hdr, seq_cnt, P3IO_CMD_GET_VIDEO_FREQ, sizeof(req.video_freq));
// Must be set to 5 in order to return the right values. There might be more
// to this, e.g. this being some kind of selector where to read from the
// JAMMA harness (?) but this is good for now to get what we want to know
// for DDR
req.video_freq.unknown_05 = 5;
hr = p3iodrv_device_transfer(handle, &req, &resp);
if (FAILED(hr)) {
return hr;
}
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.video_freq), &req);
if (FAILED(hr)) {
return hr;
}
*freq = (enum p3io_video_freq) resp.video_freq.video_freq;
return S_OK;
}
HRESULT p3iodrv_ddr_get_jamma(HANDLE handle, struct p3io_ddr_jamma *jamma)
{
HRESULT hr;
uint32_t *jamma_raw;
jamma_raw = (uint32_t *) jamma;
hr = p3iodrv_device_read_jamma(handle, jamma_raw);
if (FAILED(hr)) {
return hr;
}
// Inputs are active low for p1, p2 and operator bits
jamma_raw[0] ^= 0xFFFFFF00;
return S_OK;
}
HRESULT
p3iodrv_ddr_set_outputs(HANDLE handle, const struct p3io_ddr_output *state)
{
HRESULT hr;
uint8_t seq_cnt;
union p3io_req_any req;
union p3io_resp_any resp;
log_assert(handle != INVALID_HANDLE_VALUE);
log_assert(state);
seq_cnt = p3iodrv_ddr_get_and_update_seq_counter();
p3io_req_hdr_init(
&req.hdr, seq_cnt, P3IO_CMD_SET_OUTPUTS, sizeof(req.set_outputs));
memcpy(&req.set_outputs.outputs, state, sizeof(req.set_outputs.outputs));
// Always set by the game like this
req.set_outputs.unk_FF = 0xFF;
hr = p3iodrv_device_transfer(handle, &req, &resp);
if (FAILED(hr)) {
return hr;
}
hr = p3iodrv_ddr_check_resp(&resp, sizeof(resp.set_outputs), &req);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}