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)
58#define PHY_FILE_NAME_SIZE (16)
70#define PRINT_HWADDR(a) MSG("%02X:%02X:%02X:%02X:%02X:%02X", a[0], a[1], a[2], a[3], a[4], a[5]);
72#define NOTIF_QUIT_TRANSCEIVER_THREAD 'Q'
80#define FD_TO_CLOCKID(fd) ((clockid_t)((((unsigned int)~fd) << 3) | CLOCKFD))
81#define CLOCKID_TO_FD(clk) ((unsigned int)~((clk) >> 3))
87 strncpy(
if_name, ifn, IFNAMSIZ - 1);
91 int fd = socket(PF_INET, SOCK_DGRAM, 0);
93 MSG(
"Could not create a dummy socket to retrieve interface data!\n");
102 memset(&ifr, 0,
sizeof(ifr));
103 strncpy(ifr.ifr_name,
if_name, IFNAMSIZ - 1);
104 err = ioctl(fd, SIOCGIFINDEX, &ifr);
106 MSG(
"Could not get interface index!\n");
113 memset(&ifr, 0,
sizeof(ifr));
114 strncpy(ifr.ifr_name,
if_name, IFNAMSIZ - 1);
115 err = ioctl(fd, SIOCGIFHWADDR, &ifr);
117 MSG(
"Failed to retrieve the hardware address!\n");
120 memcpy(
if_hwaddr, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
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);
129 MSG(
"Failed to retrieve the interface IP address!\n");
132 memcpy(&
if_ipaddr, &ifr.ifr_addr,
sizeof(
struct sockaddr_in));
135 MSG(
"Network Interface information\n");
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);
151 MSG(
"Failed to query the interface timestamp capabilities!\n");
156 MSG(
" Hardware timestamping: ");
157 if ((tsi.so_timestamping & SOF_TIMESTAMPING_TX_HARDWARE) && (tsi.so_timestamping & SOF_TIMESTAMPING_RX_HARDWARE)) {
169 MSG(
"---------------\n\n");
174 MSG(
"Failed to open PHC file!\n");
181 MSG(
"Failed to access the PHC time!\n");
187 MSG(
"Failed to create notification queue!\n");
193 MSG(
"Failed to create buffer matching pointer queue!\n");
224 mreq.imr_multiaddr.s_addr = igmp_addr;
229 int err = setsockopt(
primary_event_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
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;
264 int sfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
266 MSG(
"Could not open the %s socket!\n", hint);
273 int err = setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &optval,
sizeof(optval));
275 MSG(
"Could not set the %s socket REUSEADDR option!\n", hint);
281 err = setsockopt(sfd, IPPROTO_IP, IP_MULTICAST_IF, &
if_ipaddr.sin_addr,
sizeof(
struct in_addr));
283 MSG(
"Could not set the %s socket IP_MULTICAST_IF option!\n", hint);
289 addr.sin_port = htons(port);
290 err = bind(sfd, (
struct sockaddr *) &addr,
sizeof(addr));
292 MSG(
"Could not bind the %s socket!\n", hint);
304static int open_raw_socket(
const uint8_t *ethaddr,
bool bind_socket,
const char *hint) {
307 int sfd = socket(AF_PACKET, SOCK_DGRAM, htons(
PTP_ETHERTYPE));
309 MSG(
"Could not open the %s socket!\n", hint);
315 struct sockaddr_ll addr;
316 memset(&addr, 0,
sizeof(addr));
317 addr.sll_ifindex =
if_idx;
318 addr.sll_halen = ETH_ALEN;
320 addr.sll_family = AF_PACKET;
321 addr.sll_pkttype = PACKET_MULTICAST;
322 memcpy(addr.sll_addr, ethaddr, ETH_ALEN);
328 err = bind(sfd, (
struct sockaddr *) &addr,
sizeof(addr));
330 MSG(
"Could not bind the %s socket!\n", hint);
336 struct packet_mreq mreq;
338 mreq.mr_type = PACKET_MR_MULTICAST;
339 mreq.mr_alen = ETH_ALEN;
340 memcpy(mreq.mr_address, ethaddr, ETH_ALEN);
342 err = setsockopt(sfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq,
sizeof(mreq));
344 MSG(
"Could not set the %s socket ADD_MEMBERSHIP option!\n", hint);
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));
356 MSG(
"Failed to enable timestamping\n");
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;
368 err = ioctl(sfd, SIOCGHWTSTAMP, &ifreq);
370 MSG(
"Failed to get timestamping settings.\n");
377 cfg.tx_type = HWTSTAMP_TX_ON;
378 cfg.rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
379 err = ioctl(sfd, SIOCSHWTSTAMP, &ifreq);
381 MSG(
"Failed to set timestamping settings.\n");
387 err = setsockopt(sfd, SOL_SOCKET, SO_SELECT_ERR_QUEUE, &optval,
sizeof(optval));
389 MSG(
"Could not enable TX timestamp communication through the error queue!\n");
393#define NAME_BUF_SIZE (256)
395#define MSG_BUF_SIZE (1600)
397#define CTRL_BUF_SIZE (256)
401 for (
int i = 0; i < nfds; i++) {
402 if (pfd[i].fd == fd) {
403 pfd[i].revents &= (
short int)(~revent);
412 struct pollfd pfd[] = {
413 {.fd =
notif_q[0], .events = POLLIN},
424 const int pret = poll(pfd, n, -1);
427 if (pfd[0].revents & POLLIN) {
429 read(
notif_q[0], &c,
sizeof(
char));
437 if ((pfd[1].revents != 0) || (pfd[3].revents != 0)) {
442 memset(&msg, 0,
sizeof(msg));
454 while ((pfd[1].revents & POLLPRI) || (pfd[3].revents & POLLPRI)) {
456 ssize_t size = recvmsg(fd, &msg, MSG_ERRQUEUE);
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);
469 clock_gettime(CLOCK_REALTIME, &now);
471 uid, ts[2].tv_sec, ts[2].tv_nsec);
483 while ((pfd[1].revents & POLLIN) || (pfd[3].revents & POLLIN)) {
485 ssize_t size = recvmsg(fd, &msg, 0);
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);
513 if (pfd[2].revents & POLLIN) {
554 if ((init->
tp == -1) || (init->
dm == -1)) {
602 MSG(
"Failed to create the transceiver thread!\n");
619 bool send_ok =
false;
634 struct sockaddr_in addr;
635 memset(&addr, 0,
sizeof(addr));
636 addr.sin_family = PF_INET;
642 if (sendto(sfd, pMsg->
data, pMsg->
size, 0, (
struct sockaddr *) &addr,
sizeof(addr)) == pMsg->
size) {
650 struct sockaddr_ll addr;
651 memset(&addr, 0,
sizeof(addr));
652 addr.sll_ifindex =
if_idx;
653 addr.sll_halen = ETH_ALEN;
655 memcpy(addr.sll_addr, ethaddr, ETH_ALEN);
657 if (sendto(sfd, pMsg->
data, pMsg->
size, 0, (
struct sockaddr *) &addr,
sizeof(addr)) == pMsg->
size) {
665 clock_gettime(CLOCK_REALTIME, &now);
682#define PPB_TO_TUNING_SCALER (((double)(1 << 16)) / 1000.0)
686 memset(&tx, 0,
sizeof(
struct timex));
688 MSG(
"Failed to retrieve PHC tuning!\n");
690 memset(&tx, 0,
sizeof(
struct timex));
691 tx.modes = ADJ_FREQUENCY;
693 if (clock_adjtime(
phc_clkid, &tx) != 0) {
694 MSG(
"Failed to adjust PHC frequency!\n");
699 struct timespec ts = {.tv_sec = seconds, .tv_nsec = nanoseconds};
701 MSG(
"Failed to set the PHC time!\n");
708 MSG(
"Failed to get the PHC time!\n");
711 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)
void ptp_nsd_init(const NsdInitSettings *init)
static int primary_event_fd
static int open_udp_socket(in_addr_t s_addr, uint16_t port, const char *hint)
static void enable_timestamping(int sfd)
static PtpDelayMechanism DM
static int open_raw_socket(const uint8_t *ethaddr, bool bind_socket, const char *hint)
void ptp_nsd_igmp_join_leave(bool join)
#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
void linux_adjust_clock(double tuning_ppb)
static uint8_t p2p_8023_pdel_dest[6]
bool linux_nsd_preinit(const char *ifn)
static char msg_buf[(1600)]
void ptp_nsd_get_interface_address(uint8_t *hwa)
static char name_buf[(256)]
static char if_name[IFNAMSIZ]
static void socket_join_igmp(int fd, in_addr_t igmp_addr)
#define NOTIF_QUIT_TRANSCEIVER_THREAD
static void clear_revents_by_fd(int fd, unsigned short revent, struct pollfd *pfd, int nfds)
static uint8_t if_hwaddr[IFHWADDRLEN]
static PtpTransportType TP
static bool identical_p2p_8023_dests
static int pdelay_general_fd
static int pdelay_event_fd
static uint16_t phc_index
static pthread_t transceiver_thread
static uint8_t p2p_8023_primary_dest[6]
static char rx_ctrl_buf[(256)]
static int primary_general_fd
#define FD_TO_CLOCKID(fd)
static void post_notification(char c)
#define PHY_FILE_NAME_SIZE
static const uint8_t zero_mac[ETH_ALEN]
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.
PtpMessageType
PTP packet type enumeration.
@ PTP_MT_PDelay_Resp
Peer Delay Response.
@ PTP_MT_PDelay_Req
Peer Delay Request.
@ PTP_MT_PDelay_Resp_Follow_Up
Peer Delay Response Follow Up.
PtpDelayMechanism
PTP Delay mechanism enumeration.
@ PTP_DM_P2P
Peer-to-Peer Delay Mechanism.
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
PtpMessageType tx_mt
transmit message type
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)