ccid: Introduce ccid_slot_ops + implement simulator/stub for it

This adds a new interface to the CCID implementation, on the card/slot
side.  The purpose of this interface (based on function pointers) is
to allow for different real hardware or virtual implementations.

Change-Id: I2c38aa69594a3b22bb5b5e256edfb48481e42793
This commit is contained in:
Harald Welte 2019-05-16 13:31:16 +02:00
parent 922ff938b3
commit cab5d15346
6 changed files with 142 additions and 32 deletions

View File

@ -1,6 +1,6 @@
CFLAGS=-Wall -g
ccid_functionfs: ccid_main_functionfs.o ccid_proto.o ccid_device.o
ccid_functionfs: ccid_main_functionfs.o ccid_proto.o ccid_device.o ccid_slot_sim.o
$(CC) $(CFLAGS) -lasan -losmocore -ltalloc -laio -o $@ $^
%.o: %.c

View File

@ -421,7 +421,7 @@ static int ccid_handle_icc_power_off(struct ccid_slot *cs, struct msgb *msg)
uint8_t seq = u->icc_power_off.hdr.bSeq;
struct msgb *resp;
/* FIXME */
cs->ci->slot_ops->set_power(cs, false);
resp = ccid_gen_slot_status(cs, seq, CCID_CMD_STATUS_OK, 0);
return ccid_slot_send_unbusy(cs, resp);
}
@ -434,7 +434,7 @@ static int ccid_handle_xfr_block(struct ccid_slot *cs, struct msgb *msg)
uint8_t seq = u->xfr_block.hdr.bSeq;
struct msgb *resp;
/* FIXME */
/* FIXME: handle this asynchronously */
resp = ccid_gen_data_block(cs, seq, CCID_CMD_STATUS_OK, 0, NULL, 0);
return ccid_slot_send_unbusy(cs, resp);
}
@ -460,8 +460,11 @@ static int ccid_handle_reset_parameters(struct ccid_slot *cs, struct msgb *msg)
uint8_t seq = u->reset_parameters.hdr.bSeq;
struct msgb *resp;
/* FIXME: copy default parameters from somewhere */
/* copy default parameters from somewhere */
/* FIXME: T=1 */
cs->ci->slot_ops->set_params(cs, CCID_PROTOCOL_NUM_T0, cs->default_pars);
cs->pars = *cs->default_pars;
resp = ccid_gen_parameters_t0(cs, seq, CCID_CMD_STATUS_OK, 0);
return ccid_slot_send_unbusy(cs, resp);
}
@ -480,30 +483,29 @@ static int ccid_handle_set_parameters(struct ccid_slot *cs, struct msgb *msg)
switch (spar->bProtocolNum) {
case CCID_PROTOCOL_NUM_T0:
rc = decode_ccid_pars_t0(&pars_dec, &spar->abProtocolData.t0);
if (rc < 0) {
LOGP(DCCID, LOGL_ERROR, "SetParameters: Unable to parse T0: %d\n", rc);
resp = ccid_gen_parameters_t0(cs, seq, CCID_CMD_STATUS_FAILED, -rc);
goto out;
}
/* FIXME: validate parameters; abort if they are not supported */
cs->pars = pars_dec;
resp = ccid_gen_parameters_t0(cs, seq, CCID_CMD_STATUS_OK, 0);
break;
case CCID_PROTOCOL_NUM_T1:
rc = decode_ccid_pars_t1(&pars_dec, &spar->abProtocolData.t1);
if (rc < 0) {
LOGP(DCCID, LOGL_ERROR, "SetParameters: Unable to parse T1: %d\n", rc);
resp = ccid_gen_parameters_t1(cs, seq, CCID_CMD_STATUS_FAILED, -rc);
goto out;
}
/* FIXME: validate parameters; abort if they are not supported */
cs->pars = pars_dec;
resp = ccid_gen_parameters_t1(cs, seq, CCID_CMD_STATUS_OK, 0);
break;
default:
LOGP(DCCID, LOGL_ERROR, "SetParameters: Invalid Protocol 0x%02x\n",spar->bProtocolNum);
resp = ccid_gen_parameters_t0(cs, seq, CCID_CMD_STATUS_FAILED, 0);
break;
goto out;
}
if (rc < 0) {
LOGP(DCCID, LOGL_ERROR, "SetParameters: Unable to parse: %d\n", rc);
resp = ccid_gen_parameters_t0(cs, seq, CCID_CMD_STATUS_FAILED, -rc);
goto out;
}
/* validate parameters; abort if they are not supported */
rc = cs->ci->slot_ops->set_params(cs, spar->bProtocolNum, &pars_dec);
if (rc < 0) {
resp = ccid_gen_parameters_t0(cs, seq, CCID_CMD_STATUS_FAILED, -rc);
} else {
cs->pars = pars_dec;
resp = ccid_gen_parameters_t0(cs, seq, CCID_CMD_STATUS_OK, 0);
}
out:
return ccid_slot_send_unbusy(cs, resp);
@ -529,7 +531,7 @@ static int ccid_handle_icc_clock(struct ccid_slot *cs, struct msgb *msg)
uint8_t seq = u->icc_clock.hdr.bSeq;
struct msgb *resp;
/* FIXME: Actually Stop/Start the clock */
cs->ci->slot_ops->set_clock(cs, u->icc_clock.bClockCommand);
resp = ccid_gen_slot_status(cs, seq, CCID_CMD_STATUS_OK, 0);
return ccid_slot_send_unbusy(cs, resp);
}
@ -542,7 +544,7 @@ static int ccid_handle_t0apdu(struct ccid_slot *cs, struct msgb *msg)
uint8_t seq = u->t0apdu.hdr.bSeq;
struct msgb *resp;
/* FIXME */
/* FIXME: Required for APDU level exchange */
//resp = ccid_gen_slot_status(cs, seq, CCID_CMD_STATUS_OK, 0);
resp = ccid_gen_slot_status(cs, seq, CCID_CMD_STATUS_FAILED, CCID_ERR_CMD_NOT_SUPPORTED);
return ccid_slot_send_unbusy(cs, resp);
@ -608,10 +610,17 @@ static int ccid_handle_set_rate_and_clock(struct ccid_slot *cs, struct msgb *msg
const union ccid_pc_to_rdr *u = msgb_ccid_out(msg);
const struct ccid_header *ch = (const struct ccid_header *) u;
uint8_t seq = u->set_rate_and_clock.hdr.bSeq;
uint32_t freq_hz = osmo_load32le(&u->set_rate_and_clock.dwClockFrequency);
uint32_t rate_bps = osmo_load32le(&u->set_rate_and_clock.dwDataRate);
struct msgb *resp;
int rc;
/* FIXME */
resp = ccid_gen_clock_and_rate(cs, seq, CCID_CMD_STATUS_OK, 0, 9600, 2500000);
/* FIXME: which rate to return in failure case? */
rc = cs->ci->slot_ops->set_rate_and_clock(cs, freq_hz, rate_bps);
if (rc < 0)
resp = ccid_gen_clock_and_rate(cs, seq, CCID_CMD_STATUS_FAILED, -rc, 9600, 2500000);
else
resp = ccid_gen_clock_and_rate(cs, seq, CCID_CMD_STATUS_OK, 0, rate_bps, freq_hz);
return ccid_slot_send_unbusy(cs, resp);
}
@ -660,6 +669,10 @@ int ccid_handle_out(struct ccid_instance *ci, struct msgb *msg)
/* TODO: enqueue into the per-slot specific input queue */
/* call pre-processing call-back function; allows reader to update state */
if (ci->slot_ops->pre_proc_cb)
ci->slot_ops->pre_proc_cb(cs, msg);
switch (ch->bMessageType) {
case PC_to_RDR_GetSlotStatus:
if (len != sizeof(u->get_slot_status))
@ -751,8 +764,8 @@ short_msg:
return -1;
}
void ccid_instance_init(struct ccid_instance *ci, const struct ccid_ops *ops, const char *name,
void *priv)
void ccid_instance_init(struct ccid_instance *ci, const struct ccid_ops *ops,
const struct ccid_slot_ops *slot_ops, const char *name, void *priv)
{
int i;
@ -761,7 +774,8 @@ void ccid_instance_init(struct ccid_instance *ci, const struct ccid_ops *ops, co
cs->slot_nr = i;
cs->ci = ci;
}
ci->ops= ops;
ci->ops = ops;
ci->slot_ops = slot_ops;
ci->name = name;
ci->priv = priv;
}

