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

Change-Id: Ic7e324d99f78b3bfb98fc667d9a1b7fa363f092d
This commit is contained in:
Harald Welte 2019-09-27 19:22:34 +02:00 committed by Harald Welte
parent 06348367fa
commit a40c8e502d
7 changed files with 836 additions and 1 deletions

133
ccid_common/cuart.c Normal file
View File

@ -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;
}

107
ccid_common/cuart.h Normal file
View File

@ -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);

View File

@ -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

View File

@ -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);
}

76
ccid_host/cuart_test.c Normal file
View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 */