|
Packit |
cfa437 |
/*
|
|
Packit |
cfa437 |
* Supply a vreader using the PC/SC interface.
|
|
Packit |
cfa437 |
*
|
|
Packit |
cfa437 |
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
|
|
Packit |
cfa437 |
* See the COPYING.LIB file in the top-level directory.
|
|
Packit |
cfa437 |
*/
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
#include "glib-compat.h"
|
|
Packit |
cfa437 |
#include <string.h>
|
|
Packit |
cfa437 |
#include <stdio.h>
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
#include "vcard.h"
|
|
Packit |
cfa437 |
#include "card_7816.h"
|
|
Packit |
cfa437 |
#include "capcsc.h"
|
|
Packit |
cfa437 |
#include "vreader.h"
|
|
Packit |
cfa437 |
#include "vevent.h"
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
#include <PCSC/wintypes.h>
|
|
Packit |
cfa437 |
#include <PCSC/winscard.h>
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
typedef struct _PCSCContext PCSCContext;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
typedef struct {
|
|
Packit |
cfa437 |
PCSCContext *context;
|
|
Packit |
cfa437 |
int index;
|
|
Packit |
cfa437 |
char *name;
|
|
Packit |
cfa437 |
DWORD protocol;
|
|
Packit |
cfa437 |
DWORD state;
|
|
Packit |
cfa437 |
SCARDHANDLE card;
|
|
Packit |
cfa437 |
BYTE atr[MAX_ATR_SIZE];
|
|
Packit |
cfa437 |
DWORD atrlen;
|
|
Packit |
cfa437 |
int card_connected;
|
|
Packit |
cfa437 |
unsigned long request_count;
|
|
Packit |
cfa437 |
} SCardReader;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
typedef struct _PCSCContext {
|
|
Packit |
cfa437 |
SCARDCONTEXT context;
|
|
Packit |
cfa437 |
SCardReader readers[CAPCSC_MAX_READERS];
|
|
Packit |
cfa437 |
int reader_count;
|
|
Packit |
cfa437 |
int readers_changed;
|
|
Packit |
cfa437 |
GThread *thread;
|
|
Packit |
cfa437 |
CompatGMutex lock;
|
|
Packit |
cfa437 |
} PCSCContext;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void delete_reader(PCSCContext *pc, int i)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
SCardReader *r = &pc->readers[i];
|
|
Packit |
cfa437 |
g_free(r->name);
|
|
Packit |
cfa437 |
r->name = NULL;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (i < (pc->reader_count - 1)) {
|
|
Packit |
cfa437 |
int rem = pc->reader_count - i - 1;
|
|
Packit |
cfa437 |
memmove(&pc->readers[i], &pc->readers[i + 1],
|
|
Packit |
cfa437 |
sizeof(SCardReader) * rem);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
pc->reader_count--;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void delete_reader_cb(VReaderEmul *ve)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
SCardReader *r = (SCardReader *) ve;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
g_mutex_lock(&r->context->lock);
|
|
Packit |
cfa437 |
delete_reader(r->context, r->index);
|
|
Packit |
cfa437 |
g_mutex_unlock(&r->context->lock);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static int new_reader(PCSCContext *pc, const char *name, DWORD state)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
SCardReader *r;
|
|
Packit |
cfa437 |
VReader *vreader;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (pc->reader_count >= CAPCSC_MAX_READERS - 1) {
|
|
Packit |
cfa437 |
return 1;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
r = &pc->readers[pc->reader_count];
|
|
Packit |
cfa437 |
memset(r, 0, sizeof(*r));
|
|
Packit |
cfa437 |
r->index = pc->reader_count++;
|
|
Packit |
cfa437 |
r->context = pc;
|
|
Packit |
cfa437 |
r->name = g_strdup(name);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
vreader = vreader_new(name, (VReaderEmul *) r, delete_reader_cb);
|
|
Packit |
cfa437 |
vreader_add_reader(vreader);
|
|
Packit |
cfa437 |
vreader_free(vreader);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return 0;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static int find_reader(PCSCContext *pc, const char *name)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
int i;
|
|
Packit |
cfa437 |
for (i = 0; i < pc->reader_count; i++)
|
|
Packit |
cfa437 |
if (strcmp(pc->readers[i].name, name) == 0) {
|
|
Packit |
cfa437 |
return i;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return -1;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static int scan_for_readers(PCSCContext *pc)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
int i;
|
|
Packit |
cfa437 |
char buf[8192];
|
|
Packit |
cfa437 |
DWORD buflen = sizeof(buf);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
char *p;
|
|
Packit |
cfa437 |
int matches[CAPCSC_MAX_READERS] = { 0, };
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
g_mutex_lock(&pc->lock);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
pc->readers_changed = 1;
|
|
Packit |
cfa437 |
memset(buf, 0, sizeof(buf));
|
|
Packit |
cfa437 |
rc = SCardListReaders(pc->context, NULL, buf, &buflen);
|
|
Packit |
cfa437 |
if (rc == SCARD_E_NO_READERS_AVAILABLE) {
|
|
Packit |
cfa437 |
rc = 0;
|
|
Packit |
cfa437 |
goto exit;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (rc != SCARD_S_SUCCESS) {
|
|
Packit |
cfa437 |
fprintf(stderr, "SCardListReaders failed: %s (0x%lX)\n",
|
|
Packit |
cfa437 |
pcsc_stringify_error(rc), rc);
|
|
Packit |
cfa437 |
goto exit;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
for (p = buf; p && p < buf + sizeof(buf); p += (strlen(p) + 1)) {
|
|
Packit |
cfa437 |
if (strlen(p) > 0) {
|
|
Packit |
cfa437 |
i = find_reader(pc, p);
|
|
Packit |
cfa437 |
if (i >= 0) {
|
|
Packit |
cfa437 |
matches[i]++;
|
|
Packit |
cfa437 |
} else {
|
|
Packit |
cfa437 |
if (!new_reader(pc, p, SCARD_STATE_UNAWARE)) {
|
|
Packit |
cfa437 |
matches[pc->reader_count - 1]++;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
rc = 0;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
exit:
|
|
Packit |
cfa437 |
i = pc->reader_count - 1;
|
|
Packit |
cfa437 |
g_mutex_unlock(&pc->lock);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
for (; i >= 0; i--) {
|
|
Packit |
cfa437 |
if (!matches[i]) {
|
|
Packit |
cfa437 |
VReader *reader = vreader_get_reader_by_name(pc->readers[i].name);
|
|
Packit |
cfa437 |
if (reader) {
|
|
Packit |
cfa437 |
vreader_free(reader);
|
|
Packit |
cfa437 |
vreader_remove_reader(reader);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return rc;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static int init_pcsc(PCSCContext *pc)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
memset(pc, 0, sizeof(*pc));
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &pc->context);
|
|
Packit |
cfa437 |
if (rc != SCARD_S_SUCCESS) {
|
|
Packit |
cfa437 |
fprintf(stderr, "SCardEstablishContext: "
|
|
Packit |
cfa437 |
"Cannot Connect to Resource Manager %lX\n", rc);
|
|
Packit |
cfa437 |
return rc;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return 0;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void prepare_reader_states(PCSCContext *pc, SCARD_READERSTATE **states,
|
|
Packit |
cfa437 |
DWORD *reader_count)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
SCARD_READERSTATE *state;
|
|
Packit |
cfa437 |
int i;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (*states) {
|
|
Packit |
cfa437 |
g_free(*states);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
*reader_count = pc->reader_count;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
(*reader_count)++;
|
|
Packit |
cfa437 |
*states = g_malloc((*reader_count) * sizeof(**states));
|
|
Packit |
cfa437 |
memset(*states, 0, sizeof((*reader_count) * sizeof(**states)));
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
for (i = 0, state = *states; i < pc->reader_count; i++, state++) {
|
|
Packit |
cfa437 |
state->szReader = pc->readers[i].name;
|
|
Packit |
cfa437 |
state->dwCurrentState = pc->readers[i].state;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
/* Leave a space to be notified of new readers */
|
|
Packit |
cfa437 |
state->szReader = "\\\\?PnP?\\Notification";
|
|
Packit |
cfa437 |
state->dwCurrentState = SCARD_STATE_UNAWARE;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static int connect_card(SCardReader *r)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
r->protocol = -1;
|
|
Packit |
cfa437 |
rc = SCardConnect(r->context->context, r->name, SCARD_SHARE_SHARED,
|
|
Packit |
cfa437 |
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
|
Packit |
cfa437 |
&r->card, &r->protocol);
|
|
Packit |
cfa437 |
if (rc != SCARD_S_SUCCESS) {
|
|
Packit |
cfa437 |
fprintf(stderr, "Failed to connect to a card reader: %s (0x%lX)\n",
|
|
Packit |
cfa437 |
pcsc_stringify_error(rc), rc);
|
|
Packit |
cfa437 |
return rc;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
r->card_connected = 1;
|
|
Packit |
cfa437 |
r->request_count = 0;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return 0;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static LONG send_receive(SCardReader *r, BYTE *transmit, DWORD transmit_len,
|
|
Packit |
cfa437 |
BYTE *receive, DWORD *receive_len)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
const SCARD_IO_REQUEST *send_header;
|
|
Packit |
cfa437 |
SCARD_IO_REQUEST receive_header;
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (!r->card_connected) {
|
|
Packit |
cfa437 |
rc = connect_card(r);
|
|
Packit |
cfa437 |
if (rc) {
|
|
Packit |
cfa437 |
return rc;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (r->protocol == SCARD_PROTOCOL_T0) {
|
|
Packit |
cfa437 |
send_header = SCARD_PCI_T0;
|
|
Packit |
cfa437 |
} else if (r->protocol == SCARD_PROTOCOL_T1) {
|
|
Packit |
cfa437 |
send_header = SCARD_PCI_T1;
|
|
Packit |
cfa437 |
} else {
|
|
Packit |
cfa437 |
fprintf(stderr, "Unknown protocol %lX\n", r->protocol);
|
|
Packit |
cfa437 |
return 1;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
rc = SCardTransmit(r->card, send_header, transmit, transmit_len,
|
|
Packit |
cfa437 |
&receive_header, receive, receive_len);
|
|
Packit |
cfa437 |
if (rc != SCARD_S_SUCCESS) {
|
|
Packit |
cfa437 |
fprintf(stderr, "Failed to transmit %ld bytes: %s (0x%lX)\n",
|
|
Packit |
cfa437 |
transmit_len, pcsc_stringify_error(rc), rc);
|
|
Packit |
cfa437 |
return rc;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return 0;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static VCardStatus apdu_cb(VCard *card, VCardAPDU *apdu,
|
|
Packit |
cfa437 |
VCardResponse **response)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
VCardStatus ret = VCARD_DONE;
|
|
Packit |
cfa437 |
SCardReader *r = (SCardReader *) vcard_get_private(card);
|
|
Packit |
cfa437 |
BYTE outbuf[4096];
|
|
Packit |
cfa437 |
DWORD outlen = sizeof(outbuf);
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
rc = send_receive(r, apdu->a_data, apdu->a_len, outbuf, &outlen);
|
|
Packit |
cfa437 |
if (rc || outlen < 2) {
|
|
Packit |
cfa437 |
ret = VCARD_FAIL;
|
|
Packit |
cfa437 |
} else {
|
|
Packit |
cfa437 |
*response = vcard_response_new_data(outbuf, outlen - 2);
|
|
Packit |
cfa437 |
if (*response == NULL) {
|
|
Packit |
cfa437 |
return VCARD_FAIL;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
vcard_response_set_status_bytes(*response, outbuf[outlen - 2],
|
|
Packit |
cfa437 |
outbuf[outlen - 1]);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return ret;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static VCardStatus reset_cb(VCard *card, int channel)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
SCardReader *r = (SCardReader *) vcard_get_private(card);
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
/* vreader_power_on is a bit too free with it's resets.
|
|
Packit |
cfa437 |
And a reconnect is expensive; as much as 10-20 seconds.
|
|
Packit |
cfa437 |
Hence, we discard any initial reconnect request. */
|
|
Packit |
cfa437 |
if (r->request_count++ == 0) {
|
|
Packit |
cfa437 |
return VCARD_DONE;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
rc = SCardReconnect(r->card, SCARD_SHARE_SHARED,
|
|
Packit |
cfa437 |
SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1,
|
|
Packit |
cfa437 |
SCARD_RESET_CARD, &r->protocol);
|
|
Packit |
cfa437 |
if (rc != SCARD_S_SUCCESS) {
|
|
Packit |
cfa437 |
fprintf(stderr, "Failed to reconnect to a card reader: %s (0x%lX)\n",
|
|
Packit |
cfa437 |
pcsc_stringify_error(rc), rc);
|
|
Packit |
cfa437 |
return VCARD_FAIL;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
return VCARD_DONE;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void get_atr_cb(VCard *card, unsigned char *atr, int *atr_len)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
SCardReader *r = (SCardReader *) vcard_get_private(card);
|
|
Packit |
cfa437 |
*atr_len = r->atrlen;
|
|
Packit |
cfa437 |
if (atr) {
|
|
Packit |
cfa437 |
memcpy(atr, r->atr, r->atrlen);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void delete_card_cb(VCardEmul *ve)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
fprintf(stderr, "TODO, got a delete_card_cb\n");
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void insert_card(SCardReader *r, SCARD_READERSTATE *s)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
VReader *reader;
|
|
Packit |
cfa437 |
VCardApplet *applet;
|
|
Packit |
cfa437 |
VCard *card;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
memcpy(r->atr, s->rgbAtr, MIN(sizeof(r->atr), sizeof(s->rgbAtr)));
|
|
Packit |
cfa437 |
r->atrlen = s->cbAtr;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
reader = vreader_get_reader_by_name(r->name);
|
|
Packit |
cfa437 |
if (!reader) {
|
|
Packit |
cfa437 |
return;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (connect_card(r)) {
|
|
Packit |
cfa437 |
return;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
applet =
|
|
Packit |
cfa437 |
vcard_new_applet(apdu_cb,
|
|
Packit |
cfa437 |
reset_cb,
|
|
Packit |
cfa437 |
(const unsigned char *)CAPCSC_APPLET,
|
|
Packit |
cfa437 |
strlen(CAPCSC_APPLET));
|
|
Packit |
cfa437 |
if (!applet) {
|
|
Packit |
cfa437 |
return;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
card = vcard_new((VCardEmul *) r, delete_card_cb);
|
|
Packit |
cfa437 |
if (!card) {
|
|
Packit |
cfa437 |
vcard_delete_applet(applet);
|
|
Packit |
cfa437 |
vreader_free(reader);
|
|
Packit |
cfa437 |
return;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
vcard_set_type(card, VCARD_DIRECT);
|
|
Packit |
cfa437 |
vcard_set_atr_func(card, get_atr_cb);
|
|
Packit |
cfa437 |
vcard_add_applet(card, applet);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
vreader_insert_card(reader, card);
|
|
Packit |
cfa437 |
vreader_free(reader);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void remove_card(SCardReader *r)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
VReader *reader;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
memset(r->atr, 0, sizeof(r->atr));
|
|
Packit |
cfa437 |
r->atrlen = 0;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
rc = SCardDisconnect(r->card, SCARD_LEAVE_CARD);
|
|
Packit |
cfa437 |
if (rc != SCARD_S_SUCCESS) {
|
|
Packit |
cfa437 |
fprintf(stderr, "Non fatal info:"
|
|
Packit |
cfa437 |
"failed to disconnect card reader: %s (0x%lX)\n",
|
|
Packit |
cfa437 |
pcsc_stringify_error(rc), rc);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
r->card_connected = 0;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
reader = vreader_get_reader_by_name(r->name);
|
|
Packit |
cfa437 |
if (!reader) {
|
|
Packit |
cfa437 |
return;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
vreader_insert_card(reader, NULL);
|
|
Packit |
cfa437 |
vreader_free(reader);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static void process_reader_change(SCardReader *r, SCARD_READERSTATE *s)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
if (s->dwEventState & SCARD_STATE_PRESENT) {
|
|
Packit |
cfa437 |
insert_card(r, s);
|
|
Packit |
cfa437 |
} else if (s->dwEventState & SCARD_STATE_EMPTY) {
|
|
Packit |
cfa437 |
remove_card(r);
|
|
Packit |
cfa437 |
} else {
|
|
Packit |
cfa437 |
fprintf(stderr, "Unexpected card state change from %lx to %lx:\n",
|
|
Packit |
cfa437 |
r->state, s->dwEventState);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
r->state = s->dwEventState & ~SCARD_STATE_CHANGED;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
/*
|
|
Packit |
cfa437 |
* This thread looks for card and reader insertions and puts events on the
|
|
Packit |
cfa437 |
* event queue.
|
|
Packit |
cfa437 |
*/
|
|
Packit |
cfa437 |
static gpointer event_thread(gpointer arg)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
PCSCContext *pc = (PCSCContext *) arg;
|
|
Packit |
cfa437 |
DWORD reader_count = 0;
|
|
Packit |
cfa437 |
SCARD_READERSTATE *reader_states = NULL;
|
|
Packit |
cfa437 |
LONG rc;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
scan_for_readers(pc);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
do {
|
|
Packit |
cfa437 |
DWORD i;
|
|
Packit |
cfa437 |
DWORD timeout = INFINITE;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
g_mutex_lock(&pc->lock);
|
|
Packit |
cfa437 |
if (pc->readers_changed) {
|
|
Packit |
cfa437 |
prepare_reader_states(pc, &reader_states, &reader_count);
|
|
Packit |
cfa437 |
timeout = 0;
|
|
Packit |
cfa437 |
} else if (reader_count > 1) {
|
|
Packit |
cfa437 |
timeout = 0;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
pc->readers_changed = 0;
|
|
Packit |
cfa437 |
g_mutex_unlock(&pc->lock);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
rc = SCardGetStatusChange(pc->context, timeout, reader_states,
|
|
Packit |
cfa437 |
reader_count);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
/* If we have a new reader, or an unknown reader,
|
|
Packit |
cfa437 |
rescan and go back and do it again */
|
|
Packit |
cfa437 |
if ((rc == SCARD_S_SUCCESS && (reader_states[reader_count - 1].dwEventState & SCARD_STATE_CHANGED))
|
|
Packit |
cfa437 |
||
|
|
Packit |
cfa437 |
rc == SCARD_E_UNKNOWN_READER) {
|
|
Packit |
cfa437 |
scan_for_readers(pc);
|
|
Packit |
cfa437 |
continue;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (rc != SCARD_S_SUCCESS && rc != SCARD_E_TIMEOUT) {
|
|
Packit |
cfa437 |
fprintf(stderr, "Unexpected SCardGetStatusChange ret %lx(%s)\n",
|
|
Packit |
cfa437 |
rc, pcsc_stringify_error(rc));
|
|
Packit |
cfa437 |
continue;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
g_mutex_lock(&pc->lock);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
for (i = 0; i < reader_count; i++) {
|
|
Packit |
cfa437 |
if (reader_states[i].dwEventState & SCARD_STATE_CHANGED) {
|
|
Packit |
cfa437 |
process_reader_change(&pc->readers[i], &reader_states[i]);
|
|
Packit |
cfa437 |
pc->readers_changed++;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
g_mutex_unlock(&pc->lock);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
/* libpcsclite is only thread safe at a high level. If we constantly
|
|
Packit |
cfa437 |
hold long calls into SCardGetStatusChange, we'll starve any running
|
|
Packit |
cfa437 |
clients. So, if we have an active session, and nothing has changed
|
|
Packit |
cfa437 |
on our front, we just idle. */
|
|
Packit |
cfa437 |
if (!pc->readers_changed && reader_count > 1) {
|
|
Packit |
cfa437 |
g_usleep(CAPCSC_POLL_TIME * 1000);
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
} while (1);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return NULL;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
/*
|
|
Packit |
cfa437 |
* We poll the PC/SC interface, looking for device changes
|
|
Packit |
cfa437 |
*/
|
|
Packit |
cfa437 |
static int new_event_thread(PCSCContext *pc)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
pc->thread = g_thread_new("capcsc_event_thread", event_thread, pc);
|
|
Packit |
cfa437 |
return pc->thread == NULL;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
static PCSCContext context;
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
int capcsc_init(void)
|
|
Packit |
cfa437 |
{
|
|
Packit |
cfa437 |
g_mutex_init(&context.lock);
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (init_pcsc(&context)) {
|
|
Packit |
cfa437 |
return -1;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
if (new_event_thread(&context)) {
|
|
Packit |
cfa437 |
return -1;
|
|
Packit |
cfa437 |
}
|
|
Packit |
cfa437 |
|
|
Packit |
cfa437 |
return 0;
|
|
Packit |
cfa437 |
}
|