add aime reader stuff from segatools

This commit is contained in:
Hay1tsme 2023-09-18 04:00:09 -04:00
parent 4c413da0f1
commit d1459ab07e
32 changed files with 2587 additions and 5 deletions

284
aimeio/aimeio.c Normal file
View File

@ -0,0 +1,284 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "aimeio/aimeio.h"
#include "util/crc.h"
#include "util/dprintf.h"
struct aime_io_config {
wchar_t aime_path[MAX_PATH];
wchar_t felica_path[MAX_PATH];
bool felica_gen;
uint8_t vk_scan;
};
static struct aime_io_config aime_io_cfg;
static uint8_t aime_io_aime_id[10];
static uint8_t aime_io_felica_id[8];
static bool aime_io_aime_id_present;
static bool aime_io_felica_id_present;
static void aime_io_config_read(
struct aime_io_config *cfg,
const wchar_t *filename);
static HRESULT aime_io_read_id_file(
const wchar_t *path,
uint8_t *bytes,
size_t nbytes);
static HRESULT aime_io_generate_felica(
const wchar_t *path,
uint8_t *bytes,
size_t nbytes);
static void aime_io_config_read(
struct aime_io_config *cfg,
const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"aime",
L"aimePath",
L"DEVICE\\aime.txt",
cfg->aime_path,
_countof(cfg->aime_path),
filename);
GetPrivateProfileStringW(
L"aime",
L"felicaPath",
L"DEVICE\\felica.txt",
cfg->felica_path,
_countof(cfg->felica_path),
filename);
dprintf("NFC: felicaPath GetLastError %lx\n", GetLastError());
cfg->felica_gen = GetPrivateProfileIntW(
L"aime",
L"felicaGen",
1,
filename);
cfg->vk_scan = GetPrivateProfileIntW(
L"aime",
L"scan",
VK_RETURN,
filename);
}
static HRESULT aime_io_read_id_file(
const wchar_t *path,
uint8_t *bytes,
size_t nbytes)
{
HRESULT hr;
FILE *f;
size_t i;
int byte;
int r;
f = _wfopen(path, L"r");
if (f == NULL) {
return S_FALSE;
}
memset(bytes, 0, nbytes);
for (i = 0 ; i < nbytes ; i++) {
r = fscanf(f, "%02x ", &byte);
if (r != 1) {
hr = E_FAIL;
dprintf("AimeIO DLL: %S: fscanf[%i] failed: %i\n",
path,
(int) i,
r);
goto end;
}
bytes[i] = byte;
}
hr = S_OK;
end:
if (f != NULL) {
fclose(f);
}
return hr;
}
static HRESULT aime_io_generate_felica(
const wchar_t *path,
uint8_t *bytes,
size_t nbytes)
{
size_t i;
FILE *f;
assert(path != NULL);
assert(bytes != NULL);
assert(nbytes > 0);
srand(time(NULL));
for (i = 0 ; i < nbytes ; i++) {
bytes[i] = rand();
}
/* FeliCa IDm values should have a 0 in their high nibble. I think. */
bytes[0] &= 0x0F;
f = _wfopen(path, L"w");
if (f == NULL) {
dprintf("AimeIO DLL: %S: fopen failed: %i\n", path, (int) errno);
return E_FAIL;
}
for (i = 0 ; i < nbytes ; i++) {
fprintf(f, "%02X", bytes[i]);
}
fprintf(f, "\n");
fclose(f);
dprintf("AimeIO DLL: Generated random FeliCa ID\n");
return S_OK;
}
uint16_t aime_io_get_api_version(void)
{
return 0x0100;
}
HRESULT aime_io_init(void)
{
aime_io_config_read(&aime_io_cfg, L".\\segatools.ini");
return S_OK;
}
HRESULT aime_io_nfc_poll(uint8_t unit_no)
{
bool sense;
HRESULT hr;
if (unit_no != 0) {
return S_OK;
}
/* Reset presence flags */
aime_io_aime_id_present = false;
aime_io_felica_id_present = false;
/* Don't do anything more if the scan key is not held */
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
if (!sense) {
return S_OK;
}
/* Try AiMe IC */
hr = aime_io_read_id_file(
aime_io_cfg.aime_path,
aime_io_aime_id,
sizeof(aime_io_aime_id));
if (SUCCEEDED(hr) && hr != S_FALSE) {
aime_io_aime_id_present = true;
return S_OK;
}
/* Try FeliCa IC */
hr = aime_io_read_id_file(
aime_io_cfg.felica_path,
aime_io_felica_id,
sizeof(aime_io_felica_id));
if (SUCCEEDED(hr) && hr != S_FALSE) {
aime_io_felica_id_present = true;
return S_OK;
}
/* Try generating FeliCa IC (if enabled) */
if (aime_io_cfg.felica_gen) {
hr = aime_io_generate_felica(
aime_io_cfg.felica_path,
aime_io_felica_id,
sizeof(aime_io_felica_id));
if (FAILED(hr)) {
return hr;
}
aime_io_felica_id_present = true;
}
return S_OK;
}
HRESULT aime_io_nfc_get_aime_id(
uint8_t unit_no,
uint8_t *luid,
size_t luid_size)
{
assert(luid != NULL);
assert(luid_size == sizeof(aime_io_aime_id));
if (unit_no != 0 || !aime_io_aime_id_present) {
return S_FALSE;
}
memcpy(luid, aime_io_aime_id, luid_size);
return S_OK;
}
HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
{
uint64_t val;
size_t i;
assert(IDm != NULL);
if (unit_no != 0 || !aime_io_felica_id_present) {
return S_FALSE;
}
val = 0;
for (i = 0 ; i < 8 ; i++) {
val = (val << 8) | aime_io_felica_id[i];
}
*IDm = val;
return S_OK;
}
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b)
{}

