drivers: eth: Add ethernet driver for native posix arch

This creates zeth network interface in your host and allows user
to send and receive data sent to this network interface.

Fixes #6007

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
This commit is contained in:
Jukka Rissanen 2018-02-21 16:18:35 +02:00 committed by Anas Nashif
parent b047f816f4
commit 0f66426f4a
7 changed files with 480 additions and 0 deletions

View file

@ -389,6 +389,14 @@ The following peripherals are currently provided with this board:
and provides interrupt priorities. Interrupts can be individually masked or
unmasked. SW interrupts are also supported.
**Ethernet driver**:
A simple TAP based ethernet driver is provided. The driver will create
a **zeth** network interface to the host system. One can communicate with
Zephyr via this network interface. Multiple TAP based network interfaces can
be created if needed. The IP address configuration can be specified for each
network interface instance.
See :option:`CONFIG_ETH_NATIVE_POSIX_SETUP_SCRIPT` option for more details.
Shell support
*************

View file

@ -6,3 +6,7 @@ zephyr_sources_ifdef(CONFIG_ETH_DW eth_dw.c)
zephyr_sources_ifdef(CONFIG_ETH_ENC28J60 eth_enc28j60.c)
zephyr_sources_ifdef(CONFIG_ETH_MCUX eth_mcux.c)
zephyr_sources_ifdef(CONFIG_ETH_STM32_HAL eth_stm32_hal.c)
zephyr_sources_ifdef(CONFIG_ETH_NATIVE_POSIX
eth_native_posix.c
eth_native_posix_adapt.c
)

View file

@ -39,5 +39,6 @@ source "drivers/ethernet/Kconfig.mcux"
source "drivers/ethernet/Kconfig.dw"
source "drivers/ethernet/Kconfig.sam_gmac"
source "drivers/ethernet/Kconfig.stm32_hal"
source "drivers/ethernet/Kconfig.native_posix"
endmenu

View file

@ -0,0 +1,67 @@
# Kconfig - Native posix ethernet driver configuration options
# Copyright (c) 2018 Intel Corporation
#
# SPDX-License-Identifier: Apache-2.0
menuconfig ETH_NATIVE_POSIX
bool
prompt "Native Posix Ethernet driver"
depends on ARCH_POSIX
select NET_L2_ETHERNET
default n
help
Enable native posix ethernet driver. Note, this driver is run inside
a process in your host system.
if ETH_NATIVE_POSIX
config ETH_NATIVE_POSIX_SETUP_SCRIPT
string "Host setup script"
default "${ZEPHYR_BASE}/samples/net/eth_native_posix/net_setup_host"
help
This option sets the name of the script that is run when the host TAP
network interface is created. The script should setup IP addresses
etc. for the host TAP network interface.
The default script accepts following options:
-i|--interface <network interface name>, default is zeth
-f|--file <config file name>, default is net_setup_host.conf
If needed, you can add these options to this script name option.
Note that the driver will add -i option with the value of
CONFIG_ETH_NATIVE_POSIX_DRV_NAME option to the end of the options
list when calling the host setup script.
config ETH_NATIVE_POSIX_DRV_NAME
string "Ethernet driver name"
default "zeth"
help
This option sets the driver name and name of the network interface
in your host system.
config ETH_NATIVE_POSIX_DEV_NAME
string "Host ethernet TUN/TAP device name"
default "/dev/net/tun"
help
This option sets the TUN/TAP device name in your host system.
config ETH_NATIVE_POSIX_RANDOM_MAC
bool "Random MAC address"
depends on ENTROPY_GENERATOR
default y
help
Generate a random MAC address dynamically.
if ! ETH_NATIVE_POSIX_RANDOM_MAC
config ETH_NATIVE_POSIX_MAC_ADDR
string "MAC address for the interface"
default ""
help
Specify a MAC address for the ethernet interface in the form of
six hex 8-bit chars separated by colons (e.g.:
aa:33:cc:22:e2:c0). The default is an empty string, which
means the code will make 00:00:5E:00:53:XX, where XX will be
random.
endif
endif

View file

