WIP: Attempt at using osmo_fsm from bankd. futile.

osmo timers and osmo fsm are both not thread safe :(

Change-Id: I6eb72fdbe3cc02e7fdc8afcdc033d007355d3fe1
diff --git a/README.md b/README.md
index 8808338..22f7fea 100644
--- a/README.md
+++ b/README.md
@@ -72,6 +72,7 @@
 * ACCEPTING (they're blocking in the accept() call on the server socket fd)
 * CONNECTED_WAIT_ID (TCP established, but peer not yet identified itself)
 * CONNECTED_CLIENT (TCP established, client has identified itself, no mapping)
+* CONNECTED_CLIENT_WAIT_MAP(TCP established, client has identified, waiting for mapping)
 * CONNECTED_CLIENT_MAPPED (TCP established, client has identified itself, mapping exists)
 * CONNECTED_CLIENT_MAPPED_CARD (TCP established, client identified, mapping exists, card opened)
 * CONNECTED_SERVER (TCP established, server has identified itself)
diff --git a/src/bankd.h b/src/bankd.h
index 21d0ccb..bd7130a 100644
--- a/src/bankd.h
+++ b/src/bankd.h
@@ -46,6 +46,15 @@
 		return false;
 }
 
+static inline ClientSlot_t client_slot2asn(const struct client_slot *in)
+{
+	ClientSlot_t out = {
+		.clientId = in->client_id,
+		.slotNr = in->slot_nr,
+	};
+	return out;
+}
+
 /* slot mappings are created / removed by the server */
 struct bankd_slot_mapping {
 	/* global lits of bankd slot mappings */
@@ -100,6 +109,9 @@
 	/* worker thread state */
 	enum bankd_worker_state state;
 
+	uint8_t atr[32];
+	unsigned int atr_len;
+
 	/* slot number we are representing */
 	struct bank_slot slot;
 
@@ -116,12 +128,15 @@
 
 	struct {
 		const char *name;
+		struct osmo_fsm_inst *fi;
 		union {
 			struct {
 				/* PC/SC context / application handle */
 				SCARDCONTEXT hContext;
 				/* PC/SC card handle */
 				SCARDHANDLE hCard;
+				/* PC/SC slot status (SCARD_ABSENT, ...) bit-mask */
+				DWORD dwState;
 			} pcsc;
 		};
 	} reader;
@@ -152,3 +167,14 @@
 
 int bankd_pcsc_read_slotnames(struct bankd *bankd, const char *csv_file);
 const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot *slot);
+
+enum sc_fsm_events {
+	SC_E_CONNECT_CMD,
+	SC_E_DISCONNECT_CMD,
+	SC_E_TPDU_CMD,
+};
+
+struct osmo_fsm_inst *sc_fsm_alloc(struct bankd_worker *worker);
+
+int worker_handle_tpduModemToCard(struct bankd_worker *worker, const RsproPDU_t *pdu);
+int worker_send_rspro(struct bankd_worker *worker, RsproPDU_t *pdu);
diff --git a/src/bankd_main.c b/src/bankd_main.c
index 3545d68..5f1844a 100644
--- a/src/bankd_main.c
+++ b/src/bankd_main.c
@@ -16,6 +16,7 @@
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/logging.h>
 #include <osmocom/core/application.h>
+#include <osmocom/core/fsm.h>
 
 #include <osmocom/gsm/ipa.h>
 #include <osmocom/gsm/protocol/ipaccess.h>
@@ -206,10 +207,7 @@
 	rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
 	PCSC_ERROR(worker, rc, "SCardEstablishContext")
 
-	DWORD dwActiveProtocol;
-	rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name, SCARD_SHARE_SHARED,
-			  SCARD_PROTOCOL_T0, &worker->reader.pcsc.hCard, &dwActiveProtocol);
-	PCSC_ERROR(worker, rc, "SCardConnect")
+	worker->reader.fi = sc_fsm_alloc(worker);
 
 	worker_set_state(worker, BW_ST_CONN_CLIENT_MAPPED_CARD);
 
@@ -246,7 +244,7 @@
 	return len;
 }
 
-static int worker_send_rspro(struct bankd_worker *worker, RsproPDU_t *pdu)
+int worker_send_rspro(struct bankd_worker *worker, RsproPDU_t *pdu)
 {
 	struct msgb *msg = rspro_enc_msg(pdu);
 	int rc;
@@ -261,6 +259,7 @@
 	ipa_prepend_header_ext(msg, IPAC_PROTO_EXT_RSPRO);
 	ipa_prepend_header(msg, IPAC_PROTO_OSMO);
 
+	printf("tx: %s\n", msgb_hexdump(msg));
 	/* actually send it through the socket */
 	rc = write(worker->client.fd, msgb_data(msg), msgb_length(msg));
 	if (rc == msgb_length(msg))
@@ -327,7 +326,7 @@
 	return worker_send_rspro(worker, resp);
 }
 
