flexPTP 1.0
An IEEE 1588 PTP implementation designed for microcontrollers
Loading...
Searching...
No Matches
nsd_linux.c
Go to the documentation of this file.
1#include "../../network_stack_driver.h"
2
3#include "../../ptp_defs.h"
4#include "../../task_ptp.h"
5
6#include <arpa/inet.h>
7#include <asm-generic/errno-base.h>
8#include <asm/socket.h>
9#include <fcntl.h>
10#include <linux/ethtool.h>
11#include <linux/net_tstamp.h>
12#include <linux/sockios.h>
13#include <net/if.h>
14#include <netinet/in.h>
15#include <stdio.h>
16#include <string.h>
17#include <sys/ioctl.h>
18#include <sys/socket.h>
19#include <unistd.h>
20#include <inttypes.h>
21
22#include <features.h>
23#include <asm-generic/unistd.h>
24#include <bits/time.h>
25
26#include <linux/if_packet.h>
27#include <netinet/ether.h>
28#include <poll.h>
29#include <pthread.h>
30#include <sys/socket.h>
31#include <sys/timex.h>
32
33#define LINUX_NSD_TS_DEBUG (0) // timestamp debugging
34#define LINUX_NSD_TX_ENQUEUE_DEBUG (0) // transmit enqueue debugging
35
36// initialize sockets with invalid states
37static int primary_event_fd = -1;
38static int primary_general_fd = -1;
39static int pdelay_event_fd = -1;
40static int pdelay_general_fd = -1;
41
42// store current settings
43static PtpTransportType TP = -1;
45static uint8_t p2p_8023_primary_dest[6] = {};
46static uint8_t p2p_8023_pdel_dest[6] = {};
47static bool identical_p2p_8023_dests = false;
48
49static const uint8_t zero_mac[ETH_ALEN] = {};
50
51// interface data
52static uint16_t if_idx; // interface index
53static char if_name[IFNAMSIZ]; // name of the interface
54static uint8_t if_hwaddr[IFHWADDRLEN]; // hardware address of the interface
55static struct sockaddr_in if_ipaddr; // IP-address of the interface
56
57// hardware clock data
58#define PHY_FILE_NAME_SIZE (16)
59static uint16_t phc_index; // index of the PHC
60static char phc_file_name[PHY_FILE_NAME_SIZE]; // PHC device file name
61static int phc_fd; // PHC file descriptor
62static clockid_t phc_clkid; // PHC clock id
63
64// transception management
65static int notif_q[2]; // notification queue
66static int matching_q[2]; // message pointer queue
67static pthread_t transceiver_thread; // thread managing transmission and reception
68static void *nsd_thread(void *arg); // thread function
69
70#define PRINT_HWADDR(a) MSG("%02X:%02X:%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5]);
71
72#define NOTIF_QUIT_TRANSCEIVER_THREAD 'Q'
73
74static void post_notification(char c) {
75 write(notif_q[1], &c, 1);
76}
77
78/* FD <-> CLOCKID conversions (man 2 clock_getres, 'Dynamic clocks' section) */
79#define CLOCKFD 3
80#define FD_TO_CLOCKID(fd) ((clockid_t)((((unsigned int)~fd) << 3) | CLOCKFD))
81#define CLOCKID_TO_FD(clk) ((unsigned int)~((clk) >> 3))
82
83bool linux_nsd_preinit(const char *ifn) {
84 bool init_ok = false;
85
86 // copy interface name
87 strncpy(if_name, ifn, IFNAMSIZ - 1);
88 if_name[IFNAMSIZ - 1] = '\0';
89
90 // create dummy socket
91 int fd = socket(PF_INET, SOCK_DGRAM, 0);
92 if (fd < 0) {
93 MSG("Could not create a dummy socket to retrieve interface data!\n");
94 return false;
95 }
96
97 int err;
98
99 // retrieve the network interface index
100 // man 7 netdevice
101 struct ifreq ifr;
102 memset(&ifr, 0, sizeof(ifr));
103 strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
104 err = ioctl(fd, SIOCGIFINDEX, &ifr);
105 if (err < 0) {
106 MSG("Could not get interface index!\n");
107 goto cleanup;
108 }
109 if_idx = ifr.ifr_ifindex;
110
111 // retrieve the hardware address of the interface
112 // man 7 netdevice
113 memset(&ifr, 0, sizeof(ifr));
114 strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
115 err = ioctl(fd, SIOCGIFHWADDR, &ifr);
116 if (err < 0) {
117 MSG("Failed to retrieve the hardware address!\n");
118 goto cleanup;
119 }
120 memcpy(if_hwaddr, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
121
122 // retrieve the IP address of the interface
123 // man 7 netdevice
124 memset(&ifr, 0, sizeof(ifr));
125 ifr.ifr_addr.sa_family = AF_INET;
126 strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
127 err = ioctl(fd, SIOCGIFADDR, &ifr);
128 if (err < 0) {
129 MSG("Failed to retrieve the interface IP address!\n");
130 goto cleanup;
131 }
132 memcpy(&if_ipaddr, &ifr.ifr_addr, sizeof(struct sockaddr_in));
133
134 // print collected information
135 MSG("Network Interface information\n");
136 MSG("-- Interface: " PTP_COLOR_YELLOW "%s\n" PTP_COLOR_RESET, if_name);
137 MSG(" Hardware address: " PTP_COLOR_CYAN);
139 MSG("\n");
140 MSG(PTP_COLOR_RESET);
141 MSG(" IP-address: " PTP_COLOR_CYAN "%s\n" PTP_COLOR_RESET, inet_ntoa(if_ipaddr.sin_addr));
142
143 // check the hardware timestamp support
144 // https://docs.kernel.org/networking/ethtool-netlink.html#tsinfo-get
145 struct ethtool_ts_info tsi = {.cmd = ETHTOOL_GET_TS_INFO};
146 memset(&ifr, 0, sizeof(ifr));
147 strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
148 ifr.ifr_data = (caddr_t) &tsi;
149 err = ioctl(fd, SIOCETHTOOL, &ifr);
150 if (err < 0) {
151 MSG("Failed to query the interface timestamp capabilities!\n");
152 goto cleanup;
153 }
154
155 // print timestamp information
156 MSG(" Hardware timestamping: ");
157 if ((tsi.so_timestamping & SOF_TIMESTAMPING_TX_HARDWARE) && (tsi.so_timestamping & SOF_TIMESTAMPING_RX_HARDWARE)) {
158 phc_index = tsi.phc_index;
161
163 MSG(" PHC index: " PTP_COLOR_CYAN "%u\n" PTP_COLOR_RESET, phc_index);
164 MSG(" -- corresponding file: " PTP_COLOR_CYAN "%s\n" PTP_COLOR_RESET, phc_file_name);
165 } else {
166 MSG(PTP_COLOR_RED "MISSING\n" PTP_COLOR_RESET);
167 goto cleanup;
168 }
169 MSG("---------------\n\n");
170
171 // open PHC
172 phc_fd = open(phc_file_name, O_RDWR);
173 if (phc_fd < 0) {
174 MSG("Failed to open PHC file!\n");
175 goto cleanup;
176 }
178
179 struct timespec ts;
180 if (clock_gettime(phc_clkid, &ts) < 0) {
181 MSG("Failed to access the PHC time!\n");
182 goto cleanup;
183 }
184
185 // create the notification queue
186 if (pipe(notif_q) < 0) {
187 MSG("Failed to create notification queue!\n");
188 goto cleanup;
189 }
190
191 // create message pointer queue
192 if (pipe(matching_q) < 0) {
193 MSG("Failed to create buffer matching pointer queue!\n");
194 goto cleanup;
195 }
196
197 // clear thread handle
199
200 // initialization done
201 init_ok = true;
202 return init_ok;
203
204cleanup:
205 if (fd > 0) {
206 close(fd);
207 }
208 if (phc_fd > 0) {
209 close(phc_fd);
210 }
211 return init_ok;
212}
213
215 if (phc_fd > 0) {
216 close(phc_fd);
217 phc_fd = 0;
218 }
219}
220
221static void socket_join_igmp(int fd, in_addr_t igmp_addr) {
222 // fill in the multicast assignment request
223 struct ip_mreq mreq;
224 mreq.imr_multiaddr.s_addr = igmp_addr;
225 mreq.imr_interface = if_ipaddr.sin_addr;
226
227 // join the IGMP group
228 // man 7 ip
229 int err = setsockopt(primary_event_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
230 if (err < 0) {
231 // MSG("Could not join the required network group!\n");
232 }
233}
234
235void ptp_nsd_igmp_join_leave(bool join) {
236 // only join IGMP if Transport Type is IP
237 if ((TP == PTP_TP_IPv4) && join) {
238 if (primary_event_fd > 0) {
240 }
241 if (primary_general_fd > 0) {
243 }
244 if (pdelay_event_fd > 0) {
246 }
247 if (pdelay_general_fd > 0) {
249 }
250 }
251
252 // don't have to explicitly leave the IGMP group
253}
254
255static int open_udp_socket(in_addr_t s_addr, uint16_t port, const char *hint) {
256 // prepare socket address
257 struct sockaddr_in addr;
258 memset(&addr, 0, sizeof(addr));
259 addr.sin_family = PF_INET;
260 addr.sin_addr.s_addr = s_addr;
261
262 // create socket
263 // man 2 socket
264 int sfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
265 if (sfd < 0) {
266 MSG("Could not open the %s socket!\n", hint);
267 return -1;
268 }
269
270 // enable the reuseaddr option
271 // man 3 setsockopt
272 int optval = 1;
273 int err = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
274 if (err < 0) {
275 MSG("Could not set the %s socket REUSEADDR option!\n", hint);
276 goto cleanup;
277 }
278
279 // assign the multicast transmit interface with the socket
280 // man 7 ip
281 err = setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_IF, &if_ipaddr.sin_addr, sizeof(struct in_addr));
282 if (err < 0) {
283 MSG("Could not set the %s socket IP_MULTICAST_IF option!\n", hint);
284 goto cleanup;
285 }
286
287 // bind the socket
288 // man 2 bind
289 addr.sin_port = htons(port);
290 err = bind(sfd, (struct sockaddr *) &addr, sizeof(addr));
291 if (err < 0) {
292 MSG("Could not bind the %s socket!\n", hint);
293 goto cleanup;
294 }
295
296 // normal return
297 return sfd;
298
299cleanup:
300 close(sfd);
301 return -1;
302}
303
304static int open_raw_socket(const uint8_t *ethaddr, bool bind_socket, const char *hint) {
305 // create socket
306 // SOCK_DGRAM: use the kernel features to fill in the Ethernet header
307 int sfd = socket(AF_PACKET, SOCK_DGRAM, htons(PTP_ETHERTYPE));
308 if (sfd < 0) {
309 MSG("Could not open the %s socket!\n", hint);
310 return -1;
311 }
312
313 // setup address
314 // man 7 packet
315 struct sockaddr_ll addr;
316 memset(&addr, 0, sizeof(addr));
317 addr.sll_ifindex = if_idx;
318 addr.sll_halen = ETH_ALEN;
319 addr.sll_protocol = htons(PTP_ETHERTYPE);
320 addr.sll_family = AF_PACKET;
321 addr.sll_pkttype = PACKET_MULTICAST;
322 memcpy(addr.sll_addr, ethaddr, ETH_ALEN);
323
324 // bind socket if requested
325 // man 2 bind
326 int err;
327 if (bind_socket) {
328 err = bind(sfd, (struct sockaddr *) &addr, sizeof(addr));
329 if (err < 0) {
330 MSG("Could not bind the %s socket!\n", hint);
331 }
332 }
333
334 // assign the socket the multicast membership
335 // man 6 packet
336 struct packet_mreq mreq;
337 mreq.mr_ifindex = if_idx;
338 mreq.mr_type = PACKET_MR_MULTICAST;
339 mreq.mr_alen = ETH_ALEN;
340 memcpy(mreq.mr_address, ethaddr, ETH_ALEN);
341
342 err = setsockopt(sfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
343 if (err < 0) {
344 MSG("Could not set the %s socket ADD_MEMBERSHIP option!\n", hint);
345 }
346
347 return sfd;
348}
349
350static void enable_timestamping(int sfd) {
351 // enable timestamping on the socket
352 // https://www.kernel.org/doc/html/latest/networking/timestamping.html#scm-timestamping-records
353 int optval = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE;
354 int err = setsockopt(sfd, SOL_SOCKET, SO_TIMESTAMPING, &optval, sizeof(optval));
355 if (err < 0) {
356 MSG("Failed to enable timestamping\n");
357 }
358
359 // enable timestamping in the hardware
360 struct ifreq ifreq;
361 struct hwtstamp_config cfg;
362 memset(&ifreq, 0, sizeof(ifreq));
363 memset(&cfg, 0, sizeof(cfg));
364 strncpy(ifreq.ifr_name, if_name, IFNAMSIZ - 1);
365 ifreq.ifr_data = (void *) &cfg;
366
367 // get current timestamping settings
368 err = ioctl(sfd, SIOCGHWTSTAMP, &ifreq);
369 if (err < 0) {
370 MSG("Failed to get timestamping settings.\n");
371 return;
372 }
373
374 // turn on TX and RX timestamping
375 // https://www.kernel.org/doc/html/latest/networking/timestamping.html#hardware-timestamping-configuration-ethtool-msg-tsconfig-set-get
376 cfg.flags = 0;
377 cfg.tx_type = HWTSTAMP_TX_ON;
378 cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
379 err = ioctl(sfd, SIOCSHWTSTAMP, &ifreq);
380 if (err < 0) {
381 MSG("Failed to set timestamping settings.\n");
382 }
383
384 // enable TX timestamp communication through the socket error queue
385 // man 7 socket
386 optval = 1;
387 err = setsockopt(sfd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &optval, sizeof(optval));
388 if (err < 0) {
389 MSG("Could not enable TX timestamp communication through the error queue!\n");
390 }
391}
392
393#define NAME_BUF_SIZE (256)
395#define MSG_BUF_SIZE (1600)
397#define CTRL_BUF_SIZE (256)
399
400static void clear_revents_by_fd(int fd, unsigned short revent, struct pollfd * pfd, int nfds) {
401 for (int i = 0; i < nfds; i++) {
402 if (pfd[i].fd == fd) {
403 pfd[i].revents &= (short int)(~revent);
404 }
405 }
406}
407
408static void *nsd_thread(void *arg) {
409 bool run = true;
410 while (run) {
411 // populate the poll list
412 struct pollfd pfd[] = {
413 {.fd = notif_q[0], .events = POLLIN},
414 {.fd = primary_event_fd, .events = POLLIN | POLLPRI},
415 {.fd = primary_general_fd, .events = POLLIN},
416 {.fd = pdelay_event_fd, .events = POLLIN | POLLPRI},
417 {.fd = pdelay_general_fd, .events = POLLIN},
418 };
419
420 // pdelay_* sockets have to be listened only in P2P delay mechanism modes
421 const int n = ((DM == PTP_DM_P2P) && !identical_p2p_8023_dests) ? 5 : 3;
422
423 // make the poll
424 const int pret = poll(pfd, n, -1);
425 if (pret > 0) {
426 // notifications
427 if (pfd[0].revents & POLLIN) {
428 char c;
429 read(notif_q[0], &c, sizeof(char));
431 run = false;
432 continue;
433 }
434 }
435
436 // something have happened on the event message socket
437 if ((pfd[1].revents != 0) || (pfd[3].revents != 0)) {
438 // prepare for message reception
439 struct iovec iov = {msg_buf, MSG_BUF_SIZE};
440 struct msghdr msg;
441
442 memset(&msg, 0, sizeof(msg));
443 memset(msg_buf, 0, sizeof(msg_buf));
444
445 msg.msg_name = name_buf;
446 msg.msg_namelen = NAME_BUF_SIZE;
447 msg.msg_iov = &iov;
448 msg.msg_iovlen = 1;
449 msg.msg_control = rx_ctrl_buf;
450 msg.msg_controllen = CTRL_BUF_SIZE;
451
452 // event message TRANSMISSION timestamp feedback PRIMARY
453 // https://www.kernel.org/doc/html/latest/networking/timestamping.html#scm-timestamping-records
454 while ((pfd[1].revents & POLLPRI) || (pfd[3].revents & POLLPRI)) {
455 int fd = (pfd[1].revents & POLLPRI) ? primary_event_fd : pdelay_event_fd; // select socket
456 ssize_t size = recvmsg(fd, &msg, MSG_ERRQUEUE);
457 // get transmit timestamps from the error queue
458 struct cmsghdr *cm; // iterate over the chain of control messages
459 for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) {
460 int level = cm->cmsg_level;
461 int type = cm->cmsg_type;
462 if ((level == SOL_SOCKET) && (type == SO_TIMESTAMPING)) {
463 struct timespec *ts = (struct timespec *) CMSG_DATA(cm);
464 // get data from the timestamp control message
465 uint32_t uid = 0;
466 read(matching_q[0], &uid, sizeof(uint32_t));
467
468 struct timespec now;
469 clock_gettime(CLOCK_REALTIME, &now);
470 CLILOG(LINUX_NSD_TS_DEBUG, "[%lu.%09lu] TX TS: (%u) %lu.%09lu\n", now.tv_sec, now.tv_nsec,
471 uid, ts[2].tv_sec, ts[2].tv_nsec);
472
473 // invoke the transmit timestamp callback, the hardware timestamp always comes in ts[2]
474 ptp_transmit_timestamp_cb(uid, ts[2].tv_sec, ts[2].tv_nsec);
475 }
476 }
477
478 // clear relevant events
479 clear_revents_by_fd(fd, POLLPRI, pfd, n);
480 }
481
482 // event message RECEPTION
483 while ((pfd[1].revents & POLLIN) || (pfd[3].revents & POLLIN)) {
484 int fd = (pfd[1].revents & POLLIN) ? primary_event_fd : pdelay_event_fd; // select socket
485 ssize_t size = recvmsg(fd, &msg, 0);
486 struct cmsghdr *cm;
487 struct timespec ts;
488 memset(&ts, 0, sizeof(ts));
489 bool ts_found = false;
490 for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) {
491 int level = cm->cmsg_level;
492 int type = cm->cmsg_type;
493 if ((level == SOL_SOCKET) && (type == SO_TIMESTAMPING)) {
494 struct timespec *tsa = (struct timespec *) CMSG_DATA(cm); // get pointer to the timestamps
495 ts = tsa[2]; // extract the hardware timestamp
496 ts_found = true; // indicate that timestamp was found
497 CLILOG(LINUX_NSD_TS_DEBUG, "RX TS: %lu.%09lu\n", ts.tv_sec, ts.tv_nsec);
498 }
499 }
500
501 // forward only event messages with timestamps
502 if (((TP == PTP_TP_IPv4) && (ts_found)) || (TP == PTP_TP_802_3)) {
503 ptp_receive_enqueue(msg_buf, size, ts.tv_sec, ts.tv_nsec, TP);
504 }
505
506 // clear relevant events
507 clear_revents_by_fd(fd, POLLIN, pfd, n);
508 }
509 }
510
511 // general message reception (in IPv4 mode)
512 if (TP == PTP_TP_IPv4) {
513 if (pfd[2].revents & POLLIN) {
514 ssize_t size = recv(primary_general_fd, msg_buf, MSG_BUF_SIZE, 0);
515 if (size > 0) {
516 ptp_receive_enqueue(msg_buf, size, 0, 0, TP);
517 }
518 }
519 }
520 }
521 }
522
523 return NULL;
524}
525
526void ptp_nsd_init(const NsdInitSettings *init) {
527 // leave current IGMP group if applicable
529
530 // first, close all open connection blocks (zero CBDs won't cause trouble)
531 if (transceiver_thread != 0) {
533 pthread_join(transceiver_thread, NULL);
535 }
536 if (primary_event_fd > 0) {
537 close(primary_event_fd);
538 primary_event_fd = -1;
539 }
540 if (primary_general_fd > 0) {
541 close(primary_general_fd);
543 }
544 if (pdelay_event_fd > 0) {
545 close(pdelay_event_fd);
546 pdelay_event_fd = -1;
547 }
548 if (pdelay_general_fd > 0) {
549 close(pdelay_general_fd);
551 }
552
553 // calling either parameter with -1 just closes connections
554 if ((init->tp == -1) || (init->dm == -1)) {
555 // message transmission and reception is turned off
556 TP = -1;
557 DM = -1;
558 return;
559 }
560
561 // if custom P2P 802.3 destination are given, store them
562 if (memcmp(init->primary_p2p_8023_dest, zero_mac, ETH_ALEN) != 0) {
563 memcpy(p2p_8023_primary_dest, &init->primary_p2p_8023_dest, ETH_ALEN);
564 } else {
566 }
567
568 if (memcmp(init->pdelay_p2p_8023_dest, zero_mac, ETH_ALEN) != 0) {
569 memcpy(p2p_8023_pdel_dest, &init->pdelay_p2p_8023_dest, ETH_ALEN);
570 } else {
572 }
573
574 // detect if Primary and PDelay request addresses were the same
576
577 // open event and general connections
578 if (init->tp == PTP_TP_IPv4) {
581
582 if (init->dm == PTP_DM_P2P) {
585 }
586 } else if (init->tp == PTP_TP_802_3) {
587 primary_event_fd = open_raw_socket(PTP_ETHERNET_PRIMARY, true, "EVENT_PRIMARY");
588 primary_general_fd = open_raw_socket(PTP_ETHERNET_PRIMARY, false, "GENERAL_PRIMARY");
589
590 if ((init->dm == PTP_DM_P2P) && !identical_p2p_8023_dests) {
593 }
594 }
595
596 // enable timestamping on the event socket
598
599 // create the transceiver thread
601 if (pthread_create(&transceiver_thread, NULL, nsd_thread, NULL) != 0) {
602 MSG("Failed to create the transceiver thread!\n");
603 }
604
605 // store configuration
606 TP = init->tp;
607 DM = init->dm;
608
609 // join new IGMP group
611}
612
613void ptp_nsd_transmit_msg(RawPtpMessage *pMsg, uint32_t uid) {
614 if (pMsg == NULL) {
615 return;
616 }
617
618 // indicates if the transmission was successful
619 bool send_ok = false;
620
621 // get the message class
622 PtpMessageClass mc = pMsg->tx_mc;
623 PtpMessageType mt = pMsg->tx_mt;
624
625 // select connection by message type
627
628 // is it a Peer Delay Mechanism related message?
629 bool isPDel_ = (mt == PTP_MT_PDelay_Req) || (mt == PTP_MT_PDelay_Resp) || (mt == PTP_MT_PDelay_Resp_Follow_Up);
630
631 // narrow down by transport type
632 if (TP == PTP_TP_IPv4) {
633 // configure the address
634 struct sockaddr_in addr;
635 memset(&addr, 0, sizeof(addr));
636 addr.sin_family = PF_INET;
637 addr.sin_addr.s_addr = isPDel_ ? PTP_IGMP_PEER_DELAY : PTP_IGMP_PRIMARY;
638 // select destination IP-address by PDel*/primary message types
639 addr.sin_port = htons((mc == PTP_MC_EVENT) ? PTP_PORT_EVENT : PTP_PORT_GENERAL); // select port by message class
640
641 // send packet
642 if (sendto(sfd, pMsg->data, pMsg->size, 0, (struct sockaddr *) &addr, sizeof(addr)) == pMsg->size) {
643 send_ok = true;
644 }
645 } else if (TP == PTP_TP_802_3) {
646 // destination address
647 const uint8_t *ethaddr = isPDel_ ? PTP_ETHERNET_PEER_DELAY : PTP_ETHERNET_PRIMARY; // select destination address by PDel*/primary message types
648
649 // prepare address object
650 struct sockaddr_ll addr;
651 memset(&addr, 0, sizeof(addr));
652 addr.sll_ifindex = if_idx;
653 addr.sll_halen = ETH_ALEN;
654 addr.sll_protocol = htons(PTP_ETHERTYPE);
655 memcpy(addr.sll_addr, ethaddr, ETH_ALEN);
656
657 if (sendto(sfd, pMsg->data, pMsg->size, 0, (struct sockaddr *) &addr, sizeof(addr)) == pMsg->size) {
658 send_ok = true;
659 }
660 }
661
662 // send message UID to the queue or invoke the TX callback
663 if (send_ok) {
664 struct timespec now;
665 clock_gettime(CLOCK_REALTIME, &now);
666 CLILOG(LINUX_NSD_TX_ENQUEUE_DEBUG, "[%lu.%09lu] TX enqueue! %u\n", now.tv_sec, now.tv_nsec, uid);
667 if (mc == PTP_MC_EVENT) {
668 write(matching_q[1], &uid, sizeof(uint32_t));
669 } else if (mc == PTP_MC_GENERAL) {
670 ptp_transmit_timestamp_cb(uid, 0, 0);
671 }
672 }
673}
674
676 memcpy(hwa, if_hwaddr, IFHWADDRLEN);
677}
678
679// ------------------------
680
681/* man 2 clock_adjtime (ADJ_FREQUENCY) */
682#define PPB_TO_TUNING_SCALER (((double)(1 << 16)) / 1000.0)
683
684void linux_adjust_clock(double tuning_ppb) {
685 struct timex tx;
686 memset(&tx, 0, sizeof(struct timex));
687 if (clock_adjtime(phc_clkid, &tx) < 0) {
688 MSG("Failed to retrieve PHC tuning!\n");
689 }
690 memset(&tx, 0, sizeof(struct timex));
691 tx.modes = ADJ_FREQUENCY;
692 tx.freq = (__syscall_slong_t) (tuning_ppb * PPB_TO_TUNING_SCALER);
693 if (clock_adjtime(phc_clkid, &tx) != 0) {
694 MSG("Failed to adjust PHC frequency!\n");
695 }
696}
697
698void linux_set_time(uint32_t seconds, uint32_t nanoseconds) {
699 struct timespec ts = {.tv_sec = seconds, .tv_nsec = nanoseconds};
700 if (clock_settime(phc_clkid, &ts) < 0) {
701 MSG("Failed to set the PHC time!\n");
702 }
703}
704
706 struct timespec ts;
707 if (clock_gettime(phc_clkid, &ts) < 0) {
708 MSG("Failed to get the PHC time!\n");
709 }
710
711 pTime->sec = ts.tv_sec;
712 pTime->nanosec = ts.tv_nsec;
713}
#define CLILOG(en,...)
#define FLEXPTP_SNPRINTF(...)
static struct sockaddr_in if_ipaddr
Definition: nsd_linux.c:55
void linux_nsd_cleanup(void)
Definition: nsd_linux.c:214
void ptp_nsd_transmit_msg(RawPtpMessage *pMsg, uint32_t uid)
Definition: nsd_linux.c:613
void ptp_nsd_init(const NsdInitSettings *init)
Definition: nsd_linux.c:526
#define NAME_BUF_SIZE
Definition: nsd_linux.c:393
static int primary_event_fd
Definition: nsd_linux.c:37
static int open_udp_socket(in_addr_t s_addr, uint16_t port, const char *hint)
Definition: nsd_linux.c:255
static void enable_timestamping(int sfd)
Definition: nsd_linux.c:350
static int notif_q[2]
Definition: nsd_linux.c:65
static uint16_t if_idx
Definition: nsd_linux.c:52
static PtpDelayMechanism DM
Definition: nsd_linux.c:44
static int open_raw_socket(const uint8_t *ethaddr, bool bind_socket, const char *hint)
Definition: nsd_linux.c:304
void ptp_nsd_igmp_join_leave(bool join)
Definition: nsd_linux.c:235
#define PPB_TO_TUNING_SCALER
Definition: nsd_linux.c:682
void linux_get_time(TimestampU *pTime)
Definition: nsd_linux.c:705
void linux_set_time(uint32_t seconds, uint32_t nanoseconds)
Definition: nsd_linux.c:698
static clockid_t phc_clkid
Definition: nsd_linux.c:62
#define LINUX_NSD_TS_DEBUG
Definition: nsd_linux.c:33
void linux_adjust_clock(double tuning_ppb)
Definition: nsd_linux.c:684
#define PRINT_HWADDR(a)
Definition: nsd_linux.c:70
static uint8_t p2p_8023_pdel_dest[6]
Definition: nsd_linux.c:46
bool linux_nsd_preinit(const char *ifn)
Definition: nsd_linux.c:83
static char msg_buf[(1600)]
Definition: nsd_linux.c:396
#define CTRL_BUF_SIZE
Definition: nsd_linux.c:397
void ptp_nsd_get_interface_address(uint8_t *hwa)
Definition: nsd_linux.c:675
static char name_buf[(256)]
Definition: nsd_linux.c:394
#define MSG_BUF_SIZE
Definition: nsd_linux.c:395
static char if_name[IFNAMSIZ]
Definition: nsd_linux.c:53
static void socket_join_igmp(int fd, in_addr_t igmp_addr)
Definition: nsd_linux.c:221
#define NOTIF_QUIT_TRANSCEIVER_THREAD
Definition: nsd_linux.c:72
static void clear_revents_by_fd(int fd, unsigned short revent, struct pollfd *pfd, int nfds)
Definition: nsd_linux.c:400
static uint8_t if_hwaddr[IFHWADDRLEN]
Definition: nsd_linux.c:54
static int matching_q[2]
Definition: nsd_linux.c:66
static PtpTransportType TP
Definition: nsd_linux.c:43
static bool identical_p2p_8023_dests
Definition: nsd_linux.c:47
static int pdelay_general_fd
Definition: nsd_linux.c:40
static int phc_fd
Definition: nsd_linux.c:61
static int pdelay_event_fd
Definition: nsd_linux.c:39
static uint16_t phc_index
Definition: nsd_linux.c:59
static pthread_t transceiver_thread
Definition: nsd_linux.c:67
static uint8_t p2p_8023_primary_dest[6]
Definition: nsd_linux.c:45
static char rx_ctrl_buf[(256)]
Definition: nsd_linux.c:398
static int primary_general_fd
Definition: nsd_linux.c:38
#define FD_TO_CLOCKID(fd)
Definition: nsd_linux.c:80
static void post_notification(char c)
Definition: nsd_linux.c:74
#define PHY_FILE_NAME_SIZE
Definition: nsd_linux.c:58
static const uint8_t zero_mac[ETH_ALEN]
Definition: nsd_linux.c:49
static char phc_file_name[(16)]
Definition: nsd_linux.c:60
#define LINUX_NSD_TX_ENQUEUE_DEBUG
Definition: nsd_linux.c:34
static void * nsd_thread(void *arg)
Definition: nsd_linux.c:408
const uint8_t PTP_ETHERNET_PEER_DELAY[6]
PTP's L2 Peer_Delay Ethernet address.
Definition: ptp_defs.c:4
const uint8_t PTP_ETHERNET_PRIMARY[6]
PTP's L2 Primary Ethernet address.
Definition: ptp_defs.c:3
const ip_addr_t PTP_IGMP_PRIMARY
Primary IGMP address.
#define PTP_PORT_EVENT
PTP event message port.
Definition: ptp_defs.h:45
#define PTP_COLOR_GREEN
Green.
Definition: ptp_defs.h:206
#define PTP_ETHERTYPE
PTP EtherType.
Definition: ptp_defs.h:37
#define PTP_COLOR_CYAN
Cyan.
Definition: ptp_defs.h:213
#define PTP_PORT_GENERAL
PTP general message port.
Definition: ptp_defs.h:46
#define PTP_COLOR_RESET
Reset colors.
Definition: ptp_defs.h:214
#define PTP_COLOR_RED
< Define this to override PTP_COLOR_* macros
Definition: ptp_defs.h:204
#define PTP_COLOR_YELLOW
Yellow.
Definition: ptp_defs.h:208
const ip_addr_t PTP_IGMP_PEER_DELAY
Peer_Delay IGMP address.
PtpTransportType
PTP transport type enumeration.
Definition: ptp_types.h:136
@ PTP_TP_IPv4
IPv4 Transport Type.
Definition: ptp_types.h:137
@ PTP_TP_802_3
Ethernet Transport Type.
Definition: ptp_types.h:138
PtpMessageClass
Enumeration for different PTP message classes.
Definition: ptp_types.h:160
@ PTP_MC_EVENT
Event Message Class.
Definition: ptp_types.h:161
@ PTP_MC_GENERAL
General Message Class.
Definition: ptp_types.h:162
PtpMessageType
PTP packet type enumeration.
Definition: ptp_types.h:37
@ PTP_MT_PDelay_Resp
Peer Delay Response.
Definition: ptp_types.h:41
@ PTP_MT_PDelay_Req
Peer Delay Request.
Definition: ptp_types.h:40
@ PTP_MT_PDelay_Resp_Follow_Up
Peer Delay Response Follow Up.
Definition: ptp_types.h:44
PtpDelayMechanism
PTP Delay mechanism enumeration.
Definition: ptp_types.h:144
@ PTP_DM_P2P
Peer-to-Peer Delay Mechanism.
Definition: ptp_types.h:146
Network Stack Driver initialization object.
uint8_t primary_p2p_8023_dest[6]
Destination address for primary P2P messages over Ethernet.
uint8_t pdelay_p2p_8023_dest[6]
Destination address for PDel* P2P messages over Ethernet.
PtpDelayMechanism dm
Delay Mechanism.
PtpTransportType tp
Transport Type.
PtpMessageClass tx_mc
transmit message class
Definition: ptp_types.h:195
PtpMessageType tx_mt
transmit message type
Definition: ptp_types.h:196
uint32_t size
Packet size.
Definition: ptp_types.h:188
uint8_t data[(128)]
raw packet data
Definition: ptp_types.h:199
Timestamp (unsigned)
Definition: timeutils.h:24
uint32_t nanosec
nanoseconds
Definition: timeutils.h:26
uint64_t sec
seconds
Definition: timeutils.h:25
void ptp_receive_enqueue(const void *pPayload, uint32_t len, uint32_t ts_sec, uint32_t ts_ns, int tp)
Definition: task_ptp.c:474
void ptp_transmit_timestamp_cb(uint32_t uid, uint32_t seconds, uint32_t nanoseconds)
Definition: task_ptp.c:604