laforge has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-ccid-firmware/+/15691


Change subject: 'cuart' Card-UART abstraction + driver for simple serial reader
......................................................................

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

Change-Id: Ic7e324d99f78b3bfb98fc667d9a1b7fa363f092d
---
M ccid/Makefile
A ccid/cuart.c
A ccid/cuart.h
A ccid/cuart_driver_tty.c
A ccid/cuart_test.c
A ccid/utils_ringbuffer.c
A ccid/utils_ringbuffer.h
7 files changed, 833 insertions(+), 1 deletion(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ccid-firmware 
refs/changes/91/15691/1

diff --git a/ccid/Makefile b/ccid/Makefile
index 5f784ba..a0d6952 100644
--- a/ccid/Makefile
+++ b/ccid/Makefile
@@ -1,13 +1,18 @@
 CFLAGS=-Wall -g

+all: ccid_functionfs hub_functionfs cuart_test
+
 ccid_functionfs: ccid_main_functionfs.o logging.o ccid_proto.o ccid_device.o 
ccid_slot_sim.o
        $(CC) $(CFLAGS) -o $@ $^ -lasan -losmocore -ltalloc -laio

 hub_functionfs: hub_main_functionfs.o
        $(CC) $(CFLAGS) -o $@ $^ -lasan -losmocore -ltalloc -laio

+cuart_test: cuart_test.o cuart.o cuart_driver_tty.o utils_ringbuffer.o
+       $(CC) $(CFLAGS) -o $@ $^ -lasan -losmocore -ltalloc
+
 %.o: %.c
        $(CC) $(CFLAGS) -o $@ -c $^

 clean:
-       rm ccid_functionfs *.o
+       rm ccid_functionfs hub_functionfs cuart_test *.o
diff --git a/ccid/cuart.c b/ccid/cuart.c
new file mode 100644
index 0000000..2c0428e
--- /dev/null
+++ b/ccid/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/cuart.h b/ccid/cuart.h
new file mode 100644
index 0000000..7e217db
--- /dev/null
+++ b/ccid/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);
+
diff --git a/ccid/cuart_driver_tty.c b/ccid/cuart_driver_tty.c
new file mode 100644
index 0000000..6bd2b52
--- /dev/null
+++ b/ccid/cuart_driver_tty.c
@@ -0,0 +1,287 @@
+/* Card (ICC) UART driver for simple serial readers attached to tty.
+ * This allows you to use the CCID core in Linux userspace against a serial 
reader */
+
+#include <unistd.h>
+#include <termios.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+
+#include <osmocom/core/select.h>
+#include <osmocom/core/serial.h>
+#include <osmocom/core/utils.h>
+
+#include "cuart.h"
+#include "utils_ringbuffer.h"
+
+/***********************************************************************
+ * low-level helper routines
+ ***********************************************************************/
+
+static int _init_uart(int fd)
+{
+       struct termios tio;
+       int rc;
+
+       rc = tcgetattr(fd, &tio);
+       if (rc < 0) {
+               perror("tcgetattr()");
+               return -EIO;
+       }
+
+       tio.c_iflag = 0;
+       tio.c_oflag = 0;
+       tio.c_lflag = 0;
+       tio.c_cflag = CREAD | CLOCAL | CSTOPB | PARENB | CS8 | B9600;
+
+       rc = tcsetattr(fd, TCSANOW, &tio);
+       if (rc < 0) {
+               perror("tcsetattr()");
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static void _set_dtr(int fd, bool dtr)
+{
+       int status, rc;
+
+       rc = ioctl(fd, TIOCMGET, &status);
+       OSMO_ASSERT(rc == 0);
+       if (dtr) /* set DTR */
+               status |= TIOCM_DTR;
+       else
+               status &= ~TIOCM_DTR;
+       rc = ioctl(fd, TIOCMSET, &status);
+       OSMO_ASSERT(rc == 0);
+}
+
+static void _set_rts(int fd, bool rts)
+{
+       int status, rc;
+
+       rc = ioctl(fd, TIOCMGET, &status);
+       OSMO_ASSERT(rc == 0);
+       if (rts) /* set RTS */
+               status |= TIOCM_RTS;
+       else
+               status &= ~TIOCM_RTS;
+       rc = ioctl(fd, TIOCMSET, &status);
+       OSMO_ASSERT(rc == 0);
+}
+
+static int read_timeout(int fd, uint8_t *out, size_t len, unsigned long 
timeout_ms)
+{
+       struct timeval tv = { .tv_sec = timeout_ms / 1000, .tv_usec = 
(timeout_ms % 1000) * 1000 };
+       fd_set rd_set;
+       int rc;
+
+       FD_ZERO(&rd_set);
+       FD_SET(fd, &rd_set);
+       rc = select(fd+1, &rd_set, NULL, NULL, &tv);
+       if (rc == 0)
+               return -ETIMEDOUT;
+       else if (rc < 0)
+               return rc;
+
+       return read(fd, out, len);
+}
+
+static int _flush(int fd)
+{
+#if 0
+       uint8_t buf[1];
+       int rc;
+
+       while (1) {
+               rc = read_timeout(fd, buf, sizeof(buf), 10);
+               if (rc == -ETIMEDOUT)
+                       return 0;
+               else if (rc < 0)
+                       return rc;
+       }
+#else
+       return tcflush(fd, TCIFLUSH);
+#endif
+}
+
+/***********************************************************************
+ * Interface with card_uart (cuart) core
+ ***********************************************************************/
+
+/* forward-declaration */
+static struct card_uart_driver tty_uart_driver;
+static int tty_uart_close(struct card_uart *cuart);
+
+static int tty_uart_fd_cb(struct osmo_fd *ofd, unsigned int what)
+{
+       struct card_uart *cuart = (struct card_uart *) ofd->data;
+       uint8_t buf[256];
+       int rc;
+
+       if (what & OSMO_FD_READ) {
+               int i;
+               /* read any pending bytes and feed them into ring buffer */
+               rc = read(ofd->fd, buf, sizeof(buf));
+               OSMO_ASSERT(rc > 0);
+               for (i = 0; i < rc; i++) {
+                       /* work-around for 
https://bugzilla.kernel.org/show_bug.cgi?id=205033 */
+                       if (!cuart->rx_enabled)
+                               continue;
+
+                       if (cuart->rx_threshold == 1) {
+                               /* bypass ringbuffer and report byte directly */
+                               card_uart_notification(cuart, 
CUART_E_RX_SINGLE, &buf[i]);
+                       } else {
+                               /* go via ringbuffer and notify only after 
threshold */
+                               ringbuffer_put(&cuart->u.tty.rx_ringbuf, 
buf[i]);
+                               if (ringbuffer_num(&cuart->u.tty.rx_ringbuf) >= 
cuart->rx_threshold)
+                                       card_uart_notification(cuart, 
CUART_E_RX_COMPLETE, NULL);
+                       }
+               }
+       }
+       if (what & OSMO_FD_WRITE) {
+               unsigned int to_tx;
+               OSMO_ASSERT(cuart->u.tty.tx_buf_len > cuart->u.tty.tx_index);
+               /* push as many pending transmit bytes as possible */
+               to_tx = cuart->u.tty.tx_buf_len - cuart->u.tty.tx_index;
+               rc = write(ofd->fd, cuart->u.tty.tx_buf + 
cuart->u.tty.tx_index, to_tx);
+               OSMO_ASSERT(rc > 0);
+               cuart->u.tty.tx_index += rc;
+
+               /* if no more bytes to transmit, disable OSMO_FD_WRITE */
+               if (cuart->u.tty.tx_index >= cuart->u.tty.tx_buf_len) {
+                       ofd->when &= ~BSC_FD_WRITE;
+                       /* ensure everything is written (tx queue/fifo drained) 
*/
+                       tcdrain(cuart->u.tty.ofd.fd);
+                       osmo_select_main(true);
+                       cuart->tx_busy = false;
+                       /* notify */
+                       card_uart_notification(cuart, CUART_E_TX_COMPLETE, 
(void *)cuart->u.tty.tx_buf);
+               }
+       }
+       return 0;
+}
+
+static int tty_uart_open(struct card_uart *cuart, const char *device_name)
+{
+       int rc;
+
+       rc = ringbuffer_init(&cuart->u.tty.rx_ringbuf, cuart->u.tty.rx_buf, 
sizeof(cuart->u.tty.rx_buf));
+       if (rc < 0)
+               return rc;
+
+       cuart->u.tty.ofd.fd = -1;
+       rc = osmo_serial_init(device_name, B9600);
+       if (rc < 0)
+               return rc;
+
+       osmo_fd_setup(&cuart->u.tty.ofd, rc, BSC_FD_READ, tty_uart_fd_cb, 
cuart, 0);
+        cuart->u.tty.baudrate = B9600;
+
+       rc = _init_uart(cuart->u.tty.ofd.fd);
+       if (rc < 0) {
+               tty_uart_close(cuart);
+               return rc;
+       }
+
+       _flush(cuart->u.tty.ofd.fd);
+
+       osmo_fd_register(&cuart->u.tty.ofd);
+
+        return 0;
+}
+
+static int tty_uart_close(struct card_uart *cuart)
+{
+       OSMO_ASSERT(cuart->driver == &tty_uart_driver);
+       if (cuart->u.tty.ofd.fd != -1) {
+               osmo_fd_unregister(&cuart->u.tty.ofd);
+               close(cuart->u.tty.ofd.fd);
+               cuart->u.tty.ofd.fd = -1;
+       }
+       return 0;
+}
+
+static int tty_uart_async_tx(struct card_uart *cuart, const uint8_t *data, 
size_t len, bool rx_after)
+{
+       OSMO_ASSERT(cuart->driver == &tty_uart_driver);
+
+       cuart->u.tty.tx_buf = data;
+       cuart->u.tty.tx_buf_len = len;
+       cuart->u.tty.tx_buf_len = len;
+       cuart->tx_busy = true;
+       cuart->u.tty.ofd.when |= OSMO_FD_WRITE;
+
+       return 0;
+}
+
+static int tty_uart_async_rx(struct card_uart *cuart, uint8_t *data, size_t 
len)
+{
+       int rc, i;
+       OSMO_ASSERT(cuart->driver == &tty_uart_driver);
+
+       /* FIXME: actually do this asynchronously */
+       for (i = 0; i < len; i++) {
+               rc = ringbuffer_get(&cuart->u.tty.rx_ringbuf, &data[i]);
+               if (rc < 0)
+                       return i;
+       }
+
+       return i;
+}
+
+static int tty_uart_ctrl(struct card_uart *cuart, enum card_uart_ctl ctl, bool 
enable)
+{
+       struct termios tio;
+       int rc;
+
+       switch (ctl) {
+       case CUART_CTL_RX:
+               rc = tcgetattr(cuart->u.tty.ofd.fd, &tio);
+               if (rc < 0) {
+                       perror("tcgetattr()");
+                       return -EIO;
+               }
+               /* We do our best here, but lots of [USB] serial drivers seem 
to ignore
+                * CREAD, see 
https://bugzilla.kernel.org/show_bug.cgi?id=205033 */
+               if (enable)
+                       tio.c_cflag |= CREAD;
+               else
+                       tio.c_cflag &= ~CREAD;
+               rc = tcsetattr(cuart->u.tty.ofd.fd, TCSANOW, &tio);
+               if (rc < 0) {
+                       perror("tcsetattr()");
+                       return -EIO;
+               }
+               break;
+       case CUART_CTL_RST:
+               _set_rts(cuart->u.tty.ofd.fd, enable);
+               if (enable)
+                       _flush(cuart->u.tty.ofd.fd);
+               break;
+       case CUART_CTL_POWER:
+       case CUART_CTL_CLOCK:
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static const struct card_uart_ops tty_uart_ops = {
+       .open = tty_uart_open,
+       .close = tty_uart_close,
+       .async_tx = tty_uart_async_tx,
+       .async_rx = tty_uart_async_rx,
+       .ctrl = tty_uart_ctrl,
+};
+
+static struct card_uart_driver tty_uart_driver = {
+       .name = "tty",
+       .ops = &tty_uart_ops,
+};
+
+static __attribute__((constructor)) void on_dso_load_cuart_tty(void)
+{
+       card_uart_driver_register(&tty_uart_driver);
+}
diff --git a/ccid/cuart_test.c b/ccid/cuart_test.c
new file mode 100644
index 0000000..aff9de7
--- /dev/null
+++ b/ccid/cuart_test.c
@@ -0,0 +1,76 @@
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <osmocom/core/utils.h>
+
+#include "cuart.h"
+
+static struct card_uart g_cuart;
+
+
+static void handle_event(struct card_uart *cuart, enum card_uart_event evt, 
void *data)
+{
+       printf("Handle Event '%s'\n", get_value_string(card_uart_event_vals, 
evt));
+       switch (evt) {
+       case CUART_E_RX_SINGLE:
+               printf("\t%02x\n", *(uint8_t *)data);
+               break;
+       }
+
+}
+
+
+static int get_atr(uint8_t *atr, size_t max_len)
+{
+       int rc;
+       int i = 0;
+
+       card_uart_ctrl(&g_cuart, CUART_CTL_RST, true);
+       usleep(100000);
+       card_uart_ctrl(&g_cuart, CUART_CTL_RST, false);
+
+       sleep(1);
+       osmo_select_main(true);
+
+       for (i = 0; i < max_len; i++) {
+               rc = card_uart_rx(&g_cuart, &atr[i], 1);
+               if (rc <= 0)
+                       break;
+       }
+
+       return i;
+}
+
+static void test_apdu(void)
+{
+       const uint8_t select_mf[] = "\xa0\xa4\x04\x00\x02\x3f\x00";
+       card_uart_tx(&g_cuart, select_mf, 5, true);
+
+       osmo_select_main(true);
+       /* we should get an RX_SINGLE event here */
+}
+
+
+int main(int argc, char **argv)
+{
+       uint8_t atr[64];
+       int rc;
+
+       g_cuart.handle_event = &handle_event;
+       rc = card_uart_open(&g_cuart, "tty", "/dev/ttyUSB5");
+       if (rc < 0) {
+               perror("opening UART");
+               exit(1);
+       }
+
+       rc = get_atr(atr, sizeof(atr));
+       if (rc < 0) {
+               perror("getting ATR");
+               exit(1);
+       }
+       printf("ATR: %s\n", osmo_hexdump(atr, rc));
+
+       test_apdu();
+
+       exit(0);
+}
diff --git a/ccid/utils_ringbuffer.c b/ccid/utils_ringbuffer.c
new file mode 100644
index 0000000..49c273c
--- /dev/null
+++ b/ccid/utils_ringbuffer.c
@@ -0,0 +1,109 @@
+/**
+ * \file
+ *
+ * \brief Ringbuffer functionality implementation.
+ *
+ * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Subject to your compliance with these terms, you may use Microchip
+ * software and any derivatives exclusively with Microchip products.
+ * It is your responsibility to comply with third party license terms 
applicable
+ * to your use of third party software (including open source software) that
+ * may accompany Microchip software.
+ *
+ * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
+ * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
+ * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
+ * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
+ * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
+ * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
+ * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
+ * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
+ * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
+ * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
+ * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
+ *
+ * \asf_license_stop
+ *
+ */
+#include <errno.h>
+#include "utils_ringbuffer.h"
+
+/**
+ * \brief Ringbuffer init
+ */
+int32_t ringbuffer_init(struct ringbuffer *const rb, void *buf, uint32_t size)
+{
+       /*
+        * buf size must be aligned to power of 2
+        */
+       if ((size & (size - 1)) != 0) {
+               return -EINVAL;
+       }
+
+       /* size - 1 is faster in calculation */
+       rb->size        = size - 1;
+       rb->read_index  = 0;
+       rb->write_index = rb->read_index;
+       rb->buf         = (uint8_t *)buf;
+
+       return 0;
+}
+
+/**
+ * \brief Get one byte from ringbuffer
+ *
+ */
+int32_t ringbuffer_get(struct ringbuffer *const rb, uint8_t *data)
+{
+       if (rb->write_index != rb->read_index) {
+               *data = rb->buf[rb->read_index & rb->size];
+               rb->read_index++;
+               return 0;
+       }
+
+       return -ENOENT;
+}
+
+/**
+ * \brief Put one byte to ringbuffer
+ *
+ */
+int32_t ringbuffer_put(struct ringbuffer *const rb, uint8_t data)
+{
+       rb->buf[rb->write_index & rb->size] = data;
+
+       /*
+        * buffer full strategy: new data will overwrite the oldest data in
+        * the buffer
+        */
+       if ((rb->write_index - rb->read_index) > rb->size) {
+               rb->read_index = rb->write_index - rb->size;
+       }
+
+       rb->write_index++;
+
+       return 0;
+}
+
+/**
+ * \brief Return the element number of ringbuffer
+ */
+uint32_t ringbuffer_num(const struct ringbuffer *const rb)
+{
+       return rb->write_index - rb->read_index;
+}
+
+/**
+ * \brief Flush ringbuffer
+ */
+uint32_t ringbuffer_flush(struct ringbuffer *const rb)
+{
+       rb->read_index = rb->write_index;
+
+       return 0;
+}
diff --git a/ccid/utils_ringbuffer.h b/ccid/utils_ringbuffer.h
new file mode 100644
index 0000000..07043a6
--- /dev/null
+++ b/ccid/utils_ringbuffer.h
@@ -0,0 +1,115 @@
+/**
+ * \file
+ *
+ * \brief Ringbuffer declaration.
+ *
+ * Copyright (c) 2014-2018 Microchip Technology Inc. and its subsidiaries.
+ *
+ * \asf_license_start
+ *
+ * \page License
+ *
+ * Subject to your compliance with these terms, you may use Microchip
+ * software and any derivatives exclusively with Microchip products.
+ * It is your responsibility to comply with third party license terms 
applicable
+ * to your use of third party software (including open source software) that
+ * may accompany Microchip software.
+ *
+ * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
+ * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
+ * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
+ * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
+ * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
+ * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
+ * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
+ * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
+ * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
+ * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
+ * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
+ *
+ * \asf_license_stop
+ *
+ */
+#ifndef _UTILS_RINGBUFFER_H_INCLUDED
+#define _UTILS_RINGBUFFER_H_INCLUDED
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * \addtogroup doc_driver_hal_utils_ringbuffer
+ *
+ * @{
+ */
+
+/**
+ * \brief Ring buffer element type
+ */
+struct ringbuffer {
+       uint8_t *buf;         /** Buffer base address */
+       uint32_t size;        /** Buffer size */
+       uint32_t read_index;  /** Buffer read index */
+       uint32_t write_index; /** Buffer write index */
+};
+
+/**
+ * \brief Ring buffer init
+ *
+ * \param[in] rb The pointer to a ring buffer structure instance
+ * \param[in] buf Space to store the data
+ * \param[in] size The buffer length, must be aligned with power of 2
+ *
+ * \return ERR_NONE on success, or an error code on failure.
+ */
+int32_t ringbuffer_init(struct ringbuffer *const rb, void *buf, uint32_t size);
+
+/**
+ * \brief Get one byte from ring buffer, the user needs to handle the 
concurrent
+ * access on buffer via put/get/flush
+ *
+ * \param[in] rb The pointer to a ring buffer structure instance
+ * \param[in] data One byte space to store the read data
+ *
+ * \return ERR_NONE on success, or an error code on failure.
+ */
+int32_t ringbuffer_get(struct ringbuffer *const rb, uint8_t *data);
+
+/**
+ * \brief Put one byte to ring buffer, the user needs to handle the concurrent 
access
+ * on buffer via put/get/flush
+ *
+ * \param[in] rb The pointer to a ring buffer structure instance
+ * \param[in] data One byte data to be put into ring buffer
+ *
+ * \return ERR_NONE on success, or an error code on failure.
+ */
+int32_t ringbuffer_put(struct ringbuffer *const rb, uint8_t data);
+
+/**
+ * \brief Return the element number of ring buffer
+ *
+ * \param[in] rb The pointer to a ring buffer structure instance
+ *
+ * \return The number of elements in ring buffer [0, rb->size]
+ */
+uint32_t ringbuffer_num(const struct ringbuffer *const rb);
+
+/**
+ * \brief Flush ring buffer, the user needs to handle the concurrent access on 
buffer
+ * via put/get/flush
+ *
+ * \param[in] rb The pointer to a ring buffer structure instance
+ *
+ * \return ERR_NONE on success, or an error code on failure.
+ */
+uint32_t ringbuffer_flush(struct ringbuffer *const rb);
+
+/**@}*/
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _UTILS_RINGBUFFER_H_INCLUDED */

--
To view, visit https://gerrit.osmocom.org/c/osmo-ccid-firmware/+/15691
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ccid-firmware
Gerrit-Branch: master
Gerrit-Change-Id: Ic7e324d99f78b3bfb98fc667d9a1b7fa363f092d
Gerrit-Change-Number: 15691
Gerrit-PatchSet: 1
Gerrit-Owner: laforge <lafo...@osmocom.org>
Gerrit-MessageType: newchange

Reply via email to