mirror of
https://gitea.tendokyu.moe/TeamTofuShop/taitools.git
synced 2026-10-01 03:08:03 +03:00
UNSTABLE reader changes, bloat removal, card documentation
This commit is contained in:
@@ -7,14 +7,6 @@
|
||||
|
||||
#include "board/config.h"
|
||||
|
||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
assert(filename != NULL);
|
||||
|
||||
cfg->enable = GetPrivateProfileIntW(L"io4", L"enable", 1, filename);
|
||||
}
|
||||
|
||||
void k92x0249_config_load(struct k92x0249_config *cfg, const wchar_t *filename)
|
||||
{
|
||||
assert(cfg != NULL);
|
||||
|
||||
@@ -3,12 +3,10 @@
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
#include "board/dongle.h"
|
||||
#include "board/k92x0249.h"
|
||||
#include "board/k9101258.h"
|
||||
|
||||
void io4_config_load(struct io4_config *cfg, const wchar_t *filename);
|
||||
void k92x0249_config_load(struct k92x0249_config *cfg, const wchar_t *filename);
|
||||
void k9101258_config_load(struct usbio_config *cfg, const wchar_t *filename);
|
||||
void dongle_config_load(struct dongle_config *cfg, const wchar_t *filename);
|
||||
|
||||
-353
@@ -1,353 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <devioctl.h>
|
||||
#include <hidclass.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "board/config.h"
|
||||
#include "board/guid.h"
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
#include "hook/iohook.h"
|
||||
|
||||
#include "hooklib/setupapi.h"
|
||||
|
||||
#include "util/async.h"
|
||||
#include "util/dprintf.h"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
IO4_CMD_SET_COMM_TIMEOUT = 0x01,
|
||||
IO4_CMD_SET_SAMPLING_COUNT = 0x02,
|
||||
IO4_CMD_CLEAR_BOARD_STATUS = 0x03,
|
||||
IO4_CMD_SET_GENERAL_OUTPUT = 0x04,
|
||||
IO4_CMD_SET_PWM_OUTPUT = 0x05,
|
||||
IO4_CMD_UNIMPLEMENTED = 0x41,
|
||||
IO4_CMD_UPDATE_FIRMWARE = 0x85,
|
||||
};
|
||||
|
||||
struct io4_report_in {
|
||||
uint8_t report_id;
|
||||
uint16_t adcs[8];
|
||||
uint16_t spinners[4];
|
||||
uint16_t chutes[2];
|
||||
uint16_t buttons[2];
|
||||
uint8_t system_status;
|
||||
uint8_t usb_status;
|
||||
uint8_t unknown[29];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct io4_report_in) == 0x40, "IO4 IN report size");
|
||||
|
||||
struct io4_report_out {
|
||||
uint8_t report_id;
|
||||
uint8_t cmd;
|
||||
uint8_t payload[62];
|
||||
};
|
||||
|
||||
static_assert(sizeof(struct io4_report_out) == 0x40, "IO4 OUT report size");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
static HRESULT io4_handle_irp(struct irp *irp);
|
||||
static HRESULT io4_handle_open(struct irp *irp);
|
||||
static HRESULT io4_handle_close(struct irp *irp);
|
||||
static HRESULT io4_handle_read(struct irp *irp);
|
||||
static HRESULT io4_handle_write(struct irp *irp);
|
||||
static HRESULT io4_handle_ioctl(struct irp *irp);
|
||||
|
||||
static HRESULT io4_ioctl_get_manufacturer_string(struct irp *irp);
|
||||
static HRESULT io4_ioctl_get_product_string(struct irp *irp);
|
||||
|
||||
static HRESULT io4_async_poll(void *ctx, struct irp *irp);
|
||||
|
||||
/* Device node path must contain substring "vid_0ca3" (case-insensitive). */
|
||||
static const wchar_t io4_path[] = L"$io4\\vid_0ca3";
|
||||
|
||||
static const wchar_t io4_manf[] = L"SEGA";
|
||||
static const wchar_t io4_prod[] =
|
||||
/* "Product" (N.B. numbers are in hex) */
|
||||
|
||||
L"I/O CONTROL BD;" /* Board type */
|
||||
L"15257;" /* Board number */
|
||||
L"01;" /* "Mode" (prob. USB vs JVS?) */
|
||||
L"90;" /* Firmware revision */
|
||||
L"1831;" /* Firmware checksum */
|
||||
L"6679A;" /* "Custom chip no" */
|
||||
L"00;" /* "Config" */
|
||||
|
||||
/* "Function" (N.B. all values are in hex) */
|
||||
|
||||
L"GOUT=14_" /* General-purpose output */
|
||||
L"ADIN=8,E_" /* ADC inputs */
|
||||
L"ROTIN=4_" /* Rotary inputs */
|
||||
L"COININ=2_" /* Coin inputs */
|
||||
L"SWIN=2,E_" /* Switch inputs */
|
||||
L"UQ1=41,6" /* "Unique function 1" */
|
||||
;
|
||||
|
||||
static HANDLE io4_fd;
|
||||
static struct async io4_async;
|
||||
static uint8_t io4_system_status;
|
||||
static const struct io4_ops *io4_ops;
|
||||
static void *io4_ops_ctx;
|
||||
|
||||
HRESULT io4_hook_init(
|
||||
const struct io4_config *cfg,
|
||||
const struct io4_ops *ops,
|
||||
void *ctx)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
assert(cfg != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
if (!cfg->enable) {
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
async_init(&io4_async, NULL);
|
||||
|
||||
hr = iohook_open_nul_fd(&io4_fd);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
io4_ops = ops;
|
||||
io4_ops_ctx = ctx;
|
||||
io4_system_status = 0x02; /* idk */
|
||||
iohook_push_handler(io4_handle_irp);
|
||||
|
||||
hr = setupapi_add_phantom_dev(&hid_guid, io4_path);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_irp(struct irp *irp)
|
||||
{
|
||||
assert(irp != NULL);
|
||||
|
||||
if (irp->op != IRP_OP_OPEN && irp->fd != io4_fd) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
switch (irp->op) {
|
||||
case IRP_OP_OPEN: return io4_handle_open(irp);
|
||||
case IRP_OP_CLOSE: return io4_handle_close(irp);
|
||||
case IRP_OP_READ: return io4_handle_read(irp);
|
||||
case IRP_OP_WRITE: return io4_handle_write(irp);
|
||||
case IRP_OP_IOCTL: return io4_handle_ioctl(irp);
|
||||
default: return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_open(struct irp *irp)
|
||||
{
|
||||
if (wcscmp(irp->open_filename, io4_path) != 0) {
|
||||
return iohook_invoke_next(irp);
|
||||
}
|
||||
|
||||
dprintf("USB I/O: Device opened\n");
|
||||
irp->fd = io4_fd;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_close(struct irp *irp)
|
||||
{
|
||||
dprintf("USB I/O: Device closed\n");
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_read(struct irp *irp)
|
||||
{
|
||||
/* The amdaemon USBIO driver will continuously poll the IO until the IO
|
||||
call returns an async operation in progress. We have to return and then
|
||||
signal the OVERLAPPED event object "a little bit later" in order to avoid
|
||||
an infinite loop. */
|
||||
|
||||
return async_submit(&io4_async, irp, io4_async_poll);
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_write(struct irp *irp)
|
||||
{
|
||||
struct io4_report_out out;
|
||||
HRESULT hr;
|
||||
|
||||
hr = iobuf_read(&irp->write, &out, sizeof(out));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
if (out.report_id != 0x10) {
|
||||
dprintf("USB I/O: OUT Report ID is incorrect");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
switch (out.cmd) {
|
||||
case IO4_CMD_SET_COMM_TIMEOUT:
|
||||
dprintf("USB I/O: Set comm timeout\n");
|
||||
|
||||
// Ongeki Summer expects the system status to be 0x30 at this point
|
||||
io4_system_status = 0x30;
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_SAMPLING_COUNT:
|
||||
dprintf("USB I/O: Set sampling count\n");
|
||||
|
||||
// Ongeki Summer expects the system status to be 0x30 at this point
|
||||
io4_system_status = 0x30;
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_CLEAR_BOARD_STATUS:
|
||||
dprintf("USB I/O: Clear board status\n");
|
||||
io4_system_status = 0x00;
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_GENERAL_OUTPUT:
|
||||
dprintf("USB I/O: GPIO Out\n");
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_SET_PWM_OUTPUT:
|
||||
dprintf("USB I/O: PWM Out\n");
|
||||
|
||||
return S_OK;
|
||||
|
||||
case IO4_CMD_UPDATE_FIRMWARE:
|
||||
dprintf("USB I/O: Update firmware..?\n");
|
||||
|
||||
return E_FAIL;
|
||||
|
||||
case IO4_CMD_UNIMPLEMENTED:
|
||||
//dprintf("USB I/O: Unimplemented cmd 41\n");
|
||||
|
||||
return S_OK;
|
||||
|
||||
default:
|
||||
dprintf("USB I/O: Unknown command %02x\n", out.cmd);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT io4_handle_ioctl(struct irp *irp)
|
||||
{
|
||||
switch (irp->ioctl) {
|
||||
case IOCTL_HID_GET_MANUFACTURER_STRING:
|
||||
return io4_ioctl_get_manufacturer_string(irp);
|
||||
|
||||
case IOCTL_HID_GET_PRODUCT_STRING:
|
||||
return io4_ioctl_get_product_string(irp);
|
||||
|
||||
case IOCTL_HID_GET_INPUT_REPORT:
|
||||
dprintf("USB I/O: Control IN (untested!!)\n");
|
||||
|
||||
return io4_handle_read(irp);
|
||||
|
||||
case IOCTL_HID_SET_OUTPUT_REPORT:
|
||||
dprintf("USB I/O: Control OUT (untested!!)\n");
|
||||
|
||||
return io4_handle_write(irp);
|
||||
|
||||
default:
|
||||
dprintf("USB I/O: Unknown ioctl %#08x, write %i read %i\n",
|
||||
irp->ioctl,
|
||||
(int) irp->write.nbytes,
|
||||
(int) irp->read.nbytes);
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT io4_ioctl_get_manufacturer_string(struct irp *irp)
|
||||
{
|
||||
dprintf("USB I/O: Get manufacturer string\n");
|
||||
|
||||
if (irp->read.nbytes < sizeof(io4_manf)) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
memcpy(irp->read.bytes, io4_manf, sizeof(io4_manf));
|
||||
irp->read.pos = sizeof(io4_manf);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_ioctl_get_product_string(struct irp *irp)
|
||||
{
|
||||
dprintf("USB I/O: Get product string\n");
|
||||
|
||||
if (irp->read.nbytes < sizeof(io4_prod)) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
memcpy(irp->read.bytes, io4_prod, sizeof(io4_prod));
|
||||
irp->read.pos = sizeof(io4_prod);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT io4_async_poll(void *ctx, struct irp *irp)
|
||||
{
|
||||
struct io4_report_in in;
|
||||
struct io4_state state;
|
||||
HRESULT hr;
|
||||
size_t i;
|
||||
|
||||
/* Delay long enough for the instigating thread in amdaemon to be satisfied
|
||||
that all queued-up reports have been drained. */
|
||||
|
||||
Sleep(1);
|
||||
|
||||
/* Call into ops to poll the underlying inputs */
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
hr = io4_ops->poll(io4_ops_ctx, &state);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
/* Construct IN report. Values are all little-endian, unlike JVS. */
|
||||
|
||||
memset(&in, 0, sizeof(in));
|
||||
in.report_id = 0x01;
|
||||
in.system_status = io4_system_status;
|
||||
|
||||
for (i = 0 ; i < 8 ; i++) {
|
||||
in.adcs[i] = state.adcs[i];
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 4 ; i++) {
|
||||
in.spinners[i] = state.spinners[i];
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 2 ; i++) {
|
||||
in.chutes[i] = state.chutes[i];
|
||||
}
|
||||
|
||||
for (i = 0 ; i < 2 ; i++) {
|
||||
in.buttons[i] = state.buttons[i];
|
||||
}
|
||||
|
||||
return iobuf_write(&irp->read, &in, sizeof(in));
|
||||
}
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum {
|
||||
/* System buttons in button[0] */
|
||||
|
||||
IO4_BUTTON_TEST = 1 << 9,
|
||||
IO4_BUTTON_SERVICE = 1 << 6,
|
||||
};
|
||||
|
||||
struct io4_config {
|
||||
bool enable;
|
||||
};
|
||||
|
||||
struct io4_state {
|
||||
uint16_t adcs[8];
|
||||
uint16_t spinners[4];
|
||||
uint16_t chutes[2];
|
||||
uint16_t buttons[2];
|
||||
};
|
||||
|
||||
struct io4_ops {
|
||||
HRESULT (*poll)(void *ctx, struct io4_state *state);
|
||||
};
|
||||
|
||||
HRESULT io4_hook_init(
|
||||
const struct io4_config *cfg,
|
||||
const struct io4_ops *ops,
|
||||
void *ctx);
|
||||
+20
-2
@@ -13,6 +13,8 @@
|
||||
#include "jvs/jvs-cmd.h"
|
||||
#include "jvs/jvs-util.h"
|
||||
|
||||
#include "iccard/nesica.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
@@ -84,6 +86,8 @@ void k92x0249_init(
|
||||
k92x0249->addr = 0xFF;
|
||||
k92x0249->state = IDLE;
|
||||
k92x0249->ops_ctx = ops_ctx;
|
||||
|
||||
nesica_card_populate(&k92x0249->mifare, cfg.serial, 16);
|
||||
}
|
||||
|
||||
struct jvs_node *k92x0249_to_jvs_node(struct k92x0249 *k92x0249)
|
||||
@@ -411,13 +415,27 @@ static HRESULT k92x0249_cmd_taito_nesica_read(struct k92x0249 *k92x0249, struct
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = iobuf_write(resp_buf, card_uid, 8); // AICC: Unused, should be IDm
|
||||
// Mifare: UID (3 bytes from page 1, 4 bytes from page 2, null byte)
|
||||
hr = iobuf_write(resp_buf, k92x0249->mifare.pages[0].bytes, 3); // AICC: Unused, should be IDm
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = iobuf_write(resp_buf, k92x0249->mifare.pages[1].bytes, 4); // AICC: Unused, should be IDm
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = iobuf_write_8(resp_buf, 0x00); // AICC: Unused, should be IDm
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return iobuf_write(resp_buf, cfg.serial, 16); // AICC: SPAD0
|
||||
// Mifare: sn (pages 5-8)
|
||||
return iobuf_write(resp_buf, k92x0249->mifare.pages[5].bytes, 16); // AICC: SPAD0
|
||||
}
|
||||
|
||||
static HRESULT k92x0249_cmd_taito_aic_read(struct k92x0249 *k92x0249, struct const_iobuf *req_buf, struct iobuf *resp_buf)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "jvs/jvs-bus.h"
|
||||
#include "iccard/mifare.h"
|
||||
#include "iccard/felica.h"
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct k92x0249 {
|
||||
@@ -9,6 +11,8 @@ struct k92x0249 {
|
||||
uint8_t state;
|
||||
uint8_t addr;
|
||||
void *ops_ctx;
|
||||
struct mifare_ul mifare;
|
||||
struct felica felica;
|
||||
};
|
||||
|
||||
struct k92x0249_config {
|
||||
|
||||
@@ -22,17 +22,5 @@ board_lib = static_library(
|
||||
'k92x0249.h',
|
||||
'k9101258.c',
|
||||
'k9101258.h',
|
||||
'io4.c',
|
||||
'io4.h',
|
||||
'sg-cmd.c',
|
||||
'sg-cmd.h',
|
||||
'sg-frame.c',
|
||||
'sg-frame.h',
|
||||
'sg-led.c',
|
||||
'sg-led.h',
|
||||
'sg-led-cmd.h',
|
||||
'sg-nfc.c',
|
||||
'sg-nfc.h',
|
||||
'sg-nfc-cmd.h',
|
||||
],
|
||||
)
|
||||
|
||||
-134
@@ -1,134 +0,0 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
#include "board/sg-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
union sg_req_any {
|
||||
struct sg_req_header req;
|
||||
uint8_t bytes[256];
|
||||
};
|
||||
|
||||
union sg_res_any {
|
||||
struct sg_res_header res;
|
||||
uint8_t bytes[256];
|
||||
};
|
||||
|
||||
static HRESULT sg_req_validate(const void *ptr, size_t nbytes);
|
||||
|
||||
static void sg_res_error(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req);
|
||||
|
||||
static HRESULT sg_req_validate(const void *ptr, size_t nbytes)
|
||||
{
|
||||
const struct sg_req_header *req;
|
||||
size_t payload_len;
|
||||
|
||||
assert(ptr != NULL);
|
||||
|
||||
if (nbytes < sizeof(*req)) {
|
||||
dprintf("SG Cmd: Request header truncated\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
req = ptr;
|
||||
|
||||
if (req->hdr.frame_len != nbytes) {
|
||||
dprintf("SG Cmd: Frame length mismatch: got %i exp %i\n",
|
||||
req->hdr.frame_len,
|
||||
(int) nbytes);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
payload_len = req->hdr.frame_len - sizeof(*req);
|
||||
|
||||
if (req->payload_len != payload_len) {
|
||||
dprintf("SG Cmd: Payload length mismatch: got %i exp %i\n",
|
||||
req->payload_len,
|
||||
(int) payload_len);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void sg_req_transact(
|
||||
struct iobuf *res_frame,
|
||||
const uint8_t *req_bytes,
|
||||
size_t req_nbytes,
|
||||
sg_dispatch_fn_t dispatch,
|
||||
void *ctx)
|
||||
{
|
||||
struct iobuf req_span;
|
||||
union sg_req_any req;
|
||||
union sg_res_any res;
|
||||
HRESULT hr;
|
||||
|
||||
assert(res_frame != NULL);
|
||||
assert(req_bytes != NULL);
|
||||
assert(dispatch != NULL);
|
||||
|
||||
req_span.bytes = req.bytes;
|
||||
req_span.nbytes = sizeof(req.bytes);
|
||||
req_span.pos = 0;
|
||||
|
||||
hr = sg_frame_decode(&req_span, req_bytes, req_nbytes);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = sg_req_validate(req.bytes, req_span.pos);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
hr = dispatch(ctx, &req, &res);
|
||||
|
||||
if (hr != S_FALSE) {
|
||||
if (FAILED(hr)) {
|
||||
sg_res_error(&res.res, &req.req);
|
||||
}
|
||||
|
||||
sg_frame_encode(res_frame, res.bytes, res.res.hdr.frame_len);
|
||||
}
|
||||
}
|
||||
|
||||
void sg_res_init(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req,
|
||||
size_t payload_len)
|
||||
{
|
||||
assert(res != NULL);
|
||||
assert(req != NULL);
|
||||
|
||||
res->hdr.frame_len = sizeof(*res) + payload_len;
|
||||
res->hdr.addr = req->hdr.addr;
|
||||
res->hdr.seq_no = req->hdr.seq_no;
|
||||
res->hdr.cmd = req->hdr.cmd;
|
||||
res->status = 0;
|
||||
res->payload_len = payload_len;
|
||||
}
|
||||
|
||||
static void sg_res_error(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req)
|
||||
{
|
||||
assert(res != NULL);
|
||||
assert(req != NULL);
|
||||
|
||||
res->hdr.frame_len = sizeof(*res);
|
||||
res->hdr.addr = req->hdr.addr;
|
||||
res->hdr.seq_no = req->hdr.seq_no;
|
||||
res->hdr.cmd = req->hdr.cmd;
|
||||
res->status = 1;
|
||||
res->payload_len = 0;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
struct sg_header {
|
||||
uint8_t frame_len;
|
||||
uint8_t addr;
|
||||
uint8_t seq_no;
|
||||
uint8_t cmd;
|
||||
};
|
||||
|
||||
struct sg_req_header {
|
||||
struct sg_header hdr;
|
||||
uint8_t payload_len;
|
||||
};
|
||||
|
||||
struct sg_res_header {
|
||||
struct sg_header hdr;
|
||||
uint8_t status;
|
||||
uint8_t payload_len;
|
||||
};
|
||||
|
||||
typedef HRESULT (*sg_dispatch_fn_t)(
|
||||
void *ctx,
|
||||
const void *req,
|
||||
void *res);
|
||||
|
||||
void sg_req_transact(
|
||||
struct iobuf *res_frame,
|
||||
const uint8_t *req_bytes,
|
||||
size_t req_nbytes,
|
||||
sg_dispatch_fn_t dispatch,
|
||||
void *ctx);
|
||||
|
||||
void sg_res_init(
|
||||
struct sg_res_header *res,
|
||||
const struct sg_req_header *req,
|
||||
size_t payload_len);
|
||||
@@ -1,165 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/sg-frame.h"
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT sg_frame_accept(struct iobuf *dest);
|
||||
static HRESULT sg_frame_encode_byte(struct iobuf *dest, uint8_t byte);
|
||||
|
||||
/* Frame structure:
|
||||
|
||||
[0] Sync byte (0xE0)
|
||||
[1] Frame size (including self)
|
||||
[2] Address
|
||||
[3] Sequence no
|
||||
... Body
|
||||
[n] Checksum: Sum of all non-framing bytes
|
||||
|
||||
Byte stuffing:
|
||||
|
||||
0xD0 is an escape byte. Un-escape the subsequent byte by adding 1. */
|
||||
|
||||
static HRESULT sg_frame_accept(struct iobuf *dest)
|
||||
{
|
||||
uint8_t checksum;
|
||||
size_t i;
|
||||
|
||||
if (dest->pos < 1 || dest->pos != dest->bytes[0] + 1) {
|
||||
dprintf("SG Frame: Size mismatch\n");
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
checksum = 0;
|
||||
|
||||
for (i = 0 ; i < dest->pos - 1 ; i++) {
|
||||
checksum += dest->bytes[i];
|
||||
}
|
||||
|
||||
if (checksum != dest->bytes[dest->pos - 1]) {
|
||||
dprintf("SG Frame: Checksum mismatch\n");
|
||||
|
||||
return HRESULT_FROM_WIN32(ERROR_CRC);
|
||||
}
|
||||
|
||||
/* Discard checksum */
|
||||
dest->pos--;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT sg_frame_decode(struct iobuf *dest, const uint8_t *bytes, size_t nbytes)
|
||||
{
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(bytes != NULL);
|
||||
|
||||
if (nbytes < 1 || bytes[0] != 0xE0) {
|
||||
dprintf("SG Frame: Bad sync\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
dest->pos = 0;
|
||||
i = 1;
|
||||
|
||||
while (i < nbytes) {
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
byte = bytes[i++];
|
||||
|
||||
if (byte == 0xE0) {
|
||||
dprintf("SG Frame: Unescaped sync\n");
|
||||
|
||||
return E_FAIL;
|
||||
} else if (byte == 0xD0) {
|
||||
if (i >= nbytes) {
|
||||
dprintf("SG Frame: Trailing escape\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
byte = bytes[i++];
|
||||
dest->bytes[dest->pos++] = byte + 1;
|
||||
} else {
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
}
|
||||
|
||||
return sg_frame_accept(dest);
|
||||
}
|
||||
|
||||
HRESULT sg_frame_encode(
|
||||
struct iobuf *dest,
|
||||
const void *ptr,
|
||||
size_t nbytes)
|
||||
{
|
||||
const uint8_t *src;
|
||||
uint8_t checksum;
|
||||
uint8_t byte;
|
||||
size_t i;
|
||||
HRESULT hr;
|
||||
|
||||
assert(dest != NULL);
|
||||
assert(dest->bytes != NULL || dest->nbytes == 0);
|
||||
assert(dest->pos <= dest->nbytes);
|
||||
assert(ptr != NULL);
|
||||
|
||||
src = ptr;
|
||||
|
||||
assert(nbytes != 0 && src[0] == nbytes);
|
||||
|
||||
if (dest->pos >= dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xE0;
|
||||
checksum = 0;
|
||||
|
||||
for (i = 0 ; i < nbytes ; i++) {
|
||||
byte = src[i];
|
||||
checksum += byte;
|
||||
|
||||
hr = sg_frame_encode_byte(dest, byte);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
return sg_frame_encode_byte(dest, checksum);
|
||||
}
|
||||
|
||||
static HRESULT sg_frame_encode_byte(struct iobuf *dest, uint8_t byte)
|
||||
{
|
||||
if (byte == 0xD0 || byte == 0xE0) {
|
||||
if (dest->pos + 2 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = 0xD0;
|
||||
dest->bytes[dest->pos++] = byte - 1;
|
||||
} else {
|
||||
if (dest->pos + 1 > dest->nbytes) {
|
||||
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
dest->bytes[dest->pos++] = byte;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
HRESULT sg_frame_decode(
|
||||
struct iobuf *dest,
|
||||
const uint8_t *bytes,
|
||||
size_t nbytes);
|
||||
|
||||
HRESULT sg_frame_encode(struct iobuf *dest, const void *ptr, size_t nbytes);
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
|
||||
enum {
|
||||
SG_RGB_CMD_SET_COLOR = 0x81,
|
||||
SG_RGB_CMD_RESET = 0xF5,
|
||||
SG_RGB_CMD_GET_INFO = 0xF0,
|
||||
};
|
||||
|
||||
struct sg_led_res_reset {
|
||||
struct sg_res_header res;
|
||||
uint8_t payload;
|
||||
};
|
||||
|
||||
struct sg_led_res_get_info {
|
||||
struct sg_res_header res;
|
||||
uint8_t payload[9];
|
||||
};
|
||||
|
||||
struct sg_led_req_set_color {
|
||||
struct sg_req_header req;
|
||||
uint8_t payload[3];
|
||||
};
|
||||
|
||||
union sg_led_req_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_req_header simple;
|
||||
struct sg_led_req_set_color set_color;
|
||||
};
|
||||
|
||||
union sg_led_res_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_res_header simple;
|
||||
struct sg_led_res_reset reset;
|
||||
struct sg_led_res_get_info get_info;
|
||||
};
|
||||
-178
@@ -1,178 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
#include "board/sg-led.h"
|
||||
#include "board/sg-led-cmd.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
static HRESULT sg_led_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res);
|
||||
|
||||
static HRESULT sg_led_cmd_reset(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_reset *res);
|
||||
|
||||
static HRESULT sg_led_cmd_get_info(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_get_info *res);
|
||||
|
||||
static HRESULT sg_led_cmd_set_color(
|
||||
const struct sg_led *led,
|
||||
const struct sg_led_req_set_color *req);
|
||||
|
||||
static const uint8_t sg_led_info[] = {
|
||||
'1', '5', '0', '8', '4', 0xFF, 0x10, 0x00, 0x12,
|
||||
};
|
||||
|
||||
void sg_led_init(
|
||||
struct sg_led *led,
|
||||
uint8_t addr,
|
||||
const struct sg_led_ops *ops,
|
||||
void *ctx)
|
||||
{
|
||||
assert(led != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
led->ops = ops;
|
||||
led->ops_ctx = ctx;
|
||||
led->addr = addr;
|
||||
}
|
||||
|
||||
void sg_led_transact(
|
||||
struct sg_led *led,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes)
|
||||
{
|
||||
assert(led != NULL);
|
||||
assert(res_frame != NULL);
|
||||
assert(req_bytes != NULL);
|
||||
|
||||
sg_req_transact(res_frame, req_bytes, req_nbytes, sg_led_dispatch, led);
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define sg_led_dprintfv(led, fmt, ap)
|
||||
#define sg_led_dprintf(led, fmt, ...)
|
||||
#else
|
||||
static void sg_led_dprintfv(
|
||||
const struct sg_led *led,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
dprintf("RGB LED %02x: ", led->addr);
|
||||
dprintfv(fmt, ap);
|
||||
}
|
||||
|
||||
static void sg_led_dprintf(
|
||||
const struct sg_led *led,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
sg_led_dprintfv(led, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
static HRESULT sg_led_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res)
|
||||
{
|
||||
const struct sg_led *led;
|
||||
const union sg_led_req_any *req;
|
||||
union sg_led_res_any *res;
|
||||
|
||||
led = ctx;
|
||||
req = v_req;
|
||||
res = v_res;
|
||||
|
||||
if (req->simple.hdr.addr != led->addr) {
|
||||
/* Not addressed to us, don't send a response */
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
switch (req->simple.hdr.cmd) {
|
||||
case SG_RGB_CMD_RESET:
|
||||
return sg_led_cmd_reset(led, &req->simple, &res->reset);
|
||||
|
||||
case SG_RGB_CMD_GET_INFO:
|
||||
return sg_led_cmd_get_info(led, &req->simple, &res->get_info);
|
||||
|
||||
case SG_RGB_CMD_SET_COLOR:
|
||||
return sg_led_cmd_set_color(led, &req->set_color);
|
||||
|
||||
default:
|
||||
sg_led_dprintf(led, "Unimpl command %02x\n", req->simple.hdr.cmd);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT sg_led_cmd_reset(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_reset *res)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
sg_led_dprintf(led, "Reset\n");
|
||||
sg_res_init(&res->res, req, sizeof(res->payload));
|
||||
res->payload = 0;
|
||||
|
||||
if (led->ops->reset != NULL) {
|
||||
hr = led->ops->reset(led->ops_ctx);
|
||||
} else {
|
||||
hr = S_OK;
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
sg_led_dprintf(led, "led->ops->reset: Error %x\n", hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_led_cmd_get_info(
|
||||
const struct sg_led *led,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_led_res_get_info *res)
|
||||
{
|
||||
sg_led_dprintf(led, "Get info\n");
|
||||
sg_res_init(&res->res, req, sizeof(res->payload));
|
||||
memcpy(res->payload, sg_led_info, sizeof(sg_led_info));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_led_cmd_set_color(
|
||||
const struct sg_led *led,
|
||||
const struct sg_led_req_set_color *req)
|
||||
{
|
||||
if (req->req.payload_len != sizeof(req->payload)) {
|
||||
sg_led_dprintf(led, "%s: Payload size is incorrect\n", __func__);
|
||||
|
||||
goto fail;
|
||||
}
|
||||
|
||||
led->ops->set_color(
|
||||
led->ops_ctx,
|
||||
req->payload[0],
|
||||
req->payload[1],
|
||||
req->payload[2]);
|
||||
|
||||
fail:
|
||||
/* No response */
|
||||
return S_FALSE;
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
struct sg_led_ops {
|
||||
HRESULT (*reset)(void *ctx);
|
||||
void (*set_color)(void *ctx, uint8_t r, uint8_t g, uint8_t b);
|
||||
};
|
||||
|
||||
struct sg_led {
|
||||
const struct sg_led_ops *ops;
|
||||
void *ops_ctx;
|
||||
uint8_t addr;
|
||||
};
|
||||
|
||||
void sg_led_init(
|
||||
struct sg_led *led,
|
||||
uint8_t addr,
|
||||
const struct sg_led_ops *ops,
|
||||
void *ctx);
|
||||
|
||||
void sg_led_transact(
|
||||
struct sg_led *led,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes);
|
||||
@@ -1,115 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
enum {
|
||||
SG_NFC_CMD_GET_FW_VERSION = 0x30,
|
||||
SG_NFC_CMD_GET_HW_VERSION = 0x32,
|
||||
SG_NFC_CMD_RADIO_ON = 0x40,
|
||||
SG_NFC_CMD_RADIO_OFF = 0x41,
|
||||
SG_NFC_CMD_POLL = 0x42,
|
||||
SG_NFC_CMD_MIFARE_SELECT_TAG = 0x43,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_BANA = 0x50,
|
||||
SG_NFC_CMD_MIFARE_READ_BLOCK = 0x52,
|
||||
SG_NFC_CMD_MIFARE_SET_KEY_AIME = 0x54,
|
||||
SG_NFC_CMD_MIFARE_AUTHENTICATE = 0x55, /* guess based on time sent */
|
||||
SG_NFC_CMD_RESET = 0x62,
|
||||
SG_NFC_CMD_FELICA_ENCAP = 0x71,
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_fw_version {
|
||||
struct sg_res_header res;
|
||||
char version[23];
|
||||
};
|
||||
|
||||
struct sg_nfc_res_get_hw_version {
|
||||
struct sg_res_header res;
|
||||
char version[23];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_set_key {
|
||||
struct sg_req_header req;
|
||||
uint8_t key_a[6];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_50 {
|
||||
struct sg_req_header req;
|
||||
uint8_t payload[6];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_poll_40 {
|
||||
struct sg_req_header req;
|
||||
uint8_t payload;
|
||||
};
|
||||
|
||||
struct sg_nfc_poll_mifare {
|
||||
uint8_t type;
|
||||
uint8_t id_len;
|
||||
uint32_t uid;
|
||||
};
|
||||
|
||||
struct sg_nfc_poll_felica {
|
||||
uint8_t type;
|
||||
uint8_t id_len;
|
||||
uint64_t IDm;
|
||||
uint64_t PMm;
|
||||
};
|
||||
|
||||
struct sg_nfc_res_poll {
|
||||
struct sg_res_header res;
|
||||
uint8_t count;
|
||||
uint8_t payload[250];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_select_tag {
|
||||
struct sg_res_header res;
|
||||
uint32_t uid;
|
||||
};
|
||||
|
||||
struct sg_nfc_req_mifare_read_block {
|
||||
struct sg_req_header req;
|
||||
struct {
|
||||
uint32_t uid;
|
||||
uint8_t block_no;
|
||||
} payload;
|
||||
};
|
||||
|
||||
struct sg_nfc_res_mifare_read_block {
|
||||
struct sg_res_header res;
|
||||
uint8_t block[16];
|
||||
};
|
||||
|
||||
struct sg_nfc_req_felica_encap {
|
||||
struct sg_req_header req;
|
||||
uint64_t IDm;
|
||||
uint8_t payload[243];
|
||||
};
|
||||
|
||||
struct sg_nfc_res_felica_encap {
|
||||
struct sg_res_header res;
|
||||
uint8_t payload[250];
|
||||
};
|
||||
|
||||
union sg_nfc_req_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_req_header simple;
|
||||
struct sg_nfc_req_mifare_set_key mifare_set_key;
|
||||
struct sg_nfc_req_mifare_read_block mifare_read_block;
|
||||
struct sg_nfc_req_mifare_50 mifare_50;
|
||||
struct sg_nfc_req_poll_40 poll_40;
|
||||
struct sg_nfc_req_felica_encap felica_encap;
|
||||
};
|
||||
|
||||
union sg_nfc_res_any {
|
||||
uint8_t bytes[256];
|
||||
struct sg_res_header simple;
|
||||
struct sg_nfc_res_get_fw_version get_fw_version;
|
||||
struct sg_nfc_res_get_hw_version get_hw_version;
|
||||
struct sg_nfc_res_poll poll;
|
||||
struct sg_nfc_res_mifare_read_block mifare_read_block;
|
||||
struct sg_nfc_res_felica_encap felica_encap;
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
-434
@@ -1,434 +0,0 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "board/sg-cmd.h"
|
||||
#include "board/sg-nfc.h"
|
||||
#include "board/sg-nfc-cmd.h"
|
||||
|
||||
#include "iccard/nesica.h"
|
||||
#include "iccard/felica.h"
|
||||
|
||||
#include "util/dprintf.h"
|
||||
#include "util/dump.h"
|
||||
|
||||
static HRESULT sg_nfc_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_reset(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_fw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_fw_version *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_hw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_hw_version *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_poll(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_poll *res);
|
||||
|
||||
static HRESULT sg_nfc_poll_aime(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_mifare *mifare);
|
||||
|
||||
static HRESULT sg_nfc_poll_felica(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_felica *felica);
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_mifare_read_block *req,
|
||||
struct sg_nfc_res_mifare_read_block *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_felica_encap(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_felica_encap *req,
|
||||
struct sg_nfc_res_felica_encap *res);
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res);
|
||||
|
||||
void sg_nfc_init(
|
||||
struct sg_nfc *nfc,
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
void *ops_ctx)
|
||||
{
|
||||
assert(nfc != NULL);
|
||||
assert(ops != NULL);
|
||||
|
||||
nfc->ops = ops;
|
||||
nfc->ops_ctx = ops_ctx;
|
||||
nfc->addr = addr;
|
||||
}
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define sg_nfc_dprintfv(nfc, fmt, ap)
|
||||
#define sg_nfc_dprintf(nfc, fmt, ...)
|
||||
#else
|
||||
static void sg_nfc_dprintfv(
|
||||
const struct sg_nfc *nfc,
|
||||
const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
dprintf("NFC %02x: ", nfc->addr);
|
||||
dprintfv(fmt, ap);
|
||||
}
|
||||
|
||||
static void sg_nfc_dprintf(
|
||||
const struct sg_nfc *nfc,
|
||||
const char *fmt,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
sg_nfc_dprintfv(nfc, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
#endif
|
||||
|
||||
void sg_nfc_transact(
|
||||
struct sg_nfc *nfc,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes)
|
||||
{
|
||||
assert(nfc != NULL);
|
||||
assert(res_frame != NULL);
|
||||
assert(req_bytes != NULL);
|
||||
|
||||
sg_req_transact(res_frame, req_bytes, req_nbytes, sg_nfc_dispatch, nfc);
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_dispatch(
|
||||
void *ctx,
|
||||
const void *v_req,
|
||||
void *v_res)
|
||||
{
|
||||
struct sg_nfc *nfc;
|
||||
const union sg_nfc_req_any *req;
|
||||
union sg_nfc_res_any *res;
|
||||
|
||||
nfc = ctx;
|
||||
req = v_req;
|
||||
res = v_res;
|
||||
|
||||
if (req->simple.hdr.addr != nfc->addr) {
|
||||
/* Not addressed to us, don't send a response */
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
switch (req->simple.hdr.cmd) {
|
||||
case SG_NFC_CMD_RESET:
|
||||
return sg_nfc_cmd_reset(nfc, &req->simple, &res->simple);
|
||||
|
||||
case SG_NFC_CMD_GET_FW_VERSION:
|
||||
return sg_nfc_cmd_get_fw_version(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->get_fw_version);
|
||||
|
||||
case SG_NFC_CMD_GET_HW_VERSION:
|
||||
return sg_nfc_cmd_get_hw_version(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->get_hw_version);
|
||||
|
||||
case SG_NFC_CMD_POLL:
|
||||
return sg_nfc_cmd_poll(
|
||||
nfc,
|
||||
&req->simple,
|
||||
&res->poll);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_READ_BLOCK:
|
||||
return sg_nfc_cmd_mifare_read_block(
|
||||
nfc,
|
||||
&req->mifare_read_block,
|
||||
&res->mifare_read_block);
|
||||
|
||||
case SG_NFC_CMD_FELICA_ENCAP:
|
||||
return sg_nfc_cmd_felica_encap(
|
||||
nfc,
|
||||
&req->felica_encap,
|
||||
&res->felica_encap);
|
||||
|
||||
case SG_NFC_CMD_MIFARE_AUTHENTICATE:
|
||||
case SG_NFC_CMD_MIFARE_SELECT_TAG:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_AIME:
|
||||
case SG_NFC_CMD_MIFARE_SET_KEY_BANA:
|
||||
case SG_NFC_CMD_RADIO_ON:
|
||||
case SG_NFC_CMD_RADIO_OFF:
|
||||
return sg_nfc_cmd_dummy(nfc, &req->simple, &res->simple);
|
||||
|
||||
default:
|
||||
sg_nfc_dprintf(nfc, "Unimpl command %02x\n", req->simple.hdr.cmd);
|
||||
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_reset(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_nfc_dprintf(nfc, "Reset\n");
|
||||
sg_res_init(res, req, 0);
|
||||
res->status = 3;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_fw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_fw_version *res)
|
||||
{
|
||||
/* Dest version is not NUL terminated, this is intentional */
|
||||
sg_res_init(&res->res, req, sizeof(res->version));
|
||||
memcpy(res->version, "TN32MSEC003S F/W Ver1.2E", sizeof(res->version));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_get_hw_version(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_get_hw_version *res)
|
||||
{
|
||||
/* Dest version is not NUL terminated, this is intentional */
|
||||
sg_res_init(&res->res, req, sizeof(res->version));
|
||||
memcpy(res->version, "TN32MSEC003S H/W Ver3.0J", sizeof(res->version));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_poll(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_nfc_res_poll *res)
|
||||
{
|
||||
struct sg_nfc_poll_mifare mifare;
|
||||
struct sg_nfc_poll_felica felica;
|
||||
HRESULT hr;
|
||||
|
||||
hr = nfc->ops->poll(nfc->ops_ctx);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
hr = sg_nfc_poll_felica(nfc, &felica);
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
sg_res_init(&res->res, req, 1 + sizeof(felica));
|
||||
memcpy(res->payload, &felica, sizeof(felica));
|
||||
res->count = 1;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
hr = sg_nfc_poll_aime(nfc, &mifare);
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE) {
|
||||
sg_res_init(&res->res, req, 1 + sizeof(mifare));
|
||||
memcpy(res->payload, &mifare, sizeof(mifare));
|
||||
res->count = 1;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, req, 1);
|
||||
res->count = 0;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_poll_aime(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_mifare *mifare)
|
||||
{
|
||||
uint8_t luid[10];
|
||||
HRESULT hr;
|
||||
|
||||
/* Call backend */
|
||||
|
||||
if (nfc->ops->get_aime_id != NULL) {
|
||||
hr = nfc->ops->get_aime_id(nfc->ops_ctx, luid, sizeof(luid));
|
||||
} else {
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
if (FAILED(hr) || hr == S_FALSE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sg_nfc_dprintf(nfc, "AiMe card is present\n");
|
||||
|
||||
/* Construct response (use an arbitrary UID) */
|
||||
|
||||
mifare->type = 0x10;
|
||||
mifare->id_len = sizeof(mifare->uid);
|
||||
mifare->uid = _byteswap_ulong(0x01020304);
|
||||
|
||||
/* Initialize MIFARE IC emulator */
|
||||
|
||||
hr = aime_card_populate(&nfc->mifare, luid, sizeof(luid));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_poll_felica(
|
||||
struct sg_nfc *nfc,
|
||||
struct sg_nfc_poll_felica *felica)
|
||||
{
|
||||
uint64_t IDm;
|
||||
HRESULT hr;
|
||||
|
||||
/* Call backend */
|
||||
|
||||
if (nfc->ops->get_felica_id != NULL) {
|
||||
hr = nfc->ops->get_felica_id(nfc->ops_ctx, &IDm);
|
||||
} else {
|
||||
hr = S_FALSE;
|
||||
}
|
||||
|
||||
if (FAILED(hr) || hr == S_FALSE) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sg_nfc_dprintf(nfc, "FeliCa card is present\n");
|
||||
|
||||
/* Construct poll response */
|
||||
|
||||
felica->type = 0x20;
|
||||
felica->id_len = sizeof(felica->IDm) + sizeof(felica->PMm);
|
||||
felica->IDm = _byteswap_uint64(IDm);
|
||||
felica->PMm = _byteswap_uint64(felica_get_generic_PMm());
|
||||
|
||||
/* Initialize FeliCa IC emulator */
|
||||
|
||||
nfc->felica.IDm = IDm;
|
||||
nfc->felica.PMm = felica_get_generic_PMm();
|
||||
nfc->felica.system_code = 0x0000;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_mifare_read_block(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_mifare_read_block *req,
|
||||
struct sg_nfc_res_mifare_read_block *res)
|
||||
{
|
||||
uint32_t uid;
|
||||
|
||||
if (req->req.payload_len != sizeof(req->payload)) {
|
||||
sg_nfc_dprintf(nfc, "%s: Payload size is incorrect\n", __func__);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
uid = _byteswap_ulong(req->payload.uid);
|
||||
|
||||
sg_nfc_dprintf(nfc, "Read uid %08x block %i\n", uid, req->payload.block_no);
|
||||
|
||||
if (req->payload.block_no > 3) {
|
||||
sg_nfc_dprintf(nfc, "MIFARE block number out of range\n");
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, &req->req, sizeof(res->block));
|
||||
|
||||
memcpy( res->block,
|
||||
nfc->mifare.sectors[0].blocks[req->payload.block_no].bytes,
|
||||
sizeof(res->block));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_felica_encap(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_nfc_req_felica_encap *req,
|
||||
struct sg_nfc_res_felica_encap *res)
|
||||
{
|
||||
struct const_iobuf f_req;
|
||||
struct iobuf f_res;
|
||||
HRESULT hr;
|
||||
|
||||
/* First byte of encapsulated request and response is a length byte
|
||||
(inclusive of itself). The FeliCa emulator expects its caller to handle
|
||||
that length byte on its behalf (we adopt the convention that the length
|
||||
byte is part of the FeliCa protocol's framing layer). */
|
||||
|
||||
if (req->req.payload_len != 8 + req->payload[0]) {
|
||||
sg_nfc_dprintf(
|
||||
nfc,
|
||||
"FeliCa encap payload length mismatch: sg %i != felica %i + 8",
|
||||
req->req.payload_len,
|
||||
req->payload[0]);
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
f_req.bytes = req->payload;
|
||||
f_req.nbytes = req->payload[0];
|
||||
f_req.pos = 1;
|
||||
|
||||
f_res.bytes = res->payload;
|
||||
f_res.nbytes = sizeof(res->payload);
|
||||
f_res.pos = 1;
|
||||
|
||||
#if 0
|
||||
dprintf("FELICA OUTBOUND:\n");
|
||||
dump_const_iobuf(&f_req);
|
||||
#endif
|
||||
|
||||
hr = felica_transact(&nfc->felica, &f_req, &f_res);
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
}
|
||||
|
||||
sg_res_init(&res->res, &req->req, f_res.pos);
|
||||
res->payload[0] = f_res.pos;
|
||||
|
||||
#if 0
|
||||
dprintf("FELICA INBOUND:\n");
|
||||
dump_iobuf(&f_res);
|
||||
#endif
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static HRESULT sg_nfc_cmd_dummy(
|
||||
struct sg_nfc *nfc,
|
||||
const struct sg_req_header *req,
|
||||
struct sg_res_header *res)
|
||||
{
|
||||
sg_res_init(res, req, 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "hook/iobuf.h"
|
||||
|
||||
#include "iccard/felica.h"
|
||||
#include "iccard/mifare.h"
|
||||
|
||||
struct sg_nfc_ops {
|
||||
HRESULT (*poll)(void *ctx);
|
||||
HRESULT (*get_aime_id)(void *ctx, uint8_t *luid, size_t nbytes);
|
||||
HRESULT (*get_felica_id)(void *ctx, uint64_t *IDm);
|
||||
|
||||
// TODO Banapass, AmuseIC
|
||||
};
|
||||
|
||||
struct sg_nfc {
|
||||
const struct sg_nfc_ops *ops;
|
||||
void *ops_ctx;
|
||||
uint8_t addr;
|
||||
struct felica felica;
|
||||
struct mifare mifare;
|
||||
};
|
||||
|
||||
void sg_nfc_init(
|
||||
struct sg_nfc *nfc,
|
||||
uint8_t addr,
|
||||
const struct sg_nfc_ops *ops,
|
||||
void *ops_ctx);
|
||||
|
||||
void sg_nfc_transact(
|
||||
struct sg_nfc *nfc,
|
||||
struct iobuf *res_frame,
|
||||
const void *req_bytes,
|
||||
size_t req_nbytes);
|
||||
@@ -2,8 +2,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "hook/process.h"
|
||||
|
||||
#include "hooklib/serial.h"
|
||||
|
||||
@@ -14,6 +14,7 @@ static uint16_t coins;
|
||||
static uint16_t services;
|
||||
|
||||
static void gc_fastio_digital_read(void *ctx, uint32_t *out);
|
||||
static void gc_fastio_analog_read(void* ctx, uint8_t num, uint32_t *out);
|
||||
static void gc_fastio_coin_read(void *ctx, uint8_t slot_no, uint32_t *out);
|
||||
|
||||
HRESULT gc_fastio_init(struct fastio_node *out)
|
||||
@@ -24,6 +25,7 @@ HRESULT gc_fastio_init(struct fastio_node *out)
|
||||
|
||||
out->digital_in = gc_fastio_digital_read;
|
||||
out->coins_in = gc_fastio_coin_read;
|
||||
out->analog_in = gc_fastio_analog_read;
|
||||
|
||||
dprintf("Gc Nesica Fastio: Init\n");
|
||||
|
||||
@@ -116,3 +118,9 @@ static void gc_fastio_coin_read(void *ctx, uint8_t slot_no, uint32_t *out)
|
||||
*out += coin;
|
||||
*out += service;
|
||||
}
|
||||
|
||||
static void gc_fastio_analog_read(void* ctx, uint8_t num, uint32_t *out)
|
||||
{
|
||||
if (num == 2) { *out = 0xff; }
|
||||
else { *out = 0x00; }
|
||||
}
|
||||
|
||||
+1
-1
@@ -30,4 +30,4 @@ void gc_io_config_load(struct gc_input_config *cfg, const wchar_t *filename)
|
||||
cfg->stick_r_right = GetPrivateProfileIntW(L"deck", L"right_stick_right", 'L', filename);
|
||||
cfg->stick_r_down = GetPrivateProfileIntW(L"deck", L"right_stick_down", 'K', filename);
|
||||
cfg->stick_r_left = GetPrivateProfileIntW(L"deck", L"right_stick_left", 'J', filename);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,4 +28,4 @@ struct gc_input_config {
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
void gc_io_config_load(struct gc_input_config *cfg, const wchar_t *filename);
|
||||
void gc_io_config_load(struct gc_input_config *cfg, const wchar_t *filename);
|
||||
|
||||
+1
-1
@@ -56,4 +56,4 @@ HRESULT gc_io_init(void);
|
||||
|
||||
void gc_io_get_btns(uint8_t *btn, uint8_t *stick);
|
||||
|
||||
void gc_io_read_coin_counter(uint16_t *coins, uint16_t *services);
|
||||
void gc_io_read_coin_counter(uint16_t *coins, uint16_t *services);
|
||||
|
||||
@@ -15,6 +15,14 @@ struct mifare {
|
||||
struct mifare_sector sectors[16];
|
||||
};
|
||||
|
||||
struct mifare_ul_page {
|
||||
uint8_t bytes[4];
|
||||
};
|
||||
|
||||
struct mifare_ul {
|
||||
struct mifare_ul_page pages[16];
|
||||
};
|
||||
|
||||
struct nesica_mifare {
|
||||
uint8_t uid[7];
|
||||
char nesica_id[16];
|
||||
|
||||
+38
-13
@@ -7,8 +7,8 @@
|
||||
|
||||
#include "util/dprintf.h"
|
||||
|
||||
HRESULT aime_card_populate(
|
||||
struct mifare *mifare,
|
||||
HRESULT nesica_card_populate(
|
||||
struct mifare_ul *mifare,
|
||||
const uint8_t *card_sn,
|
||||
size_t nbytes)
|
||||
{
|
||||
@@ -18,7 +18,7 @@ HRESULT aime_card_populate(
|
||||
assert(mifare != NULL);
|
||||
assert(card_sn != NULL);
|
||||
|
||||
memset(mifare, 0, sizeof(*mifare));
|
||||
memset(mifare, 0xFF, sizeof(*mifare));
|
||||
|
||||
if (nbytes != 16) {
|
||||
dprintf("Nesica IC: Card Serial must be 16 characters\n");
|
||||
@@ -26,19 +26,44 @@ HRESULT aime_card_populate(
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
mifare->sectors[0].blocks[0].bytes[i] = i;
|
||||
}
|
||||
// page0: first 3 bytes of the UID, BCC0
|
||||
// page1: rest of the UID
|
||||
// page2: BCC1, INT, LOCK0+1
|
||||
// page3: 4 one-time programmable bytes
|
||||
// page4: "T053" (T<3 numbers of unknown purpose>)
|
||||
// page5-8: 16-byte ascii nesica ID
|
||||
// page9-f: FF
|
||||
|
||||
memset(mifare->sectors[0].blocks[2].bytes, 0xff, 16);
|
||||
memset(mifare->sectors[0].blocks[3].bytes, 0xff, 16);
|
||||
// https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf
|
||||
// BCC0 is CT (0x88) ^ UIC[0] ^ UIC[1] ^ UIC[2]
|
||||
// BCC1 is UIC[3] ^ UIC[4] ^ UIC[5] ^ UIC[6]
|
||||
// Lock bits signal which parts of the card are write-locked
|
||||
// On real blue nesicas, OTP and pages 4-8 inclusive are locked (0xF8 0x01)
|
||||
// Internal is "reserved for internal data" and observed to be 0x48
|
||||
// OTP bytes are impl specific, observed to be 0x23 0x21 0x54 0x55 on sample card (#!TU)
|
||||
|
||||
mifare->sectors[0].blocks[0].bytes[14] = 'T';
|
||||
mifare->sectors[0].blocks[0].bytes[15] = 'U';
|
||||
// UID of 00010203040506 with BCC0 8B and BCC1 04
|
||||
// TODO: User-setable UID?
|
||||
mifare->pages[0].bytes[0] = 0x00;
|
||||
mifare->pages[0].bytes[1] = 0x01;
|
||||
mifare->pages[0].bytes[2] = 0x02;
|
||||
mifare->pages[0].bytes[3] = 0x8B;
|
||||
mifare->pages[1].bytes[0] = 0x03;
|
||||
mifare->pages[1].bytes[1] = 0x04;
|
||||
mifare->pages[1].bytes[2] = 0x05;
|
||||
mifare->pages[1].bytes[3] = 0x06;
|
||||
mifare->pages[2].bytes[0] = 0x04;
|
||||
|
||||
memcpy_s(mifare->sectors[0].blocks[1].bytes, 4, (uint8_t *)"T053", 4);
|
||||
memcpy_s(mifare->sectors[0].blocks[1].bytes + 4, 12, card_sn, 12);
|
||||
memcpy_s(mifare->sectors[0].blocks[2].bytes, 16, card_sn + 12, 4);
|
||||
// INT + lock bits
|
||||
mifare->pages[2].bytes[1] = 0x48;
|
||||
mifare->pages[2].bytes[2] = 0xF8;
|
||||
mifare->pages[2].bytes[3] = 0x01;
|
||||
|
||||
memcpy_s(mifare->pages[3].bytes, 4, (uint8_t *)"#!TU", 4); // OTP
|
||||
memcpy_s(mifare->pages[4].bytes, 4, (uint8_t *)"T053", 4); // UID header?
|
||||
memcpy_s(mifare->pages[5].bytes, 16, card_sn, 16); // UID, should clobber pages 5-8 safely
|
||||
|
||||
// The rest of the card was set to 0xFF above, so we're done here
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@
|
||||
|
||||
#include "iccard/mifare.h"
|
||||
|
||||
HRESULT aime_card_populate(
|
||||
struct mifare *mifare,
|
||||
HRESULT nesica_card_populate(
|
||||
struct mifare_ul *mifare,
|
||||
const uint8_t *luid,
|
||||
size_t nbytes);
|
||||
|
||||
+2
-2
@@ -312,6 +312,7 @@ static int hook_iDmacDrvRegisterRead(HANDLE a1, DWORD register_id, DWORD* regist
|
||||
*register_val = 0;
|
||||
*err = 0;
|
||||
|
||||
EnterCriticalSection(&idmac_critsec);
|
||||
switch (register_id) {
|
||||
case 0x0400: *register_val = 0x00010201; break; // DMA card ID
|
||||
|
||||
@@ -321,9 +322,7 @@ static int hook_iDmacDrvRegisterRead(HANDLE a1, DWORD register_id, DWORD* regist
|
||||
case 0x4004: *register_val = 0x00FF0000; break; // Sub Status
|
||||
|
||||
case 0x4120: // Main Buttons
|
||||
EnterCriticalSection(&idmac_critsec);
|
||||
fastio_digital_read(NULL, 0, (uint32_t *)register_val);
|
||||
LeaveCriticalSection(&idmac_critsec);
|
||||
break;
|
||||
|
||||
case 0x4124: // Analog 1
|
||||
@@ -386,6 +385,7 @@ static int hook_iDmacDrvRegisterRead(HANDLE a1, DWORD register_id, DWORD* regist
|
||||
dprintf("iDmac: Requested read register %04lX\n", register_id);
|
||||
break;
|
||||
}
|
||||
LeaveCriticalSection(&idmac_critsec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "board/io4.h"
|
||||
|
||||
#include "hook/process.h"
|
||||
|
||||
#include "hooklib/serial.h"
|
||||
|
||||
Reference in New Issue
Block a user