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:
parent
b047f816f4
commit
0f66426f4a
|
@ -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
|
||||
*************
|
||||
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
|
67
drivers/ethernet/Kconfig.native_posix
Normal file
67
drivers/ethernet/Kconfig.native_posix
Normal 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
|
239
drivers/ethernet/eth_native_posix.c
Normal file
239
drivers/ethernet/eth_native_posix.c
Normal 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, ð_context_data, NULL,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, ð_if_api,
|
||||
_ETH_L2_LAYER, _ETH_L2_CTX_TYPE, _ETH_MTU);
|
140
drivers/ethernet/eth_native_posix_adapt.c
Normal file
140
drivers/ethernet/eth_native_posix_adapt.c
Normal 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);
|
||||
}
|
21
drivers/ethernet/eth_native_posix_priv.h
Normal file
21
drivers/ethernet/eth_native_posix_priv.h
Normal 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 */
|
Loading…
Reference in a new issue