87
aimeio/aimeio.h Normal file
View File

@ -0,0 +1,87 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
/*
Get the version of the Aime IO API that this DLL supports. This function
should return a positive 16-bit integer, where the high byte is the major
version and the low byte is the minor version (as defined by the Semantic
Versioning standard).
The latest API version as of this writing is 0x0100.
*/
uint16_t aime_io_get_api_version(void);
/*
Initialize Aime IO provider DLL. Only called once, before any other
functions exported from this DLL are called (except for
aime_io_get_api_version).
Minimum API version: 0x0100
*/
HRESULT aime_io_init(void);
/*
Poll for IC cards in the vicinity.
- unit_no: Always 0 as of the current API version
Minimum API version: 0x0100
*/
HRESULT aime_io_nfc_poll(uint8_t unit_no);
/*
Attempt to read out a classic Aime card ID
- unit_no: Always 0 as of the current API version
- luid: Pointer to a ten-byte buffer that will receive the ID
- luid_size: Size of the buffer at *luid. Always 10.
Returns:
- S_OK if a classic Aime is present and was read successfully
- S_FALSE if no classic Aime card is present (*luid will be ignored)
- Any HRESULT error if an error occured.
Minimum API version: 0x0100
*/
HRESULT aime_io_nfc_get_aime_id(
uint8_t unit_no,
uint8_t *luid,
size_t luid_size);
/*
Attempt to read out a FeliCa card ID ("IDm"). The following are examples
of FeliCa cards:
- Amuse IC (which includes new-style Aime-branded cards, among others)
- Smartphones with FeliCa NFC capability (uncommon outside Japan)
- Various Japanese e-cash cards and train passes
Parameters:
- unit_no: Always 0 as of the current API version
- IDm: Output parameter that will receive the card ID
Returns:
- S_OK if a FeliCa device is present and was read successfully
- S_FALSE if no FeliCa device is present (*IDm will be ignored)
- Any HRESULT error if an error occured.
Minimum API version: 0x0100
*/
HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm);
/*
Change the color and brightness of the card reader's RGB lighting
- unit_no: Always 0 as of the current API version
- r, g, b: Primary color intensity, from 0 to 255 inclusive.
Minimum API version: 0x0100
*/
void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b);

13
aimeio/meson.build Normal file
View File

@ -0,0 +1,13 @@
aimeio_lib = static_library(
'aimeio',
name_prefix : '',
include_directories: inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
link_with : [
util_lib,
],
sources : [
'aimeio.c',
],
)

112
board/aime-dll.c Normal file
View File

