partial : Cleaned code a bit, prepping for reading Mifare tags

Added two new options in settings :
- One to not error out if a reader is missing
- One to force using a reader using it's name.
This commit is contained in:
Farewell_ 2024-02-28 12:09:06 +01:00
parent 50fd77e42c
commit f2769e094f
5 changed files with 301 additions and 448 deletions

View File

@ -1,41 +1,12 @@
#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.h" #include "aimeio.h"
#include "scard/scard.h" #include "scard/scard.h"
char module[] = "CardReader";
// Reader Thread // Reader Thread
static bool READER_RUNNER_INITIALIZED = false;
static HANDLE READER_POLL_THREAD; static HANDLE READER_POLL_THREAD;
static bool READER_POLL_STOP_FLAG; static bool READER_POLL_STOP_FLAG;
static bool polling = false;
static bool HasCard = false;
uint8_t UID[8] = {0};
struct aime_io_config
{
bool debug;
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 struct aime_io_config aime_io_cfg;
static uint8_t aime_io_aime_id[10]; static struct card_data card_data;
static uint8_t aime_io_felica_id[8];
static bool aime_io_aime_id_present;
static bool aime_io_felica_id_present;
#pragma region CONFIG #pragma region CONFIG
static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filename) static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filename)
@ -65,10 +36,18 @@ static void aime_io_config_read(struct aime_io_config *cfg, const wchar_t *filen
_countof(cfg->felica_path), _countof(cfg->felica_path),
filename); filename);
cfg->felica_gen = GetPrivateProfileIntW( GetPrivateProfileStringW(
L"aimeio", L"aimeio",
L"felicaGen", L"readerName",
1, L"",
cfg->reader_name,
_countof(cfg->reader_name),
filename);
cfg->reader_optional = GetPrivateProfileIntW(
L"aimeio",
L"readerOptional",
0,
filename); filename);
cfg->vk_scan = GetPrivateProfileIntW( cfg->vk_scan = GetPrivateProfileIntW(
@ -104,7 +83,7 @@ static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t
if (c == EOF) if (c == EOF)
{ {
// If the end of the file is reached before the desired line, print an error // If the end of the file is reached before the desired line, print an error
printf("%s: %S: Error: Line %d does not exist\n", module, path, LineToRead); printf("aime_io_read_id_file: %S: Error: Line %d does not exist\n", path, LineToRead);
hr = E_FAIL; hr = E_FAIL;
goto end; goto end;
} }
@ -125,7 +104,7 @@ static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t
if (r != 1) if (r != 1)
{ {
printf("%s: %S: Error parsing line %d\n", module, path, LineToRead); printf("aime_io_read_id_file: %S: Error parsing line %d\n", path, LineToRead);
hr = E_FAIL; hr = E_FAIL;
goto end; goto end;
} }
@ -136,7 +115,7 @@ static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t
// Check if the line is not nbytes long // Check if the line is not nbytes long
if (fgetc(f) != '\n' && !feof(f)) if (fgetc(f) != '\n' && !feof(f))
{ {
printf("%s: %S: Error: Line %d is not %zu bytes long\n", module, path, LineToRead, nbytes); printf("aime_io_read_id_file: %S: Error: Line %d is not %zu bytes long\n", path, LineToRead, nbytes);
hr = E_FAIL; hr = E_FAIL;
goto end; goto end;
} }
@ -151,43 +130,6 @@ end:
return hr; 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) // If we somehow can't create the file, we error out.
{
printf("%s: %S: fopen failed: %i\n", module, path, (int)errno);
return E_FAIL;
}
for (i = 0; i < nbytes; i++)
fprintf(f, "%02X", bytes[i]);
fprintf(f, "\n");
fclose(f);
printf("%s: Generated random FeliCa ID\n", module);
return S_OK;
}
#pragma endregion #pragma endregion
#pragma region READER SPECIFIC #pragma region READER SPECIFIC
@ -197,20 +139,8 @@ static unsigned int __stdcall reader_poll_thread_proc(void *ctx)
printf("DEBUG: reader_poll_thread_proc(). \r\n"); printf("DEBUG: reader_poll_thread_proc(). \r\n");
while (!READER_POLL_STOP_FLAG) while (!READER_POLL_STOP_FLAG)
{ {
if (!HasCard && polling) if (card_data.card_type != 0) // Halting polling once a card is found, waiting for the game to read it's value.
{ scard_poll(&card_data);
uint8_t _UID[8] = {0};
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]);
for (int i = 0; i < 8; i++)
UID[i] = _UID[i];
HasCard = true;
}
}
} }
return 0; return 0;
@ -233,26 +163,26 @@ HRESULT aime_io_init(void)
if (ret != 0) if (ret != 0)
freopen_s(&fp, "CONOUT$", "w", stdout); // only when we allocate a console, we need to redirect stdout freopen_s(&fp, "CONOUT$", "w", stdout); // only when we allocate a console, we need to redirect stdout
memset(&card_data, 0, sizeof(card_data)); // Init card_data structure
// We then read the segatools config file to get settings. // We then read the segatools config file to get settings.
aime_io_config_read(&aime_io_cfg, L".\\segatools.ini"); aime_io_config_read(&aime_io_cfg, L".\\segatools.ini");
if (aime_io_cfg.debug)
printf("DEBUG: aime_io_init(). \r\n");
// Find and initialize reader(s) // Find and initialize reader(s)
if (!READER_RUNNER_INITIALIZED) printf("aime_io_init: Initializing SmartCard\n");
if (!scard_init(aime_io_cfg))
{ {
READER_RUNNER_INITIALIZED = true; // If we couldn't init reader, error out.
printf("%s: Initializing SmartCard\n", module); printf("aime_io_init: Couldn't init SmartCard\n");
if (!aime_io_cfg.reader_optional)
if (!scard_init())
{
printf("%s: Couldn't init SmartCard\n", module);
return E_FAIL; return E_FAIL;
}
// If however the readerOptional flag is set to 1 in segatools.ini, continue with keyboard only.
printf("aime_io_init: Reader is optional, using keyboard only !\n");
return S_OK;
} }
printf("%s: Starting reader thread.\n", module); printf("aime_io_init: Starting reader thread.\n");
// Start reader thread // Start reader thread
READER_POLL_STOP_FLAG = false; READER_POLL_STOP_FLAG = false;
@ -275,8 +205,6 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
if (unit_no != 0) if (unit_no != 0)
return S_OK; return S_OK;
polling = true;
bool sense; bool sense;
HRESULT hr; HRESULT hr;
@ -284,8 +212,6 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000; sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
if (!sense) if (!sense)
{ {
aime_io_aime_id_present = false;
aime_io_felica_id_present = false;
return S_OK; return S_OK;
} }
@ -301,48 +227,33 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
} }
} }
printf("%s: Attempting to read card %d from file. \r\n", module, card); printf("aime_io_nfc_poll: Attempting to read card %d from file. \r\n", card);
// Try AiMe IC // Try AiMe IC
hr = aime_io_read_id_file( hr = aime_io_read_id_file(
aime_io_cfg.aime_path, aime_io_cfg.aime_path,
aime_io_aime_id, card_data.card_id,
sizeof(aime_io_aime_id), 10,
card); card);
if (SUCCEEDED(hr) && hr != S_FALSE) if (SUCCEEDED(hr) && hr != S_FALSE)
{ {
aime_io_aime_id_present = true; card_data.card_type = Mifare;
return S_OK; return S_OK;
} }
// Try FeliCa IC // Try FeliCa IC
hr = aime_io_read_id_file( hr = aime_io_read_id_file(
aime_io_cfg.felica_path, aime_io_cfg.felica_path,
aime_io_felica_id, card_data.card_id,
sizeof(aime_io_felica_id), 8,
card); card);
if (SUCCEEDED(hr) && hr != S_FALSE) if (SUCCEEDED(hr) && hr != S_FALSE)
{ {
aime_io_felica_id_present = true; card_data.card_type = FeliCa;
return S_OK; 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; return S_OK;
} }
@ -352,18 +263,17 @@ HRESULT aime_io_nfc_get_aime_id(uint8_t unit_no, uint8_t *luid, size_t luid_size
printf("DEBUG: aime_io_nfc_get_aime_id(unit_no : %d). \r\n", unit_no); printf("DEBUG: aime_io_nfc_get_aime_id(unit_no : %d). \r\n", unit_no);
assert(luid != NULL); assert(luid != NULL);
assert(luid_size == sizeof(aime_io_aime_id)); assert(luid_size == 10);
if (unit_no != 0) if (unit_no != 0)
return S_FALSE; return S_FALSE;
if (aime_io_aime_id_present) if (card_data.card_type == Mifare)
{ {
memcpy(luid, aime_io_aime_id, luid_size); memcpy(luid, card_data.card_id, luid_size);
printf("%s: Read Aime card from file with uid ", module); printf("aime_io_nfc_get_aime_id: Read Aime card from file with uid %02X%02X %02X%02X %02X%02X %02X%02X %02X%02X\r\n", card_data.card_id[0], card_data.card_id[1], card_data.card_id[2], card_data.card_id[3], card_data.card_id[4], card_data.card_id[5], card_data.card_id[6], card_data.card_id[7], card_data.card_id[8], card_data.card_id[9]);
for (int i = 0; i < 10; i++)
printf("%02x ", aime_io_aime_id[i]); memset(&card_data, 0, sizeof(card_data)); // Reset card_data structure
printf("\r\n");
return S_OK; return S_OK;
} }
@ -382,33 +292,34 @@ HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
if (unit_no != 0) if (unit_no != 0)
return S_FALSE; return S_FALSE;
if (aime_io_felica_id_present) if (card_data.card_type == FeliCa)
{ {
val = 0; val = 0;
for (i = 0; i < 8; i++) for (i = 0; i < 8; i++)
val = (val << 8) | aime_io_felica_id[i]; val = (val << 8) | card_data.card_id[i];
*IDm = val; *IDm = val;
printf("%s: Read FeliCa card from file with uid %llx\r\n", module, val); printf("aime_io_nfc_get_felica_id: Read FeliCa card from file with uid %02X%02X %02X%02X %02X%02X %02X%02X\r\n", card_data.card_id[0], card_data.card_id[1], card_data.card_id[2], card_data.card_id[3], card_data.card_id[4], card_data.card_id[5], card_data.card_id[6], card_data.card_id[7]);
memset(&card_data, 0, sizeof(card_data)); // Reset card_data structure
return S_OK; return S_OK;
} }
if (HasCard) // if (HasCard)
{ // {
polling = false; // polling = false;
HasCard = false; // HasCard = false;
uint64_t val; // uint64_t val;
for (int i = 0; i < 8; i++) // for (int i = 0; i < 8; i++)
{ // {
val = (val << 8) | UID[i]; // val = (val << 8) | UID[i];
} // }
*IDm = val; // *IDm = val;
printf("%s: FeliCa card has been scanned ! %llx\r\n", module, val); // printf("aime_io_nfc_get_felica_id: FeliCa card has been scanned ! %llx\r\n", val);
return S_OK; // return S_OK;
} // }
return S_FALSE; return S_FALSE;
} }

