1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /**
8 * @file
9 *
10 * Ethernet driver for native posix board. This is meant for network
11 * connectivity between host and Zephyr.
12 */
13
14 #define LOG_MODULE_NAME eth_posix
15 #define LOG_LEVEL CONFIG_ETHERNET_LOG_LEVEL
16
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
19
20 #include <stdio.h>
21
22 #include <zephyr/kernel.h>
23 #include <stdbool.h>
24 #include <errno.h>
25 #include <stddef.h>
26
27 #include <zephyr/net/net_pkt.h>
28 #include <zephyr/net/net_core.h>
29 #include <zephyr/net/net_if.h>
30 #include <zephyr/net/ethernet.h>
31 #include <ethernet/eth_stats.h>
32
33 #include <zephyr/drivers/ptp_clock.h>
34 #include <zephyr/net/gptp.h>
35 #include <zephyr/net/lldp.h>
36
37 #include "eth_native_posix_priv.h"
38 #include "nsi_host_trampolines.h"
39 #include "eth.h"
40
41 #define NET_BUF_TIMEOUT K_MSEC(100)
42
43 #if defined(CONFIG_NET_VLAN)
44 #define ETH_HDR_LEN sizeof(struct net_eth_vlan_hdr)
45 #else
46 #define ETH_HDR_LEN sizeof(struct net_eth_hdr)
47 #endif
48
49 struct eth_context {
50 uint8_t recv[NET_ETH_MTU + ETH_HDR_LEN];
51 uint8_t send[NET_ETH_MTU + ETH_HDR_LEN];
52 uint8_t mac_addr[6];
53 struct net_linkaddr ll_addr;
54 struct net_if *iface;
55 const char *if_name;
56 k_tid_t rx_thread;
57 struct z_thread_stack_element *rx_stack;
58 size_t rx_stack_size;
59 int dev_fd;
60 bool init_done;
61 bool status;
62 bool promisc_mode;
63
64 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
65 struct net_stats_eth stats;
66 #endif
67 #if defined(CONFIG_ETH_NATIVE_POSIX_PTP_CLOCK)
68 const struct device *ptp_clock;
69 #endif
70 };
71
72 #define DEFINE_RX_THREAD(x, _) \
73 K_KERNEL_STACK_DEFINE(rx_thread_stack_##x, \
74 CONFIG_ARCH_POSIX_RECOMMENDED_STACK_SIZE);\
75 static struct k_thread rx_thread_data_##x
76
77 LISTIFY(CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT, DEFINE_RX_THREAD, (;), _);
78
79 #if defined(CONFIG_NET_GPTP)
need_timestamping(struct gptp_hdr * hdr)80 static bool need_timestamping(struct gptp_hdr *hdr)
81 {
82 switch (hdr->message_type) {
83 case GPTP_SYNC_MESSAGE:
84 case GPTP_PATH_DELAY_RESP_MESSAGE:
85 return true;
86 default:
87 return false;
88 }
89 }
90
check_gptp_msg(struct net_if * iface,struct net_pkt * pkt,bool is_tx)91 static struct gptp_hdr *check_gptp_msg(struct net_if *iface,
92 struct net_pkt *pkt,
93 bool is_tx)
94 {
95 uint8_t *msg_start = net_pkt_data(pkt);
96 struct gptp_hdr *gptp_hdr;
97 int eth_hlen;
98
99 #if defined(CONFIG_NET_VLAN)
100 if (net_eth_get_vlan_status(iface)) {
101 struct net_eth_vlan_hdr *hdr_vlan;
102
103 hdr_vlan = (struct net_eth_vlan_hdr *)msg_start;
104 if (ntohs(hdr_vlan->type) != NET_ETH_PTYPE_PTP) {
105 return NULL;
106 }
107
108 eth_hlen = sizeof(struct net_eth_vlan_hdr);
109 } else
110 #endif
111 {
112 struct net_eth_hdr *hdr;
113
114 hdr = (struct net_eth_hdr *)msg_start;
115 if (ntohs(hdr->type) != NET_ETH_PTYPE_PTP) {
116 return NULL;
117 }
118
119
120 eth_hlen = sizeof(struct net_eth_hdr);
121 }
122
123 /* In TX, the first net_buf contains the Ethernet header
124 * and the actual gPTP header is in the second net_buf.
125 * In RX, the Ethernet header + other headers are in the
126 * first net_buf.
127 */
128 if (is_tx) {
129 if (pkt->frags->frags == NULL) {
130 return false;
131 }
132
133 gptp_hdr = (struct gptp_hdr *)pkt->frags->frags->data;
134 } else {
135 gptp_hdr = (struct gptp_hdr *)(pkt->frags->data + eth_hlen);
136 }
137
138 return gptp_hdr;
139 }
140
update_pkt_priority(struct gptp_hdr * hdr,struct net_pkt * pkt)141 static void update_pkt_priority(struct gptp_hdr *hdr, struct net_pkt *pkt)
142 {
143 if (GPTP_IS_EVENT_MSG(hdr->message_type)) {
144 net_pkt_set_priority(pkt, NET_PRIORITY_CA);
145 } else {
146 net_pkt_set_priority(pkt, NET_PRIORITY_IC);
147 }
148 }
149
update_gptp(struct net_if * iface,struct net_pkt * pkt,bool send)150 static void update_gptp(struct net_if *iface, struct net_pkt *pkt,
151 bool send)
152 {
153 struct net_ptp_time timestamp;
154 struct gptp_hdr *hdr;
155 int ret;
156
157 ret = eth_clock_gettime(×tamp.second, ×tamp.nanosecond);
158 if (ret < 0) {
159 return;
160 }
161
162 net_pkt_set_timestamp(pkt, ×tamp);
163
164 hdr = check_gptp_msg(iface, pkt, send);
165 if (!hdr) {
166 return;
167 }
168
169 if (send) {
170 ret = need_timestamping(hdr);
171 if (ret) {
172 net_if_add_tx_timestamp(pkt);
173 }
174 } else {
175 update_pkt_priority(hdr, pkt);
176 }
177 }
178 #else
179 #define update_gptp(iface, pkt, send)
180 #endif /* CONFIG_NET_GPTP */
181
eth_send(const struct device * dev,struct net_pkt * pkt)182 static int eth_send(const struct device *dev, struct net_pkt *pkt)
183 {
184 struct eth_context *ctx = dev->data;
185 int count = net_pkt_get_len(pkt);
186 int ret;
187
188 ret = net_pkt_read(pkt, ctx->send, count);
189 if (ret) {
190 return ret;
191 }
192
193 update_gptp(net_pkt_iface(pkt), pkt, true);
194
195 LOG_DBG("Send pkt %p len %d", pkt, count);
196
197 ret = nsi_host_write(ctx->dev_fd, ctx->send, count);
198 if (ret < 0) {
199 LOG_DBG("Cannot send pkt %p (%d)", pkt, ret);
200 }
201
202 return ret < 0 ? ret : 0;
203 }
204
eth_get_mac(struct eth_context * ctx)205 static struct net_linkaddr *eth_get_mac(struct eth_context *ctx)
206 {
207 ctx->ll_addr.addr = ctx->mac_addr;
208 ctx->ll_addr.len = sizeof(ctx->mac_addr);
209
210 return &ctx->ll_addr;
211 }
212
get_iface(struct eth_context * ctx,uint16_t vlan_tag)213 static inline struct net_if *get_iface(struct eth_context *ctx,
214 uint16_t vlan_tag)
215 {
216 #if defined(CONFIG_NET_VLAN)
217 struct net_if *iface;
218
219 iface = net_eth_get_vlan_iface(ctx->iface, vlan_tag);
220 if (!iface) {
221 return ctx->iface;
222 }
223
224 return iface;
225 #else
226 ARG_UNUSED(vlan_tag);
227
228 return ctx->iface;
229 #endif
230 }
231
232 #if defined(CONFIG_NET_VLAN)
prepare_vlan_pkt(struct eth_context * ctx,int count,uint16_t * vlan_tag,int * status)233 static struct net_pkt *prepare_vlan_pkt(struct eth_context *ctx,
234 int count, uint16_t *vlan_tag, int *status)
235 {
236 struct net_eth_vlan_hdr *hdr = (struct net_eth_vlan_hdr *)ctx->recv;
237 struct net_pkt *pkt;
238 uint8_t pos;
239
240 if (IS_ENABLED(CONFIG_ETH_NATIVE_POSIX_VLAN_TAG_STRIP)) {
241 count -= NET_ETH_VLAN_HDR_SIZE;
242 }
243
244 pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, count,
245 AF_UNSPEC, 0, NET_BUF_TIMEOUT);
246 if (!pkt) {
247 *status = -ENOMEM;
248 return NULL;
249 }
250
251 net_pkt_set_vlan_tci(pkt, ntohs(hdr->vlan.tci));
252 *vlan_tag = net_pkt_vlan_tag(pkt);
253
254 pos = 0;
255
256 if (IS_ENABLED(CONFIG_ETH_NATIVE_POSIX_VLAN_TAG_STRIP)) {
257 if (net_pkt_write(pkt, ctx->recv,
258 2 * sizeof(struct net_eth_addr))) {
259 goto error;
260 }
261
262 pos = (2 * sizeof(struct net_eth_addr)) + NET_ETH_VLAN_HDR_SIZE;
263 count -= (2 * sizeof(struct net_eth_addr));
264 }
265
266 if (net_pkt_write(pkt, ctx->recv + pos, count)) {
267 goto error;
268 }
269
270 #if CONFIG_NET_TC_RX_COUNT > 1
271 {
272 enum net_priority prio;
273
274 prio = net_vlan2priority(net_pkt_vlan_priority(pkt));
275 net_pkt_set_priority(pkt, prio);
276 }
277 #endif
278
279 *status = 0;
280
281 LOG_DBG("Recv pkt %p len %d", pkt, count);
282
283 return pkt;
284
285 error:
286 net_pkt_unref(pkt);
287 *status = -ENOBUFS;
288 return NULL;
289 }
290 #endif
291
prepare_non_vlan_pkt(struct eth_context * ctx,int count,int * status)292 static struct net_pkt *prepare_non_vlan_pkt(struct eth_context *ctx,
293 int count, int *status)
294 {
295 struct net_pkt *pkt;
296
297 pkt = net_pkt_rx_alloc_with_buffer(ctx->iface, count,
298 AF_UNSPEC, 0, NET_BUF_TIMEOUT);
299 if (!pkt) {
300 *status = -ENOMEM;
301 return NULL;
302 }
303
304 if (net_pkt_write(pkt, ctx->recv, count)) {
305 net_pkt_unref(pkt);
306 *status = -ENOBUFS;
307 return NULL;
308 }
309
310 *status = 0;
311
312 LOG_DBG("Recv pkt %p len %d", pkt, count);
313
314 return pkt;
315 }
316
read_data(struct eth_context * ctx,int fd)317 static int read_data(struct eth_context *ctx, int fd)
318 {
319 uint16_t vlan_tag = NET_VLAN_TAG_UNSPEC;
320 struct net_if *iface;
321 struct net_pkt *pkt = NULL;
322 int status;
323 int count;
324
325 count = nsi_host_read(fd, ctx->recv, sizeof(ctx->recv));
326 if (count <= 0) {
327 return 0;
328 }
329
330 #if defined(CONFIG_NET_VLAN)
331 {
332 struct net_eth_hdr *hdr = (struct net_eth_hdr *)(ctx->recv);
333
334 if (ntohs(hdr->type) == NET_ETH_PTYPE_VLAN) {
335 pkt = prepare_vlan_pkt(ctx, count, &vlan_tag, &status);
336 if (!pkt) {
337 return status;
338 }
339 } else {
340 pkt = prepare_non_vlan_pkt(ctx, count, &status);
341 if (!pkt) {
342 return status;
343 }
344
345 net_pkt_set_vlan_tci(pkt, 0);
346 }
347 }
348 #else
349 {
350 pkt = prepare_non_vlan_pkt(ctx, count, &status);
351 if (!pkt) {
352 return status;
353 }
354 }
355 #endif
356
357 iface = get_iface(ctx, vlan_tag);
358
359 update_gptp(iface, pkt, false);
360
361 if (net_recv_data(iface, pkt) < 0) {
362 net_pkt_unref(pkt);
363 }
364
365 return 0;
366 }
367
eth_rx(void * p1,void * p2,void * p3)368 static void eth_rx(void *p1, void *p2, void *p3)
369 {
370 ARG_UNUSED(p2);
371 ARG_UNUSED(p3);
372
373 struct eth_context *ctx = p1;
374 LOG_DBG("Starting ZETH RX thread");
375
376 while (1) {
377 if (net_if_is_up(ctx->iface)) {
378 while (!eth_wait_data(ctx->dev_fd)) {
379 read_data(ctx, ctx->dev_fd);
380 k_yield();
381 }
382 }
383
384 k_sleep(K_MSEC(CONFIG_ETH_NATIVE_POSIX_RX_TIMEOUT));
385 }
386 }
387
388 #if defined(CONFIG_THREAD_MAX_NAME_LEN)
389 #define THREAD_MAX_NAME_LEN CONFIG_THREAD_MAX_NAME_LEN
390 #else
391 #define THREAD_MAX_NAME_LEN 1
392 #endif
393
create_rx_handler(struct eth_context * ctx)394 static void create_rx_handler(struct eth_context *ctx)
395 {
396 k_thread_create(ctx->rx_thread,
397 ctx->rx_stack,
398 ctx->rx_stack_size,
399 eth_rx,
400 ctx, NULL, NULL, K_PRIO_COOP(14),
401 0, K_NO_WAIT);
402
403 if (IS_ENABLED(CONFIG_THREAD_NAME)) {
404 char name[THREAD_MAX_NAME_LEN];
405
406 snprintk(name, sizeof(name), "eth_native_posix_rx-%s",
407 ctx->if_name);
408 k_thread_name_set(ctx->rx_thread, name);
409 }
410 }
411
eth_iface_init(struct net_if * iface)412 static void eth_iface_init(struct net_if *iface)
413 {
414 struct eth_context *ctx = net_if_get_device(iface)->data;
415 struct net_linkaddr *ll_addr = eth_get_mac(ctx);
416
417 /* The iface pointer in context should contain the main interface
418 * if the VLANs are enabled.
419 */
420 if (ctx->iface == NULL) {
421 ctx->iface = iface;
422 }
423
424 ethernet_init(iface);
425
426 if (ctx->init_done) {
427 return;
428 }
429
430 net_lldp_set_lldpdu(iface);
431
432 ctx->init_done = true;
433
434 #if defined(CONFIG_ETH_NATIVE_POSIX_RANDOM_MAC)
435 /* 00-00-5E-00-53-xx Documentation RFC 7042 */
436 gen_random_mac(ctx->mac_addr, 0x00, 0x00, 0x5E);
437
438 ctx->mac_addr[3] = 0x00;
439 ctx->mac_addr[4] = 0x53;
440
441 /* The TUN/TAP setup script will by default set the MAC address of host
442 * interface to 00:00:5E:00:53:FF so do not allow that.
443 */
444 if (ctx->mac_addr[5] == 0xff) {
445 ctx->mac_addr[5] = 0x01;
446 }
447 #else
448 /* Difficult to configure MAC addresses any sane way if we have more
449 * than one network interface.
450 */
451 BUILD_ASSERT(CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT == 1,
452 "Cannot have static MAC if interface count > 1");
453
454 if (CONFIG_ETH_NATIVE_POSIX_MAC_ADDR[0] != 0) {
455 if (net_bytes_from_str(ctx->mac_addr, sizeof(ctx->mac_addr),
456 CONFIG_ETH_NATIVE_POSIX_MAC_ADDR) < 0) {
457 LOG_ERR("Invalid MAC address %s",
458 CONFIG_ETH_NATIVE_POSIX_MAC_ADDR);
459 }
460 }
461 #endif
462
463 /* If we have only one network interface, then use the name
464 * defined in the Kconfig directly. This way there is no need to
465 * change the documentation etc. and break things.
466 */
467 if (CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT == 1) {
468 ctx->if_name = CONFIG_ETH_NATIVE_POSIX_DRV_NAME;
469 }
470
471 LOG_DBG("Interface %p using \"%s\"", iface, ctx->if_name);
472
473 net_if_set_link_addr(iface, ll_addr->addr, ll_addr->len,
474 NET_LINK_ETHERNET);
475
476 ctx->dev_fd = eth_iface_create(CONFIG_ETH_NATIVE_POSIX_DEV_NAME, ctx->if_name, false);
477 if (ctx->dev_fd < 0) {
478 LOG_ERR("Cannot create %s (%d)", ctx->if_name, -errno);
479 } else {
480 /* Create a thread that will handle incoming data from host */
481 create_rx_handler(ctx);
482 }
483 }
484
485 static
eth_posix_native_get_capabilities(const struct device * dev)486 enum ethernet_hw_caps eth_posix_native_get_capabilities(const struct device *dev)
487 {
488 ARG_UNUSED(dev);
489
490 return ETHERNET_TXTIME
491 #if defined(CONFIG_NET_VLAN)
492 | ETHERNET_HW_VLAN
493 #endif
494 #if defined(CONFIG_ETH_NATIVE_POSIX_VLAN_TAG_STRIP)
495 | ETHERNET_HW_VLAN_TAG_STRIP
496 #endif
497 #if defined(CONFIG_ETH_NATIVE_POSIX_PTP_CLOCK)
498 | ETHERNET_PTP
499 #endif
500 #if defined(CONFIG_NET_PROMISCUOUS_MODE)
501 | ETHERNET_PROMISC_MODE
502 #endif
503 #if defined(CONFIG_NET_LLDP)
504 | ETHERNET_LLDP
505 #endif
506 ;
507 }
508
509 #if defined(CONFIG_ETH_NATIVE_POSIX_PTP_CLOCK)
eth_get_ptp_clock(const struct device * dev)510 static const struct device *eth_get_ptp_clock(const struct device *dev)
511 {
512 struct eth_context *context = dev->data;
513
514 return context->ptp_clock;
515 }
516 #endif
517
518 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
get_stats(const struct device * dev)519 static struct net_stats_eth *get_stats(const struct device *dev)
520 {
521 struct eth_context *context = dev->data;
522
523 return &(context->stats);
524 }
525 #endif
526
set_config(const struct device * dev,enum ethernet_config_type type,const struct ethernet_config * config)527 static int set_config(const struct device *dev,
528 enum ethernet_config_type type,
529 const struct ethernet_config *config)
530 {
531 int ret = 0;
532
533 if (IS_ENABLED(CONFIG_NET_PROMISCUOUS_MODE) &&
534 type == ETHERNET_CONFIG_TYPE_PROMISC_MODE) {
535 struct eth_context *context = dev->data;
536
537 if (config->promisc_mode) {
538 if (context->promisc_mode) {
539 return -EALREADY;
540 }
541
542 context->promisc_mode = true;
543 } else {
544 if (!context->promisc_mode) {
545 return -EALREADY;
546 }
547
548 context->promisc_mode = false;
549 }
550
551 ret = eth_promisc_mode(context->if_name,
552 context->promisc_mode);
553 } else if (type == ETHERNET_CONFIG_TYPE_MAC_ADDRESS) {
554 struct eth_context *context = dev->data;
555
556 memcpy(context->mac_addr, config->mac_address.addr,
557 sizeof(context->mac_addr));
558 }
559
560 return ret;
561 }
562
563 #if defined(CONFIG_NET_VLAN)
vlan_setup(const struct device * dev,struct net_if * iface,uint16_t tag,bool enable)564 static int vlan_setup(const struct device *dev, struct net_if *iface,
565 uint16_t tag, bool enable)
566 {
567 if (enable) {
568 net_lldp_set_lldpdu(iface);
569 } else {
570 net_lldp_unset_lldpdu(iface);
571 }
572
573 return 0;
574 }
575 #endif /* CONFIG_NET_VLAN */
576
577 static const struct ethernet_api eth_if_api = {
578 .iface_api.init = eth_iface_init,
579
580 .get_capabilities = eth_posix_native_get_capabilities,
581 .set_config = set_config,
582 .send = eth_send,
583
584 #if defined(CONFIG_NET_VLAN)
585 .vlan_setup = vlan_setup,
586 #endif
587 #if defined(CONFIG_NET_STATISTICS_ETHERNET)
588 .get_stats = get_stats,
589 #endif
590 #if defined(CONFIG_ETH_NATIVE_POSIX_PTP_CLOCK)
591 .get_ptp_clock = eth_get_ptp_clock,
592 #endif
593 };
594
595 #define DEFINE_ETH_DEV_DATA(x, _) \
596 static struct eth_context eth_context_data_##x = { \
597 .if_name = CONFIG_ETH_NATIVE_POSIX_DRV_NAME #x, \
598 .rx_thread = &rx_thread_data_##x, \
599 .rx_stack = rx_thread_stack_##x, \
600 .rx_stack_size = K_KERNEL_STACK_SIZEOF(rx_thread_stack_##x), \
601 }
602
603 LISTIFY(CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT, DEFINE_ETH_DEV_DATA, (;), _);
604
605 #define DEFINE_ETH_DEVICE(x, _) \
606 ETH_NET_DEVICE_INIT(eth_native_posix_##x, \
607 CONFIG_ETH_NATIVE_POSIX_DRV_NAME #x, \
608 NULL, NULL, ð_context_data_##x, NULL, \
609 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
610 ð_if_api, \
611 NET_ETH_MTU)
612
613 LISTIFY(CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT, DEFINE_ETH_DEVICE, (;), _);
614
615 #if defined(CONFIG_ETH_NATIVE_POSIX_PTP_CLOCK)
616
617 #if defined(CONFIG_NET_GPTP)
618 BUILD_ASSERT( \
619 CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT == CONFIG_NET_GPTP_NUM_PORTS, \
620 "Number of network interfaces must match gPTP port count");
621 #endif
622
623 struct ptp_context {
624 struct eth_context *eth_context;
625 };
626
627 #define DEFINE_PTP_DEV_DATA(x, _) \
628 static struct ptp_context ptp_context_##x
629
630 LISTIFY(CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT, DEFINE_PTP_DEV_DATA, (;), _);
631
ptp_clock_set_native_posix(const struct device * clk,struct net_ptp_time * tm)632 static int ptp_clock_set_native_posix(const struct device *clk,
633 struct net_ptp_time *tm)
634 {
635 ARG_UNUSED(clk);
636 ARG_UNUSED(tm);
637
638 /* We cannot set the host device time so this function
639 * does nothing.
640 */
641
642 return 0;
643 }
644
ptp_clock_get_native_posix(const struct device * clk,struct net_ptp_time * tm)645 static int ptp_clock_get_native_posix(const struct device *clk,
646 struct net_ptp_time *tm)
647 {
648 ARG_UNUSED(clk);
649
650 return eth_clock_gettime(&tm->second, &tm->nanosecond);
651 }
652
ptp_clock_adjust_native_posix(const struct device * clk,int increment)653 static int ptp_clock_adjust_native_posix(const struct device *clk,
654 int increment)
655 {
656 ARG_UNUSED(clk);
657 ARG_UNUSED(increment);
658
659 /* We cannot adjust the host device time so this function
660 * does nothing.
661 */
662
663 return 0;
664 }
665
ptp_clock_rate_adjust_native_posix(const struct device * clk,double ratio)666 static int ptp_clock_rate_adjust_native_posix(const struct device *clk,
667 double ratio)
668 {
669 ARG_UNUSED(clk);
670 ARG_UNUSED(ratio);
671
672 /* We cannot adjust the host device time so this function
673 * does nothing.
674 */
675
676 return 0;
677 }
678
679 static const struct ptp_clock_driver_api api = {
680 .set = ptp_clock_set_native_posix,
681 .get = ptp_clock_get_native_posix,
682 .adjust = ptp_clock_adjust_native_posix,
683 .rate_adjust = ptp_clock_rate_adjust_native_posix,
684 };
685
686 #define PTP_INIT_FUNC(x, _) \
687 static int ptp_init_##x(const struct device *port) \
688 { \
689 const struct device *const eth_dev = DEVICE_GET(eth_native_posix_##x); \
690 struct eth_context *context = eth_dev->data; \
691 struct ptp_context *ptp_context = port->data; \
692 \
693 context->ptp_clock = port; \
694 ptp_context->eth_context = context; \
695 \
696 return 0; \
697 }
698
699 LISTIFY(CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT, PTP_INIT_FUNC, (), _)
700
701 #define DEFINE_PTP_DEVICE(x, _) \
702 DEVICE_DEFINE(eth_native_posix_ptp_clock_##x, \
703 PTP_CLOCK_NAME "_" #x, \
704 ptp_init_##x, \
705 NULL, \
706 &ptp_context_##x, \
707 NULL, \
708 POST_KERNEL, \
709 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT, \
710 &api)
711
712 LISTIFY(CONFIG_ETH_NATIVE_POSIX_INTERFACE_COUNT, DEFINE_PTP_DEVICE, (;), _);
713
714 #endif /* CONFIG_ETH_NATIVE_POSIX_PTP_CLOCK */
715