@ -0,0 +1,112 @@
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include "board/aime-dll.h"
#include "util/dll-bind.h"
#include "util/dprintf.h"
const struct dll_bind_sym aime_dll_syms[] = {
{
.sym = "aime_io_init",
.off = offsetof(struct aime_dll, init),
}, {
.sym = "aime_io_nfc_poll",
.off = offsetof(struct aime_dll, nfc_poll),
}, {
.sym = "aime_io_nfc_get_aime_id",
.off = offsetof(struct aime_dll, nfc_get_aime_id),
}, {
.sym = "aime_io_nfc_get_felica_id",
.off = offsetof(struct aime_dll, nfc_get_felica_id),
}, {
.sym = "aime_io_led_set_color",
.off = offsetof(struct aime_dll, led_set_color),
}
};
struct aime_dll aime_dll;
// Copypasta DLL binding and diagnostic message boilerplate.
// Not much of this lends itself to being easily factored out. Also there
// will be a lot of API-specific branching code here eventually as new API
// versions get defined, so even though these functions all look the same
// now this won't remain the case forever.
HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self)
{
uint16_t (*get_api_version)(void);
const struct dll_bind_sym *sym;
HINSTANCE owned;
HINSTANCE src;
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (cfg->path[0] != L'\0') {
owned = LoadLibraryW(cfg->path);
if (owned == NULL) {
hr = HRESULT_FROM_WIN32(GetLastError());
dprintf("NFC Assembly: Failed to load IO DLL: %lx: %S\n",
hr,
cfg->path);
goto end;
}
dprintf("NFC Assembly: Using custom IO DLL: %S\n", cfg->path);
src = owned;
} else {
owned = NULL;
src = self;
}
get_api_version = (void *) GetProcAddress(src, "aime_io_get_api_version");
if (get_api_version != NULL) {
aime_dll.api_version = get_api_version();
} else {
aime_dll.api_version = 0x0100;
dprintf("Custom IO DLL does not expose aime_io_get_api_version, "
"assuming API version 1.0.\n"
"Please ask the developer to update their DLL.\n");
}
if (aime_dll.api_version >= 0x0200) {
hr = E_NOTIMPL;
dprintf("NFC Assembly: Custom IO DLL implements an unsupported "
"API version (%#04x). Please update Segatools.\n",
aime_dll.api_version);
goto end;
}
sym = aime_dll_syms;
hr = dll_bind(&aime_dll, src, &sym, _countof(aime_dll_syms));
if (FAILED(hr)) {
if (src != self) {
dprintf("NFC Assembly: Custom IO DLL does not provide function "
"\"%s\". Please contact your IO DLL's developer for "
"further assistance.\n",
sym->sym);
goto end;
} else {
dprintf("Internal error: could not reflect \"%s\"\n", sym->sym);
}
}
owned = NULL;
end:
if (owned != NULL) {
FreeLibrary(owned);
}
return hr;
}

25
board/aime-dll.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include <windows.h>
#include "aimeio/aimeio.h"
struct aime_dll {
uint16_t api_version;
HRESULT (*init)(void);
HRESULT (*nfc_poll)(uint8_t unit_no);
HRESULT (*nfc_get_aime_id)(
uint8_t unit_no,
uint8_t *luid,
size_t luid_size);
HRESULT (*nfc_get_felica_id)(uint8_t unit_no, uint64_t *IDm);
void (*led_set_color)(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b);
};
struct aime_dll_config {
wchar_t path[MAX_PATH];
};
extern struct aime_dll aime_dll;
HRESULT aime_dll_init(const struct aime_dll_config *cfg, HINSTANCE self);

View File

@ -58,4 +58,27 @@ void qr_config_load(struct qr_config *cfg, const wchar_t *filename)
cfg->enable = GetPrivateProfileIntW(L"qr", L"enable", 1, filename);
cfg->port = GetPrivateProfileIntW(L"qr", L"port", 0, filename);
}
}
static void aime_dll_config_load(struct aime_dll_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
GetPrivateProfileStringW(
L"aimeio",
L"path",
L"",
cfg->path,
_countof(cfg->path),
filename);
}
void aime_config_load(struct aime_config *cfg, const wchar_t *filename)
{
assert(cfg != NULL);
assert(filename != NULL);
aime_dll_config_load(&cfg->dll, filename);
cfg->enable = GetPrivateProfileIntW(L"aime", L"enable", 1, filename);
}

View File

@ -6,7 +6,9 @@
#include "board/usio.h"
#include "board/bpreader.h"
#include "board/qr.h"
#include "board/sg-reader.h"
void bpreader_config_load(struct bpreader_config *cfg, const wchar_t *filename);
void usio_config_load(struct usio_config *cfg, const wchar_t *filename);
void qr_config_load(struct qr_config *cfg, const wchar_t *filename);
void qr_config_load(struct qr_config *cfg, const wchar_t *filename);
void aime_config_load(struct aime_config *cfg, const wchar_t *filename);

View File

@ -10,8 +10,11 @@ board_lib = static_library(
hooklib_lib,
util_lib,
jvs_lib,
iccard_lib,
],
sources : [
'aime-dll.c',
'aime-dll.h',
'bpreader.c',
'bpreader.h',
'najv4.c',
@ -24,5 +27,19 @@ board_lib = static_library(
'usio.h',
'qr.c',
'qr.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',
'sg-reader.c',
'sg-reader.h',
'vfd.c',
'vfd.h',
],
)

134
board/sg-cmd.c Normal file
View File

@ -0,0 +1,134 @@
#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;
}

43
board/sg-cmd.h Normal file
View File

