/* * 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 #include #include #include #include #include #include #include #include #include #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 K_MSEC(100) #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; int count = 0; 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 { 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(K_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);