2002-02-24 19:32:14 +00:00
|
|
|
|
/*
|
2003-12-10 14:52:58 +00:00
|
|
|
|
* reader-pcsc.c: Reader driver for PC/SC interface
|
2002-02-24 19:32:14 +00:00
|
|
|
|
*
|
|
|
|
|
* Copyright (C) 2002 Juha Yrj<EFBFBD>l<EFBFBD> <juha.yrjola@iki.fi>
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
*/
|
|
|
|
|
|
2002-04-05 10:44:51 +00:00
|
|
|
|
#include "internal.h"
|
2003-09-26 08:15:15 +00:00
|
|
|
|
#ifdef HAVE_PCSC
|
2002-12-23 18:47:27 +00:00
|
|
|
|
#include "ctbcs.h"
|
2002-02-24 19:32:14 +00:00
|
|
|
|
#include <assert.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2003-01-05 18:06:35 +00:00
|
|
|
|
#include <time.h>
|
2003-04-15 15:59:32 +00:00
|
|
|
|
#ifdef __APPLE__
|
2003-07-28 10:02:23 +00:00
|
|
|
|
#include <PCSC/wintypes.h>
|
|
|
|
|
#include <PCSC/winscard.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <winscard.h>
|
2003-04-15 15:59:32 +00:00
|
|
|
|
#endif
|
2002-02-24 19:32:14 +00:00
|
|
|
|
|
2002-02-24 20:16:07 +00:00
|
|
|
|
/* Default timeout value for SCardGetStatusChange
|
|
|
|
|
* Needs to be increased for some broken PC/SC
|
|
|
|
|
* Lite implementations.
|
|
|
|
|
*/
|
|
|
|
|
#ifndef SC_CUSTOM_STATUS_TIMEOUT
|
|
|
|
|
#define SC_STATUS_TIMEOUT 0
|
|
|
|
|
#else
|
|
|
|
|
#define SC_STATUS_TIMEOUT SC_CUSTOM_STATUS_TIMEOUT
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-06-14 12:52:56 +00:00
|
|
|
|
/* Some windows specific kludge */
|
2004-08-31 17:31:00 +00:00
|
|
|
|
#undef SCARD_PROTOCOL_ANY
|
2003-03-20 12:52:39 +00:00
|
|
|
|
#define SCARD_PROTOCOL_ANY (SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1)
|
2004-08-31 17:31:00 +00:00
|
|
|
|
#ifdef _WIN32
|
2002-06-14 12:52:56 +00:00
|
|
|
|
#define SCARD_SCOPE_GLOBAL SCARD_SCOPE_USER
|
|
|
|
|
|
|
|
|
|
/* Error printing */
|
2003-08-25 14:21:18 +00:00
|
|
|
|
#define PCSC_ERROR(ctx, desc, rv) sc_error(ctx, desc ": %lx\n", rv);
|
2002-06-14 12:52:56 +00:00
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
2003-08-25 14:21:18 +00:00
|
|
|
|
#define PCSC_ERROR(ctx, desc, rv) sc_error(ctx, desc ": %s\n", pcsc_stringify_error(rv));
|
2002-06-14 12:52:56 +00:00
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
|
#define GET_SLOT_PTR(s, i) (&(s)->slot[(i)])
|
|
|
|
|
#define GET_PRIV_DATA(r) ((struct pcsc_private_data *) (r)->drv_data)
|
|
|
|
|
#define GET_SLOT_DATA(r) ((struct pcsc_slot_data *) (r)->drv_data)
|
|
|
|
|
|
|
|
|
|
struct pcsc_global_private_data {
|
|
|
|
|
SCARDCONTEXT pcsc_ctx;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct pcsc_private_data {
|
|
|
|
|
SCARDCONTEXT pcsc_ctx;
|
|
|
|
|
char *reader_name;
|
2002-06-14 11:33:20 +00:00
|
|
|
|
struct pcsc_global_private_data *gpriv;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct pcsc_slot_data {
|
|
|
|
|
SCARDHANDLE pcsc_card;
|
2003-01-19 17:47:07 +00:00
|
|
|
|
SCARD_READERSTATE_A readerState;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
};
|
|
|
|
|
|
2003-02-12 14:20:00 +00:00
|
|
|
|
static int pcsc_detect_card_presence(struct sc_reader *reader, struct sc_slot_info *slot);
|
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
|
static int pcsc_ret_to_error(long rv)
|
|
|
|
|
{
|
|
|
|
|
switch (rv) {
|
|
|
|
|
case SCARD_W_REMOVED_CARD:
|
|
|
|
|
return SC_ERROR_CARD_REMOVED;
|
|
|
|
|
case SCARD_W_RESET_CARD:
|
|
|
|
|
return SC_ERROR_CARD_RESET;
|
|
|
|
|
case SCARD_E_NOT_TRANSACTED:
|
|
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
2003-02-19 13:36:58 +00:00
|
|
|
|
case SCARD_W_UNRESPONSIVE_CARD:
|
|
|
|
|
return SC_ERROR_CARD_UNRESPONSIVE;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
default:
|
|
|
|
|
return SC_ERROR_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned int pcsc_proto_to_opensc(DWORD proto)
|
|
|
|
|
{
|
|
|
|
|
switch (proto) {
|
|
|
|
|
case SCARD_PROTOCOL_T0:
|
|
|
|
|
return SC_PROTO_T0;
|
|
|
|
|
case SCARD_PROTOCOL_T1:
|
|
|
|
|
return SC_PROTO_T1;
|
|
|
|
|
case SCARD_PROTOCOL_RAW:
|
|
|
|
|
return SC_PROTO_RAW;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static DWORD opensc_proto_to_pcsc(unsigned int proto)
|
|
|
|
|
{
|
|
|
|
|
switch (proto) {
|
|
|
|
|
case SC_PROTO_T0:
|
|
|
|
|
return SCARD_PROTOCOL_T0;
|
|
|
|
|
case SC_PROTO_T1:
|
|
|
|
|
return SCARD_PROTOCOL_T1;
|
|
|
|
|
case SC_PROTO_RAW:
|
|
|
|
|
return SCARD_PROTOCOL_RAW;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pcsc_transmit(struct sc_reader *reader, struct sc_slot_info *slot,
|
|
|
|
|
const u8 *sendbuf, size_t sendsize,
|
2002-12-23 18:47:27 +00:00
|
|
|
|
u8 *recvbuf, size_t *recvsize,
|
|
|
|
|
int control)
|
2002-02-24 19:32:14 +00:00
|
|
|
|
{
|
|
|
|
|
SCARD_IO_REQUEST sSendPci, sRecvPci;
|
|
|
|
|
DWORD dwSendLength, dwRecvLength;
|
|
|
|
|
LONG rv;
|
|
|
|
|
SCARDHANDLE card;
|
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
|
|
|
|
|
|
|
|
|
assert(pslot != NULL);
|
|
|
|
|
card = pslot->pcsc_card;
|
|
|
|
|
|
|
|
|
|
sSendPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol);
|
2002-06-14 12:52:56 +00:00
|
|
|
|
sSendPci.cbPciLength = sizeof(sSendPci);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
sRecvPci.dwProtocol = opensc_proto_to_pcsc(slot->active_protocol);
|
2002-06-14 12:52:56 +00:00
|
|
|
|
sRecvPci.cbPciLength = sizeof(sRecvPci);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
|
|
|
|
|
dwSendLength = sendsize;
|
|
|
|
|
dwRecvLength = *recvsize;
|
2002-06-14 12:52:56 +00:00
|
|
|
|
|
2003-11-14 10:14:54 +00:00
|
|
|
|
if (dwRecvLength > 258)
|
|
|
|
|
dwRecvLength = 258;
|
2002-06-14 12:52:56 +00:00
|
|
|
|
|
2002-12-23 18:47:27 +00:00
|
|
|
|
if (!control) {
|
|
|
|
|
rv = SCardTransmit(card, &sSendPci, sendbuf, dwSendLength,
|
|
|
|
|
&sRecvPci, recvbuf, &dwRecvLength);
|
|
|
|
|
} else {
|
2004-07-20 20:52:21 +00:00
|
|
|
|
#ifdef HAVE_PCSC_OLD
|
2002-12-23 18:47:27 +00:00
|
|
|
|
rv = SCardControl(card, sendbuf, dwSendLength,
|
|
|
|
|
recvbuf, &dwRecvLength);
|
2003-01-02 15:31:53 +00:00
|
|
|
|
#else
|
|
|
|
|
rv = SCardControl(card, 0, sendbuf, dwSendLength,
|
|
|
|
|
recvbuf, dwRecvLength, &dwRecvLength);
|
|
|
|
|
#endif
|
2002-12-23 18:47:27 +00:00
|
|
|
|
}
|
2002-06-14 12:52:56 +00:00
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
|
|
|
|
switch (rv) {
|
|
|
|
|
case SCARD_W_REMOVED_CARD:
|
|
|
|
|
return SC_ERROR_CARD_REMOVED;
|
|
|
|
|
case SCARD_W_RESET_CARD:
|
|
|
|
|
return SC_ERROR_CARD_RESET;
|
|
|
|
|
case SCARD_E_NOT_TRANSACTED:
|
2003-04-16 15:56:40 +00:00
|
|
|
|
if ((pcsc_detect_card_presence(reader, slot) &
|
|
|
|
|
SC_SLOT_CARD_PRESENT) == 0)
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return SC_ERROR_CARD_REMOVED;
|
|
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
2002-06-14 12:52:56 +00:00
|
|
|
|
default:
|
2003-02-12 14:20:00 +00:00
|
|
|
|
/* Windows' PC/SC returns 0x8010002f (??) if a card is removed */
|
|
|
|
|
if (pcsc_detect_card_presence(reader, slot) != 1)
|
|
|
|
|
return SC_ERROR_CARD_REMOVED;
|
2002-06-14 12:52:56 +00:00
|
|
|
|
PCSC_ERROR(reader->ctx, "SCardTransmit failed", rv);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (dwRecvLength < 2)
|
2002-04-06 12:02:28 +00:00
|
|
|
|
return SC_ERROR_UNKNOWN_DATA_RECEIVED;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
*recvsize = dwRecvLength;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int refresh_slot_attributes(struct sc_reader *reader, struct sc_slot_info *slot)
|
|
|
|
|
{
|
|
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
2003-01-19 17:47:07 +00:00
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
LONG ret;
|
|
|
|
|
|
2003-01-19 17:47:07 +00:00
|
|
|
|
if (pslot->readerState.szReader == NULL) {
|
|
|
|
|
pslot->readerState.szReader = priv->reader_name;
|
|
|
|
|
pslot->readerState.dwCurrentState = SCARD_STATE_UNAWARE;
|
|
|
|
|
pslot->readerState.dwEventState = SCARD_STATE_UNAWARE;
|
|
|
|
|
} else {
|
|
|
|
|
pslot->readerState.dwCurrentState = pslot->readerState.dwEventState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = SCardGetStatusChange(priv->pcsc_ctx, SC_STATUS_TIMEOUT, &pslot->readerState, 1);
|
2003-01-27 13:01:17 +00:00
|
|
|
|
if (ret == SCARD_E_TIMEOUT) { /* timeout: nothing changed */
|
|
|
|
|
slot->flags &= ~SCARD_STATE_CHANGED;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2002-02-24 19:32:14 +00:00
|
|
|
|
if (ret != 0) {
|
2002-06-14 12:52:56 +00:00
|
|
|
|
PCSC_ERROR(reader->ctx, "SCardGetStatusChange failed", ret);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return pcsc_ret_to_error(ret);
|
|
|
|
|
}
|
2003-01-19 17:47:07 +00:00
|
|
|
|
if (pslot->readerState.dwEventState & SCARD_STATE_PRESENT) {
|
|
|
|
|
int old_flags = slot->flags;
|
2003-04-17 14:35:58 +00:00
|
|
|
|
int maybe_changed = 0;
|
2003-01-19 17:47:07 +00:00
|
|
|
|
|
2002-12-06 21:40:41 +00:00
|
|
|
|
slot->flags |= SC_SLOT_CARD_PRESENT;
|
2003-01-19 17:47:07 +00:00
|
|
|
|
slot->atr_len = pslot->readerState.cbAtr;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
if (slot->atr_len > SC_MAX_ATR_SIZE)
|
|
|
|
|
slot->atr_len = SC_MAX_ATR_SIZE;
|
2003-01-19 17:47:07 +00:00
|
|
|
|
memcpy(slot->atr, pslot->readerState.rgbAtr, slot->atr_len);
|
2003-04-17 14:35:58 +00:00
|
|
|
|
|
2003-01-27 13:01:17 +00:00
|
|
|
|
#ifndef _WIN32
|
2003-04-17 14:35:58 +00:00
|
|
|
|
/* On Linux, SCARD_STATE_CHANGED always means there was an
|
|
|
|
|
* insert or removal. But we may miss events that way. */
|
|
|
|
|
if (pslot->readerState.dwEventState & SCARD_STATE_CHANGED) {
|
|
|
|
|
slot->flags |= SC_SLOT_CARD_CHANGED;
|
|
|
|
|
} else {
|
|
|
|
|
maybe_changed = 1;
|
|
|
|
|
}
|
2003-03-20 12:52:39 +00:00
|
|
|
|
#else
|
2003-04-17 14:35:58 +00:00
|
|
|
|
/* On windows, SCARD_STATE_CHANGED is turned on by lots of
|
|
|
|
|
* other events, so it gives us a lot of false positives.
|
|
|
|
|
* But if it's off, there really no change */
|
|
|
|
|
if (pslot->readerState.dwEventState & SCARD_STATE_CHANGED) {
|
|
|
|
|
maybe_changed = 1;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
/* If we aren't sure if the card state changed, check if
|
|
|
|
|
* the card handle is still valid. If the card changed,
|
|
|
|
|
* the handle will be invalid. */
|
2003-04-22 07:51:45 +00:00
|
|
|
|
slot->flags &= ~SC_SLOT_CARD_CHANGED;
|
2003-04-17 14:35:58 +00:00
|
|
|
|
if (maybe_changed && (old_flags & SC_SLOT_CARD_PRESENT)) {
|
2003-03-20 12:52:39 +00:00
|
|
|
|
DWORD readers_len = 0, state, prot, atr_len = 32;
|
2003-04-17 14:35:58 +00:00
|
|
|
|
unsigned char atr[32];
|
2003-03-20 12:52:39 +00:00
|
|
|
|
int rv = SCardStatus(pslot->pcsc_card, NULL, &readers_len,
|
|
|
|
|
&state, &prot, atr, &atr_len);
|
|
|
|
|
if (rv == SCARD_W_REMOVED_CARD)
|
|
|
|
|
slot->flags |= SC_SLOT_CARD_CHANGED;
|
|
|
|
|
}
|
2002-12-06 21:40:41 +00:00
|
|
|
|
} else {
|
2003-01-19 17:47:07 +00:00
|
|
|
|
slot->flags &= ~(SC_SLOT_CARD_PRESENT|SC_SLOT_CARD_CHANGED);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-12-06 21:40:41 +00:00
|
|
|
|
static int pcsc_detect_card_presence(struct sc_reader *reader, struct sc_slot_info *slot)
|
|
|
|
|
{
|
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
|
|
if ((rv = refresh_slot_attributes(reader, slot)) < 0)
|
|
|
|
|
return rv;
|
2003-01-19 17:47:07 +00:00
|
|
|
|
return slot->flags;
|
2002-12-06 21:40:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-01-03 16:32:06 +00:00
|
|
|
|
/* Wait for an event to occur.
|
|
|
|
|
* This function ignores the list of slots, because with
|
|
|
|
|
* pcsc we have a 1:1 mapping of readers and slots anyway
|
|
|
|
|
*/
|
|
|
|
|
static int pcsc_wait_for_event(struct sc_reader **readers,
|
|
|
|
|
struct sc_slot_info **slots,
|
|
|
|
|
size_t nslots,
|
|
|
|
|
unsigned int event_mask,
|
|
|
|
|
int *reader,
|
|
|
|
|
unsigned int *event, int timeout)
|
|
|
|
|
{
|
|
|
|
|
struct sc_context *ctx;
|
|
|
|
|
SCARDCONTEXT pcsc_ctx;
|
|
|
|
|
LONG ret;
|
|
|
|
|
SCARD_READERSTATE_A rgReaderStates[SC_MAX_READERS];
|
|
|
|
|
unsigned long on_bits, off_bits;
|
|
|
|
|
time_t end_time, now, delta;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* Prevent buffer overflow */
|
|
|
|
|
if (nslots >= SC_MAX_READERS)
|
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
|
|
on_bits = off_bits = 0;
|
|
|
|
|
if (event_mask & SC_EVENT_CARD_INSERTED) {
|
|
|
|
|
event_mask &= ~SC_EVENT_CARD_INSERTED;
|
|
|
|
|
on_bits |= SCARD_STATE_PRESENT;
|
|
|
|
|
}
|
|
|
|
|
if (event_mask & SC_EVENT_CARD_REMOVED) {
|
|
|
|
|
event_mask &= ~SC_EVENT_CARD_REMOVED;
|
|
|
|
|
off_bits |= SCARD_STATE_PRESENT;
|
|
|
|
|
}
|
|
|
|
|
if (event_mask != 0)
|
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
2003-07-24 13:10:53 +00:00
|
|
|
|
/* Find out the current status */
|
2003-01-03 16:32:06 +00:00
|
|
|
|
ctx = readers[0]->ctx;
|
|
|
|
|
pcsc_ctx = GET_PRIV_DATA(readers[0])->pcsc_ctx;
|
|
|
|
|
for (i = 0; i < nslots; i++) {
|
|
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(readers[i]);
|
|
|
|
|
|
|
|
|
|
rgReaderStates[i].szReader = priv->reader_name;
|
|
|
|
|
rgReaderStates[i].dwCurrentState = SCARD_STATE_UNAWARE;
|
|
|
|
|
rgReaderStates[i].dwEventState = SCARD_STATE_UNAWARE;
|
|
|
|
|
|
|
|
|
|
/* Can we handle readers from different PCSC contexts? */
|
|
|
|
|
if (priv->pcsc_ctx != pcsc_ctx)
|
|
|
|
|
return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = SCardGetStatusChange(pcsc_ctx, 0, rgReaderStates, nslots);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
PCSC_ERROR(ctx, "SCardGetStatusChange(1) failed", ret);
|
|
|
|
|
return pcsc_ret_to_error(ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
time(&now);
|
2003-01-05 17:59:24 +00:00
|
|
|
|
end_time = now + (timeout + 999) / 1000;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
|
|
|
|
|
/* Wait for a status change and return if it's a card insert/removal
|
|
|
|
|
*/
|
|
|
|
|
for( ; ; ) {
|
2003-03-10 21:23:08 +00:00
|
|
|
|
SCARD_READERSTATE_A *rsp;
|
|
|
|
|
|
2003-01-03 16:32:06 +00:00
|
|
|
|
/* Scan the current state of all readers to see if they
|
|
|
|
|
* match any of the events we're polling for */
|
|
|
|
|
*event = 0;
|
2003-03-10 21:23:08 +00:00
|
|
|
|
for (i = 0, rsp = rgReaderStates; i < nslots; i++, rsp++) {
|
2003-01-27 13:01:17 +00:00
|
|
|
|
unsigned long state, prev_state;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
|
2003-03-10 21:23:08 +00:00
|
|
|
|
prev_state = rsp->dwCurrentState;
|
|
|
|
|
state = rsp->dwEventState;
|
2003-01-27 13:01:17 +00:00
|
|
|
|
if ((state & on_bits & SCARD_STATE_PRESENT) &&
|
|
|
|
|
(prev_state & SCARD_STATE_EMPTY))
|
2003-01-03 16:32:06 +00:00
|
|
|
|
*event |= SC_EVENT_CARD_INSERTED;
|
2003-01-27 13:01:17 +00:00
|
|
|
|
if ((~state & off_bits & SCARD_STATE_PRESENT) &&
|
|
|
|
|
(prev_state & SCARD_STATE_PRESENT))
|
2003-01-03 16:32:06 +00:00
|
|
|
|
*event |= SC_EVENT_CARD_REMOVED;
|
|
|
|
|
if (*event) {
|
|
|
|
|
*reader = i;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* No match - copy the state so pcscd knows
|
|
|
|
|
* what to watch out for */
|
2003-03-10 21:23:08 +00:00
|
|
|
|
rsp->dwCurrentState = rsp->dwEventState;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set the timeout if caller wants to time out */
|
|
|
|
|
if (timeout == 0)
|
|
|
|
|
return SC_ERROR_EVENT_TIMEOUT;
|
|
|
|
|
if (timeout > 0) {
|
|
|
|
|
time(&now);
|
|
|
|
|
if (now >= end_time)
|
|
|
|
|
return SC_ERROR_EVENT_TIMEOUT;
|
|
|
|
|
delta = end_time - now;
|
|
|
|
|
} else {
|
|
|
|
|
delta = 3600;
|
|
|
|
|
}
|
|
|
|
|
|
2003-01-05 17:59:24 +00:00
|
|
|
|
ret = SCardGetStatusChange(pcsc_ctx, 1000 * delta,
|
2003-01-03 16:32:06 +00:00
|
|
|
|
rgReaderStates, nslots);
|
2003-01-05 17:59:24 +00:00
|
|
|
|
if (ret == SCARD_E_TIMEOUT) {
|
|
|
|
|
if (timeout < 0)
|
|
|
|
|
continue;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
return SC_ERROR_EVENT_TIMEOUT;
|
2003-01-05 17:59:24 +00:00
|
|
|
|
}
|
2003-01-03 16:32:06 +00:00
|
|
|
|
if (ret != 0) {
|
|
|
|
|
PCSC_ERROR(ctx, "SCardGetStatusChange(2) failed", ret);
|
|
|
|
|
return pcsc_ret_to_error(ret);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
|
static int pcsc_connect(struct sc_reader *reader, struct sc_slot_info *slot)
|
|
|
|
|
{
|
2004-12-21 09:54:47 +00:00
|
|
|
|
DWORD active_proto, protocol = SCARD_PROTOCOL_ANY;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
SCARDHANDLE card_handle;
|
|
|
|
|
LONG rv;
|
|
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
2004-12-21 09:54:47 +00:00
|
|
|
|
scconf_block *conf_block = NULL;
|
|
|
|
|
int r, i;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
|
|
|
|
|
r = refresh_slot_attributes(reader, slot);
|
|
|
|
|
if (r)
|
|
|
|
|
return r;
|
|
|
|
|
if (!(slot->flags & SC_SLOT_CARD_PRESENT))
|
|
|
|
|
return SC_ERROR_CARD_NOT_PRESENT;
|
2004-08-19 08:55:15 +00:00
|
|
|
|
|
|
|
|
|
/* force a protocol, addon by -mp */
|
2004-12-21 09:54:47 +00:00
|
|
|
|
for (i = 0; reader->ctx->conf_blocks[i] != NULL; i++) {
|
|
|
|
|
scconf_block **blocks;
|
|
|
|
|
char name[3 * SC_MAX_ATR_SIZE];
|
2004-08-19 08:55:15 +00:00
|
|
|
|
|
2004-12-21 09:54:47 +00:00
|
|
|
|
r = sc_bin_to_hex(slot->atr, slot->atr_len, name, sizeof(name), ':');
|
|
|
|
|
assert(r == 0);
|
|
|
|
|
sc_debug(reader->ctx, "Looking for a card_atr %s", name);
|
|
|
|
|
blocks = scconf_find_blocks(reader->ctx->conf, reader->ctx->conf_blocks[i],
|
|
|
|
|
"card_atr", name);
|
|
|
|
|
conf_block = blocks[0];
|
|
|
|
|
free(blocks);
|
|
|
|
|
if (conf_block != NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (conf_block != NULL) {
|
|
|
|
|
const char *forcestr;
|
|
|
|
|
|
|
|
|
|
sc_debug(reader->ctx, "Found card_atr with current atr");
|
|
|
|
|
forcestr = scconf_get_str(conf_block, "force_protocol", NULL);
|
|
|
|
|
if (forcestr) {
|
|
|
|
|
sc_debug(reader->ctx,"Protocol force in action: %s", forcestr);
|
|
|
|
|
if (!strcmp(forcestr,"t0"))
|
|
|
|
|
protocol = SCARD_PROTOCOL_T0;
|
|
|
|
|
else if (!strcmp(forcestr,"t1"))
|
|
|
|
|
protocol = SCARD_PROTOCOL_T1;
|
|
|
|
|
else if (!strcmp(forcestr,"raw"))
|
|
|
|
|
protocol = SCARD_PROTOCOL_RAW;
|
|
|
|
|
else
|
|
|
|
|
sc_error(reader->ctx,"Unknown force_protocol: %s (Ignored)", forcestr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-08-19 08:55:15 +00:00
|
|
|
|
rv = SCardConnect(priv->pcsc_ctx, priv->reader_name,
|
|
|
|
|
SCARD_SHARE_SHARED, protocol, &card_handle, &active_proto);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
if (rv != 0) {
|
2002-06-14 12:52:56 +00:00
|
|
|
|
PCSC_ERROR(reader->ctx, "SCardConnect failed", rv);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return pcsc_ret_to_error(rv);
|
|
|
|
|
}
|
|
|
|
|
slot->active_protocol = pcsc_proto_to_opensc(active_proto);
|
|
|
|
|
pslot->pcsc_card = card_handle;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pcsc_disconnect(struct sc_reader *reader, struct sc_slot_info *slot,
|
|
|
|
|
int action)
|
|
|
|
|
{
|
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
|
|
|
|
|
|
|
|
|
/* FIXME: check action */
|
|
|
|
|
SCardDisconnect(pslot->pcsc_card, SCARD_LEAVE_CARD);
|
2003-01-19 17:47:07 +00:00
|
|
|
|
memset(pslot, 0, sizeof(*pslot));
|
|
|
|
|
slot->flags = 0;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pcsc_lock(struct sc_reader *reader, struct sc_slot_info *slot)
|
|
|
|
|
{
|
|
|
|
|
long rv;
|
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
|
|
|
|
|
|
|
|
|
assert(pslot != NULL);
|
|
|
|
|
rv = SCardBeginTransaction(pslot->pcsc_card);
|
|
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
2002-06-14 12:52:56 +00:00
|
|
|
|
PCSC_ERROR(reader->ctx, "SCardBeginTransaction failed", rv);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return pcsc_ret_to_error(rv);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pcsc_unlock(struct sc_reader *reader, struct sc_slot_info *slot)
|
|
|
|
|
{
|
|
|
|
|
long rv;
|
|
|
|
|
struct pcsc_slot_data *pslot = GET_SLOT_DATA(slot);
|
|
|
|
|
|
|
|
|
|
assert(pslot != NULL);
|
|
|
|
|
rv = SCardEndTransaction(pslot->pcsc_card, SCARD_LEAVE_CARD);
|
|
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
2002-06-14 12:52:56 +00:00
|
|
|
|
PCSC_ERROR(reader->ctx, "SCardEndTransaction failed", rv);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return pcsc_ret_to_error(rv);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int pcsc_release(struct sc_reader *reader)
|
|
|
|
|
{
|
2004-07-09 15:31:08 +00:00
|
|
|
|
int i;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
struct pcsc_private_data *priv = GET_PRIV_DATA(reader);
|
|
|
|
|
|
|
|
|
|
free(priv->reader_name);
|
|
|
|
|
free(priv);
|
2004-07-09 15:31:08 +00:00
|
|
|
|
for (i = 0; i < reader->slot_count; i++) {
|
|
|
|
|
if (reader->slot[i].drv_data != NULL) {
|
|
|
|
|
free(reader->slot[i].drv_data);
|
|
|
|
|
reader->slot[i].drv_data = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct sc_reader_operations pcsc_ops;
|
|
|
|
|
|
2003-12-18 16:35:28 +00:00
|
|
|
|
static struct sc_reader_driver pcsc_drv = {
|
2003-12-10 14:52:58 +00:00
|
|
|
|
"PC/SC reader",
|
2002-02-24 19:32:14 +00:00
|
|
|
|
"pcsc",
|
|
|
|
|
&pcsc_ops
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static int pcsc_init(struct sc_context *ctx, void **reader_data)
|
|
|
|
|
{
|
|
|
|
|
LONG rv;
|
|
|
|
|
DWORD reader_buf_size;
|
|
|
|
|
char *reader_buf, *p;
|
2004-07-20 20:52:21 +00:00
|
|
|
|
const char *mszGroups = NULL;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
SCARDCONTEXT pcsc_ctx;
|
2003-01-06 17:45:06 +00:00
|
|
|
|
int r, i;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
struct pcsc_global_private_data *gpriv;
|
2002-06-14 11:33:20 +00:00
|
|
|
|
scconf_block **blocks = NULL, *conf_block = NULL;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
|
2002-06-14 12:52:56 +00:00
|
|
|
|
rv = SCardEstablishContext(SCARD_SCOPE_GLOBAL,
|
|
|
|
|
NULL,
|
|
|
|
|
NULL,
|
2002-02-24 19:32:14 +00:00
|
|
|
|
&pcsc_ctx);
|
|
|
|
|
if (rv != SCARD_S_SUCCESS)
|
|
|
|
|
return pcsc_ret_to_error(rv);
|
2004-04-27 17:41:02 +00:00
|
|
|
|
rv = SCardListReaders(pcsc_ctx, NULL, NULL,
|
2002-02-24 19:32:14 +00:00
|
|
|
|
(LPDWORD) &reader_buf_size);
|
2004-04-27 17:41:02 +00:00
|
|
|
|
if (rv != SCARD_S_SUCCESS || reader_buf_size < 2) {
|
2002-02-24 19:32:14 +00:00
|
|
|
|
SCardReleaseContext(pcsc_ctx);
|
2004-04-27 17:41:02 +00:00
|
|
|
|
return pcsc_ret_to_error(rv); /* No readers configured */
|
2002-02-24 19:32:14 +00:00
|
|
|
|
}
|
2002-04-19 14:23:31 +00:00
|
|
|
|
gpriv = (struct pcsc_global_private_data *) malloc(sizeof(struct pcsc_global_private_data));
|
2002-02-24 19:32:14 +00:00
|
|
|
|
if (gpriv == NULL) {
|
|
|
|
|
SCardReleaseContext(pcsc_ctx);
|
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
gpriv->pcsc_ctx = pcsc_ctx;
|
|
|
|
|
*reader_data = gpriv;
|
|
|
|
|
|
|
|
|
|
reader_buf = (char *) malloc(sizeof(char) * reader_buf_size);
|
2004-04-27 17:41:02 +00:00
|
|
|
|
if (!reader_buf) {
|
|
|
|
|
free(gpriv);
|
|
|
|
|
*reader_data = NULL;
|
|
|
|
|
SCardReleaseContext(pcsc_ctx);
|
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
|
}
|
|
|
|
|
rv = SCardListReaders(pcsc_ctx, mszGroups, reader_buf,
|
|
|
|
|
(LPDWORD) &reader_buf_size);
|
|
|
|
|
if (rv != SCARD_S_SUCCESS) {
|
|
|
|
|
free(reader_buf);
|
|
|
|
|
free(gpriv);
|
|
|
|
|
*reader_data = NULL;
|
|
|
|
|
SCardReleaseContext(pcsc_ctx);
|
|
|
|
|
return pcsc_ret_to_error(rv);
|
|
|
|
|
}
|
2002-02-24 19:32:14 +00:00
|
|
|
|
p = reader_buf;
|
|
|
|
|
do {
|
2002-04-19 14:23:31 +00:00
|
|
|
|
struct sc_reader *reader = (struct sc_reader *) malloc(sizeof(struct sc_reader));
|
|
|
|
|
struct pcsc_private_data *priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data));
|
2003-01-19 17:47:07 +00:00
|
|
|
|
struct pcsc_slot_data *pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data));
|
2002-02-24 19:32:14 +00:00
|
|
|
|
struct sc_slot_info *slot;
|
|
|
|
|
|
2003-01-19 17:47:07 +00:00
|
|
|
|
if (reader == NULL || priv == NULL || pslot == NULL) {
|
2002-02-24 19:32:14 +00:00
|
|
|
|
if (reader)
|
|
|
|
|
free(reader);
|
|
|
|
|
if (priv)
|
|
|
|
|
free(priv);
|
2004-07-09 15:31:08 +00:00
|
|
|
|
if (pslot)
|
|
|
|
|
free(pslot);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2003-01-19 17:47:07 +00:00
|
|
|
|
|
2002-12-06 21:40:41 +00:00
|
|
|
|
memset(reader, 0, sizeof(*reader));
|
2002-02-24 19:32:14 +00:00
|
|
|
|
reader->drv_data = priv;
|
|
|
|
|
reader->ops = &pcsc_ops;
|
|
|
|
|
reader->driver = &pcsc_drv;
|
|
|
|
|
reader->slot_count = 1;
|
|
|
|
|
reader->name = strdup(p);
|
2002-06-14 11:33:20 +00:00
|
|
|
|
priv->gpriv = gpriv;
|
2002-03-24 22:47:35 +00:00
|
|
|
|
priv->pcsc_ctx = pcsc_ctx;
|
|
|
|
|
priv->reader_name = strdup(p);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
r = _sc_add_reader(ctx, reader);
|
|
|
|
|
if (r) {
|
|
|
|
|
free(priv->reader_name);
|
|
|
|
|
free(priv);
|
2002-03-26 11:38:40 +00:00
|
|
|
|
free(reader->name);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
free(reader);
|
2004-07-09 15:31:08 +00:00
|
|
|
|
free(pslot);
|
2002-02-24 19:32:14 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
slot = &reader->slot[0];
|
2003-01-19 17:47:07 +00:00
|
|
|
|
memset(slot, 0, sizeof(*slot));
|
|
|
|
|
slot->drv_data = pslot;
|
|
|
|
|
memset(pslot, 0, sizeof(*pslot));
|
2002-02-24 19:32:14 +00:00
|
|
|
|
refresh_slot_attributes(reader, slot);
|
|
|
|
|
|
|
|
|
|
while (*p++ != 0);
|
|
|
|
|
} while (p < (reader_buf + reader_buf_size - 1));
|
|
|
|
|
free(reader_buf);
|
|
|
|
|
|
2002-06-14 11:33:20 +00:00
|
|
|
|
for (i = 0; ctx->conf_blocks[i] != NULL; i++) {
|
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i],
|
|
|
|
|
"reader_driver", "pcsc");
|
|
|
|
|
conf_block = blocks[0];
|
|
|
|
|
free(blocks);
|
|
|
|
|
if (conf_block != NULL)
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-01-06 17:45:06 +00:00
|
|
|
|
|
2002-02-24 19:32:14 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2002-04-05 14:46:44 +00:00
|
|
|
|
static int pcsc_finish(struct sc_context *ctx, void *prv_data)
|
2002-02-24 19:32:14 +00:00
|
|
|
|
{
|
|
|
|
|
struct pcsc_global_private_data *priv = (struct pcsc_global_private_data *) prv_data;
|
2002-02-24 21:14:17 +00:00
|
|
|
|
|
|
|
|
|
if (priv) {
|
|
|
|
|
SCardReleaseContext(priv->pcsc_ctx);
|
|
|
|
|
free(priv);
|
|
|
|
|
}
|
2002-02-24 19:32:14 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-18 16:35:28 +00:00
|
|
|
|
struct sc_reader_driver * sc_get_pcsc_driver(void)
|
2002-02-24 19:32:14 +00:00
|
|
|
|
{
|
|
|
|
|
pcsc_ops.init = pcsc_init;
|
|
|
|
|
pcsc_ops.finish = pcsc_finish;
|
|
|
|
|
pcsc_ops.transmit = pcsc_transmit;
|
|
|
|
|
pcsc_ops.detect_card_presence = pcsc_detect_card_presence;
|
|
|
|
|
pcsc_ops.lock = pcsc_lock;
|
|
|
|
|
pcsc_ops.unlock = pcsc_unlock;
|
|
|
|
|
pcsc_ops.release = pcsc_release;
|
|
|
|
|
pcsc_ops.connect = pcsc_connect;
|
|
|
|
|
pcsc_ops.disconnect = pcsc_disconnect;
|
2003-06-23 12:56:36 +00:00
|
|
|
|
pcsc_ops.perform_verify = ctbcs_pin_cmd;
|
2003-01-03 16:32:06 +00:00
|
|
|
|
pcsc_ops.wait_for_event = pcsc_wait_for_event;
|
2002-02-24 19:32:14 +00:00
|
|
|
|
|
|
|
|
|
return &pcsc_drv;
|
|
|
|
|
}
|
2003-09-03 09:28:55 +00:00
|
|
|
|
|
2003-09-26 08:15:15 +00:00
|
|
|
|
#endif /* HAVE_PCSC */
|