ccid: Handle IccPowerOn and XfrBlock asynchronously

In real hardware, the CCID code will have to wait for the related
UART transfers to complete.  Hence, simulate this by starting a timer
and responding asynchronously to those commands.

Change-Id: I6aa13a3c6e6ea37902c07584174413bfd058dd36
diff --git a/ccid/ccid_device.c b/ccid/ccid_device.c
index ff523d3..9dafebc 100644
--- a/ccid/ccid_device.c
+++ b/ccid/ccid_device.c
@@ -403,14 +403,10 @@
 {
 	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->icc_power_on.hdr.bSeq;
-	struct msgb *resp;
 
-	/* TODO: send actual ATR; handle error cases */
-	/* TODO: handle this asynchronously */
-	resp = ccid_gen_data_block(cs, seq, CCID_CMD_STATUS_OK, 0, NULL, 0);
-
-	return ccid_slot_send_unbusy(cs, resp);
+	/* handle this asynchronously */
+	cs->ci->slot_ops->icc_power_on_async(cs, msg, &u->icc_power_on);
+	return 1;
 }
 
 /* Section 6.1.2 */
@@ -431,12 +427,10 @@
 {
 	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->xfr_block.hdr.bSeq;
-	struct msgb *resp;
 
-	/* 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);
+	/* handle this asynchronously */
+	cs->ci->slot_ops->xfr_block_async(cs, msg, &u->xfr_block);
+	return 1;
 }
 
 /* Section 6.1.5 */
diff --git a/ccid/ccid_device.h b/ccid/ccid_device.h
index 5b3a5bc..7eba842 100644
--- a/ccid/ccid_device.h
+++ b/ccid/ccid_device.h
@@ -78,6 +78,10 @@
 	 * update the (power/clock/...) status from the hardware */
 	void (*pre_proc_cb)(struct ccid_slot *cs, struct msgb *msg);
 
+	void (*icc_power_on_async)(struct ccid_slot *cs, struct msgb *msg,
+				   const struct ccid_pc_to_rdr_icc_power_on *ipo);
+	void (*xfr_block_async)(struct ccid_slot *cs, struct msgb *msg,
+				const struct ccid_pc_to_rdr_xfr_block *xfb);
 	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,
diff --git a/ccid/ccid_slot_sim.c b/ccid/ccid_slot_sim.c
index d7a5952..57fe3cf 100644
--- a/ccid/ccid_slot_sim.c
+++ b/ccid/ccid_slot_sim.c
@@ -1,8 +1,30 @@
 /* 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 <osmocom/core/msgb.h>
+#include <osmocom/core/timer.h>
+
 #include "ccid_device.h"
 
+struct slotsim_slot {
+	struct osmo_timer_list pwron_timer;
+	struct osmo_timer_list xfr_timer;
+	/* bSeq of the operation currently in progress */
+	uint8_t seq;
+};
+
+struct slotsim_instance {
+	struct slotsim_slot slot[NR_SLOTS];
+};
+
+static struct slotsim_instance g_si;
+
+struct slotsim_slot *ccid_slot2slotsim_slot(struct ccid_slot *cs)
+{
+	OSMO_ASSERT(cs->slot_nr < ARRAY_SIZE(g_si.slot));
+	return &g_si.slot[cs->slot_nr];
+}
+
 static const struct ccid_pars_decoded slotsim_def_pars = {
 	.fi = 0,
 	.di = 0,
@@ -20,6 +42,47 @@
 	/* do nothing; real hardware would update the slot related state here */
 }
 
+static void slotsim_icc_power_on_async(struct ccid_slot *cs, struct msgb *msg,
+					const struct ccid_pc_to_rdr_icc_power_on *ipo)
+{
+	struct slotsim_slot *ss = ccid_slot2slotsim_slot(cs);
+
+	ss->seq = ipo->hdr.bSeq;
+	osmo_timer_schedule(&ss->pwron_timer, 1, 0);
+	msgb_free(msg);
+	/* continues in timer call-back below */
+}
+static void slotsim_pwron_timer_cb(void *data)
+{
+	struct ccid_slot *cs = data;
+	struct slotsim_slot *ss = ccid_slot2slotsim_slot(cs);
+	struct msgb *resp;
+
+	resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_OK, 0, NULL, 0);
+	ccid_slot_send_unbusy(cs, resp);
+}
+
+static void slotsim_xfr_block_async(struct ccid_slot *cs, struct msgb *msg,
+				const struct ccid_pc_to_rdr_xfr_block *xfb)
+{
+	struct slotsim_slot *ss = ccid_slot2slotsim_slot(cs);
+
+	ss->seq = xfb->hdr.bSeq;
+	osmo_timer_schedule(&ss->xfr_timer, 0, 50000);
+	msgb_free(msg);
+	/* continues in timer call-back below */
+}
+static void slotsim_xfr_timer_cb(void *data)
+{
+	struct ccid_slot *cs = data;
+	struct slotsim_slot *ss = ccid_slot2slotsim_slot(cs);
+	struct msgb *resp;
+
+	resp = ccid_gen_data_block(cs, ss->seq, CCID_CMD_STATUS_OK, 0, NULL, 0);
+	ccid_slot_send_unbusy(cs, resp);
+}
+
+
 static void slotsim_set_power(struct ccid_slot *cs, bool enable)
 {
 	if (enable) {
@@ -56,8 +119,12 @@
 	return 0;
 }
 
+
 static int slotsim_init(struct ccid_slot *cs)
 {
+	struct slotsim_slot *ss = ccid_slot2slotsim_slot(cs);
+	osmo_timer_setup(&ss->pwron_timer, slotsim_pwron_timer_cb, cs);
+	osmo_timer_setup(&ss->xfr_timer, slotsim_xfr_timer_cb, cs);
 	cs->default_pars = &slotsim_def_pars;
 	return 0;
 }
@@ -65,6 +132,8 @@
 const struct ccid_slot_ops slotsim_slot_ops = {
 	.init = slotsim_init,
 	.pre_proc_cb = slotsim_pre_proc_cb,
+	.icc_power_on_async = slotsim_icc_power_on_async,
+	.xfr_block_async = slotsim_xfr_block_async,
 	.set_power = slotsim_set_power,
 	.set_clock = slotsim_set_clock,
 	.set_params = slotsim_set_params,