diff --git a/Makefile.dep b/Makefile.dep index ff30654c85..4b23a0cf0e 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -419,3 +419,7 @@ endif ifneq (,$(filter phydat,$(USEMODULE))) USEMODULE += fmt endif + +ifneq (,$(filter ethos,$(USEMODULE))) + USEMODULE += netdev2_eth +endif diff --git a/drivers/ethos/Makefile b/drivers/ethos/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/drivers/ethos/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/drivers/ethos/ethos.c b/drivers/ethos/ethos.c new file mode 100644 index 0000000000..99f158c554 --- /dev/null +++ b/drivers/ethos/ethos.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2016 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup driver_ethos + * @{ + * + * @file + * @brief Implementation of a simple ethernet-over-serial driver + * + * @author Kaspar Schleiser + * + * @} + */ + +#include +#include +#include + +#include "random.h" +#include "ethos.h" +#include "periph/uart.h" +#include "tsrb.h" + +#include "net/netdev2.h" +#include "net/netdev2_eth.h" +#include "net/eui64.h" +#include "net/ethernet.h" + +#ifdef USE_ETHOS_FOR_STDIO +#include "uart_stdio.h" +#endif + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static void _get_mac_addr(netdev2_t *dev, uint8_t* buf); +static void ethos_isr(void *arg, char c); +const static netdev2_driver_t netdev2_driver_ethos; + +static const uint8_t _esc_esc[] = {ETHOS_ESC_CHAR, (ETHOS_ESC_CHAR ^ 0x20)}; +static const uint8_t _esc_delim[] = {ETHOS_ESC_CHAR, (ETHOS_FRAME_DELIMITER ^ 0x20)}; + + +void ethos_setup(ethos_t *dev, uart_t uart, uint32_t baudrate, uint8_t *buf, size_t bufsize) +{ + dev->netdev.driver = &netdev2_driver_ethos; + dev->uart = uart; + dev->state = WAIT_FRAMESTART; + dev->framesize = 0; + dev->frametype = 0; + dev->last_framesize = 0; + + tsrb_init(&dev->inbuf, (char*)buf, bufsize); + mutex_init(&dev->out_mutex); + + uint32_t a = genrand_uint32(); + memcpy(dev->mac_addr, (char*)&a, 4); + a = genrand_uint32(); + memcpy(dev->mac_addr+4, (char*)&a, 2); + + dev->mac_addr[0] &= (0x2); /* unset globally unique bit */ + dev->mac_addr[0] &= ~(0x1); /* set unicast bit*/ + + uart_init(uart, baudrate, ethos_isr, (void*)dev); + + uint8_t frame_delim = ETHOS_FRAME_DELIMITER; + uart_write(dev->uart, &frame_delim, 1); + ethos_send_frame(dev, dev->mac_addr, 6, ETHOS_FRAME_TYPE_HELLO); +} + +static void _reset_state(ethos_t *dev) +{ + dev->state = WAIT_FRAMESTART; + dev->frametype = 0; + dev->framesize = 0; +} + +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 (tsrb_add_one(&dev->inbuf, c) == 0) { + dev->framesize++; + } else { + //puts("lost frame"); + dev->inbuf.reads = 0; + dev->inbuf.writes = 0; + _reset_state(dev); + } + break; +#ifdef USE_ETHOS_FOR_STDIO + case ETHOS_FRAME_TYPE_TEXT: + dev->framesize++; + uart_stdio_rx_cb(NULL, c); +#endif + } +} + +static void _end_of_frame(ethos_t *dev) +{ + switch(dev->frametype) { + case ETHOS_FRAME_TYPE_DATA: + if (dev->framesize) { + dev->last_framesize = dev->framesize; + dev->netdev.event_callback((netdev2_t*) dev, NETDEV2_EVENT_ISR, NULL); + } + 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, (char*)dev->remote_mac_addr, 6); + } + break; + } + + _reset_state(dev); +} + +static void ethos_isr(void *arg, char c) +{ + ethos_t *dev = (ethos_t *) arg; + + switch (dev->state) { + case WAIT_FRAMESTART: + if (c == ETHOS_FRAME_DELIMITER) { + _reset_state(dev); + dev->state = IN_FRAME; + } + break; + case IN_FRAME: + 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); + } + 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; + 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; + break; + } + dev->state = IN_FRAME; + break; + } +} + +static void _isr(netdev2_t *netdev) +{ + ethos_t *dev = (ethos_t *) netdev; + dev->netdev.event_callback((netdev2_t*) dev, NETDEV2_EVENT_RX_COMPLETE, NULL); +} + +static int _init(netdev2_t *encdev) +{ + ethos_t *dev = (ethos_t *) encdev; + (void)dev; + return 0; +} + +static size_t iovec_count_total(const struct iovec *vector, int count) +{ + size_t result = 0; + while(count--) { + result += vector->iov_len; + vector++; + } + return result; +} + +static void _write_escaped(uart_t uart, uint8_t c) +{ + uint8_t *out; + int n; + + switch(c) { + case ETHOS_FRAME_DELIMITER: + out = (uint8_t*)_esc_delim; + n = 2; + break; + case ETHOS_ESC_CHAR: + out = (uint8_t*)_esc_esc; + n = 2; + break; + default: + out = &c; + n = 1; + } + + uart_write(uart, out, n); +} + +void ethos_send_frame(ethos_t *dev, const uint8_t *data, size_t len, unsigned frame_type) +{ + uint8_t frame_delim = ETHOS_FRAME_DELIMITER; + + if (!inISR()) { + 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); + } + + /* send frame delimiter */ + uart_write(dev->uart, &frame_delim, 1); + + /* set frame type */ + if (frame_type) { + uint8_t out[2] = { ETHOS_ESC_CHAR, (frame_type ^ 0x20) }; + uart_write(dev->uart, out, 2); + } + + /* send frame content */ + while(len--) { + _write_escaped(dev->uart, *(uint8_t*)data++); + } + + /* end of frame */ + uart_write(dev->uart, &frame_delim, 1); + + if (!inISR()) { + mutex_unlock(&dev->out_mutex); + } +} + +static int _send(netdev2_t *netdev, const struct iovec *vector, int count) +{ + ethos_t * dev = (ethos_t *) netdev; + (void)dev; + + /* count total packet length */ + size_t pktlen = iovec_count_total(vector, count); + + /* lock line in order to prevent multiple writes */ + mutex_lock(&dev->out_mutex); + + /* send start-frame-delimiter */ + uint8_t frame_delim = ETHOS_FRAME_DELIMITER; + uart_write(dev->uart, &frame_delim, 1); + + /* send iovec */ + while(count--) { + size_t n = vector->iov_len; + uint8_t *ptr = vector->iov_base; + while(n--) { + _write_escaped(dev->uart, *ptr++); + } + vector++; + } + + uart_write(dev->uart, &frame_delim, 1); + + mutex_unlock(&dev->out_mutex); + + return pktlen; +} + +static void _get_mac_addr(netdev2_t *encdev, uint8_t* buf) +{ + ethos_t * dev = (ethos_t *) encdev; + memcpy(buf, dev->mac_addr, 6); +} + +static int _recv(netdev2_t *netdev, char* buf, int len) +{ + ethos_t * dev = (ethos_t *) netdev; + + if (buf) { + if (len != dev->last_framesize) { + DEBUG("ethos _recv(): unmatched receive buffer size."); + return -1; + } + + dev->last_framesize = 0; + + if ((tsrb_get(&dev->inbuf, buf, len) != len)) { + DEBUG("ethos _recv(): inbuf doesn't contain enough bytes."); + return -1; + } + + return len; + } + else { + return dev->last_framesize; + } +} + +int _get(netdev2_t *dev, netopt_t opt, void *value, size_t max_len) +{ + int res = 0; + + switch (opt) { + case NETOPT_ADDRESS: + if (max_len < ETHERNET_ADDR_LEN) { + res = -EINVAL; + } + else { + _get_mac_addr(dev, (uint8_t*)value); + res = ETHERNET_ADDR_LEN; + } + break; + default: + res = netdev2_eth_get(dev, opt, value, max_len); + break; + } + + return res; +} + +/* netdev2 interface */ +const static netdev2_driver_t netdev2_driver_ethos = { + .send = _send, + .recv = _recv, + .init = _init, + .isr = _isr, + .get = _get, + .set = netdev2_eth_set +}; diff --git a/drivers/include/ethos.h b/drivers/include/ethos.h new file mode 100644 index 0000000000..3faa3a8caf --- /dev/null +++ b/drivers/include/ethos.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser General + * Public License v2.1. See the file LICENSE in the top level directory for more + * details. + */ + +/** + * @defgroup drivers_ethos ethos + * @ingroup drivers_netdev + * @brief Driver for the ethernet-over-serial module + * @{ + * + * @file + * @brief Interface definition for the ethernet-over-serial module + * + * @author Kaspar Schleiser + */ + +#ifndef ETHOS_H +#define ETHOS_H + +#include "kernel_types.h" +#include "periph/uart.h" +#include "net/netdev2.h" +#include "tsrb.h" +#include "mutex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* if using ethos + stdio, use STDIO values unless overridden */ +#ifdef USE_ETHOS_FOR_STDIO +#include "board.h" +#ifndef ETHOS_UART +#define ETHOS_UART STDIO +#endif +#ifndef ETHOS_BAUDRATE +#define ETHOS_BAUDRATE STDIO_BAUDRATE +#endif +#endif + +/** + * @name Escape char definitions + * @{ + */ +#define ETHOS_FRAME_DELIMITER (0x7E) +#define ETHOS_ESC_CHAR (0x7D) +#define ETHOS_FRAME_TYPE_DATA (0x0) +#define ETHOS_FRAME_TYPE_TEXT (0x1) +#define ETHOS_FRAME_TYPE_HELLO (0x2) +#define ETHOS_FRAME_TYPE_HELLO_REPLY (0x3) +/** @} */ + +/** + * @brief enum describing line state + */ +typedef enum { + WAIT_FRAMESTART, + IN_FRAME, + IN_ESCAPE +} line_state_t; + +/** + * @brief ethos netdev2 device + * @extends netdev2_t + */ +typedef struct { + netdev2_t netdev; /**< extended netdev2 structure */ + uart_t uart; /**< UART device the to use */ + uint8_t mac_addr[6]; /**< this device's MAC address */ + 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 */ +} ethos_t; + +/** + * @brief Setup an ethos based device state. + * + * The supplied buffer *must* have a power-of-two size, and it *must* be large + * enough for the largest expected packet + enough buffer space to buffer + * bytes that arrive while one packet is being handled. + * + * E.g., if 1536b ethernet frames are expected, 2048 is probably a good size for @p buf. + * + * @param[out] dev handle of the device to initialize + * @param[in] uart UART device to use + * @param[in] baudrate baudrate for UART device + * @param[in] buf buffer for incoming packets + * @param[in] bufsize size of @p buf + */ +void ethos_setup(ethos_t *dev, uart_t uart, uint32_t baudrate, uint8_t *buf, size_t bufsize); + +/** + * @brief send frame over serial port using ethos' framing + * + * This is used by e.g., stdio over ethos to send text frames. + * + * @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 + * @param[in] frame_type frame type to use + */ +void ethos_send_frame(ethos_t *dev, const uint8_t *data, size_t len, unsigned frame_type); + +#ifdef __cplusplus +} +#endif +#endif /* ETHOS_H */ +/** @} */ diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 7fa2fc35bf..dfbd168888 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -158,6 +158,11 @@ void auto_init(void) auto_init_enc28j60(); #endif +#ifdef MODULE_ETHOS + extern void auto_init_ethos(void); + auto_init_ethos(); +#endif + #ifdef MODULE_GNRC_SLIP extern void auto_init_slip(void); auto_init_slip(); diff --git a/sys/auto_init/netif/auto_init_ethos.c b/sys/auto_init/netif/auto_init_ethos.c new file mode 100644 index 0000000000..d881342501 --- /dev/null +++ b/sys/auto_init/netif/auto_init_ethos.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +/** + * @ingroup auto_init_gnrc_netif + * @{ + * + * @file + * @brief Auto initialization for ethernet-over-serial module + * + * @author Kaspar Schleiser + */ + +#ifdef MODULE_ETHOS + +#include "ethos.h" +#include "periph/uart.h" +#include "net/gnrc/netdev2/eth.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief global ethos object, used by uart_stdio + */ +ethos_t ethos; + +/** + * @brief Define stack parameters for the MAC layer thread + * @{ + */ +#define MAC_STACKSIZE (THREAD_STACKSIZE_DEFAULT + DEBUG_EXTRA_STACKSIZE) +#define MAC_PRIO (THREAD_PRIORITY_MAIN - 4) + +/** + * @brief Stacks for the MAC layer threads + */ +static char _netdev2_eth_stack[MAC_STACKSIZE]; +static gnrc_netdev2_t _gnrc_ethos; + +static uint8_t _inbuf[2048]; + +void auto_init_ethos(void) +{ + DEBUG("auto_init_ethos(): initializing device...\n"); + + /* setup netdev2 device */ + ethos_setup(ðos, ETHOS_UART, + ETHOS_BAUDRATE, _inbuf, sizeof(_inbuf)); + + /* initialize netdev2<->gnrc adapter state */ + gnrc_netdev2_eth_init(&_gnrc_ethos, (netdev2_t*)ðos); + + /* start gnrc netdev2 thread */ + gnrc_netdev2_init(_netdev2_eth_stack, MAC_STACKSIZE, + MAC_PRIO, "gnrc_ethos", &_gnrc_ethos); +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_ETHOS */ +/** @} */ diff --git a/sys/uart_stdio/uart_stdio.c b/sys/uart_stdio/uart_stdio.c index 3564e42add..0fcbd0f421 100644 --- a/sys/uart_stdio/uart_stdio.c +++ b/sys/uart_stdio/uart_stdio.c @@ -33,6 +33,11 @@ #include "board.h" #include "periph/uart.h" +#ifdef USE_ETHOS_FOR_STDIO +#include "ethos.h" +extern ethos_t ethos; +#endif + #define ENABLE_DEBUG 0 #include "debug.h" @@ -67,7 +72,11 @@ void uart_stdio_rx_cb(void *arg, char data) void uart_stdio_init(void) { +#ifndef USE_ETHOS_FOR_STDIO uart_init(STDIO, STDIO_BAUDRATE, uart_stdio_rx_cb, NULL); +#else + uart_init(ETHOS_UART, ETHOS_BAUDRATE, uart_stdio_rx_cb, NULL); +#endif } int uart_stdio_read(char* buffer, int count) @@ -81,6 +90,10 @@ int uart_stdio_read(char* buffer, int count) int uart_stdio_write(const char* buffer, int len) { +#ifndef USE_ETHOS_FOR_STDIO uart_write(STDIO, (uint8_t *)buffer, (size_t)len); +#else + ethos_send_frame(ðos, (uint8_t*)buffer, len, ETHOS_FRAME_TYPE_TEXT); +#endif return len; }