2002-03-26 11:38:40 +00:00
|
|
|
/*
|
|
|
|
* reader-ctapi.c: Reader driver for CT-API
|
|
|
|
*
|
2006-12-19 21:31:17 +00:00
|
|
|
* Copyright (C) 2002 Juha Yrjölä <juha.yrjola@iki.fi>
|
2002-03-26 11:38:40 +00:00
|
|
|
*
|
|
|
|
* 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"
|
2009-11-15 18:03:04 +00:00
|
|
|
#ifdef ENABLE_CTAPI
|
2002-03-26 11:38:40 +00:00
|
|
|
#include "ctbcs.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
2005-09-01 14:01:58 +00:00
|
|
|
#include <ltdl.h>
|
2002-03-26 11:38:40 +00:00
|
|
|
|
|
|
|
#define GET_PRIV_DATA(r) ((struct ctapi_private_data *) (r)->drv_data)
|
|
|
|
|
2005-01-16 14:24:57 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
typedef char pascal CT_INIT_TYPE(unsigned short ctn, unsigned short Pn);
|
|
|
|
typedef char pascal CT_CLOSE_TYPE(unsigned short ctn);
|
|
|
|
typedef char pascal CT_DATA_TYPE(unsigned short ctn, unsigned char *dad, \
|
|
|
|
unsigned char *sad, unsigned short lc, \
|
|
|
|
unsigned char *cmd, unsigned short *lr, \
|
|
|
|
unsigned char *rsp);
|
|
|
|
#else
|
|
|
|
typedef char CT_INIT_TYPE(unsigned short ctn, unsigned short Pn);
|
|
|
|
typedef char CT_CLOSE_TYPE(unsigned short ctn);
|
|
|
|
typedef char CT_DATA_TYPE(unsigned short ctn, unsigned char *dad, \
|
|
|
|
unsigned char *sad, unsigned short lc, \
|
|
|
|
unsigned char *cmd, unsigned short *lr, \
|
|
|
|
unsigned char *rsp);
|
|
|
|
#endif
|
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
struct ctapi_module {
|
|
|
|
char *name;
|
|
|
|
void *dlhandle;
|
|
|
|
int ctn_count;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ctapi_global_private_data {
|
|
|
|
int module_count;
|
|
|
|
struct ctapi_module *modules;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ctapi_functions {
|
2005-01-16 14:24:57 +00:00
|
|
|
CT_INIT_TYPE *CT_init;
|
|
|
|
CT_CLOSE_TYPE *CT_close;
|
|
|
|
CT_DATA_TYPE *CT_data;
|
2002-03-26 11:38:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Reader specific private data */
|
2005-01-29 10:49:48 +00:00
|
|
|
#define CTAPI_FU_KEYBOARD 0x1
|
|
|
|
#define CTAPI_FU_DISPLAY 0x2
|
|
|
|
#define CTAPI_FU_BIOMETRIC 0x4
|
|
|
|
#define CTAPI_FU_PRINTER 0x8
|
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
struct ctapi_private_data {
|
|
|
|
struct ctapi_functions funcs;
|
|
|
|
unsigned short ctn;
|
2005-01-29 10:49:48 +00:00
|
|
|
int ctapi_functional_units;
|
2010-01-24 15:25:08 +00:00
|
|
|
int slot;
|
2002-03-26 11:38:40 +00:00
|
|
|
};
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
/* Reset reader */
|
|
|
|
static int ctapi_reset(sc_reader_t *reader)
|
2005-01-29 10:49:48 +00:00
|
|
|
{
|
|
|
|
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;
|
2010-01-24 15:25:08 +00:00
|
|
|
cmd[2] = priv->slot ? CTBCS_P1_INTERFACE1 + priv->slot : CTBCS_P1_CT_KERNEL;
|
2005-01-29 10:49:48 +00:00
|
|
|
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)) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "Error getting status of terminal: %d, using defaults\n", rv);
|
2005-01-29 10:49:48 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
|
|
|
if (rbuf[lr-2] != 0x90) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "SW1/SW2: 0x%x/0x%x\n", rbuf[lr-2], rbuf[lr-1]);
|
2005-01-29 10:49:48 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int refresh_attributes(sc_reader_t *reader)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
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_STATUS;
|
|
|
|
cmd[2] = CTBCS_P1_CT_KERNEL;
|
|
|
|
cmd[3] = CTBCS_P2_STATUS_ICC;
|
|
|
|
cmd[4] = 0x00;
|
|
|
|
dad = 1;
|
|
|
|
sad = 2;
|
|
|
|
lr = 256;
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
reader->flags = 0;
|
2002-03-26 11:38:40 +00:00
|
|
|
|
|
|
|
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
|
2005-01-29 10:49:48 +00:00
|
|
|
if (rv || (lr < 3) || (rbuf[lr-2] != 0x90)) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "Error getting status of terminal: %d/%d/0x%x\n", rv, lr, rbuf[lr-2]);
|
2002-03-26 11:38:40 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
2005-01-29 10:49:48 +00:00
|
|
|
if (lr < 4) {
|
|
|
|
if (rbuf[0] & CTBCS_DATA_STATUS_CARD)
|
2010-01-24 15:25:08 +00:00
|
|
|
reader->flags = SC_READER_CARD_PRESENT;
|
2005-01-29 10:49:48 +00:00
|
|
|
} else {
|
|
|
|
if (rbuf[0] != CTBCS_P2_STATUS_ICC) {
|
|
|
|
/* Should we be more tolerant here? I do not think so... */
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "Invalid data object returnd on CTBCS_P2_STATUS_ICC: 0x%x\n", rbuf[0]);
|
2005-01-29 10:49:48 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
2010-01-24 15:25:08 +00:00
|
|
|
/* Fixme - should not be reached */
|
|
|
|
sc_debug(reader->ctx, "Returned status for %d slots\n", rbuf[1]);
|
|
|
|
reader->flags = SC_READER_CARD_PRESENT;
|
2005-01-29 10:49:48 +00:00
|
|
|
}
|
2002-03-26 11:38:40 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int ctapi_internal_transmit(sc_reader_t *reader,
|
2002-03-26 11:38:40 +00:00
|
|
|
const u8 *sendbuf, size_t sendsize,
|
2002-12-23 18:47:27 +00:00
|
|
|
u8 *recvbuf, size_t *recvsize,
|
2005-01-24 11:31:11 +00:00
|
|
|
unsigned long control)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
|
|
|
|
u8 dad, sad;
|
|
|
|
unsigned short lr;
|
|
|
|
char rv;
|
|
|
|
|
2005-01-29 10:49:48 +00:00
|
|
|
if (control)
|
|
|
|
dad = 1;
|
|
|
|
else
|
2010-01-24 15:25:08 +00:00
|
|
|
dad = 0;
|
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
sad = 2;
|
|
|
|
lr = *recvsize;
|
|
|
|
|
2005-01-29 10:49:48 +00:00
|
|
|
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, (unsigned short)sendsize, (u8 *) sendbuf, &lr, recvbuf);
|
2002-03-26 11:38:40 +00:00
|
|
|
if (rv != 0) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "Error transmitting APDU: %d\n", rv);
|
2002-03-26 11:38:40 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
|
|
|
*recvsize = lr;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int ctapi_transmit(sc_reader_t *reader, sc_apdu_t *apdu)
|
2006-02-05 19:00:01 +00:00
|
|
|
{
|
|
|
|
size_t ssize, rsize, rbuflen = 0;
|
|
|
|
u8 *sbuf = NULL, *rbuf = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
rsize = rbuflen = apdu->resplen + 2;
|
|
|
|
rbuf = malloc(rbuflen);
|
|
|
|
if (rbuf == NULL) {
|
2010-01-24 12:38:34 +00:00
|
|
|
r = SC_ERROR_OUT_OF_MEMORY;
|
2006-02-05 19:00:01 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/* encode and log the APDU */
|
2006-03-03 22:56:41 +00:00
|
|
|
r = sc_apdu_get_octets(reader->ctx, apdu, &sbuf, &ssize, SC_PROTO_RAW);
|
2006-02-05 19:00:01 +00:00
|
|
|
if (r != SC_SUCCESS)
|
|
|
|
goto out;
|
2006-11-23 22:40:01 +00:00
|
|
|
if (reader->ctx->debug >= 6)
|
|
|
|
sc_apdu_log(reader->ctx, sbuf, ssize, 1);
|
2010-01-24 15:25:08 +00:00
|
|
|
r = ctapi_internal_transmit(reader, sbuf, ssize,
|
2006-02-05 19:00:01 +00:00
|
|
|
rbuf, &rsize, apdu->control);
|
|
|
|
if (r < 0) {
|
|
|
|
/* unable to transmit ... most likely a reader problem */
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "unable to transmit");
|
2006-02-05 19:00:01 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2006-11-23 22:40:01 +00:00
|
|
|
if (reader->ctx->debug >= 6)
|
|
|
|
sc_apdu_log(reader->ctx, rbuf, rsize, 0);
|
2006-03-03 22:56:41 +00:00
|
|
|
/* set response */
|
|
|
|
r = sc_apdu_set_resp(reader->ctx, apdu, rbuf, rsize);
|
2006-02-05 19:00:01 +00:00
|
|
|
out:
|
|
|
|
if (sbuf != NULL) {
|
|
|
|
sc_mem_clear(sbuf, ssize);
|
|
|
|
free(sbuf);
|
|
|
|
}
|
|
|
|
if (rbuf != NULL) {
|
|
|
|
sc_mem_clear(rbuf, rbuflen);
|
|
|
|
free(rbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int ctapi_detect_card_presence(sc_reader_t *reader)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
r = refresh_attributes(reader);
|
2002-03-26 11:38:40 +00:00
|
|
|
if (r)
|
|
|
|
return r;
|
2010-01-24 15:25:08 +00:00
|
|
|
return reader->flags;
|
2002-03-26 11:38:40 +00:00
|
|
|
}
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int ctapi_connect(sc_reader_t *reader)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
|
|
|
|
char rv;
|
|
|
|
u8 cmd[9], rbuf[256], sad, dad;
|
|
|
|
unsigned short lr;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
cmd[0] = CTBCS_CLA;
|
|
|
|
cmd[1] = CTBCS_INS_REQUEST;
|
2010-01-24 15:25:08 +00:00
|
|
|
cmd[2] = CTBCS_P1_INTERFACE1;
|
2002-03-26 11:38:40 +00:00
|
|
|
cmd[3] = CTBCS_P2_REQUEST_GET_ATR;
|
|
|
|
cmd[4] = 0x00;
|
|
|
|
dad = 1;
|
|
|
|
sad = 2;
|
|
|
|
lr = 256;
|
|
|
|
|
|
|
|
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 5, cmd, &lr, rbuf);
|
|
|
|
if (rv || rbuf[lr-2] != 0x90) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "Error activating card: %d\n", rv);
|
2002-03-26 11:38:40 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
|
|
|
if (lr < 2)
|
|
|
|
SC_FUNC_RETURN(reader->ctx, 0, SC_ERROR_INTERNAL);
|
|
|
|
lr -= 2;
|
|
|
|
if (lr > SC_MAX_ATR_SIZE)
|
|
|
|
lr = SC_MAX_ATR_SIZE;
|
2010-01-24 15:25:08 +00:00
|
|
|
memcpy(reader->atr, rbuf, lr);
|
|
|
|
reader->atr_len = lr;
|
|
|
|
r = _sc_parse_atr(reader);
|
2002-03-26 11:38:40 +00:00
|
|
|
|
|
|
|
#if 0
|
2010-01-24 15:25:08 +00:00
|
|
|
if (reader->atr_info.Fi > 0) {
|
2002-03-26 11:38:40 +00:00
|
|
|
/* Perform PPS negotiation */
|
|
|
|
cmd[1] = CTBCS_INS_RESET;
|
|
|
|
cmd[4] = 0x03;
|
|
|
|
cmd[5] = 0xFF;
|
|
|
|
cmd[6] = 0x10;
|
2010-01-24 15:25:08 +00:00
|
|
|
cmd[7] = (reader->atr_info.FI << 4) | reader->atr_info.DI;
|
2002-03-26 11:38:40 +00:00
|
|
|
cmd[8] = 0x00;
|
|
|
|
dad = 1;
|
|
|
|
sad = 2;
|
|
|
|
lr = 256;
|
|
|
|
|
|
|
|
rv = priv->funcs.CT_data(priv->ctn, &dad, &sad, 9, cmd, &lr, rbuf);
|
|
|
|
if (rv) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(reader->ctx, "Error negotiating PPS: %d\n", rv);
|
2002-03-26 11:38:40 +00:00
|
|
|
return SC_ERROR_TRANSMIT_FAILED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int ctapi_disconnect(sc_reader_t *reader)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2005-01-29 10:49:48 +00:00
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int ctapi_lock(sc_reader_t *reader)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-01-24 15:25:08 +00:00
|
|
|
static int ctapi_unlock(sc_reader_t *reader)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int ctapi_release(sc_reader_t *reader)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
|
|
|
|
|
2002-04-04 09:20:44 +00:00
|
|
|
priv->funcs.CT_close(priv->ctn);
|
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
free(priv);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sc_reader_operations ctapi_ops;
|
|
|
|
|
2003-12-18 16:35:28 +00:00
|
|
|
static struct sc_reader_driver ctapi_drv = {
|
2002-03-26 11:38:40 +00:00
|
|
|
"CT-API module",
|
|
|
|
"ctapi",
|
2005-09-07 08:33:55 +00:00
|
|
|
&ctapi_ops,
|
2006-02-05 19:00:01 +00:00
|
|
|
0, 0, NULL
|
2002-03-26 11:38:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct ctapi_module * add_module(struct ctapi_global_private_data *gpriv,
|
|
|
|
const char *name, void *dlhandle)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = gpriv->module_count;
|
2002-04-19 14:23:31 +00:00
|
|
|
gpriv->modules = (struct ctapi_module *) realloc(gpriv->modules, sizeof(struct ctapi_module) * (i+1));
|
2002-03-26 11:38:40 +00:00
|
|
|
gpriv->modules[i].name = strdup(name);
|
|
|
|
gpriv->modules[i].dlhandle = dlhandle;
|
|
|
|
gpriv->modules[i].ctn_count = 0;
|
|
|
|
gpriv->module_count++;
|
|
|
|
|
|
|
|
return &gpriv->modules[i];
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int ctapi_load_module(sc_context_t *ctx,
|
2002-03-26 11:38:40 +00:00
|
|
|
struct ctapi_global_private_data *gpriv,
|
|
|
|
scconf_block *conf)
|
|
|
|
{
|
|
|
|
const char *val;
|
|
|
|
struct ctapi_functions funcs;
|
|
|
|
struct ctapi_module *mod;
|
|
|
|
const scconf_list *list;
|
2002-03-31 15:26:25 +00:00
|
|
|
void *dlh;
|
2010-01-24 15:25:08 +00:00
|
|
|
int r, i, NumUnits;
|
|
|
|
char rv;
|
|
|
|
u8 cmd[5], rbuf[256], sad, dad;
|
|
|
|
unsigned short lr;
|
|
|
|
|
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
|
|
|
|
list = scconf_find_list(conf, "ports");
|
|
|
|
if (list == NULL) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(ctx, "No ports configured.\n");
|
2002-03-26 11:38:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = conf->name->data;
|
2005-09-01 14:01:58 +00:00
|
|
|
dlh = lt_dlopen(val);
|
2004-10-18 21:35:24 +00:00
|
|
|
if (!dlh) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(ctx, "Unable to open shared library '%s': %s\n", val, lt_dlerror());
|
2002-03-26 11:38:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2004-10-18 21:35:24 +00:00
|
|
|
|
2005-09-01 14:01:58 +00:00
|
|
|
funcs.CT_init = (CT_INIT_TYPE *) lt_dlsym(dlh, "CT_init");
|
2004-10-18 21:35:24 +00:00
|
|
|
if (!funcs.CT_init)
|
2002-03-26 11:38:40 +00:00
|
|
|
goto symerr;
|
2005-09-01 14:01:58 +00:00
|
|
|
funcs.CT_close = (CT_CLOSE_TYPE *) lt_dlsym(dlh, "CT_close");
|
2004-10-18 21:35:24 +00:00
|
|
|
if (!funcs.CT_close)
|
2002-03-26 11:38:40 +00:00
|
|
|
goto symerr;
|
2005-09-01 14:01:58 +00:00
|
|
|
funcs.CT_data = (CT_DATA_TYPE *) lt_dlsym(dlh, "CT_data");
|
2005-01-04 19:45:05 +00:00
|
|
|
if (!funcs.CT_data)
|
2002-03-26 11:38:40 +00:00
|
|
|
goto symerr;
|
2004-10-18 21:35:24 +00:00
|
|
|
|
2002-03-26 11:38:40 +00:00
|
|
|
mod = add_module(gpriv, val, dlh);
|
|
|
|
for (; list != NULL; list = list->next) {
|
|
|
|
int port;
|
|
|
|
char namebuf[128];
|
|
|
|
char rv;
|
2005-03-08 20:59:35 +00:00
|
|
|
sc_reader_t *reader;
|
2002-03-26 11:38:40 +00:00
|
|
|
struct ctapi_private_data *priv;
|
|
|
|
|
|
|
|
if (sscanf(list->data, "%d", &port) != 1) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(ctx, "Port '%s' is not a number.\n", list->data);
|
2002-03-26 11:38:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2005-01-29 10:49:48 +00:00
|
|
|
rv = funcs.CT_init((unsigned short)mod->ctn_count, (unsigned short)port);
|
2002-03-26 11:38:40 +00:00
|
|
|
if (rv) {
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(ctx, "CT_init() failed with %d\n", rv);
|
2002-03-26 11:38:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2010-01-24 15:25:08 +00:00
|
|
|
|
|
|
|
/* 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) */
|
|
|
|
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_debug(reader->ctx, "Error getting status of terminal: %d, using defaults\n", rv);
|
|
|
|
}
|
|
|
|
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... ;) */
|
|
|
|
sc_debug(reader->ctx, "Invalid data object returnd on CTBCS_P2_STATUS_TFU: 0x%x\n", rbuf[0]);
|
|
|
|
}
|
|
|
|
NumUnits = rbuf[1];
|
|
|
|
if (NumUnits + 4 > lr) {
|
|
|
|
sc_debug(reader->ctx, "Invalid data returnd: %d functional units, size %d\n", NumUnits, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
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? */
|
|
|
|
sc_debug(reader->ctx, "Found slot id 0x%x\n", rbuf[i+2]);
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* 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) {
|
|
|
|
if (priv->ctapi_functional_units & CTAPI_FU_KEYBOARD)
|
|
|
|
reader->capabilities |= SC_READER_CAP_PIN_PAD;
|
|
|
|
if (priv->ctapi_functional_units & CTAPI_FU_DISPLAY)
|
|
|
|
reader->capabilities |= SC_READER_CAP_DISPLAY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-09-17 09:40:12 +00:00
|
|
|
reader = (sc_reader_t *) calloc(1, sizeof(sc_reader_t));
|
2010-01-24 15:25:08 +00:00
|
|
|
priv = (struct ctapi_private_data *) calloc(1, sizeof(struct ctapi_private_data));
|
|
|
|
if (!priv) return SC_ERROR_OUT_OF_MEMORY;
|
2002-03-26 11:38:40 +00:00
|
|
|
reader->drv_data = priv;
|
|
|
|
reader->ops = &ctapi_ops;
|
|
|
|
reader->driver = &ctapi_drv;
|
|
|
|
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) {
|
2005-01-29 10:49:48 +00:00
|
|
|
funcs.CT_close((unsigned short)mod->ctn_count);
|
2002-03-26 11:38:40 +00:00
|
|
|
free(priv);
|
|
|
|
free(reader->name);
|
|
|
|
free(reader);
|
|
|
|
break;
|
|
|
|
}
|
2005-01-29 10:49:48 +00:00
|
|
|
/* slot count and properties are set in detect_functional_units */
|
|
|
|
detect_functional_units(reader);
|
2002-03-26 11:38:40 +00:00
|
|
|
|
2005-01-29 10:49:48 +00:00
|
|
|
ctapi_reset(reader, NULL);
|
2010-01-24 15:25:08 +00:00
|
|
|
refresh_attributes(reader);
|
2002-03-26 11:38:40 +00:00
|
|
|
mod->ctn_count++;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
symerr:
|
2009-09-14 08:46:59 +00:00
|
|
|
sc_debug(ctx, "Unable to resolve CT-API symbols.\n");
|
2005-09-01 14:01:58 +00:00
|
|
|
lt_dlclose(dlh);
|
2002-03-26 11:38:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int ctapi_init(sc_context_t *ctx, void **reader_data)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct ctapi_global_private_data *gpriv;
|
|
|
|
scconf_block **blocks = NULL, *conf_block = NULL;
|
|
|
|
|
2005-09-17 09:40:12 +00:00
|
|
|
gpriv = (struct ctapi_global_private_data *) calloc(1, sizeof(struct ctapi_global_private_data));
|
2002-03-26 11:38:40 +00:00
|
|
|
if (gpriv == NULL)
|
|
|
|
return SC_ERROR_OUT_OF_MEMORY;
|
|
|
|
*reader_data = gpriv;
|
|
|
|
|
|
|
|
for (i = 0; ctx->conf_blocks[i] != NULL; i++) {
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i],
|
|
|
|
"reader_driver", "ctapi");
|
2005-12-05 21:38:33 +00:00
|
|
|
if (blocks && blocks[0])
|
|
|
|
conf_block = blocks[0];
|
2002-03-26 11:38:40 +00:00
|
|
|
free(blocks);
|
|
|
|
if (conf_block != NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (conf_block == NULL)
|
|
|
|
return 0;
|
|
|
|
blocks = scconf_find_blocks(ctx->conf, conf_block, "module", NULL);
|
|
|
|
for (i = 0; blocks != NULL && blocks[i] != NULL; i++)
|
|
|
|
ctapi_load_module(ctx, gpriv, blocks[i]);
|
|
|
|
free(blocks);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-03-08 20:59:35 +00:00
|
|
|
static int ctapi_finish(sc_context_t *ctx, void *prv_data)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
struct ctapi_global_private_data *priv = (struct ctapi_global_private_data *) prv_data;
|
|
|
|
|
|
|
|
if (priv) {
|
2002-04-05 14:46:44 +00:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < priv->module_count; i++) {
|
|
|
|
struct ctapi_module *mod = &priv->modules[i];
|
|
|
|
|
|
|
|
free(mod->name);
|
2005-09-01 14:01:58 +00:00
|
|
|
lt_dlclose(mod->dlhandle);
|
2002-04-05 14:46:44 +00:00
|
|
|
}
|
|
|
|
if (priv->module_count)
|
|
|
|
free(priv->modules);
|
2002-03-26 11:38:40 +00:00
|
|
|
free(priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-12-18 16:35:28 +00:00
|
|
|
struct sc_reader_driver * sc_get_ctapi_driver(void)
|
2002-03-26 11:38:40 +00:00
|
|
|
{
|
|
|
|
ctapi_ops.init = ctapi_init;
|
|
|
|
ctapi_ops.finish = ctapi_finish;
|
2008-04-29 17:01:19 +00:00
|
|
|
ctapi_ops.detect_readers = NULL;
|
2002-03-26 11:38:40 +00:00
|
|
|
ctapi_ops.transmit = ctapi_transmit;
|
|
|
|
ctapi_ops.detect_card_presence = ctapi_detect_card_presence;
|
|
|
|
ctapi_ops.lock = ctapi_lock;
|
|
|
|
ctapi_ops.unlock = ctapi_unlock;
|
|
|
|
ctapi_ops.release = ctapi_release;
|
|
|
|
ctapi_ops.connect = ctapi_connect;
|
|
|
|
ctapi_ops.disconnect = ctapi_disconnect;
|
2003-06-23 12:56:36 +00:00
|
|
|
ctapi_ops.perform_verify = ctbcs_pin_cmd;
|
2002-03-26 11:38:40 +00:00
|
|
|
|
|
|
|
return &ctapi_drv;
|
|
|
|
}
|
2009-11-15 18:03:04 +00:00
|
|
|
#endif
|