Plug&Play support

This is not the best solution, but focus on smallest code change.

Changes:

1. Add detect_readers() to reader opts, this adds new readers to the end
   of the readers list until list is full.

2. Add sc_ctx_detect_readers() that calls readers' detect_readers().

3. Fixup pcsc_lock() so that it reconnect to the card and report proper
   error so caller may be notified if session was lost.

4. Allow context to be created without readers.

5. Call sc_ctx_detect_readers() from PKCS#11 C_GetSlotList with NULL_PTR.

6. Allow no reader at detect_card, as reader my be removed.

7. Since I broke ABI, I updated the external module version requirement
   to match OpenSC version. In the future a separate version should be
   maintained for each interface, this should be unrelated to the package
   version.

Alon

---

svn merge -r 3480:3505 https://www.opensc-project.org/svn/opensc/branches/alonbl/pnp

M    src/tools/opensc-tool.c
M    src/pkcs11/pkcs11-global.c
M    src/pkcs11/slot.c
M    src/libopensc/reader-pcsc.c
M    src/libopensc/internal-winscard.h
M    src/libopensc/ctx.c
M    src/libopensc/reader-ctapi.c
M    src/libopensc/libopensc.exports
M    src/libopensc/reader-openct.c
M    src/libopensc/opensc.h


git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3506 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
alonbl 2008-04-29 17:01:19 +00:00
parent c862f88ce7
commit e237574742
10 changed files with 203 additions and 97 deletions

View File

@ -354,7 +354,8 @@ static void *load_dynamic_driver(sc_context_t *ctx, void **dll,
}
/* verify module version */
version = modversion();
if (version == NULL || strncmp(version, "0.9.", strlen("0.9.")) > 0) {
/* XXX: We really need to have ABI version for each interface */
if (version == NULL || strncmp(version, PACKAGE_VERSION, strlen(PACKAGE_VERSION)) != 0) {
sc_error(ctx,"dynamic library '%s': invalid module version\n",libname);
lt_dlclose(handle);
return NULL;
@ -637,6 +638,25 @@ static void process_config_file(sc_context_t *ctx, struct _sc_ctx_options *opts)
load_parameters(ctx, ctx->conf_blocks[i], opts);
}
int sc_ctx_detect_readers(sc_context_t *ctx)
{
int i;
sc_mutex_lock(ctx, ctx->mutex);
for (i = 0; ctx->reader_drivers[i] != NULL; i++) {
const struct sc_reader_driver *drv = ctx->reader_drivers[i];
if (drv->ops->detect_readers != NULL)
drv->ops->detect_readers(ctx, ctx->reader_drv_data[i]);
}
sc_mutex_unlock(ctx, ctx->mutex);
/* XXX: Do not ignore erros? */
return SC_SUCCESS;
}
sc_reader_t *sc_ctx_get_reader(sc_context_t *ctx, unsigned int i)
{
if (i >= (unsigned int)ctx->reader_count || i >= SC_MAX_READERS)
@ -724,10 +744,7 @@ int sc_context_create(sc_context_t **ctx_out, const sc_context_param_t *parm)
}
del_drvs(&opts, 0);
del_drvs(&opts, 1);
if (ctx->reader_count == 0) {
sc_release_context(ctx);
return SC_ERROR_NO_READERS_FOUND;
}
sc_ctx_detect_readers(ctx);
*ctx_out = ctx;
return SC_SUCCESS;
}

View File

@ -51,9 +51,11 @@ typedef unsigned __int8 uint8_t;
#define SCARD_SCOPE_USER 0x0000 /**< Scope in user space */
#define SCARD_S_SUCCESS 0x00000000 /**< No error was encountered. */
#define SCARD_E_INVALID_HANDLE 0x80100003 /**< The supplied handle was invalid. */
#define SCARD_E_TIMEOUT 0x8010000A /**< The user-specified timeout value has expired. */
#define SCARD_E_SHARING_VIOLATION 0x8010000B /**< The smart card cannot be accessed because of other connections outstanding. */
#define SCARD_E_NOT_TRANSACTED 0x80100016 /**< An attempt was made to end a non-existent transaction. */
#define SCARD_E_READER_UNAVAILABLE 0x80100017 /**< The specified reader is not currently available for use. */
#define SCARD_E_NO_READERS_AVAILABLE 0x8010002E /**< Cannot find a smart card reader. */
#define SCARD_W_UNRESPONSIVE_CARD 0x80100066 /**< The smart card is not responding to a reset. */
#define SCARD_W_UNPOWERED_CARD 0x80100067 /**< Power has been removed from the smart card, so that further communication is not possible. */

