From a40c8e502dbd8331542c975d68be487ccc543dfa Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 27 Sep 2019 19:22:34 +0200 Subject: [PATCH] 'cuart' Card-UART abstraction + driver for simple serial reader Change-Id: Ic7e324d99f78b3bfb98fc667d9a1b7fa363f092d --- ccid_common/cuart.c | 133 ++++++++++++++++ ccid_common/cuart.h | 107 +++++++++++++ ccid_host/Makefile | 10 +- ccid_host/cuart_driver_tty.c | 287 +++++++++++++++++++++++++++++++++++ ccid_host/cuart_test.c | 76 ++++++++++ ccid_host/utils_ringbuffer.c | 109 +++++++++++++ ccid_host/utils_ringbuffer.h | 115 ++++++++++++++ 7 files changed, 836 insertions(+), 1 deletion(-) create mode 100644 ccid_common/cuart.c create mode 100644 ccid_common/cuart.h create mode 100644 ccid_host/cuart_driver_tty.c create mode 100644 ccid_host/cuart_test.c create mode 100644 ccid_host/utils_ringbuffer.c create mode 100644 ccid_host/utils_ringbuffer.h 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 +#include +#include +#include + +#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 +#include +#include + +#include +#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_host/Makefile b/ccid_host/Makefile index 2ed6c83..8f75559 100644 --- a/ccid_host/Makefile +++ b/ccid_host/Makefile @@ -1,6 +1,8 @@ CFLAGS=-Wall -g $(shell pkg-config --cflags libosmocore) -I../ccid_common -I. LIBS?=-lasan $(shell pkg-config --libs libosmocore) +all: ccid_functionfs hub_functionfs cuart_test + ccid_functionfs: ccid_main_functionfs.o \ ccid_slot_sim.o \ logging.o \ @@ -11,8 +13,14 @@ ccid_functionfs: ccid_main_functionfs.o \ hub_functionfs: hub_main_functionfs.o $(CC) $(CFLAGS) -o $@ $^ $(LIBS) -laio +cuart_test: cuart_test.o \ + cuart_driver_tty.o \ + utils_ringbuffer.o \ + ../ccid_common/cuart.o + $(CC) $(CFLAGS) -o $@ $^ $(LIBS) + %.o: %.c $(CC) $(CFLAGS) -o $@ -c $^ clean: - rm -f ccid_functionfs hub_functionfs *.o + rm -f ccid_functionfs hub_functionfs cuart_test *.o diff --git a/ccid_host/cuart_driver_tty.c b/ccid_host/cuart_driver_tty.c new file mode 100644 index 0000000..6bd2b52 --- /dev/null +++ b/ccid_host/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 +#include +#include +#include + +#include +#include +#include + +#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_host/cuart_test.c b/ccid_host/cuart_test.c new file mode 100644 index 0000000..aff9de7 --- /dev/null +++ b/ccid_host/cuart_test.c @@ -0,0 +1,76 @@ +#include +#include +#include +#include + +#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_host/utils_ringbuffer.c b/ccid_host/utils_ringbuffer.c new file mode 100644 index 0000000..49c273c --- /dev/null +++ b/ccid_host/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 +#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_host/utils_ringbuffer.h b/ccid_host/utils_ringbuffer.h new file mode 100644 index 0000000..07043a6 --- /dev/null +++ b/ccid_host/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 + +#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 */