WIP

Change-Id: I9b7dbbc72dd566be19b217a57767f8404e1b02c4
diff --git a/sysmoOCTSIM/main.c b/sysmoOCTSIM/main.c
index 3c189eb..c94a1ed 100644
--- a/sysmoOCTSIM/main.c
+++ b/sysmoOCTSIM/main.c
@@ -102,8 +102,192 @@
 		usart_async_register_callback(SIM_peripheral_descriptors[i], USART_ASYNC_TXC_CB, SIM_tx_cb); // to count the number of bytes transmitted since we are using it asynchronously
 		usart_async_enable(SIM_peripheral_descriptors[i]);
 	}
+
+	ccid_app_init();
 }
 
+/***********************************************************************
+ * CCID Driver integration
+ ***********************************************************************/
+
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/msgb.h>
+#include "linuxlist_atomic.h"
+#include "ccid_df.h"
+
+struct usb_ep_q {
+	const char *name;
+	/* msgb queue of pending to-be-transmitted (IN/IRQ) or completed received (OUT)
+	 * USB transfers */
+	struct llist_head list;
+	/* currently ongoing/processed msgb (USB transmit or receive */
+	struct msgb *in_progress;
+};
+
+struct ccid_state {
+	/* msgb queue of free msgs */
+	struct llist_head free_q;
+
+	/* msgb queue of pending to-be-transmitted (IN EP) */
+	struct usb_ep_q in_ep;
+	/* msgb queue of pending to-be-transmitted (IRQ EP) */
+	struct usb_ep_q irq_ep;
+	/* msgb queue of completed received (OUT EP) */
+	struct usb_ep_q out_ep;
+};
+static struct ccid_state g_ccid_s;
+
+static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
+static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
+static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred);
+
+static void usb_ep_q_init(struct usb_ep_q *ep_q, const char *name)
+{
+	ep_q->name = name;
+	INIT_LLIST_HEAD(&ep_q->list);
+	ep_q->in_progress = NULL;
+}
+
+void ccid_app_init(void)
+{
+	/* initialize data structures */
+	INIT_LLIST_HEAD(&g_ccid_s.free_q);
+	usb_ep_q_init(&g_ccid_s.in_ep, "IN");
+	usb_ep_q_init(&g_ccid_s.irq_ep, "IRQ");
+	usb_ep_q_init(&g_ccid_s.out_ep, "OUT");
+
+	/* OUT endpoint read complete callback (irq context) */
+	ccid_df_register_callback(CCID_DF_CB_READ_OUT, &ccid_out_read_compl);
+	/* IN endpoint write complete callback (irq context) */
+	ccid_df_register_callback(CCID_DF_CB_WRITE_IN, &ccid_in_write_compl);
+	/* IRQ endpoint write complete callback (irq context) */
+	ccid_df_register_callback(CCID_DF_CB_WRITE_IRQ, &ccid_irq_write_compl);
+}
+
+/* irqsafe version of msgb_enqueue */
+struct msgb *msgb_dequeue_irqsafe(struct llist_head *q)
+{
+	struct msgb *msg;
+	CRITICAL_SECTION_ENTER()
+	msg = msgb_dequeue(q);
+	CRITICAL_SECTION_LEAVE()
+	return msg;
+}
+
+/* submit the next pending (if any) message for the IN EP */
+static int submit_next_in(void)
+{
+	struct usb_ep_q *ep_q = &g_ccid_s.in_ep;
+	struct msgb *msg;
+	int rc;
+
+	OSMO_ASSERT(!ep_q->in_progress);
+	msg = msgb_dequeue_irqsafe(&ep_q->list);
+	if (!msg)
+		return 0;
+
+	ep_q->in_progress = msg;
+	rc = ccid_df_write_in(msgb_data(msg), msgb_length(msg));
+	if (rc != ERR_NONE) {
+		printf("EP %s failed: %d\r\n", ep_q->name, rc);
+		return -1;
+	}
+	return 1;
+
+}
+
+/* submit the next pending (if any) message for the IRQ EP */
+static int submit_next_irq(void)
+{
+	struct usb_ep_q *ep_q = &g_ccid_s.irq_ep;
+	struct msgb *msg;
+	int rc;
+
+	OSMO_ASSERT(!ep_q->in_progress);
+	msg = msgb_dequeue_irqsafe(&ep_q->list);
+	if (!msg)
+		return 0;
+
+	ep_q->in_progress = msg;
+	rc = ccid_df_write_irq(msgb_data(msg), msgb_length(msg));
+	/* may return HALTED/ERROR/DISABLED/BUSY/ERR_PARAM/ERR_FUNC/ERR_DENIED */
+	if (rc != ERR_NONE) {
+		printf("EP %s failed: %d\r\n", ep_q->name, rc);
+		return -1;
+	}
+	return 1;
+}
+
+static int submit_next_out(void)
+{
+	struct usb_ep_q *ep_q = &g_ccid_s.out_ep;
+	struct msgb *msg;
+	int rc;
+
+	OSMO_ASSERT(!ep_q->in_progress);
+	msg = msgb_dequeue_irqsafe(&g_ccid_s.free_q);
+	if (!msg)
+		return -1;
+	ep_q->in_progress = msg;
+
+	rc = ccid_df_read_out(msgb_data(msg), msgb_tailroom(msg));
+	if (rc != ERR_NONE) {
+		/* re-add to the list of free msgb's */
+		llist_add_tail_at(&g_ccid_s.free_q, &msg->list);
+		return 0;
+	}
+	return 1;
+}
+
+/* OUT endpoint read complete callback (irq context) */
+static void ccid_out_read_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
+{
+	struct msgb *msg = g_ccid_s.out_ep.in_progress;
+
+	/* add just-received msg to tail of endpoint queue */
+	OSMO_ASSERT(msg);
+	/* update msgb with the amount of data received */
+	msgb_put(msg, transferred);
+	/* append to list of pending-to-be-handed messages */
+	llist_add_tail_at(&msg->list, &g_ccid_s.out_ep.list);
+
+	/* submit another [free] msgb to receive the next transfer */
+	submit_next_out();
+}
+
+/* IN endpoint write complete callback (irq context) */
+static void ccid_in_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
+{
+	struct msgb *msg = g_ccid_s.in_ep.in_progress;
+
+	OSMO_ASSERT(msg);
+	/* return the message back to the queue of free message buffers */
+	llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
+	g_ccid_s.in_ep.in_progress = NULL;
+
+	/* submit the next pending to-be-transmitted msgb (if any) */
+	submit_next_in();
+}
+
+/* IRQ endpoint write complete callback (irq context) */
+static void ccid_irq_write_compl(const uint8_t ep, enum usb_xfer_code code, uint32_t transferred)
+{
+	struct msgb *msg = g_ccid_s.irq_ep.in_progress;
+
+	OSMO_ASSERT(msg);
+	/* return the message back to the queue of free message buffers */
+	llist_add_tail_at(&msg->list, &g_ccid_s.free_q);
+	g_ccid_s.irq_ep.in_progress = NULL;
+
+	/* submit the next pending to-be-transmitted msgb (if any) */
+	submit_next_irq();
+}
+
+
+/***********************************************************************
+ * Command Line interface
+ ***********************************************************************/
+
 static int validate_slotnr(int argc, char **argv, int idx)
 {
 	int slotnr;
@@ -711,6 +895,7 @@
 #include "talloc.h"
 #include <osmocom/core/msgb.h>
 void *g_tall_ctx;
+void *g_msgb_ctx;
 
 DEFUN(_talloc_report, cmd_talloc_report, "talloc-report", "Generate a talloc report")
 {
@@ -721,7 +906,6 @@
 {
 	for (int i = 0; i < 10; i++)
 		talloc_named_const(g_tall_ctx, 10, "sibling");
-	msgb_alloc_c(g_tall_ctx, 1024, "foo");
 }
 
 DEFUN(v_talloc_free, cmd_talloc_free, "talloc-free", "Release all memory")
@@ -800,6 +984,8 @@
 	talloc_enable_null_tracking();
 	g_tall_ctx = talloc_named_const(NULL, 0, "global");
 	printf("g_tall_ctx=%p\r\n", g_tall_ctx);
+	g_msgb_ctx = talloc_pool(g_tall_ctx, 20480);
+	talloc_set_memlimit(g_msgb_ctx, 20480);
 
 	command_print_prompt();
 	while (true) { // main loop