View File

@ -37,6 +37,7 @@ sc_connect_card
sc_context_create
sc_copy_asn1_entry
sc_create_file
sc_ctx_detect_readers
sc_ctx_get_reader
sc_ctx_get_reader_count
sc_ctx_suppress_errors_off

View File

@ -373,6 +373,9 @@ struct sc_reader_operations {
/* Called when the driver is being unloaded. finish() has to
* deallocate the private data and any resources. */
int (*finish)(struct sc_context *ctx, void *priv_data);
/* Called when library wish to detect new readers
* should add only new readers. */
int (*detect_readers)(struct sc_context *ctx, void *priv_data);
/* Called when releasing a reader. release() has to
* deallocate the private data. Other fields will be
* freed by OpenSC. */
@ -726,6 +729,13 @@ int sc_context_create(sc_context_t **ctx, const sc_context_param_t *parm);
*/
int sc_release_context(sc_context_t *ctx);
/**
* Detect new readers available on system.
* @param ctx OpenSC context
* @return SC_SUCCESS on success and an error code otherwise.
*/
int sc_ctx_detect_readers(sc_context_t *ctx);
/**
* Returns a pointer to the specified sc_reader_t object
* @param ctx OpenSC context

View File

@ -611,6 +611,7 @@ struct sc_reader_driver * sc_get_ctapi_driver(void)
{
ctapi_ops.init = ctapi_init;
ctapi_ops.finish = ctapi_finish;
ctapi_ops.detect_readers = NULL;
ctapi_ops.transmit = ctapi_transmit;
ctapi_ops.detect_card_presence = ctapi_detect_card_presence;
ctapi_ops.lock = ctapi_lock;

View File

@ -479,6 +479,7 @@ struct sc_reader_driver *sc_get_openct_driver(void)
{
openct_ops.init = openct_reader_init;
openct_ops.finish = openct_reader_finish;
openct_ops.detect_readers = NULL;
openct_ops.release = openct_reader_release;
openct_ops.detect_card_presence = openct_reader_detect_card_presence;
openct_ops.connect = openct_reader_connect;

View File

@ -644,26 +644,32 @@ static int pcsc_lock(sc_reader_t *reader, sc_slot_info_t *slot)
rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card);
if ((unsigned int)rv == SCARD_W_RESET_CARD) {
/* try to reconnect if the card was reset by some other application */
rv = pcsc_reconnect(reader, slot, 0);
if (rv != SCARD_S_SUCCESS) {
PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv);
switch (rv) {
case SCARD_E_INVALID_HANDLE:
case SCARD_E_READER_UNAVAILABLE:
rv = pcsc_connect(reader, slot);
if (rv != SCARD_S_SUCCESS) {
PCSC_ERROR(reader->ctx, "SCardConnect failed", rv);
return pcsc_ret_to_error(rv);
}
/* return failure so that upper layers will be notified and try to lock again */
return SC_ERROR_READER_REATTACHED;
case SCARD_W_RESET_CARD:
/* try to reconnect if the card was reset by some other application */
rv = pcsc_reconnect(reader, slot, 0);
if (rv != SCARD_S_SUCCESS) {
PCSC_ERROR(reader->ctx, "SCardReconnect failed", rv);
return pcsc_ret_to_error(rv);
}
/* return failure so that upper layers will be notified and try to lock again */
return SC_ERROR_CARD_RESET;
case SCARD_S_SUCCESS:
pslot->locked = 1;
return SC_SUCCESS;
default:
PCSC_ERROR(reader->ctx, "SCardBeginTransaction failed", rv);
return pcsc_ret_to_error(rv);
}
/* Now try to begin a new transaction after we reconnected and we fail if
some other program was faster to lock the reader */
rv = priv->gpriv->SCardBeginTransaction(pslot->pcsc_card);
}
if (rv != SCARD_S_SUCCESS) {
PCSC_ERROR(reader->ctx, "SCardBeginTransaction failed", rv);
return pcsc_ret_to_error(rv);
}
pslot->locked = 1;
return SC_SUCCESS;
}
static int pcsc_unlock(sc_reader_t *reader, sc_slot_info_t *slot)
@ -731,10 +737,6 @@ static struct sc_reader_driver pcsc_drv = {
static int pcsc_init(sc_context_t *ctx, void **reader_data)
{
LONG rv;
DWORD reader_buf_size;
char *reader_buf = NULL, *p;
const char *mszGroups = NULL;
int r;
struct pcsc_global_private_data *gpriv;
scconf_block *conf_block = NULL;
int ret = SC_ERROR_INTERNAL;
@ -753,6 +755,7 @@ static int pcsc_init(sc_context_t *ctx, void **reader_data)
gpriv->transaction_reset = 0;
gpriv->enable_pinpad = 0;
gpriv->provider_library = DEFAULT_PCSC_PROVIDER;
gpriv->pcsc_ctx = -1;
conf_block = sc_get_conf_block(ctx, "reader_driver", "pcsc", 1);
if (conf_block) {
@ -818,72 +821,6 @@ static int pcsc_init(sc_context_t *ctx, void **reader_data)
goto out;
}
rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER,
NULL, NULL, &gpriv->pcsc_ctx);
if (rv != SCARD_S_SUCCESS) {
ret = pcsc_ret_to_error(rv);
goto out;
}
rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL,
(LPDWORD) &reader_buf_size);
if (rv != SCARD_S_SUCCESS || reader_buf_size < 2) {
ret = pcsc_ret_to_error(rv); /* No readers configured */
goto out;
}
reader_buf = (char *) malloc(sizeof(char) * reader_buf_size);
if (!reader_buf) {
ret = SC_ERROR_OUT_OF_MEMORY;
goto out;
}
rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, mszGroups, reader_buf,
(LPDWORD) &reader_buf_size);
if (rv != SCARD_S_SUCCESS) {
ret = pcsc_ret_to_error(rv);
goto out;
}
p = reader_buf;
do {
sc_reader_t *reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t));
struct pcsc_private_data *priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data));
struct pcsc_slot_data *pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data));
sc_slot_info_t *slot;
if (reader == NULL || priv == NULL || pslot == NULL) {
if (reader)
free(reader);
if (priv)
free(priv);
if (pslot)
free(pslot);
break;
}
reader->drv_data = priv;
reader->ops = &pcsc_ops;
reader->driver = &pcsc_drv;
reader->slot_count = 1;
reader->name = strdup(p);
priv->gpriv = gpriv;
priv->reader_name = strdup(p);
r = _sc_add_reader(ctx, reader);
if (r) {
free(priv->reader_name);
free(priv);
free(reader->name);
free(reader);
free(pslot);
break;
}
slot = &reader->slot[0];
memset(slot, 0, sizeof(*slot));
slot->drv_data = pslot;
memset(pslot, 0, sizeof(*pslot));
refresh_slot_attributes(reader, slot);
while (*p++ != 0);
} while (p < (reader_buf + reader_buf_size - 1));
*reader_data = gpriv;
gpriv = NULL;
ret = SC_SUCCESS;
@ -892,8 +829,6 @@ out:
if (ret != SC_SUCCESS) {
if (gpriv->pcsc_ctx != 0)
gpriv->SCardReleaseContext(gpriv->pcsc_ctx);
if (reader_buf != NULL)
free(reader_buf);
if (gpriv->dlhandle != NULL)
lt_dlclose(gpriv->dlhandle);
if (gpriv != NULL)
@ -916,6 +851,141 @@ static int pcsc_finish(sc_context_t *ctx, void *prv_data)
return 0;
}
static int pcsc_detect_readers(sc_context_t *ctx, void *prv_data)
{
struct pcsc_global_private_data *gpriv = (struct pcsc_global_private_data *) prv_data;
LONG rv;
DWORD reader_buf_size;
char *reader_buf = NULL, *p;
const char *mszGroups = NULL;
int ret = SC_ERROR_INTERNAL;
int again;
if (!prv_data) {
ret = SC_ERROR_NO_READERS_FOUND;
goto out;
}
do {
rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, NULL, NULL,
(LPDWORD) &reader_buf_size);
if (rv != SCARD_S_SUCCESS) {
if (rv != SCARD_E_INVALID_HANDLE) {
ret = pcsc_ret_to_error(rv);
goto out;
}
rv = gpriv->SCardEstablishContext(SCARD_SCOPE_USER,
NULL, NULL, &gpriv->pcsc_ctx);
if (rv != SCARD_S_SUCCESS) {
ret = pcsc_ret_to_error(rv);
goto out;
}
rv = SCARD_E_INVALID_HANDLE;
}
} while (rv != SCARD_S_SUCCESS);
reader_buf = (char *) malloc(sizeof(char) * reader_buf_size);
if (!reader_buf) {
ret = SC_ERROR_OUT_OF_MEMORY;
goto out;
}
rv = gpriv->SCardListReaders(gpriv->pcsc_ctx, mszGroups, reader_buf,
(LPDWORD) &reader_buf_size);
if (rv != SCARD_S_SUCCESS) {
ret = pcsc_ret_to_error(rv);
goto out;
}
for (p = reader_buf; *p != '\x0'; p += strlen (p) + 1) {
sc_reader_t *reader = NULL;
struct pcsc_private_data *priv = NULL;
struct pcsc_slot_data *pslot = NULL;
sc_slot_info_t *slot = NULL;
int i;
int found = 0;
for (i=0;i < sc_ctx_get_reader_count (ctx) && !found;i++) {
sc_reader_t *reader = sc_ctx_get_reader (ctx, i);
if (reader == NULL) {
ret = SC_ERROR_INTERNAL;
goto err1;
}
if (reader->ops == &pcsc_ops && !strcmp (reader->name, p)) {
found = 1;
}
}
/* Reader already available, skip */
if (found) {
continue;
}
if ((reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t))) == NULL) {
ret = SC_ERROR_OUT_OF_MEMORY;
goto err1;
}
if ((priv = (struct pcsc_private_data *) malloc(sizeof(struct pcsc_private_data))) == NULL) {
ret = SC_ERROR_OUT_OF_MEMORY;
goto err1;
}
if ((pslot = (struct pcsc_slot_data *) malloc(sizeof(struct pcsc_slot_data))) == NULL) {
ret = SC_ERROR_OUT_OF_MEMORY;
goto err1;
}
reader->drv_data = priv;
reader->ops = &pcsc_ops;
reader->driver = &pcsc_drv;
reader->slot_count = 1;
if ((reader->name = strdup(p)) == NULL) {
ret = SC_ERROR_OUT_OF_MEMORY;
goto err1;
}
priv->gpriv = gpriv;
if ((priv->reader_name = strdup(p)) == NULL) {
ret = SC_ERROR_OUT_OF_MEMORY;
goto err1;
}
slot = &reader->slot[0];
memset(slot, 0, sizeof(*slot));
slot->drv_data = pslot;
memset(pslot, 0, sizeof(*pslot));
if (_sc_add_reader(ctx, reader)) {
ret = SC_SUCCESS; /* silent ignore */
goto err1;
}
refresh_slot_attributes(reader, slot);
continue;
err1:
if (priv != NULL) {
if (priv->reader_name)
free(priv->reader_name);
free(priv);
}
if (reader != NULL) {
if (reader->name)
free(reader->name);
free(reader);
}
if (slot != NULL)
free(pslot);
goto out;
}
ret = SC_SUCCESS;
out:
if (reader_buf != NULL)
free (reader_buf);
return ret;
}
static int
pcsc_pin_cmd(sc_reader_t *reader, sc_slot_info_t * slot, struct sc_pin_cmd_data *data)
{
@ -931,6 +1001,7 @@ struct sc_reader_driver * sc_get_pcsc_driver(void)
{
pcsc_ops.init = pcsc_init;
pcsc_ops.finish = pcsc_finish;
pcsc_ops.detect_readers = pcsc_detect_readers;
pcsc_ops.transmit = pcsc_transmit;
pcsc_ops.detect_card_presence = pcsc_detect_card_presence;
pcsc_ops.lock = pcsc_lock;

View File

@ -347,6 +347,9 @@ CK_RV C_GetSlotList(CK_BBOOL tokenPresent, /* only slots with token prese
}
sc_debug(context, "Getting slot listing\n");
if (pSlotList == NULL_PTR) {
sc_ctx_detect_readers(context);
}
card_detect_all();
numMatches = 0;

View File

@ -86,7 +86,7 @@ CK_RV card_detect(int reader)
sc_reader_t *rdr = sc_ctx_get_reader(context, (unsigned int)reader);
if (rdr == NULL)
return CKR_GENERAL_ERROR;
return CKR_TOKEN_NOT_PRESENT;
slot = virtual_slots + card->first_slot + i;
strcpy_bp(slot->slot_info.slotDescription, rdr->name, 64);
slot->reader = reader;

View File

@ -246,7 +246,7 @@ static int list_readers(void)
unsigned int i, rcount = sc_ctx_get_reader_count(ctx);
if (rcount == 0) {
printf("No readers configured!\n");
printf("No readers found.\n");
return 0;
}
printf("Readers known about:\n");