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