'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);
+