-static int worker_handle_tpduModemToCard(struct bankd_worker *worker, const RsproPDU_t *pdu)
+int worker_handle_tpduModemToCard(struct bankd_worker *worker, const RsproPDU_t *pdu)
 {
 	const struct TpduModemToCard *mdm2sim = &pdu->msg.choice.tpduModemToCard;
 	const SCARD_IO_REQUEST *pioSendPci = SCARD_PCI_T0;
@@ -339,11 +338,6 @@
 
 	LOGW(worker, "tpduModemToCard(%s)\n", osmo_hexdump_nospc(mdm2sim->data.buf, mdm2sim->data.size));
 
-	if (worker->state != BW_ST_CONN_CLIENT_MAPPED_CARD) {
-		LOGW(worker, "Unexpected tpduModemToCaard\n");
-		return -104;
-	}
-
 	/* FIXME: Validate that toBankSlot / fromClientSlot match our expectations */
 
 	rc = SCardTransmit(worker->reader.pcsc.hCard,
@@ -371,7 +365,8 @@
 		rc = worker_handle_connectClientReq(worker, pdu);
 		break;
 	case RsproPDUchoice_PR_tpduModemToCard:
-		rc = worker_handle_tpduModemToCard(worker, pdu);
+		osmo_fsm_inst_dispatch(worker->reader.fi, SC_E_TPDU_CMD, (void *)pdu);
+		rc = 0;
 		break;
 	case RsproPDUchoice_PR_clientSlotStatusInd:
 		/* FIXME */
diff --git a/src/bankd_pcsc.c b/src/bankd_pcsc.c
index 2ab768c..01fe877 100644
--- a/src/bankd_pcsc.c
+++ b/src/bankd_pcsc.c
@@ -2,10 +2,17 @@
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/talloc.h>
 #include <osmocom/core/utils.h>
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/logging.h>
 
 #include <csv.h>
 
 #include "bankd.h"
+#include "rspro_util.h"
+
+/***********************************************************************
+ * RSPRO bank/slot-id <-> PCSC Reader name mapping
+ ***********************************************************************/
 
 struct pcsc_slot_name {
 	struct llist_head list;
@@ -116,3 +123,184 @@
 	}
 	return NULL;
 }
+
+
+/***********************************************************************
+ * SCard related FSM
+ ***********************************************************************/
+
+#define S(x)	(1 << (x))
+
+#define T2_TIMEOUT_SECS		10
+#define T1_TIMEOUT_SECS		10
+
+enum sc_fsm_states {
+	SC_ST_CARD_ABSENT,
+	SC_ST_CARD_PRESENT,
+};
+
+static const struct value_string sc_fsm_event_names[] = {
+	{ SC_E_CONNECT_CMD,	"CONNECT_CMD" },
+	{ SC_E_DISCONNECT_CMD,	"DISCONNECT_CMD" },
+	{ SC_E_TPDU_CMD,	"TPDU_CMD" },
+	{ 0, NULL }
+};
+
+/* an attempt at SCardConnect */
+static void attempt_sc_connect(struct osmo_fsm_inst *fi)
+{
+	struct bankd_worker *worker = fi->priv;
+	LONG rc;
+	DWORD protocol;
+
+	/* another attempt at SCardConnect */
+	rc = SCardConnect(worker->reader.pcsc.hContext, worker->reader.name,
+			  SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T0,
+			  &worker->reader.pcsc.hCard, &protocol);
+	if (rc == SCARD_S_SUCCESS) {
+		osmo_fsm_inst_state_chg(fi, SC_ST_CARD_PRESENT, T2_TIMEOUT_SECS, 2);
+		/* FIXME: inform client */
+	} else {
+		/* schedule the next SCardConnect request */
+		osmo_timer_schedule(&fi->timer, T1_TIMEOUT_SECS, 1);
+	}
+}
+
+/* no card currently present; attempt to re-connect via timer if asked to */
+static void sc_st_card_absent(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct bankd_worker *worker = fi->priv;
+	const struct TpduModemToCard *mdm2sim;
+	const RsproPDU_t *pdu, *pdu_resp;
+
+	switch (event) {
+	case SC_E_CONNECT_CMD:
+		attempt_sc_connect(fi);
+		break;
+	case SC_E_TPDU_CMD:
+		pdu = data;
+		mdm2sim = &pdu->msg.choice.tpduModemToCard;
+		/* reject transceiving the PDU; we're not connected */
+#if 0
+		pdu_resp = rspro_gen_TpduCard2Modem(&mdm2sim->toBankSlot, &mdm2sim->fromClientSlot,
+						    rx_buf, rx_buf_len);
+		worker_send_rspro(worker, pdu_resp);
+#endif
+		break;
+	default:
+		OSMO_ASSERT(0);
+	}
+}
+
+static void sc_st_card_present(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct bankd_worker *worker = fi->priv;
+	const RsproPDU_t *pdu;
+	LONG rc;
+
+	switch (event) {
+	case SC_E_TPDU_CMD:
+		/* transceive an APDU */
+		pdu = data;
+		worker_handle_tpduModemToCard(worker, pdu);
+		break;
+	case SC_E_DISCONNECT_CMD:
+		rc = SCardDisconnect(worker->reader.pcsc.hCard, SCARD_UNPOWER_CARD);
+		/* FIXME: evaluate rc */
+		osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, 0, 0);
+		break;
+	default:
+		OSMO_ASSERT(0);
+	}
+}
+
+static int sc_timer_cb(struct osmo_fsm_inst *fi)
+{
+	struct bankd_worker *worker = fi->priv;
+	char reader_name[32];
+	uint8_t atr[32];
+	DWORD reader_state, protocol;
+	DWORD atr_len = sizeof(atr);
+	DWORD reader_name_len = sizeof(atr);
+	LONG rc;
+
+	switch (fi->T) {
+	case 1:
+		attempt_sc_connect(fi);
+		break;
+	case 2:
+		/* another iteration of SCardStatus */
+		rc = SCardStatus(worker->reader.pcsc.hCard, reader_name, &reader_name_len,
+				 &reader_state, &protocol, atr, &atr_len);
+		if (rc == SCARD_S_SUCCESS) {
+			RsproPDU_t *pdu = NULL;
+			/* Determine any changes in state, and if so, report to client */
+			if (reader_state != worker->reader.pcsc.dwState) {
+				worker->reader.pcsc.dwState = reader_state;
+				/* FIXME: inform client */
+				//pdu = rspro_gen_SetAtrReq(foo, bar, worker->atr, worker->atr_len);
+				//worker_send_rspro(worker, pdu);
+			}
+			if (atr_len != worker->atr_len || memcmp(atr, worker->atr, atr_len)) {
+				ClientSlot_t clslot = client_slot2asn(&worker->client.clslot);
+				OSMO_ASSERT(atr_len < sizeof(worker->atr));
+				memcpy(worker->atr, atr, atr_len);
+				worker->atr_len = atr_len;
+				/* inform client */
+				pdu = rspro_gen_SetAtrReq(&clslot, worker->atr, worker->atr_len);
+				worker_send_rspro(worker, pdu);
+			}
+			/* schedule the next SCardStatus request */
+			osmo_timer_schedule(&fi->timer, T2_TIMEOUT_SECS, 0);
+		} else
+			osmo_fsm_inst_state_chg(fi, SC_ST_CARD_ABSENT, T1_TIMEOUT_SECS, 1);
+		break;
+	default:
+		OSMO_ASSERT(0);
+	}
+	return 0;
+}
+
+static const struct osmo_fsm_state sc_fsm_states[] = {
+	[SC_ST_CARD_ABSENT] = {
+		.in_event_mask = S(SC_E_CONNECT_CMD) | S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD),
+		.out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT),
+		.name = "CARD_ABSENT",
+		.action = sc_st_card_absent,
+	},
+	[SC_ST_CARD_PRESENT] = {
+		.in_event_mask = S(SC_E_DISCONNECT_CMD) | S(SC_E_TPDU_CMD),
+		.out_state_mask = S(SC_ST_CARD_PRESENT) | S(SC_ST_CARD_ABSENT),
+		.name = "CART_PRESENT",
+		.action = sc_st_card_present,
+	},
+};
+
+static struct osmo_fsm sc_fsm = {
+	.name = "SC",
+	.states = sc_fsm_states,
+	.num_states = ARRAY_SIZE(sc_fsm_states),
+	.timer_cb = sc_timer_cb,
+	.event_names = sc_fsm_event_names,
+};
+
+static bool fsm_initialized = false;
+
+struct osmo_fsm_inst *sc_fsm_alloc(struct bankd_worker *worker)
+{
+	struct osmo_fsm_inst *fi;
+	char num[8];
+
+	if (!fsm_initialized) {
+		osmo_fsm_register(&sc_fsm);
+		fsm_initialized = true;
+	}
+
+	snprintf(num, 8, "%d", worker->num);
+
+	fi = osmo_fsm_inst_alloc(&sc_fsm, worker, worker, LOGL_DEBUG, num);
+
+	osmo_fsm_inst_dispatch(fi, SC_E_CONNECT_CMD, NULL);
+
+	return fi;
+}
diff --git a/src/remsim_client.c b/src/remsim_client.c
index 850ded6..73a1798 100644
--- a/src/remsim_client.c
+++ b/src/remsim_client.c
@@ -33,6 +33,7 @@
 
 static int bankd_handle_msg(struct bankd_client *bc, struct msgb *msg)
 {
+	printf("Decoding RSPRO %s\n", msgb_hexdump(msg));
 	RsproPDU_t *pdu = rspro_dec_msg(msg);
 	if (!pdu) {
 		fprintf(stderr, "Error decoding PDU\n");
diff --git a/src/remsim_client_fsm.c b/src/remsim_client_fsm.c
index 2459a14..e116ca3 100644
--- a/src/remsim_client_fsm.c
+++ b/src/remsim_client_fsm.c
@@ -91,7 +91,7 @@
 	struct bankd_client *bc = (struct bankd_client *) fi->priv;
 	RsproPDU_t *pdu;
 
-	/* FIXME: Send ClientConnReq */
+	/* FIXME: make configurable */
 	const ClientSlot_t clslot = { .clientId = 23, .slotNr = 1 };
 	pdu = rspro_gen_ConnectClientReq(&bc->own_comp_id, &clslot);
 	ipa_client_conn_send_rspro(bc->bankd_conn, pdu);
@@ -307,12 +307,14 @@
 	if (!srvc->conn) {
 		fprintf(stderr, "Unable to create socket: %s\n", strerror(errno));
 		/* FIXME */
+		OSMO_ASSERT(0);
 	}
 	/* Attempt to connect TCP socket */
 	rc = ipa_client_conn_open(srvc->conn);
 	if (rc < 0) {
 		fprintf(stderr, "Unable to connect: %s\n", strerror(errno));
 		/* FIXME */
+		OSMO_ASSERT(0);
 	}
 }
 
diff --git a/src/rspro_util.c b/src/rspro_util.c
index 002bd81..67d4ac0 100644
--- a/src/rspro_util.c
+++ b/src/rspro_util.c
@@ -43,6 +43,7 @@
 		return NULL;
 	}
 	msgb_put(msg, rval.encoded);
