bankd_pcsc: Add CSV based mapping of bank-id/slot-nr to PC/SC reader name

In the PC/SC world, each slot is associated with a string name. In the
bankd for PC/SC readers, we need to establish a mapping which
bank_id/slot_nr maps to which given string name.  We use a minimalistic
CSV file for defining those mappings.  The file is read only once at
bankd startup time.

Change-Id: Ifd2caab670625e2e3fbc57b966dce2f43b690417
diff --git a/src/Makefile.am b/src/Makefile.am
index 2324831..1514a16 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,9 +19,9 @@
 pcsc_test_LDADD = $(OSMOCORE_LIBS) \
 		  $(ASN1C_LIBS) $(PCSC_LIBS) libosmo-rspro.la
 
-remsim_bankd_SOURCES = bankd_slotmap.c bankd_main.c
-remsim_bankd_LDADD = $(OSMOCORE_LIBS) \
-		     $(ASN1C_LIBS) $(PCSC_LIBS) libosmo-rspro.la
+remsim_bankd_SOURCES = bankd_slotmap.c bankd_main.c bankd_pcsc.c
+remsim_bankd_LDADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) \
+		     $(ASN1C_LIBS) $(PCSC_LIBS) libosmo-rspro.la -lcsv
 
 remsim_client_SOURCES = remsim_client.c remsim_client_fsm.c
 remsim_client_LDADD = $(OSMOCORE_LIBS) $(OSMOGSM_LIBS) $(OSMOABIS_LIBS) \
diff --git a/src/bankd.h b/src/bankd.h
index 61bf34a..2759478 100644
--- a/src/bankd.h
+++ b/src/bankd.h
@@ -138,4 +138,9 @@
 	/* list of bankd_workers. accessed/modified by multiple threads; protected by mutex */
 	struct llist_head workers;
 	pthread_mutex_t workers_mutex;
+
+	struct llist_head pcsc_slot_names;
 };
+
+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);
diff --git a/src/bankd_main.c b/src/bankd_main.c
index 96eee0e..7cb46d2 100644
--- a/src/bankd_main.c
+++ b/src/bankd_main.c
@@ -38,6 +38,12 @@
 	pthread_rwlock_init(&bankd->slot_mappings_rwlock, NULL);
 	INIT_LLIST_HEAD(&bankd->workers);
 	pthread_mutex_init(&bankd->workers_mutex, NULL);
+
+	/* Np lock or mutex required for the pcsc_slot_names list, as this is only
+	 * read once during bankd initialization, when the worker threads haven't
+	 * started yet */
+	INIT_LLIST_HEAD(&bankd->pcsc_slot_names);
+	OSMO_ASSERT(bankd_pcsc_read_slotnames(bankd, "bankd_pcsc_slots.csv") == 0);
 }
 
 /* create + start a new bankd_worker thread */
@@ -154,6 +160,12 @@
 {
 	long rc;
 
+	/* resolve PC/SC reader name from slot_id -> name map */
+	worker->reader.name = bankd_pcsc_get_slot_name(worker->bankd, &worker->slot);
+	OSMO_ASSERT(worker->reader.name);
+
+	LOGW(worker, "Attempting to open card/slot '%s'\n", worker->reader.name);
+
 	/* The PC/SC context must be created inside the thread where we'll later use it */
 	rc = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &worker->reader.pcsc.hContext);
 	PCSC_ERROR(worker, rc, "SCardEstablishContext")
