| /* |
| * 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" |
| #include "ethernet/eth_stats.h" |
| |
| #if defined(CONFIG_NET_L2_ETHERNET) |
| #define _ETH_MTU 1500 |
| #endif |
| |
| #define NET_BUF_TIMEOUT MSEC(10) |
| |
| #if defined(CONFIG_NET_VLAN) |
| #define ETH_HDR_LEN sizeof(struct net_eth_vlan_hdr) |
| #else |
| #define ETH_HDR_LEN sizeof(struct net_eth_hdr) |
| #endif |
| |
| struct eth_context { |
| u8_t recv[_ETH_MTU + ETH_HDR_LEN]; |
| u8_t send[_ETH_MTU + ETH_HDR_LEN]; |
| 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; |
| |
| #if defined(CONFIG_NET_STATISTICS_ETHERNET) |
| struct net_stats_eth stats; |
| #endif |
| }; |
| |
| 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; |
| } |
| |
| eth_stats_update_bytes_tx(iface, count); |
| eth_stats_update_pkts_tx(iface); |
| |
| if (IS_ENABLED(CONFIG_NET_STATISTICS_ETHERNET)) { |
| if (net_eth_is_addr_broadcast( |
| &((struct net_eth_hdr *)NET_ETH_HDR(pkt))->dst)) { |
| eth_stats_update_broadcast_tx(iface); |
| } else if (net_eth_is_addr_multicast( |
| &((struct net_eth_hdr *) |
| NET_ETH_HDR(pkt))->dst)) { |
| eth_stats_update_multicast_tx(iface); |
| } |
| } |
| |
| SYS_LOG_DBG("Send pkt %p len %d", pkt, count); |
| |
| eth_write_data(ctx->dev_fd, ctx->send, count); |
| |
| net_pkt_unref(pkt); |
| |
| 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 inline struct net_if *get_iface(struct eth_context *ctx, |
| u16_t vlan_tag) |
| { |
| #if defined(CONFIG_NET_VLAN) |
| struct net_if *iface; |
| |
| iface = net_eth_get_vlan_iface(ctx->iface, vlan_tag); |
| if (!iface) { |
| return ctx->iface; |
| } |
| |
| return iface; |
| #else |
| ARG_UNUSED(vlan_tag); |
| |
| return ctx->iface; |
| #endif |
| } |
| |
| static int read_data(struct eth_context *ctx, int fd) |
| { |
| u16_t vlan_tag = NET_VLAN_TAG_UNSPEC; |
| struct net_if *iface; |
| struct net_pkt *pkt; |
| struct net_buf *frag; |
| u32_t pkt_len; |
| 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); |
| |
| #if defined(CONFIG_NET_VLAN) |
| { |
| struct net_eth_hdr *hdr = NET_ETH_HDR(pkt); |
| |
| if (ntohs(hdr->type) == NET_ETH_PTYPE_VLAN) { |
| struct net_eth_vlan_hdr *hdr_vlan = |
| (struct net_eth_vlan_hdr *)NET_ETH_HDR(pkt); |
| |
| net_pkt_set_vlan_tci(pkt, ntohs(hdr_vlan->vlan.tci)); |
| vlan_tag = net_pkt_vlan_tag(pkt); |
| } |
| } |
| #endif |
| |
| iface = get_iface(ctx, vlan_tag); |
| pkt_len = net_pkt_get_len(pkt); |
| |
| eth_stats_update_bytes_rx(iface, pkt_len); |
| eth_stats_update_pkts_rx(iface); |
| |
| if (IS_ENABLED(CONFIG_NET_STATISTICS_ETHERNET)) { |
| if (net_eth_is_addr_broadcast( |
| &((struct net_eth_hdr *)NET_ETH_HDR(pkt))->dst)) { |
| eth_stats_update_broadcast_rx(iface); |
| } else if (net_eth_is_addr_multicast( |
| &((struct net_eth_hdr *) |
| NET_ETH_HDR(pkt))->dst)) { |
| eth_stats_update_multicast_rx(iface); |
| } |
| } |
| |
| SYS_LOG_DBG("Recv pkt %p len %d", pkt, pkt_len); |
| |
| if (net_recv_data(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); |
| |
| ctx->iface = iface; |
| |
| ethernet_init(iface); |
| |
| if (ctx->init_done) { |
| return; |
| } |
| |
| ctx->init_done = true; |
| |
| #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 |
| enum ethernet_hw_caps eth_posix_native_get_capabilities(struct device *dev) |
| { |
| ARG_UNUSED(dev); |
| |
| return ETHERNET_HW_VLAN; |
| } |
| |
| static const struct ethernet_api eth_if_api = { |
| .iface_api.init = eth_iface_init, |
| .iface_api.send = eth_send, |
| |
| .get_capabilities = eth_posix_native_get_capabilities, |
| |
| #if defined(CONFIG_NET_STATISTICS_ETHERNET) |
| .stats = ð_context_data.stats, |
| #endif |
| }; |
| |
| ETH_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_MTU); |