mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
slip: port to be used with netdev
This commit is contained in:
parent
9368b1deb6
commit
b74ee8869b
@ -377,10 +377,6 @@ ifneq (,$(filter gnrc_pktdump,$(USEMODULE)))
|
||||
USEMODULE += od
|
||||
endif
|
||||
|
||||
ifneq (,$(filter gnrc_slip,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_uart
|
||||
endif
|
||||
|
||||
ifneq (,$(filter od,$(USEMODULE)))
|
||||
USEMODULE += fmt
|
||||
endif
|
||||
|
2
dist/tools/tunslip/README.md
vendored
2
dist/tools/tunslip/README.md
vendored
@ -1,7 +1,7 @@
|
||||
Creating a SLIP network interface
|
||||
=================================
|
||||
|
||||
The module `gnrc_slip` (Serial line IP) enables the RIOT network stack to
|
||||
The module `slipdev` (Serial line IP) enables the RIOT network stack to
|
||||
communicate IP packets over the serial interface. This collection of tools
|
||||
originally from Contiki [1] enables Linux to interpret this data. Though there
|
||||
is a tool for such operations on Linux (`slattach`) it is only able to handle
|
||||
|
@ -246,6 +246,10 @@ ifneq (,$(filter lsm6dsl,$(USEMODULE)))
|
||||
USEMODULE += xtimer
|
||||
endif
|
||||
|
||||
ifneq (,$(filter slipdev,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_uart
|
||||
endif
|
||||
|
||||
ifneq (,$(filter adc%1c,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_gpio
|
||||
FEATURES_REQUIRED += periph_i2c
|
||||
|
@ -213,7 +213,8 @@ enum {
|
||||
NETDEV_TYPE_IEEE802154,
|
||||
NETDEV_TYPE_CC110X,
|
||||
NETDEV_TYPE_LORA,
|
||||
NETDEV_TYPE_NRFMIN
|
||||
NETDEV_TYPE_NRFMIN,
|
||||
NETDEV_TYPE_SLIP,
|
||||
};
|
||||
|
||||
/**
|
||||
|
95
drivers/include/slipdev.h
Normal file
95
drivers/include/slipdev.h
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2015-17 Freie Universität Berlin
|
||||
*
|
||||
* 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_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)
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SLIP device definitions
|
||||
*
|
||||
* @author Martine Lenders <m.lenders@fu-berlin.de>
|
||||
*/
|
||||
#ifndef SLIPDEV_H
|
||||
#define SLIPDEV_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "cib.h"
|
||||
#include "net/netdev.h"
|
||||
#include "periph/uart.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART buffer size used for TX and RX buffers
|
||||
*
|
||||
* Reduce this value if your expected traffic does not include full IPv6 MTU
|
||||
* sized packets
|
||||
*/
|
||||
#ifndef SLIPDEV_BUFSIZE
|
||||
#define SLIPDEV_BUFSIZE (1500U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Packet FIFO size
|
||||
*
|
||||
* @note For GNRC it is recommended to have it the same size as the link-layer
|
||||
* thread's message queue, but it MUST be of power of 2
|
||||
*/
|
||||
#ifndef SLIPDEV_PKTFIFO_SIZE
|
||||
#define SLIPDEV_PKTFIFO_SIZE (8U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configuration parameters for a slipdev
|
||||
*/
|
||||
typedef struct {
|
||||
uart_t uart; /**< UART interface the device is connected to */
|
||||
uint32_t baudrate; /**< baudrate to use with slipdev_params_t::uart */
|
||||
} slipdev_params_t;
|
||||
|
||||
/**
|
||||
* @brief Device descriptor for slipdev
|
||||
*
|
||||
* @extends netdev_t
|
||||
*/
|
||||
typedef struct {
|
||||
netdev_t netdev; /**< parent class */
|
||||
slipdev_params_t config; /**< configuration parameters */
|
||||
ringbuffer_t inbuf; /**< RX buffer */
|
||||
char rxmem[SLIPDEV_BUFSIZE]; /**< memory used by RX buffer */
|
||||
uint16_t pktfifo[SLIPDEV_PKTFIFO_SIZE]; /**< FIFO of sizes of fully received
|
||||
* packets */
|
||||
cib_t pktfifo_idx; /**< CIB for slipdev_t::pktfifo */
|
||||
uint16_t inbytes; /**< the number of bytes received of
|
||||
* a currently incoming packet */
|
||||
uint16_t inesc; /**< device previously received an escape
|
||||
* byte */
|
||||
} slipdev_t;
|
||||
|
||||
/**
|
||||
* @brief Setup a slipdev device state
|
||||
*
|
||||
* @param[in] dev device descriptor
|
||||
* @param[in] params parameters for device initialization
|
||||
*/
|
||||
void slipdev_setup(slipdev_t *dev, const slipdev_params_t *params);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SLIPDEV_H */
|
||||
/** @} */
|
@ -1,3 +1 @@
|
||||
MODULE = gnrc_slip
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
222
drivers/slipdev/slipdev.c
Normal file
222
drivers/slipdev/slipdev.c
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Freie Universität Berlin
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "slipdev.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define SLIP_END (0xc0U)
|
||||
#define SLIP_ESC (0xdbU)
|
||||
#define SLIP_END_ESC (0xdcU)
|
||||
#define SLIP_ESC_ESC (0xddU)
|
||||
|
||||
static int _send(netdev_t *dev, const struct iovec *vector, unsigned count);
|
||||
static int _recv(netdev_t *dev, void *buf, size_t len, void *info);
|
||||
static int _init(netdev_t *dev);
|
||||
static void _isr(netdev_t *dev);
|
||||
static int _get(netdev_t *dev, netopt_t opt, void *value, size_t max_len);
|
||||
static int _set(netdev_t *dev, netopt_t opt, const void *value,
|
||||
size_t value_len);
|
||||
|
||||
static const netdev_driver_t slip_driver = {
|
||||
.send = _send,
|
||||
.recv = _recv,
|
||||
.init = _init,
|
||||
.isr = _isr,
|
||||
.get = _get,
|
||||
.set = _set,
|
||||
};
|
||||
|
||||
void slipdev_setup(slipdev_t *dev, const slipdev_params_t *params)
|
||||
{
|
||||
/* set device descriptor fields */
|
||||
memcpy(&dev->config, params, sizeof(dev->config));
|
||||
dev->inbytes = 0U;
|
||||
dev->inesc = 0U;
|
||||
dev->netdev.driver = &slip_driver;
|
||||
}
|
||||
|
||||
static inline void _add_byte_to_inbuf(slipdev_t *dev, uint8_t byte)
|
||||
{
|
||||
if (ringbuffer_add_one(&dev->inbuf, byte) < 0) {
|
||||
dev->inbytes++;
|
||||
}
|
||||
}
|
||||
|
||||
static void _slip_rx_cb(void *arg, uint8_t data)
|
||||
{
|
||||
slipdev_t *dev = arg;
|
||||
|
||||
if ((data == SLIP_END) && (dev->netdev.event_callback != NULL)) {
|
||||
int idx = cib_put(&dev->pktfifo_idx);
|
||||
if (idx >= 0) {
|
||||
dev->netdev.event_callback((netdev_t *)dev, NETDEV_EVENT_ISR);
|
||||
dev->pktfifo[idx] = dev->inbytes;
|
||||
}
|
||||
else {
|
||||
/* can't handover packet => dropping it */
|
||||
ringbuffer_remove(&dev->inbuf, dev->inbytes);
|
||||
}
|
||||
dev->inbytes = 0;
|
||||
}
|
||||
else if (dev->inesc) {
|
||||
dev->inesc = 0U;
|
||||
uint8_t actual = (data == SLIP_END_ESC) ? SLIP_END :
|
||||
((data == SLIP_ESC_ESC) ? SLIP_ESC : 0);
|
||||
|
||||
switch (data) {
|
||||
case SLIP_END_ESC:
|
||||
case SLIP_ESC_ESC:
|
||||
_add_byte_to_inbuf(dev, actual);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (data == SLIP_ESC) {
|
||||
dev->inesc = 1U;
|
||||
}
|
||||
else {
|
||||
_add_byte_to_inbuf(dev, data);
|
||||
}
|
||||
}
|
||||
|
||||
static int _init(netdev_t *netdev)
|
||||
{
|
||||
slipdev_t *dev = (slipdev_t *)netdev;
|
||||
|
||||
DEBUG("slipdev: initializing device %p on UART %i with baudrate %" PRIu32 "\n",
|
||||
(void *)dev, dev->config.uart, dev->config.baudrate);
|
||||
/* initialize buffers */
|
||||
ringbuffer_init(&dev->inbuf, dev->rxmem, sizeof(dev->rxmem));
|
||||
cib_init(&dev->pktfifo_idx, SLIPDEV_PKTFIFO_SIZE);
|
||||
if (uart_init(dev->config.uart, dev->config.baudrate, _slip_rx_cb,
|
||||
dev) != UART_OK) {
|
||||
LOG_ERROR("slipdev: error initializing UART %i with baudrate %" PRIu32 "\n",
|
||||
dev->config.uart, dev->config.baudrate);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void _write_byte(slipdev_t *dev, uint8_t byte)
|
||||
{
|
||||
uart_write(dev->config.uart, &byte, 1);
|
||||
}
|
||||
|
||||
static int _send(netdev_t *netdev, const struct iovec *vector, unsigned count)
|
||||
{
|
||||
slipdev_t *dev = (slipdev_t *)netdev;
|
||||
int bytes = 0;
|
||||
|
||||
DEBUG("slipdev: sending vector of length %u\n", count);
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
uint8_t *data = vector[i].iov_base;
|
||||
|
||||
for (unsigned j = 0; j < vector[i].iov_len; j++, data++) {
|
||||
switch(*data) {
|
||||
case SLIP_END:
|
||||
/* escaping END byte*/
|
||||
_write_byte(dev, SLIP_ESC);
|
||||
_write_byte(dev, SLIP_END_ESC);
|
||||
break;
|
||||
case SLIP_ESC:
|
||||
/* escaping ESC byte*/
|
||||
_write_byte(dev, SLIP_ESC);
|
||||
_write_byte(dev, SLIP_ESC_ESC);
|
||||
break;
|
||||
default:
|
||||
_write_byte(dev, *data);
|
||||
}
|
||||
bytes++;
|
||||
}
|
||||
}
|
||||
_write_byte(dev, SLIP_END);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
static int _recv(netdev_t *netdev, void *buf, size_t len, void *info)
|
||||
{
|
||||
slipdev_t *dev = (slipdev_t *)netdev;
|
||||
int res, idx = cib_peek(&dev->pktfifo_idx);
|
||||
|
||||
(void)info;
|
||||
if (idx < 0) {
|
||||
return -EFAULT;
|
||||
}
|
||||
if (buf == NULL) {
|
||||
if (len > 0) {
|
||||
/* drop packet */
|
||||
cib_get(&dev->pktfifo_idx);
|
||||
/* and remove data */
|
||||
res = ringbuffer_remove(&dev->inbuf, len);
|
||||
}
|
||||
else {
|
||||
res = dev->pktfifo[idx];
|
||||
}
|
||||
}
|
||||
else if (len < dev->pktfifo[idx]) {
|
||||
res = -ENOBUFS;
|
||||
}
|
||||
else {
|
||||
size_t bytes = dev->pktfifo[cib_get(&dev->pktfifo_idx)];
|
||||
bytes = ringbuffer_get(&dev->inbuf, buf, bytes);
|
||||
res = bytes;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _isr(netdev_t *netdev)
|
||||
{
|
||||
DEBUG("slipdev: handling ISR event\n");
|
||||
if (netdev->event_callback != NULL) {
|
||||
DEBUG("slipdev: event handler set, issuing RX_COMPLETE event\n");
|
||||
netdev->event_callback(netdev, NETDEV_EVENT_RX_COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
static int _get(netdev_t *netdev, netopt_t opt, void *value, size_t max_len)
|
||||
{
|
||||
(void)netdev;
|
||||
(void)value;
|
||||
(void)max_len;
|
||||
switch (opt) {
|
||||
case NETOPT_IS_WIRED:
|
||||
return 1;
|
||||
case NETOPT_DEVICE_TYPE:
|
||||
assert(max_len == sizeof(uint16_t));
|
||||
*((uint16_t *)value) = NETDEV_TYPE_SLIP;
|
||||
return sizeof(uint16_t);
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static int _set(netdev_t *netdev, netopt_t opt, const void *value,
|
||||
size_t value_len)
|
||||
{
|
||||
(void)netdev;
|
||||
(void)opt;
|
||||
(void)value;
|
||||
(void)value_len;
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/** @} */
|
@ -48,7 +48,7 @@ USEMODULE += gnrc_rpl
|
||||
|
||||
# Border router requirements
|
||||
# Include SLIP package for IP over Serial communication
|
||||
USEMODULE += gnrc_slip
|
||||
USEMODULE += slipdev
|
||||
# Specify the mandatory networking modules for 6LoWPAN border router
|
||||
USEMODULE += gnrc_sixlowpan_border_router_default
|
||||
# Additional networking modules that can be dropped if not needed
|
||||
|
@ -16,16 +16,16 @@
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef SLIP_PARAMS_H
|
||||
#define SLIP_PARAMS_H
|
||||
#ifndef SLIPDEV_PARAMS_H
|
||||
#define SLIPDEV_PARAMS_H
|
||||
|
||||
#include "net/gnrc/slip.h"
|
||||
#include "slipdev.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static gnrc_slip_params_t gnrc_slip_params[] = {
|
||||
static slipdev_params_t slipdev_params[] = {
|
||||
{
|
||||
.uart = SLIP_UART,
|
||||
.baudrate = SLIP_BAUDRATE,
|
||||
@ -35,5 +35,5 @@ static gnrc_slip_params_t gnrc_slip_params[] = {
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SLIP_PARAMS_H */
|
||||
#endif /* SLIPDEV_PARAMS_H */
|
||||
/** @} */
|
@ -47,7 +47,7 @@ endif
|
||||
#CFLAGS += -DSLIP_UART=$(SLIP_UART)
|
||||
#CFLAGS += -DSLIP_BAUDRATE=$(SLIP_BAUDRATE)
|
||||
# Include SLIP package for IP over Serial communication
|
||||
#USEMODULE += gnrc_slip
|
||||
#USEMODULE += slipdev
|
||||
|
||||
# Include packages that pull up and auto-init the link layer.
|
||||
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
|
||||
|
@ -210,7 +210,7 @@ INCLUDES += -I$(CURDIR)
|
||||
CFLAGS += -DSLIP_UART=$(SLIP_UART)
|
||||
CFLAGS += -DSLIP_BAUDRATE=$(SLIP_BAUDRATE)
|
||||
# Include SLIP package for IP over Serial communication
|
||||
USEMODULE += gnrc_slip
|
||||
USEMODULE += slipdev
|
||||
```
|
||||
|
||||
|
||||
|
@ -200,9 +200,9 @@ void auto_init(void)
|
||||
auto_init_ethos();
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_GNRC_SLIP
|
||||
extern void auto_init_slip(void);
|
||||
auto_init_slip();
|
||||
#ifdef MODULE_SLIPDEV
|
||||
extern void auto_init_slipdev(void);
|
||||
auto_init_slipdev();
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_CC110X
|
||||
|
@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* 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 XBee network interfaces
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*/
|
||||
|
||||
#ifdef MODULE_GNRC_SLIP
|
||||
|
||||
#include "log.h"
|
||||
#include "board.h"
|
||||
#include "net/gnrc/netdev.h"
|
||||
#include "net/gnrc.h"
|
||||
|
||||
#include "net/gnrc/slip.h"
|
||||
#include "slip_params.h"
|
||||
|
||||
#define SLIP_NUM (sizeof(gnrc_slip_params)/sizeof(gnrc_slip_params_t))
|
||||
|
||||
static gnrc_slip_dev_t slip_devs[SLIP_NUM];
|
||||
|
||||
/**
|
||||
* @brief Define stack parameters for the MAC layer thread
|
||||
* @{
|
||||
*/
|
||||
#define SLIP_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
|
||||
#ifndef SLIP_PRIO
|
||||
#define SLIP_PRIO (GNRC_NETDEV_MAC_PRIO)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Stacks for the MAC layer threads
|
||||
*/
|
||||
static char _slip_stacks[SLIP_NUM][SLIP_STACKSIZE];
|
||||
|
||||
void auto_init_slip(void)
|
||||
{
|
||||
for (unsigned i = 0; i < SLIP_NUM; i++) {
|
||||
const gnrc_slip_params_t *p = &gnrc_slip_params[i];
|
||||
|
||||
LOG_DEBUG("[auto_init_netif] initializing slip #%u\n", i);
|
||||
|
||||
kernel_pid_t res = gnrc_slip_init(&slip_devs[i], p->uart, p->baudrate,
|
||||
_slip_stacks[i], SLIP_STACKSIZE,
|
||||
SLIP_PRIO);
|
||||
|
||||
if (res <= KERNEL_PID_UNDEF) {
|
||||
LOG_ERROR("[auto_init_netif] error initializing slip #%u\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
typedef int dont_be_pedantic;
|
||||
#endif /* MODULE_GNRC_SLIP */
|
||||
/** @} */
|
69
sys/auto_init/netif/auto_init_slipdev.c
Normal file
69
sys/auto_init/netif/auto_init_slipdev.c
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* 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 XBee network interfaces
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*/
|
||||
|
||||
#ifdef MODULE_SLIPDEV
|
||||
|
||||
#include "log.h"
|
||||
#include "board.h"
|
||||
#include "net/gnrc/netdev.h"
|
||||
#include "net/gnrc/netdev/raw.h"
|
||||
#include "net/gnrc.h"
|
||||
|
||||
#include "slipdev.h"
|
||||
#include "slipdev_params.h"
|
||||
|
||||
#define SLIPDEV_NUM (sizeof(slipdev_params)/sizeof(slipdev_params_t))
|
||||
|
||||
/**
|
||||
* @brief Define stack parameters for the MAC layer thread
|
||||
* @{
|
||||
*/
|
||||
#define SLIPDEV_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
|
||||
#ifndef SLIPDEV_PRIO
|
||||
#define SLIPDEV_PRIO (GNRC_NETDEV_MAC_PRIO)
|
||||
#endif
|
||||
|
||||
static slipdev_t slipdevs[SLIPDEV_NUM];
|
||||
static gnrc_netdev_t gnrc_adpt[SLIPDEV_NUM];
|
||||
static char _slipdev_stacks[SLIPDEV_NUM][SLIPDEV_STACKSIZE];
|
||||
|
||||
void auto_init_slipdev(void)
|
||||
{
|
||||
for (unsigned i = 0; i < SLIPDEV_NUM; i++) {
|
||||
const slipdev_params_t *p = &slipdev_params[i];
|
||||
|
||||
LOG_DEBUG("[auto_init_netif] initializing slip #%u\n", i);
|
||||
|
||||
slipdev_setup(&slipdevs[i], p);
|
||||
kernel_pid_t res = gnrc_netdev_raw_init(&gnrc_adpt[i],
|
||||
(netdev_t *)&slipdevs[i]);
|
||||
|
||||
if (res < 0) {
|
||||
LOG_ERROR("[auto_init_netif] error initializing slipdev #%u\n", i);
|
||||
}
|
||||
else {
|
||||
gnrc_netdev_init(_slipdev_stacks[i], SLIPDEV_STACKSIZE,
|
||||
SLIPDEV_PRIO, "slipdev", &gnrc_adpt[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
typedef int dont_be_pedantic;
|
||||
#endif /* MODULE_SLIPDEV */
|
||||
/** @} */
|
43
sys/include/net/gnrc/netdev/raw.h
Normal file
43
sys/include/net/gnrc/netdev/raw.h
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Freie Universität Berlin
|
||||
*
|
||||
* 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 net_gnrc_netdev
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief netdev gnrc raw (i.e. pure L3) glue code interface
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
#ifndef NET_GNRC_NETDEV_RAW_H
|
||||
#define NET_GNRC_NETDEV_RAW_H
|
||||
|
||||
#include "net/gnrc/netdev.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Initialize gnrc handler for netdev raw L3 devices
|
||||
*
|
||||
* @param[in] gnrc_netdev gnrc_netdev struct to initialize
|
||||
* @param[in] dev network device to handle
|
||||
*
|
||||
* @return 1 on success
|
||||
* @return <=0 on error
|
||||
*/
|
||||
int gnrc_netdev_raw_init(gnrc_netdev_t *gnrc_netdev, netdev_t *dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_GNRC_NETDEV_RAW_H */
|
||||
/** @} */
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* 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 net_gnrc_slip SLIP
|
||||
* @ingroup net_gnrc
|
||||
* @brief Provides a SLIP interface over UART utilizing
|
||||
* @ref drivers_periph_uart.
|
||||
* @see <a href="https://www.ietf.org/rfc/rfc1055">RFC 1055</a>
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SLIP interface defintion
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef NET_GNRC_SLIP_H
|
||||
#define NET_GNRC_SLIP_H
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "net/gnrc.h"
|
||||
#include "periph/uart.h"
|
||||
#include "ringbuffer.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief UART buffer size used for TX and RX buffers
|
||||
*
|
||||
* Reduce this value if your expected traffic does not include full IPv6 MTU
|
||||
* sized packets
|
||||
*/
|
||||
#ifndef GNRC_SLIP_BUFSIZE
|
||||
#define GNRC_SLIP_BUFSIZE (1500U)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Device descriptor for SLIP devices
|
||||
*/
|
||||
typedef struct {
|
||||
uart_t uart; /**< the UART interface */
|
||||
ringbuffer_t in_buf; /**< RX buffer */
|
||||
ringbuffer_t out_buf; /**< TX buffer */
|
||||
char rx_mem[GNRC_SLIP_BUFSIZE]; /**< memory used by RX buffer */
|
||||
uint32_t in_bytes; /**< the number of bytes received of a
|
||||
* currently incoming packet */
|
||||
uint16_t in_esc; /**< receiver is in escape mode */
|
||||
kernel_pid_t slip_pid; /**< PID of the device thread */
|
||||
} gnrc_slip_dev_t;
|
||||
|
||||
/**
|
||||
* @brief auto_init struct holding SLIP initalization params
|
||||
*/
|
||||
typedef struct xbee_params {
|
||||
uart_t uart; /**< UART interfaced the device is connected to */
|
||||
uint32_t baudrate; /**< baudrate to use */
|
||||
} gnrc_slip_params_t;
|
||||
|
||||
/**
|
||||
* @brief Initializes a new @ref net_gnrc_slip control thread for UART device
|
||||
* @p uart
|
||||
*
|
||||
* @param[in] dev un-initialized SLIP device descriptor
|
||||
* @param[in] uart UART device to use
|
||||
* @param[in] baudrate baudrate to use
|
||||
* @param[in] stack stack memory to use for the SLIP devices thread
|
||||
* @param[in] stack_size size of @p stack
|
||||
* @param[in] priority priority for the newly created thread
|
||||
*
|
||||
* @return PID of SLIP thread on success
|
||||
* @return -EFAULT, if slip thread could not be created
|
||||
* @return -ENODEV, if gnrc_slip_dev_t::uart of @p dev was no valid UART
|
||||
*/
|
||||
kernel_pid_t gnrc_slip_init(gnrc_slip_dev_t *dev, uart_t uart, uint32_t baudrate,
|
||||
char *stack, size_t stack_size, char priority);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* NET_GNRC_SLIP_H */
|
||||
/** @} */
|
@ -118,9 +118,6 @@ endif
|
||||
ifneq (,$(filter gnrc_sixlowpan_netif,$(USEMODULE)))
|
||||
DIRS += network_layer/sixlowpan/netif
|
||||
endif
|
||||
ifneq (,$(filter gnrc_slip,$(USEMODULE)))
|
||||
DIRS += link_layer/slip
|
||||
endif
|
||||
ifneq (,$(filter gnrc_sock,$(USEMODULE)))
|
||||
DIRS += sock
|
||||
endif
|
||||
|
99
sys/net/gnrc/link_layer/netdev/gnrc_netdev_raw.c
Normal file
99
sys/net/gnrc/link_layer/netdev/gnrc_netdev_raw.c
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2017 Freie Universität Berlin
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#include "net/gnrc/netdev/raw.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define IP_VERSION_MASK (0xf0)
|
||||
#define IP_VERSION4 (0x40)
|
||||
#define IP_VERSION6 (0x60)
|
||||
|
||||
static gnrc_pktsnip_t *_recv(gnrc_netdev_t *gnrc_netdev)
|
||||
{
|
||||
netdev_t *dev = gnrc_netdev->dev;
|
||||
int bytes_expected = dev->driver->recv(dev, NULL, 0, NULL);
|
||||
gnrc_pktsnip_t *pkt = NULL;
|
||||
|
||||
if (bytes_expected > 0) {
|
||||
int nread;
|
||||
|
||||
pkt = gnrc_pktbuf_add(NULL, NULL, bytes_expected, GNRC_NETTYPE_UNDEF);
|
||||
|
||||
if (!pkt) {
|
||||
DEBUG("gnrc_netdev_raw: cannot allocate pktsnip.\n");
|
||||
/* drop packet */
|
||||
dev->driver->recv(dev, NULL, bytes_expected, NULL);
|
||||
return pkt;
|
||||
}
|
||||
nread = dev->driver->recv(dev, pkt->data, bytes_expected, NULL);
|
||||
if (nread <= 0) {
|
||||
DEBUG("gnrc_netdev_raw: read error.\n");
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return NULL;
|
||||
}
|
||||
if (nread < bytes_expected) {
|
||||
/* we've got less then the expected packet size,
|
||||
* so free the unused space.*/
|
||||
DEBUG("gnrc_netdev_raw: reallocating.\n");
|
||||
gnrc_pktbuf_realloc_data(pkt, nread);
|
||||
}
|
||||
switch (((uint8_t *)pkt->data)[0] & IP_VERSION_MASK) {
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
case IP_VERSION6:
|
||||
pkt->type = GNRC_NETTYPE_IPV6;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* leave UNDEF */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pkt;
|
||||
}
|
||||
|
||||
static int _send(gnrc_netdev_t *gnrc_netdev, gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
gnrc_pktsnip_t *vector;
|
||||
int res = -ENOBUFS;
|
||||
size_t n;
|
||||
|
||||
if (pkt->type == GNRC_NETTYPE_NETIF) {
|
||||
/* we don't need the netif snip: remove it */
|
||||
pkt = gnrc_pktbuf_remove_snip(pkt, pkt);
|
||||
}
|
||||
vector = gnrc_pktbuf_get_iovec(pkt, &n);
|
||||
if (vector != NULL) {
|
||||
struct iovec *v = (struct iovec *)vector->data;
|
||||
netdev_t *dev = gnrc_netdev->dev;
|
||||
|
||||
#ifdef MODULE_NETSTATS_L2
|
||||
gnrc_netdev->dev->stats.tx_unicast_count++;
|
||||
#endif
|
||||
res = dev->driver->send(dev, v, n);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int gnrc_netdev_raw_init(gnrc_netdev_t *gnrc_netdev, netdev_t *dev)
|
||||
{
|
||||
gnrc_netdev->send = _send;
|
||||
gnrc_netdev->recv = _recv;
|
||||
gnrc_netdev->dev = dev;
|
||||
|
||||
return 0;
|
||||
}
|
||||
/** @} */
|
@ -1,277 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*
|
||||
* 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 net_gnrc_slip
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief SLIP device implementation
|
||||
*
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "kernel_types.h"
|
||||
#include "msg.h"
|
||||
#include "net/gnrc.h"
|
||||
#include "periph/uart.h"
|
||||
#include "od.h"
|
||||
#include "ringbuffer.h"
|
||||
#include "thread.h"
|
||||
#include "net/ipv6/hdr.h"
|
||||
|
||||
#include "net/gnrc/slip.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define _SLIP_END ('\xc0')
|
||||
#define _SLIP_ESC ('\xdb')
|
||||
#define _SLIP_END_ESC ('\xdc')
|
||||
#define _SLIP_ESC_ESC ('\xdd')
|
||||
|
||||
#define _SLIP_MSG_TYPE (0xc1dc) /* chosen randomly */
|
||||
#define _SLIP_NAME "SLIP"
|
||||
#define _SLIP_MSG_QUEUE_SIZE (8U)
|
||||
|
||||
#define _SLIP_DEV(arg) ((gnrc_slip_dev_t *)arg)
|
||||
|
||||
/* UART callbacks */
|
||||
static void _slip_rx_cb(void *arg, uint8_t data)
|
||||
{
|
||||
if (data == (uint8_t)_SLIP_END) {
|
||||
msg_t msg;
|
||||
|
||||
msg.type = _SLIP_MSG_TYPE;
|
||||
msg.content.value = _SLIP_DEV(arg)->in_bytes;
|
||||
|
||||
msg_send_int(&msg, _SLIP_DEV(arg)->slip_pid);
|
||||
|
||||
_SLIP_DEV(arg)->in_bytes = 0;
|
||||
}
|
||||
else if (_SLIP_DEV(arg)->in_esc) {
|
||||
_SLIP_DEV(arg)->in_esc = 0;
|
||||
|
||||
switch (data) {
|
||||
case ((uint8_t)_SLIP_END_ESC):
|
||||
if (ringbuffer_add_one(&_SLIP_DEV(arg)->in_buf, _SLIP_END) < 0) {
|
||||
_SLIP_DEV(arg)->in_bytes++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ((uint8_t)_SLIP_ESC_ESC):
|
||||
if (ringbuffer_add_one(&_SLIP_DEV(arg)->in_buf, _SLIP_ESC) < 0) {
|
||||
_SLIP_DEV(arg)->in_bytes++;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (data == (uint8_t)_SLIP_ESC) {
|
||||
_SLIP_DEV(arg)->in_esc = 1;
|
||||
}
|
||||
else {
|
||||
if (ringbuffer_add_one(&_SLIP_DEV(arg)->in_buf, data) < 0) {
|
||||
_SLIP_DEV(arg)->in_bytes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* SLIP receive handler */
|
||||
static void _slip_receive(gnrc_slip_dev_t *dev, size_t bytes)
|
||||
{
|
||||
gnrc_pktsnip_t *pkt, *hdr;
|
||||
|
||||
hdr = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
|
||||
if (hdr == NULL) {
|
||||
DEBUG("slip: no space left in packet buffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
((gnrc_netif_hdr_t *)(hdr->data))->if_pid = thread_getpid();
|
||||
|
||||
pkt = gnrc_pktbuf_add(hdr, NULL, bytes, GNRC_NETTYPE_UNDEF);
|
||||
|
||||
if (pkt == NULL) {
|
||||
DEBUG("slip: no space left in packet buffer\n");
|
||||
gnrc_pktbuf_release(hdr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ringbuffer_get(&dev->in_buf, pkt->data, bytes) != bytes) {
|
||||
DEBUG("slip: could not read %u bytes from ringbuffer\n", (unsigned)bytes);
|
||||
gnrc_pktbuf_release(pkt);
|
||||
return;
|
||||
}
|
||||
#if ENABLE_DEBUG && defined(MODULE_OD)
|
||||
else {
|
||||
DEBUG("slip: received data\n");
|
||||
od_hex_dump(pkt->data, bytes, OD_WIDTH_DEFAULT);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_GNRC_IPV6
|
||||
if ((pkt->size >= sizeof(ipv6_hdr_t)) && ipv6_hdr_is(pkt->data)) {
|
||||
pkt->type = GNRC_NETTYPE_IPV6;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (gnrc_netapi_dispatch_receive(pkt->type, GNRC_NETREG_DEMUX_CTX_ALL, pkt) == 0) {
|
||||
DEBUG("slip: unable to forward packet of type %i\n", pkt->type);
|
||||
gnrc_pktbuf_release(pkt);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _slip_send_char(gnrc_slip_dev_t *dev, char c)
|
||||
{
|
||||
uart_write(dev->uart, (uint8_t *)&c, 1);
|
||||
}
|
||||
|
||||
/* SLIP send handler */
|
||||
static void _slip_send(gnrc_slip_dev_t *dev, gnrc_pktsnip_t *pkt)
|
||||
{
|
||||
gnrc_pktsnip_t *ptr;
|
||||
|
||||
ptr = pkt->next; /* ignore gnrc_netif_hdr_t, we don't need it */
|
||||
|
||||
while (ptr != NULL) {
|
||||
DEBUG("slip: send pktsnip of length %u over UART_%d\n", (unsigned)ptr->size, dev->uart);
|
||||
char *data = ptr->data;
|
||||
|
||||
for (size_t i = 0; i < ptr->size; i++) {
|
||||
switch (data[i]) {
|
||||
case _SLIP_END:
|
||||
DEBUG("slip: encountered END byte on send: stuff with ESC\n");
|
||||
_slip_send_char(dev, _SLIP_ESC);
|
||||
_slip_send_char(dev, _SLIP_END_ESC);
|
||||
break;
|
||||
|
||||
case _SLIP_ESC:
|
||||
DEBUG("slip: encountered ESC byte on send: stuff with ESC\n");
|
||||
_slip_send_char(dev, _SLIP_ESC);
|
||||
_slip_send_char(dev, _SLIP_ESC_ESC);
|
||||
break;
|
||||
|
||||
default:
|
||||
_slip_send_char(dev, data[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
_slip_send_char(dev, _SLIP_END);
|
||||
|
||||
gnrc_pktbuf_release(pkt);
|
||||
}
|
||||
|
||||
static inline int _slip_get(gnrc_netapi_opt_t *opt)
|
||||
{
|
||||
switch (opt->opt) {
|
||||
case NETOPT_IS_WIRED:
|
||||
return 1;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
|
||||
static void *_slip(void *args)
|
||||
{
|
||||
gnrc_slip_dev_t *dev = _SLIP_DEV(args);
|
||||
msg_t msg, reply, msg_q[_SLIP_MSG_QUEUE_SIZE];
|
||||
|
||||
msg_init_queue(msg_q, _SLIP_MSG_QUEUE_SIZE);
|
||||
dev->slip_pid = thread_getpid();
|
||||
gnrc_netif_add(dev->slip_pid);
|
||||
|
||||
DEBUG("slip: SLIP runs on UART_%d\n", dev->uart);
|
||||
|
||||
while (1) {
|
||||
DEBUG("slip: waiting for incoming messages\n");
|
||||
msg_receive(&msg);
|
||||
|
||||
switch (msg.type) {
|
||||
case _SLIP_MSG_TYPE:
|
||||
DEBUG("slip: incoming message of size %" PRIu32 " from UART_%d in buffer\n",
|
||||
msg.content.value, dev->uart);
|
||||
_slip_receive(dev, (size_t)msg.content.value);
|
||||
break;
|
||||
|
||||
case GNRC_NETAPI_MSG_TYPE_SND:
|
||||
DEBUG("slip: GNRC_NETAPI_MSG_TYPE_SND received\n");
|
||||
_slip_send(dev, msg.content.ptr);
|
||||
break;
|
||||
|
||||
case GNRC_NETAPI_MSG_TYPE_GET:
|
||||
DEBUG("slip: GNRC_NETAPI_MSG_TYPE_GET received\n");
|
||||
reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
|
||||
reply.content.value = (uint32_t)_slip_get(msg.content.ptr);
|
||||
msg_reply(&msg, &reply);
|
||||
break;
|
||||
|
||||
case GNRC_NETAPI_MSG_TYPE_SET:
|
||||
DEBUG("slip: GNRC_NETAPI_MSG_TYPE_SET received\n");
|
||||
reply.type = GNRC_NETAPI_MSG_TYPE_ACK;
|
||||
reply.content.value = (uint32_t)(-ENOTSUP);
|
||||
DEBUG("slip: I don't support this but have to reply.\n");
|
||||
msg_reply(&msg, &reply);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* should be never reached */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
kernel_pid_t gnrc_slip_init(gnrc_slip_dev_t *dev, uart_t uart, uint32_t baudrate,
|
||||
char *stack, size_t stack_size, char priority)
|
||||
{
|
||||
kernel_pid_t pid;
|
||||
|
||||
/* reset device descriptor fields */
|
||||
dev->uart = uart;
|
||||
dev->in_bytes = 0;
|
||||
dev->in_esc = 0;
|
||||
dev->slip_pid = KERNEL_PID_UNDEF;
|
||||
|
||||
/* initialize buffers */
|
||||
ringbuffer_init(&dev->in_buf, dev->rx_mem, sizeof(dev->rx_mem));
|
||||
|
||||
/* initialize UART */
|
||||
DEBUG("slip: initialize UART_%d with baudrate %" PRIu32 "\n", uart,
|
||||
baudrate);
|
||||
if (uart_init(uart, baudrate, _slip_rx_cb, dev) != UART_OK) {
|
||||
DEBUG("slip: error initializing UART_%i with baudrate %" PRIu32 "\n",
|
||||
uart, baudrate);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* start SLIP thread */
|
||||
DEBUG("slip: starting SLIP thread\n");
|
||||
pid = thread_create(stack, stack_size, priority, THREAD_CREATE_STACKTEST,
|
||||
_slip, dev, _SLIP_NAME);
|
||||
if (pid < 0) {
|
||||
DEBUG("slip: unable to create SLIP thread\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
return pid;
|
||||
}
|
@ -10,7 +10,9 @@ BOARD_BLACKLIST += mips-malta
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
USEMODULE += gnrc
|
||||
USEMODULE += gnrc_pktdump
|
||||
USEMODULE += gnrc_slip
|
||||
USEMODULE += gnrc_netdev
|
||||
USEMODULE += gnrc_txtsnd
|
||||
USEMODULE += slipdev
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
|
||||
|
@ -16,16 +16,16 @@
|
||||
* @author Martine Lenders <mlenders@inf.fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef SLIP_PARAMS_H
|
||||
#define SLIP_PARAMS_H
|
||||
#ifndef SLIPDEV_PARAMS_H
|
||||
#define SLIPDEV_PARAMS_H
|
||||
|
||||
#include "net/gnrc/slip.h"
|
||||
#include "slipdev.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static gnrc_slip_params_t gnrc_slip_params[] = {
|
||||
static slipdev_params_t slipdev_params[] = {
|
||||
{
|
||||
.uart = SLIP_UART,
|
||||
.baudrate = SLIP_BAUDRATE,
|
||||
@ -35,5 +35,5 @@ static gnrc_slip_params_t gnrc_slip_params[] = {
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* SLIP_PARAMS_H */
|
||||
#endif /* SLIPDEV_PARAMS_H */
|
||||
/** @} */
|
Loading…
Reference in New Issue
Block a user