18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * This program demonstrates how the various time stamping features in
48c2ecf20Sopenharmony_ci * the Linux kernel work. It emulates the behavior of a PTP
58c2ecf20Sopenharmony_ci * implementation in stand-alone master mode by sending PTPv1 Sync
68c2ecf20Sopenharmony_ci * multicasts once every second. It looks for similar packets, but
78c2ecf20Sopenharmony_ci * beyond that doesn't actually implement PTP.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Outgoing packets are time stamped with SO_TIMESTAMPING with or
108c2ecf20Sopenharmony_ci * without hardware support.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Incoming packets are time stamped with SO_TIMESTAMPING with or
138c2ecf20Sopenharmony_ci * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
148c2ecf20Sopenharmony_ci * SO_TIMESTAMP[NS].
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * Copyright (C) 2009 Intel Corporation.
178c2ecf20Sopenharmony_ci * Author: Patrick Ohly <patrick.ohly@intel.com>
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <stdio.h>
218c2ecf20Sopenharmony_ci#include <stdlib.h>
228c2ecf20Sopenharmony_ci#include <errno.h>
238c2ecf20Sopenharmony_ci#include <string.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <sys/time.h>
268c2ecf20Sopenharmony_ci#include <sys/socket.h>
278c2ecf20Sopenharmony_ci#include <sys/select.h>
288c2ecf20Sopenharmony_ci#include <sys/ioctl.h>
298c2ecf20Sopenharmony_ci#include <arpa/inet.h>
308c2ecf20Sopenharmony_ci#include <net/if.h>
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <asm/types.h>
338c2ecf20Sopenharmony_ci#include <linux/net_tstamp.h>
348c2ecf20Sopenharmony_ci#include <linux/errqueue.h>
358c2ecf20Sopenharmony_ci#include <linux/sockios.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#ifndef SO_TIMESTAMPING
388c2ecf20Sopenharmony_ci# define SO_TIMESTAMPING         37
398c2ecf20Sopenharmony_ci# define SCM_TIMESTAMPING        SO_TIMESTAMPING
408c2ecf20Sopenharmony_ci#endif
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#ifndef SO_TIMESTAMPNS
438c2ecf20Sopenharmony_ci# define SO_TIMESTAMPNS 35
448c2ecf20Sopenharmony_ci#endif
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void usage(const char *error)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	if (error)
498c2ecf20Sopenharmony_ci		printf("invalid option: %s\n", error);
508c2ecf20Sopenharmony_ci	printf("timestamping interface option*\n\n"
518c2ecf20Sopenharmony_ci	       "Options:\n"
528c2ecf20Sopenharmony_ci	       "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
538c2ecf20Sopenharmony_ci	       "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
548c2ecf20Sopenharmony_ci	       "  SO_TIMESTAMPNS - more accurate software time stamping\n"
558c2ecf20Sopenharmony_ci	       "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
568c2ecf20Sopenharmony_ci	       "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
578c2ecf20Sopenharmony_ci	       "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
588c2ecf20Sopenharmony_ci	       "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
598c2ecf20Sopenharmony_ci	       "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
608c2ecf20Sopenharmony_ci	       "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
618c2ecf20Sopenharmony_ci	       "  SIOCGSTAMP - check last socket time stamp\n"
628c2ecf20Sopenharmony_ci	       "  SIOCGSTAMPNS - more accurate socket time stamp\n");
638c2ecf20Sopenharmony_ci	exit(1);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic void bail(const char *error)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	printf("%s: %s\n", error, strerror(errno));
698c2ecf20Sopenharmony_ci	exit(1);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic const unsigned char sync[] = {
738c2ecf20Sopenharmony_ci	0x00, 0x01, 0x00, 0x01,
748c2ecf20Sopenharmony_ci	0x5f, 0x44, 0x46, 0x4c,
758c2ecf20Sopenharmony_ci	0x54, 0x00, 0x00, 0x00,
768c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
778c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
788c2ecf20Sopenharmony_ci	0x01, 0x01,
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* fake uuid */
818c2ecf20Sopenharmony_ci	0x00, 0x01,
828c2ecf20Sopenharmony_ci	0x02, 0x03, 0x04, 0x05,
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	0x00, 0x01, 0x00, 0x37,
858c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x08,
868c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
878c2ecf20Sopenharmony_ci	0x49, 0x05, 0xcd, 0x01,
888c2ecf20Sopenharmony_ci	0x29, 0xb1, 0x8d, 0xb0,
898c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
908c2ecf20Sopenharmony_ci	0x00, 0x01,
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* fake uuid */
938c2ecf20Sopenharmony_ci	0x00, 0x01,
948c2ecf20Sopenharmony_ci	0x02, 0x03, 0x04, 0x05,
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x37,
978c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x04,
988c2ecf20Sopenharmony_ci	0x44, 0x46, 0x4c, 0x54,
998c2ecf20Sopenharmony_ci	0x00, 0x00, 0xf0, 0x60,
1008c2ecf20Sopenharmony_ci	0x00, 0x01, 0x00, 0x00,
1018c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x01,
1028c2ecf20Sopenharmony_ci	0x00, 0x00, 0xf0, 0x60,
1038c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
1048c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x04,
1058c2ecf20Sopenharmony_ci	0x44, 0x46, 0x4c, 0x54,
1068c2ecf20Sopenharmony_ci	0x00, 0x01,
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	/* fake uuid */
1098c2ecf20Sopenharmony_ci	0x00, 0x01,
1108c2ecf20Sopenharmony_ci	0x02, 0x03, 0x04, 0x05,
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
1138c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
1148c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00,
1158c2ecf20Sopenharmony_ci	0x00, 0x00, 0x00, 0x00
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	struct timeval now;
1218c2ecf20Sopenharmony_ci	int res;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	res = sendto(sock, sync, sizeof(sync), 0,
1248c2ecf20Sopenharmony_ci		addr, addr_len);
1258c2ecf20Sopenharmony_ci	gettimeofday(&now, 0);
1268c2ecf20Sopenharmony_ci	if (res < 0)
1278c2ecf20Sopenharmony_ci		printf("%s: %s\n", "send", strerror(errno));
1288c2ecf20Sopenharmony_ci	else
1298c2ecf20Sopenharmony_ci		printf("%ld.%06ld: sent %d bytes\n",
1308c2ecf20Sopenharmony_ci		       (long)now.tv_sec, (long)now.tv_usec,
1318c2ecf20Sopenharmony_ci		       res);
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void printpacket(struct msghdr *msg, int res,
1358c2ecf20Sopenharmony_ci			char *data,
1368c2ecf20Sopenharmony_ci			int sock, int recvmsg_flags,
1378c2ecf20Sopenharmony_ci			int siocgstamp, int siocgstampns)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
1408c2ecf20Sopenharmony_ci	struct cmsghdr *cmsg;
1418c2ecf20Sopenharmony_ci	struct timeval tv;
1428c2ecf20Sopenharmony_ci	struct timespec ts;
1438c2ecf20Sopenharmony_ci	struct timeval now;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	gettimeofday(&now, 0);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
1488c2ecf20Sopenharmony_ci	       (long)now.tv_sec, (long)now.tv_usec,
1498c2ecf20Sopenharmony_ci	       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
1508c2ecf20Sopenharmony_ci	       res,
1518c2ecf20Sopenharmony_ci	       inet_ntoa(from_addr->sin_addr),
1528c2ecf20Sopenharmony_ci	       msg->msg_controllen);
1538c2ecf20Sopenharmony_ci	for (cmsg = CMSG_FIRSTHDR(msg);
1548c2ecf20Sopenharmony_ci	     cmsg;
1558c2ecf20Sopenharmony_ci	     cmsg = CMSG_NXTHDR(msg, cmsg)) {
1568c2ecf20Sopenharmony_ci		printf("   cmsg len %zu: ", cmsg->cmsg_len);
1578c2ecf20Sopenharmony_ci		switch (cmsg->cmsg_level) {
1588c2ecf20Sopenharmony_ci		case SOL_SOCKET:
1598c2ecf20Sopenharmony_ci			printf("SOL_SOCKET ");
1608c2ecf20Sopenharmony_ci			switch (cmsg->cmsg_type) {
1618c2ecf20Sopenharmony_ci			case SO_TIMESTAMP: {
1628c2ecf20Sopenharmony_ci				struct timeval *stamp =
1638c2ecf20Sopenharmony_ci					(struct timeval *)CMSG_DATA(cmsg);
1648c2ecf20Sopenharmony_ci				printf("SO_TIMESTAMP %ld.%06ld",
1658c2ecf20Sopenharmony_ci				       (long)stamp->tv_sec,
1668c2ecf20Sopenharmony_ci				       (long)stamp->tv_usec);
1678c2ecf20Sopenharmony_ci				break;
1688c2ecf20Sopenharmony_ci			}
1698c2ecf20Sopenharmony_ci			case SO_TIMESTAMPNS: {
1708c2ecf20Sopenharmony_ci				struct timespec *stamp =
1718c2ecf20Sopenharmony_ci					(struct timespec *)CMSG_DATA(cmsg);
1728c2ecf20Sopenharmony_ci				printf("SO_TIMESTAMPNS %ld.%09ld",
1738c2ecf20Sopenharmony_ci				       (long)stamp->tv_sec,
1748c2ecf20Sopenharmony_ci				       (long)stamp->tv_nsec);
1758c2ecf20Sopenharmony_ci				break;
1768c2ecf20Sopenharmony_ci			}
1778c2ecf20Sopenharmony_ci			case SO_TIMESTAMPING: {
1788c2ecf20Sopenharmony_ci				struct timespec *stamp =
1798c2ecf20Sopenharmony_ci					(struct timespec *)CMSG_DATA(cmsg);
1808c2ecf20Sopenharmony_ci				printf("SO_TIMESTAMPING ");
1818c2ecf20Sopenharmony_ci				printf("SW %ld.%09ld ",
1828c2ecf20Sopenharmony_ci				       (long)stamp->tv_sec,
1838c2ecf20Sopenharmony_ci				       (long)stamp->tv_nsec);
1848c2ecf20Sopenharmony_ci				stamp++;
1858c2ecf20Sopenharmony_ci				/* skip deprecated HW transformed */
1868c2ecf20Sopenharmony_ci				stamp++;
1878c2ecf20Sopenharmony_ci				printf("HW raw %ld.%09ld",
1888c2ecf20Sopenharmony_ci				       (long)stamp->tv_sec,
1898c2ecf20Sopenharmony_ci				       (long)stamp->tv_nsec);
1908c2ecf20Sopenharmony_ci				break;
1918c2ecf20Sopenharmony_ci			}
1928c2ecf20Sopenharmony_ci			default:
1938c2ecf20Sopenharmony_ci				printf("type %d", cmsg->cmsg_type);
1948c2ecf20Sopenharmony_ci				break;
1958c2ecf20Sopenharmony_ci			}
1968c2ecf20Sopenharmony_ci			break;
1978c2ecf20Sopenharmony_ci		case IPPROTO_IP:
1988c2ecf20Sopenharmony_ci			printf("IPPROTO_IP ");
1998c2ecf20Sopenharmony_ci			switch (cmsg->cmsg_type) {
2008c2ecf20Sopenharmony_ci			case IP_RECVERR: {
2018c2ecf20Sopenharmony_ci				struct sock_extended_err *err =
2028c2ecf20Sopenharmony_ci					(struct sock_extended_err *)CMSG_DATA(cmsg);
2038c2ecf20Sopenharmony_ci				printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
2048c2ecf20Sopenharmony_ci					strerror(err->ee_errno),
2058c2ecf20Sopenharmony_ci					err->ee_origin,
2068c2ecf20Sopenharmony_ci#ifdef SO_EE_ORIGIN_TIMESTAMPING
2078c2ecf20Sopenharmony_ci					err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
2088c2ecf20Sopenharmony_ci					"bounced packet" : "unexpected origin"
2098c2ecf20Sopenharmony_ci#else
2108c2ecf20Sopenharmony_ci					"probably SO_EE_ORIGIN_TIMESTAMPING"
2118c2ecf20Sopenharmony_ci#endif
2128c2ecf20Sopenharmony_ci					);
2138c2ecf20Sopenharmony_ci				if (res < sizeof(sync))
2148c2ecf20Sopenharmony_ci					printf(" => truncated data?!");
2158c2ecf20Sopenharmony_ci				else if (!memcmp(sync, data + res - sizeof(sync),
2168c2ecf20Sopenharmony_ci							sizeof(sync)))
2178c2ecf20Sopenharmony_ci					printf(" => GOT OUR DATA BACK (HURRAY!)");
2188c2ecf20Sopenharmony_ci				break;
2198c2ecf20Sopenharmony_ci			}
2208c2ecf20Sopenharmony_ci			case IP_PKTINFO: {
2218c2ecf20Sopenharmony_ci				struct in_pktinfo *pktinfo =
2228c2ecf20Sopenharmony_ci					(struct in_pktinfo *)CMSG_DATA(cmsg);
2238c2ecf20Sopenharmony_ci				printf("IP_PKTINFO interface index %u",
2248c2ecf20Sopenharmony_ci					pktinfo->ipi_ifindex);
2258c2ecf20Sopenharmony_ci				break;
2268c2ecf20Sopenharmony_ci			}
2278c2ecf20Sopenharmony_ci			default:
2288c2ecf20Sopenharmony_ci				printf("type %d", cmsg->cmsg_type);
2298c2ecf20Sopenharmony_ci				break;
2308c2ecf20Sopenharmony_ci			}
2318c2ecf20Sopenharmony_ci			break;
2328c2ecf20Sopenharmony_ci		default:
2338c2ecf20Sopenharmony_ci			printf("level %d type %d",
2348c2ecf20Sopenharmony_ci				cmsg->cmsg_level,
2358c2ecf20Sopenharmony_ci				cmsg->cmsg_type);
2368c2ecf20Sopenharmony_ci			break;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci		printf("\n");
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	if (siocgstamp) {
2428c2ecf20Sopenharmony_ci		if (ioctl(sock, SIOCGSTAMP, &tv))
2438c2ecf20Sopenharmony_ci			printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
2448c2ecf20Sopenharmony_ci		else
2458c2ecf20Sopenharmony_ci			printf("SIOCGSTAMP %ld.%06ld\n",
2468c2ecf20Sopenharmony_ci			       (long)tv.tv_sec,
2478c2ecf20Sopenharmony_ci			       (long)tv.tv_usec);
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci	if (siocgstampns) {
2508c2ecf20Sopenharmony_ci		if (ioctl(sock, SIOCGSTAMPNS, &ts))
2518c2ecf20Sopenharmony_ci			printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
2528c2ecf20Sopenharmony_ci		else
2538c2ecf20Sopenharmony_ci			printf("SIOCGSTAMPNS %ld.%09ld\n",
2548c2ecf20Sopenharmony_ci			       (long)ts.tv_sec,
2558c2ecf20Sopenharmony_ci			       (long)ts.tv_nsec);
2568c2ecf20Sopenharmony_ci	}
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic void recvpacket(int sock, int recvmsg_flags,
2608c2ecf20Sopenharmony_ci		       int siocgstamp, int siocgstampns)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	char data[256];
2638c2ecf20Sopenharmony_ci	struct msghdr msg;
2648c2ecf20Sopenharmony_ci	struct iovec entry;
2658c2ecf20Sopenharmony_ci	struct sockaddr_in from_addr;
2668c2ecf20Sopenharmony_ci	struct {
2678c2ecf20Sopenharmony_ci		struct cmsghdr cm;
2688c2ecf20Sopenharmony_ci		char control[512];
2698c2ecf20Sopenharmony_ci	} control;
2708c2ecf20Sopenharmony_ci	int res;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	memset(&msg, 0, sizeof(msg));
2738c2ecf20Sopenharmony_ci	msg.msg_iov = &entry;
2748c2ecf20Sopenharmony_ci	msg.msg_iovlen = 1;
2758c2ecf20Sopenharmony_ci	entry.iov_base = data;
2768c2ecf20Sopenharmony_ci	entry.iov_len = sizeof(data);
2778c2ecf20Sopenharmony_ci	msg.msg_name = (caddr_t)&from_addr;
2788c2ecf20Sopenharmony_ci	msg.msg_namelen = sizeof(from_addr);
2798c2ecf20Sopenharmony_ci	msg.msg_control = &control;
2808c2ecf20Sopenharmony_ci	msg.msg_controllen = sizeof(control);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
2838c2ecf20Sopenharmony_ci	if (res < 0) {
2848c2ecf20Sopenharmony_ci		printf("%s %s: %s\n",
2858c2ecf20Sopenharmony_ci		       "recvmsg",
2868c2ecf20Sopenharmony_ci		       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
2878c2ecf20Sopenharmony_ci		       strerror(errno));
2888c2ecf20Sopenharmony_ci	} else {
2898c2ecf20Sopenharmony_ci		printpacket(&msg, res, data,
2908c2ecf20Sopenharmony_ci			    sock, recvmsg_flags,
2918c2ecf20Sopenharmony_ci			    siocgstamp, siocgstampns);
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ciint main(int argc, char **argv)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	int so_timestamping_flags = 0;
2988c2ecf20Sopenharmony_ci	int so_timestamp = 0;
2998c2ecf20Sopenharmony_ci	int so_timestampns = 0;
3008c2ecf20Sopenharmony_ci	int siocgstamp = 0;
3018c2ecf20Sopenharmony_ci	int siocgstampns = 0;
3028c2ecf20Sopenharmony_ci	int ip_multicast_loop = 0;
3038c2ecf20Sopenharmony_ci	char *interface;
3048c2ecf20Sopenharmony_ci	int i;
3058c2ecf20Sopenharmony_ci	int enabled = 1;
3068c2ecf20Sopenharmony_ci	int sock;
3078c2ecf20Sopenharmony_ci	struct ifreq device;
3088c2ecf20Sopenharmony_ci	struct ifreq hwtstamp;
3098c2ecf20Sopenharmony_ci	struct hwtstamp_config hwconfig, hwconfig_requested;
3108c2ecf20Sopenharmony_ci	struct sockaddr_in addr;
3118c2ecf20Sopenharmony_ci	struct ip_mreq imr;
3128c2ecf20Sopenharmony_ci	struct in_addr iaddr;
3138c2ecf20Sopenharmony_ci	int val;
3148c2ecf20Sopenharmony_ci	socklen_t len;
3158c2ecf20Sopenharmony_ci	struct timeval next;
3168c2ecf20Sopenharmony_ci	size_t if_len;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	if (argc < 2)
3198c2ecf20Sopenharmony_ci		usage(0);
3208c2ecf20Sopenharmony_ci	interface = argv[1];
3218c2ecf20Sopenharmony_ci	if_len = strlen(interface);
3228c2ecf20Sopenharmony_ci	if (if_len >= IFNAMSIZ) {
3238c2ecf20Sopenharmony_ci		printf("interface name exceeds IFNAMSIZ\n");
3248c2ecf20Sopenharmony_ci		exit(1);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = 2; i < argc; i++) {
3288c2ecf20Sopenharmony_ci		if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
3298c2ecf20Sopenharmony_ci			so_timestamp = 1;
3308c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
3318c2ecf20Sopenharmony_ci			so_timestampns = 1;
3328c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
3338c2ecf20Sopenharmony_ci			siocgstamp = 1;
3348c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
3358c2ecf20Sopenharmony_ci			siocgstampns = 1;
3368c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
3378c2ecf20Sopenharmony_ci			ip_multicast_loop = 1;
3388c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
3398c2ecf20Sopenharmony_ci			so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
3408c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
3418c2ecf20Sopenharmony_ci			so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
3428c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
3438c2ecf20Sopenharmony_ci			so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
3448c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
3458c2ecf20Sopenharmony_ci			so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
3468c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
3478c2ecf20Sopenharmony_ci			so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
3488c2ecf20Sopenharmony_ci		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
3498c2ecf20Sopenharmony_ci			so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
3508c2ecf20Sopenharmony_ci		else
3518c2ecf20Sopenharmony_ci			usage(argv[i]);
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
3558c2ecf20Sopenharmony_ci	if (sock < 0)
3568c2ecf20Sopenharmony_ci		bail("socket");
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	memset(&device, 0, sizeof(device));
3598c2ecf20Sopenharmony_ci	memcpy(device.ifr_name, interface, if_len + 1);
3608c2ecf20Sopenharmony_ci	if (ioctl(sock, SIOCGIFADDR, &device) < 0)
3618c2ecf20Sopenharmony_ci		bail("getting interface IP address");
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	memset(&hwtstamp, 0, sizeof(hwtstamp));
3648c2ecf20Sopenharmony_ci	memcpy(hwtstamp.ifr_name, interface, if_len + 1);
3658c2ecf20Sopenharmony_ci	hwtstamp.ifr_data = (void *)&hwconfig;
3668c2ecf20Sopenharmony_ci	memset(&hwconfig, 0, sizeof(hwconfig));
3678c2ecf20Sopenharmony_ci	hwconfig.tx_type =
3688c2ecf20Sopenharmony_ci		(so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
3698c2ecf20Sopenharmony_ci		HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
3708c2ecf20Sopenharmony_ci	hwconfig.rx_filter =
3718c2ecf20Sopenharmony_ci		(so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
3728c2ecf20Sopenharmony_ci		HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
3738c2ecf20Sopenharmony_ci	hwconfig_requested = hwconfig;
3748c2ecf20Sopenharmony_ci	if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
3758c2ecf20Sopenharmony_ci		if ((errno == EINVAL || errno == ENOTSUP) &&
3768c2ecf20Sopenharmony_ci		    hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
3778c2ecf20Sopenharmony_ci		    hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
3788c2ecf20Sopenharmony_ci			printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
3798c2ecf20Sopenharmony_ci		else
3808c2ecf20Sopenharmony_ci			bail("SIOCSHWTSTAMP");
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
3838c2ecf20Sopenharmony_ci	       hwconfig_requested.tx_type, hwconfig.tx_type,
3848c2ecf20Sopenharmony_ci	       hwconfig_requested.rx_filter, hwconfig.rx_filter);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* bind to PTP port */
3878c2ecf20Sopenharmony_ci	addr.sin_family = AF_INET;
3888c2ecf20Sopenharmony_ci	addr.sin_addr.s_addr = htonl(INADDR_ANY);
3898c2ecf20Sopenharmony_ci	addr.sin_port = htons(319 /* PTP event port */);
3908c2ecf20Sopenharmony_ci	if (bind(sock,
3918c2ecf20Sopenharmony_ci		 (struct sockaddr *)&addr,
3928c2ecf20Sopenharmony_ci		 sizeof(struct sockaddr_in)) < 0)
3938c2ecf20Sopenharmony_ci		bail("bind");
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/* set multicast group for outgoing packets */
3968c2ecf20Sopenharmony_ci	inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
3978c2ecf20Sopenharmony_ci	addr.sin_addr = iaddr;
3988c2ecf20Sopenharmony_ci	imr.imr_multiaddr.s_addr = iaddr.s_addr;
3998c2ecf20Sopenharmony_ci	imr.imr_interface.s_addr =
4008c2ecf20Sopenharmony_ci		((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
4018c2ecf20Sopenharmony_ci	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
4028c2ecf20Sopenharmony_ci		       &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
4038c2ecf20Sopenharmony_ci		bail("set multicast");
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	/* join multicast group, loop our own packet */
4068c2ecf20Sopenharmony_ci	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
4078c2ecf20Sopenharmony_ci		       &imr, sizeof(struct ip_mreq)) < 0)
4088c2ecf20Sopenharmony_ci		bail("join multicast group");
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
4118c2ecf20Sopenharmony_ci		       &ip_multicast_loop, sizeof(enabled)) < 0) {
4128c2ecf20Sopenharmony_ci		bail("loop multicast");
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* set socket options for time stamping */
4168c2ecf20Sopenharmony_ci	if (so_timestamp &&
4178c2ecf20Sopenharmony_ci		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
4188c2ecf20Sopenharmony_ci			   &enabled, sizeof(enabled)) < 0)
4198c2ecf20Sopenharmony_ci		bail("setsockopt SO_TIMESTAMP");
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	if (so_timestampns &&
4228c2ecf20Sopenharmony_ci		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
4238c2ecf20Sopenharmony_ci			   &enabled, sizeof(enabled)) < 0)
4248c2ecf20Sopenharmony_ci		bail("setsockopt SO_TIMESTAMPNS");
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	if (so_timestamping_flags &&
4278c2ecf20Sopenharmony_ci		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
4288c2ecf20Sopenharmony_ci			   &so_timestamping_flags,
4298c2ecf20Sopenharmony_ci			   sizeof(so_timestamping_flags)) < 0)
4308c2ecf20Sopenharmony_ci		bail("setsockopt SO_TIMESTAMPING");
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	/* request IP_PKTINFO for debugging purposes */
4338c2ecf20Sopenharmony_ci	if (setsockopt(sock, SOL_IP, IP_PKTINFO,
4348c2ecf20Sopenharmony_ci		       &enabled, sizeof(enabled)) < 0)
4358c2ecf20Sopenharmony_ci		printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* verify socket options */
4388c2ecf20Sopenharmony_ci	len = sizeof(val);
4398c2ecf20Sopenharmony_ci	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
4408c2ecf20Sopenharmony_ci		printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
4418c2ecf20Sopenharmony_ci	else
4428c2ecf20Sopenharmony_ci		printf("SO_TIMESTAMP %d\n", val);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
4458c2ecf20Sopenharmony_ci		printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
4468c2ecf20Sopenharmony_ci		       strerror(errno));
4478c2ecf20Sopenharmony_ci	else
4488c2ecf20Sopenharmony_ci		printf("SO_TIMESTAMPNS %d\n", val);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
4518c2ecf20Sopenharmony_ci		printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
4528c2ecf20Sopenharmony_ci		       strerror(errno));
4538c2ecf20Sopenharmony_ci	} else {
4548c2ecf20Sopenharmony_ci		printf("SO_TIMESTAMPING %d\n", val);
4558c2ecf20Sopenharmony_ci		if (val != so_timestamping_flags)
4568c2ecf20Sopenharmony_ci			printf("   not the expected value %d\n",
4578c2ecf20Sopenharmony_ci			       so_timestamping_flags);
4588c2ecf20Sopenharmony_ci	}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* send packets forever every five seconds */
4618c2ecf20Sopenharmony_ci	gettimeofday(&next, 0);
4628c2ecf20Sopenharmony_ci	next.tv_sec = (next.tv_sec + 1) / 5 * 5;
4638c2ecf20Sopenharmony_ci	next.tv_usec = 0;
4648c2ecf20Sopenharmony_ci	while (1) {
4658c2ecf20Sopenharmony_ci		struct timeval now;
4668c2ecf20Sopenharmony_ci		struct timeval delta;
4678c2ecf20Sopenharmony_ci		long delta_us;
4688c2ecf20Sopenharmony_ci		int res;
4698c2ecf20Sopenharmony_ci		fd_set readfs, errorfs;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci		gettimeofday(&now, 0);
4728c2ecf20Sopenharmony_ci		delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
4738c2ecf20Sopenharmony_ci			(long)(next.tv_usec - now.tv_usec);
4748c2ecf20Sopenharmony_ci		if (delta_us > 0) {
4758c2ecf20Sopenharmony_ci			/* continue waiting for timeout or data */
4768c2ecf20Sopenharmony_ci			delta.tv_sec = delta_us / 1000000;
4778c2ecf20Sopenharmony_ci			delta.tv_usec = delta_us % 1000000;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci			FD_ZERO(&readfs);
4808c2ecf20Sopenharmony_ci			FD_ZERO(&errorfs);
4818c2ecf20Sopenharmony_ci			FD_SET(sock, &readfs);
4828c2ecf20Sopenharmony_ci			FD_SET(sock, &errorfs);
4838c2ecf20Sopenharmony_ci			printf("%ld.%06ld: select %ldus\n",
4848c2ecf20Sopenharmony_ci			       (long)now.tv_sec, (long)now.tv_usec,
4858c2ecf20Sopenharmony_ci			       delta_us);
4868c2ecf20Sopenharmony_ci			res = select(sock + 1, &readfs, 0, &errorfs, &delta);
4878c2ecf20Sopenharmony_ci			gettimeofday(&now, 0);
4888c2ecf20Sopenharmony_ci			printf("%ld.%06ld: select returned: %d, %s\n",
4898c2ecf20Sopenharmony_ci			       (long)now.tv_sec, (long)now.tv_usec,
4908c2ecf20Sopenharmony_ci			       res,
4918c2ecf20Sopenharmony_ci			       res < 0 ? strerror(errno) : "success");
4928c2ecf20Sopenharmony_ci			if (res > 0) {
4938c2ecf20Sopenharmony_ci				if (FD_ISSET(sock, &readfs))
4948c2ecf20Sopenharmony_ci					printf("ready for reading\n");
4958c2ecf20Sopenharmony_ci				if (FD_ISSET(sock, &errorfs))
4968c2ecf20Sopenharmony_ci					printf("has error\n");
4978c2ecf20Sopenharmony_ci				recvpacket(sock, 0,
4988c2ecf20Sopenharmony_ci					   siocgstamp,
4998c2ecf20Sopenharmony_ci					   siocgstampns);
5008c2ecf20Sopenharmony_ci				recvpacket(sock, MSG_ERRQUEUE,
5018c2ecf20Sopenharmony_ci					   siocgstamp,
5028c2ecf20Sopenharmony_ci					   siocgstampns);
5038c2ecf20Sopenharmony_ci			}
5048c2ecf20Sopenharmony_ci		} else {
5058c2ecf20Sopenharmony_ci			/* write one packet */
5068c2ecf20Sopenharmony_ci			sendpacket(sock,
5078c2ecf20Sopenharmony_ci				   (struct sockaddr *)&addr,
5088c2ecf20Sopenharmony_ci				   sizeof(addr));
5098c2ecf20Sopenharmony_ci			next.tv_sec += 5;
5108c2ecf20Sopenharmony_ci			continue;
5118c2ecf20Sopenharmony_ci		}
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return 0;
5158c2ecf20Sopenharmony_ci}
516