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
diff --git a/ccid/Makefile b/ccid/Makefile
index cd8321f..2194180 100644
--- a/ccid/Makefile
+++ b/ccid/Makefile
@@ -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
diff --git a/ccid/ccid_device.c b/ccid/ccid_device.c
index 87d99ce..d9e59b0 100644
--- a/ccid/ccid_device.c
+++ b/ccid/ccid_device.c
@@ -421,7 +421,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
cs->slot_nr = i;
cs->ci = ci;
}
- ci->ops= ops;
+ ci->ops = ops;
+ ci->slot_ops = slot_ops;
ci->name = name;
ci->priv = priv;
}
diff --git a/ccid/ccid_device.h b/ccid/ccid_device.h
index d0fcdc0..e7bb557 100644
--- a/ccid/ccid_device.h
+++ b/ccid/ccid_device.h
@@ -2,6 +2,8 @@
#include <stdbool.h>
#include <stdint.h>
+#include "ccid_proto.h"
+
enum {
DCCID,
DUSB,
@@ -55,9 +57,11 @@
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 @@
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);
diff --git a/ccid/ccid_main_functionfs.c b/ccid/ccid_main_functionfs.c
index e32fbf7..06ab0f3 100644
--- a/ccid/ccid_main_functionfs.c
+++ b/ccid/ccid_main_functionfs.c
@@ -139,6 +139,7 @@
#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 @@
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) {
diff --git a/ccid/ccid_slot_sim.c b/ccid/ccid_slot_sim.c
new file mode 100644
index 0000000..d7a5952
--- /dev/null
+++ b/ccid/ccid_slot_sim.c
@@ -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,
+};
diff --git a/ccid/ccid_slot_sim.h b/ccid/ccid_slot_sim.h
new file mode 100644
index 0000000..6217756
--- /dev/null
+++ b/ccid/ccid_slot_sim.h
@@ -0,0 +1,3 @@
+#pragma once
+
+extern struct ccid_slot_ops slotsim_slot_ops;