@ -0,0 +1,43 @@
#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);

165
board/sg-frame.c Normal file
View File

@ -0,0 +1,165 @@
#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;
}

15
board/sg-frame.h Normal file
View File

@ -0,0 +1,15 @@
#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);

39
board/sg-led-cmd.h Normal file
View File

@ -0,0 +1,39 @@
#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
board/sg-led.c Normal file
View File

@ -0,0 +1,178 @@
#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;
}

30
board/sg-led.h Normal file
View File

@ -0,0 +1,30 @@
#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);

115
board/sg-nfc-cmd.h Normal file
View File

@ -0,0 +1,115 @@
#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
board/sg-nfc.c Normal file
View File

@ -0,0 +1,434 @@
#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/aime.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;
}

39
board/sg-nfc.h Normal file
View File

@ -0,0 +1,39 @@
#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);

186
board/sg-reader.c Normal file
View File

@ -0,0 +1,186 @@
#include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include "board/aime-dll.h"
#include "board/sg-led.h"
#include "board/sg-nfc.h"
#include "board/sg-reader.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT sg_reader_handle_irp(struct irp *irp);
static HRESULT sg_reader_handle_irp_locked(struct irp *irp);
static HRESULT sg_reader_nfc_poll(void *ctx);
static HRESULT sg_reader_nfc_get_aime_id(
void *ctx,
uint8_t *luid,
size_t luid_size);
static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm);
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b);
static const struct sg_nfc_ops sg_reader_nfc_ops = {
.poll = sg_reader_nfc_poll,
.get_aime_id = sg_reader_nfc_get_aime_id,
.get_felica_id = sg_reader_nfc_get_felica_id,
};
static const struct sg_led_ops sg_reader_led_ops = {
.set_color = sg_reader_led_set_color,
};
static CRITICAL_SECTION sg_reader_lock;
static bool sg_reader_started;
static HRESULT sg_reader_start_hr;
static struct uart sg_reader_uart;
static uint8_t sg_reader_written_bytes[520];
static uint8_t sg_reader_readable_bytes[520];
static struct sg_nfc sg_reader_nfc;
static struct sg_led sg_reader_led;
HRESULT sg_reader_hook_init(
const struct aime_config *cfg,
unsigned int port_no,
HINSTANCE self)
{
HRESULT hr;
assert(cfg != NULL);
assert(self != NULL);
if (!cfg->enable) {
return S_FALSE;
}
hr = aime_dll_init(&cfg->dll, self);
if (FAILED(hr)) {
return hr;
}
sg_nfc_init(&sg_reader_nfc, 0x00, &sg_reader_nfc_ops, NULL);
sg_led_init(&sg_reader_led, 0x08, &sg_reader_led_ops, NULL);
InitializeCriticalSection(&sg_reader_lock);
uart_init(&sg_reader_uart, port_no);
sg_reader_uart.written.bytes = sg_reader_written_bytes;
sg_reader_uart.written.nbytes = sizeof(sg_reader_written_bytes);
sg_reader_uart.readable.bytes = sg_reader_readable_bytes;
sg_reader_uart.readable.nbytes = sizeof(sg_reader_readable_bytes);
return iohook_push_handler(sg_reader_handle_irp);
}
static HRESULT sg_reader_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&sg_reader_uart, irp)) {
return iohook_invoke_next(irp);
}
EnterCriticalSection(&sg_reader_lock);
hr = sg_reader_handle_irp_locked(irp);
LeaveCriticalSection(&sg_reader_lock);
return hr;
}
static HRESULT sg_reader_handle_irp_locked(struct irp *irp)
{
HRESULT hr;
#if 0
if (irp->op == IRP_OP_WRITE) {
dprintf("WRITE:\n");
dump_const_iobuf(&irp->write);
}
#endif
#if 0
if (irp->op == IRP_OP_READ) {
dprintf("READ:\n");
dump_iobuf(&sg_reader_uart.readable);
}
#endif
if (irp->op == IRP_OP_OPEN) {
/* Unfortunately the card reader UART gets opened and closed
repeatedly */
if (!sg_reader_started) {
dprintf("NFC Assembly: Starting backend DLL\n");
hr = aime_dll.init();
sg_reader_started = true;
sg_reader_start_hr = hr;
if (FAILED(hr)) {
dprintf("NFC Assembly: Backend error: %x\n", (int) hr);
return hr;
}
} else {
hr = sg_reader_start_hr;
if (FAILED(hr)) {
return hr;
}
}
}
hr = uart_handle_irp(&sg_reader_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
sg_nfc_transact(
&sg_reader_nfc,
&sg_reader_uart.readable,
sg_reader_uart.written.bytes,
sg_reader_uart.written.pos);
sg_led_transact(
&sg_reader_led,
&sg_reader_uart.readable,
sg_reader_uart.written.bytes,
sg_reader_uart.written.pos);
sg_reader_uart.written.pos = 0;
return hr;
}
static HRESULT sg_reader_nfc_poll(void *ctx)
{
return aime_dll.nfc_poll(0);
}
static HRESULT sg_reader_nfc_get_aime_id(
void *ctx,
uint8_t *luid,
size_t luid_size)
{
return aime_dll.nfc_get_aime_id(0, luid, luid_size);
}
static HRESULT sg_reader_nfc_get_felica_id(void *ctx, uint64_t *IDm)
{
return aime_dll.nfc_get_felica_id(0, IDm);
}
static void sg_reader_led_set_color(void *ctx, uint8_t r, uint8_t g, uint8_t b)
{
aime_dll.led_set_color(0, r, g, b);
}

17
board/sg-reader.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <windows.h>
#include <stdbool.h>
#include "board/aime-dll.h"
struct aime_config {
struct aime_dll_config dll;
bool enable;
};
HRESULT sg_reader_hook_init(
const struct aime_config *cfg,
unsigned int port_no,
HINSTANCE self);

View File

@ -19,12 +19,12 @@
#include "hook/iohook.h"
#include "hooklib/setupapi.h"
#include "hooklib/procaddr.h"
#include "util/dprintf.h"
#include "util/dump.h"
static const wchar_t usio_path[] = L"$usio";
//static const wchar_t usio_path[] = L"USBIO_Device0";
static int my_bnusio_Open();
static int my_bnusio_Close();
@ -44,7 +44,7 @@ static int my_bnusio_GetCoinError(uint8_t id);
static int my_bnusio_GetService(uint8_t id);
static int my_bnusio_GetServiceError(uint8_t id);
static const struct hook_symbol winusb_syms[] = {
static struct hook_symbol usio_syms[] = {
{
.name = "bnusio_Open",
.patch = my_bnusio_Open
@ -112,6 +112,74 @@ static const struct hook_symbol winusb_syms[] = {
{
.name = "bnusio_GetServiceError",
.patch = my_bnusio_GetServiceError
},
{
.name = "Open",
.patch = my_bnusio_Open
},
{
.name = "GetFirmwareVersion",
.patch = my_bnusio_GetFirmwareVersion
},
{
.name = "Close",
.patch = my_bnusio_Close
},
{
.name = "SetSystemError",
.patch = my_bnusio_SetSystemError
},
{
.name = "ClearSram",
.patch = my_bnusio_ClearSram
},
{
.name = "ResetIoBoard",
.patch = my_bnusio_ResetIoBoard
},
{
.name = "Communication",
.patch = my_bnusio_Communication
},
{
.name = "GetSystemError",
.patch = my_bnusio_GetSystemError
},
{
.name = "SetPLCounter",
.patch = my_bnusio_SetPLCounter
},
{
.name = "SetGout",
.patch = my_bnusio_SetGout
},
{
.name = "GetAnalogIn",
.patch = my_bnusio_GetAnalogIn
},
{
.name = "GetSwIn",
.patch = my_bnusio_GetSwIn
},
{
.name = "SetCoinLock",
.patch = my_bnusio_SetCoinLock
},
{
.name = "GetCoin",
.patch = my_bnusio_GetCoin
},
{
.name = "GetCoinError",
.patch = my_bnusio_GetCoinError
},
{
.name = "GetService",
.patch = my_bnusio_GetService
},
{
.name = "GetServiceError",
.patch = my_bnusio_GetServiceError
}
};
@ -138,7 +206,7 @@ HRESULT usio_hook_init(
usio_ops = ops;
usio_ops_ctx = ctx;
hook_table_apply(target, "bnusio.dll", winusb_syms, _countof(winusb_syms));
hook_table_apply(target, "bnusio.dll", usio_syms, _countof(usio_syms));
memset(&state, 0, sizeof(state));
dprintf("USIO: Init\n");
@ -146,6 +214,14 @@ HRESULT usio_hook_init(
return S_OK;
}
HRESULT usio_hook_proc_addr(HMODULE target)
{
if (usio_ops != NULL)
return proc_addr_table_push(target, "bnusio.dll", usio_syms, _countof(usio_syms));
return S_OK;
}
static int my_bnusio_Open()
{
dprintf("USIO: Open\n");

View File

@ -47,3 +47,5 @@ HRESULT usio_hook_init(
const struct usio_ops *ops,
void *ctx,
HMODULE target);
HRESULT usio_hook_proc_addr(HMODULE target);

62
board/vfd.c Normal file
View File

@ -0,0 +1,62 @@
/* This is some sort of LCD display found on various cabinets. It is driven
directly by amdaemon, and it has something to do with displaying the status
of electronic payments.
Part number in schematics is "VFD GP1232A02A FUTABA".
Little else about this board is known. Black-holing the RS232 comms that it
receives seems to be sufficient for the time being. */
#include <windows.h>
#include <assert.h>
#include <stdint.h>
#include "board/vfd.h"
#include "hook/iohook.h"
#include "hooklib/uart.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT vfd_handle_irp(struct irp *irp);
static struct uart vfd_uart;
static uint8_t vfd_written[512];
static uint8_t vfd_readable[512];
HRESULT vfd_hook_init(unsigned int port_no)
{
uart_init(&vfd_uart, port_no);
vfd_uart.written.bytes = vfd_written;
vfd_uart.written.nbytes = sizeof(vfd_written);
vfd_uart.readable.bytes = vfd_readable;
vfd_uart.readable.nbytes = sizeof(vfd_readable);
return iohook_push_handler(vfd_handle_irp);
}
static HRESULT vfd_handle_irp(struct irp *irp)
{
HRESULT hr;
assert(irp != NULL);
if (!uart_match_irp(&vfd_uart, irp)) {
return iohook_invoke_next(irp);
}
hr = uart_handle_irp(&vfd_uart, irp);
if (FAILED(hr) || irp->op != IRP_OP_WRITE) {
return hr;
}
dprintf("VFD TX:\n");
dump_iobuf(&vfd_uart.written);
vfd_uart.written.pos = 0;
return hr;
}

5
board/vfd.h Normal file
View File

@ -0,0 +1,5 @@
#pragma once
#include <windows.h>
HRESULT vfd_hook_init(unsigned int port_no);

64
iccard/aime.c Normal file
View File

@ -0,0 +1,64 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "iccard/aime.h"
#include "iccard/mifare.h"
#include "iccard/solitaire.h"
#include "util/dprintf.h"
HRESULT aime_card_populate(
struct mifare *mifare,
const uint8_t *luid,
size_t nbytes)
{
uint8_t b;
size_t i;
char accessCode[21];
char hashed_id_wrk[9];
char id_wrk[9];
assert(mifare != NULL);
assert(luid != NULL);
memset(mifare, 0, sizeof(*mifare));
if (nbytes != 10) {
dprintf("AiMe IC: LUID must be 10 bytes\n");
return E_INVALIDARG;
}
for (i = 0 ; i < 10 ; i++) {
b = luid[i];
if ((b & 0xF0) > 0x90 || (b & 0x0F) > 0x09) {
dprintf("AiMe IC: LUID must be binary-coded decimal\n");
return E_INVALIDARG;
}
mifare->sectors[0].blocks[2].bytes[6 + i] = b;
}
sprintf_s(accessCode, sizeof accessCode, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
luid[0], luid[1], luid[2], luid[3], luid[4], luid[5], luid[6], luid[7], luid[8], luid[9]);
memcpy_s(hashed_id_wrk, sizeof(hashed_id_wrk), &accessCode[5],
8);
hashed_id_wrk[8] = '\0';
SolitaireCipherDecode(&accessCode[13], hashed_id_wrk, id_wrk);
DWORD nSerial = atoi(id_wrk);
mifare->sectors[0].blocks[1].bytes[12] = (nSerial >> 24) & 0xff;
mifare->sectors[0].blocks[1].bytes[13] = (nSerial >> 16) & 0xff;
mifare->sectors[0].blocks[1].bytes[14] = (nSerial >> 8) & 0xff;
mifare->sectors[0].blocks[1].bytes[15] = nSerial & 0xff;
return S_OK;
}

13
iccard/aime.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "iccard/mifare.h"
HRESULT aime_card_populate(
struct mifare *mifare,
const uint8_t *luid,
size_t nbytes);

196
iccard/felica.c Normal file
View File

@ -0,0 +1,196 @@
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
#include "iccard/felica.h"
#include "util/dprintf.h"
#include "util/dump.h"
static HRESULT felica_cmd_poll(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_get_system_code(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
static HRESULT felica_cmd_nda_a4(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
uint64_t felica_get_generic_PMm(void)
{
/* A FeliCa PMm contains low-level protocol timing information for
communicating with a particular IC card. The exact values are not
particularly important for our purposes, so we'll just return a hard-
coded PMm. This current value has been taken from an iPhone, emulating
a Suica pass via Apple Wallet, which seems to be one of the few
universally accepted FeliCa types for these games. Certain older
Suica passes and other payment and transportation cards
do not seem to be supported anymore. */
return 0x01168B868FBECBFFULL;
}
HRESULT felica_transact(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
uint64_t IDm;
uint8_t code;
HRESULT hr;
assert(f != NULL);
assert(req != NULL);
assert(res != NULL);
hr = iobuf_read_8(req, &code);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_write_8(res, code + 1);
if (FAILED(hr)) {
return hr;
}
if (code != FELICA_CMD_POLL) {
hr = iobuf_read_be64(req, &IDm);
if (FAILED(hr)) {
return hr;
}
if (IDm != f->IDm) {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
hr = iobuf_write_be64(res, IDm);
if (FAILED(hr)) {
return hr;
}
}
switch (code) {
case FELICA_CMD_POLL:
return felica_cmd_poll(f, req, res);
case FELICA_CMD_GET_SYSTEM_CODE:
return felica_cmd_get_system_code(f, req, res);
case FELICA_CMD_NDA_A4:
return felica_cmd_nda_a4(f, req, res);
default:
dprintf("FeliCa: Unimplemented command %02x, payload:\n", code);
dump_const_iobuf(req);
return E_NOTIMPL;
}
}
static HRESULT felica_cmd_poll(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
uint16_t system_code;
uint8_t request_code;
uint8_t time_slot;
HRESULT hr;
/* Request: */
hr = iobuf_read_be16(req, &system_code);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_read_8(req, &request_code);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_read_8(req, &time_slot);
if (FAILED(hr)) {
return hr;
}
if (system_code != 0xFFFF && system_code != f->system_code) {
return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
// TODO handle other params correctly...
/* Response: */
hr = iobuf_write_be64(res, f->IDm);
if (FAILED(hr)) {
return hr;
}
hr = iobuf_write_be64(res, f->PMm);
if (FAILED(hr)) {
return hr;
}
if (request_code == 0x01) {
hr = iobuf_write_be16(res, f->system_code);
if (FAILED(hr)) {
return hr;
}
}
return S_OK;
}
static HRESULT felica_cmd_get_system_code(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
HRESULT hr;
hr = iobuf_write_8(res, 1); /* Number of system codes */
if (FAILED(hr)) {
return hr;
}
hr = iobuf_write_be16(res, f->system_code);
if (FAILED(hr)) {
return hr;
}
return S_OK;
}
static HRESULT felica_cmd_nda_a4(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res)
{
/* The specification for this command is probably only available under NDA.
Returning what the driver seems to want. */
return iobuf_write_8(res, 0);
}

27
iccard/felica.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
#include <windows.h>
#include <stddef.h>
#include <stdint.h>
#include "hook/iobuf.h"
enum {
FELICA_CMD_POLL = 0x00,
FELICA_CMD_GET_SYSTEM_CODE = 0x0c,
FELICA_CMD_NDA_A4 = 0xa4,
};
struct felica {
uint64_t IDm;
uint64_t PMm;
uint16_t system_code;
};
HRESULT felica_transact(
struct felica *f,
struct const_iobuf *req,
struct iobuf *res);
uint64_t felica_get_generic_PMm(void);

18
iccard/meson.build Normal file
View File

@ -0,0 +1,18 @@
iccard_lib = static_library(
'iccard',
include_directories : inc,
implicit_include_directories : false,
c_pch : '../precompiled.h',
dependencies : [
capnhook.get_variable('hook_dep'),
],
sources : [
'aime.c',
'aime.h',
'felica.c',
'felica.h',
'mifare.h',
'solitaire.c',
'solitaire.h',
],
)

15
iccard/mifare.h Normal file
View File

@ -0,0 +1,15 @@
#pragma once
#include <stdint.h>
struct mifare_block {
uint8_t bytes[16];
};
struct mifare_sector {
struct mifare_block blocks[4];
};
struct mifare {
struct mifare_sector sectors[16];
};

143
iccard/solitaire.c Normal file
View File

@ -0,0 +1,143 @@
#include "solitaire.h"
#include <memory.h>
#define DECK_SIZE 22
#define JOKER_A 21
#define JOKER_B 22
typedef struct {
char m_Deck[DECK_SIZE];
} DECK, *PDECK;
static DECK SOL_INIT_DECK = {
.m_Deck = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 },
};
#define char2num(c) ((c) - '0' + 1)
static inline char num2char(char num) {
while (num < 1) num = num + 10;
return (num - 1) % 10 + '0';
}
static void SolMoveCard(PDECK lpDeck, char card) {
int p = 0;
for (int i = 0; i < DECK_SIZE; i++) {
if (lpDeck->m_Deck[i] == card) {
p = i;
break;
}
}
if (p < DECK_SIZE - 1) {
lpDeck->m_Deck[p] = lpDeck->m_Deck[p + 1];
lpDeck->m_Deck[p + 1] = card;
} else {
for (int i = DECK_SIZE - 1; i > 1; i--) lpDeck->m_Deck[i] = lpDeck->m_Deck[i - 1];
lpDeck->m_Deck[1] = card;
}
}
static void SolCutDeck(PDECK lpDeck, char point) {
DECK tmp;
memcpy(tmp.m_Deck, &lpDeck->m_Deck[(size_t)point], DECK_SIZE - point - 1);
memcpy(&tmp.m_Deck[DECK_SIZE - point - 1], lpDeck->m_Deck, point);
memcpy(lpDeck->m_Deck, tmp.m_Deck, DECK_SIZE - 1);
}
static void SolSwapOutsideJoker(PDECK lpDeck) {
int j1 = -1;
int j2 = -1;
DECK tmp;
for (int i = 0; i < DECK_SIZE; i++) {
if (lpDeck->m_Deck[i] == JOKER_A || lpDeck->m_Deck[i] == JOKER_B) {
if (j1 == -1) {
j1 = i;
} else {
j2 = i;
}
}
}
if (0 < DECK_SIZE - j2 - 1) memcpy(tmp.m_Deck, &lpDeck->m_Deck[j2 + 1], DECK_SIZE - j2 - 1);
tmp.m_Deck[DECK_SIZE - j2 - 1] = lpDeck->m_Deck[j1];
if (0 < j2 - j1 - 1) memcpy(&tmp.m_Deck[DECK_SIZE - j2], &lpDeck->m_Deck[j1 + 1], j2 - j1 - 1);
tmp.m_Deck[DECK_SIZE - j1 - 1] = lpDeck->m_Deck[j2];
if (0 < j1) memcpy(&tmp.m_Deck[DECK_SIZE - j1], lpDeck->m_Deck, j1);
memcpy(lpDeck->m_Deck, tmp.m_Deck, DECK_SIZE);
}
static void SolCutByBottomCard(PDECK lpDeck) {
char p = lpDeck->m_Deck[DECK_SIZE - 1];
if (p == JOKER_B) p = JOKER_A;
SolCutDeck(lpDeck, p);
}
static char SolGetTopCardNum(PDECK lpDeck) {
char p = lpDeck->m_Deck[0];
if (p == JOKER_B) p = JOKER_A;
return lpDeck->m_Deck[(size_t)p];
}
static void SolDeckHash(PDECK lpDeck) {
char p;
do {
SolMoveCard(lpDeck, JOKER_A);
SolMoveCard(lpDeck, JOKER_B);
SolMoveCard(lpDeck, JOKER_B);
SolSwapOutsideJoker(lpDeck);
SolCutByBottomCard(lpDeck);
p = SolGetTopCardNum(lpDeck);
} while (p == JOKER_A || p == JOKER_B);
}
static void SolCreateDeck(PDECK lpDeck, const char *key) {
memcpy_s(lpDeck, sizeof *lpDeck, &SOL_INIT_DECK, sizeof SOL_INIT_DECK);
int p = 0;
while (key[p] != '\0') {
SolDeckHash(lpDeck);
char c = char2num(key[p]);
SolCutDeck(lpDeck, c);
p++;
}
}
void SolitaireCipherEncode(const char *szKey, const char *szSrc, char *szDst) {
DECK deck;
SolCreateDeck(&deck, szKey);
int i = 0;
while (szSrc[i] != '\0') {
SolDeckHash(&deck);
char p = SolGetTopCardNum(&deck);
szDst[i] = num2char(char2num(szSrc[i]) + p);
i++;
}
szDst[i] = '\0';
}
void SolitaireCipherDecode(const char *szKey, const char *szSrc, char *szDst) {
DECK deck;
SolCreateDeck(&deck, szKey);
int i = 0;
while (szSrc[i] != '\0') {
SolDeckHash(&deck);
char p = SolGetTopCardNum(&deck);
szDst[i] = num2char(char2num(szSrc[i]) - p);
i++;
}
szDst[i] = '\0';
}
void SolitaireCipher(int nMode, const char *szKey, const char *szSrc, char *szDst) {
if (nMode == 0)
SolitaireCipherEncode(szKey, szSrc, szDst);
else if (nMode == 1)
SolitaireCipherDecode(szKey, szSrc, szDst);
}

3
iccard/solitaire.h Normal file
View File

@ -0,0 +1,3 @@
void SolitaireCipherDecode(const char *szKey, const char *szSrc, char *szDst);
void SolitaireCipherEncode(const char *szKey, const char *szSrc, char *szDst);
void SolitaireCipher(int mode, const char *key, const char *src_str, char *dest_str);