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