diff --git a/drivers/Kconfig b/drivers/Kconfig index 6c150c77d00..0b1a72343b4 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -27,6 +27,8 @@ source "drivers/console/Kconfig" source "drivers/ethernet/Kconfig" +source "drivers/slip/Kconfig" + source "drivers/serial/Kconfig" source "drivers/interrupt_controller/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index c1b3dcd95fb..b9db8632bb7 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -19,6 +19,7 @@ obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_PWM) += pwm/ obj-$(CONFIG_ADC) += adc/ obj-$(CONFIG_ETHERNET) += ethernet/ +obj-$(CONFIG_SLIP) += slip/ obj-$(CONFIG_NETWORKING_WITH_15_4) += ieee802154/ obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_RTC) += rtc/ diff --git a/drivers/slip/Kconfig b/drivers/slip/Kconfig new file mode 100644 index 00000000000..40ae29e4d2f --- /dev/null +++ b/drivers/slip/Kconfig @@ -0,0 +1,73 @@ +# Kconfig - SLIP driver configuration options + +# +# Copyright (c) 2016 Intel Corporation +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1) Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2) Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3) Neither the name of Intel Corporation nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +# +# SLIP options +# +menuconfig SLIP + bool + prompt "SLIP driver" + depends on NET_SLIP + select UART_PIPE + select UART_INTERRUPT_DRIVEN + +if SLIP + +config SLIP_DRV_NAME + string "SLIP Driver name" + default "slip" + help + This option sets the driver name + +config SLIP_MTU + int "SLIP MTU" + default 128 + range 80 1280 + help + This option sets the MTU for the SLIP connection. + The value is only used when fragmenting the network + data into net_buf's. The actual SLIP connection + does not use this value. + +config SLIP_DEBUG + bool "SLIP driver debug" + default n + help + This option enables debug support for SLIP driver. + +config SLIP_STATISTICS + bool "SLIP network connection statistics" + default n + help + This option enables statistics support for SLIP driver. + +endif diff --git a/drivers/slip/Makefile b/drivers/slip/Makefile new file mode 100644 index 00000000000..cc0e00acca3 --- /dev/null +++ b/drivers/slip/Makefile @@ -0,0 +1,3 @@ +subdir-ccflags-y +=-I${srctree}/net/yaip + +obj-$(CONFIG_SLIP) = slip.o diff --git a/drivers/slip/slip.c b/drivers/slip/slip.c new file mode 100644 index 00000000000..6e7f410d57e --- /dev/null +++ b/drivers/slip/slip.c @@ -0,0 +1,374 @@ +/* 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 +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 { + 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 state; + + uint16_t ll_reserve; /* Reserve any space for link layer headers */ + uint8_t mac_addr[6]; + struct net_linkaddr ll_addr; + +#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) +{ + 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 + + ptr = frag->data; + + 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", iface->dev->driver_data, + frag->len); + if (frag->len) { + char msg[7 + 1]; + snprintf(msg, sizeof(msg), "slip %d", frag_count++); + msg[7] = '\0'; + hexdump(msg, frag->data, frag->len); + } +#endif + + net_buf_frag_del(buf, frag); + net_buf_unref(frag); + } + + net_buf_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 defined(CONFIG_NET_IPV4) +#error "FIXME - TBDL" +#endif /* IPv4 */ + +#if defined(CONFIG_NET_IPV6) + if (buf->frags) { + if (net_recv(net_if_get_by_link_addr(&slip->ll_addr), + buf) < 0) { + net_nbuf_unref(buf); + } + slip->rx = slip->last = NULL; + } +#endif /* CONFIG_NET_IPV6 */ +} + +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); + } + + 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(frag); + net_nbuf_unref(slip->rx); + slip->rx = NULL; + slip->last = NULL; + return 0; + } + net_buf_frag_insert(slip->last, frag); + slip->last = frag; + } + net_buf_add_u8(slip->last, c); + + 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; + + 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); + + SYS_LOG_DBG("[%p] received data %d bytes", slip, + bytes); + while (bytes && frag) { + char msg[7 + 1]; + snprintf(msg, sizeof(msg), "slip %d", count); + msg[7] = '\0'; + hexdump(msg, frag->data, frag->len); + frag = frag->frags; + count++; + } +#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); + + dev->driver_api = NULL; + + slip->state = STATE_OK; + slip->rx = NULL; + slip->ll_reserve = 0; + + uart_pipe_register(slip->buf, sizeof(slip->buf), recv_cb); + + return 0; +} + +static inline struct net_linkaddr *slip_get_mac(struct device *dev) +{ + struct slip_context *slip = dev->driver_data; + + 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 net_linkaddr *ll_addr = slip_get_mac(net_if_get_device(iface)); + + 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; + +NET_DEVICE_INIT(slip, CONFIG_SLIP_DRV_NAME, slip_init, &slip_context_data, + NULL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &slip_if_api, 127); diff --git a/net/yaip/Kconfig b/net/yaip/Kconfig index d23ca80184b..88782d0a78c 100644 --- a/net/yaip/Kconfig +++ b/net/yaip/Kconfig @@ -78,3 +78,17 @@ config NET_NBUF_DATA_SIZE which leaves in worst case 81 bytes for user data (MTU). In order to be able to receive at least full IPv6 packet which has a size of 1280 bytes, the one should allocate 16 fragments here. + +config NET_SLIP + bool "Use SLIP connectivity with Qemu" + default n + select SLIP + select UART_PIPE + select UART_INTERRUPT_DRIVEN + help + The SLIP support is only used when the application is + run inside Qemu and the network peer is run in your + host. The host needs to have tunslip running in order + to receive and send network packets via the SLIP driver. + The SLIP driver Kconfig options can be tweaked in drivers + section.