View File

@ -1,9 +1,23 @@
#pragma once #pragma once
#include <windows.h> #include <windows.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h> #include <stddef.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct aime_io_config
{
bool debug;
wchar_t aime_path[MAX_PATH];
wchar_t felica_path[MAX_PATH];
wchar_t reader_name[MAX_PATH];
bool reader_optional;
uint8_t vk_scan;
};
/* /*
Get the version of the Aime IO API that this DLL supports. This function Get the version of the Aime IO API that this DLL supports. This function

View File

@ -14,16 +14,16 @@ int main()
switch (aime_io_init()) switch (aime_io_init())
{ {
case E_FAIL: case E_FAIL:
printf("aime_io_init() returned E_FAIL. Reader is either missing or incompatible !\r\n"); printf("AIMETEST: aime_io_init() returned E_FAIL. Reader is either missing or incompatible !\r\n");
break; return E_FAIL;
case S_OK: case S_OK:
printf("aime_io_init() returned S_OK !\r\n"); printf("AIMETEST: aime_io_init() returned S_OK !\r\n");
break; break;
default: default:
printf("aime_io_init() returned an unknown state !\r\n"); printf("AIMETEST: aime_io_init() returned an unknown state !\r\n");
break; return E_FAIL;
} }
// printf("aime_io_led_set_color(red) : "); // printf("aime_io_led_set_color(red) : ");
@ -40,7 +40,7 @@ int main()
// Sleep(2000); // Sleep(2000);
// aime_io_led_set_color(0, 0, 0, 0); // aime_io_led_set_color(0, 0, 0, 0);
printf("Running input loop. Press Ctrl+C to exit.\r\n"); printf("AIMETEST: Running input loop. Press Ctrl+C to exit.\r\n");
uint8_t luid[10] = {0}; uint8_t luid[10] = {0};
uint64_t IDm = 0; uint64_t IDm = 0;
@ -51,12 +51,12 @@ int main()
if (aime_io_nfc_get_felica_id(0, &IDm) == S_OK) if (aime_io_nfc_get_felica_id(0, &IDm) == S_OK)
{ {
// aime_io_led_set_color(0, 0, 255, 0); // aime_io_led_set_color(0, 0, 255, 0);
printf("Found FeliCa card with uid %llx\r\n\n", IDm); printf("AIMETEST: Found FeliCa card with uid %llx\r\n\n", IDm);
} }
if (aime_io_nfc_get_aime_id(0, luid, 10) == S_OK) if (aime_io_nfc_get_aime_id(0, luid, 10) == S_OK)
{ {
// aime_io_led_set_color(0, 0, 0, 255); // aime_io_led_set_color(0, 0, 0, 255);
printf("Found old card with uid "); printf("AIMETEST: Found old card with uid ");
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
printf("%02x ", luid[i]); printf("%02x ", luid[i]);

View File

@ -1,33 +1,4 @@
/**
* MIT-License
* Copyright (c) 2018 by nolm <nolan@nolm.name>
*
* 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 "scard.h"
// #include <tchar.h>
// #include <thread>
extern char module[];
#define MAX_APDU_SIZE 255 #define MAX_APDU_SIZE 255
int readCooldown = 500; int readCooldown = 500;
@ -35,35 +6,168 @@ int readCooldown = 500;
// based off acr122u reader, see page 26 in api document. // based off acr122u reader, see page 26 in api document.
// https://www.acs.com.hk/en/download-manual/419/API-ACR122U-2.04.pdf // https://www.acs.com.hk/en/download-manual/419/API-ACR122U-2.04.pdf
#define PICC_OPERATING_PARAMS 0xDFu #define PARAM_POLLRATE 0xDFu
BYTE PICC_OPERATING_PARAM_CMD[5] = {0xFFu, 0x00u, 0x51u, PICC_OPERATING_PARAMS, 0x00u}; static const BYTE PARAM_SET_PICC[5] = {0xFFu, 0x00u, 0x51u, PARAM_POLLRATE, 0x00u};
static const BYTE COMMAND_GET_UID[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u};
// return bytes from device // return bytes from device
#define PICC_SUCCESS 0x90u #define PICC_SUCCESS 0x90u
#define PICC_ERROR 0x63u #define PICC_ERROR 0x63u
static const BYTE UID_CMD[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u};
enum scard_atr_protocol enum scard_atr_protocol
{ {
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03, SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B,
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11, SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
SCARD_ATR_PROTOCOL_FELICA_424K = 0x12,
}; };
// winscard_config_t WINSCARD_CONFIG; // winscard_config_t WINSCARD_CONFIG;
SCARDCONTEXT hContext = 0; static SCARDCONTEXT hContext = 0;
SCARD_READERSTATE reader_states[2]; static SCARD_READERSTATE reader_state;
LPTSTR reader_name_slots[2] = {NULL, NULL}; static LONG lRet = 0;
int reader_count = 0;
LONG lRet = 0;
void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no) bool scard_init(struct aime_io_config config)
{ {
printf("%s: Update on reader : %s\n", module, reader_states[unit_no].szReader); 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 FALSE;
}
// get list of readers
LPTSTR reader_list = NULL;
auto pcchReaders = SCARD_AUTOALLOCATE;
lRet = SCardListReaders(hContext, NULL, (LPTSTR)&reader_list, &pcchReaders);
switch (lRet)
{
case SCARD_E_NO_READERS_AVAILABLE:
printf("scard_init: No readers available\n");
return FALSE;
case SCARD_S_SUCCESS:
LPTSTR reader_name = NULL;
int readerNameLen = 0;
// Iterate through the multi-string to get individual reader names
printf("scard_init: listing all readers : ");
while (*reader_list != '\0')
{
printf("%s, ", reader_list);
reader_list += strlen(reader_list) + 1;
}
printf("\r\n", reader_list);
// if the readerName array is populated, replace the first reader in the list
char ReaderCharArray[sizeof(config.reader_name)];
wcstombs(ReaderCharArray, config.reader_name, sizeof(ReaderCharArray));
if (strcmp(ReaderCharArray, "") != 0)
{
size_t newLen = strlen(ReaderCharArray) + 1;
LPSTR newMszReaders = (LPSTR)malloc(newLen);
if (newMszReaders != NULL)
{
// Copy the new selected reader
strcpy(newMszReaders, ReaderCharArray);
// Update the original pointer to the new modified list
reader_list = newMszReaders;
}
printf("scard_init: Forced using reader : %hs\n", ReaderCharArray);
}
// Connect to reader and send PICC operating params command
SCARDHANDLE hCard;
DWORD dwActiveProtocol;
lRet = SCardConnect(hContext, reader_list, SCARD_SHARE_DIRECT, 0, &hCard, &dwActiveProtocol);
if (lRet != SCARD_S_SUCCESS)
{
printf("scard_init: Error connecting to the reader: 0x%08X\n", lRet);
return FALSE;
}
printf("scard_init: Connected to reader: %s, sending PICC params\n", reader_list);
// set the reader params
DWORD cbRecv = MAX_APDU_SIZE;
BYTE pbRecv[MAX_APDU_SIZE];
lRet = SCardControl(hCard, SCARD_CTL_CODE(3500), PARAM_SET_PICC, sizeof(PARAM_SET_PICC), pbRecv, cbRecv, &cbRecv);
Sleep(100);
if (lRet != SCARD_S_SUCCESS)
{
printf("scard_init: Error setting PICC params : 0x%08X\n", lRet);
return FALSE;
}
if (cbRecv > 2 && pbRecv[0] != PICC_SUCCESS && pbRecv[1] != PARAM_POLLRATE)
{
printf("scard_init: PICC params not valid 0x%02X != 0x%02X\n", pbRecv[1], PARAM_POLLRATE);
return FALSE;
}
// Disconnect from reader
if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS)
printf("scard_init: Failed SCardDisconnect : 0x%08X\n", lRet);
// Extract the relevant names from the multi-string.
readerNameLen = lstrlen(reader_list);
reader_name = (LPTSTR)HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, sizeof(TCHAR) * (readerNameLen + 1));
memcpy(reader_name, &reader_list, (size_t)(readerNameLen + 1));
if (reader_name)
printf("scard_init: Using reader : %s\n", reader_name);
memset(&reader_state, 0, sizeof(SCARD_READERSTATE));
reader_state.szReader = reader_name;
return TRUE;
default:
printf("scard_init: Failed SCardListReaders: 0x%08X\n", lRet);
return FALSE;
}
}
void scard_poll(struct card_data *card_data)
{
lRet = SCardGetStatusChange(hContext, readCooldown, &reader_state, 1);
if (lRet == SCARD_E_TIMEOUT)
{
return;
}
else if (lRet != SCARD_S_SUCCESS)
{
printf("scard_poll: Failed SCardGetStatusChange: 0x%08X\n", lRet);
return;
}
if (!(reader_state.dwEventState & SCARD_STATE_CHANGED))
return;
DWORD newState = reader_state.dwEventState ^ SCARD_STATE_CHANGED;
bool wasCardPresent = (reader_state.dwCurrentState & SCARD_STATE_PRESENT) > 0;
if (newState & SCARD_STATE_UNAVAILABLE)
{
printf("scard_poll: New card state: unavailable\n");
Sleep(readCooldown);
}
else if (newState & SCARD_STATE_EMPTY)
{
printf("scard_poll: New card state: empty\n");
// scard_clear(unit_no);
}
else if (newState & SCARD_STATE_PRESENT && !wasCardPresent)
{
printf("scard_poll: New card state: present\n");
scard_update(card_data, hContext, reader_state.szReader);
}
reader_state.dwCurrentState = reader_state.dwEventState;
return;
}
void scard_update(struct card_data *card_data, SCARDCONTEXT _hContext, LPCTSTR _readerName)
{
printf("scard_update: Update on reader : %s\n", reader_state.szReader);
// Connect to the smart card. // Connect to the smart card.
LONG lRet = 0;
SCARDHANDLE hCard; SCARDHANDLE hCard;
DWORD dwActiveProtocol; DWORD dwActiveProtocol;
for (int retry = 0; retry < 100; retry++) // retry times has to be increased since poll rate is set to 500ms for (int retry = 0; retry < 100; retry++) // retry times has to be increased since poll rate is set to 500ms
@ -76,12 +180,11 @@ void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8
if (lRet != SCARD_S_SUCCESS) if (lRet != SCARD_S_SUCCESS)
{ {
printf("%s: Error connecting to the card: 0x%08X\n", module, lRet); printf("scard_update: Error connecting to the card: 0x%08X\n", lRet);
return; return;
} }
// set the reader params // set the reader params
lRet = 0;
LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0; LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0;
DWORD cbRecv = MAX_APDU_SIZE; DWORD cbRecv = MAX_APDU_SIZE;
BYTE pbRecv[MAX_APDU_SIZE]; BYTE pbRecv[MAX_APDU_SIZE];
@ -94,263 +197,76 @@ void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8
lRet = SCardStatus(hCard, szReader, &cchReader, NULL, NULL, atr, &cByteAtr); lRet = SCardStatus(hCard, szReader, &cchReader, NULL, NULL, atr, &cByteAtr);
if (lRet != SCARD_S_SUCCESS) if (lRet != SCARD_S_SUCCESS)
{ {
printf("%s: Error getting card status: 0x%08X\n", module, lRet); printf("scard_update: Error getting card status: 0x%08X\n", lRet);
return; return;
} }
// Only care about 20-byte ATRs returned by arcade-type smart cards // Only care about 20-byte ATRs returned by arcade-type smart cards
if (cByteAtr != 20) if (cByteAtr != 20)
{ {
printf("%s: Ignoring card with len(%d) = %02X (%08X)\n", module, cByteAtr, atr, cByteAtr); printf("scard_update: Ignoring card with len(%zu) = %02x (%08X)\n", sizeof(cByteAtr), (unsigned int)atr, cByteAtr);
return; return;
} }
printf("%s: atr Return: len(%d) = %02X (%08X)\n", module, cByteAtr, atr, cByteAtr); printf("scard_update: atr Return: len(%zu) = %02x (%08X)\n", sizeof(cByteAtr), (unsigned int)atr, cByteAtr);
// Figure out if we should reverse the UID returned by the card based on the ATR protocol // Figure out if we should reverse the UID returned by the card based on the ATR protocol
BYTE cardProtocol = atr[12]; BYTE cardProtocol = atr[12];
BOOL shouldReverseUid = false;
if (cardProtocol == SCARD_ATR_PROTOCOL_ISO15693_PART3) if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3)
{ {
printf("%s: Card protocol: ISO15693_PART3\n", module); printf("scard_update: Card protocol: ISO14443_PART3\n");
shouldReverseUid = true; card_data->card_type = Mifare;
} }
else if (cardProtocol == SCARD_ATR_PROTOCOL_ISO14443_PART3) else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) // Handling FeliCa
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); printf("scard_update: Card protocol: FELICA_212K\n");
return; card_data->card_type = FeliCa;
}
// Read UID // Read mID
cbRecv = MAX_APDU_SIZE; cbRecv = MAX_APDU_SIZE;
if ((lRet = SCardTransmit(hCard, pci, UID_CMD, sizeof(UID_CMD), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS) if ((lRet = SCardTransmit(hCard, pci, COMMAND_GET_UID, sizeof(COMMAND_GET_UID), NULL, pbRecv, &cbRecv)) != SCARD_S_SUCCESS)
{ {
printf("%s: Error querying card UID: 0x%08X\n", module, lRet); printf("scard_update: Error querying card UID: 0x%08X\n", lRet);
return; return;
} }
if (cbRecv > 1 && pbRecv[0] == PICC_ERROR) if (cbRecv > 1 && pbRecv[0] == PICC_ERROR)
{ {
printf("%s: UID query failed\n", module); printf("scard_update: UID query failed\n");
return; return;
} }
if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS) if ((lRet = SCardDisconnect(hCard, SCARD_LEAVE_CARD)) != SCARD_S_SUCCESS)
printf("%s: Failed SCardDisconnect: 0x%08X\n", module, lRet); printf("scard_update: Failed SCardDisconnect: 0x%08X\n", lRet);
if (cbRecv < 8) if (cbRecv < 8)
{ {
printf("%s: Padding card uid to 8 bytes\n", module); printf("scard_update: Padding card uid to 8 bytes\n");
memset(&pbRecv[cbRecv], 0, 8 - cbRecv); memset(&pbRecv[cbRecv], 0, 8 - cbRecv);
} }
else if (cbRecv > 8) else if (cbRecv > 8)
printf("%s: taking first 8 bytes of len(uid) = %02X\n", module, cbRecv); printf("scard_update: taking first 8 bytes of len(uid) = %02X\n", cbRecv);
card_data->card_id_len = 8;
memcpy(card_data->card_id, pbRecv, 8);
}
else
{
printf("scard_update: Unknown NFC Protocol: 0x%02X\n", cardProtocol);
return;
}
// Copy UID to struct, reversing if necessary // Copy UID to struct, reversing if necessary
card_info_t card_info; // card_info_t card_info;
if (shouldReverseUid) // if (shouldReverseUid)
for (DWORD i = 0; i < 8; i++) // for (DWORD i = 0; i < 8; i++)
card_info.uid[i] = pbRecv[7 - i]; // card_info.uid[i] = pbRecv[7 - i];
else // else
memcpy(card_info.uid, pbRecv, 8); // memcpy(card_info.uid, pbRecv, 8);
for (int i = 0; i < 8; ++i) // for (int i = 0; i < 8; ++i)
buf[i] = card_info.uid[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;
}
} }

View File

@ -24,23 +24,35 @@
*/ */
#pragma once #pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <winscard.h> #include <winscard.h>
#include <windows.h> #include <windows.h>
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <src/aimeio.h>
// cardinfo_t is a description of a card that was presented to a reader /* card types */
typedef struct card_info enum AIME_CARDTYPE
{ {
int card_type; Mifare = 0x01,
uint8_t uid[8]; FeliCa = 0x02,
} card_info_t; };
void scard_update(uint8_t *buf); // Structure containing card_type, card_id and card_id_len
struct card_data
{
/* CARDTYPE */
uint8_t card_type;
void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8_t unit_no); /* Card ID */
uint8_t card_id[32];
void scard_clear(uint8_t unitNo); /* Card ID length */
uint8_t card_id_len;
};
bool scard_init(); bool scard_init(struct aime_io_config config);
void scard_poll(struct card_data *card_data);
void scard_update(struct card_data *card_data, SCARDCONTEXT _hContext, LPCTSTR _readerName);