1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/slipdev/slipdev.c

209 lines
5.8 KiB
C
Raw Normal View History

2017-07-18 14:39:03 +02:00
/*
* 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 void _slip_rx_cb(void *arg, uint8_t byte)
2017-07-18 14:39:03 +02:00
{
slipdev_t *dev = arg;
tsrb_add_one(&dev->inbuf, byte);
if ((byte == SLIP_END) && (dev->netdev.event_callback != NULL)) {
dev->netdev.event_callback((netdev_t *)dev, NETDEV_EVENT_ISR);
2017-07-18 14:39:03 +02:00
}
}
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 */
tsrb_init(&dev->inbuf, dev->rxmem, sizeof(dev->rxmem));
2017-07-18 14:39:03 +02:00
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 iolist_t *iolist)
2017-07-18 14:39:03 +02:00
{
slipdev_t *dev = (slipdev_t *)netdev;
int bytes = 0;
DEBUG("slipdev: sending iolist\n");
for (const iolist_t *iol = iolist; iol; iol = iol->iol_next) {
uint8_t *data = iol->iol_base;
2017-07-18 14:39:03 +02:00
for (unsigned j = 0; j < iol->iol_len; j++, data++) {
2017-07-18 14:39:03 +02:00
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 = 0;
2017-07-18 14:39:03 +02:00
(void)info;
if (buf == NULL) {
if (len > 0) {
/* remove data */
for (; len > 0; len--) {
int byte = tsrb_get_one(&dev->inbuf);
if ((byte == (int)SLIP_END) || (byte < 0)) {
/* end early if end of packet or ringbuffer is reached;
* len might be larger than the actual packet */
break;
}
}
} else {
/* the user was warned not to use a buffer size > `INT_MAX` ;-) */
res = (int)tsrb_avail(&dev->inbuf);
2017-07-18 14:39:03 +02:00
}
}
else {
int byte;
uint8_t *ptr = buf;
do {
if ((byte = tsrb_get_one(&dev->inbuf)) < 0) {
/* something went wrong, return error */
return -EIO;
}
switch (byte) {
case SLIP_END:
break;
case SLIP_ESC:
dev->inesc = 1;
break;
case SLIP_END_ESC:
if (dev->inesc) {
*(ptr++) = SLIP_END;
res++;
dev->inesc = 0;
break;
}
/* Intentionally falls through */
/* to default when !dev->inesc */
case SLIP_ESC_ESC:
if (dev->inesc) {
*(ptr++) = SLIP_ESC;
res++;
dev->inesc = 0;
break;
}
/* Intentionally falls through */
/* to default when !dev->inesc */
default:
*(ptr++) = (uint8_t)byte;
res++;
break;
}
if ((unsigned)res > len) {
while (byte != SLIP_END) {
/* clear out unreceived packet */
byte = tsrb_get_one(&dev->inbuf);
}
return -ENOBUFS;
}
} while (byte != SLIP_END);
2017-07-18 14:39:03 +02:00
}
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 const netdev_driver_t slip_driver = {
.send = _send,
.recv = _recv,
.init = _init,
.isr = _isr,
.get = _get,
.set = netdev_set_notsup,
};
void slipdev_setup(slipdev_t *dev, const slipdev_params_t *params)
{
/* set device descriptor fields */
memcpy(&dev->config, params, sizeof(dev->config));
dev->inesc = 0U;
dev->netdev.driver = &slip_driver;
}
2017-07-18 14:39:03 +02:00
/** @} */