- 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
1352 lines
52 KiB
C
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;
|
|
} |