diff --git a/src/bankd_pcsc.c b/src/bankd_pcsc.c
new file mode 100644
index 0000000..2ab768c
--- /dev/null
+++ b/src/bankd_pcsc.c
@@ -0,0 +1,118 @@
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/utils.h>
+
+#include <csv.h>
+
+#include "bankd.h"
+
+struct pcsc_slot_name {
+	struct llist_head list;
+	/* RSPRO bank slot number */
+	struct bank_slot slot;
+	/* String name of the reader in PC/SC world */
+	const char *name;
+};
+
+enum parser_state_name {
+	ST_NONE,
+	ST_BANK_NR,
+	ST_SLOT_NR,
+	ST_PCSC_NAME,
+};
+struct parser_state {
+	struct bankd *bankd;
+	enum parser_state_name state;
+	struct pcsc_slot_name *cur;
+};
+
+
+static void parser_state_init(struct parser_state *ps)
+{
+	ps->state = ST_BANK_NR;
+	ps->cur = NULL;
+}
+
+static void cb1(void *s, size_t len, void *data)
+{
+	char *field = (char *) s;
+	struct parser_state *ps = data;
+
+	switch (ps->state) {
+	case ST_BANK_NR:
+		OSMO_ASSERT(!ps->cur);
+		ps->cur = talloc_zero(ps->bankd, struct pcsc_slot_name);
+		OSMO_ASSERT(ps->cur);
+		ps->cur->slot.bank_id = atoi(field);
+		ps->state = ST_SLOT_NR;
+		break;
+	case ST_SLOT_NR:
+		OSMO_ASSERT(ps->cur);
+		ps->cur->slot.slot_nr = atoi(field);
+		ps->state = ST_PCSC_NAME;
+		break;
+	case ST_PCSC_NAME:
+		OSMO_ASSERT(ps->cur);
+		ps->cur->name = talloc_strdup(ps->cur, field);
+		break;
+	default:
+		OSMO_ASSERT(0);
+	}
+}
+
+static void cb2(int c, void *data)
+{
+	struct parser_state *ps = data;
+	struct pcsc_slot_name *sn = ps->cur;
+
+	printf("PC/SC slot name: %u/%u -> '%s'\n", sn->slot.bank_id, sn->slot.slot_nr, sn->name);
+	llist_add_tail(&sn->list, &ps->bankd->pcsc_slot_names);
+
+	ps->state = ST_BANK_NR;
+	ps->cur = NULL;
+}
+
+int bankd_pcsc_read_slotnames(struct bankd *bankd, const char *csv_file)
+{
+	FILE *fp;
+	struct csv_parser p;
+	char buf[1024];
+	size_t bytes_read;
+	struct parser_state ps;
+
+	if (csv_init(&p, CSV_APPEND_NULL) != 0)
+		return -1;
+
+	fp = fopen(csv_file, "rb");
+	if (!fp)
+		return -1;
+
+	parser_state_init(&ps);
+	ps.bankd = bankd;
+
+	while ((bytes_read = fread(buf, 1, sizeof(buf), fp)) > 0) {
+		if (csv_parse(&p, buf, bytes_read, cb1, cb2, &ps) != bytes_read) {
+			fprintf(stderr, "Error parsing CSV: %s\n", csv_strerror(csv_error(&p)));
+			fclose(fp);
+			return -1;
+		}
+	}
+
+	csv_fini(&p, cb1, cb2, &ps);
+	fclose(fp);
+	csv_free(&p);
+
+	return 0;
+}
+
+const char *bankd_pcsc_get_slot_name(struct bankd *bankd, const struct bank_slot *slot)
+{
+	struct pcsc_slot_name *cur;
+
+	llist_for_each_entry(cur, &bankd->pcsc_slot_names, list) {
+		if (bank_slot_equals(&cur->slot, slot))
+			return cur->name;
+	}
+	return NULL;
+}
diff --git a/src/bankd_pcsc_slots.csv b/src/bankd_pcsc_slots.csv
new file mode 100644
index 0000000..38a22c9
--- /dev/null
+++ b/src/bankd_pcsc_slots.csv
@@ -0,0 +1,6 @@
+"1","0","ACS ACR33 ICC Reader 00 00"
+"1","1","ACS ACR33 ICC Reader 00 01"
+"1","2","ACS ACR33 ICC Reader 00 02"
+"1","3","ACS ACR33 ICC Reader 00 03"
+"1","4","ACS ACR33 ICC Reader 00 04"
+"1","23","Alcor Micro AU9560 00 00"