- Add experimental multi-slot support for CT-API
and CT-BCS 1.0 enhancements. (Bernhard Froehlich <ted@convey.de>) - Enable CT-API for win32 git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@2111 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
parent
986724c1ad
commit
16b1b6e5a2
|
@ -77,7 +77,7 @@ ctbcs_build_output_apdu(sc_apdu_t *apdu, const char *message)
|
|||
#endif
|
||||
|
||||
static int
|
||||
ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
|
||||
ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data, sc_slot_info_t *slot)
|
||||
{
|
||||
const char *prompt;
|
||||
size_t buflen, count = 0, j = 0, len;
|
||||
|
@ -87,7 +87,7 @@ ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *d
|
|||
ctbcs_init_apdu(apdu,
|
||||
SC_APDU_CASE_3_SHORT,
|
||||
CTBCS_INS_PERFORM_VERIFICATION,
|
||||
CTBCS_P1_KEYPAD,
|
||||
CTBCS_P1_INTERFACE1 + (slot ? slot->id : 0),
|
||||
0);
|
||||
|
||||
buflen = sizeof(buf);
|
||||
|
@ -121,7 +121,7 @@ ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *d
|
|||
if (data->pin1.min_length == data->pin1.max_length)
|
||||
control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT;
|
||||
buf[j++] = control;
|
||||
buf[j++] = data->pin1.offset;
|
||||
buf[j++] = data->pin1.offset+1; /* Looks like offset is 1-based in CTBCS */
|
||||
buf[j++] = data->apdu->cla;
|
||||
buf[j++] = data->apdu->ins;
|
||||
buf[j++] = data->apdu->p1;
|
||||
|
@ -145,7 +145,7 @@ ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *d
|
|||
}
|
||||
|
||||
static int
|
||||
ctbcs_build_modify_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
|
||||
ctbcs_build_modify_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data, sc_slot_info_t *slot)
|
||||
{
|
||||
/* to be implemented */
|
||||
return SC_ERROR_NOT_SUPPORTED;
|
||||
|
@ -162,11 +162,11 @@ ctbcs_pin_cmd(struct sc_reader *reader, sc_slot_info_t *slot,
|
|||
|
||||
switch (data->cmd) {
|
||||
case SC_PIN_CMD_VERIFY:
|
||||
r = ctbcs_build_perform_verification_apdu(&apdu, data);
|
||||
r = ctbcs_build_perform_verification_apdu(&apdu, data, slot);
|
||||
break;
|
||||
case SC_PIN_CMD_CHANGE:
|
||||
case SC_PIN_CMD_UNBLOCK:
|
||||
r = ctbcs_build_modify_verification_apdu(&apdu, data);
|
||||
r = ctbcs_build_modify_verification_apdu(&apdu, data, slot);
|
||||
break;
|
||||
default:
|
||||
sc_error(reader->ctx, "Unknown PIN command %d", data->cmd);
|
||||
|
@ -209,5 +209,11 @@ ctbcs_pin_cmd(struct sc_reader *reader, sc_slot_info_t *slot,
|
|||
}
|
||||
SC_TEST_RET(card->ctx, r, "PIN command failed");
|
||||
|
||||
/* Calling Function may expect SW1/SW2 in data-apdu set... */
|
||||
if (data->apdu) {
|
||||
data->apdu->sw1 = apdu.sw1;
|
||||
data->apdu->sw2 = apdu.sw2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -71,6 +71,13 @@
|
|||
#define CTBCS_P1_INTERFACE14 0x0E
|
||||
#define CTBCS_P1_DISPLAY 0x40
|
||||
#define CTBCS_P1_KEYPAD 0x50
|
||||
#define CTBCS_P1_PRINTER 0x60 /* New CT-BCS 1.0 */
|
||||
#define CTBCS_P1_FINGERPRINT 0x70 /* New CT-BCS 1.0 */
|
||||
#define CTBCS_P1_VOICEPRINT 0x71 /* New CT-BCS 1.0 */
|
||||
#define CTBCS_P1_DSV 0x72 /* "Dynamic Signature Verification" New CT-BCS 1.0 */
|
||||
#define CTBCS_P1_FACE_RECOGNITION 0x73 /* New CT-BCS 1.0 */
|
||||
#define CTBCS_P1_IRISSCAN 0x74 /* New CT-BCS 1.0 */
|
||||
/* Other biometric units may use values up to 0x7F */
|
||||
|
||||
/*
|
||||
* P2 parameter for Reset CT: data to be returned
|
||||
|
@ -91,6 +98,7 @@
|
|||
*/
|
||||
#define CTBCS_P2_STATUS_MANUFACTURER 0x46 /* Return manufacturer DO */
|
||||
#define CTBCS_P2_STATUS_ICC 0x80 /* Return ICC DO */
|
||||
#define CTBCS_P2_STATUS_TFU 0x81 /* Return Functional Units, new in Version 1.0 */
|
||||
|
||||
/*
|
||||
* P2 parameter for Input
|
||||
|
@ -153,20 +161,20 @@
|
|||
#define CTBCS_SW2_REQUEST_NO_CARD 0x00
|
||||
#define CTBCS_SW1_REQUEST_CARD_PRESENT 0x62 /* Card already present */
|
||||
#define CTBCS_SW2_REQUEST_CARD_PRESENT 0x01
|
||||
#define CTBCS_SW1_REQUEST_ERROR 0x64 /* Reset not successful */
|
||||
#define CTBCS_SW2_REQUEST_ERROR 0x00
|
||||
#define CTBCS_SW1_REQUEST_ERROR 0x64 /* Reset not successful */
|
||||
#define CTBCS_SW2_REQUEST_ERROR 0x00
|
||||
#define CTBCS_SW1_REQUEST_TIMER_ERROR 0x69 /* Timer not supported */
|
||||
#define CTBCS_SW2_REQUEST_TIMER_ERROR 0x00
|
||||
|
||||
/*
|
||||
* Return codes for Eject ICC
|
||||
*/
|
||||
#define CTBCS_SW1_EJECT_OK 0x90 /* Command succesful, */
|
||||
#define CTBCS_SW2_EJECT_OK 0x00
|
||||
#define CTBCS_SW1_EJECT_REMOVED 0x90 /* Command succesful, */
|
||||
#define CTBCS_SW2_EJECT_REMOVED 0x01 /* Card removed */
|
||||
#define CTBCS_SW1_EJECT_NOT_REMOVED 0x62 /* Card not removed */
|
||||
#define CTBCS_SW2_EJECT_NOT_REMOVED 0x00
|
||||
#define CTBCS_SW1_EJECT_OK 0x90 /* Command succesful, */
|
||||
#define CTBCS_SW2_EJECT_OK 0x00
|
||||
#define CTBCS_SW1_EJECT_REMOVED 0x90 /* Command succesful, */
|
||||
#define CTBCS_SW2_EJECT_REMOVED 0x01 /* Card removed */
|
||||
#define CTBCS_SW1_EJECT_NOT_REMOVED 0x62 /* Card not removed */
|
||||
#define CTBCS_SW2_EJECT_NOT_REMOVED 0x00
|
||||
|
||||
/*
|
||||
* Data returned on Get Status command
|
||||
|
@ -180,6 +188,4 @@
|
|||
*/
|
||||
int ctbcs_pin_cmd(struct sc_reader *, sc_slot_info_t *, struct sc_pin_cmd_data *);
|
||||
|
||||
|
||||
#endif /* _CTBCS_ */
|
||||
|
||||
|
|
|
@ -84,8 +84,8 @@ static const struct _sc_driver_entry internal_reader_drivers[] = {
|
|||
#if defined(HAVE_PCSC)
|
||||
{ "pcsc", (void *) sc_get_pcsc_driver, NULL },
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
{ "ctapi", (void *) sc_get_ctapi_driver, NULL },
|
||||
#ifndef _WIN32
|
||||
#ifdef HAVE_OPENCT
|
||||
{ "openct", (void *) sc_get_openct_driver, NULL },
|
||||
#endif
|
||||
|
|
|
@ -63,15 +63,180 @@ struct ctapi_functions {
|
|||
};
|
||||
|
||||
/* Reader specific private data */
|
||||
#define CTAPI_FU_KEYBOARD 0x1
|
||||
#define CTAPI_FU_DISPLAY 0x2
|
||||
#define CTAPI_FU_BIOMETRIC 0x4
|
||||
#define CTAPI_FU_PRINTER 0x8
|
||||
|
||||
struct ctapi_private_data {
|
||||
struct ctapi_functions funcs;
|
||||
unsigned short ctn;
|
||||
int ctapi_functional_units;
|
||||
};
|
||||
|
||||
struct ctapi_slot_data {
|
||||
void *filler;
|
||||
};
|
||||
|
||||
/* Reset slot or reader */
|
||||
static int ctapi_reset(struct sc_reader *reader, struct sc_slot_info *slot)
|
||||
{
|
||||
struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
|
||||
char rv;
|
||||
u8 cmd[5], rbuf[256], sad, dad;
|
||||
unsigned short lr;
|
||||
|
||||
cmd[0] = CTBCS_CLA;
|
||||
cmd[1] = CTBCS_INS_RESET;
|
||||
cmd[2] = slot ? CTBCS_P1_INTERFACE1 + slot->id : CTBCS_P1_CT_KERNEL;
|
||||
cmd[3] = 0x00; /* No response. We might also use 0x01 (return ATR) or 0x02 (return historical bytes) here */
|
||||
cmd[4] = 0x00;
|
||||
dad = 1;
|
||||
sad = 2;
|
||||
lr = 256;
|
||||
|
||||
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
|
||||
if (rv || (lr < 2)) {
|
||||
sc_error(reader->ctx, "Error getting status of terminal: %d, using defaults\n", rv);
|
||||
return SC_ERROR_TRANSMIT_FAILED;
|
||||
}
|
||||
if (rbuf[lr-2] != 0x90) {
|
||||
sc_error(reader->ctx, "SW1/SW2: 0x%x/0x%x\n", rbuf[lr-2], rbuf[lr-1]);
|
||||
return SC_ERROR_TRANSMIT_FAILED;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_default_fu(struct sc_reader *reader)
|
||||
{
|
||||
if (!reader) return;
|
||||
|
||||
reader->slot_count = 1;
|
||||
reader->slot[0].id = 0;
|
||||
reader->slot[0].capabilities = 0;
|
||||
reader->slot[0].atr_len = 0;
|
||||
reader->slot[0].drv_data = NULL;
|
||||
}
|
||||
|
||||
/* Detect functional units of the reader according to CT-BCS spec version 1.0
|
||||
(14.04.2004, http://www.teletrust.de/down/mct1-0_t4.zip) */
|
||||
static void detect_functional_units(struct sc_reader *reader)
|
||||
{
|
||||
struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
|
||||
char rv;
|
||||
u8 cmd[5], rbuf[256], sad, dad;
|
||||
unsigned short lr;
|
||||
int NumUnits;
|
||||
int i;
|
||||
|
||||
priv->ctapi_functional_units = 0;
|
||||
|
||||
cmd[0] = CTBCS_CLA;
|
||||
cmd[1] = CTBCS_INS_STATUS;
|
||||
cmd[2] = CTBCS_P1_CT_KERNEL;
|
||||
cmd[3] = CTBCS_P2_STATUS_TFU;
|
||||
cmd[4] = 0x00;
|
||||
dad = 1;
|
||||
sad = 2;
|
||||
lr = 256;
|
||||
|
||||
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
|
||||
if (rv || (lr < 4) || (rbuf[lr-2] != 0x90)) {
|
||||
sc_error(reader->ctx, "Error getting status of terminal: %d, using defaults\n", rv);
|
||||
set_default_fu(reader);
|
||||
return;
|
||||
}
|
||||
if (rbuf[0] != CTBCS_P2_STATUS_TFU) {
|
||||
/* Number of slots might also detected by using CTBCS_P2_STATUS_ICC.
|
||||
If you think that's important please do it... ;) */
|
||||
set_default_fu(reader);
|
||||
sc_error(reader->ctx, "Invalid data object returnd on CTBCS_P2_STATUS_TFU: 0x%x\n", rbuf[0]);
|
||||
return;
|
||||
}
|
||||
NumUnits = rbuf[1];
|
||||
if (NumUnits + 4 > lr) {
|
||||
set_default_fu(reader);
|
||||
sc_error(reader->ctx, "Invalid data returnd: %d functional units, size %d\n", NumUnits, rv);
|
||||
set_default_fu(reader);
|
||||
return;
|
||||
}
|
||||
reader->slot_count = 0;
|
||||
for(i = 0; i < NumUnits; i++) {
|
||||
switch(rbuf[i+2])
|
||||
{
|
||||
case CTBCS_P1_INTERFACE1:
|
||||
case CTBCS_P1_INTERFACE2:
|
||||
case CTBCS_P1_INTERFACE3:
|
||||
case CTBCS_P1_INTERFACE4:
|
||||
case CTBCS_P1_INTERFACE5:
|
||||
case CTBCS_P1_INTERFACE6:
|
||||
case CTBCS_P1_INTERFACE7:
|
||||
case CTBCS_P1_INTERFACE8:
|
||||
case CTBCS_P1_INTERFACE9:
|
||||
case CTBCS_P1_INTERFACE10:
|
||||
case CTBCS_P1_INTERFACE11:
|
||||
case CTBCS_P1_INTERFACE12:
|
||||
case CTBCS_P1_INTERFACE13:
|
||||
case CTBCS_P1_INTERFACE14:
|
||||
/* Maybe a weak point here if multiple interfaces are present and not returned
|
||||
in the "canonical" order. This is not forbidden by the specs, but why should
|
||||
anyone want to do that? */
|
||||
if (reader->slot_count >= SC_MAX_SLOTS) {
|
||||
sc_debug(reader->ctx, "Ignoring slot id 0x%x, can only handle %d slots\n", rbuf[i+2], SC_MAX_SLOTS);
|
||||
} else {
|
||||
reader->slot[reader->slot_count].id = reader->slot_count;
|
||||
reader->slot[reader->slot_count].capabilities = 0; /* Just to start with */
|
||||
reader->slot[reader->slot_count].atr_len = 0;
|
||||
reader->slot[reader->slot_count].drv_data = NULL;
|
||||
reader->slot_count++;
|
||||
}
|
||||
break;
|
||||
|
||||
case CTBCS_P1_DISPLAY:
|
||||
priv->ctapi_functional_units |= CTAPI_FU_DISPLAY;
|
||||
sc_debug(reader->ctx, "Display detected\n");
|
||||
break;
|
||||
|
||||
case CTBCS_P1_KEYPAD:
|
||||
priv->ctapi_functional_units |= CTAPI_FU_KEYBOARD;
|
||||
sc_debug(reader->ctx, "Keypad detected\n");
|
||||
break;
|
||||
|
||||
case CTBCS_P1_PRINTER:
|
||||
priv->ctapi_functional_units |= CTAPI_FU_PRINTER;
|
||||
sc_debug(reader->ctx, "Printer detected\n");
|
||||
break;
|
||||
|
||||
case CTBCS_P1_FINGERPRINT:
|
||||
case CTBCS_P1_VOICEPRINT:
|
||||
case CTBCS_P1_DSV:
|
||||
case CTBCS_P1_FACE_RECOGNITION:
|
||||
case CTBCS_P1_IRISSCAN:
|
||||
priv->ctapi_functional_units |= CTAPI_FU_BIOMETRIC;
|
||||
sc_debug(reader->ctx, "Biometric sensor detected\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
sc_debug(reader->ctx, "Unknown functional unit 0x%x\n", rbuf[i+2]);
|
||||
}
|
||||
|
||||
}
|
||||
if (reader->slot_count == 0) {
|
||||
sc_debug(reader->ctx, "No slots returned, assuning one default slot\n");
|
||||
set_default_fu(reader);
|
||||
}
|
||||
/* CT-BCS does not define Keyboard/Display for each slot, so I assume
|
||||
those additional units can be used for each slot */
|
||||
if (priv->ctapi_functional_units) {
|
||||
for(i = 0; i < reader->slot_count; i++) {
|
||||
if (priv->ctapi_functional_units & CTAPI_FU_KEYBOARD)
|
||||
reader->slot[i].capabilities |= SC_SLOT_CAP_PIN_PAD;
|
||||
if (priv->ctapi_functional_units & CTAPI_FU_DISPLAY)
|
||||
reader->slot[i].capabilities |= SC_SLOT_CAP_DISPLAY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int refresh_slot_attributes(struct sc_reader *reader,
|
||||
struct sc_slot_info *slot)
|
||||
{
|
||||
|
@ -92,12 +257,31 @@ static int refresh_slot_attributes(struct sc_reader *reader,
|
|||
slot->flags = 0;
|
||||
|
||||
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
|
||||
if (rv || rbuf[lr-2] != 0x90) {
|
||||
sc_error(reader->ctx, "Error getting status of terminal: %d\n", rv);
|
||||
if (rv || (lr < 3) || (rbuf[lr-2] != 0x90)) {
|
||||
sc_error(reader->ctx, "Error getting status of terminal: %d/%d/0x%x\n", rv, lr, rbuf[lr-2]);
|
||||
return SC_ERROR_TRANSMIT_FAILED;
|
||||
}
|
||||
if (rbuf[0] == CTBCS_DATA_STATUS_CARD_CONNECT)
|
||||
if (lr < 4) {
|
||||
/* Looks like older readers do not return data tag and length field, so assume one slot only */
|
||||
if (slot->id > 0) {
|
||||
sc_error(reader->ctx, "Status for slot id %d not returned, have only 1\n", slot->id);
|
||||
return SC_ERROR_SLOT_NOT_FOUND;
|
||||
}
|
||||
if (rbuf[0] & CTBCS_DATA_STATUS_CARD)
|
||||
slot->flags = SC_SLOT_CARD_PRESENT;
|
||||
} else {
|
||||
if (rbuf[0] != CTBCS_P2_STATUS_ICC) {
|
||||
/* Should we be more tolerant here? I do not think so... */
|
||||
sc_error(reader->ctx, "Invalid data object returnd on CTBCS_P2_STATUS_ICC: 0x%x\n", rbuf[0]);
|
||||
return SC_ERROR_TRANSMIT_FAILED;
|
||||
}
|
||||
if (rbuf[1] <= slot->id) {
|
||||
sc_error(reader->ctx, "Status for slot id %d not returned, only %d\n", slot->id, rbuf[1]);
|
||||
return SC_ERROR_SLOT_NOT_FOUND;
|
||||
}
|
||||
if (rbuf[2+slot->id] & CTBCS_DATA_STATUS_CARD)
|
||||
slot->flags = SC_SLOT_CARD_PRESENT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -112,11 +296,16 @@ static int ctapi_transmit(struct sc_reader *reader, struct sc_slot_info *slot,
|
|||
unsigned short lr;
|
||||
char rv;
|
||||
|
||||
dad = control? 1 : 0;
|
||||
if (control)
|
||||
dad = 1;
|
||||
else if (!slot || slot->id == 0)
|
||||
dad = 0;
|
||||
else
|
||||
dad = slot->id + 1; /* Adressing of multiple slots, according to CT API 1.0 */
|
||||
sad = 2;
|
||||
lr = *recvsize;
|
||||
|
||||
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, sendsize, (u8 *) sendbuf, &lr, recvbuf);
|
||||
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, (unsigned short)sendsize, (u8 *) sendbuf, &lr, recvbuf);
|
||||
if (rv != 0) {
|
||||
sc_error(reader->ctx, "Error transmitting APDU: %d\n", rv);
|
||||
return SC_ERROR_TRANSMIT_FAILED;
|
||||
|
@ -146,7 +335,7 @@ static int ctapi_connect(struct sc_reader *reader, struct sc_slot_info *slot)
|
|||
|
||||
cmd[0] = CTBCS_CLA;
|
||||
cmd[1] = CTBCS_INS_REQUEST;
|
||||
cmd[2] = CTBCS_P1_INTERFACE1;
|
||||
cmd[2] = CTBCS_P1_INTERFACE1+slot->id;
|
||||
cmd[3] = CTBCS_P2_REQUEST_GET_ATR;
|
||||
cmd[4] = 0x00;
|
||||
dad = 1;
|
||||
|
@ -195,7 +384,7 @@ static int ctapi_disconnect(struct sc_reader *reader, struct sc_slot_info *slot,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ctapi_lock(struct sc_reader *reader, struct sc_slot_info *slot)
|
||||
{
|
||||
return 0;
|
||||
|
@ -248,7 +437,7 @@ static int ctapi_load_module(struct sc_context *ctx,
|
|||
struct ctapi_module *mod;
|
||||
const scconf_list *list;
|
||||
void *dlh;
|
||||
int r;
|
||||
int r, i;
|
||||
|
||||
list = scconf_find_list(conf, "ports");
|
||||
if (list == NULL) {
|
||||
|
@ -263,13 +452,13 @@ static int ctapi_load_module(struct sc_context *ctx,
|
|||
return -1;
|
||||
}
|
||||
|
||||
funcs.CT_init = (CT_INIT_TYPE *) scdl_get_address(dlh, "CT_init");
|
||||
funcs.CT_init = (CT_INIT_TYPE *) scdl_get_address(dlh, "CT_init");
|
||||
if (!funcs.CT_init)
|
||||
goto symerr;
|
||||
funcs.CT_close = (CT_CLOSE_TYPE *) scdl_get_address(dlh, "CT_close");
|
||||
if (!funcs.CT_close)
|
||||
goto symerr;
|
||||
funcs.CT_data = (CT_DATA_TYPE *) scdl_get_address(dlh, "CT_data");
|
||||
funcs.CT_data = (CT_DATA_TYPE *) scdl_get_address(dlh, "CT_data");
|
||||
if (!funcs.CT_data)
|
||||
goto symerr;
|
||||
|
||||
|
@ -280,13 +469,12 @@ static int ctapi_load_module(struct sc_context *ctx,
|
|||
char rv;
|
||||
struct sc_reader *reader;
|
||||
struct ctapi_private_data *priv;
|
||||
struct sc_slot_info *slot;
|
||||
|
||||
if (sscanf(list->data, "%d", &port) != 1) {
|
||||
sc_error(ctx, "Port '%s' is not a number.\n", list->data);
|
||||
continue;
|
||||
}
|
||||
rv = funcs.CT_init(mod->ctn_count, port);
|
||||
rv = funcs.CT_init((unsigned short)mod->ctn_count, (unsigned short)port);
|
||||
if (rv) {
|
||||
sc_error(ctx, "CT_init() failed with %d\n", rv);
|
||||
continue;
|
||||
|
@ -297,26 +485,25 @@ static int ctapi_load_module(struct sc_context *ctx,
|
|||
reader->drv_data = priv;
|
||||
reader->ops = &ctapi_ops;
|
||||
reader->driver = &ctapi_drv;
|
||||
reader->slot_count = 1;
|
||||
snprintf(namebuf, sizeof(namebuf), "CT-API %s, port %d", mod->name, port);
|
||||
reader->name = strdup(namebuf);
|
||||
priv->funcs = funcs;
|
||||
priv->ctn = mod->ctn_count;
|
||||
r = _sc_add_reader(ctx, reader);
|
||||
if (r) {
|
||||
funcs.CT_close(mod->ctn_count);
|
||||
funcs.CT_close((unsigned short)mod->ctn_count);
|
||||
free(priv);
|
||||
free(reader->name);
|
||||
free(reader);
|
||||
break;
|
||||
}
|
||||
slot = &reader->slot[0];
|
||||
slot->id = 0;
|
||||
slot->capabilities = 0;
|
||||
slot->atr_len = 0;
|
||||
slot->drv_data = NULL;
|
||||
/* slot count and properties are set in detect_functional_units */
|
||||
detect_functional_units(reader);
|
||||
|
||||
refresh_slot_attributes(reader, slot);
|
||||
ctapi_reset(reader, NULL);
|
||||
for(i = 0; i < reader->slot_count; i++) {
|
||||
refresh_slot_attributes(reader, &(reader->slot[i]));
|
||||
}
|
||||
|
||||
mod->ctn_count++;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue