From 9eedc62e04b985413721ad67cc34bf32a0a8f0b3 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. --- .gitignore | 5 +- aimeio/aimeio.c | 113 ------------ aimeio/meson.build | 11 -- aimepcsc/aimepcsc.c | 222 ----------------------- aimepcsc/aimepcsc.h | 64 ------- aimepcsc/aimereader.c | 44 ----- aimepcsc/meson.build | 23 --- 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, 628 insertions(+), 542 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 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/.gitignore b/.gitignore index 600d2d3..ea4b08d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -.vscode \ No newline at end of file +.vscode +build64/* +bin/* +*.dll \ No newline at end of file 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/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