'cuart' Card-UART abstraction + driver for simple serial reader
Change-Id: Ic7e324d99f78b3bfb98fc667d9a1b7fa363f092d
This commit is contained in:
parent
06348367fa
commit
a40c8e502d
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
CFLAGS=-Wall -g $(shell pkg-config --cflags libosmocore) -I../ccid_common -I.
|
CFLAGS=-Wall -g $(shell pkg-config --cflags libosmocore) -I../ccid_common -I.
|
||||||
LIBS?=-lasan $(shell pkg-config --libs libosmocore)
|
LIBS?=-lasan $(shell pkg-config --libs libosmocore)
|
||||||
|
|
||||||
|
all: ccid_functionfs hub_functionfs cuart_test
|
||||||
|
|
||||||
ccid_functionfs: ccid_main_functionfs.o \
|
ccid_functionfs: ccid_main_functionfs.o \
|
||||||
ccid_slot_sim.o \
|
ccid_slot_sim.o \
|
||||||
logging.o \
|
logging.o \
|
||||||
|
@ -11,8 +13,14 @@ ccid_functionfs: ccid_main_functionfs.o \
|
||||||
hub_functionfs: hub_main_functionfs.o
|
hub_functionfs: hub_main_functionfs.o
|
||||||
$(CC) $(CFLAGS) -o $@ $^ $(LIBS) -laio
|
$(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
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) -o $@ -c $^
|
$(CC) $(CFLAGS) -o $@ -c $^
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f ccid_functionfs hub_functionfs *.o
|
rm -f ccid_functionfs hub_functionfs cuart_test *.o
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 */
|
Loading…
Reference in New Issue