Files
djhackersdev_bemanitools/src/main/ezusb-iidx-emu/node-serial.c
T
icex2 4a672de960 fix(iidx/ezusb): Improve code resiliance of ezusb node-serial module
- Use static assert to verify struct sizes
- Replace "magic numbers" with proper sizeof's
- Allocate largest size buffer for message response to address
  compiler warnings which indicates potential out of bounds
  reads/writes
2023-04-01 20:54:18 +02:00

1352 lines
52 KiB
C

#define LOG_MODULE "ezusb-iidx-emu-node-serial"
#include <windows.h>
#include "bemanitools/eamio.h"
#include "ezusb-iidx-emu/card-mag.c"
#include "ezusb-iidx-emu/node-serial.h"
#include "ezusb-iidx/serial-cmd.h"
#include "security/mcode.h"
#include "util/hex.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/thread.h"
#define CARD_ID_LEN 8
enum ezusb_iidx_emu_node_serial_serial_msg_cmd {
CMD_H8_REQ = 0xAA,
CMD_H8_RESP = 0xA5,
CMD_NODE_REQ = 0x00,
CMD_NODE_RESP = 0x01
};
enum ezusb_iidx_emu_node_serial_h8_cmd {
H8_CMD_NODE_ENUM = 0x01,
H8_CMD_GET_VERSION = 0x02,
H8_CMD_PROG_EXEC = 0x03,
};
enum ezusb_iidx_emu_node_serial_node_cmd {
NODE_CMD_CARD_INIT = 0x00,
NODE_CMD_KEYBOARD_INIT = 0x10,
NODE_CMD_CARD_RW_UNIT_GET_STATUS = 0x12,
NODE_CMD_CARD_SLOT_SET_STATE = 0x14,
NODE_CMD_CARD_WRITE = 0x16,
NODE_CMD_CARD_READ = 0x18,
/* Not sure what this does. Only used in combination
with writing the card on the format call */
NODE_CMD_CARD_FORMAT_COMPLETE = 0x1E,
NODE_CMD_CARD_GET_STATUS = 0x20,
NODE_CMD_KEYBOARD_GET_STATUS = 0x24,
NODE_CMD_KEYBOARD_READ_DATA = 0x26,
NODE_CMD_KEYBOARD_GET_BUFFER_SIZE = 0x27
};
enum ezusb_iidx_emu_node_serial_card_slot_state {
/* Closes/locks the card slot if a card is inserted to avoid pulling out
If no card is inserted, close means that no cards are accepted
you have to call open first to accept a card */
CARD_SLOT_STATE_CLOSE = 0,
/* Open the card slot and allow a new card to be inserted and accepted */
CARD_SLOT_STATE_OPEN = 1,
/* Eject an inserted card */
CARD_SLOT_STATE_EJECT = 2,
/* Trigger card formating and write some data to it */
CARD_SLOT_STATE_FORMAT = 3,
/* Request reading data from the card. */
CARD_SLOT_STATE_READ = 4,
/* Request writing data to the card */
CARD_SLOT_STATE_WRITE = 5,
};
#define NODE_SERIAL_MSG_HEADER_SIZE 4
#define MAG_CARD_DATA_SIZE 128
#pragma pack(push, 1)
struct ezusb_iidx_emu_node_serial_msg {
/* Header part of message */
uint8_t msg_cmd;
uint8_t node_id;
uint8_t node_cmd;
uint8_t payload_len;
union payload {
struct ezusb_iidx_emu_node_serial_node_enum_req {
uint8_t node_id_to_assign;
} node_enum_req;
struct ezusb_iidx_emu_node_serial_node_enum_resp {
uint8_t total_nodes;
} node_enum_resp;
struct ezusb_iidx_emu_node_serial_get_version_resp {
uint32_t type;
uint8_t dup;
uint8_t version_maj;
uint8_t version_min;
uint8_t version_rev;
char comment[5];
} get_version_resp;
struct ezusb_iidx_emu_node_serial_keyboard_get_buffer_size_resp {
uint16_t buffer_size_type;
} keyboard_get_buffer_size_resp;
struct ezusb_iidx_emu_node_serial_keyboard_read_data_req {
uint8_t buffer_size_type;
} keyboard_read_data_req;
struct ezusb_iidx_emu_node_serial_keyboard_read_data_resp {
/* dynamic size */
uint8_t data[0xFF];
} keyboard_read_data_resp;
struct ezusb_iidx_emu_node_serial_card_slot_state_req {
uint8_t ezusb_iidx_emu_node_serial_card_slot_state;
} card_slot_state_req;
struct ezusb_iidx_emu_node_serial_card_read_resp {
/* 1 byte header + 128 byte card data */
uint8_t header;
uint8_t data[MAG_CARD_DATA_SIZE];
} card_read_resp;
struct ezusb_iidx_emu_node_serial_card_write_req {
uint8_t data[MAG_CARD_DATA_SIZE];
} card_write_req;
/*
* get_version_req
* proc_exec_req
* card_init_req
* keyboard_init_req
* card_get_status_req
* keyboard_get_status_req
* card_rw_unit_get_status_req
* keyboard_get_buffer_size_req
* card_read_req
* card_format_complete_req
*/
struct ezusb_iidx_emu_node_serial_common_req {
/* no payload */
} common_req;
/*
* proc_exec_resp
* card_init_resp
* keyboard_init_resp
* card_get_status_resp
* keyboard_get_status_resp
* card_rw_unit_get_status_resp
* card_slot_state_resp
* card_write_resp
* card_format_complete_resp
*/
struct ezusb_iidx_emu_node_serial_common_status_resp {
uint8_t status;
} common_status_resp;
};
};
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_node_enum_req) == 1,
"ezusb_iidx_emu_node_serial_node_enum_req is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_node_enum_resp) == 1,
"ezusb_iidx_emu_node_serial_node_enum_resp is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_get_version_resp) == 13,
"ezusb_iidx_emu_node_serial_get_version_resp is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_keyboard_get_buffer_size_resp) == 2,
"ezusb_iidx_emu_node_serial_keyboard_get_buffer_size_resp is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_keyboard_read_data_req) == 1,
"ezusb_iidx_emu_node_serial_keyboard_read_data_req is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_keyboard_read_data_resp) == 255,
"ezusb_iidx_emu_node_serial_keyboard_read_data_resp is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_card_slot_state_req) == 1,
"ezusb_iidx_emu_node_serial_card_slot_state_req is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_card_read_resp) == 1 + MAG_CARD_DATA_SIZE,
"ezusb_iidx_emu_node_serial_card_read_resp is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_card_write_req) == MAG_CARD_DATA_SIZE,
"ezusb_iidx_emu_node_serial_card_write_req is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_common_req) == 0,
"ezusb_iidx_emu_node_serial_common_req is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_common_status_resp) == 1,
"ezusb_iidx_emu_node_serial_common_status_resp is the wrong size");
_Static_assert(
sizeof(struct ezusb_iidx_emu_node_serial_msg) == 259,
"ezusb_iidx_emu_node_serial_msg is the wrong size");
#pragma pack(pop)
/* ------------------------------------------------------------------------- */
enum ezusb_iidx_emu_node_serial_emu_card_slot_state {
EMU_CARD_SLOT_STATE_CLOSE = 0,
EMU_CARD_SLOT_STATE_OPEN = 1,
EMU_CARD_SLOT_STATE_EJECT = 2,
EMU_CARD_SLOT_STATE_READ = 3,
};
enum ezusb_iidx_emu_node_serial_emu_state {
EMU_STATE_UNINIT = 0,
EMU_STATE_INIT = 1,
EMU_STATE_LOOP = 2,
EMU_STATE_REQ_CARD = 3,
EMU_STATE_CARD_READ = 4,
EMU_STATE_ERROR = 5,
};
struct ezusb_iidx_emu_node_serial_emulation_state {
volatile long state_current;
volatile LONG keypad_buf;
bool card_slot_sensor_front;
bool card_slot_sensor_back;
uint8_t ezusb_iidx_emu_node_serial_card_slot_state;
CRITICAL_SECTION card_cs;
uint8_t card_id[CARD_ID_LEN];
};
/* ------------------------------------------------------------------------- */
static const char
ezusb_iidx_emu_node_serial_eamio_mapping[EAM_IO_KEYPAD_COUNT] = {
'0', '1', '4', '7', 'O', '2', '5', '8', 'E', '3', '6', '9'};
static const uint8_t HEADER_BYTE = 0xAA;
static bool ezusb_iidx_emu_node_serial_read_buf_busy;
static bool ezusb_iidx_emu_node_serial_write_buf_busy;
static uint8_t ezusb_iidx_emu_node_serial_read_buf[512];
static uint8_t ezusb_iidx_emu_node_serial_read_buf_page;
static uint16_t ezusb_iidx_emu_node_serial_read_buf_data_len;
static uint8_t ezusb_iidx_emu_node_serial_write_buf[512];
static uint8_t ezusb_iidx_emu_node_serial_wrote_buf_page;
static uint16_t ezusb_iidx_emu_node_serial_write_buf_data_len;
static uint8_t ezusb_iidx_emu_node_serial_card_attr_type;
static bool ezusb_iidx_emu_node_serial_card_attr_used = true;
static char ezusb_iidx_emu_node_serial_card_attr_version[3] =
SECURITY_MCODE_GAME_IIDX_9;
static uint8_t *ezusb_iidx_emu_node_serial_write_loopback_card_buf[2];
static int ezusb_iidx_emu_node_serial_emu_thread = -1;
static struct ezusb_iidx_emu_node_serial_emulation_state
ezusb_iidx_emu_node_serial_emulation_state[2];
static void ezusb_iidx_emu_node_serial_dump_buf_log(
const char *header_msg, const uint8_t *buffer, uint16_t length);
static void ezusb_iidx_emu_node_serial_exec_req_resp(
const uint8_t *buffer_in,
uint16_t buffer_in_length,
uint8_t *buffer_out,
uint16_t *buffer_out_length);
static int ezusb_iidx_emu_node_serial_emu_thread_proc(void *ctx);
/* ------------------------------------------------------------------------- */
void ezusb_iidx_emu_node_serial_init(void)
{
ezusb_iidx_emu_node_serial_read_buf_busy = false;
ezusb_iidx_emu_node_serial_write_buf_busy = false;
memset(
ezusb_iidx_emu_node_serial_read_buf,
0xFF,
sizeof(ezusb_iidx_emu_node_serial_read_buf));
ezusb_iidx_emu_node_serial_read_buf_page = 0;
ezusb_iidx_emu_node_serial_read_buf_data_len = 0;
memset(
ezusb_iidx_emu_node_serial_write_buf,
0xFF,
sizeof(ezusb_iidx_emu_node_serial_write_buf));
ezusb_iidx_emu_node_serial_wrote_buf_page = 0;
ezusb_iidx_emu_node_serial_write_buf_data_len = 0;
memset(
ezusb_iidx_emu_node_serial_emulation_state,
0,
sizeof(struct ezusb_iidx_emu_node_serial_emulation_state) * 2);
for (uint8_t i = 0; i < 2; i++) {
InitializeCriticalSection(
&ezusb_iidx_emu_node_serial_emulation_state[i].card_cs);
}
ezusb_iidx_emu_node_serial_emu_thread = thread_create(
ezusb_iidx_emu_node_serial_emu_thread_proc, NULL, 0x4000, 0);
}
uint8_t ezusb_iidx_emu_node_serial_process_cmd(
uint8_t cmd_id, uint8_t cmd_data, uint8_t cmd_data2)
{
switch (cmd_id) {
case EZUSB_IIDX_SERIAL_CMD_READ_BUFFER:
// log_misc("EZUSB_SERIAL_CMD_READ_BUFFER");
ezusb_iidx_emu_node_serial_read_buf_busy = false;
/* always reset page count on req */
ezusb_iidx_emu_node_serial_read_buf_page = 0;
return EZUSB_IIDX_SERIAL_CMD_STATUS_OK;
case EZUSB_IIDX_SERIAL_CMD_WRITE_BUFFER:
// log_misc("EZUSB_SERIAL_CMD_WRITE_BUFFER");
ezusb_iidx_emu_node_serial_write_buf_busy = false;
/* always reset page count on req */
ezusb_iidx_emu_node_serial_wrote_buf_page = 0;
return EZUSB_IIDX_SERIAL_CMD_STATUS_OK;
case EZUSB_IIDX_SERIAL_CMD_CLEAR_READ_BUFFER:
// log_misc("EZUSB_SERIAL_CMD_CLEAR_READ_BUFFER");
ezusb_iidx_emu_node_serial_read_buf_busy = false;
/* always reset page count on req */
ezusb_iidx_emu_node_serial_read_buf_page = 0;
return EZUSB_IIDX_SERIAL_CMD_STATUS_OK;
case EZUSB_IIDX_SERIAL_CMD_CLEAR_WRITE_BUFFER:
// log_misc("EZUSB_SERIAL_CMD_CLEAR_WRITE_BUFFER");
ezusb_iidx_emu_node_serial_write_buf_busy = false;
/* always reset page count on req */
ezusb_iidx_emu_node_serial_wrote_buf_page = 0;
return EZUSB_IIDX_SERIAL_CMD_STATUS_OK;
default:
log_warning("Unrecognised serial command: %02x", cmd_id);
return EZUSB_IIDX_SERIAL_CMD_STATUS_FAULT;
}
}
bool ezusb_iidx_emu_node_serial_read_packet(
struct ezusb_iidx_msg_bulk_packet *pkg)
{
uint16_t buffer_offset = 0;
uint16_t data_length = 0;
/* id to get accepted */
pkg->node = 0x42;
pkg->page = ezusb_iidx_emu_node_serial_read_buf_page;
memset(pkg->payload, 0xFF, sizeof(pkg->payload));
/* calc data size */
buffer_offset =
ezusb_iidx_emu_node_serial_read_buf_page * sizeof(pkg->payload);
if (ezusb_iidx_emu_node_serial_read_buf_data_len >= sizeof(pkg->payload)) {
data_length = sizeof(pkg->payload);
ezusb_iidx_emu_node_serial_read_buf_data_len -= sizeof(pkg->payload);
ezusb_iidx_emu_node_serial_read_buf_page++;
} else {
data_length = ezusb_iidx_emu_node_serial_read_buf_data_len;
pkg->page = 0x40 + data_length;
ezusb_iidx_emu_node_serial_read_buf_data_len = 0;
}
memcpy(
pkg->payload,
ezusb_iidx_emu_node_serial_read_buf + buffer_offset,
data_length);
return true;
}
bool ezusb_iidx_emu_node_serial_write_packet(
const struct ezusb_iidx_msg_bulk_packet *pkg)
{
bool execute = false;
uint16_t buffer_offset = 0;
uint16_t data_length = 0;
buffer_offset =
ezusb_iidx_emu_node_serial_wrote_buf_page * sizeof(pkg->payload);
/* the stuff getting received here is a serial stream
which is terminated by a size >= 0x42
if the page var is < 0x42, it indicates the page */
if (pkg->page >= 0x42) {
data_length = pkg->page - 0x42;
execute = true;
} else {
/* full page */
data_length = sizeof(pkg->payload);
ezusb_iidx_emu_node_serial_wrote_buf_page++;
}
ezusb_iidx_emu_node_serial_write_buf_data_len += data_length;
memcpy(
ezusb_iidx_emu_node_serial_write_buf + buffer_offset,
pkg->payload,
data_length);
/* serial buffer is filled, execute request/response chain
dunno if this is a good spot to do this... */
if (execute) {
ezusb_iidx_emu_node_serial_exec_req_resp(
ezusb_iidx_emu_node_serial_write_buf,
ezusb_iidx_emu_node_serial_write_buf_data_len,
ezusb_iidx_emu_node_serial_read_buf,
&ezusb_iidx_emu_node_serial_read_buf_data_len);
/* not sure, but there seems to be a bug with the game
not resetting the buffer in the init phase
let us make sure we got this when receiving the serial stream
terminator and having executed something */
ezusb_iidx_emu_node_serial_write_buf_data_len = 0;
}
return true;
}
/* ------------------------------------------------------------------------- */
bool ezusb_iidx_emu_node_serial_read_buffer_busy(void)
{
return ezusb_iidx_emu_node_serial_read_buf_busy;
}
bool ezusb_iidx_emu_node_serial_write_buffer_busy(void)
{
return ezusb_iidx_emu_node_serial_write_buf_busy;
}
void ezusb_iidx_emu_node_serial_set_card_attributes(
uint8_t card_type, bool used_card, const char *card_version)
{
ezusb_iidx_emu_node_serial_card_attr_type = card_type > 4 ? 4 : card_type;
ezusb_iidx_emu_node_serial_card_attr_used = used_card;
memcpy(
ezusb_iidx_emu_node_serial_card_attr_version,
card_version,
SECURITY_MCODE_GAME_LEN);
}
/* ------------------------------------------------------------------------- */
static void ezusb_iidx_emu_node_serial_dump_buf_log(
const char *header_msg, const uint8_t *buffer, uint16_t length)
{
char tmp[4096];
memset(tmp, 0, sizeof(tmp));
hex_encode_uc(buffer, length, tmp, sizeof(tmp));
log_misc(">>> Serial data dump, %s (%d): %s", header_msg, length, tmp);
}
static uint8_t
calc_serial_buffer_checksum(const uint8_t *buffer, uint16_t length)
{
uint8_t checksum = 0;
for (size_t i = 0; i < length; i++)
checksum += buffer[i];
return checksum;
}
static struct ezusb_iidx_emu_node_serial_msg *create_generic_node_response_ok(
const struct ezusb_iidx_emu_node_serial_msg *msg_in, uint16_t *msg_out_len)
{
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + sizeof(struct ezusb_iidx_emu_node_serial_common_status_resp);
struct ezusb_iidx_emu_node_serial_msg *resp = xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_NODE_RESP;
resp->node_id = msg_in->node_id;
resp->node_cmd = msg_in->node_cmd;
resp->payload_len = 1;
resp->common_status_resp.status = 0;
return resp;
}
static struct ezusb_iidx_emu_node_serial_msg *process_serial_msg(
const struct ezusb_iidx_emu_node_serial_msg *msg_in,
uint16_t msg_in_len,
uint16_t *msg_out_len)
{
// log_misc("!!! Serial processing msg_cmd %02X, node_cmd %02X, node_id
// %02X",
// msg_in->msg_cmd, msg_in->node_cmd, msg_in->node_id);
switch (msg_in->msg_cmd) {
case CMD_H8_REQ: {
switch (msg_in->node_cmd) {
case H8_CMD_NODE_ENUM: {
log_misc("H8_CMD_NODE_ENUM");
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + sizeof(struct ezusb_iidx_emu_node_serial_node_enum_resp);
struct ezusb_iidx_emu_node_serial_msg *resp =
xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_H8_RESP;
/* This response is not from a specific node, id needs to be
* 0 */
resp->node_id = 0;
resp->node_cmd = msg_in->node_cmd;
resp->payload_len = 1;
resp->node_enum_resp.total_nodes = 2;
return resp;
}
case H8_CMD_GET_VERSION: {
log_misc("H8_CMD_GET_VERSION: %d", msg_in->node_id);
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + sizeof(struct ezusb_iidx_emu_node_serial_get_version_resp);
struct ezusb_iidx_emu_node_serial_msg *resp =
xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_H8_RESP;
resp->node_id = msg_in->node_id;
resp->node_cmd = msg_in->node_cmd;
/* Payload length is not 13 here, needs to 5 (comment
length?) Might be a bug in the ezusb code */
resp->payload_len = 5;
resp->get_version_resp.type = 0x03;
resp->get_version_resp.dup = 0x00;
resp->get_version_resp.version_maj = 1;
resp->get_version_resp.version_min = 6;
resp->get_version_resp.version_rev = 0;
resp->get_version_resp.comment[0] = 'I';
resp->get_version_resp.comment[1] = 'C';
resp->get_version_resp.comment[2] = 'C';
resp->get_version_resp.comment[3] = 'A';
resp->get_version_resp.comment[4] = '\0';
return resp;
}
case H8_CMD_PROG_EXEC: {
log_misc("H8_CMD_PROG_EXEC: %d", msg_in->node_id);
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + sizeof(struct ezusb_iidx_emu_node_serial_common_status_resp);
struct ezusb_iidx_emu_node_serial_msg *resp =
xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_H8_RESP;
resp->node_id = msg_in->node_id;
resp->node_cmd = msg_in->node_cmd;
resp->payload_len = 1;
/* init reader emulation here if not initialized, yet */
uint8_t state = ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current;
if (state == EMU_STATE_UNINIT) {
InterlockedExchange(
&ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current,
EMU_STATE_INIT);
/* this should not take too long, wait */
while (true) {
state = ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current;
if (state == EMU_STATE_LOOP) {
resp->common_status_resp.status = 0;
break;
} else if (state == EMU_STATE_ERROR) {
resp->common_status_resp.status = 0xFF;
break;
}
Sleep(10);
}
} else {
if (state == EMU_STATE_ERROR) {
resp->common_status_resp.status = 0xFF;
} else {
resp->common_status_resp.status = 0;
}
}
return resp;
}
default:
log_warning(
"Serial invalid node cmd on h8 req: %02X",
msg_in->node_cmd);
ezusb_iidx_emu_node_serial_dump_buf_log(
"", (const uint8_t *) msg_in, msg_in_len);
return NULL;
}
}
case CMD_NODE_REQ: {
switch (msg_in->node_cmd) {
case NODE_CMD_CARD_INIT:
log_misc("NODE_CMD_CARD_INIT: %d", msg_in->node_id);
return create_generic_node_response_ok(msg_in, msg_out_len);
case NODE_CMD_KEYBOARD_INIT:
log_misc("NODE_CMD_KEYBOARD_INIT: %d", msg_in->node_id);
return create_generic_node_response_ok(msg_in, msg_out_len);
case NODE_CMD_KEYBOARD_GET_STATUS:
return create_generic_node_response_ok(msg_in, msg_out_len);
case NODE_CMD_CARD_GET_STATUS:
return create_generic_node_response_ok(msg_in, msg_out_len);
case NODE_CMD_CARD_RW_UNIT_GET_STATUS: {
/* report front and back sensor states */
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + sizeof(struct ezusb_iidx_emu_node_serial_common_status_resp);
struct ezusb_iidx_emu_node_serial_msg *resp =
xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_NODE_RESP;
resp->node_id = msg_in->node_id;
resp->node_cmd = msg_in->node_cmd;
resp->payload_len = 1;
resp->common_status_resp.status = 0;
if (ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current == EMU_STATE_ERROR) {
resp->common_status_resp.status = 0xFF;
} else {
if (ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_slot_sensor_back) {
/* back, triggers card inserted */
resp->common_status_resp.status |= 128;
/* needs to be set if either front or back is
* triggered */
resp->common_status_resp.status |= 2;
}
if (ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_slot_sensor_front) {
/* front */
resp->common_status_resp.status |= 64;
/* needs to be set if either front or back is
* triggered */
resp->common_status_resp.status |= 2;
}
}
return resp;
}
case NODE_CMD_CARD_SLOT_SET_STATE: {
switch (msg_in->card_slot_state_req
.ezusb_iidx_emu_node_serial_card_slot_state) {
case CARD_SLOT_STATE_CLOSE:
log_misc(
"CARD_SLOT_STATE_CLOSE, node %d",
msg_in->node_id);
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.ezusb_iidx_emu_node_serial_card_slot_state =
EMU_CARD_SLOT_STATE_CLOSE;
break;
case CARD_SLOT_STATE_OPEN:
log_misc(
"CARD_SLOT_STATE_OPEN, node %d",
msg_in->node_id);
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.ezusb_iidx_emu_node_serial_card_slot_state =
EMU_CARD_SLOT_STATE_OPEN;
break;
case CARD_SLOT_STATE_EJECT:
log_misc(
"CARD_SLOT_STATE_EJECT, node %d",
msg_in->node_id);
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.ezusb_iidx_emu_node_serial_card_slot_state =
EMU_CARD_SLOT_STATE_EJECT;
break;
case CARD_SLOT_STATE_FORMAT:
log_misc(
"CARD_SLOT_STATE_FORMAT, node %d",
msg_in->node_id);
break;
case CARD_SLOT_STATE_READ:
log_misc(
"CARD_SLOT_STATE_READ, node %d",
msg_in->node_id);
InterlockedExchange(
&ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current,
EMU_STATE_REQ_CARD);
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.ezusb_iidx_emu_node_serial_card_slot_state =
EMU_CARD_SLOT_STATE_READ;
break;
case CARD_SLOT_STATE_WRITE:
log_misc(
"CARD_SLOT_STATE_WRITE, node %d",
msg_in->node_id);
break;
default:
log_warning(
"Invalid card slot state %d for node %d",
msg_in->card_slot_state_req
.ezusb_iidx_emu_node_serial_card_slot_state,
msg_in->node_id);
}
return create_generic_node_response_ok(msg_in, msg_out_len);
}
case NODE_CMD_CARD_READ: {
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + sizeof(struct ezusb_iidx_emu_node_serial_card_read_resp);
struct ezusb_iidx_emu_node_serial_msg *resp =
xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_NODE_RESP;
resp->node_id = msg_in->node_id;
resp->node_cmd = msg_in->node_cmd;
/* game expects 24 here, even more data is sent */
resp->payload_len = 24;
/* fixed header byte */
resp->card_read_resp.header = 0x48;
memset(resp->card_read_resp.data, 0x00, MAG_CARD_DATA_SIZE);
/* loop back read for write (see write) */
if (ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1] == NULL) {
struct ezusb_iidx_emu_card_mag_data *card =
(struct ezusb_iidx_emu_card_mag_data *)
resp->card_read_resp.data;
uint8_t state =
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current;
/* wait for card to get read */
while (state == EMU_STATE_REQ_CARD) {
state = ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current;
Sleep(5);
}
if (state == EMU_STATE_CARD_READ) {
uint8_t card_invert[CARD_ID_LEN];
EnterCriticalSection(
&ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_cs);
log_info(
"Read card %02X%02X%02X%02X%02X%02X%02X%02X",
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[0],
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[1],
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[2],
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[3],
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[4],
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[5],
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[6],
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[7]);
/* game flips endianess */
for (uint8_t i = 0; i < CARD_ID_LEN; i++) {
card_invert[7 - i] =
ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_id[i];
}
ezusb_iidx_emu_card_mag_generate_data(
card,
card_invert,
ezusb_iidx_emu_node_serial_card_attr_type,
ezusb_iidx_emu_node_serial_card_attr_used,
ezusb_iidx_emu_node_serial_card_attr_version);
/* ezusb_iidx_emu_node_serial_dump_buf_log("card
data", resp->card_read_resp.data, sizeof(struct
ezusb_iidx_emu_card_mag_data)); */
InterlockedExchange(
&ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.state_current,
EMU_STATE_LOOP);
LeaveCriticalSection(
&ezusb_iidx_emu_node_serial_emulation_state
[msg_in->node_id - 1]
.card_cs);
} else if (state == EMU_STATE_ERROR) {
/* this indicates an error */
resp->payload_len = 1;
resp->card_read_resp.data[0] = 0xFF;
}
} else {
log_misc("Reading loopback card buffer");
memcpy(
resp->card_read_resp.data,
ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1],
MAG_CARD_DATA_SIZE);
free(ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1]);
ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1] = NULL;
}
return resp;
}
case NODE_CMD_CARD_WRITE: {
/* i hate this solution, but it gets rid of the problem that
we don't know the whole magnetic card spec and the game
is ok with just a bunch of them when read only
If a write occurs (for example if the card is registered
for a new player) we will cashe the data which arrives on
the write and loop it back on the next read (next read
only) */
if (ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1] != NULL) {
free(ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1]);
}
ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1] = xmalloc(MAG_CARD_DATA_SIZE);
memcpy(
ezusb_iidx_emu_node_serial_write_loopback_card_buf
[msg_in->node_id - 1],
msg_in->card_write_req.data,
MAG_CARD_DATA_SIZE);
return create_generic_node_response_ok(msg_in, msg_out_len);
}
case NODE_CMD_CARD_FORMAT_COMPLETE: {
/* just reply, no handling of data necessary */
return create_generic_node_response_ok(msg_in, msg_out_len);
}
case NODE_CMD_KEYBOARD_GET_BUFFER_SIZE: {
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + sizeof(struct ezusb_iidx_emu_node_serial_keyboard_get_buffer_size_resp);
struct ezusb_iidx_emu_node_serial_msg *resp =
xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_NODE_RESP;
resp->node_id = msg_in->node_id;
resp->node_cmd = msg_in->node_cmd;
resp->payload_len = 2;
resp->common_status_resp.status = 0;
/* read buffer sizes. the size is getting converted
and the converted value sent to us again on the read call
0 -> 0 no data to send
1 -> 1
2 -> 2
3 -> 3-4 i.e. max buffer size 4
4 -> 5-8 i.e. max buffer size 8
5 -> 9-16 i.e. max buffer size 16
6 -> 17-32 i.e. max buffer size 32
7 -> 33-64 i.e. max buffer size 64 */
/* The card reader driver is really bad performance wise
so it's even slower if we use a ring buffer like
data structure. It's easier to use a single byte
buffer and even faster to handle for the code here */
uint16_t bufferSize = 0;
uint8_t node = msg_in->node_id - 1;
if (ezusb_iidx_emu_node_serial_emulation_state[node]
.keypad_buf != 0) {
bufferSize = 1;
}
bufferSize = bufferSize >= 64 ? 64 : bufferSize;
/* convert */
if (bufferSize < 3) {
resp->keyboard_get_buffer_size_resp.buffer_size_type =
bufferSize;
} else if (bufferSize <= 4) {
resp->keyboard_get_buffer_size_resp.buffer_size_type =
3;
} else if (bufferSize <= 8) {
resp->keyboard_get_buffer_size_resp.buffer_size_type =
4;
} else if (bufferSize <= 16) {
resp->keyboard_get_buffer_size_resp.buffer_size_type =
5;
} else if (bufferSize <= 32) {
resp->keyboard_get_buffer_size_resp.buffer_size_type =
6;
} else {
/* avoid overflow, clip everything else */
resp->keyboard_get_buffer_size_resp.buffer_size_type =
7;
}
return resp;
}
case NODE_CMD_KEYBOARD_READ_DATA: {
*msg_out_len = NODE_SERIAL_MSG_HEADER_SIZE + 2;
struct ezusb_iidx_emu_node_serial_msg *resp =
xmalloc(sizeof(struct ezusb_iidx_emu_node_serial_msg));
resp->msg_cmd = CMD_NODE_RESP;
resp->node_id = msg_in->node_id;
resp->node_cmd = msg_in->node_cmd;
/* has to be the same as the size returned */
resp->payload_len =
msg_in->keyboard_read_data_req.buffer_size_type;
uint8_t maxBufferSize = 0;
/* convert back */
switch (msg_in->keyboard_read_data_req.buffer_size_type) {
case 0:
maxBufferSize = 0;
break;
case 1:
maxBufferSize = 1;
break;
case 2:
maxBufferSize = 2;
break;
case 3:
maxBufferSize = 4;
break;
case 4:
maxBufferSize = 8;
break;
case 5:
maxBufferSize = 16;
break;
case 6:
maxBufferSize = 32;
break;
case 7:
maxBufferSize = 64;
break;
default:
log_fatal(
"Invalid buffer size %d for reading keyboard "
"data.",
msg_in->keyboard_read_data_req
.buffer_size_type);
break;
}
memset(
resp->keyboard_read_data_resp.data, 0, maxBufferSize);
if (maxBufferSize > 0) {
uint8_t node = msg_in->node_id - 1;
resp->keyboard_read_data_resp
.data[0] = InterlockedExchange(
&ezusb_iidx_emu_node_serial_emulation_state[node]
.keypad_buf,
0);
}
return resp;
}
default:
log_warning(
"Serial invalid node cmd on node req: %02X",
msg_in->node_cmd);
ezusb_iidx_emu_node_serial_dump_buf_log(
"", (const uint8_t *) msg_in, msg_in_len);
return NULL;
}
}
default:
log_warning("Serial invalid msg cmd id %02X", msg_in->msg_cmd);
ezusb_iidx_emu_node_serial_dump_buf_log(
"", (const uint8_t *) msg_in, msg_in_len);
return NULL;
}
}
static void ezusb_iidx_emu_node_serial_exec_req_resp(
const uint8_t *buffer_in,
uint16_t buffer_in_length,
uint8_t *buffer_out,
uint16_t *buffer_out_length)
{
/* data layout
index 0: 0xAA header byte always
index 1 to (length-1) - 1 excluding: payload
index (length-1) - 1: checksum byte -> add up all data bytes between
first and last byte
no escape byte like on newer readers here */
/* Before we do any processing:
Filter garbage/broken messages. Maybe caused by buggy code (?) some
messages during the uart init phase start with 0x00 and don't have a
valid checksum. However, the game wants a response for that but doesn't
care about the contents so we just throw the trash back where it came
from */
/*
ezusb_iidx_emu_node_serial_dump_buf_log("request packed",
(const uint8_t*) buffer_in, buffer_in_length);
*/
if ((buffer_in_length > 0 && buffer_in[0] == 0x00) ||
/* That's some uart init/reset message. Response expected, same message
*/
(buffer_in_length == 4 && buffer_in[0] == 0xAA)) {
*buffer_out_length = buffer_in_length;
memcpy(buffer_out, buffer_in, buffer_in_length);
return;
}
/* check header */
if (buffer_in_length > 0 && buffer_in[0] != HEADER_BYTE) {
log_warning("Invalid serial message, not starting with header byte");
ezusb_iidx_emu_node_serial_dump_buf_log(
"", (const uint8_t *) buffer_in, buffer_in_length);
*buffer_out_length = 0;
return;
}
/* Checksum of each message is all data between header and checksum
byte (last byte of msg) */
if (buffer_in[buffer_in_length - 1] !=
calc_serial_buffer_checksum(buffer_in + 1, buffer_in_length - 2)) {
log_warning("Serial message invalid checksum");
ezusb_iidx_emu_node_serial_dump_buf_log(
"", buffer_in, buffer_in_length);
*buffer_out_length = 0;
return;
}
const uint8_t *msg_buf_in = &buffer_in[1];
uint16_t msg_buf_in_len = buffer_in_length - 2;
/* Exception: catch uart init/reset message, game expects to respond with
the same message this message is sent/written multiple times and read
back once only by the game */
if (msg_buf_in_len == 2 && msg_buf_in[0] == HEADER_BYTE &&
msg_buf_in[1] == HEADER_BYTE) {
uint8_t *msg_buf_out = &buffer_out[1];
uint16_t msg_buf_out_len = 0;
msg_buf_out[0] = HEADER_BYTE;
msg_buf_out[1] = HEADER_BYTE;
msg_buf_out_len = 2;
buffer_out[0] = HEADER_BYTE;
buffer_out[msg_buf_out_len + 1] =
calc_serial_buffer_checksum(msg_buf_out, msg_buf_out_len);
*buffer_out_length = msg_buf_out_len + 2;
} else {
/* Wtf, really? */
/* All messages except the uart init/reset and node enum message
must send the request they respond to along... */
const struct ezusb_iidx_emu_node_serial_msg *msg_in =
(const struct ezusb_iidx_emu_node_serial_msg *) msg_buf_in;
if (msg_in->msg_cmd == CMD_H8_REQ &&
msg_in->node_cmd == H8_CMD_NODE_ENUM) {
uint8_t *msg_buf_out = &buffer_out[1];
uint16_t msg_buf_out_len = 0;
struct ezusb_iidx_emu_node_serial_msg *msg_out = process_serial_msg(
(const struct ezusb_iidx_emu_node_serial_msg *) msg_buf_in,
msg_buf_in_len,
&msg_buf_out_len);
if (msg_out == NULL) {
*buffer_out_length = 0;
return;
}
memcpy(msg_buf_out, msg_out, msg_buf_out_len);
free(msg_out);
/* pack serial out data, add header and checksum */
buffer_out[0] = HEADER_BYTE;
buffer_out[msg_buf_out_len + 1] =
calc_serial_buffer_checksum(msg_buf_out, msg_buf_out_len);
*buffer_out_length = msg_buf_out_len + 2;
} else {
/* everything else that's not fucked up */
uint8_t *msg_buf_out = &buffer_out[buffer_in_length + 1];
uint16_t msg_buf_out_len = 0;
struct ezusb_iidx_emu_node_serial_msg *msg_out = process_serial_msg(
(const struct ezusb_iidx_emu_node_serial_msg *) msg_buf_in,
msg_buf_in_len,
&msg_buf_out_len);
if (msg_out == NULL) {
*buffer_out_length = 0;
return;
}
/* Copy request followed by packed response */
memcpy(buffer_out, buffer_in, buffer_in_length);
memcpy(msg_buf_out, msg_out, msg_buf_out_len);
free(msg_out);
/* pack serial out data (after the request which needs to be part
of the full response buffer), add header and checksum */
buffer_out[buffer_in_length] = HEADER_BYTE;
buffer_out[buffer_in_length + msg_buf_out_len + 1] =
calc_serial_buffer_checksum(msg_buf_out, msg_buf_out_len);
*buffer_out_length = buffer_in_length + msg_buf_out_len + 2;
}
}
/*
ezusb_iidx_emu_node_serial_dump_buf_log("response packed",
(const uint8_t*) buffer_out, *buffer_out_length);
*/
}
/* ------------------------------------------------------------------------- */
static uint8_t convert_keyboard_char(char c)
{
switch (c) {
case '0':
return 0x70;
case '1':
return 0x69;
case '2':
return 0x72;
case '3':
return 0x7A;
case '4':
return 0x6B;
case '5':
return 0x73;
case '6':
return 0x74;
case '7':
return 0x6C;
case '8':
return 0x75;
case '9':
return 0x7D;
/* seems like 00 is not supported, use 0 instead */
case 'O':
return 0x70;
/* that's deleting/backspace */
case 'E':
return 0x66;
default:
return 0;
}
/* more codes, don't seem to be used
resp->m_data[10] = 0x77;
resp->m_data[11] = 0x4A;
resp->m_data[12] = 0x7C;
resp->m_data[13] = 0x66;
resp->m_data[14] = 0x7B;
resp->m_data[15] = 0x76;
resp->m_data[16] = 0x5A; */
}
static void keyboard_add_key_press_to_buffer(uint8_t node, char key)
{
InterlockedCompareExchange(
&ezusb_iidx_emu_node_serial_emulation_state[node].keypad_buf,
convert_keyboard_char(key),
0);
}
static int ezusb_iidx_emu_node_serial_emu_thread_proc(void *ctx)
{
log_info("Started magnetic card reader emulation thread");
/* wait until we are allowed to execute */
uint8_t init = 0;
while (init < 2) {
for (uint8_t i = 0; i < 2; i++) {
if (ezusb_iidx_emu_node_serial_emulation_state[i].state_current ==
EMU_STATE_INIT) {
InterlockedExchange(
&ezusb_iidx_emu_node_serial_emulation_state[i]
.state_current,
EMU_STATE_LOOP);
init++;
}
}
Sleep(10);
}
uint16_t keyboard_state_prev[2] = {0, 0};
while (true) {
for (uint8_t node = 0; node < 2; node++) {
if (!eam_io_poll(node)) {
log_warning("Polling eamio, node %d failed", node);
continue;
}
/* read card slot sensors */
uint8_t sensors = eam_io_get_sensor_state(node);
if (sensors & (1 << EAM_IO_SENSOR_FRONT)) {
ezusb_iidx_emu_node_serial_emulation_state[node]
.card_slot_sensor_front = true;
} else {
ezusb_iidx_emu_node_serial_emulation_state[node]
.card_slot_sensor_front = false;
}
if (sensors & (1 << EAM_IO_SENSOR_BACK)) {
ezusb_iidx_emu_node_serial_emulation_state[node]
.card_slot_sensor_back = true;
} else {
ezusb_iidx_emu_node_serial_emulation_state[node]
.card_slot_sensor_back = false;
}
/* handle keypad */
uint16_t keypad = eam_io_get_keypad_state(node);
uint16_t keypad_rise = ~keyboard_state_prev[node] & keypad;
for (uint8_t i = 0;
i < sizeof(ezusb_iidx_emu_node_serial_eamio_mapping);
++i) {
if (keypad_rise & (1 << i)) {
keyboard_add_key_press_to_buffer(
node, ezusb_iidx_emu_node_serial_eamio_mapping[i]);
}
}
keyboard_state_prev[node] = keypad;
/* set card slot state */
switch (ezusb_iidx_emu_node_serial_emulation_state[node]
.ezusb_iidx_emu_node_serial_card_slot_state) {
case EMU_CARD_SLOT_STATE_CLOSE:
eam_io_card_slot_cmd(node, EAM_IO_CARD_SLOT_CMD_CLOSE);
break;
case EMU_CARD_SLOT_STATE_EJECT:
eam_io_card_slot_cmd(node, EAM_IO_CARD_SLOT_CMD_EJECT);
EnterCriticalSection(
&ezusb_iidx_emu_node_serial_emulation_state[node]
.card_cs);
/* invalidate card id */
memset(
ezusb_iidx_emu_node_serial_emulation_state[node]
.card_id,
0,
CARD_ID_LEN);
LeaveCriticalSection(
&ezusb_iidx_emu_node_serial_emulation_state[node]
.card_cs);
break;
case EMU_CARD_SLOT_STATE_OPEN:
eam_io_card_slot_cmd(node, EAM_IO_CARD_SLOT_CMD_OPEN);
break;
case EMU_CARD_SLOT_STATE_READ:
eam_io_card_slot_cmd(node, EAM_IO_CARD_SLOT_CMD_READ);
/* read once */
if (ezusb_iidx_emu_node_serial_emulation_state[node]
.state_current == EMU_STATE_REQ_CARD) {
EnterCriticalSection(
&ezusb_iidx_emu_node_serial_emulation_state[node]
.card_cs);
if (!eam_io_read_card(
node,
ezusb_iidx_emu_node_serial_emulation_state[node]
.card_id,
CARD_ID_LEN)) {
memset(
ezusb_iidx_emu_node_serial_emulation_state[node]
.card_id,
0,
CARD_ID_LEN);
InterlockedExchange(
&ezusb_iidx_emu_node_serial_emulation_state
[node]
.state_current,
EMU_STATE_ERROR);
} else {
InterlockedExchange(
&ezusb_iidx_emu_node_serial_emulation_state
[node]
.state_current,
EMU_STATE_CARD_READ);
}
LeaveCriticalSection(
&ezusb_iidx_emu_node_serial_emulation_state[node]
.card_cs);
}
break;
default:
break;
}
}
/* avoid cpu banging */
Sleep(5);
}
return 0;
}