'cuart' Card-UART abstraction + driver for simple serial reader

Change-Id: Ic7e324d99f78b3bfb98fc667d9a1b7fa363f092d
diff --git a/ccid_common/cuart.c b/ccid_common/cuart.c
new file mode 100644
index 0000000..2c0428e
--- /dev/null
+++ b/ccid_common/cuart.c
@@ -0,0 +1,133 @@
+#include <errno.h>
+#include <string.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/utils.h>
+
+#include "cuart.h"
+
+static LLIST_HEAD(g_cuart_drivers);
+
+const struct value_string card_uart_event_vals[] = {
+	OSMO_VALUE_STRING(CUART_E_RX_SINGLE),
+	OSMO_VALUE_STRING(CUART_E_RX_COMPLETE),
+	OSMO_VALUE_STRING(CUART_E_RX_TIMEOUT),
+	OSMO_VALUE_STRING(CUART_E_TX_COMPLETE),
+	{ 0, NULL }
+};
+
+static struct card_uart_driver *cuart_drv_by_name(const char *driver_name)
+{
+	struct card_uart_driver *drv;
+	llist_for_each_entry(drv, &g_cuart_drivers, list) {
+		if (!strcmp(drv->name, driver_name))
+			return drv;
+	}
+	return NULL;
+}
+
+int card_uart_open(struct card_uart *cuart, const char *driver_name, const char *device_name)
+{
+	struct card_uart_driver *drv = cuart_drv_by_name(driver_name);
+	int rc;
+
+	if (!drv)
+		return -ENODEV;
+
+	cuart->rx_enabled = true;
+	cuart->rx_threshold = 1;
+
+	rc = drv->ops->open(cuart, device_name);
+	if (rc < 0)
+		return rc;
+
+	cuart->driver = drv;
+	return 0;
+}
+
+int card_uart_close(struct card_uart *cuart)
+{
+	OSMO_ASSERT(cuart);
+	OSMO_ASSERT(cuart->driver);
+	OSMO_ASSERT(cuart->driver->ops);
+	OSMO_ASSERT(cuart->driver->ops->close);
+	return cuart->driver->ops->close(cuart);
+}
+
+int card_uart_ctrl(struct card_uart *cuart, enum card_uart_ctl ctl, bool enable)
+{
+	int rc;
+	OSMO_ASSERT(cuart);
+	OSMO_ASSERT(cuart->driver);
+	OSMO_ASSERT(cuart->driver->ops);
+	OSMO_ASSERT(cuart->driver->ops->ctrl);
+	rc = cuart->driver->ops->ctrl(cuart, ctl, enable);
+	if (rc < 0)
+		return rc;
+
+	switch (ctl) {
+	case CUART_CTL_RX:
+		cuart->rx_enabled = enable;
+		break;
+	default:
+		break;
+	}
+
+	return rc;
+}
+
+int card_uart_tx(struct card_uart *cuart, const uint8_t *data, size_t len, bool rx_after_complete)
+{
+	OSMO_ASSERT(cuart);
+	OSMO_ASSERT(cuart->driver);
+	OSMO_ASSERT(cuart->driver->ops);
+	OSMO_ASSERT(cuart->driver->ops->async_tx);
+
+	OSMO_ASSERT(!cuart->tx_busy);
+	cuart->tx_busy = true;
+	/* disable receiver to avoid receiving what we transmit */
+	card_uart_ctrl(cuart, CUART_CTL_RX, false);
+
+	return cuart->driver->ops->async_tx(cuart, data, len, rx_after_complete);
+}
+
+int card_uart_rx(struct card_uart *cuart, uint8_t *data, size_t len)
+{
+	OSMO_ASSERT(cuart);
+	OSMO_ASSERT(cuart->driver);
+	OSMO_ASSERT(cuart->driver->ops);
+	OSMO_ASSERT(cuart->driver->ops->async_rx);
+	return cuart->driver->ops->async_rx(cuart, data, len);
+}
+
+void card_uart_set_rx_threshold(struct card_uart *cuart, size_t rx_threshold)
+{
+	cuart->rx_threshold = rx_threshold;
+}
+
+void card_uart_notification(struct card_uart *cuart, enum card_uart_event evt, void *data)
+{
+	OSMO_ASSERT(cuart);
+	OSMO_ASSERT(cuart->handle_event);
+
+	switch (evt) {
+	case CUART_E_TX_COMPLETE:
+		cuart->tx_busy = false;
+		/* re-enable receiver if we're done with transmit */
+		sleep(1);
+		card_uart_ctrl(cuart, CUART_CTL_RX, true);
+		break;
+	default:
+		break;
+	}
+
+	cuart->handle_event(cuart, evt, data);
+}
+
+int card_uart_driver_register(struct card_uart_driver *drv)
+{
+	OSMO_ASSERT(!cuart_drv_by_name(drv->name));
+	OSMO_ASSERT(drv->name);
+	OSMO_ASSERT(drv->ops);
+	llist_add_tail(&drv->list, &g_cuart_drivers);
+	return 0;
+}
diff --git a/ccid_common/cuart.h b/ccid_common/cuart.h
new file mode 100644
index 0000000..7e217db
--- /dev/null
+++ b/ccid_common/cuart.h
@@ -0,0 +1,107 @@
+#pragma once
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/core/linuxlist.h>
+
+#include <osmocom/core/select.h>
+#include "utils_ringbuffer.h"
+
+enum card_uart_event {
+	/* a single byte was received, it's present at the (uint8_t *) data location */
+	CUART_E_RX_SINGLE,
+	/* an entire block of data was received */
+	CUART_E_RX_COMPLETE,
+	CUART_E_RX_TIMEOUT,
+	/* an entire block of data was written, as instructed in prior card_uart_tx() call */
+	CUART_E_TX_COMPLETE,
+};
+
+extern const struct value_string card_uart_event_vals[];
+
+enum card_uart_ctl {
+	CUART_CTL_RX,
+	CUART_CTL_POWER,
+	CUART_CTL_CLOCK,
+	CUART_CTL_RST,
+};
+
+struct card_uart;
+
+struct card_uart_ops {
+	int (*open)(struct card_uart *cuart, const char *device_name);
+	int (*close)(struct card_uart *cuart);
+	int (*async_tx)(struct card_uart *cuart, const uint8_t *data, size_t len, bool rx_after_complete);
+	int (*async_rx)(struct card_uart *cuart, uint8_t *data, size_t len);
+
+	int (*ctrl)(struct card_uart *cuart, enum card_uart_ctl ctl, bool enable);
+};
+
+/* Card UART driver */
+struct card_uart_driver {
+	/* global list of card UART drivers */
+	struct llist_head list;
+	/* human-readable name of driver */
+	const char *name;
+	/* operations implementing the driver */
+	const struct card_uart_ops *ops;
+};
+
+struct card_uart {
+	/* member in global list of UARTs */
+	struct llist_head list;
+	/* driver serving this UART */
+	const struct card_uart_driver *driver;
+	/* event-handler function */
+	void (*handle_event)(struct card_uart *cuart, enum card_uart_event evt, void *data);
+	/* opaque pointer for user */
+	void *priv;
+
+	/* is the transmitter currently busy (true) or not (false)? */
+	bool tx_busy;
+	/* is the receiver currently enabled or not? */
+	bool rx_enabled;
+
+	/*! after how many bytes should we notify the user? If this is '1', we will
+	 *  issue CUART_E_RX_SINGLE; if it is > 1, we will issue CUART_E_RX_COMPLETE */
+	uint32_t rx_threshold;
+
+	/* driver-specific private data */
+	union {
+		struct {
+			/* ringbuffer on receive side */
+			uint8_t rx_buf[256];
+			struct ringbuffer rx_ringbuf;
+
+			/* pointer to (user-allocated) transmit buffer and length */
+			const uint8_t *tx_buf;
+			size_t tx_buf_len;
+			/* index: offset of next to be transmitted byte in tx_buf */
+			size_t tx_index;
+
+			struct osmo_fd ofd;
+			unsigned int baudrate;
+		} tty;
+	} u;
+};
+
+/*! Open the Card UART */
+int card_uart_open(struct card_uart *cuart, const char *driver_name, const char *device_name);
+
+/*! Close the Card UART */
+int card_uart_close(struct card_uart *cuart);
+
+/*! Schedule (asynchronous) transmit data via UART; optionally enable Rx after completion */
+int card_uart_tx(struct card_uart *cuart, const uint8_t *data, size_t len, bool rx_after_complete);
+
+/*! Schedule (asynchronous) receive data via UART (after CUART_E_RX_COMPLETE) */
+int card_uart_rx(struct card_uart *cuart, uint8_t *data, size_t len);
+
+int card_uart_ctrl(struct card_uart *cuart, enum card_uart_ctl ctl, bool enable);
+
+/*! Set the Rx notification threshold in number of bytes received */
+void card_uart_set_rx_threshold(struct card_uart *cuart, size_t rx_threshold);
+
+void card_uart_notification(struct card_uart *cuart, enum card_uart_event evt, void *data);
+
+int card_uart_driver_register(struct card_uart_driver *drv);
+