1#include "../../network_stack_driver.h"
3#include "../../ptp_defs.h"
4#include "../../task_ptp.h"
7#include <asm-generic/errno-base.h>
10#include <linux/ethtool.h>
11#include <linux/net_tstamp.h>
12#include <linux/sockios.h>
14#include <netinet/in.h>
18#include <sys/socket.h>
23#include <asm-generic/unistd.h>
26#include <linux/if_packet.h>
27#include <netinet/ether.h>
30#include <sys/socket.h>
33#define LINUX_NSD_TS_DEBUG (0)
34#define LINUX_NSD_TX_ENQUEUE_DEBUG (0)
51#define PHY_FILE_NAME_SIZE (16)
63#define PRINT_HWADDR(a) MSG("%02X:%02X:%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5]);
65#define NOTIF_QUIT_TRANSCEIVER_THREAD 'Q'
73#define FD_TO_CLOCKID(fd) ((clockid_t)((((unsigned int)~fd) << 3) | CLOCKFD))
74#define CLOCKID_TO_FD(clk) ((unsigned int)~((clk) >> 3))
80 strncpy(
if_name, ifn, IFNAMSIZ - 1);
84 int fd = socket(PF_INET, SOCK_DGRAM, 0);
86 MSG(
"Could not create a dummy socket to retrieve interface data!\n");
95 memset(&ifr, 0,
sizeof(ifr));
96 strncpy(ifr.ifr_name,
if_name, IFNAMSIZ - 1);
97 err = ioctl(fd, SIOCGIFINDEX, &ifr);
99 MSG(
"Could not get interface index!\n");
106 memset(&ifr, 0,
sizeof(ifr));
107 strncpy(ifr.ifr_name,
if_name, IFNAMSIZ - 1);
108 err = ioctl(fd, SIOCGIFHWADDR, &ifr);
110 MSG(
"Failed to retrieve the hardware address!\n");
113 memcpy(
if_hwaddr, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
117 memset(&ifr, 0,
sizeof(ifr));
118 ifr.ifr_addr.sa_family = AF_INET;
119 strncpy(ifr.ifr_name,
if_name, IFNAMSIZ - 1);
120 err = ioctl(fd, SIOCGIFADDR, &ifr);
122 MSG(
"Failed to retrieve the interface IP address!\n");
125 memcpy(&
if_ipaddr, &ifr.ifr_addr,
sizeof(
struct sockaddr_in));
128 MSG(
"Network Interface information\n");
138 struct ethtool_ts_info tsi = {.cmd = ETHTOOL_GET_TS_INFO};
139 memset(&ifr, 0,
sizeof(ifr));
140 strncpy(ifr.ifr_name,
if_name, IFNAMSIZ - 1);
141 ifr.ifr_data = (caddr_t)&tsi;
142 err = ioctl(fd, SIOCETHTOOL, &ifr);
144 MSG(
"Failed to query the interface timestamp capabilities!\n");
149 MSG(
" Hardware timestamping: ");
150 if ((tsi.so_timestamping & SOF_TIMESTAMPING_TX_HARDWARE) && (tsi.so_timestamping & SOF_TIMESTAMPING_RX_HARDWARE)) {
162 MSG(
"---------------\n\n");
167 MSG(
"Failed to open PHC file!\n");
174 MSG(
"Failed to access the PHC time!\n");
180 MSG(
"Failed to create notification queue!\n");
186 MSG(
"Failed to create buffer matching pointer queue!\n");
226 int err = setsockopt(
event_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
248 struct sockaddr_in addr;
249 memset(&addr, 0,
sizeof(addr));
250 addr.sin_family = PF_INET;
255 int sfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
257 MSG(
"Could not open the %s socket!\n", hint);
264 int err = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval));
266 MSG(
"Could not set the %s socket REUSEADDR option!\n", hint);
272 err = setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_IF, &
if_ipaddr.sin_addr,
sizeof(
struct in_addr));
274 MSG(
"Could not set the %s socket IP_MULTICAST_IF option!\n", hint);
280 addr.sin_port = htons(port);
281 err = bind(sfd, (
struct sockaddr *)&addr,
sizeof(addr));
283 MSG(
"Could not bind the %s socket!\n", hint);
300 int sfd = socket(AF_PACKET, SOCK_DGRAM, htons(
PTP_ETHERTYPE));
302 MSG(
"Could not open the %s socket!\n", hint);
308 struct sockaddr_ll addr;
309 memset(&addr, 0,
sizeof(addr));
310 addr.sll_ifindex =
if_idx;
311 addr.sll_halen = ETH_ALEN;
313 addr.sll_family = AF_PACKET;
314 addr.sll_pkttype = PACKET_MULTICAST;
315 memcpy(addr.sll_addr, ethaddr, ETH_ALEN);
321 err = bind(sfd, (
struct sockaddr *)&addr,
sizeof(addr));
323 MSG(
"Could not bind the %s socket!\n", hint);
329 struct packet_mreq mreq;
331 mreq.mr_type = PACKET_MR_MULTICAST;
332 mreq.mr_alen = ETH_ALEN;
333 memcpy(mreq.mr_address, ethaddr, ETH_ALEN);
335 err = setsockopt(sfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
337 MSG(
"Could not set the %s socket ADD_MEMBERSHIP option!\n", hint);
346 int optval = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE | SOF_TIMESTAMPING_TX_HARDWARE;
347 int err = setsockopt(sfd, SOL_SOCKET, SO_TIMESTAMPING, &optval,
sizeof(optval));
349 MSG(
"Failed to enable timestamping\n");
354 struct hwtstamp_config cfg;
355 memset(&ifreq, 0,
sizeof(ifreq));
356 memset(&cfg, 0,
sizeof(cfg));
357 strncpy(ifreq.ifr_name,
if_name, IFNAMSIZ - 1);
358 ifreq.ifr_data = (
void *)&cfg;
361 err = ioctl(sfd, SIOCGHWTSTAMP, &ifreq);
363 MSG(
"Failed to get timestamping settings.\n");
370 cfg.tx_type = HWTSTAMP_TX_ON;
371 cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
372 err = ioctl(sfd, SIOCSHWTSTAMP, &ifreq);
374 MSG(
"Failed to set timestamping settings.\n");
380 err = setsockopt(sfd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &optval,
sizeof(optval));
382 MSG(
"Could not enable TX timestamp communication through the error queue!\n");
386#define NAME_BUF_SIZE (256)
388#define MSG_BUF_SIZE (1600)
390#define CTRL_BUF_SIZE (256)
397 struct pollfd pfd[] = {
398 {.fd =
notif_q[0], .events = POLLIN},
399 {.fd =
event_fd, .events = POLLIN | POLLPRI},
407 int pret = poll(pfd, n, -1);
410 if (pfd[0].revents & POLLIN) {
412 read(
notif_q[0], &c,
sizeof(
char));
420 if (pfd[1].revents != 0) {
426 memset(&msg, 0,
sizeof(msg));
438 if (pfd[1].revents & POLLPRI) {
439 ssize_t size = recvmsg(
event_fd, &msg, MSG_ERRQUEUE);
441 for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) {
442 int level = cm->cmsg_level;
443 int type = cm->cmsg_type;
444 if ((level == SOL_SOCKET) && (type == SO_TIMESTAMPING)) {
445 struct timespec *ts = (
struct timespec *)CMSG_DATA(cm);
450 clock_gettime(CLOCK_REALTIME, &now);
451 CLILOG(
LINUX_NSD_TS_DEBUG,
"[%lu.%09lu] TX TS: (%u) %lu.%09lu\n", now.tv_sec, now.tv_nsec, uid, ts[2].tv_sec, ts[2].tv_nsec);
460 if (pfd[1].revents & POLLIN) {
461 ssize_t size = recvmsg(
event_fd, &msg, 0);
464 memset(&ts, 0,
sizeof(ts));
465 bool ts_found =
false;
466 for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) {
467 int level = cm->cmsg_level;
468 int type = cm->cmsg_type;
469 if ((level == SOL_SOCKET) && (type == SO_TIMESTAMPING)) {
470 struct timespec *tsa = (
struct timespec *)CMSG_DATA(cm);
486 if (pfd[2].revents & POLLIN) {
519 if ((tp == -1) || (dm == -1)) {
541 MSG(
"Failed to create the transceiver thread!\n");
558 bool send_ok =
false;
569 struct sockaddr_in addr;
570 memset(&addr, 0,
sizeof(addr));
571 addr.sin_family = PF_INET;
576 if (sendto(sfd, pMsg->
data, pMsg->
size, 0, (
struct sockaddr *)&addr,
sizeof(addr)) == pMsg->
size) {
584 struct sockaddr_ll addr;
585 memset(&addr, 0,
sizeof(addr));
586 addr.sll_ifindex =
if_idx;
587 addr.sll_halen = ETH_ALEN;
589 memcpy(addr.sll_addr, ethaddr, ETH_ALEN);
591 if (sendto(sfd, pMsg->
data, pMsg->
size, 0, (
struct sockaddr *)&addr,
sizeof(addr)) == pMsg->
size) {
599 clock_gettime(CLOCK_REALTIME, &now);
616#define PPB_TO_TUNING_SCALER (((double)(1 << 16)) / 1000.0)
620 memset(&tx, 0,
sizeof(
struct timex));
622 MSG(
"Failed to retrieve PHC tuning!\n");
624 memset(&tx, 0,
sizeof(
struct timex));
625 tx.modes = ADJ_FREQUENCY;
627 if (clock_adjtime(
phc_clkid, &tx) != 0) {
628 MSG(
"Failed to adjust PHC frequency!\n");
633 struct timespec ts = {.tv_sec = seconds, .tv_nsec = nanoseconds};
635 MSG(
"Failed to set the PHC time!\n");
642 MSG(
"Failed to get the PHC time!\n");
645 pTime->
sec = ts.tv_sec;
#define FLEXPTP_SNPRINTF(...)
static struct sockaddr_in if_ipaddr
void linux_nsd_cleanup(void)
void ptp_nsd_transmit_msg(RawPtpMessage *pMsg, uint32_t uid)
static void enable_timestamping(int sfd)
static PtpDelayMechanism DM
void ptp_nsd_igmp_join_leave(bool join)
void ptp_nsd_init(PtpTransportType tp, PtpDelayMechanism dm)
#define PPB_TO_TUNING_SCALER
void linux_get_time(TimestampU *pTime)
void linux_set_time(uint32_t seconds, uint32_t nanoseconds)
static clockid_t phc_clkid
#define LINUX_NSD_TS_DEBUG
static int open_udp_socket(PtpDelayMechanism dm, uint16_t port, const char *hint)
void linux_adjust_clock(double tuning_ppb)
static void socket_join_igmp(int fd)
bool linux_nsd_preinit(const char *ifn)
static int open_raw_socket(PtpDelayMechanism dm, bool bind_socket, const char *hint)
static char msg_buf[(1600)]
void ptp_nsd_get_interface_address(uint8_t *hwa)
static char name_buf[(256)]
static char if_name[IFNAMSIZ]
#define NOTIF_QUIT_TRANSCEIVER_THREAD
static uint8_t if_hwaddr[IFHWADDRLEN]
static PtpTransportType TP
static uint16_t phc_index
static pthread_t transceiver_thread
static char rx_ctrl_buf[(256)]
#define FD_TO_CLOCKID(fd)
static void post_notification(char c)
#define PHY_FILE_NAME_SIZE
static char phc_file_name[(16)]
#define LINUX_NSD_TX_ENQUEUE_DEBUG
static void * nsd_thread(void *arg)
const uint8_t PTP_ETHERNET_PEER_DELAY[6]
PTP's L2 Peer_Delay Ethernet address.
const uint8_t PTP_ETHERNET_PRIMARY[6]
PTP's L2 Primary Ethernet address.
const ip_addr_t PTP_IGMP_PRIMARY
Primary IGMP address.
#define PTP_PORT_EVENT
PTP event message port.
#define PTP_COLOR_GREEN
Green.
#define PTP_ETHERTYPE
PTP EtherType.
#define PTP_COLOR_CYAN
Cyan.
#define PTP_PORT_GENERAL
PTP general message port.
#define PTP_COLOR_RESET
Reset colors.
#define PTP_COLOR_RED
< Define this to override PTP_COLOR_* macros
#define PTP_COLOR_YELLOW
Yellow.
const ip_addr_t PTP_IGMP_PEER_DELAY
Peer_Delay IGMP address.
PtpTransportType
PTP transport type enumeration.
@ PTP_TP_IPv4
IPv4 Transport Type.
@ PTP_TP_802_3
Ethernet Transport Type.
PtpMessageClass
Enumeration for different PTP message classes.
@ PTP_MC_EVENT
Event Message Class.
@ PTP_MC_GENERAL
General Message Class.
PtpDelayMechanism
PTP Delay mechanism enumeration.
@ PTP_DM_E2E
End-to-End Delay Mechanism.
@ PTP_DM_P2P
Peer-to-Peer Delay Mechanism.
PtpMessageClass tx_mc
transmit message class
uint32_t size
Packet size.
uint8_t data[(128)]
raw packet data
uint32_t nanosec
nanoseconds
void ptp_receive_enqueue(const void *pPayload, uint32_t len, uint32_t ts_sec, uint32_t ts_ns, int tp)
void ptp_transmit_timestamp_cb(uint32_t uid, uint32_t seconds, uint32_t nanoseconds)