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:
parent
922ff938b3
commit
cab5d15346
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
#pragma once
|
||||
|
||||
extern struct ccid_slot_ops slotsim_slot_ops;
|
Loading…
Reference in New Issue