zephyr/drivers/slip/slip.c
Tomasz Bursztyka b8e3415264 net: Let's use inline function for type checking for net_nbuf
Using macros does not let the compiler verifying about the type we are
providing, which usually give an error easier to understand.
Also, this will let the compiler deciding how to actually optimize
(inline or not) the code.

Change-Id: Iba49590b620ef0a1bd0ed5621453524fcfea747c
Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
2016-12-02 12:40:13 +02:00

432 lines
8.9 KiB
C

/* slip.c - SLIP driver using uart_pipe. This is meant for
* network connectivity between host and qemu. The host will
* need to run tunslip process.
*/
/*
* Copyright (c) 2016 Intel Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(CONFIG_SLIP_DEBUG)
#define SYS_LOG_DOMAIN "slip"
#define SYS_LOG_LEVEL SYS_LOG_LEVEL_DEBUG
#include <misc/sys_log.h>
#include <stdio.h>
#endif
#include <nanokernel.h>
#include <stdbool.h>
#include <errno.h>
#include <stddef.h>
#include <misc/util.h>
#include <net/buf.h>
#include <net/nbuf.h>
#include <net/net_if.h>
#include <net/arp.h>
#include <net/net_core.h>
#include <console/uart_pipe.h>
#define SLIP_END 0300
#define SLIP_ESC 0333
#define SLIP_ESC_END 0334
#define SLIP_ESC_ESC 0335
enum slip_state {
STATE_MULTI_PACKETS,
STATE_GARBAGE,
STATE_OK,
STATE_ESC,
};
struct slip_context {
bool init_done;
uint8_t buf[1]; /* SLIP data is read into this buf */
struct net_buf *rx; /* and then placed into this net_buf */
struct net_buf *last; /* Pointer to last fragment in the list */
uint8_t *ptr; /* Where in net_buf to add data */
uint8_t state;
uint16_t ll_reserve; /* Reserve any space for link layer headers */
uint8_t mac_addr[6];
struct net_linkaddr ll_addr;
int mtu;
#if defined(CONFIG_SLIP_STATISTICS)
#define SLIP_STATS(statement)
#else
uint16_t garbage;
uint16_t multi_packets;
uint16_t overflows;
uint16_t ip_drop;
#define SLIP_STATS(statement) statement
#endif
};
#if defined(CONFIG_SLIP_DEBUG)
static void hexdump(const char *str, const uint8_t *packet, size_t length)
{
int n = 0;
if (!length) {
SYS_LOG_DBG("%s zero-length packet", str);
return;
}
while (length--) {
if (n % 16 == 0) {
printf("%s %08X ", str, n);
}
printf("%02X ", *packet++);
n++;
if (n % 8 == 0) {
if (n % 16 == 0) {
printf("\n");
} else {
printf(" ");
}
}
}
if (n % 16) {
printf("\n");
}
}
#else
#define hexdump(str, packet, length)
#endif
static inline void slip_writeb(unsigned char c)
{
uint8_t buf[1] = { c };
uart_pipe_send(&buf[0], 1);
}
static int slip_send(struct net_if *iface, struct net_buf *buf)
{
struct slip_context *slip = iface->dev->driver_data;
uint16_t i;
uint8_t *ptr;
uint8_t c;
if (!buf->frags) {
/* No data? */
return -ENODATA;
}
slip_writeb(SLIP_END);
while (buf->frags) {
struct net_buf *frag = buf->frags;
#if defined(CONFIG_SLIP_DEBUG)
int frag_count = 0;
#endif
#if defined(CONFIG_SLIP_TAP)
ptr = frag->data - slip->ll_reserve;
/* This writes ethernet header */
if (slip->ll_reserve) {
for (i = 0; i < sizeof(struct net_eth_hdr); i++) {
slip_writeb(*ptr++);
}
}
#else
/* There is no ll header in tun device */
ptr = frag->data;
#endif
for (i = 0; i < frag->len; ++i) {
c = *ptr++;
if (c == SLIP_END) {
slip_writeb(SLIP_ESC);
c = SLIP_ESC_END;
} else if (c == SLIP_ESC) {
slip_writeb(SLIP_ESC);
c = SLIP_ESC_ESC;
}
slip_writeb(c);
}
#if defined(CONFIG_SLIP_DEBUG)
SYS_LOG_DBG("[%p] sent data %d bytes", slip,
frag->len + slip->ll_reserve);
if (frag->len + slip->ll_reserve) {
char msg[7 + 1];
snprintf(msg, sizeof(msg), "slip %d", frag_count++);
msg[7] = '\0';
hexdump(msg, frag->data - slip->ll_reserve,
frag->len + slip->ll_reserve);
}
#endif
net_buf_frag_del(buf, frag);
net_nbuf_unref(frag);
}
net_nbuf_unref(buf);
slip_writeb(SLIP_END);
return 0;
}
static struct net_buf *slip_poll_handler(struct slip_context *slip)
{
if (slip->last && slip->last->len) {
if (slip->state == STATE_MULTI_PACKETS) {
/* Assume no bytes where lost */
slip->state = STATE_OK;
}
return slip->rx;
}
return NULL;
}
static void process_msg(struct slip_context *slip)
{
struct net_buf *buf;
buf = slip_poll_handler(slip);
if (!buf) {
return;
}
if (buf->frags) {
if (net_recv_data(net_if_get_by_link_addr(&slip->ll_addr),
buf) < 0) {
net_nbuf_unref(buf);
}
slip->rx = slip->last = NULL;
}
}
static inline int slip_input_byte(struct slip_context *slip,
unsigned char c)
{
switch (slip->state) {
case STATE_GARBAGE:
if (c == SLIP_END) {
slip->state = STATE_OK;
}
return 0;
case STATE_MULTI_PACKETS:
return 0;
case STATE_ESC:
if (c == SLIP_ESC_END) {
c = SLIP_END;
} else if (c == SLIP_ESC_ESC) {
c = SLIP_ESC;
} else {
slip->state = STATE_GARBAGE;
SLIP_STATS(slip->garbage++);
return 0;
}
slip->state = STATE_OK;
break;
case STATE_OK:
if (c == SLIP_ESC) {
slip->state = STATE_ESC;
return 0;
} else if (c == SLIP_END) {
if (slip->last->len) {
slip->state = STATE_MULTI_PACKETS;
SLIP_STATS(slip->multi_packets++);
return 1;
}
return 0;
}
break;
}
if (!slip->rx) {
slip->rx = net_nbuf_get_reserve_rx(0);
if (!slip->rx) {
return 0;
}
slip->last = net_nbuf_get_reserve_data(slip->ll_reserve);
if (!slip->last) {
net_nbuf_unref(slip->rx);
slip->rx = NULL;
return 0;
}
net_buf_frag_add(slip->rx, slip->last);
net_nbuf_set_ll_reserve(slip->rx, slip->ll_reserve);
slip->ptr = net_nbuf_ip_data(slip->rx) - slip->ll_reserve;
}
if (!net_buf_tailroom(slip->last)) {
/* We need to allocate a new fragment */
struct net_buf *frag;
frag = net_nbuf_get_reserve_data(slip->ll_reserve);
if (!frag) {
SYS_LOG_ERR("[%p] cannot allocate data fragment",
slip);
net_nbuf_unref(slip->rx);
slip->rx = NULL;
slip->last = NULL;
return 0;
}
net_buf_frag_insert(slip->last, frag);
slip->last = frag;
if (slip->mtu > net_buf_tailroom(slip->last)) {
/* Do not add link layer header if the mtu is bigger
* than fragment size.
*/
slip->ptr = slip->last->data;
} else {
slip->ptr = slip->last->data - slip->ll_reserve;
}
}
/* The net_buf_add_u8() cannot add data to ll header so we need
* a way to do it.
*/
if (slip->ptr < slip->last->data) {
*slip->ptr = c;
} else {
slip->ptr = net_buf_add_u8(slip->last, c);
}
slip->ptr++;
return 0;
}
static uint8_t *recv_cb(uint8_t *buf, size_t *off)
{
struct slip_context *slip =
CONTAINER_OF(buf, struct slip_context, buf);
int i;
if (!slip->init_done) {
*off = 0;
return buf;
}
for (i = 0; i < *off; i++) {
if (slip_input_byte(slip, buf[i])) {
#if defined(CONFIG_SLIP_DEBUG)
struct net_buf *frag = slip->rx->frags;
int count = 0;
int bytes = net_buf_frags_len(slip->rx->frags);
while (bytes && frag) {
char msg[7 + 1];
snprintf(msg, sizeof(msg), "slip %d", count);
msg[7] = '\0';
hexdump(msg, frag->data - slip->ll_reserve,
frag->len + slip->ll_reserve);
frag = frag->frags;
count++;
}
SYS_LOG_DBG("[%p] received data %d bytes", slip,
bytes + count * slip->ll_reserve);
#endif
process_msg(slip);
break;
}
}
*off = 0;
return buf;
}
static int slip_init(struct device *dev)
{
struct slip_context *slip = dev->driver_data;
SYS_LOG_DBG("[%p] dev %p", slip, dev);
slip->state = STATE_OK;
slip->rx = NULL;
#if defined(CONFIG_SLIP_TAP)
slip->ll_reserve = sizeof(struct net_eth_hdr);
slip->mtu = 1500; /* assume for ethernet */
#else
slip->ll_reserve = 0;
slip->mtu = 576; /* assume for tun */
#endif
SYS_LOG_DBG("%sll reserve %d",
#if defined(CONFIG_SLIP_TAP) && defined(CONFIG_NET_IPV4)
"ARP enabled, ",
#else
"",
#endif
slip->ll_reserve);
uart_pipe_register(slip->buf, sizeof(slip->buf), recv_cb);
return 0;
}
static inline struct net_linkaddr *slip_get_mac(struct slip_context *slip)
{
if (slip->mac_addr[0] == 0x00) {
/* 10-00-00-00-00 to 10-00-00-00-FF Documentation RFC7042 */
slip->mac_addr[0] = 0x10;
slip->mac_addr[1] = 0x00;
slip->mac_addr[2] = 0x00;
slip->mac_addr[3] = 0x00;
slip->mac_addr[4] = 0x00;
slip->mac_addr[5] = sys_rand32_get();
}
slip->ll_addr.addr = slip->mac_addr;
slip->ll_addr.len = sizeof(slip->mac_addr);
return &slip->ll_addr;
}
static void slip_iface_init(struct net_if *iface)
{
struct slip_context *slip = net_if_get_device(iface)->driver_data;
struct net_linkaddr *ll_addr = slip_get_mac(slip);
slip->init_done = true;
net_if_set_link_addr(iface, ll_addr->addr, ll_addr->len);
}
static struct net_if_api slip_if_api = {
.init = slip_iface_init,
.send = slip_send,
};
static struct slip_context slip_context_data;
#if defined(CONFIG_SLIP_TAP) && defined(CONFIG_NET_L2_ETHERNET)
#define _SLIP_L2_LAYER ETHERNET_L2
#else
#define _SLIP_L2_LAYER DUMMY_L2
#endif
NET_DEVICE_INIT(slip, CONFIG_SLIP_DRV_NAME, slip_init, &slip_context_data,
NULL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &slip_if_api,
_SLIP_L2_LAYER, 127);