From 302da9e9ee378a6a74ec22a04da2ad604891b636 Mon Sep 17 00:00:00 2001 From: KIT! Date: Sat, 24 Feb 2024 10:30:13 +0100 Subject: [PATCH] Added ACR122U support This currently only really works well with felica lite cards (amusement IC). Older cards or different types (Mifare) should work too but their id won't be correct. --- aimeio/aimeio.c | 113 ------------ aimeio/meson.build | 11 -- aimepcsc/aimepcsc.c | 222 ----------------------- aimepcsc/aimepcsc.h | 64 ------- aimepcsc/aimereader.c | 44 ----- aimepcsc/meson.build | 23 --- bin/aimeio.dll | Bin 0 -> 16896 bytes build64.bat | 10 ++ cross-mingw-32.txt | 10 -- cross-mingw-64.txt | 10 -- meson.build | 41 +---- src/aimeio.c | 182 +++++++++++++++++++ {aimeio => src}/aimeio.def | 4 +- {aimeio => src}/aimeio.h | 5 +- src/meson.build | 24 +++ src/scard/scard.c | 356 +++++++++++++++++++++++++++++++++++++ src/scard/scard.h | 46 +++++ 17 files changed, 624 insertions(+), 541 deletions(-) delete mode 100644 aimeio/aimeio.c delete mode 100644 aimeio/meson.build delete mode 100644 aimepcsc/aimepcsc.c delete mode 100644 aimepcsc/aimepcsc.h delete mode 100644 aimepcsc/aimereader.c delete mode 100644 aimepcsc/meson.build create mode 100644 bin/aimeio.dll create mode 100644 build64.bat delete mode 100644 cross-mingw-32.txt delete mode 100644 cross-mingw-64.txt create mode 100644 src/aimeio.c rename {aimeio => src}/aimeio.def (82%) rename {aimeio => src}/aimeio.h (95%) create mode 100644 src/meson.build create mode 100644 src/scard/scard.c create mode 100644 src/scard/scard.h diff --git a/aimeio/aimeio.c b/aimeio/aimeio.c deleted file mode 100644 index eaa04f8..0000000 --- a/aimeio/aimeio.c +++ /dev/null @@ -1,113 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "aimeio/aimeio.h" -#include "aimepcsc/aimepcsc.h" - -static struct aimepcsc_context *ctx; -static struct aime_data data; - -uint16_t aime_io_get_api_version(void) -{ - return 0x0100; -} - -HRESULT aime_io_init(void) -{ - int ret; - FILE* fp; - - ret = AllocConsole(); - - // someone might already allocated a console - seeing this on fufubot's segatools - if (ret != 0) { - // only when we allocate a console, we need to redirect stdout - freopen_s(&fp, "CONOUT$", "w", stdout); - } - - ctx = aimepcsc_create(); - if (!ctx) { - return E_OUTOFMEMORY; - } - - ret = aimepcsc_init(ctx); - - if (ret != 0) { - printf("aimeio-pcsc: failed to initialize: %s\n", aimepcsc_error(ctx)); - aimepcsc_destroy(ctx); - ctx = NULL; - return E_FAIL; - } - - printf("aimeio-pcsc: initialized with reader: %s\n", aimepcsc_reader_name(ctx)); - - return S_OK; -} - -HRESULT aime_io_nfc_poll(uint8_t unit_no) -{ - int ret; - HRESULT hr; - - if (unit_no != 0) { - return S_OK; - } - - memset(&data, 0, sizeof(data)); - - ret = aimepcsc_poll(ctx, &data); - - if (ret < 0) { - printf("aimeio-pcsc: poll failed: %s\n", aimepcsc_error(ctx)); - } - - return S_OK; -} - -HRESULT aime_io_nfc_get_aime_id( - uint8_t unit_no, - uint8_t *luid, - size_t luid_size) -{ - assert(luid != NULL); - - if (unit_no != 0 || data.card_type != Mifare || luid_size != data.card_id_len) { - return S_FALSE; - } - - memcpy(luid, data.card_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 || data.card_type != FeliCa || data.card_id_len != 8) { - return S_FALSE; - } - - val = 0; - - for (i = 0 ; i < 8 ; i++) { - val = (val << 8) | data.card_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) -{} diff --git a/aimeio/meson.build b/aimeio/meson.build deleted file mode 100644 index 2a00a3c..0000000 --- a/aimeio/meson.build +++ /dev/null @@ -1,11 +0,0 @@ -aimeio_lib = shared_library( - 'aimeio', - name_prefix : '', - include_directories: inc, - implicit_include_directories : false, - vs_module_defs : 'aimeio.def', - link_with: [aimepcsc_lib], - sources : [ - 'aimeio.c', - ], -) diff --git a/aimepcsc/aimepcsc.c b/aimepcsc/aimepcsc.c deleted file mode 100644 index a170ed9..0000000 --- a/aimepcsc/aimepcsc.c +++ /dev/null @@ -1,222 +0,0 @@ -#include "aimepcsc.h" -#include - -static const uint8_t atr_ios14443_common[] = {0x3B, 0x8F, 0x80, 0x01, 0x80, 0x4F, 0x0C, 0xA0, 0x00, 0x00, 0x03, 0x06}; -static const uint8_t cardtype_m1k[] = {0x03, 0x00, 0x01}; -static const uint8_t cardtype_felica[] = {0x11, 0x00, 0x3B}; - -static const uint8_t felica_cmd_readidm[] = {0xFF, 0xCA, 0x00, 0x00, 0x00}; - -static const uint8_t m1k_cmd_loadkey[] = {0xFF, 0x82, 0x00, 0x00, 0x06, 0x57, 0x43, 0x43, 0x46, 0x76, 0x32}; -static const uint8_t m1k_cmd_auth_block2[] = {0xFF, 0x86, 0x00, 0x00, 0x05, 0x01, 0x00, 0x02, 0x61, 0x00}; -static const uint8_t m1k_cmd_read_block2[] = {0xFF, 0xB0, 0x00, 0x02, 0x10}; - -struct aimepcsc_context { - SCARDCONTEXT hContext; - LPSTR mszReaders; - DWORD pcchReaders; - - CHAR last_error[256]; -}; - -#define APDU_SEND(card, cmd, expected_res_len) \ - do { \ - len = sizeof(buf); \ - ret = SCardTransmit(*card, SCARD_PCI_T1, cmd, sizeof(cmd), NULL, buf, &len); \ - if (ret != SCARD_S_SUCCESS) { \ - snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardTransmit failed during " #cmd ": %08lX", (ULONG) ret); \ - return -1; \ - } \ - if (len != expected_res_len || buf[expected_res_len - 2] != 0x90 || buf[expected_res_len - 1] != 0x00) { \ - snprintf(ctx->last_error, sizeof(ctx->last_error), #cmd " failed; res_len=%lu, res_code=%02x%02x", len, buf[2], buf[3]); \ - return 1; \ - } \ - } while (0) - -static int read_felica_aime(struct aimepcsc_context *ctx, LPSCARDHANDLE card, struct aime_data *data) { - uint8_t buf[32]; - DWORD len; - LONG ret; - - /* read card ID */ - APDU_SEND(card, felica_cmd_readidm, 10); - - data->card_id_len = 8; - memcpy(data->card_id, buf, 8); - - return 0; -} - -static int read_m1k_aime(struct aimepcsc_context *ctx, LPSCARDHANDLE card, struct aime_data *data) { - uint8_t buf[32]; - DWORD len; - LONG ret; - - /* load key onto reader */ - APDU_SEND(card, m1k_cmd_loadkey, 2); - - /* authenticate block 2 */ - APDU_SEND(card, m1k_cmd_auth_block2, 2); - - /* read block 2 */ - APDU_SEND(card, m1k_cmd_read_block2, 18); - - data->card_id_len = 10; - memcpy(data->card_id, buf + 6, 10); - - return 0; -} - -struct aimepcsc_context* aimepcsc_create(void) { - struct aimepcsc_context* ctx; - - ctx = (struct aimepcsc_context*) malloc(sizeof(struct aimepcsc_context)); - if (!ctx) { - return NULL; - } - - memset(ctx, 0, sizeof(struct aimepcsc_context)); - return ctx; -} - -void aimepcsc_destroy(struct aimepcsc_context *ctx) { - free(ctx); -} - -int aimepcsc_init(struct aimepcsc_context *ctx) { - LONG ret; - - ret = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &ctx->hContext); - - if (ret != SCARD_S_SUCCESS) { - snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardEstablishContext failed: %08lX", (ULONG) ret); - return -1; - } - - ctx->pcchReaders = SCARD_AUTOALLOCATE; - - ret = SCardListReaders(ctx->hContext, NULL, (LPSTR) &ctx->mszReaders, &ctx->pcchReaders); - - if (ret != SCARD_S_SUCCESS) { - snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardListReaders failed: %08lX", (ULONG) ret); - goto errout; - } - - return 0; - -errout: - SCardReleaseContext(ctx->hContext); - return -1; -} - -void aimepcsc_shutdown(struct aimepcsc_context *ctx) { - if (ctx->mszReaders != NULL) { - SCardFreeMemory(ctx->hContext, ctx->mszReaders); - } - - SCardReleaseContext(ctx->hContext); -} - -int aimepcsc_poll(struct aimepcsc_context *ctx, struct aime_data *data) { - SCARDHANDLE hCard; - SCARD_READERSTATE rs; - LONG ret; - LPBYTE pbAttr = NULL; - DWORD cByte = SCARD_AUTOALLOCATE; - int retval; - - retval = 1; - - memset(&rs, 0, sizeof(SCARD_READERSTATE)); - - rs.szReader = ctx->mszReaders; - rs.dwCurrentState = SCARD_STATE_UNAWARE; - - /* check if a card is present */ - ret = SCardGetStatusChange(ctx->hContext, 0, &rs, 1); - - if (ret == SCARD_E_TIMEOUT) { - return 1; - } - - if (ret != SCARD_S_SUCCESS) { - snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardGetStatusChange failed: %08lX", (ULONG) ret); - return -1; - } - - if (rs.dwEventState & SCARD_STATE_EMPTY) { - return 1; - } - - if (!(rs.dwEventState & SCARD_STATE_PRESENT)) { - sprintf(ctx->last_error, "unknown dwCurrentState: %08lX", rs.dwCurrentState); - return -1; - } - - /* connect to card */ - ret = SCardConnect(ctx->hContext, rs.szReader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, NULL); - - if (ret != SCARD_S_SUCCESS) { - snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardConnect failed: %08lX", (ULONG) ret); - return -1; - } - - /* get ATR string */ - ret = SCardGetAttrib(hCard, SCARD_ATTR_ATR_STRING, (LPBYTE) &pbAttr, &cByte); - - if (ret != SCARD_S_SUCCESS) { - snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardGetAttrib failed: %08lX", (ULONG) ret); - return -1; - } - - if (cByte != 20) { - snprintf(ctx->last_error, sizeof(ctx->last_error), "invalid ATR length: %lu", cByte); - goto out; - } - - /* check ATR */ - if (memcmp(pbAttr, atr_ios14443_common, sizeof(atr_ios14443_common)) != 0) { - snprintf(ctx->last_error, sizeof(ctx->last_error), "invalid card type."); - goto out; - } - - /* check card type */ - if (memcmp(pbAttr + sizeof(atr_ios14443_common), cardtype_m1k, sizeof(cardtype_m1k)) == 0) { - data->card_type = Mifare; - ret = read_m1k_aime(ctx, &hCard, data); - if (ret < 0) { - retval = -1; - goto out; - } else if (ret > 0) { - goto out; - } - } else if (memcmp(pbAttr + sizeof(atr_ios14443_common), cardtype_felica, sizeof(cardtype_felica)) == 0) { - data->card_type = FeliCa; - ret = read_felica_aime(ctx, &hCard, data); - if (ret < 0) { - retval = -1; - goto out; - } else if (ret > 0) { - goto out; - } - } else { - snprintf(ctx->last_error, sizeof(ctx->last_error), "invalid card type."); - goto out; - } - - retval = 0; - -out: - SCardFreeMemory(ctx->hContext, pbAttr); - SCardDisconnect(hCard, SCARD_LEAVE_CARD); - - return retval; -} - -const char* aimepcsc_error(struct aimepcsc_context *ctx) { - return ctx->last_error; -} - -const char* aimepcsc_reader_name(struct aimepcsc_context *ctx) { - return ctx->mszReaders; -} \ No newline at end of file diff --git a/aimepcsc/aimepcsc.h b/aimepcsc/aimepcsc.h deleted file mode 100644 index 934abea..0000000 --- a/aimepcsc/aimepcsc.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -/* opaque context */ -struct aimepcsc_context; - -/* aime card types */ -enum AIME_CARDTYPE { - Mifare = 0x01, - FeliCa = 0x02, -}; - -/* aime card data */ -struct aime_data { - /* AIME_CARDTYPE */ - uint8_t card_type; - - /* Card ID */ - uint8_t card_id[32]; - - /* Card ID length */ - uint8_t card_id_len; -}; - -/* create new context for aimepcsc - * @return context on success, NULL on failure - */ -struct aimepcsc_context* aimepcsc_create(void); - -/* destroy context for aimepcsc */ -void aimepcsc_destroy(struct aimepcsc_context *ctx); - -/* setup reader (first detected reader will be used) - * @param ctx context - * @return 0 on success, -1 on failure - */ -int aimepcsc_init(struct aimepcsc_context *ctx); - -/* shutdown reader */ -void aimepcsc_shutdown(struct aimepcsc_context *ctx); - -/* poll for card - * @param ctx context - * @param data data to be filled - * @return 0 on success, -1 on failure, 1 on no card - */ -int aimepcsc_poll(struct aimepcsc_context *ctx, struct aime_data *data); - -/* get last error - * @param ctx context - * @return error string - */ -const char* aimepcsc_error(struct aimepcsc_context *ctx); - -/* get reader name - * @param ctx context - * @return reader name - */ -const char* aimepcsc_reader_name(struct aimepcsc_context *ctx); \ No newline at end of file diff --git a/aimepcsc/aimereader.c b/aimepcsc/aimereader.c deleted file mode 100644 index 73346c7..0000000 --- a/aimepcsc/aimereader.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "aimepcsc.h" -#include - -int main(int argc, char** argv) { - struct aimepcsc_context* ctx; - int ret; - - ctx = aimepcsc_create(); - if (!ctx) { - return -1; - } - - ret = aimepcsc_init(ctx); - if (ret != 0) { - fprintf(stderr, "aimepcsc_init failed: %s\n", aimepcsc_error(ctx)); - return -1; - } - - fprintf(stderr, "connected to reader: %s; waiting for cards...\n", aimepcsc_reader_name(ctx)); - - while (1) { - struct aime_data data; - - ret = aimepcsc_poll(ctx, &data); - if (ret == 0) { - printf("card detected: "); - printf("type=%s, id=", data.card_type == Mifare ? "Mifare (old Aime)" : "FeliCa (AIC-Aime)"); - for (int i = 0; i < data.card_id_len; i++) { - printf("%02X", data.card_id[i]); - } - printf("\n"); - } else if (ret == -1) { - fprintf(stderr, "aimepcsc_poll failed: %s\n", aimepcsc_error(ctx)); - break; - } - - Sleep(500); - } - - aimepcsc_shutdown(ctx); - aimepcsc_destroy(ctx); - - return 0; -} \ No newline at end of file diff --git a/aimepcsc/meson.build b/aimepcsc/meson.build deleted file mode 100644 index 67e3751..0000000 --- a/aimepcsc/meson.build +++ /dev/null @@ -1,23 +0,0 @@ -winscard_lib = cc.find_library('winscard') - -aimepcsc_lib = static_library( - 'aimepcsc', - name_prefix : '', - include_directories: inc, - implicit_include_directories : false, - dependencies: [winscard_lib], - sources : [ - 'aimepcsc.c', - ], -) - -aimereader = executable( - 'aimereader', - name_prefix : '', - include_directories : [inc], - implicit_include_directories : false, - link_with: [aimepcsc_lib], - sources : [ - 'aimereader.c' - ] -) diff --git a/bin/aimeio.dll b/bin/aimeio.dll new file mode 100644 index 0000000000000000000000000000000000000000..ab4330562cef7c3dc981cfd197665d7b89a11710 GIT binary patch literal 16896 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 literal 0 HcmV?d00001 diff --git a/build64.bat b/build64.bat new file mode 100644 index 0000000..947c2d5 --- /dev/null +++ b/build64.bat @@ -0,0 +1,10 @@ +cd /d %~dp0 + +REM Edit this with the right path to vcvarsall.bat. +call "C:\Program Files\Microsoft Visual Studio\2022\Preview\VC\Auxiliary\Build\vcvarsall.bat" x64 +meson setup build64 --buildtype=release +meson configure build64 +ninja -C build64 + +mkdir bin +copy build64\src\aimeio.dll bin\aimeio.dll diff --git a/cross-mingw-32.txt b/cross-mingw-32.txt deleted file mode 100644 index 21ed951..0000000 --- a/cross-mingw-32.txt +++ /dev/null @@ -1,10 +0,0 @@ -[binaries] -c = 'i686-w64-mingw32-gcc' -ar = 'i686-w64-mingw32-ar' -strip = 'i686-w64-mingw32-strip' - -[host_machine] -system = 'windows' -cpu_family = 'x86' -cpu = 'i686' -endian = 'little' diff --git a/cross-mingw-64.txt b/cross-mingw-64.txt deleted file mode 100644 index 6b15798..0000000 --- a/cross-mingw-64.txt +++ /dev/null @@ -1,10 +0,0 @@ -[binaries] -c = 'x86_64-w64-mingw32-gcc' -ar = 'x86_64-w64-mingw32-ar' -strip = 'x86_64-w64-mingw32-strip' - -[host_machine] -system = 'windows' -cpu_family = 'x86_64' -cpu = 'x86_64' -endian = 'little' diff --git a/meson.build b/meson.build index 26c1063..0cfafac 100644 --- a/meson.build +++ b/meson.build @@ -1,44 +1,9 @@ project( - 'aimeio-pcsc', + 'aimeio_scard', 'c', - version: '0.0.1', - default_options: [ - 'warning_level=3', - ], + version : '0.0.1' ) -add_project_arguments( - '-DCOBJMACROS', - '-DDIRECTINPUT_VERSION=0x0800', - '-DWIN32_LEAN_AND_MEAN', - '-D_WIN32_WINNT=_WIN32_WINNT_WIN7', - '-DMINGW_HAS_SECURE_API=1', - '-Wno-unused', - language: 'c', -) - -cc = meson.get_compiler('c') - -if cc.get_id() != 'msvc' - add_project_arguments( - '-ffunction-sections', - '-fdata-sections', - '-flto', # Enable Link-Time Optimization - language: 'c', - ) - - add_project_link_arguments( - '-Wl,--enable-stdcall-fixup', - '-Wl,--exclude-all-symbols', - '-Wl,--gc-sections', - '-static-libgcc', - '-flto', # Enable Link-Time Optimization - '-Wl,-s', # Strip debug symbols - language: 'c', - ) -endif - inc = include_directories('.') -subdir('aimepcsc') -subdir('aimeio') +subdir('src/') \ No newline at end of file diff --git a/src/aimeio.c b/src/aimeio.c new file mode 100644 index 0000000..add2d26 --- /dev/null +++ b/src/aimeio.c @@ -0,0 +1,182 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "aimeio.h" +#include "scard/scard.h" + +char module[] = "CardReader"; + +// Reader Thread +static bool READER_RUNNER_INITIALIZED = false; +static HANDLE READER_POLL_THREAD; +static bool READER_POLL_STOP_FLAG; + +char AccessID[21] = "00000000000000000001"; +static bool HasCard = false; +uint8_t UID[8] = {0}; + +static struct aimepcsc_context *ctx; +// static struct aime_data data; + +#pragma region READER SPECIFIC +static unsigned int __stdcall reader_poll_thread_proc(void *ctx) +{ + while (!READER_POLL_STOP_FLAG) + { + if (!HasCard) + { + scard_update(UID); + + if (UID[0] > 0) // If a card was read, format it properly and set HasCard to true so the game can insert it on next frame. + { + printf("%s: Read card %02X%02X%02X%02X%02X%02X%02X%02X\n", module, UID[0], UID[1], UID[2], UID[3], UID[4], UID[5], UID[6], UID[7]); + + // Properly format the AccessID + uint64_t ReversedAccessID; + for (int i = 0; i < 8; i++) + ReversedAccessID = (ReversedAccessID << 8) | UID[i]; + sprintf(AccessID, "%020llu", ReversedAccessID); + + HasCard = true; + } + } + } + + return 0; +} +#pragma endregion + +#pragma region AIME +uint16_t aime_io_get_api_version(void) +{ + return 0x0100; +} + +HRESULT aime_io_init(void) +{ + // At init we want to open a console... + int ret = AllocConsole(); + FILE *fp; + // someone might already allocated a console - seeing this on fufubot's segatools + if (ret != 0) + freopen_s(&fp, "CONOUT$", "w", stdout); // only when we allocate a console, we need to redirect stdout + + // Find and initialize reader(s) + if (!READER_RUNNER_INITIALIZED) + { + READER_RUNNER_INITIALIZED = true; + printf("%s: Initializing SmartCard\n", module); + + if (!scard_init()) + { + printf("%s: Couldn't init SmartCard\n", module); + return E_FAIL; + } + } + + printf("%s: Starting reader thread.\n", module); + + // Start reader thread + READER_POLL_STOP_FLAG = false; + READER_POLL_THREAD = (HANDLE)_beginthreadex( + NULL, + 0, + reader_poll_thread_proc, + NULL, + 0, + NULL); + + // // int ret; + // // FILE *fp; + + // // ret = AllocConsole(); + + // // // someone might already allocated a console - seeing this on fufubot's segatools + // // if (ret != 0) + // // { + // // // only when we allocate a console, we need to redirect stdout + // // freopen_s(&fp, "CONOUT$", "w", stdout); + // // } + + // // ctx = aimepcsc_create(); + // // if (!ctx) + // // { + // // return E_OUTOFMEMORY; + // // } + + // // ret = aimepcsc_init(ctx); + + // // if (ret != 0) + // // { + // // printf("aimeio-pcsc: failed to initialize: %s\n", aimepcsc_error(ctx)); + // // aimepcsc_destroy(ctx); + // // ctx = NULL; + // // return E_FAIL; + // // } + + // // printf("aimeio-pcsc: initialized with reader: %s\n", aimepcsc_reader_name(ctx)); + + return S_OK; +} + +HRESULT aime_io_nfc_poll(uint8_t unit_no) +{ + return S_OK; +} + +HRESULT aime_io_nfc_get_aime_id(uint8_t unit_no, uint8_t *luid, size_t luid_size) +{ + // Lol what + return S_FALSE; +} + +HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm) +{ + assert(IDm != NULL); + + if (HasCard) + { + printf("%s: Card has been scanned ! : %s\n", module, AccessID); + HasCard = false; + + uint64_t val; + for (int i = 0; i < 8; i++) + { + val = (val << 8) | UID[i]; + UID[i] = 0; + } + *IDm = val; + + return S_OK; + } + + // // assert(IDm != NULL); + + // // if (unit_no != 0 || data.card_type != FeliCa || data.card_id_len != 8) + // // { + // // return S_FALSE; + // // } + + // // val = 0; + + // // for (i = 0; i < 8; i++) + // // { + // // val = (val << 8) | data.card_id[i]; + // // } + + // // *IDm = val; + + return S_FALSE; +} + +void aime_io_led_set_color(uint8_t unit_no, uint8_t r, uint8_t g, uint8_t b) +{ +} +#pragma endregion \ No newline at end of file diff --git a/aimeio/aimeio.def b/src/aimeio.def similarity index 82% rename from aimeio/aimeio.def rename to src/aimeio.def index fba6464..176b2d2 100644 --- a/aimeio/aimeio.def +++ b/src/aimeio.def @@ -4,6 +4,6 @@ EXPORTS aime_io_get_api_version aime_io_init aime_io_led_set_color - aime_io_nfc_get_aime_id - aime_io_nfc_get_felica_id aime_io_nfc_poll + aime_io_nfc_get_aime_id + aime_io_nfc_get_felica_id \ No newline at end of file diff --git a/aimeio/aimeio.h b/src/aimeio.h similarity index 95% rename from aimeio/aimeio.h rename to src/aimeio.h index 221f8e0..42630b5 100644 --- a/aimeio/aimeio.h +++ b/src/aimeio.h @@ -48,10 +48,7 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no); Minimum API version: 0x0100 */ -HRESULT aime_io_nfc_get_aime_id( - uint8_t unit_no, - uint8_t *luid, - size_t luid_size); +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 diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..4d3ee88 --- /dev/null +++ b/src/meson.build @@ -0,0 +1,24 @@ +compiler = meson.get_compiler('c') +hid_lib = compiler.find_library('hid') +winscard_lib = compiler.find_library('winscard') +setupapi_lib = compiler.find_library('setupapi') + +deps = [ + hid_lib, + winscard_lib, + setupapi_lib, +] + +shared_library( + 'aimeio', + implicit_include_directories : false, + vs_module_defs : 'aimeio.def', + sources: [ + 'aimeio.c', + 'scard/scard.c', + ], + dependencies: deps, + include_directories: [ + inc + ] +) \ No newline at end of file diff --git a/src/scard/scard.c b/src/scard/scard.c new file mode 100644 index 0000000..eb1ed5f --- /dev/null +++ b/src/scard/scard.c @@ -0,0 +1,356 @@ +/** + * MIT-License + * Copyright (c) 2018 by nolm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Modified version. + */ + +#include "scard.h" +// #include +// #include + +extern char module[]; + +#define MAX_APDU_SIZE 255 +int readCooldown = 500; +// set to detect all cards, reduce polling rate to 500ms. +// based off acr122u reader, see page 26 in api document. +// https://www.acs.com.hk/en/download-manual/419/API-ACR122U-2.04.pdf + +#define PICC_OPERATING_PARAMS 0xDFu +BYTE PICC_OPERATING_PARAM_CMD[5] = {0xFFu, 0x00u, 0x51u, PICC_OPERATING_PARAMS, 0x00u}; + +// return bytes from device +#define PICC_SUCCESS 0x90u +#define PICC_ERROR 0x63u + +static const BYTE UID_CMD[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u}; + +enum scard_atr_protocol +{ + SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03, + SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B, + SCARD_ATR_PROTOCOL_FELICA_212K = 0x11, + SCARD_ATR_PROTOCOL_FELICA_424K = 0x12, +}; + +// winscard_config_t WINSCARD_CONFIG; +SCARDCONTEXT hContext = 0; +SCARD_READERSTATE reader_states[2]; +LPTSTR reader_name_slots[2] = {NULL, NULL}; +int reader_count = 0; +LONG lRet = 0; + +void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no) +{ + printf("%s: Update on reader : %s\n", module, reader_states[unit_no].szReader); + // Connect to the smart card. + LONG lRet = 0; + SCARDHANDLE hCard; + DWORD dwActiveProtocol; + for (int retry = 0; retry < 100; retry++) // retry times has to be increased since poll rate is set to 500ms + { + if ((lRet = SCardConnect(_hContext, _readerName, SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, &dwActiveProtocol)) == SCARD_S_SUCCESS) + break; + + Sleep(20); + } + + if (lRet != SCARD_S_SUCCESS) + { + printf("%s: Error connecting to the card: 0x%08X\n", module, lRet); + return; + } + + // set the reader params + lRet = 0; + LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0; + DWORD cbRecv = MAX_APDU_SIZE; + BYTE pbRecv[MAX_APDU_SIZE]; + + // Read ATR to determine card type. + TCHAR szReader[200]; + DWORD cchReader = 200; + BYTE atr[32]; + DWORD cByteAtr = 32; + lRet = SCardStatus(hCard, szReader, &cchReader, NULL, NULL, atr, &cByteAtr); + if (lRet != SCARD_S_SUCCESS) + { + printf("%s: Error getting card status: 0x%08X\n", module, lRet); + return; + } + + // Only care about 20-byte ATRs returned by arcade-type smart cards + if (cByteAtr != 20) + { + printf("%s: Ignoring card with len(%d) = %02X (%08X)\n", module, cByteAtr, atr, cByteAtr); + return; + } + + printf("%s: atr Return: len(%d) = %02X (%08X)\n", module, cByteAtr, atr, cByteAtr); + + // Figure out if we should reverse the UID returned by the card based on the ATR protocol + BYTE cardProtocol = atr[12]; + BOOL shouldReverseUid = false; + if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART3) + { + printf("%s: Card protocol: ISO15693_PART3\n", module); + shouldReverseUid = true; + } + + else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3) + printf("%s: Card protocol: ISO14443_PART3\n", module); + + else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) + printf("%s: Card protocol: FELICA_212K\n", module); + + else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_424K) + printf("%s: Card protocol: FELICA_424K\n", module); + + else + { + printf("%s: Unknown NFC Protocol: 0x%02X\n", module, cardProtocol); + return; + } + + // Read UID + cbRecv = MAX_APDU_SIZE; + if ((lRet = SCardTransmit(hCard, pci, UID_CMD, sizeof(UID_CMD), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS) + { + printf("%s: Error querying card UID: 0x%08X\n", module, lRet); + return; + } + + if (cbRecv > 1 && pbRecv[0] == PICC_ERROR) + { + printf("%s: UID query failed\n", module); + return; + } + + if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS) + printf("%s: Failed SCardDisconnect: 0x%08X\n", module, lRet); + + if (cbRecv < 8) + { + printf("%s: Padding card uid to 8 bytes\n", module); + memset(&pbRecv[cbRecv], 0, 8 - cbRecv); + } + else if (cbRecv > 8) + printf("%s: taking first 8 bytes of len(uid) = %02X\n", module, cbRecv); + + // Copy UID to struct, reversing if necessary + card_info_t card_info; + if (shouldReverseUid) + for (DWORD i = 0; i < 8; i++) + card_info.uid[i] = pbRecv[7 - i]; + else + memcpy(card_info.uid, pbRecv, 8); + + for (int i = 0; i < 8; ++i) + buf[i] = card_info.uid[i]; +} + +// void scard_clear(uint8_t unitNo) +// { +// card_info_t empty_cardinfo; +// } + +void scard_update(uint8_t *buf) +{ + if (reader_count < 1) + { + return; + } + + lRet = SCardGetStatusChange(hContext, readCooldown, reader_states, reader_count); + if (lRet == SCARD_E_TIMEOUT) + { + return; + } + else if (lRet != SCARD_S_SUCCESS) + { + printf("%s: Failed SCardGetStatusChange: 0x%08X\n", module, lRet); + return; + } + + for (uint8_t unit_no = 0; unit_no < reader_count; unit_no++) + { + if (!(reader_states[unit_no].dwEventState & SCARD_STATE_CHANGED)) + continue; + + DWORD newState = reader_states[unit_no].dwEventState ^ SCARD_STATE_CHANGED; + bool wasCardPresent = (reader_states[unit_no].dwCurrentState & SCARD_STATE_PRESENT) > 0; + if (newState & SCARD_STATE_UNAVAILABLE) + { + printf("%s: New card state: unavailable\n", module); + Sleep(readCooldown); + } + else if (newState & SCARD_STATE_EMPTY) + { + printf("%s: New card state: empty\n", module); + // scard_clear(unit_no); + } + else if (newState & SCARD_STATE_PRESENT && !wasCardPresent) + { + printf("%s: New card state: present\n", module); + scard_poll(buf, hContext, reader_states[unit_no].szReader, unit_no); + } + + reader_states[unit_no].dwCurrentState = reader_states[unit_no].dwEventState; + } + + return; +} + +bool scard_init() +{ + if ((lRet = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext)) != SCARD_S_SUCCESS) + { + // log_warning("scard", "failed to establish SCard context: {}", bin2hex(&lRet, sizeof(LONG))); + return lRet; + } + + LPCTSTR reader = NULL; + + int readerNameLen = 0; + + // get list of readers + LPTSTR reader_list = NULL; + auto pcchReaders = SCARD_AUTOALLOCATE; + lRet = SCardListReaders(hContext, NULL, (LPTSTR)&reader_list, &pcchReaders); + + int slot0_idx = -1; + int slot1_idx = -1; + int readerCount = 0; + switch (lRet) + { + case SCARD_E_NO_READERS_AVAILABLE: + printf("%s: No readers available\n", module); + return FALSE; + + case SCARD_S_SUCCESS: + + // So WinAPI has this terrible "multi-string" concept wherein you have a list + // of null-terminated strings, terminated by a double-null. + for (reader = reader_list; *reader; reader = reader + lstrlen(reader) + 1) + { + printf("%s: Found reader: %s\n", module, reader); + readerCount++; + + // Connect to reader and send PICC operating params command + LONG lRet = 0; + SCARDHANDLE hCard; + DWORD dwActiveProtocol; + lRet = SCardConnect(hContext, reader, SCARD_SHARE_DIRECT, 0, &hCard, &dwActiveProtocol); + if (lRet != SCARD_S_SUCCESS) + { + printf("%s: Error connecting to the reader: 0x%08X\n", module, lRet); + continue; + } + printf("%s: Connected to reader: %s, sending PICC operating params command\n", module, reader); + + // set the reader params + lRet = 0; + DWORD cbRecv = MAX_APDU_SIZE; + BYTE pbRecv[MAX_APDU_SIZE]; + lRet = SCardControl(hCard, SCARD_CTL_CODE(3500), PICC_OPERATING_PARAM_CMD, sizeof(PICC_OPERATING_PARAM_CMD), pbRecv, cbRecv, &cbRecv); + Sleep(100); + if (lRet != SCARD_S_SUCCESS) + { + printf("%s: Error setting PICC params: 0x%08X\n", module, lRet); + return FALSE; + } + + if (cbRecv > 2 && pbRecv[0] != PICC_SUCCESS && pbRecv[1] != PICC_OPERATING_PARAMS) + { + printf("%s: PICC params not valid 0x%02X != 0x%02X\n", module, pbRecv[1], PICC_OPERATING_PARAMS); + return FALSE; + } + + // Disconnect from reader + if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS) + { + printf("%s: Failed SCardDisconnect: 0x%08X\n", module, lRet); + } + else + { + printf("%s: Disconnected from reader: %s, this is expected behavior\n", module, reader); + } + } + + // If we have at least two readers, assign readers to slots as necessary. + if (readerCount >= 2) + { + if (slot1_idx != 0) + slot0_idx = 0; + if (slot0_idx != 1) + slot1_idx = 1; + } + + // if the reader count is 1 and no reader was set, set first reader + if (readerCount == 1 && slot0_idx < 0 && slot1_idx < 0) + slot0_idx = 0; + + // If we somehow only found slot 1, promote slot 1 to slot 0. + if (slot0_idx < 0 && slot1_idx >= 0) + { + slot0_idx = slot1_idx; + slot1_idx = -1; + } + + // Extract the relevant names from the multi-string. + int i; + for (i = 0, reader = reader_list; *reader; reader = reader + lstrlen(reader) + 1, i++) + { + if (slot0_idx == i) + { + readerNameLen = lstrlen(reader); + reader_name_slots[0] = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(TCHAR) * (readerNameLen + 1)); + memcpy(reader_name_slots[0], &reader[0], (size_t)(readerNameLen + 1)); + } + if (slot1_idx == i) + { + readerNameLen = lstrlen(reader); + reader_name_slots[1] = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(TCHAR) * (readerNameLen + 1)); + memcpy(reader_name_slots[1], &reader[0], (size_t)(readerNameLen + 1)); + } + } + + if (reader_name_slots[0]) + printf("%s: Using reader slot 0: %s\n", module, reader_name_slots[0]); + + if (reader_name_slots[1]) + printf("%s: Using reader slot 1: %s\n", module, reader_name_slots[1]); + + reader_count = reader_name_slots[1] ? 2 : 1; + + memset(&reader_states[0], 0, sizeof(SCARD_READERSTATE)); + reader_states[0].szReader = reader_name_slots[0]; + + memset(&reader_states[1], 0, sizeof(SCARD_READERSTATE)); + reader_states[1].szReader = reader_name_slots[1]; + return TRUE; + + default: + printf("%s: Failed SCardListReaders: 0x%08X\n", module, lRet); + return FALSE; + } +} \ No newline at end of file diff --git a/src/scard/scard.h b/src/scard/scard.h new file mode 100644 index 0000000..70c05fd --- /dev/null +++ b/src/scard/scard.h @@ -0,0 +1,46 @@ +/** + * MIT-License + * Copyright (c) 2018 by nolm + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Modified version. + */ + +#pragma once +#include +#include +#include +#include +#include + +// cardinfo_t is a description of a card that was presented to a reader +typedef struct card_info +{ + int card_type; + uint8_t uid[8]; +} card_info_t; + +void scard_update(uint8_t *buf); + +void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no); + +void scard_clear(uint8_t unitNo); + +bool scard_init(); \ No newline at end of file