diff --git a/drivers/include/slipdev.h b/drivers/include/slipdev.h index bf4418958d..675f65dc8d 100644 --- a/drivers/include/slipdev.h +++ b/drivers/include/slipdev.h @@ -12,6 +12,10 @@ * @defgroup drivers_slipdev_stdio STDIO via SLIP * @ingroup sys_stdio * @brief Standard input/output backend multiplexed via SLIP + * @see [draft-bormann-t2trg-slipmux-03](https://datatracker.ietf.org/doc/html/draft-bormann-t2trg-slipmux-03) + * + * This extension is part of the [Slipmux draft](https://datatracker.ietf.org/doc/html/draft-bormann-t2trg-slipmux-03). + * @warning This module is under development for optimizations and module names might change! * * This will multiplex STDIO via the Serial Line Internet Protocol. * The shell can be accessed via the `sliptty` tool. @@ -23,11 +27,32 @@ * @see drivers_slipdev */ +/** + * @defgroup drivers_slipdev_configuration CoAP via SLIP + * @ingroup drivers_slipdev + * @brief Exchange CoAP requests and responses via SLIP + * @see [draft-bormann-t2trg-slipmux-03](https://datatracker.ietf.org/doc/html/draft-bormann-t2trg-slipmux-03) + * + * This extension is part of the [Slipmux draft](https://datatracker.ietf.org/doc/html/draft-bormann-t2trg-slipmux-03). + * @warning This module is under development for optimizations and module names might change! + * + * This will multiplex CoAP messages via the Serial Line Internet Protocol. + * It spawns an extra thread as a CoAP server. The incoming requests are handled with `nanocoap` + * according to any `NANOCOAP_RESOURCE`s present in the binary. See @ref net_nanocoap for more. + * + * To enable this implementation, select + * + * USEMODULE += slipdev_config + * + * @see drivers_slipdev + */ + /** * @defgroup drivers_slipdev SLIP network device * @ingroup drivers_netdev * @brief SLIP network device over @ref drivers_periph_uart - * @see [RFC 1055](https://github.com/RIOT-OS/RIOT/pull/6487) + * @see [RFC 1055](https://datatracker.ietf.org/doc/html/rfc1055) + * * @{ * * @file @@ -42,6 +67,7 @@ #include "net/netdev.h" #include "periph/uart.h" #include "chunked_ringbuffer.h" +#include "sched.h" #ifdef __cplusplus extern "C" { @@ -93,6 +119,14 @@ enum { * @brief Device writes received data to stdin, next byte is escaped */ SLIPDEV_STATE_STDIN_ESC, + /** + * @brief Device writes received data as CoAP message + */ + SLIPDEV_STATE_CONFIG, + /** + * @brief Device writes received data as CoAP message, next byte is escaped + */ + SLIPDEV_STATE_CONFIG_ESC, /** * @brief Device is in standby, will wake up when sending data */ @@ -120,13 +154,19 @@ typedef struct { typedef struct { netdev_t netdev; /**< parent class */ slipdev_params_t config; /**< configuration parameters */ - chunk_ringbuf_t rb; /**< Ringbuffer to store received frames. */ + chunk_ringbuf_t rb; /**< Ringbuffer to store received networking frames.*/ /* Written to from interrupts (with irq_disable */ /* to prevent any simultaneous writes), */ /* consumed exclusively in the network stack's */ /* loop at _isr. */ uint8_t rxmem[CONFIG_SLIPDEV_BUFSIZE]; /**< memory used by RX buffer */ + +#if IS_USED(MODULE_SLIPDEV_CONFIG) + chunk_ringbuf_t rb_config; /**< Ringbuffer stores received configuration frames */ + uint8_t rxmem_config[CONFIG_SLIPDEV_BUFSIZE]; /**< memory used by RX buffer */ + kernel_pid_t coap_server_pid; /**< The PID of the CoAP server */ +#endif /** * @brief Device state * @see [Device state definitions](@ref drivers_slipdev_states) diff --git a/drivers/slipdev/Makefile.dep b/drivers/slipdev/Makefile.dep index a91b6e3e2a..f738e8b960 100644 --- a/drivers/slipdev/Makefile.dep +++ b/drivers/slipdev/Makefile.dep @@ -7,3 +7,10 @@ FEATURES_REQUIRED += periph_uart ifneq (,$(filter slipdev_stdio,$(USEMODULE))) USEMODULE += isrpipe endif +ifneq (,$(filter slipdev_config,$(USEMODULE))) + USEMODULE += checksum + USEMODULE += sock_udp + USEMODULE += gnrc_ipv6 + USEMODULE += nanocoap_sock + USEMODULE += nanocoap_resources +endif diff --git a/drivers/slipdev/include/slipdev_internal.h b/drivers/slipdev/include/slipdev_internal.h index b53e7c8a2c..822bfdf1d7 100644 --- a/drivers/slipdev/include/slipdev_internal.h +++ b/drivers/slipdev/include/slipdev_internal.h @@ -43,9 +43,16 @@ extern "C" { /** * @brief Marker byte for beginning of stdio * @see taken from diagnostic transfer from - * [SLIPMUX](https://tools.ietf.org/html/draft-bormann-t2trg-slipmux-02#section-4) + * [SLIPMUX](https://tools.ietf.org/html/draft-bormann-t2trg-slipmux-03#section-4) */ #define SLIPDEV_STDIO_START (0x0aU) + +/** + * @brief Marker byte for beginning of configuration/CoAP + * @see taken from configuration from + * [SLIPMUX](https://tools.ietf.org/html/draft-bormann-t2trg-slipmux-03#section-5) + */ +#define SLIPDEV_CONFIG_START (0xa9U) /** @} */ /** diff --git a/drivers/slipdev/slipdev.c b/drivers/slipdev/slipdev.c index a2ef698b4b..97bed93c0b 100644 --- a/drivers/slipdev/slipdev.c +++ b/drivers/slipdev/slipdev.c @@ -12,6 +12,7 @@ * @file * @author Martine Lenders * @author Benjamin Valentin + * @author Bennet Hattesen */ #include @@ -31,20 +32,37 @@ #include "isrpipe.h" #include "mutex.h" +#if IS_USED(MODULE_SLIPDEV_CONFIG) +#include "checksum/crc16_ccitt.h" +#include "net/nanocoap.h" +#endif #include "stdio_uart.h" +#if (IS_USED(MODULE_SLIPDEV_STDIO) || IS_USED(MODULE_SLIPDEV_CONFIG)) +/* For synchronization with stdio/config threads */ +mutex_t slipdev_mutex = MUTEX_INIT; +#endif + +#if IS_USED(MODULE_SLIPDEV_CONFIG) +/* The special init is the result of normal fcs init combined with slipmux config start (0xa9) */ +#define SPECIAL_INIT_FCS (0x374cU) +#define COAP_STACKSIZE (1024) + +static char coap_stack[COAP_STACKSIZE]; +#endif /* IS_USED(MODULE_SLIPDEV_CONFIG) */ + static int _check_state(slipdev_t *dev); static inline void slipdev_lock(void) { - if (IS_USED(MODULE_SLIPDEV_STDIO)) { + if (IS_USED(MODULE_SLIPDEV_STDIO) || IS_USED(MODULE_SLIPDEV_CONFIG)) { mutex_lock(&slipdev_mutex); } } static inline void slipdev_unlock(void) { - if (IS_USED(MODULE_SLIPDEV_STDIO)) { + if (IS_USED(MODULE_SLIPDEV_STDIO) || IS_USED(MODULE_SLIPDEV_CONFIG)) { mutex_unlock(&slipdev_mutex); } } @@ -54,7 +72,6 @@ static void _slip_rx_cb(void *arg, uint8_t byte) slipdev_t *dev = arg; switch (dev->state) { -#if IS_USED(MODULE_SLIPDEV_STDIO) case SLIPDEV_STATE_STDIN: switch (byte) { case SLIPDEV_ESC: @@ -64,7 +81,11 @@ static void _slip_rx_cb(void *arg, uint8_t byte) dev->state = SLIPDEV_STATE_NONE; break; default: - isrpipe_write_one(&stdin_isrpipe, byte); +#if IS_USED(MODULE_SLIPDEV_STDIO) + if (dev->config.uart == STDIO_UART_DEV) { + isrpipe_write_one(&stdin_isrpipe, byte); + } +#endif break; } return; @@ -78,24 +99,81 @@ static void _slip_rx_cb(void *arg, uint8_t byte) break; } dev->state = SLIPDEV_STATE_STDIN; - isrpipe_write_one(&stdin_isrpipe, byte); - return; +#if IS_USED(MODULE_SLIPDEV_STDIO) + if (dev->config.uart == STDIO_UART_DEV) { + isrpipe_write_one(&stdin_isrpipe, byte); + } #endif + return; + case SLIPDEV_STATE_CONFIG: + switch (byte) { + case SLIPDEV_ESC: + dev->state = SLIPDEV_STATE_CONFIG_ESC; + break; + case SLIPDEV_END: + dev->state = SLIPDEV_STATE_NONE; +#if IS_USED(MODULE_SLIPDEV_CONFIG) + crb_end_chunk(&dev->rb_config, true); + thread_flags_set(thread_get(dev->coap_server_pid), 1); +#endif + break; + default: +#if IS_USED(MODULE_SLIPDEV_CONFIG) + /* discard frame if byte can't be added */ + if (!crb_add_byte(&dev->rb_config, byte)) { + DEBUG("slipdev: rx buffer full, drop frame\n"); + crb_end_chunk(&dev->rb_config, false); + dev->state = SLIPDEV_STATE_NONE; + return; + } +#endif + break; + } + return; + case SLIPDEV_STATE_CONFIG_ESC: + switch (byte) { + case SLIPDEV_END_ESC: + byte = SLIPDEV_END; + break; + case SLIPDEV_ESC_ESC: + byte = SLIPDEV_ESC; + break; + } +#if IS_USED(MODULE_SLIPDEV_CONFIG) + /* discard frame if byte can't be added */ + if (!crb_add_byte(&dev->rb_config, byte)) { + DEBUG("slipdev: rx buffer full, drop frame\n"); + crb_end_chunk(&dev->rb_config, false); + dev->state = SLIPDEV_STATE_NONE; + return; + } +#endif + dev->state = SLIPDEV_STATE_CONFIG; + return; case SLIPDEV_STATE_NONE: /* is diagnostic frame? */ - if (IS_USED(MODULE_SLIPDEV_STDIO) && - (byte == SLIPDEV_STDIO_START) && - (dev->config.uart == STDIO_UART_DEV)) { + if (byte == SLIPDEV_STDIO_START) { dev->state = SLIPDEV_STATE_STDIN; return; } + if (byte == SLIPDEV_CONFIG_START) { +#if IS_USED(MODULE_SLIPDEV_CONFIG) + /* try to create new frame */ + if (!crb_start_chunk(&dev->rb_config)) { + return; + } +#endif + dev->state = SLIPDEV_STATE_CONFIG; + return; + } + /* ignore empty frame */ if (byte == SLIPDEV_END) { return; } - /* try to create new frame */ + /* try to create new ip frame */ if (!crb_start_chunk(&dev->rb)) { return; } @@ -198,8 +276,8 @@ void slipdev_write_bytes(uart_t uart, const uint8_t *data, size_t len) static int _check_state(slipdev_t *dev) { - /* power states not supported when multiplexing stdio */ - if (IS_USED(MODULE_SLIPDEV_STDIO)) { + /* power states not supported when multiplexing stdio / configuration */ + if (IS_USED(MODULE_SLIPDEV_STDIO) || IS_USED(MODULE_SLIPDEV_CONFIG)) { return 0; } @@ -272,7 +350,7 @@ static void _isr(netdev_t *netdev) } } -#if !IS_USED(MODULE_SLIPDEV_STDIO) +#if !(IS_USED(MODULE_SLIPDEV_STDIO) || IS_USED(MODULE_SLIPDEV_CONFIG)) static int _set_state(slipdev_t *dev, netopt_state_t state) { if (IS_USED(MODULE_SLIPDEV_STDIO)) { @@ -309,7 +387,7 @@ static int _set(netdev_t *netdev, netopt_t opt, const void *value, size_t max_le return -ENOTSUP; } } -#endif /* !MODULE_SLIPDEV_STDIO */ +#endif /* !(MODULE_SLIPDEV_STDIO || MODULE_SLIPDEV_CONFIG) */ static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len) { @@ -348,15 +426,69 @@ static const netdev_driver_t slip_driver = { .isr = _isr, .get = _get, .confirm_send = _confirm_send, -#if IS_USED(MODULE_SLIPDEV_STDIO) +#if (IS_USED(MODULE_SLIPDEV_STDIO) || IS_USED(MODULE_SLIPDEV_CONFIG)) .set = netdev_set_notsup, #else .set = _set, #endif }; +#if IS_USED(MODULE_SLIPDEV_CONFIG) +static void *_coap_server_thread(void *arg) +{ + static uint8_t buf[512]; + slipdev_t *dev = arg; + while (1) { + thread_flags_wait_any(1); + size_t len; + while (crb_get_chunk_size(&dev->rb_config, &len)) { + crb_consume_chunk(&dev->rb_config, buf, len); + + /* Is the crc correct via residue(=0xF0B8) test */ + if (crc16_ccitt_fcs_update(SPECIAL_INIT_FCS, buf, len) != 0xF0B8) { + break; + } + + /* cut off the FCS checksum at the end */ + size_t pktlen = len - 2; + + coap_pkt_t pkt; + sock_udp_ep_t remote; + coap_request_ctx_t ctx = { + .remote = &remote, + }; + if (coap_parse(&pkt, buf, pktlen) < 0) { + break; + } + unsigned int res = 0; + if ((res = coap_handle_req(&pkt, buf, sizeof(buf), &ctx)) <= 0) { + break; + } + + uint16_t fcs_sum = crc16_ccitt_fcs_finish(SPECIAL_INIT_FCS, buf, res); + + slipdev_lock(); + slipdev_write_byte(dev->config.uart, SLIPDEV_CONFIG_START); + slipdev_write_bytes(dev->config.uart, buf, res); + slipdev_write_bytes(dev->config.uart, (uint8_t *) &fcs_sum, 2); + slipdev_write_byte(dev->config.uart, SLIPDEV_END); + slipdev_unlock(); + } + } + + return NULL; +} +#endif /* MODULE_SLIPDEV_CONFIG */ + void slipdev_setup(slipdev_t *dev, const slipdev_params_t *params, uint8_t index) { +#if IS_USED(MODULE_SLIPDEV_CONFIG) + crb_init(&dev->rb_config, dev->rxmem_config, sizeof(dev->rxmem_config)); + + dev->coap_server_pid = thread_create(coap_stack, sizeof(coap_stack), THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, _coap_server_thread, + (void *)dev, "Slipmux CoAP server"); +#endif /* set device descriptor fields */ dev->config = *params; dev->state = 0; diff --git a/drivers/slipdev/stdio.c b/drivers/slipdev/stdio.c index efe336f456..833e26e41b 100644 --- a/drivers/slipdev/stdio.c +++ b/drivers/slipdev/stdio.c @@ -24,8 +24,6 @@ #include "stdio_base.h" #include "stdio_uart.h" -mutex_t slipdev_mutex = MUTEX_INIT; - static void _isrpipe_write(void *arg, uint8_t data) { isrpipe_write_one(arg, (char)data); @@ -34,9 +32,11 @@ static void _isrpipe_write(void *arg, uint8_t data) static void _init(void) { /* intentionally overwritten in netdev init so we have stdio before - * the network device is initialized is initialized */ + * the network device is initialized */ uart_init(slipdev_params[0].uart, slipdev_params[0].baudrate, _isrpipe_write, &stdin_isrpipe); + + slipdev_write_byte(slipdev_params[0].uart, SLIPDEV_END); } static ssize_t _write(const void *buffer, size_t len) diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 03e4c7d27f..0e2855ddf4 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -464,6 +464,7 @@ PSEUDOMODULES += shield_llcc68 PSEUDOMODULES += shield_sx1262 PSEUDOMODULES += shield_w5100 PSEUDOMODULES += slipdev_stdio +PSEUDOMODULES += slipdev_config PSEUDOMODULES += slipdev_l2addr PSEUDOMODULES += sock PSEUDOMODULES += sock_async diff --git a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c index 4c8506dd14..b71f9ff2b6 100644 --- a/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c +++ b/tests/unittests/tests-checksum/tests-checksum-crc16-ccitt-fcs.c @@ -5,7 +5,6 @@ * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ -#include #include #include "embUnit/embUnit.h"