Bemanitools v5.26 release

This commit is contained in:
icex2
2019-09-27 22:36:50 +02:00
commit cbd7720349
718 changed files with 62731 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
libs += acioemu
src_acioemu := \
addr.c \
emu.c \
h44b.c \
hdxs.c \
icca.c \
iccb.c \
pipe.c \
+39
View File
@@ -0,0 +1,39 @@
#include <stdint.h>
#include "acio/acio.h"
#include "acioemu/addr.h"
#include "acioemu/emu.h"
#include "util/log.h"
void ac_io_emu_cmd_assign_addrs(
struct ac_io_emu *emu,
const struct ac_io_message *req,
uint8_t node_count)
{
struct ac_io_message resp;
uint16_t cmd;
log_assert(emu != NULL);
log_assert(req != NULL);
cmd = ac_io_u16(req->cmd.code);
if (cmd != AC_IO_CMD_ASSIGN_ADDRS) {
log_warning(
"Address 0 expects address assignment cmd, got %04x",
cmd);
return;
}
memset(&resp, 0, sizeof(resp));
resp.addr = 0;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.count);
resp.cmd.count = node_count;
ac_io_emu_response_push(emu, &resp, 0);
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef AC_IO_EMU_ADDR_H
#define AC_IO_EMU_ADDR_H
#include "acio/acio.h"
#include "acioemu/emu.h"
#include <stdint.h>
void ac_io_emu_cmd_assign_addrs(
struct ac_io_emu *emu,
const struct ac_io_message *req,
uint8_t node_count);
#endif
+194
View File
@@ -0,0 +1,194 @@
#include <windows.h> /* Usermode API */
#include <ntdef.h> /* Kernel-mode API for ioctls */
#include <devioctl.h>
#include <ntddser.h>
#include <stdbool.h>
#include <stdint.h>
#include <wchar.h>
#include "acioemu/emu.h"
#include "acioemu/pipe.h"
#include "hook/iohook.h"
#include "util/log.h"
#include "util/str.h"
static HRESULT ac_io_emu_open(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_close(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_read(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_write(struct ac_io_emu *emu, struct irp *irp);
static HRESULT ac_io_emu_ioctl(struct ac_io_emu *emu, struct irp *irp);
void ac_io_emu_init(struct ac_io_emu *emu, const wchar_t *filename)
{
log_assert(emu != NULL);
log_assert(filename != NULL);
memset(emu, 0, sizeof(*emu));
emu->fd = iohook_open_dummy_fd();
emu->wfilename = wstr_dup(filename);
wstr_narrow(filename, &emu->filename);
ac_io_in_init(&emu->in);
ac_io_out_init(&emu->out);
}
void ac_io_emu_fini(struct ac_io_emu *emu)
{
log_assert(emu != NULL);
free(emu->filename);
free(emu->wfilename);
if (emu->fd != NULL) {
CloseHandle(emu->fd);
}
memset(emu, 0, sizeof(*emu));
}
bool ac_io_emu_match_irp(const struct ac_io_emu *emu, const struct irp *irp)
{
log_assert(emu != NULL);
log_assert(irp != NULL);
if (irp->op == IRP_OP_OPEN) {
return wstr_eq(emu->wfilename, irp->open_filename);
} else {
return irp->fd == emu->fd;
}
}
HRESULT ac_io_emu_dispatch_irp(struct ac_io_emu *emu, struct irp *irp)
{
log_assert(irp != NULL);
switch (irp->op) {
case IRP_OP_OPEN: return ac_io_emu_open(emu, irp);
case IRP_OP_CLOSE: return ac_io_emu_close(emu, irp);
case IRP_OP_READ: return ac_io_emu_read(emu, irp);
case IRP_OP_WRITE: return ac_io_emu_write(emu, irp);
case IRP_OP_IOCTL: return ac_io_emu_ioctl(emu, irp);
case IRP_OP_FSYNC: return S_FALSE;
default: return E_NOTIMPL;
}
}
static HRESULT ac_io_emu_open(struct ac_io_emu *emu, struct irp *irp)
{
irp->fd = emu->fd;
log_info("%s: ACIO port opened", emu->filename);
return S_FALSE;
}
static HRESULT ac_io_emu_close(struct ac_io_emu *emu, struct irp *irp)
{
log_info("%s: ACIO port closed", emu->filename);
return S_FALSE;
}
static HRESULT ac_io_emu_read(struct ac_io_emu *emu, struct irp *irp)
{
ac_io_in_drain(&emu->in, &irp->read);
return S_FALSE;
}
static HRESULT ac_io_emu_write(struct ac_io_emu *emu, struct irp *irp)
{
const struct ac_io_message *msg;
for (;;) {
ac_io_out_supply(&emu->out, &irp->write);
if (!ac_io_out_have_message(&emu->out)) {
break;
}
msg = ac_io_out_get_message(&emu->out);
if (msg != NULL) {
break;
}
ac_io_in_supply(&emu->in, NULL, 0);
ac_io_out_consume_message(&emu->out);
}
return ac_io_out_have_message(&emu->out) ? S_OK : S_FALSE;
}
static HRESULT ac_io_emu_ioctl(struct ac_io_emu *emu, struct irp *irp)
{
SERIAL_STATUS *status;
log_assert(irp != NULL);
switch (irp->ioctl) {
case IOCTL_SERIAL_GET_COMMSTATUS:
if (irp->read.bytes == NULL) {
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Output buffer is NULL");
return E_INVALIDARG;
}
if (irp->read.nbytes < sizeof(*status)) {
log_warning("IOCTL_SERIAL_GET_COMMSTATUS: Buffer is too small");
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
status = (SERIAL_STATUS *) irp->read.bytes;
status->Errors = 0;
status->AmountInInQueue = ac_io_in_is_msg_pending(&emu->in);
irp->read.pos = sizeof(*status);
return S_FALSE;
default:
return S_FALSE;
}
}
const struct ac_io_message *ac_io_emu_request_peek(const struct ac_io_emu *emu)
{
log_assert(emu != NULL);
return ac_io_out_get_message(&emu->out);
}
void ac_io_emu_request_pop(struct ac_io_emu *emu)
{
log_assert(emu != NULL);
ac_io_out_consume_message(&emu->out);
}
void ac_io_emu_response_push(
struct ac_io_emu *emu,
const struct ac_io_message *resp,
uint64_t delay_us)
{
log_assert(emu != NULL);
log_assert(resp != NULL);
ac_io_in_supply(&emu->in, resp, delay_us);
}
void ac_io_emu_response_push_thunk(
struct ac_io_emu *emu,
ac_io_in_thunk_t thunk,
void *ctx,
uint64_t delay_us)
{
log_assert(emu != NULL);
log_assert(thunk != NULL);
ac_io_in_supply_thunk(&emu->in, thunk, ctx, delay_us);
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef ACIOEMU_EMU_H
#define ACIOEMU_EMU_H
#include <windows.h>
#include <stdbool.h>
#include <wchar.h>
#include "acio/acio.h"
#include "acioemu/pipe.h"
#include "hook/iohook.h"
struct ac_io_emu {
HANDLE fd;
wchar_t *wfilename;
char *filename;
struct ac_io_in in;
struct ac_io_out out;
};
void ac_io_emu_init(struct ac_io_emu *emu, const wchar_t *filename);
void ac_io_emu_fini(struct ac_io_emu *emu);
bool ac_io_emu_match_irp(const struct ac_io_emu *emu, const struct irp *irp);
HRESULT ac_io_emu_dispatch_irp(struct ac_io_emu *emu, struct irp *irp);
const struct ac_io_message *ac_io_emu_request_peek(const struct ac_io_emu *emu);
void ac_io_emu_request_pop(struct ac_io_emu *emu);
void ac_io_emu_response_push(
struct ac_io_emu *emu,
const struct ac_io_message *resp,
uint64_t delay_ms);
void ac_io_emu_response_push_thunk(
struct ac_io_emu *emu,
ac_io_in_thunk_t thunk,
void *ctx,
uint64_t delay_ms);
#endif
+118
View File
@@ -0,0 +1,118 @@
#define LOG_MODULE "acioemu-h44b"
#include "acioemu/h44b.h"
#include <windows.h> /* for _BitScanForward */
#include <stdint.h>
#include <string.h>
#include "acio/h44b.h"
#include "acioemu/emu.h"
#include "bemanitools/jbio.h"
#include "util/hex.h"
static void ac_io_emu_h44b_cmd_send_version(
struct ac_io_emu_h44b *h44b,
const struct ac_io_message *req);
static void ac_io_emu_h44b_send_status(
struct ac_io_emu_h44b *h44b,
const struct ac_io_message *req,
uint8_t status);
void ac_io_emu_h44b_init(
struct ac_io_emu_h44b *h44b,
struct ac_io_emu *emu,
uint8_t unit_no)
{
memset(h44b, 0, sizeof(*h44b));
h44b->emu = emu;
h44b->unit_no = unit_no;
}
void ac_io_emu_h44b_dispatch_request(
struct ac_io_emu_h44b *h44b,
const struct ac_io_message *req)
{
uint16_t cmd_code;
cmd_code = ac_io_u16(req->cmd.code);
switch (cmd_code) {
case AC_IO_CMD_GET_VERSION:
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
ac_io_emu_h44b_cmd_send_version(h44b, req);
break;
case AC_IO_CMD_START_UP:
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
ac_io_emu_h44b_send_status(h44b, req, 0x00);
break;
case AC_IO_H44B_CMD_SET_OUTPUTS:
/* Not using the struct ac_io_h44b_output here */
for (int i = 0; i < 6; i++) {
jb_io_set_rgb_led((enum jb_io_rgb_led) i,
req->cmd.raw[i * 3],
req->cmd.raw[i * 3 + 1],
req->cmd.raw[i * 3 + 2]);
}
jb_io_write_outputs();
ac_io_emu_h44b_send_status(h44b, req, 0x00);
break;
default:
log_warning("Unknown ACIO message %04x on h44b node, addr=%d",
cmd_code, req->addr);
break;
}
}
static void ac_io_emu_h44b_cmd_send_version(
struct ac_io_emu_h44b *h44b,
const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.version);
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_H44B);
resp.cmd.version.flag = 0x00;
resp.cmd.version.major = 0x01;
resp.cmd.version.minor = 0x00;
resp.cmd.version.revision = 0x02;
memcpy(resp.cmd.version.product_code, "H44B",
sizeof(resp.cmd.version.product_code));
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
ac_io_emu_response_push(h44b->emu, &resp, 0);
}
static void ac_io_emu_h44b_send_status(
struct ac_io_emu_h44b *h44b,
const struct ac_io_message *req,
uint8_t status)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.status);
resp.cmd.status = status;
ac_io_emu_response_push(h44b->emu, &resp, 0);
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef AC_IO_EMU_H44B_H
#define AC_IO_EMU_H44B_H
#include <stdbool.h>
#include <stdint.h>
#include "acioemu/emu.h"
struct ac_io_emu_h44b {
struct ac_io_emu *emu;
uint8_t unit_no;
// TODO
};
void acioemu_h44b_init(void);
void ac_io_emu_h44b_init(
struct ac_io_emu_h44b *h44b,
struct ac_io_emu *emu,
uint8_t unit_no);
void ac_io_emu_h44b_dispatch_request(
struct ac_io_emu_h44b *h44b,
const struct ac_io_message *req);
#endif
+133
View File
@@ -0,0 +1,133 @@
#define LOG_MODULE "acioemu-hdxs"
#include "acio/acio.h"
#include "acioemu/emu.h"
#include "acioemu/hdxs.h"
#include "util/log.h"
static void ac_io_emu_hdxs_cmd_send_version(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req);
static void ac_io_emu_hdxs_send_empty(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req);
static void ac_io_emu_hdxs_send_status(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req,
uint8_t status);
void ac_io_emu_hdxs_init(
struct ac_io_emu_hdxs *hdxs,
struct ac_io_emu *emu)
{
log_assert(hdxs != NULL);
log_assert(emu != NULL);
hdxs->emu = emu;
}
void ac_io_emu_hdxs_dispatch_request(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req)
{
uint16_t cmd_code;
log_assert(hdxs != NULL);
log_assert(req != NULL);
cmd_code = ac_io_u16(req->cmd.code);
switch (cmd_code) {
case AC_IO_CMD_GET_VERSION:
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
ac_io_emu_hdxs_cmd_send_version(hdxs, req);
break;
case AC_IO_CMD_START_UP:
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
ac_io_emu_hdxs_send_status(hdxs, req, 0x00);
break;
case AC_IO_CMD_CLEAR:
log_misc("AC_IO_CMD_CLEAR(%d)", req->addr);
case 0x110:
case 0x112:
case 0x128:
ac_io_emu_hdxs_send_status(hdxs, req, 0x00);
break;
case AC_IO_CMD_KEEPALIVE:
ac_io_emu_hdxs_send_empty(hdxs, req);
break;
default:
log_warning(
"Unknown ACIO message %04x on HDXS node, addr=%d",
cmd_code,
req->addr);
break;
}
}
static void ac_io_emu_hdxs_cmd_send_version(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.version);
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_LED_STRIP);
resp.cmd.version.flag = 0x00;
resp.cmd.version.major = 0x01;
resp.cmd.version.minor = 0x06;
resp.cmd.version.revision = 0x00;
memcpy(resp.cmd.version.product_code, "HDXS",
sizeof(resp.cmd.version.product_code));
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
ac_io_emu_response_push(hdxs->emu, &resp, 0);
}
static void ac_io_emu_hdxs_send_empty(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = 0;
ac_io_emu_response_push(hdxs->emu, &resp, 0);
}
static void ac_io_emu_hdxs_send_status(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req,
uint8_t status)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.status);
resp.cmd.status = status;
ac_io_emu_response_push(hdxs->emu, &resp, 0);
}
+21
View File
@@ -0,0 +1,21 @@
#ifndef AC_IO_EMU_HDXS_H
#define AC_IO_EMU_HDXS_H
#include "acio/acio.h"
#include "acioemu/emu.h"
struct ac_io_emu_hdxs {
struct ac_io_emu *emu;
// TODO ops vtbl
};
void ac_io_emu_hdxs_init(
struct ac_io_emu_hdxs *hdxs,
struct ac_io_emu *emu);
void ac_io_emu_hdxs_dispatch_request(
struct ac_io_emu_hdxs *hdxs,
const struct ac_io_message *req);
#endif
+385
View File
@@ -0,0 +1,385 @@
#define LOG_MODULE "acioemu-icca"
#include "acioemu/icca.h"
#include <windows.h> /* for _BitScanForward */
#include <stdint.h>
#include <string.h>
#include "acio/icca.h"
#include "acioemu/emu.h"
#include "acioemu/icca.h"
#include "bemanitools/eamio.h"
#include "util/time.h"
enum ac_io_icca_subcmd {
AC_IO_ICCA_SUBCMD_CARD_SLOT_CLOSE = 0x00,
AC_IO_ICCA_SUBCMD_CARD_SLOT_OPEN = 0x11,
AC_IO_ICCA_SUBCMD_CARD_SLOT_EJECT = 0x12,
};
enum ac_io_icca_flag {
AC_IO_ICCA_FLAG_FRONT_SENSOR = 0x10,
AC_IO_ICCA_FLAG_REAR_SENSOR = 0x20,
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
};
static void ac_io_emu_icca_cmd_send_version(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req);
static void ac_io_emu_icca_send_state(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req,
uint64_t delay_us);
static void ac_io_emu_icca_send_empty(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req);
static void ac_io_emu_icca_send_status(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req,
uint8_t status);
void ac_io_emu_icca_init(
struct ac_io_emu_icca *icca,
struct ac_io_emu *emu,
uint8_t unit_no)
{
memset(icca, 0, sizeof(*icca));
icca->emu = emu;
icca->unit_no = unit_no;
// queue must be started
icca->fault = true;
}
void ac_io_emu_icca_dispatch_request(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req)
{
uint16_t cmd_code;
uint64_t delay_us;
cmd_code = ac_io_u16(req->cmd.code);
switch (cmd_code) {
case AC_IO_CMD_GET_VERSION:
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
ac_io_emu_icca_cmd_send_version(icca, req);
break;
case AC_IO_CMD_START_UP:
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
icca->detected_new_reader = false;
ac_io_emu_icca_send_status(icca, req, 0x00);
break;
case AC_IO_CMD_CLEAR:
log_misc("AC_IO_ICCA_CMD_CLEAR(%d)", req->addr);
ac_io_emu_icca_send_status(icca, req, 0x00);
break;
case AC_IO_CMD_KEEPALIVE:
ac_io_emu_icca_send_empty(icca, req);
break;
case AC_IO_ICCA_CMD_QUEUE_LOOP_START:
log_misc("AC_IO_CMD_QUEUE_LOOP_START(%d)", req->addr);
// queue started, reset error state
icca->fault = false;
ac_io_emu_icca_send_status(icca, req, 0x00);
icca->polling_started = true;
break;
case AC_IO_CMD_UNKN_00FF:
log_misc("AC_IO_CMD_UNKN_00FF(%d)", req->addr);
ac_io_emu_icca_send_status(icca, req, 0x00);
break;
case AC_IO_ICCA_CMD_UNKN_0120:
log_misc("AC_IO_ICCA_CMD_UNKN_0120(%d)", req->addr);
ac_io_emu_icca_send_status(icca, req, 0x00);
break;
case AC_IO_ICCA_CMD_BEGIN_KEYPAD:
log_misc("AC_IO_ICCA_CMD_BEGIN_KEYPAD(%d)", req->addr);
ac_io_emu_icca_send_status(icca, req, 0x00);
icca->keypad_started = true;
break;
case AC_IO_ICCA_CMD_ENGAGE:
ac_io_emu_icca_send_state(icca, req, 0);
break;
case AC_IO_ICCA_CMD_SET_SLOT_STATE:
{
struct ac_io_icca_misc* misc =
(struct ac_io_icca_misc*) &req->cmd.raw;
uint8_t cmd;
switch (misc->subcmd) {
case AC_IO_ICCA_SUBCMD_CARD_SLOT_CLOSE:
cmd = EAM_IO_CARD_SLOT_CMD_CLOSE;
break;
case AC_IO_ICCA_SUBCMD_CARD_SLOT_OPEN:
cmd = EAM_IO_CARD_SLOT_CMD_OPEN;
break;
case AC_IO_ICCA_SUBCMD_CARD_SLOT_EJECT:
cmd = EAM_IO_CARD_SLOT_CMD_EJECT;
icca->engaged = false;
break;
case 3:
cmd = EAM_IO_CARD_SLOT_CMD_READ;
break;
default:
cmd = 0xFF;
log_warning("Unhandled slot command %X, node %d",
misc->subcmd, icca->unit_no);
break;
}
if (cmd != 0xFF) {
if (!eam_io_card_slot_cmd(icca->unit_no, cmd)) {
log_warning("Eamio failed to handle slot cmd %d for node %d",
cmd, icca->unit_no);
}
}
/* response with current slot state */
ac_io_emu_icca_send_status(icca, req, misc->subcmd);
break;
}
case AC_IO_ICCA_CMD_POLL:
delay_us = time_get_elapsed_us(time_get_counter() - icca->time_counter_last_poll);
/* emulating delay implemented by hardware. do not delay messages that exceed a certain threshold. */
if (delay_us > 16000) {
delay_us = 0;
}
icca->time_counter_last_poll = time_get_counter();
ac_io_emu_icca_send_state(icca, req, delay_us);
break;
case AC_IO_ICCA_CMD_POLL_FELICA:
icca->detected_new_reader = true;
ac_io_emu_icca_send_status(icca, req, 0x01);
break;
default:
log_warning("Unknown ACIO message %04x on ICCA node, addr=%d",
cmd_code, req->addr);
break;
}
}
static void ac_io_emu_icca_cmd_send_version(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.version);
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_ICCA);
resp.cmd.version.flag = 0x00;
resp.cmd.version.major = 0x01;
resp.cmd.version.minor = 0x06;
resp.cmd.version.revision = 0x00;
memcpy(resp.cmd.version.product_code, "ICCA",
sizeof(resp.cmd.version.product_code));
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
ac_io_emu_response_push(icca->emu, &resp, 0);
}
static void ac_io_emu_icca_send_empty(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = 0;
ac_io_emu_response_push(icca->emu, &resp, 0);
}
static void ac_io_emu_icca_send_status(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req,
uint8_t status)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.status);
resp.cmd.status = status;
ac_io_emu_response_push(icca->emu, &resp, 0);
}
static void ac_io_emu_icca_send_state(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req,
uint64_t delay_us)
{
struct ac_io_message resp;
struct ac_io_icca_state *body;
unsigned long bit;
uint8_t event;
uint16_t keypad;
uint16_t keypad_rise;
uint8_t sensor_state;
bool card_full_insert;
if (!eam_io_poll(icca->unit_no)) {
log_warning("Polling eamio failed");
}
memset(&resp, 0, sizeof(resp));
keypad = eam_io_get_keypad_state(icca->unit_no);
sensor_state = eam_io_get_sensor_state(icca->unit_no);
keypad_rise = keypad & (icca->last_keypad ^ keypad);
card_full_insert = sensor_state & (1 << EAM_IO_SENSOR_FRONT) &&
sensor_state & (1 << EAM_IO_SENSOR_BACK);
if (sensor_state != icca->last_sensor) {
if (card_full_insert) {
if (!eam_io_card_slot_cmd(icca->unit_no,
EAM_IO_CARD_SLOT_CMD_READ)) {
log_warning("EAM_IO_CARD_SLOT_CMD_READ to unit %d failed",
icca->unit_no);
}
icca->card_result = eam_io_read_card(
icca->unit_no,
icca->uid,
sizeof(icca->uid));
// fault if sensor says to read but we got no card
icca->fault = (icca->card_result == EAM_IO_CARD_NONE);
} else {
icca->fault = false;
}
}
icca->last_sensor = sensor_state;
icca->last_keypad = keypad;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(struct ac_io_icca_state);
body = (struct ac_io_icca_state *) &resp.cmd.raw;
if (icca->fault) {
body->status_code = AC_IO_ICCA_STATUS_FAULT;
} else if (card_full_insert) {
body->status_code = AC_IO_ICCA_STATUS_GOT_UID;
} else {
body->status_code = AC_IO_ICCA_STATUS_IDLE;
}
body->sensor_state = 0;
if (sensor_state & (1 << EAM_IO_SENSOR_FRONT)) {
body->sensor_state |= AC_IO_ICCA_FLAG_FRONT_SENSOR;
}
if (sensor_state & (1 << EAM_IO_SENSOR_BACK)) {
body->sensor_state |= AC_IO_ICCA_FLAG_REAR_SENSOR;
}
if (icca->engaged) {
body->sensor_state |= AC_IO_ICCA_FLAG_SOLENOID;
}
memcpy(body->uid, icca->uid, sizeof(body->uid));
body->card_type = 0;
if (body->status_code == AC_IO_ICCA_STATUS_GOT_UID){
if (icca->detected_new_reader){
// sensor_state actually refers to cardtype for wavepass readers
// EAM_IO_CARD_ISO15696 = 1 -> 0
// EAM_IO_CARD_FELICA = 2 -> 1
body->card_type = icca->card_result - 1;
body->sensor_state = icca->card_result - 1;
}
}
if (keypad_rise) {
if (icca->key_events[0]) {
event = (icca->key_events[0] + 0x10) & 0xF0;
} else {
event = 0x00;
}
_BitScanForward(&bit, keypad_rise);
event |= 0x80 | bit;
icca->key_events[1] = icca->key_events[0];
icca->key_events[0] = event;
}
// this doesn't seem to be an error code. If this is not set to 0x03
// on slotted readers (only?), the game throws an unknown status error
if (icca->keypad_started) {
body->keypad_started = 0x03;
} else {
body->keypad_started = 0x00;
}
body->key_events[0] = icca->key_events[0];
body->key_events[1] = icca->key_events[1];
body->key_state = ac_io_u16(keypad);
// replace status code if polling hasn't started
// this fixes SDVX IC CARD boot errors
if (!icca->polling_started) {
body->status_code = AC_IO_ICCA_STATUS_FAULT;
}
ac_io_emu_response_push(icca->emu, &resp, delay_us);
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef AC_IO_EMU_ICCA_H
#define AC_IO_EMU_ICCA_H
#include <stdbool.h>
#include <stdint.h>
#include "acioemu/emu.h"
struct ac_io_emu_icca {
struct ac_io_emu *emu;
uint8_t unit_no;
bool fault;
bool engaged;
uint8_t last_sensor;
uint16_t last_keypad;
uint8_t key_events[2];
uint8_t uid[8];
uint8_t card_result;
bool detected_new_reader;
bool keypad_started;
bool polling_started;
uint64_t time_counter_last_poll;
};
void ac_io_emu_icca_init(
struct ac_io_emu_icca *icca,
struct ac_io_emu *emu,
uint8_t unit_no);
void ac_io_emu_icca_dispatch_request(
struct ac_io_emu_icca *icca,
const struct ac_io_message *req);
#endif
+227
View File
@@ -0,0 +1,227 @@
#define LOG_MODULE "acioemu-iccb"
#include "acioemu/iccb.h"
#include <windows.h> /* for _BitScanForward */
#include <stdint.h>
#include <string.h>
#include "acio/iccb.h"
#include "acioemu/emu.h"
#include "bemanitools/eamio.h"
static void ac_io_emu_iccb_cmd_send_version(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req);
static void ac_io_emu_iccb_send_state(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req);
static void ac_io_emu_iccb_send_empty(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req);
static void ac_io_emu_iccb_send_status(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req,
uint8_t status);
void ac_io_emu_iccb_init(
struct ac_io_emu_iccb *iccb,
struct ac_io_emu *emu,
uint8_t unit_no)
{
memset(iccb, 0, sizeof(*iccb));
iccb->emu = emu;
iccb->unit_no = unit_no;
}
void ac_io_emu_iccb_dispatch_request(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req)
{
uint16_t cmd_code;
cmd_code = ac_io_u16(req->cmd.code);
switch (cmd_code) {
case AC_IO_CMD_GET_VERSION:
log_misc("AC_IO_CMD_GET_VERSION(%d)", req->addr);
ac_io_emu_iccb_cmd_send_version(iccb, req);
break;
case AC_IO_CMD_START_UP:
log_misc("AC_IO_CMD_START_UP(%d)", req->addr);
ac_io_emu_iccb_send_status(iccb, req, 0x00);
break;
case AC_IO_CMD_KEEPALIVE:
ac_io_emu_iccb_send_empty(iccb, req);
break;
case AC_IO_ICCB_CMD_QUEUE_LOOP_START:
log_misc("AC_IO_CMD_QUEUE_LOOP_START(%d)", req->addr);
ac_io_emu_iccb_send_status(iccb, req, 0x00);
break;
case AC_IO_ICCB_CMD_UNK_0100:
case AC_IO_ICCB_CMD_UNK_0116:
case AC_IO_ICCB_CMD_UNK_0120:
log_misc("AC_IO_ICCB_CMD_UNK_%04X(%d)", cmd_code, req->addr);
ac_io_emu_iccb_send_status(iccb, req, 0x00);
break;
case AC_IO_ICCB_CMD_SLEEP:
ac_io_emu_iccb_send_status(iccb, req, 0x00);
break;
case AC_IO_ICCB_CMD_UNK_135:
/* log_misc("AC_IO_ICCB_CMD_UNK_135"); */
ac_io_emu_iccb_send_state(iccb, req);
break;
case AC_IO_ICCB_CMD_POLL:
/* log_misc("AC_IO_ICCB_CMD_POLL"); */
ac_io_emu_iccb_send_state(iccb, req);
break;
case AC_IO_ICCB_CMD_READ_CARD:
/* log_misc("AC_IO_ICCB_CMD_READ_CARD"); */
ac_io_emu_iccb_send_state(iccb, req);
break;
default:
log_warning("Unknown ACIO message %04x on ICCB node, addr=%d",
cmd_code, req->addr);
break;
}
}
static void ac_io_emu_iccb_cmd_send_version(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.version);
resp.cmd.version.type = ac_io_u32(AC_IO_NODE_TYPE_ICCB);
resp.cmd.version.flag = 0x00;
resp.cmd.version.major = 0x01;
resp.cmd.version.minor = 0x05;
resp.cmd.version.revision = 0x01;
memcpy(resp.cmd.version.product_code, "ICCB",
sizeof(resp.cmd.version.product_code));
strncpy(resp.cmd.version.date, __DATE__, sizeof(resp.cmd.version.date));
strncpy(resp.cmd.version.time, __TIME__, sizeof(resp.cmd.version.time));
ac_io_emu_response_push(iccb->emu, &resp, 0);
}
static void ac_io_emu_iccb_send_empty(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = 0;
ac_io_emu_response_push(iccb->emu, &resp, 0);
}
static void ac_io_emu_iccb_send_status(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req,
uint8_t status)
{
struct ac_io_message resp;
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(resp.cmd.status);
resp.cmd.status = status;
ac_io_emu_response_push(iccb->emu, &resp, 0);
}
static void ac_io_emu_iccb_send_state(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req)
{
struct ac_io_message resp;
struct ac_io_iccb_state *body;
bool sensor;
/* state update */
sensor = eam_io_get_sensor_state(iccb->unit_no);
if (sensor != iccb->last_sensor) {
if (sensor) {
iccb->card_result = eam_io_read_card(
iccb->unit_no,
iccb->uid,
sizeof(iccb->uid));
// fault if sensor says to read but we got no card
iccb->fault = (iccb->card_result == EAM_IO_CARD_NONE);
} else {
iccb->fault = false;
}
}
iccb->last_sensor = sensor;
if (iccb->fault) {
memset(iccb->uid, 0, sizeof(iccb->uid));
}
/* create response */
resp.addr = req->addr | AC_IO_RESPONSE_FLAG;
resp.cmd.code = req->cmd.code;
resp.cmd.seq_no = req->cmd.seq_no;
resp.cmd.nbytes = sizeof(struct ac_io_iccb_state);
body = (struct ac_io_iccb_state *) &resp.cmd.raw;
if (sensor) {
body->sensor_state = AC_IO_ICCB_SENSOR_CARD;
} else {
body->sensor_state = AC_IO_ICCB_SENSOR_NO_CARD;
}
if (!iccb->fault) {
memcpy(body->uid, iccb->uid, sizeof(body->uid));
}
body->card_type = 0x30 | (iccb->card_result - 1);
body->unk2 = 0;
// this doesn't seem to be an error code. If this is not set to 0x03
// on slotted readers (only?), the game throws an unknown status error
body->unk3 = 0x03;
memset(body->unk4, 0, sizeof(body->unk4));
ac_io_emu_response_push(iccb->emu, &resp, 0);
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef AC_IO_EMU_ICCB_H
#define AC_IO_EMU_ICCB_H
#include <stdbool.h>
#include <stdint.h>
#include "acioemu/emu.h"
struct ac_io_emu_iccb {
struct ac_io_emu *emu;
uint8_t unit_no;
bool fault;
bool last_sensor;
uint8_t uid[8];
uint8_t card_result;
};
void ac_io_emu_iccb_init(
struct ac_io_emu_iccb *iccb,
struct ac_io_emu *emu,
uint8_t unit_no);
void ac_io_emu_iccb_dispatch_request(
struct ac_io_emu_iccb *iccb,
const struct ac_io_message *req);
#endif
+366
View File
@@ -0,0 +1,366 @@
#define LOG_MODULE "acioemu-emu"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "acioemu/pipe.h"
#include "util/defs.h"
#include "util/list.h"
#include "util/mem.h"
#include "util/time.h"
static struct ac_io_in_queued *ac_io_in_queued_alloc(struct ac_io_in *in,
uint64_t delay_us);
static void ac_io_in_queued_populate(
struct ac_io_in_queued *iq,
const struct ac_io_message *msg);
static void ac_io_in_queued_putc(struct ac_io_in_queued *iq, uint8_t b);
static bool ac_io_out_supply_byte(struct ac_io_out *out, uint8_t b);
static bool ac_io_out_supply_frame_byte(struct ac_io_out *out, uint8_t b);
static bool ac_io_out_detect_broadcast_eof(struct ac_io_out *out);
static bool ac_io_out_detect_command_eof(struct ac_io_out *out);
static bool ac_io_out_check_sum(struct ac_io_out *out);
static bool ac_io_out_accept_message(struct ac_io_out *out);
static bool ac_io_out_reject_message(struct ac_io_out *out);
static bool ac_io_enable_legacy_mode = false;
static struct ac_io_in_queued *ac_io_in_queued_alloc(struct ac_io_in *in,
uint64_t delay_us)
{
struct ac_io_in_queued *iq;
/* TODO ordered insert by scheduled_time */
iq = xmalloc(sizeof(*iq));
iq->iobuf.bytes = iq->bytes;
iq->iobuf.nbytes = 1;
iq->iobuf.pos = 0;
iq->scheduled_time = time_get_counter();
iq->delay_us = delay_us;
iq->bytes[0] = AC_IO_SOF;
list_append(&in->queue, &iq->node);
return iq;
}
static void ac_io_in_queued_populate(
struct ac_io_in_queued *iq,
const struct ac_io_message *msg)
{
uint8_t checksum;
const uint8_t *src;
size_t nbytes;
size_t i;
if (msg->addr == AC_IO_BROADCAST) {
nbytes = offsetof(struct ac_io_message, bcast.raw) + msg->bcast.nbytes;
} else {
nbytes = offsetof(struct ac_io_message, cmd.raw) + msg->cmd.nbytes;
}
src = (const uint8_t *) msg;
checksum = 0;
for (i = 0 ; i < nbytes ; i++) {
ac_io_in_queued_putc(iq, src[i]);
checksum += src[i];
}
ac_io_in_queued_putc(iq, checksum);
}
static void ac_io_in_queued_putc(struct ac_io_in_queued *iq, uint8_t b)
{
if (b == AC_IO_SOF || b == AC_IO_ESCAPE) {
iq->bytes[iq->iobuf.nbytes++] = AC_IO_ESCAPE;
iq->bytes[iq->iobuf.nbytes++] = ~b;
} else {
iq->bytes[iq->iobuf.nbytes++] = b;
}
}
void ac_io_legacy_mode(void)
{
log_info("Running acioemu legacy mode");
ac_io_enable_legacy_mode = true;
}
void ac_io_in_init(struct ac_io_in *in)
{
list_init(&in->queue);
}
void ac_io_in_supply(struct ac_io_in *in, const struct ac_io_message *msg,
uint64_t delay_us)
{
struct ac_io_in_queued *dest;
dest = ac_io_in_queued_alloc(in, delay_us);
dest->thunk = NULL;
if (msg == NULL) {
return;
}
ac_io_in_queued_populate(dest, msg);
}
void ac_io_in_supply_thunk(
struct ac_io_in *in,
ac_io_in_thunk_t thunk,
void *ctx,
uint64_t delay_us)
{
struct ac_io_in_queued *dest;
dest = ac_io_in_queued_alloc(in, delay_us);
dest->thunk = thunk;
dest->thunk_ctx = ctx;
}
void ac_io_in_drain(struct ac_io_in *in, struct iobuf *dest)
{
struct ac_io_in_queued *iq;
struct list_node *node;
struct ac_io_message msg;
size_t nmoved;
uint64_t now;
uint64_t elapsed_us;
now = time_get_counter();
do {
node = list_peek_head(&in->queue);
if (node == NULL) {
break;
}
iq = containerof(node, struct ac_io_in_queued, node);
elapsed_us = time_get_elapsed_us(
now > iq->scheduled_time ? now - iq->scheduled_time : 0);
if (elapsed_us < iq->delay_us) {
break;
}
if (iq->thunk != NULL) {
iq->thunk(iq->thunk_ctx, &msg);
iq->thunk = NULL;
ac_io_in_queued_populate(iq, &msg);
}
nmoved = iobuf_move(dest, &iq->iobuf);
if (iq->iobuf.pos == iq->iobuf.nbytes) {
list_pop_head(&in->queue);
free(iq);
}
/* Legacy mode for old libacio versions like DistorteD:
spit out single responses instead of combining them into a single
io buffer. Some old libacio versions expect separate messages and
will error (code 0x00000002) on multiple messages in a single buffer
because they just read the first message and drop the remaining ones.
If using legacy mode on newer libacio versions (e.g. Copula),
the game will error with the same error code.
*/
if (ac_io_enable_legacy_mode) {
break;
}
} while (nmoved > 0);
}
bool ac_io_in_is_msg_pending(const struct ac_io_in *in)
{
return list_peek_head_const(&in->queue) != NULL;
}
void ac_io_out_init(struct ac_io_out *out)
{
out->pos = 0;
out->in_frame = false;
out->escape = false;
out->have_message = false;
}
void ac_io_out_supply(struct ac_io_out *out, struct const_iobuf *src)
{
log_assert(!ac_io_out_have_message(out));
while (src->pos < src->nbytes) {
if (!ac_io_out_supply_byte(out, src->bytes[src->pos++])) {
break;
}
}
}
static bool ac_io_out_supply_byte(struct ac_io_out *out, uint8_t b)
{
if (out->in_frame) {
return ac_io_out_supply_frame_byte(out, b);
} else {
if (b == AC_IO_SOF) {
out->in_frame = true;
} else if (b == AC_IO_ESCAPE) {
log_warning("Framing error: Escape byte outside frame");
}
return true;
}
}
static bool ac_io_out_supply_frame_byte(struct ac_io_out *out, uint8_t b)
{
if (out->escape) {
out->escape = false;
if (b == AC_IO_SOF || b == AC_IO_ESCAPE) {
log_warning("Framing error: Control byte after escape byte");
}
b = ~b;
} else if (b == AC_IO_ESCAPE) {
out->escape = true;
return true;
} else if (b == AC_IO_SOF) {
if (out->pos == 0) {
/* Got autobaud/empty message */
out->have_message = true;
return false;
} else {
log_warning("Truncated message");
out->pos = 0;
return true;
}
}
/* Payload byte */
out->bytes[out->pos++] = b;
/* Handle contextually-implied end-of-packet events */
if (out->pos > offsetof(struct ac_io_message, addr)) {
if (out->msg.addr == AC_IO_BROADCAST) {
return ac_io_out_detect_broadcast_eof(out);
} else {
return ac_io_out_detect_command_eof(out);
}
}
return true;
}
static bool ac_io_out_detect_broadcast_eof(struct ac_io_out *out)
{
size_t end;
if (out->pos > offsetof(struct ac_io_message, bcast.nbytes)) {
end = offsetof(struct ac_io_message,bcast.raw)
+ out->msg.bcast.nbytes
+ 1;
if (out->pos == end) {
return ac_io_out_check_sum(out);
}
}
return true;
}
static bool ac_io_out_detect_command_eof(struct ac_io_out *out)
{
size_t end;
if (out->pos > offsetof(struct ac_io_message, cmd.nbytes)) {
end = offsetof(struct ac_io_message, cmd.raw)
+ out->msg.cmd.nbytes
+ 1;
if (out->pos == end) {
return ac_io_out_check_sum(out);
}
}
return true;
}
static bool ac_io_out_check_sum(struct ac_io_out *out)
{
uint8_t checksum;
size_t i;
checksum = 0;
for (i = 0 ; i < out->pos - 1 ; i++) {
checksum += out->bytes[i];
}
if (checksum == out->bytes[out->pos - 1]) {
return ac_io_out_accept_message(out);
} else {
log_warning("Checksum bad: expected %02x got %02x",
checksum, out->bytes[out->pos - 1]);
return ac_io_out_reject_message(out);
}
}
static bool ac_io_out_accept_message(struct ac_io_out *out)
{
out->have_message = true;
return false;
}
static bool ac_io_out_reject_message(struct ac_io_out *out)
{
ac_io_out_consume_message(out);
return true;
}
bool ac_io_out_have_message(const struct ac_io_out *out)
{
return out->have_message;
}
const struct ac_io_message *ac_io_out_get_message(const struct ac_io_out *out)
{
if (out->pos == 0) {
/* Autobaud byte/empty frame */
return NULL;
} else {
return &out->msg;
}
}
void ac_io_out_consume_message(struct ac_io_out *out)
{
if (out->pos != 0) {
out->in_frame = false;
}
out->pos = 0;
out->have_message = false;
out->escape = false;
}
+66
View File
@@ -0,0 +1,66 @@
#ifndef ACIOEMU_PIPE_H
#define ACIOEMU_PIPE_H
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "acio/acio.h"
#include "util/iobuf.h"
#include "util/list.h"
#include "util/log.h"
/* This uses the USB convention where OUT and IN are from the host's (game's)
perspective. So an OUT transaction comes in to us and vice versa.
(I made those terms up, not Konami). */
typedef void (*ac_io_in_thunk_t)(void *ctx, struct ac_io_message *msg);
struct ac_io_in_queued {
struct list_node node;
struct const_iobuf iobuf;
uint64_t scheduled_time;
uint64_t delay_us;
ac_io_in_thunk_t thunk;
void *thunk_ctx;
uint8_t bytes[sizeof(struct ac_io_message)];
};
struct ac_io_in {
struct list queue;
};
struct ac_io_out {
union {
uint8_t bytes[sizeof(struct ac_io_message)];
struct ac_io_message msg;
};
size_t pos;
bool in_frame;
bool escape;
bool have_message;
};
void ac_io_legacy_mode(void);
void ac_io_in_init(struct ac_io_in *in);
void ac_io_in_supply(struct ac_io_in *in, const struct ac_io_message *msg,
uint64_t delay);
void ac_io_in_supply_thunk(
struct ac_io_in *in,
ac_io_in_thunk_t thunk,
void *ctx,
uint64_t delay);
void ac_io_in_drain(struct ac_io_in *in, struct iobuf *dest);
bool ac_io_in_is_msg_pending(const struct ac_io_in *in);
void ac_io_out_init(struct ac_io_out *out);
void ac_io_out_supply(struct ac_io_out *out, struct const_iobuf *src);
bool ac_io_out_have_message(const struct ac_io_out *out);
const struct ac_io_message *ac_io_out_get_message(const struct ac_io_out *out);
void ac_io_out_consume_message(struct ac_io_out *out);
#endif