add aime reader stuff from segatools
This commit is contained in:
parent
4c413da0f1
commit
d1459ab07e
284
aimeio/aimeio.c
Normal file
284
aimeio/aimeio.c
Normal 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
87
aimeio/aimeio.h
Normal 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
13
aimeio/meson.build
Normal 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
112
board/aime-dll.c
Normal 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
25
board/aime-dll.h
Normal 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);
|
@ -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->enable = GetPrivateProfileIntW(L"qr", L"enable", 1, filename);
|
||||||
cfg->port = GetPrivateProfileIntW(L"qr", L"port", 0, 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);
|
||||||
|
}
|
||||||
|
@ -6,7 +6,9 @@
|
|||||||
#include "board/usio.h"
|
#include "board/usio.h"
|
||||||
#include "board/bpreader.h"
|
#include "board/bpreader.h"
|
||||||
#include "board/qr.h"
|
#include "board/qr.h"
|
||||||
|
#include "board/sg-reader.h"
|
||||||
|
|
||||||
void bpreader_config_load(struct bpreader_config *cfg, const wchar_t *filename);
|
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 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);
|
||||||
|
@ -10,8 +10,11 @@ board_lib = static_library(
|
|||||||
hooklib_lib,
|
hooklib_lib,
|
||||||
util_lib,
|
util_lib,
|
||||||
jvs_lib,
|
jvs_lib,
|
||||||
|
iccard_lib,
|
||||||
],
|
],
|
||||||
sources : [
|
sources : [
|
||||||
|
'aime-dll.c',
|
||||||
|
'aime-dll.h',
|
||||||
'bpreader.c',
|
'bpreader.c',
|
||||||
'bpreader.h',
|
'bpreader.h',
|
||||||
'najv4.c',
|
'najv4.c',
|
||||||
@ -24,5 +27,19 @@ board_lib = static_library(
|
|||||||
'usio.h',
|
'usio.h',
|
||||||
'qr.c',
|
'qr.c',
|
||||||
'qr.h',
|
'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
134
board/sg-cmd.c
Normal 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
43
board/sg-cmd.h
Normal 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
165
board/sg-frame.c
Normal 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
15
board/sg-frame.h
Normal 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
39
board/sg-led-cmd.h
Normal 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
178
board/sg-led.c
Normal 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
30
board/sg-led.h
Normal 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
115
board/sg-nfc-cmd.h
Normal 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
434
board/sg-nfc.c
Normal 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
39
board/sg-nfc.h
Normal 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
186
board/sg-reader.c
Normal 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
17
board/sg-reader.h
Normal 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);
|
82
board/usio.c
82
board/usio.c
@ -19,12 +19,12 @@
|
|||||||
#include "hook/iohook.h"
|
#include "hook/iohook.h"
|
||||||
|
|
||||||
#include "hooklib/setupapi.h"
|
#include "hooklib/setupapi.h"
|
||||||
|
#include "hooklib/procaddr.h"
|
||||||
|
|
||||||
#include "util/dprintf.h"
|
#include "util/dprintf.h"
|
||||||
#include "util/dump.h"
|
#include "util/dump.h"
|
||||||
|
|
||||||
static const wchar_t usio_path[] = L"$usio";
|
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_Open();
|
||||||
static int my_bnusio_Close();
|
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_GetService(uint8_t id);
|
||||||
static int my_bnusio_GetServiceError(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",
|
.name = "bnusio_Open",
|
||||||
.patch = my_bnusio_Open
|
.patch = my_bnusio_Open
|
||||||
@ -112,6 +112,74 @@ static const struct hook_symbol winusb_syms[] = {
|
|||||||
{
|
{
|
||||||
.name = "bnusio_GetServiceError",
|
.name = "bnusio_GetServiceError",
|
||||||
.patch = my_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 = ops;
|
||||||
usio_ops_ctx = ctx;
|
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));
|
memset(&state, 0, sizeof(state));
|
||||||
|
|
||||||
dprintf("USIO: Init\n");
|
dprintf("USIO: Init\n");
|
||||||
@ -146,6 +214,14 @@ HRESULT usio_hook_init(
|
|||||||
return S_OK;
|
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()
|
static int my_bnusio_Open()
|
||||||
{
|
{
|
||||||
dprintf("USIO: Open\n");
|
dprintf("USIO: Open\n");
|
||||||
|
@ -47,3 +47,5 @@ HRESULT usio_hook_init(
|
|||||||
const struct usio_ops *ops,
|
const struct usio_ops *ops,
|
||||||
void *ctx,
|
void *ctx,
|
||||||
HMODULE target);
|
HMODULE target);
|
||||||
|
|
||||||
|
HRESULT usio_hook_proc_addr(HMODULE target);
|
||||||
|
62
board/vfd.c
Normal file
62
board/vfd.c
Normal 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
5
board/vfd.h
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
HRESULT vfd_hook_init(unsigned int port_no);
|
64
iccard/aime.c
Normal file
64
iccard/aime.c
Normal 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
13
iccard/aime.h
Normal 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
196
iccard/felica.c
Normal 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
27
iccard/felica.h
Normal 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
18
iccard/meson.build
Normal 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
15
iccard/mifare.h
Normal 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
143
iccard/solitaire.c
Normal 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
3
iccard/solitaire.h
Normal 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);
|
Loading…
Reference in New Issue
Block a user