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:
parent
d794acb7b7
commit
3987e35b3d
207
src/aimeio.c
207
src/aimeio.c
@ -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 "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;
|
||||
|
||||
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 uint8_t aime_io_aime_id[10];
|
||||
static uint8_t aime_io_felica_id[8];
|
||||
static bool aime_io_aime_id_present;
|
||||
static bool aime_io_felica_id_present;
|
||||
static struct card_data card_data;
|
||||
|
||||
#pragma region CONFIG
|
||||
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),
|
||||
filename);
|
||||
|
||||
cfg->felica_gen = GetPrivateProfileIntW(
|
||||
GetPrivateProfileStringW(
|
||||
L"aimeio",
|
||||
L"felicaGen",
|
||||
1,
|
||||
L"readerName",
|
||||
L"",
|
||||
cfg->reader_name,
|
||||
_countof(cfg->reader_name),
|
||||
filename);
|
||||
|
||||
cfg->reader_optional = GetPrivateProfileIntW(
|
||||
L"aimeio",
|
||||
L"readerOptional",
|
||||
0,
|
||||
filename);
|
||||
|
||||
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 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;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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
|
||||
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;
|
||||
goto end;
|
||||
}
|
||||
@ -151,43 +130,6 @@ end:
|
||||
|
||||
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 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");
|
||||
while (!READER_POLL_STOP_FLAG)
|
||||
{
|
||||
if (!HasCard && polling)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -233,26 +163,26 @@ HRESULT aime_io_init(void)
|
||||
if (ret != 0)
|
||||
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.
|
||||
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)
|
||||
if (!READER_RUNNER_INITIALIZED)
|
||||
printf("aime_io_init: Initializing SmartCard\n");
|
||||
if (!scard_init(aime_io_cfg))
|
||||
{
|
||||
READER_RUNNER_INITIALIZED = true;
|
||||
printf("%s: Initializing SmartCard\n", module);
|
||||
|
||||
if (!scard_init())
|
||||
{
|
||||
printf("%s: Couldn't init SmartCard\n", module);
|
||||
// If we couldn't init reader, error out.
|
||||
printf("aime_io_init: Couldn't init SmartCard\n");
|
||||
if (!aime_io_cfg.reader_optional)
|
||||
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
|
||||
READER_POLL_STOP_FLAG = false;
|
||||
@ -275,8 +205,6 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||
if (unit_no != 0)
|
||||
return S_OK;
|
||||
|
||||
polling = true;
|
||||
|
||||
bool sense;
|
||||
HRESULT hr;
|
||||
|
||||
@ -284,8 +212,6 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
|
||||
if (!sense)
|
||||
{
|
||||
aime_io_aime_id_present = false;
|
||||
aime_io_felica_id_present = false;
|
||||
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
|
||||
hr = aime_io_read_id_file(
|
||||
aime_io_cfg.aime_path,
|
||||
aime_io_aime_id,
|
||||
sizeof(aime_io_aime_id),
|
||||
card_data.card_id,
|
||||
10,
|
||||
card);
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE)
|
||||
{
|
||||
aime_io_aime_id_present = true;
|
||||
card_data.card_type = Mifare;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Try FeliCa IC
|
||||
hr = aime_io_read_id_file(
|
||||
aime_io_cfg.felica_path,
|
||||
aime_io_felica_id,
|
||||
sizeof(aime_io_felica_id),
|
||||
card_data.card_id,
|
||||
8,
|
||||
card);
|
||||
|
||||
if (SUCCEEDED(hr) && hr != S_FALSE)
|
||||
{
|
||||
aime_io_felica_id_present = true;
|
||||
card_data.card_type = FeliCa;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
assert(luid != NULL);
|
||||
assert(luid_size == sizeof(aime_io_aime_id));
|
||||
assert(luid_size == 10);
|
||||
|
||||
if (unit_no != 0)
|
||||
return S_FALSE;
|
||||
|
||||
if (aime_io_aime_id_present)
|
||||
if (card_data.card_type == Mifare)
|
||||
{
|
||||
memcpy(luid, aime_io_aime_id, luid_size);
|
||||
printf("%s: Read Aime card from file with uid ", module);
|
||||
for (int i = 0; i < 10; i++)
|
||||
printf("%02x ", aime_io_aime_id[i]);
|
||||
printf("\r\n");
|
||||
memcpy(luid, card_data.card_id, luid_size);
|
||||
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]);
|
||||
|
||||
memset(&card_data, 0, sizeof(card_data)); // Reset card_data structure
|
||||
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)
|
||||
return S_FALSE;
|
||||
|
||||
if (aime_io_felica_id_present)
|
||||
if (card_data.card_type == FeliCa)
|
||||
{
|
||||
val = 0;
|
||||
for (i = 0; i < 8; i++)
|
||||
val = (val << 8) | aime_io_felica_id[i];
|
||||
val = (val << 8) | card_data.card_id[i];
|
||||
|
||||
*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;
|
||||
}
|
||||
|
||||
if (HasCard)
|
||||
{
|
||||
polling = false;
|
||||
HasCard = false;
|
||||
// if (HasCard)
|
||||
// {
|
||||
// polling = false;
|
||||
// HasCard = false;
|
||||
|
||||
uint64_t val;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
val = (val << 8) | UID[i];
|
||||
}
|
||||
*IDm = val;
|
||||
printf("%s: FeliCa card has been scanned ! %llx\r\n", module, val);
|
||||
// uint64_t val;
|
||||
// for (int i = 0; i < 8; i++)
|
||||
// {
|
||||
// val = (val << 8) | UID[i];
|
||||
// }
|
||||
// *IDm = 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;
|
||||
}
|
||||
|
||||
|
16
src/aimeio.h
16
src/aimeio.h
@ -1,9 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.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
|
||||
|
@ -14,16 +14,16 @@ int main()
|
||||
switch (aime_io_init())
|
||||
{
|
||||
case E_FAIL:
|
||||
printf("aime_io_init() returned E_FAIL. Reader is either missing or incompatible !\r\n");
|
||||
break;
|
||||
printf("AIMETEST: aime_io_init() returned E_FAIL. Reader is either missing or incompatible !\r\n");
|
||||
return E_FAIL;
|
||||
|
||||
case S_OK:
|
||||
printf("aime_io_init() returned S_OK !\r\n");
|
||||
printf("AIMETEST: aime_io_init() returned S_OK !\r\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("aime_io_init() returned an unknown state !\r\n");
|
||||
break;
|
||||
printf("AIMETEST: aime_io_init() returned an unknown state !\r\n");
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// printf("aime_io_led_set_color(red) : ");
|
||||
@ -40,7 +40,7 @@ int main()
|
||||
// Sleep(2000);
|
||||
// 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};
|
||||
uint64_t IDm = 0;
|
||||
@ -51,12 +51,12 @@ int main()
|
||||
if (aime_io_nfc_get_felica_id(0, &IDm) == S_OK)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
// 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++)
|
||||
{
|
||||
printf("%02x ", luid[i]);
|
||||
|
@ -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 <tchar.h>
|
||||
// #include <thread>
|
||||
|
||||
extern char module[];
|
||||
|
||||
#define MAX_APDU_SIZE 255
|
||||
int readCooldown = 500;
|
||||
@ -35,35 +6,168 @@ int readCooldown = 500;
|
||||
// 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};
|
||||
#define PARAM_POLLRATE 0xDFu
|
||||
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
|
||||
#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;
|
||||
static SCARDCONTEXT hContext = 0;
|
||||
static SCARD_READERSTATE reader_state;
|
||||
static 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.
|
||||
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
|
||||
@ -76,12 +180,11 @@ void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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];
|
||||
@ -94,263 +197,76 @@ void scard_poll(uint8_t *buf, SCARDCONTEXT _hContext, LPCTSTR _readerName, uint8
|
||||
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);
|
||||
printf("scard_update: Error getting card status: 0x%08X\n", 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);
|
||||
printf("scard_update: Ignoring card with len(%zu) = %02x (%08X)\n", sizeof(cByteAtr), (unsigned int)atr, cByteAtr);
|
||||
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
|
||||
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);
|
||||
shouldReverseUid = true;
|
||||
printf("scard_update: Card protocol: ISO14443_PART3\n");
|
||||
card_data->card_type = Mifare;
|
||||
}
|
||||
|
||||
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
|
||||
else if (cardProtocol == SCARD_ATR_PROTOCOL_FELICA_212K) // Handling FeliCa
|
||||
{
|
||||
printf("%s: Unknown NFC Protocol: 0x%02X\n", module, cardProtocol);
|
||||
return;
|
||||
}
|
||||
printf("scard_update: Card protocol: FELICA_212K\n");
|
||||
card_data->card_type = FeliCa;
|
||||
|
||||
// Read UID
|
||||
// Read mID
|
||||
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;
|
||||
}
|
||||
|
||||
if (cbRecv > 1 && pbRecv[0] == PICC_ERROR)
|
||||
{
|
||||
printf("%s: UID query failed\n", module);
|
||||
printf("scard_update: UID query failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
// for (int i = 0; i < 8; ++i)
|
||||
// buf[i] = card_info.uid[i];
|
||||
}
|
@ -24,23 +24,35 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <winscard.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
|
||||
typedef struct card_info
|
||||
/* card types */
|
||||
enum AIME_CARDTYPE
|
||||
{
|
||||
int card_type;
|
||||
uint8_t uid[8];
|
||||
} card_info_t;
|
||||
Mifare = 0x01,
|
||||
FeliCa = 0x02,
|
||||
};
|
||||
|
||||
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);
|
||||
|
Loading…
Reference in New Issue
Block a user