@ -0,0 +1,239 @@
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* Ethernet driver for native posix board. This is meant for network
* connectivity between host and Zephyr.
*/
#define SYS_LOG_DOMAIN "eth-posix"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ETHERNET_LEVEL
#include <logging/sys_log.h>
#include <stdio.h>
#include <kernel.h>
#include <stdbool.h>
#include <errno.h>
#include <stddef.h>
#include <net/net_pkt.h>
#include <net/net_core.h>
#include <net/net_if.h>
#include <net/ethernet.h>
#include "eth_native_posix_priv.h"
#if defined(CONFIG_NET_L2_ETHERNET)
#define _ETH_L2_LAYER ETHERNET_L2
#define _ETH_L2_CTX_TYPE NET_L2_GET_CTX_TYPE(ETHERNET_L2)
#define _ETH_MTU 1500
#endif
#define NET_BUF_TIMEOUT MSEC(10)
struct eth_context {
u8_t recv[_ETH_MTU + sizeof(struct net_eth_hdr)];
u8_t send[_ETH_MTU + sizeof(struct net_eth_hdr)];
u8_t mac_addr[6];
struct net_linkaddr ll_addr;
struct net_if *iface;
const char *if_name;
int dev_fd;
bool init_done;
bool status;
};
NET_STACK_DEFINE(RX_ZETH, eth_rx_stack,
CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE,
CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);
static struct k_thread rx_thread_data;
/* TODO: support multiple interfaces */
static struct eth_context eth_context_data;
static struct eth_context *get_context(struct net_if *iface)
{
return net_if_get_device(iface)->driver_data;
}
static int eth_send(struct net_if *iface, struct net_pkt *pkt)
{
struct eth_context *ctx = get_context(iface);
struct net_buf *frag;
int count = 0;
/* First fragment contains link layer (Ethernet) headers.
*/
count = net_pkt_ll_reserve(pkt) + pkt->frags->len;
memcpy(ctx->send, net_pkt_ll(pkt), count);
/* Then the remaining data */
frag = pkt->frags->frags;
while (frag) {
memcpy(ctx->send + count, frag->data, frag->len);
count += frag->len;
frag = frag->frags;
}
net_pkt_unref(pkt);
SYS_LOG_DBG("Send pkt %p len %d", pkt, count);
eth_write_data(ctx->dev_fd, ctx->send, count);
return 0;
}
static int eth_init(struct device *dev)
{
ARG_UNUSED(dev);
return 0;
}
static struct net_linkaddr *eth_get_mac(struct eth_context *ctx)
{
ctx->ll_addr.addr = ctx->mac_addr;
ctx->ll_addr.len = sizeof(ctx->mac_addr);
return &ctx->ll_addr;
}
static int read_data(struct eth_context *ctx, int fd)
{
struct net_pkt *pkt;
struct net_buf *frag;
int ret;
ret = eth_read_data(fd, ctx->recv, sizeof(ctx->recv));
if (ret <= 0) {
return 0;
}
pkt = net_pkt_get_reserve_rx(0, NET_BUF_TIMEOUT);
if (!pkt) {
return -ENOMEM;
}
do {
int count = 0;
frag = net_pkt_get_frag(pkt, NET_BUF_TIMEOUT);
if (!frag) {
net_pkt_unref(pkt);
return -ENOMEM;
}
net_pkt_frag_add(pkt, frag);
net_buf_add_mem(frag, ctx->recv + count,
min(net_buf_tailroom(frag), ret));
ret -= frag->len;
count += frag->len;
} while (ret > 0);
SYS_LOG_DBG("Recv pkt %p len %d", pkt, net_pkt_get_len(pkt));
if (net_recv_data(ctx->iface, pkt) < 0) {
net_pkt_unref(pkt);
}
return 0;
}
static void eth_rx(struct eth_context *ctx)
{
int ret;
SYS_LOG_DBG("Starting ZETH RX thread");
while (1) {
if (net_if_is_up(ctx->iface)) {
ret = eth_wait_data(ctx->dev_fd);
if (!ret) {
read_data(ctx, ctx->dev_fd);
}
}
k_sleep(MSEC(50));
}
}
static void create_rx_handler(struct eth_context *ctx)
{
k_thread_create(&rx_thread_data, eth_rx_stack,
K_THREAD_STACK_SIZEOF(eth_rx_stack),
(k_thread_entry_t)eth_rx,
ctx, NULL, NULL, K_PRIO_COOP(14),
0, K_NO_WAIT);
}
static void eth_iface_init(struct net_if *iface)
{
struct eth_context *ctx = net_if_get_device(iface)->driver_data;
struct net_linkaddr *ll_addr = eth_get_mac(ctx);
if (ctx->init_done) {
return;
}
ctx->init_done = true;
ctx->iface = iface;
#if defined(CONFIG_ETH_NATIVE_POSIX_RANDOM_MAC)
/* 00-00-5E-00-53-xx Documentation RFC 7042 */
ctx->mac_addr[0] = 0x00;
ctx->mac_addr[1] = 0x00;
ctx->mac_addr[2] = 0x5E;
ctx->mac_addr[3] = 0x00;
ctx->mac_addr[4] = 0x53;
ctx->mac_addr[5] = sys_rand32_get();
/* The TUN/TAP setup script will by default set the MAC address of host
* interface to 00:00:5E:00:53:FF so do not allow that.
*/
if (ctx->mac_addr[5] == 0xff) {
ctx->mac_addr[5] = 0x01;
}
#else
if (CONFIG_ETH_NATIVE_POSIX_MAC_ADDR[0] != 0) {
if (net_bytes_from_str(ctx->mac_addr, sizeof(ctx->mac_addr),
CONFIG_ETH_NATIVE_POSIX_MAC_ADDR) < 0) {
SYS_LOG_ERR("Invalid MAC address %s",
CONFIG_ETH_NATIVE_POSIX_MAC_ADDR);
}
}
#endif
net_if_set_link_addr(iface, ll_addr->addr, ll_addr->len,
NET_LINK_ETHERNET);
ctx->if_name = CONFIG_ETH_NATIVE_POSIX_DRV_NAME;
ctx->dev_fd = eth_iface_create(ctx->if_name, false);
if (ctx->dev_fd < 0) {
SYS_LOG_ERR("Cannot create %s (%d)", ctx->if_name,
ctx->dev_fd);
} else {
/* Create a thread that will handle incoming data from host */
create_rx_handler(ctx);
eth_setup_host(ctx->if_name);
}
}
static struct net_if_api eth_if_api = {
.init = eth_iface_init,
.send = eth_send,
};
NET_DEVICE_INIT(eth_native_posix, CONFIG_ETH_NATIVE_POSIX_DRV_NAME,
eth_init, &eth_context_data, NULL,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, &eth_if_api,
_ETH_L2_LAYER, _ETH_L2_CTX_TYPE, _ETH_MTU);

