WIP: Actual 'main' integration of CCID using msgb queues for USB transfers

Change-Id: I9b7dbbc72dd566be19b217a57767f8404e1b02c4
diff --git a/sysmoOCTSIM/main.c b/sysmoOCTSIM/main.c
index 3c189eb..00ac669 100644
--- a/sysmoOCTSIM/main.c
+++ b/sysmoOCTSIM/main.c
@@ -21,6 +21,9 @@
 #include <math.h>
 #include <parts.h>
 #include <errno.h>
+
+#include <osmocom/core/utils.h>
+
 #include <hal_cache.h>
 #include <hri_port_e54.h>
 
@@ -76,6 +79,8 @@
  */
 static const uint8_t SIM_peripheral_GCLK_ID[] = {SERCOM0_GCLK_ID_CORE, SERCOM1_GCLK_ID_CORE, SERCOM2_GCLK_ID_CORE, SERCOM3_GCLK_ID_CORE, SERCOM4_GCLK_ID_CORE, SERCOM5_GCLK_ID_CORE, SERCOM6_GCLK_ID_CORE, SERCOM7_GCLK_ID_CORE};
 
+static void ccid_app_init(void);
+
 static void board_init()
 {
 	int i;
@@ -102,8 +107,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;
+}
+
+static 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, (FUNC_PTR)&ccid_out_read_compl);
+	/* IN endpoint write complete callback (irq context) */
+	ccid_df_register_callback(CCID_DF_CB_WRITE_IN, (FUNC_PTR)&ccid_in_write_compl);
+	/* IRQ endpoint write complete callback (irq context) */
+	ccid_df_register_callback(CCID_DF_CB_WRITE_IRQ, (FUNC_PTR)&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 +900,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 +911,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 +989,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
diff --git a/sysmoOCTSIM/usb_start.c b/sysmoOCTSIM/usb_start.c
index 83fac81..c6d9fe9 100644
--- a/sysmoOCTSIM/usb_start.c
+++ b/sysmoOCTSIM/usb_start.c
@@ -89,4 +89,5 @@
 {
 
 	cdc_device_acm_init();
+	ccid_df_init();
 }
diff --git a/sysmoOCTSIM/usb_start.h b/sysmoOCTSIM/usb_start.h
index daf30cc..a99d1ad 100644
--- a/sysmoOCTSIM/usb_start.h
+++ b/sysmoOCTSIM/usb_start.h
@@ -14,6 +14,7 @@
 
 #include "cdcdf_acm.h"
 #include "cdcdf_acm_desc.h"
+#include "ccid_df.h"
 
 void usb_start(void);
 void cdc_device_acm_init(void);