View File

@ -2,6 +2,8 @@
#include <stdbool.h>
#include <stdint.h>
#include "ccid_proto.h"
enum {
DCCID,
DUSB,
@ -55,9 +57,11 @@ struct ccid_slot {
bool cmd_busy;
/* decided CCID parameters */
struct ccid_pars_decoded pars;
/* default parameters; applied on ResetParameters */
const struct ccid_pars_decoded *default_pars;
};
/* CCID operations */
/* CCID operations provided by USB transport layer */
struct ccid_ops {
/* msgb ownership in below functions is transferred, i.e. whoever
* provides the callback function must make sure to msgb_free() them
@ -66,17 +70,33 @@ struct ccid_ops {
int (*send_int)(struct ccid_instance *ci, struct msgb *msg);
};
/* CCID operations provided by actual slot hardware */
struct ccid_slot_ops {
/* called once on start-up for initialization */
int (*init)(struct ccid_slot *cs);
/* called before processing any command for a slot; used e.g. to
* update the (power/clock/...) status from the hardware */
void (*pre_proc_cb)(struct ccid_slot *cs, struct msgb *msg);
void (*set_power)(struct ccid_slot *cs, bool enable);
void (*set_clock)(struct ccid_slot *cs, enum ccid_clock_command cmd);
int (*set_params)(struct ccid_slot *cs, enum ccid_protocol_num proto,
const struct ccid_pars_decoded *pars_dec);
int (*set_rate_and_clock)(struct ccid_slot *cs, uint32_t freq_hz, uint32_t rate_bps);
};
/* An instance of CCID (i.e. a card reader device) */
struct ccid_instance {
/* slots within the reader */
struct ccid_slot slot[NR_SLOTS];
/* set of function pointers implementing specific operations */
const struct ccid_ops *ops;
const struct ccid_slot_ops *slot_ops;
const char *name;
/* user-supplied opaque data */
void *priv;
};
void ccid_instance_init(struct ccid_instance *ci, const struct ccid_ops *ops, const char *name,
void *priv);
void ccid_instance_init(struct ccid_instance *ci, const struct ccid_ops *ops,
const struct ccid_slot_ops *slot_ops, const char *name, void *priv);
int ccid_handle_out(struct ccid_instance *ci, struct msgb *msg);

View File

@ -139,6 +139,7 @@ static const struct {
#include <osmocom/core/logging.h>
#include "ccid_device.h"
#include "ccid_slot_sim.h"
#ifndef FUNCTIONFS_SUPPORTS_POLL
#include <libaio.h>
@ -509,7 +510,7 @@ int main(int argc, char **argv)
signal(SIGUSR1, &signal_handler);
ccid_instance_init(&ci, &c_ops, "", &ufh);
ccid_instance_init(&ci, &c_ops, &slotsim_slot_ops, "", &ufh);
ufh.ccid_handle = &ci;
if (argc < 2) {

72
ccid/ccid_slot_sim.c Normal file
View File

@ -0,0 +1,72 @@
/* Simulated CCID card slot. This is used in absence of a real hardware back-end
* in order to test the CCID firmware codebase in a virtual environment */
#include "ccid_device.h"
static const struct ccid_pars_decoded slotsim_def_pars = {
.fi = 0,
.di = 0,
.clock_stop = CCID_CLOCK_STOP_NOTALLOWED,
.inverse_convention = false,
.t0 = {
.guard_time_etu = 0,
.waiting_integer = 0,
},
/* FIXME: T=1 */
};
static void slotsim_pre_proc_cb(struct ccid_slot *cs, struct msgb *msg)
{
/* do nothing; real hardware would update the slot related state here */
}
static void slotsim_set_power(struct ccid_slot *cs, bool enable)
{
if (enable) {
cs->icc_powered = true;
/* FIXME: What to do about ATR? */
} else {
cs->icc_powered = false;
}
}
static void slotsim_set_clock(struct ccid_slot *cs, enum ccid_clock_command cmd)
{
/* FIXME */
switch (cmd) {
case CCID_CLOCK_CMD_STOP:
break;
case CCID_CLOCK_CMD_RESTART:
break;
default:
OSMO_ASSERT(0);
}
}
static int slotsim_set_params(struct ccid_slot *cs, enum ccid_protocol_num proto,
const struct ccid_pars_decoded *pars_dec)
{
/* we always acknowledge all parameters */
return 0;
}
static int slotsim_set_rate_and_clock(struct ccid_slot *cs, uint32_t freq_hz, uint32_t rate_bps)
{
/* we always acknowledge all rates/clocks */
return 0;
}
static int slotsim_init(struct ccid_slot *cs)
{
cs->default_pars = &slotsim_def_pars;
return 0;
}
const struct ccid_slot_ops slotsim_slot_ops = {
.init = slotsim_init,
.pre_proc_cb = slotsim_pre_proc_cb,
.set_power = slotsim_set_power,
.set_clock = slotsim_set_clock,
.set_params = slotsim_set_params,
.set_rate_and_clock = slotsim_set_rate_and_clock,
};

3
ccid/ccid_slot_sim.h Normal file
View File

@ -0,0 +1,3 @@
#pragma once
extern struct ccid_slot_ops slotsim_slot_ops;