1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 06:23:53 +01:00

Merge pull request #21418 from Teufelchen1/feat/slipmux_c

drivers/slipmux: Add to RIOT
This commit is contained in:
Teufelchen 2025-07-07 09:50:29 +00:00 committed by GitHub
commit b2406da63f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 208 additions and 22 deletions

View File

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

View File

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

View File

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

View File

@ -12,6 +12,7 @@
* @file
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
* @author Bennet Hattesen <bennet.hattesen@haw-hamburg.de>
*/
#include <assert.h>
@ -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;

View File

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

View File

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

View File

@ -5,7 +5,6 @@
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
#include <stdio.h>
#include <stdint.h>
#include "embUnit/embUnit.h"