mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:32:45 +01:00
ethos: move bulk of state machine out of ISR context
This moves the following parts of ethos' state machine out of ISR context: - Sending and replying to HELLO messages - Byte-unstuffing Some escape handling is still needed in the ISR handler, due to ethos' protocol design, to determine if a received byte must go into the netdev queue (tsrb) or the STDIO queue (isrpipe), but the actual unstuffing is now done in the STDIO and netdev handler threads, respectively.
This commit is contained in:
parent
568be105f2
commit
820a3976ad
@ -20,6 +20,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ethos.h"
|
||||
@ -37,7 +38,7 @@
|
||||
#error USE_ETHOS_FOR_STDIO is deprecated, use USEMODULE+=stdio_ethos instead
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_STDIO_ETHOS
|
||||
#ifdef MODULE_ETHOS_STDIO
|
||||
#include "stdio_uart.h"
|
||||
#include "isrpipe.h"
|
||||
extern isrpipe_t ethos_stdio_isrpipe;
|
||||
@ -46,6 +47,8 @@ extern isrpipe_t ethos_stdio_isrpipe;
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
#define ETHOS_FRAME_TYPE_ERRORED (0xff)
|
||||
|
||||
static void _get_mac_addr(netdev_t *dev, uint8_t* buf);
|
||||
static void ethos_isr(void *arg, uint8_t c);
|
||||
static const netdev_driver_t netdev_driver_ethos;
|
||||
@ -59,10 +62,7 @@ void ethos_setup(ethos_t *dev, const ethos_params_t *params, uint8_t idx,
|
||||
dev->netdev.driver = &netdev_driver_ethos;
|
||||
dev->uart = params->uart;
|
||||
dev->state = WAIT_FRAMESTART;
|
||||
dev->framesize = 0;
|
||||
dev->frametype = 0;
|
||||
dev->last_framesize = 0;
|
||||
dev->accept_new = true;
|
||||
|
||||
tsrb_init(&dev->inbuf, inbuf, inbuf_size);
|
||||
mutex_init(&dev->out_mutex);
|
||||
@ -81,33 +81,56 @@ static void _reset_state(ethos_t *dev)
|
||||
{
|
||||
dev->state = WAIT_FRAMESTART;
|
||||
dev->frametype = 0;
|
||||
dev->framesize = 0;
|
||||
dev->accept_new = true;
|
||||
}
|
||||
|
||||
static void _fail_frame(ethos_t *dev)
|
||||
{
|
||||
switch (dev->frametype) {
|
||||
case ETHOS_FRAME_TYPE_DATA:
|
||||
tsrb_clear(&dev->inbuf);
|
||||
/* signal to handler thread that frame is at an end (makes handler thread to
|
||||
* truncate frame) */
|
||||
tsrb_add_one(&dev->inbuf, ETHOS_FRAME_DELIMITER);
|
||||
break;
|
||||
case ETHOS_FRAME_TYPE_TEXT:
|
||||
tsrb_clear(ðos_stdio_isrpipe.tsrb);
|
||||
/* signal to handler thread that frame is at an end (makes handler thread to
|
||||
* truncate frame) */
|
||||
isrpipe_write_one(ðos_stdio_isrpipe, ETHOS_FRAME_DELIMITER);
|
||||
break;
|
||||
case ETHOS_FRAME_TYPE_ERRORED:
|
||||
return;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
dev->frametype = ETHOS_FRAME_TYPE_ERRORED;
|
||||
}
|
||||
|
||||
static void _handle_char(ethos_t *dev, char c)
|
||||
{
|
||||
switch (dev->frametype) {
|
||||
case ETHOS_FRAME_TYPE_DATA:
|
||||
case ETHOS_FRAME_TYPE_HELLO:
|
||||
case ETHOS_FRAME_TYPE_HELLO_REPLY:
|
||||
if (dev->accept_new) {
|
||||
if (tsrb_add_one(&dev->inbuf, c) == 0) {
|
||||
dev->framesize++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (tsrb_add_one(&dev->inbuf, c) < 0) {
|
||||
//puts("lost frame");
|
||||
dev->inbuf.reads = 0;
|
||||
dev->inbuf.writes = 0;
|
||||
_reset_state(dev);
|
||||
_fail_frame(dev);
|
||||
}
|
||||
break;
|
||||
#ifdef MODULE_STDIO_ETHOS
|
||||
case ETHOS_FRAME_TYPE_TEXT:
|
||||
dev->framesize++;
|
||||
isrpipe_write_one(ðos_stdio_isrpipe, c);
|
||||
#ifdef MODULE_ETHOS_STDIO
|
||||
if (isrpipe_write_one(ðos_stdio_isrpipe, c) < 0) {
|
||||
//puts("lost frame");
|
||||
_fail_frame(dev);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
break;
|
||||
case ETHOS_FRAME_TYPE_ERRORED:
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,20 +138,15 @@ static void _end_of_frame(ethos_t *dev)
|
||||
{
|
||||
switch(dev->frametype) {
|
||||
case ETHOS_FRAME_TYPE_DATA:
|
||||
if (dev->framesize) {
|
||||
assert(dev->last_framesize == 0);
|
||||
dev->last_framesize = dev->framesize;
|
||||
netdev_trigger_event_isr(&dev->netdev);
|
||||
|
||||
}
|
||||
netdev_trigger_event_isr(&dev->netdev);
|
||||
break;
|
||||
case ETHOS_FRAME_TYPE_HELLO:
|
||||
ethos_send_frame(dev, dev->mac_addr, 6, ETHOS_FRAME_TYPE_HELLO_REPLY);
|
||||
/* fall through */
|
||||
case ETHOS_FRAME_TYPE_HELLO_REPLY:
|
||||
if (dev->framesize == 6) {
|
||||
tsrb_get(&dev->inbuf, dev->remote_mac_addr, 6);
|
||||
}
|
||||
case ETHOS_FRAME_TYPE_TEXT:
|
||||
break;
|
||||
case ETHOS_FRAME_TYPE_ERRORED:
|
||||
break;
|
||||
default:
|
||||
/* Unexpected frametype */
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -142,45 +160,50 @@ static void ethos_isr(void *arg, uint8_t c)
|
||||
switch (dev->state) {
|
||||
case WAIT_FRAMESTART:
|
||||
if (c == ETHOS_FRAME_DELIMITER) {
|
||||
_reset_state(dev);
|
||||
if (dev->last_framesize) {
|
||||
dev->accept_new = false;
|
||||
}
|
||||
dev->state = IN_FRAME;
|
||||
dev->frametype = ETHOS_FRAME_TYPE_DATA;
|
||||
}
|
||||
break;
|
||||
case IN_FRAME:
|
||||
_handle_char(dev, c);
|
||||
if (c == ETHOS_ESC_CHAR) {
|
||||
dev->state = IN_ESCAPE;
|
||||
}
|
||||
else if (c == ETHOS_FRAME_DELIMITER) {
|
||||
if (dev->framesize) {
|
||||
_end_of_frame(dev);
|
||||
}
|
||||
}
|
||||
else {
|
||||
_handle_char(dev, c);
|
||||
_end_of_frame(dev);
|
||||
}
|
||||
break;
|
||||
case IN_ESCAPE:
|
||||
switch (c) {
|
||||
case (ETHOS_FRAME_DELIMITER ^ 0x20):
|
||||
_handle_char(dev, ETHOS_FRAME_DELIMITER);
|
||||
break;
|
||||
case (ETHOS_ESC_CHAR ^ 0x20):
|
||||
_handle_char(dev, ETHOS_ESC_CHAR);
|
||||
break;
|
||||
case (ETHOS_FRAME_TYPE_TEXT ^ 0x20):
|
||||
dev->frametype = ETHOS_FRAME_TYPE_TEXT;
|
||||
break;
|
||||
/* reset tsrb (used for networking) */
|
||||
dev->inbuf.reads = 0;
|
||||
dev->inbuf.writes = 0;
|
||||
dev->state = IN_FRAME;
|
||||
return;
|
||||
case (ETHOS_FRAME_TYPE_HELLO ^ 0x20):
|
||||
dev->frametype = ETHOS_FRAME_TYPE_HELLO;
|
||||
break;
|
||||
case (ETHOS_FRAME_TYPE_HELLO_REPLY ^ 0x20):
|
||||
dev->frametype = ETHOS_FRAME_TYPE_HELLO_REPLY;
|
||||
dev->frametype = ETHOS_FRAME_TYPE_DATA;
|
||||
break;
|
||||
case ETHOS_FRAME_DELIMITER:
|
||||
_handle_char(dev, c);
|
||||
_reset_state(dev);
|
||||
return;
|
||||
default:
|
||||
/* unknown escaped character or raw delimiter */
|
||||
_fail_frame(dev);
|
||||
return;
|
||||
}
|
||||
dev->state = IN_FRAME;
|
||||
/* write marker to tsrb for thread layer to handle */
|
||||
_handle_char(dev, c);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -231,14 +254,7 @@ void ethos_send_frame(ethos_t *dev, const uint8_t *data, size_t len, unsigned fr
|
||||
{
|
||||
uint8_t frame_delim = ETHOS_FRAME_DELIMITER;
|
||||
|
||||
if (!irq_is_in()) {
|
||||
mutex_lock(&dev->out_mutex);
|
||||
}
|
||||
else {
|
||||
/* Send frame delimiter. This cancels the current frame,
|
||||
* but enables in-ISR writes. */
|
||||
uart_write(dev->uart, &frame_delim, 1);
|
||||
}
|
||||
mutex_lock(&dev->out_mutex);
|
||||
|
||||
/* send frame delimiter */
|
||||
uart_write(dev->uart, &frame_delim, 1);
|
||||
@ -257,9 +273,7 @@ void ethos_send_frame(ethos_t *dev, const uint8_t *data, size_t len, unsigned fr
|
||||
/* end of frame */
|
||||
uart_write(dev->uart, &frame_delim, 1);
|
||||
|
||||
if (!irq_is_in()) {
|
||||
mutex_unlock(&dev->out_mutex);
|
||||
}
|
||||
mutex_unlock(&dev->out_mutex);
|
||||
}
|
||||
|
||||
static int _send(netdev_t *netdev, const iolist_t *iolist)
|
||||
@ -299,38 +313,110 @@ static void _get_mac_addr(netdev_t *encdev, uint8_t* buf)
|
||||
memcpy(buf, dev->mac_addr, 6);
|
||||
}
|
||||
|
||||
static unsigned _copy_byte(uint8_t *buf, uint8_t byte, bool *escaped)
|
||||
{
|
||||
*buf = byte;
|
||||
*escaped = false;
|
||||
return 1U;
|
||||
}
|
||||
|
||||
unsigned ethos_unstuff_readbyte(uint8_t *buf, uint8_t byte, bool *escaped, uint8_t *frametype)
|
||||
{
|
||||
switch (byte) {
|
||||
case ETHOS_ESC_CHAR:
|
||||
*escaped = true;
|
||||
/* Intentionally falls through */
|
||||
case ETHOS_FRAME_DELIMITER:
|
||||
break;
|
||||
case ETHOS_ESC_CHAR ^ 0x20:
|
||||
if (*escaped) {
|
||||
return _copy_byte(buf, ETHOS_ESC_CHAR, escaped);
|
||||
}
|
||||
/* Intentionally falls through */
|
||||
/* to default when !(*escaped) */
|
||||
case ETHOS_FRAME_DELIMITER ^ 0x20:
|
||||
if (*escaped) {
|
||||
return _copy_byte(buf, ETHOS_FRAME_DELIMITER, escaped);
|
||||
}
|
||||
/* Intentionally falls through */
|
||||
/* to default when !(*escaped) */
|
||||
case ETHOS_FRAME_TYPE_TEXT ^ 0x20:
|
||||
case ETHOS_FRAME_TYPE_HELLO ^ 0x20:
|
||||
case ETHOS_FRAME_TYPE_HELLO_REPLY ^ 0x20:
|
||||
if (*escaped) {
|
||||
*frametype = byte ^ 0x20;
|
||||
break;
|
||||
}
|
||||
/* Intentionally falls through */
|
||||
default:
|
||||
return _copy_byte(buf, byte, escaped);
|
||||
}
|
||||
return 0U;
|
||||
}
|
||||
|
||||
static int _recv(netdev_t *netdev, void *buf, size_t len, void* info)
|
||||
{
|
||||
(void) info;
|
||||
ethos_t * dev = (ethos_t *) netdev;
|
||||
int res = 0;
|
||||
|
||||
if (buf) {
|
||||
if (len < dev->last_framesize) {
|
||||
DEBUG("ethos _recv(): receive buffer too small.\n");
|
||||
return -1;
|
||||
int byte;
|
||||
bool escaped = false;
|
||||
uint8_t *ptr = buf;
|
||||
uint8_t frametype = ETHOS_FRAME_TYPE_DATA;
|
||||
|
||||
do {
|
||||
int tmp;
|
||||
|
||||
if ((byte = tsrb_get_one(&dev->inbuf)) < 0) {
|
||||
DEBUG("ethos _recv(): inbuf doesn't contain enough bytes.\n");
|
||||
return -EIO;
|
||||
}
|
||||
tmp = ethos_unstuff_readbyte(ptr, (uint8_t)byte, &escaped, &frametype);
|
||||
ptr += tmp;
|
||||
res += tmp;
|
||||
if ((unsigned)res > len) {
|
||||
while (byte != (int)ETHOS_FRAME_DELIMITER) {
|
||||
/* clear out unreceived packet */
|
||||
byte = tsrb_get_one(&dev->inbuf);
|
||||
}
|
||||
return -ENOBUFS;
|
||||
}
|
||||
} while (byte != ETHOS_FRAME_DELIMITER);
|
||||
|
||||
switch (frametype) {
|
||||
case ETHOS_FRAME_TYPE_HELLO:
|
||||
ethos_send_frame(dev, dev->mac_addr, 6, ETHOS_FRAME_TYPE_HELLO_REPLY);
|
||||
/* fall through */
|
||||
case ETHOS_FRAME_TYPE_HELLO_REPLY:
|
||||
if (res == 6) {
|
||||
memcpy(dev->remote_mac_addr, buf, 6);
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
len = dev->last_framesize;
|
||||
|
||||
if ((tsrb_get(&dev->inbuf, buf, len) != (int)len)) {
|
||||
DEBUG("ethos _recv(): inbuf doesn't contain enough bytes.\n");
|
||||
dev->last_framesize = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->last_framesize = 0;
|
||||
return (int)len;
|
||||
}
|
||||
else {
|
||||
if (len) {
|
||||
int dropsize = dev->last_framesize;
|
||||
dev->last_framesize = 0;
|
||||
return tsrb_drop(&dev->inbuf, dropsize);
|
||||
/* remove data */
|
||||
for (; len > 0; len--) {
|
||||
int byte = tsrb_get_one(&dev->inbuf);
|
||||
if ((byte == (int)ETHOS_FRAME_DELIMITER) || (byte < 0)) {
|
||||
/* end early if end of packet or ringbuffer is reached;
|
||||
* len might be larger than the actual packet */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return dev->last_framesize;
|
||||
/* set to 2048 in sys/net/gnrc/netif/init_devs/auto_init_ethos.c so safe to cast
|
||||
* unsigned to int */
|
||||
res = (int)tsrb_avail(&dev->inbuf);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len)
|
||||
|
@ -49,7 +49,33 @@ extern unsigned ethos_unstuff_readbyte(uint8_t *buf, uint8_t byte,
|
||||
|
||||
ssize_t stdio_read(void* buffer, size_t len)
|
||||
{
|
||||
return (ssize_t)isrpipe_read(ðos_stdio_isrpipe, buffer, len);
|
||||
uint8_t *ptr = buffer;
|
||||
bool escaped = false;
|
||||
uint8_t frametype = ETHOS_FRAME_TYPE_TEXT;
|
||||
uint8_t byte;
|
||||
|
||||
do {
|
||||
int read = isrpipe_read(ðos_stdio_isrpipe, &byte, 1);
|
||||
int tmp;
|
||||
|
||||
if (read == 0) {
|
||||
continue;
|
||||
}
|
||||
else if (len == 0) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
tmp = ethos_unstuff_readbyte(ptr, byte, &escaped, &frametype);
|
||||
ptr += tmp;
|
||||
if ((unsigned)(ptr - (uint8_t *)buffer) > len) {
|
||||
while (byte != ETHOS_FRAME_DELIMITER) {
|
||||
/* clear out unreceived frame */
|
||||
isrpipe_read(ðos_stdio_isrpipe, &byte, 1);
|
||||
}
|
||||
return -ENOBUFS;
|
||||
}
|
||||
} while (byte != ETHOS_FRAME_DELIMITER);
|
||||
|
||||
return ptr - (uint8_t *)buffer;
|
||||
}
|
||||
|
||||
ssize_t stdio_write(const void* buffer, size_t len)
|
||||
|
@ -89,11 +89,8 @@ typedef struct {
|
||||
uint8_t remote_mac_addr[6]; /**< this device's MAC address */
|
||||
tsrb_t inbuf; /**< ringbuffer for incoming data */
|
||||
line_state_t state; /**< Line status variable */
|
||||
size_t framesize; /**< size of currently incoming frame */
|
||||
unsigned frametype; /**< type of currently incoming frame */
|
||||
size_t last_framesize; /**< size of last completed frame */
|
||||
mutex_t out_mutex; /**< mutex used for locking concurrent sends */
|
||||
bool accept_new; /**< incoming frame can be stored or not */
|
||||
} ethos_t;
|
||||
|
||||
/**
|
||||
@ -128,6 +125,8 @@ void ethos_setup(ethos_t *dev, const ethos_params_t *params, uint8_t index,
|
||||
*
|
||||
* This is used by e.g., stdio over ethos to send text frames.
|
||||
*
|
||||
* @note Uses mutexes to synchronize sending multiple frames so it should not be called from ISR.
|
||||
*
|
||||
* @param[in] dev handle of the device to initialize
|
||||
* @param[in] data ptr to data to be sent
|
||||
* @param[in] len nr of bytes to send
|
||||
|
Loading…
Reference in New Issue
Block a user