| /* main.c - Application main entry point */ |
| |
| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define NET_LOG_LEVEL CONFIG_NET_L2_ETHERNET_LOG_LEVEL |
| |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(net_test, NET_LOG_LEVEL); |
| |
| #include <zephyr/types.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <misc/printk.h> |
| #include <linker/sections.h> |
| |
| #include <ztest.h> |
| |
| #include <ptp_clock.h> |
| #include <net/ptp_time.h> |
| |
| #include <net/ethernet.h> |
| #include <net/buf.h> |
| #include <net/net_ip.h> |
| #include <net/net_l2.h> |
| |
| #define NET_LOG_ENABLED 1 |
| #include "net_private.h" |
| |
| #if NET_LOG_LEVEL >= LOG_LEVEL_DBG |
| #define DBG(fmt, ...) printk(fmt, ##__VA_ARGS__) |
| #else |
| #define DBG(fmt, ...) |
| #endif |
| |
| /* Interface 1 addresses */ |
| static struct in6_addr my_addr1 = { { { 0x20, 0x01, 0x0d, 0xb8, 1, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| |
| /* Interface 2 addresses */ |
| static struct in6_addr my_addr2 = { { { 0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| |
| /* Interface 3 addresses */ |
| static struct in6_addr my_addr3 = { { { 0x20, 0x01, 0x0d, 0xb8, 2, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0x1 } } }; |
| |
| /* Extra address is assigned to ll_addr */ |
| static struct in6_addr ll_addr = { { { 0xfe, 0x80, 0x43, 0xb8, 0, 0, 0, 0, |
| 0, 0, 0, 0xf2, 0xaa, 0x29, 0x02, |
| 0x04 } } }; |
| |
| #define MAX_NUM_INTERFACES 3 |
| |
| /* Keep track of all ethernet interfaces */ |
| static struct net_if *eth_interfaces[MAX_NUM_INTERFACES]; |
| |
| static int ptp_interface[MAX_NUM_INTERFACES - 1]; |
| static int non_ptp_interface; |
| static bool test_failed; |
| static bool test_started; |
| |
| static K_SEM_DEFINE(wait_data, 0, UINT_MAX); |
| |
| #define WAIT_TIME K_SECONDS(1) |
| |
| struct eth_context { |
| struct net_if *iface; |
| u8_t mac_addr[6]; |
| |
| struct net_ptp_time time; |
| struct device *ptp_clock; |
| }; |
| |
| static struct eth_context eth_context_1; |
| static struct eth_context eth_context_2; |
| static struct eth_context eth_context_3; |
| |
| static void eth_iface_init(struct net_if *iface) |
| { |
| struct device *dev = net_if_get_device(iface); |
| struct eth_context *context = dev->driver_data; |
| |
| net_if_set_link_addr(iface, context->mac_addr, |
| sizeof(context->mac_addr), |
| NET_LINK_ETHERNET); |
| |
| ethernet_init(iface); |
| } |
| |
| static int eth_tx(struct device *dev, struct net_pkt *pkt) |
| { |
| struct eth_context *context = dev->driver_data; |
| |
| if (ð_context_1 != context && ð_context_2 != context) { |
| zassert_true(false, "Context pointers do not match\n"); |
| } |
| |
| if (!pkt->frags) { |
| DBG("No data to send!\n"); |
| return -ENODATA; |
| } |
| |
| if (test_started) { |
| k_sem_give(&wait_data); |
| } |
| |
| |
| return 0; |
| } |
| |
| static enum ethernet_hw_caps eth_capabilities(struct device *dev) |
| { |
| return ETHERNET_PTP; |
| } |
| |
| static struct device *eth_get_ptp_clock(struct device *dev) |
| { |
| struct eth_context *context = dev->driver_data; |
| |
| return context->ptp_clock; |
| } |
| |
| static struct ethernet_api api_funcs = { |
| .iface_api.init = eth_iface_init, |
| |
| .get_capabilities = eth_capabilities, |
| .get_ptp_clock = eth_get_ptp_clock, |
| .send = eth_tx, |
| }; |
| |
| static void generate_mac(u8_t *mac_addr) |
| { |
| /* 00-00-5E-00-53-xx Documentation RFC 7042 */ |
| mac_addr[0] = 0x00; |
| mac_addr[1] = 0x00; |
| mac_addr[2] = 0x5E; |
| mac_addr[3] = 0x00; |
| mac_addr[4] = 0x53; |
| mac_addr[5] = sys_rand32_get(); |
| } |
| |
| static int eth_init(struct device *dev) |
| { |
| struct eth_context *context = dev->driver_data; |
| |
| generate_mac(context->mac_addr); |
| |
| return 0; |
| } |
| |
| ETH_NET_DEVICE_INIT(eth_test_1, "eth_test_1", eth_init, ð_context_1, NULL, |
| CONFIG_ETH_INIT_PRIORITY, &api_funcs, |
| NET_ETH_MTU); |
| |
| ETH_NET_DEVICE_INIT(eth_test_2, "eth_test_2", eth_init, ð_context_2, NULL, |
| CONFIG_ETH_INIT_PRIORITY, &api_funcs, |
| NET_ETH_MTU); |
| |
| ETH_NET_DEVICE_INIT(eth_test_3, "eth_test_3", eth_init, ð_context_3, NULL, |
| CONFIG_ETH_INIT_PRIORITY, &api_funcs, |
| NET_ETH_MTU); |
| |
| static u64_t timestamp_to_nsec(struct net_ptp_time *ts) |
| { |
| if (!ts) { |
| return 0; |
| } |
| |
| return (ts->second * NSEC_PER_SEC) + ts->nanosecond; |
| } |
| |
| static int my_ptp_clock_set(struct device *dev, struct net_ptp_time *tm) |
| { |
| struct eth_context *ctx = dev->driver_data; |
| |
| memcpy(&ctx->time, tm, sizeof(struct net_ptp_time)); |
| |
| return 0; |
| } |
| |
| static int my_ptp_clock_get(struct device *dev, struct net_ptp_time *tm) |
| { |
| struct eth_context *ctx = dev->driver_data; |
| |
| memcpy(tm, &ctx->time, sizeof(struct net_ptp_time)); |
| |
| return 0; |
| } |
| |
| static int my_ptp_clock_adjust(struct device *dev, int increment) |
| { |
| struct eth_context *ctx = dev->driver_data; |
| |
| ctx->time.nanosecond += increment; |
| |
| return 0; |
| } |
| |
| static int my_ptp_clock_rate_adjust(struct device *dev, float ratio) |
| { |
| return 0; |
| } |
| |
| struct ptp_context { |
| struct eth_context *eth_context; |
| }; |
| |
| static struct ptp_context ptp_test_1_context; |
| static struct ptp_context ptp_test_2_context; |
| |
| static const struct ptp_clock_driver_api api = { |
| .set = my_ptp_clock_set, |
| .get = my_ptp_clock_get, |
| .adjust = my_ptp_clock_adjust, |
| .rate_adjust = my_ptp_clock_rate_adjust, |
| }; |
| |
| static int ptp_test_1_init(struct device *port) |
| { |
| struct device *eth_dev = DEVICE_GET(eth_test_1); |
| struct eth_context *context = eth_dev->driver_data; |
| struct ptp_context *ptp_context = port->driver_data; |
| |
| context->ptp_clock = port; |
| ptp_context->eth_context = context; |
| |
| return 0; |
| } |
| |
| DEVICE_AND_API_INIT(ptp_clock_1, PTP_CLOCK_NAME, ptp_test_1_init, |
| &ptp_test_1_context, NULL, POST_KERNEL, |
| CONFIG_APPLICATION_INIT_PRIORITY, &api); |
| |
| static int ptp_test_2_init(struct device *port) |
| { |
| struct device *eth_dev = DEVICE_GET(eth_test_2); |
| struct eth_context *context = eth_dev->driver_data; |
| struct ptp_context *ptp_context = port->driver_data; |
| |
| context->ptp_clock = port; |
| ptp_context->eth_context = context; |
| |
| return 0; |
| } |
| |
| DEVICE_AND_API_INIT(ptp_clock_2, PTP_CLOCK_NAME, ptp_test_2_init, |
| &ptp_test_2_context, NULL, POST_KERNEL, |
| CONFIG_APPLICATION_INIT_PRIORITY, &api); |
| |
| struct user_data { |
| int eth_if_count; |
| int total_if_count; |
| }; |
| |
| #if NET_LOG_LEVEL >= LOG_LEVEL_DBG |
| static const char *iface2str(struct net_if *iface) |
| { |
| #ifdef CONFIG_NET_L2_ETHERNET |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { |
| return "Ethernet"; |
| } |
| #endif |
| |
| return "<unknown type>"; |
| } |
| #endif |
| |
| static void iface_cb(struct net_if *iface, void *user_data) |
| { |
| struct user_data *ud = user_data; |
| |
| DBG("Interface %p (%s) [%d]\n", iface, iface2str(iface), |
| net_if_get_by_iface(iface)); |
| |
| if (net_if_l2(iface) == &NET_L2_GET_NAME(ETHERNET)) { |
| static int ptp_iface_idx; |
| struct device *clk; |
| |
| if (ud->eth_if_count >= ARRAY_SIZE(eth_interfaces)) { |
| DBG("Invalid interface %p\n", iface); |
| return; |
| } |
| |
| clk = net_eth_get_ptp_clock(iface); |
| if (!clk) { |
| non_ptp_interface = ud->eth_if_count; |
| } else { |
| ptp_interface[ptp_iface_idx++] = ud->eth_if_count; |
| } |
| |
| eth_interfaces[ud->eth_if_count++] = iface; |
| } |
| |
| /* By default all interfaces are down initially */ |
| net_if_down(iface); |
| |
| ud->total_if_count++; |
| } |
| |
| static void check_interfaces(void) |
| { |
| struct user_data ud = { 0 }; |
| |
| /* Make sure we have enough interfaces */ |
| net_if_foreach(iface_cb, &ud); |
| |
| zassert_equal(ud.eth_if_count, MAX_NUM_INTERFACES, |
| "Invalid numer of ethernet interfaces %d vs %d\n", |
| ud.eth_if_count, MAX_NUM_INTERFACES); |
| |
| zassert_equal(ud.total_if_count, ud.eth_if_count, |
| "Invalid numer of interfaces %d vs %d\n", |
| ud.total_if_count, ud.eth_if_count); |
| } |
| |
| /* As we are testing the ethernet controller clock, the IP addresses are not |
| * relevant for this testing. Anyway, set the IP addresses to the interfaces so |
| * we have a real life scenario. |
| */ |
| static void address_setup(void) |
| { |
| struct net_if_addr *ifaddr; |
| struct net_if *iface1, *iface2, *iface3; |
| |
| iface1 = eth_interfaces[0]; |
| iface2 = eth_interfaces[1]; |
| iface3 = eth_interfaces[2]; |
| |
| zassert_not_null(iface1, "Interface 1\n"); |
| zassert_not_null(iface2, "Interface 2\n"); |
| zassert_not_null(iface3, "Interface 3\n"); |
| |
| ifaddr = net_if_ipv6_addr_add(iface1, &my_addr1, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr1)); |
| zassert_not_null(ifaddr, "addr1\n"); |
| } |
| |
| /* For testing purposes we need to set the adddresses preferred */ |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv6_addr_add(iface1, &ll_addr, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&ll_addr)); |
| zassert_not_null(ifaddr, "ll_addr\n"); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv6_addr_add(iface2, &my_addr2, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr2)); |
| zassert_not_null(ifaddr, "addr2\n"); |
| } |
| |
| ifaddr->addr_state = NET_ADDR_PREFERRED; |
| |
| ifaddr = net_if_ipv6_addr_add(iface3, &my_addr3, |
| NET_ADDR_MANUAL, 0); |
| if (!ifaddr) { |
| DBG("Cannot add IPv6 address %s\n", |
| net_sprint_ipv6_addr(&my_addr3)); |
| zassert_not_null(ifaddr, "addr3\n"); |
| } |
| |
| net_if_up(iface1); |
| net_if_up(iface2); |
| net_if_up(iface3); |
| |
| test_failed = false; |
| } |
| |
| static void test_ptp_clock_interfaces(void) |
| { |
| struct device *clk; |
| int idx; |
| |
| idx = ptp_interface[0]; |
| clk = net_eth_get_ptp_clock(eth_interfaces[idx]); |
| zassert_not_null(clk, "Clock not found for interface %p\n", |
| eth_interfaces[idx]); |
| |
| idx = ptp_interface[1]; |
| clk = net_eth_get_ptp_clock(eth_interfaces[idx]); |
| zassert_not_null(clk, "Clock not found for interface %p\n", |
| eth_interfaces[idx]); |
| |
| clk = net_eth_get_ptp_clock(eth_interfaces[non_ptp_interface]); |
| zassert_is_null(clk, "Clock found for interface %p\n", |
| eth_interfaces[non_ptp_interface]); |
| } |
| |
| static void test_ptp_clock_iface(int idx) |
| { |
| int rnd_value = sys_rand32_get(); |
| struct net_ptp_time tm = { |
| .second = 1, |
| .nanosecond = 1, |
| }; |
| struct device *clk; |
| u64_t orig, new_value; |
| |
| clk = net_eth_get_ptp_clock(eth_interfaces[idx]); |
| |
| zassert_not_null(clk, "Clock not found for interface %p\n", |
| eth_interfaces[idx]); |
| |
| ptp_clock_set(clk, &tm); |
| |
| orig = timestamp_to_nsec(&tm); |
| |
| if (rnd_value == 0 || rnd_value < 0) { |
| rnd_value = 2; |
| } |
| |
| ptp_clock_adjust(clk, rnd_value); |
| |
| (void)memset(&tm, 0, sizeof(tm)); |
| ptp_clock_get(clk, &tm); |
| |
| new_value = timestamp_to_nsec(&tm); |
| |
| /* The clock value must be the same after incrementing it */ |
| zassert_equal(orig + rnd_value, new_value, |
| "Time adjust failure (%llu vs %llu)\n", |
| orig + rnd_value, new_value); |
| } |
| |
| static void test_ptp_clock_iface_1(void) |
| { |
| test_ptp_clock_iface(ptp_interface[0]); |
| } |
| |
| static void test_ptp_clock_iface_2(void) |
| { |
| test_ptp_clock_iface(ptp_interface[1]); |
| } |
| |
| void test_main(void) |
| { |
| ztest_test_suite(ptp_clock_test, |
| ztest_unit_test(check_interfaces), |
| ztest_unit_test(address_setup), |
| ztest_unit_test(test_ptp_clock_interfaces), |
| ztest_unit_test(test_ptp_clock_iface_1), |
| ztest_unit_test(test_ptp_clock_iface_2) |
| ); |
| |
| ztest_run_test_suite(ptp_clock_test); |
| } |