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/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;
 }