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