+	printf("encoded %s\n", msgb_hexdump(msg));
 
 	ASN_STRUCT_FREE(asn_DEF_RsproPDU, pdu);
 
@@ -128,8 +129,8 @@
 	RsproPDU_t *pdu = CALLOC(1, sizeof(*pdu));
 	if (!pdu)
 		return NULL;
-	pdu->version = 2;
-	pdu->tag = 2342;
+	//pdu->version = 2;
+	//pdu->tag = 2342;
 	pdu->msg.present = RsproPDUchoice_PR_connectClientRes;
 	fill_comp_id(&pdu->msg.choice.connectClientRes.identity, a_cid);
 	pdu->msg.choice.connectClientRes.result = res;
@@ -161,15 +162,13 @@
 	return pdu;
 }
 
-RsproPDU_t *rspro_gen_SetAtrReq(uint16_t client_id, uint16_t slot_nr, const uint8_t *atr,
-				unsigned int atr_len)
+RsproPDU_t *rspro_gen_SetAtrReq(const ClientSlot_t *client, const uint8_t *atr, unsigned int atr_len)
 {
 	RsproPDU_t *pdu = CALLOC(1, sizeof(*pdu));
 	if (!pdu)
 		return NULL;
 	pdu->msg.present = RsproPDUchoice_PR_setAtrReq;
-	pdu->msg.choice.setAtrReq.slot.clientId = client_id;
-	pdu->msg.choice.setAtrReq.slot.slotNr = slot_nr;
+	pdu->msg.choice.setAtrReq.slot = *client;
 	OCTET_STRING_fromBuf(&pdu->msg.choice.setAtrReq.atr, (const char *)atr, atr_len);
 
 	return pdu;
diff --git a/src/rspro_util.h b/src/rspro_util.h
index 5411a48..feef77d 100644
--- a/src/rspro_util.h
+++ b/src/rspro_util.h
@@ -26,8 +26,7 @@
 RsproPDU_t *rspro_gen_ConnectClientRes(const struct app_comp_id *a_cid, e_ResultCode res);
 RsproPDU_t *rspro_gen_CreateMappingReq(const ClientSlot_t *client, const BankSlot_t *bank);
 RsproPDU_t *rspro_gen_ConfigClientReq(const ClientSlot_t *client, uint32_t ip, uint16_t port);
-RsproPDU_t *rspro_gen_SetAtrReq(uint16_t client_id, uint16_t slot_nr, const uint8_t *atr,
-				unsigned int atr_len);
+RsproPDU_t *rspro_gen_SetAtrReq(const ClientSlot_t *client, const uint8_t *atr, unsigned int atr_len);
 RsproPDU_t *rspro_gen_TpduModem2Card(const ClientSlot_t *client, const BankSlot_t *bank,
 				     const uint8_t *tpdu, unsigned int tpdu_len);
 RsproPDU_t *rspro_gen_TpduCard2Modem(const BankSlot_t *bank, const ClientSlot_t *client,