View file

@ -0,0 +1,140 @@
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
*
* Routines setting up the host system. Those are placed in separate file
* because there is naming conflicts between host and zephyr network stacks.
*/
#define _DEFAULT_SOURCE
/* Host include files */
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#ifdef __linux
#include <linux/if_tun.h>
#endif
/* Zephyr include files. Be very careful here and only include minimum
* things needed.
*/
#define SYS_LOG_DOMAIN "eth-posix-adapt"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_ETHERNET_LEVEL
#include <zephyr/types.h>
#include <sys_clock.h>
#include <logging/sys_log.h>
#include "eth_native_posix_priv.h"
/* Note that we cannot create the TUN/TAP device from the setup script
* as we need to get a file descriptor to communicate with the interface.
*/
int eth_iface_create(const char *if_name, bool tun_only)
{
struct ifreq ifr;
int fd, ret = -EINVAL;
fd = open(CONFIG_ETH_NATIVE_POSIX_DEV_NAME, O_RDWR);
if (fd < 0) {
return -errno;
}
memset(&ifr, 0, sizeof(ifr));
#ifdef __linux
ifr.ifr_flags = (tun_only ? IFF_TUN : IFF_TAP) | IFF_NO_PI;
strncpy(ifr.ifr_name, if_name, IFNAMSIZ);
ret = ioctl(fd, TUNSETIFF, (void *)&ifr);
if (ret < 0) {
ret = -errno;
close(fd);
return ret;
}
#endif
return fd;
}
int eth_iface_remove(int fd)
{
return close(fd);
}
static int ssystem(const char *fmt, ...)
__attribute__((__format__(__printf__, 1, 2)));
static int ssystem(const char *fmt, ...)
{
char cmd[255];
va_list ap;
va_start(ap, fmt);
vsnprintf(cmd, sizeof(cmd), fmt, ap);
va_end(ap);
printk("%s\n", cmd);
fflush(stdout);
return system(cmd);
}
int eth_setup_host(const char *if_name)
{
/* User might have added -i option to setup script string, so
* check that situation in the script itself so that the -i option
* we add here is ignored in that case.
*/
return ssystem("%s -i %s", CONFIG_ETH_NATIVE_POSIX_SETUP_SCRIPT,
if_name);
}
int eth_wait_data(int fd)
{
struct timeval timeout;
fd_set rset;
int ret;
FD_ZERO(&rset);
FD_SET(fd, &rset);
timeout.tv_sec = 0;
timeout.tv_usec = USEC(50);
ret = select(fd + 1, &rset, NULL, NULL, &timeout);
if (ret < 0 && errno != EINTR) {
return -errno;
} else if (ret > 0) {
if (FD_ISSET(fd, &rset)) {
return 0;
}
}
return -EAGAIN;
}
ssize_t eth_read_data(int fd, void *buf, size_t buf_len)
{
return read(fd, buf, buf_len);
}
ssize_t eth_write_data(int fd, void *buf, size_t buf_len)
{
return write(fd, buf, buf_len);
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
/** @file
* @brief Private functions for native posix ethernet driver.
*/
#ifndef _ETH_NATIVE_POSIX_PRIV_H
#define _ETH_NATIVE_POSIX_PRIV_H
int eth_iface_create(const char *if_name, bool tun_only);
int eth_iface_remove(int fd);
int eth_setup_host(const char *if_name);
int eth_wait_data(int fd);
ssize_t eth_read_data(int fd, void *buf, size_t buf_len);
ssize_t eth_write_data(int fd, void *buf, size_t buf_len);
#endif /* _ETH_NATIVE_POSIX_PRIV_H */