partial : using nat's code to handle aime cards
This commit is contained in:
parent
6bcc546b4b
commit
b1f9dcf0b2
16
readme.md
16
readme.md
@ -18,23 +18,23 @@ To use it with a game, copy `aimeio.dll` to your `segatools` folder and add the
|
|||||||
|
|
||||||
```ini
|
```ini
|
||||||
[aimeio]
|
[aimeio]
|
||||||
path=aimeio.dll
|
path=aimeio.dll ;Path to aimeio.dll
|
||||||
scan=0x0D ;Sets the key which will be used to insert a card in game. The default is 'Return'
|
scan=0x0D ;Sets the key which will be used to insert a card in game. The default is 'Return'
|
||||||
|
|
||||||
|
|
||||||
;Everything below this line is optional.
|
;Everything below this line is optional.
|
||||||
|
|
||||||
;aimePath= ;Manually specify an aime.txt file
|
;readerOptional=1 ;Make reader optional, so that you can still use the keyboard
|
||||||
;felicaPath= ;Manually specify a felica.txt file
|
;readerName="" ;Force using a reader by setting it's name (in case multiple readers are detected)
|
||||||
;felicaGen=0 ;Generate a new random card if it's missing from the file
|
;aimePath="" ;Manually specify an aime.txt file
|
||||||
;debug=0 ;Display function calls
|
;felicaPath="" ;Manually specify a felica.txt file
|
||||||
|
;debug=0 ;Display function calls
|
||||||
```
|
```
|
||||||
|
|
||||||
## Scanning cards
|
## Scanning cards
|
||||||
|
|
||||||
If you scan a newer AIC-based Aime, its FeliCa IDm will be provided to the game. The game will not see the correct "access code," but the IDm should be unique to each card so that particular card can still track your plays.
|
If you scan a newer AIC-based Aime, its FeliCa IDm will be provided to the game. The game will not see the correct "access code," but the IDm should be unique to each card so that particular card can still track your plays.
|
||||||
|
|
||||||
As for Mifare cards, their access code won't be 1:1 with a real reader (i still need to fix this). You can still use them and they will work though !
|
|
||||||
|
|
||||||
## Inserting cards
|
## Inserting cards
|
||||||
|
|
||||||
By pressing the key you have set in segatools.ini and holding it for a few moments, you will insert a card set in either aime.txt or felica.txt
|
By pressing the key you have set in segatools.ini and holding it for a few moments, you will insert a card set in either aime.txt or felica.txt
|
||||||
|
227
src/aimeio.c
227
src/aimeio.c
@ -1,42 +1,16 @@
|
|||||||
#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";
|
static struct scard_context *ctx;
|
||||||
|
static struct card_data card_data;
|
||||||
|
|
||||||
// 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_aime_id[10];
|
||||||
static uint8_t aime_io_felica_id[8];
|
static uint8_t aime_io_felica_id[8];
|
||||||
static bool aime_io_aime_id_present;
|
static bool aime_io_aime_id_present;
|
||||||
static bool aime_io_felica_id_present;
|
static bool aime_io_felica_id_present;
|
||||||
|
|
||||||
|
static struct aime_io_config aime_io_cfg;
|
||||||
|
|
||||||
#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 +39,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(
|
||||||
@ -92,7 +74,10 @@ static HRESULT aime_io_read_id_file(const wchar_t *path, uint8_t *bytes, size_t
|
|||||||
|
|
||||||
f = _wfopen(path, L"r");
|
f = _wfopen(path, L"r");
|
||||||
if (f == NULL) // If the file doesn't exist, we bail.
|
if (f == NULL) // If the file doesn't exist, we bail.
|
||||||
|
{
|
||||||
|
printf("AimeIO: %S: fopen failed: %i\n", path, (int)errno);
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
memset(bytes, 0, nbytes);
|
memset(bytes, 0, nbytes);
|
||||||
|
|
||||||
@ -104,7 +89,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("AimeIO: %S: Error: Line %d does not exist\n", path, LineToRead);
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -125,7 +110,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("AimeIO: %S: Error parsing line %d\n", path, LineToRead);
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -136,7 +121,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("AimeIO: %S: Error: Line %d is not %zu bytes long\n", path, LineToRead, nbytes);
|
||||||
hr = E_FAIL;
|
hr = E_FAIL;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
@ -152,69 +137,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 region READER SPECIFIC
|
|
||||||
static unsigned int __stdcall reader_poll_thread_proc(void *ctx)
|
|
||||||
{
|
|
||||||
if (aime_io_cfg.debug)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region AIME
|
#pragma region AIME
|
||||||
@ -239,32 +161,43 @@ HRESULT aime_io_init(void)
|
|||||||
if (aime_io_cfg.debug)
|
if (aime_io_cfg.debug)
|
||||||
printf("DEBUG: aime_io_init(). \r\n");
|
printf("DEBUG: aime_io_init(). \r\n");
|
||||||
|
|
||||||
// Find and initialize reader(s)
|
// we create the context for the scard reader
|
||||||
if (!READER_RUNNER_INITIALIZED)
|
ctx = scard_create();
|
||||||
{
|
if (!ctx)
|
||||||
READER_RUNNER_INITIALIZED = true;
|
goto error;
|
||||||
printf("%s: Initializing SmartCard\n", module);
|
|
||||||
|
|
||||||
if (!scard_init())
|
// we then init with the first detected card reader
|
||||||
{
|
ret = scard_init(ctx, aime_io_cfg);
|
||||||
printf("%s: Couldn't init SmartCard\n", module);
|
if (ret != 0)
|
||||||
return E_FAIL;
|
{
|
||||||
}
|
printf("SCard: failed to initialize: %s\n", scard_error(ctx));
|
||||||
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s: Starting reader thread.\n", module);
|
ret = scard_set_picc(ctx);
|
||||||
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
printf("SCard: failed to set PICC params for %s : %s\n", scard_reader_name(ctx), scard_error(ctx));
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
// Start reader thread
|
printf("SCard: initialized with reader: %s\n", scard_reader_name(ctx));
|
||||||
READER_POLL_STOP_FLAG = false;
|
|
||||||
READER_POLL_THREAD = (HANDLE)_beginthreadex(
|
|
||||||
NULL,
|
|
||||||
0,
|
|
||||||
reader_poll_thread_proc,
|
|
||||||
NULL,
|
|
||||||
0,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
||||||
|
error:
|
||||||
|
printf("SCard: failed to init reader %s...", scard_reader_name(ctx));
|
||||||
|
scard_destroy(ctx);
|
||||||
|
ctx = NULL;
|
||||||
|
|
||||||
|
if (aime_io_cfg.reader_optional) // We can skip to continue using keyboard
|
||||||
|
{
|
||||||
|
printf(" but reader is optional. Continuing !\n");
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
||||||
@ -275,12 +208,21 @@ 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;
|
int ret;
|
||||||
|
|
||||||
bool sense;
|
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
|
||||||
|
if (ctx != NULL)
|
||||||
|
{
|
||||||
|
memset(&card_data, 0, sizeof(card_data));
|
||||||
|
ret = scard_poll(ctx, &card_data);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
printf("SCard: Poll failed: %s\n", scard_error(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Don't do anything more if the scan key is not held
|
// Don't do anything more if the scan key is not held
|
||||||
|
bool sense;
|
||||||
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
|
sense = GetAsyncKeyState(aime_io_cfg.vk_scan) & 0x8000;
|
||||||
if (!sense)
|
if (!sense)
|
||||||
{
|
{
|
||||||
@ -301,14 +243,10 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("%s: Attempting to read card %d from file. \r\n", module, card);
|
printf("AimeIO: 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_aime_id, sizeof(aime_io_aime_id), card);
|
||||||
aime_io_cfg.aime_path,
|
|
||||||
aime_io_aime_id,
|
|
||||||
sizeof(aime_io_aime_id),
|
|
||||||
card);
|
|
||||||
|
|
||||||
if (SUCCEEDED(hr) && hr != S_FALSE)
|
if (SUCCEEDED(hr) && hr != S_FALSE)
|
||||||
{
|
{
|
||||||
@ -329,20 +267,6 @@ HRESULT aime_io_nfc_poll(uint8_t unit_no)
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +284,7 @@ HRESULT aime_io_nfc_get_aime_id(uint8_t unit_no, uint8_t *luid, size_t luid_size
|
|||||||
if (aime_io_aime_id_present)
|
if (aime_io_aime_id_present)
|
||||||
{
|
{
|
||||||
memcpy(luid, aime_io_aime_id, luid_size);
|
memcpy(luid, aime_io_aime_id, luid_size);
|
||||||
printf("%s: Read Aime card from file with uid ", module);
|
printf("AimeIO: Read Aime card from file with uid ");
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
printf("%02x ", aime_io_aime_id[i]);
|
printf("%02x ", aime_io_aime_id[i]);
|
||||||
printf("\r\n");
|
printf("\r\n");
|
||||||
@ -389,26 +313,11 @@ HRESULT aime_io_nfc_get_felica_id(uint8_t unit_no, uint64_t *IDm)
|
|||||||
val = (val << 8) | aime_io_felica_id[i];
|
val = (val << 8) | aime_io_felica_id[i];
|
||||||
|
|
||||||
*IDm = val;
|
*IDm = val;
|
||||||
printf("%s: Read FeliCa card from file with uid %llx\r\n", module, val);
|
printf("AimeIO: Read FeliCa card from file with uid %llx\r\n", val);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
return S_FALSE;
|
return S_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
15
src/aimeio.h
15
src/aimeio.h
@ -2,8 +2,23 @@
|
|||||||
|
|
||||||
#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
|
||||||
|
@ -9,21 +9,22 @@
|
|||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
printf("AIMETEST\r\n---------\r\n");
|
printf("AIMETEST\r\n---------\r\n");
|
||||||
|
Sleep(500);
|
||||||
// printf("api version = %04x\r\n", chuni_io_get_api_version()); /* not compatible with older dlls */
|
// printf("api version = %04x\r\n", chuni_io_get_api_version()); /* not compatible with older dlls */
|
||||||
printf("aime_io_init() : \n");
|
printf("AIMETEST: aime_io_init() : \n");
|
||||||
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;
|
exit(1);
|
||||||
|
|
||||||
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;
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// printf("aime_io_led_set_color(red) : ");
|
// printf("aime_io_led_set_color(red) : ");
|
||||||
@ -40,7 +41,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 +52,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]);
|
||||||
|
@ -1,356 +1,258 @@
|
|||||||
/**
|
|
||||||
* 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 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};
|
||||||
|
|
||||||
#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_SUCCESS 0x90u
|
||||||
#define PICC_ERROR 0x63u
|
#define PICC_ERROR 0x63u
|
||||||
|
|
||||||
static const BYTE UID_CMD[5] = {0xFFu, 0xCAu, 0x00u, 0x00u, 0x00u};
|
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};
|
||||||
|
|
||||||
enum scard_atr_protocol
|
// 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,
|
||||||
|
// };
|
||||||
|
|
||||||
|
struct scard_context *scard_create(void)
|
||||||
{
|
{
|
||||||
SCARD_ATR_PROTOCOL_ISO14443_PART3 = 0x03,
|
struct scard_context *ctx;
|
||||||
SCARD_ATR_PROTOCOL_ISO15693_PART3 = 0x0B,
|
|
||||||
SCARD_ATR_PROTOCOL_FELICA_212K = 0x11,
|
|
||||||
SCARD_ATR_PROTOCOL_FELICA_424K = 0x12,
|
|
||||||
};
|
|
||||||
|
|
||||||
// winscard_config_t WINSCARD_CONFIG;
|
ctx = (struct scard_context *)malloc(sizeof(struct scard_context));
|
||||||
SCARDCONTEXT hContext = 0;
|
if (!ctx)
|
||||||
SCARD_READERSTATE reader_states[2];
|
return NULL;
|
||||||
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)
|
memset(ctx, 0, sizeof(struct scard_context));
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scard_destroy(struct scard_context *ctx)
|
||||||
{
|
{
|
||||||
printf("%s: Update on reader : %s\n", module, reader_states[unit_no].szReader);
|
free(ctx);
|
||||||
// Connect to the smart card.
|
}
|
||||||
|
|
||||||
|
int scard_init(struct scard_context *ctx, struct aime_io_config config)
|
||||||
|
{
|
||||||
|
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 (config.debug)
|
||||||
|
{
|
||||||
|
// Iterate through the multi-string to get individual reader names
|
||||||
|
printf("SCard: listing all readers : ");
|
||||||
|
LPSTR pReader = ctx->mszReaders;
|
||||||
|
while (*pReader != '\0')
|
||||||
|
{
|
||||||
|
printf("%s, ", pReader);
|
||||||
|
pReader += strlen(pReader) + 1;
|
||||||
|
}
|
||||||
|
printf("\r\n", pReader);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
ctx->mszReaders = newMszReaders;
|
||||||
|
}
|
||||||
|
printf("SCard: Forced using reader : %hs\n", ReaderCharArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scard_set_picc(struct scard_context *ctx)
|
||||||
|
{
|
||||||
|
// Connect to reader and send PICC operating params command
|
||||||
LONG lRet = 0;
|
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
|
lRet = SCardConnect(ctx->hContext, ctx->mszReaders, SCARD_SHARE_DIRECT, 0, &hCard, &dwActiveProtocol);
|
||||||
{
|
|
||||||
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)
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
{
|
{
|
||||||
printf("%s: Error connecting to the card: 0x%08X\n", module, lRet);
|
snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardConnect failed: 0x%08lX", lRet);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("SCard: Sending PICC params for %s\n", ctx->mszReaders);
|
||||||
|
|
||||||
// set the reader params
|
// set the reader params
|
||||||
lRet = 0;
|
DWORD cbRecv = 255;
|
||||||
LPCSCARD_IO_REQUEST pci = dwActiveProtocol == SCARD_PROTOCOL_T1 ? SCARD_PCI_T1 : SCARD_PCI_T0;
|
BYTE pbRecv[sizeof(cbRecv)];
|
||||||
DWORD cbRecv = MAX_APDU_SIZE;
|
lRet = SCardControl(hCard, SCARD_CTL_CODE(3500), PARAM_SET_PICC, sizeof(PARAM_SET_PICC), pbRecv, cbRecv, &cbRecv);
|
||||||
BYTE pbRecv[MAX_APDU_SIZE];
|
Sleep(100);
|
||||||
|
|
||||||
// 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)
|
if (lRet != SCARD_S_SUCCESS)
|
||||||
{
|
{
|
||||||
printf("%s: Error getting card status: 0x%08X\n", module, lRet);
|
snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardControl failed: 0x%08lX", lRet);
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only care about 20-byte ATRs returned by arcade-type smart cards
|
if (cbRecv > 2 && pbRecv[0] != PICC_SUCCESS && pbRecv[1] != PARAM_POLLRATE)
|
||||||
if (cByteAtr != 20)
|
|
||||||
{
|
{
|
||||||
printf("%s: Ignoring card with len(%d) = %02X (%08X)\n", module, cByteAtr, atr, cByteAtr);
|
snprintf(ctx->last_error, sizeof(ctx->last_error), "PICC params not valid : 0x%02X != 0x%02X", pbRecv[1], PARAM_POLLRATE);
|
||||||
return;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disconnect from reader
|
||||||
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);
|
|
||||||
|
|
||||||
if (cbRecv < 8)
|
|
||||||
{
|
{
|
||||||
printf("%s: Padding card uid to 8 bytes\n", module);
|
snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardDisconnect failed: 0x%08lX", lRet);
|
||||||
memset(&pbRecv[cbRecv], 0, 8 - cbRecv);
|
return -1;
|
||||||
}
|
}
|
||||||
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
|
else
|
||||||
memcpy(card_info.uid, pbRecv, 8);
|
return 0;
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
|
||||||
buf[i] = card_info.uid[i];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// void scard_clear(uint8_t unitNo)
|
int scard_poll(struct scard_context *ctx, struct card_data *data)
|
||||||
// {
|
|
||||||
// card_info_t empty_cardinfo;
|
|
||||||
// }
|
|
||||||
|
|
||||||
void scard_update(uint8_t *buf)
|
|
||||||
{
|
{
|
||||||
if (reader_count < 1)
|
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)
|
||||||
{
|
{
|
||||||
return;
|
snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardGetStatusChange failed: %08lX", (ULONG)ret);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
lRet = SCardGetStatusChange(hContext, readCooldown, reader_states, reader_count);
|
if (rs.dwEventState & SCARD_STATE_EMPTY)
|
||||||
if (lRet == SCARD_E_TIMEOUT)
|
return 1;
|
||||||
|
|
||||||
|
if (!(rs.dwEventState & SCARD_STATE_PRESENT))
|
||||||
{
|
{
|
||||||
return;
|
sprintf(ctx->last_error, "unknown dwCurrentState: %08lX", rs.dwCurrentState);
|
||||||
}
|
return -1;
|
||||||
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++)
|
/* connect to card */
|
||||||
|
ret = SCardConnect(ctx->hContext, rs.szReader, SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, &hCard, NULL);
|
||||||
|
|
||||||
|
if (ret != SCARD_S_SUCCESS)
|
||||||
{
|
{
|
||||||
if (!(reader_states[unit_no].dwEventState & SCARD_STATE_CHANGED))
|
snprintf(ctx->last_error, sizeof(ctx->last_error), "SCardConnect failed: %08lX", (ULONG)ret);
|
||||||
continue;
|
return -1;
|
||||||
|
|
||||||
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;
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool scard_init()
|
const char *scard_reader_name(struct scard_context *ctx)
|
||||||
{
|
{
|
||||||
if ((lRet = SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &hContext)) != SCARD_S_SUCCESS)
|
return ctx->mszReaders;
|
||||||
{
|
}
|
||||||
// log_warning("scard", "failed to establish SCard context: {}", bin2hex(&lRet, sizeof(LONG)));
|
|
||||||
return lRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
LPCTSTR reader = NULL;
|
const char *scard_error(struct scard_context *ctx)
|
||||||
|
{
|
||||||
int readerNameLen = 0;
|
return ctx->last_error;
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,46 +1,100 @@
|
|||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 <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();
|
// opaque context
|
||||||
|
struct scard_context
|
||||||
|
{
|
||||||
|
SCARDCONTEXT hContext;
|
||||||
|
LPSTR mszReaders;
|
||||||
|
DWORD pcchReaders;
|
||||||
|
|
||||||
|
CHAR last_error[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* create new context for the cardereader
|
||||||
|
* @return context on success, NULL on failure
|
||||||
|
*/
|
||||||
|
struct scard_context *scard_create(void);
|
||||||
|
|
||||||
|
// destroy context for scard
|
||||||
|
void scard_destroy(struct scard_context *ctx);
|
||||||
|
|
||||||
|
/* init readers (first detected reader will be used)
|
||||||
|
* @param ctx context
|
||||||
|
* @return 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int scard_init(struct scard_context *ctx, struct aime_io_config config);
|
||||||
|
|
||||||
|
/* setup reader (set picc params to be able to read felica lite cards)
|
||||||
|
* @param ctx context
|
||||||
|
* @return 0 on success, -1 on failure
|
||||||
|
*/
|
||||||
|
int scard_set_picc(struct scard_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 scard_poll(struct scard_context *ctx, struct card_data *data);
|
||||||
|
|
||||||
|
/* get last error
|
||||||
|
* @param ctx context
|
||||||
|
* @return error string
|
||||||
|
*/
|
||||||
|
const char *scard_error(struct scard_context *ctx);
|
||||||
|
|
||||||
|
/* get reader name
|
||||||
|
* @param ctx context
|
||||||
|
* @return reader name
|
||||||
|
*/
|
||||||
|
const char *scard_reader_name(struct scard_context *ctx);
|
||||||
|
|
||||||
|
#pragma region APDU_SEND
|
||||||
|
/* get reader name
|
||||||
|
* @param card context
|
||||||
|
* @param card context
|
||||||
|
* @return 0 on success, -1 on transmission failure, 1 if response length isn't as expected
|
||||||
|
*/
|
||||||
|
#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; \
|
||||||
|
} \
|
||||||
|
return 0 \
|
||||||
|
} while (0)
|
||||||
|
#pragma endregion
|
Loading…
Reference in New Issue
Block a user