xref: /OK3568_Linux_fs/kernel/tools/testing/selftests/net/timestamping.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * This program demonstrates how the various time stamping features in
4*4882a593Smuzhiyun  * the Linux kernel work. It emulates the behavior of a PTP
5*4882a593Smuzhiyun  * implementation in stand-alone master mode by sending PTPv1 Sync
6*4882a593Smuzhiyun  * multicasts once every second. It looks for similar packets, but
7*4882a593Smuzhiyun  * beyond that doesn't actually implement PTP.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  * Outgoing packets are time stamped with SO_TIMESTAMPING with or
10*4882a593Smuzhiyun  * without hardware support.
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * Incoming packets are time stamped with SO_TIMESTAMPING with or
13*4882a593Smuzhiyun  * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
14*4882a593Smuzhiyun  * SO_TIMESTAMP[NS].
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * Copyright (C) 2009 Intel Corporation.
17*4882a593Smuzhiyun  * Author: Patrick Ohly <patrick.ohly@intel.com>
18*4882a593Smuzhiyun  */
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include <stdio.h>
21*4882a593Smuzhiyun #include <stdlib.h>
22*4882a593Smuzhiyun #include <errno.h>
23*4882a593Smuzhiyun #include <string.h>
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include <sys/time.h>
26*4882a593Smuzhiyun #include <sys/socket.h>
27*4882a593Smuzhiyun #include <sys/select.h>
28*4882a593Smuzhiyun #include <sys/ioctl.h>
29*4882a593Smuzhiyun #include <arpa/inet.h>
30*4882a593Smuzhiyun #include <net/if.h>
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun #include <asm/types.h>
33*4882a593Smuzhiyun #include <linux/net_tstamp.h>
34*4882a593Smuzhiyun #include <linux/errqueue.h>
35*4882a593Smuzhiyun #include <linux/sockios.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun #ifndef SO_TIMESTAMPING
38*4882a593Smuzhiyun # define SO_TIMESTAMPING         37
39*4882a593Smuzhiyun # define SCM_TIMESTAMPING        SO_TIMESTAMPING
40*4882a593Smuzhiyun #endif
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #ifndef SO_TIMESTAMPNS
43*4882a593Smuzhiyun # define SO_TIMESTAMPNS 35
44*4882a593Smuzhiyun #endif
45*4882a593Smuzhiyun 
usage(const char * error)46*4882a593Smuzhiyun static void usage(const char *error)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun 	if (error)
49*4882a593Smuzhiyun 		printf("invalid option: %s\n", error);
50*4882a593Smuzhiyun 	printf("timestamping interface option*\n\n"
51*4882a593Smuzhiyun 	       "Options:\n"
52*4882a593Smuzhiyun 	       "  IP_MULTICAST_LOOP - looping outgoing multicasts\n"
53*4882a593Smuzhiyun 	       "  SO_TIMESTAMP - normal software time stamping, ms resolution\n"
54*4882a593Smuzhiyun 	       "  SO_TIMESTAMPNS - more accurate software time stamping\n"
55*4882a593Smuzhiyun 	       "  SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
56*4882a593Smuzhiyun 	       "  SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
57*4882a593Smuzhiyun 	       "  SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
58*4882a593Smuzhiyun 	       "  SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
59*4882a593Smuzhiyun 	       "  SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
60*4882a593Smuzhiyun 	       "  SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
61*4882a593Smuzhiyun 	       "  SIOCGSTAMP - check last socket time stamp\n"
62*4882a593Smuzhiyun 	       "  SIOCGSTAMPNS - more accurate socket time stamp\n");
63*4882a593Smuzhiyun 	exit(1);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun 
bail(const char * error)66*4882a593Smuzhiyun static void bail(const char *error)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	printf("%s: %s\n", error, strerror(errno));
69*4882a593Smuzhiyun 	exit(1);
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun static const unsigned char sync[] = {
73*4882a593Smuzhiyun 	0x00, 0x01, 0x00, 0x01,
74*4882a593Smuzhiyun 	0x5f, 0x44, 0x46, 0x4c,
75*4882a593Smuzhiyun 	0x54, 0x00, 0x00, 0x00,
76*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
77*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
78*4882a593Smuzhiyun 	0x01, 0x01,
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	/* fake uuid */
81*4882a593Smuzhiyun 	0x00, 0x01,
82*4882a593Smuzhiyun 	0x02, 0x03, 0x04, 0x05,
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	0x00, 0x01, 0x00, 0x37,
85*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x08,
86*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
87*4882a593Smuzhiyun 	0x49, 0x05, 0xcd, 0x01,
88*4882a593Smuzhiyun 	0x29, 0xb1, 0x8d, 0xb0,
89*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
90*4882a593Smuzhiyun 	0x00, 0x01,
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	/* fake uuid */
93*4882a593Smuzhiyun 	0x00, 0x01,
94*4882a593Smuzhiyun 	0x02, 0x03, 0x04, 0x05,
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x37,
97*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x04,
98*4882a593Smuzhiyun 	0x44, 0x46, 0x4c, 0x54,
99*4882a593Smuzhiyun 	0x00, 0x00, 0xf0, 0x60,
100*4882a593Smuzhiyun 	0x00, 0x01, 0x00, 0x00,
101*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x01,
102*4882a593Smuzhiyun 	0x00, 0x00, 0xf0, 0x60,
103*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
104*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x04,
105*4882a593Smuzhiyun 	0x44, 0x46, 0x4c, 0x54,
106*4882a593Smuzhiyun 	0x00, 0x01,
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	/* fake uuid */
109*4882a593Smuzhiyun 	0x00, 0x01,
110*4882a593Smuzhiyun 	0x02, 0x03, 0x04, 0x05,
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
113*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
114*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00,
115*4882a593Smuzhiyun 	0x00, 0x00, 0x00, 0x00
116*4882a593Smuzhiyun };
117*4882a593Smuzhiyun 
sendpacket(int sock,struct sockaddr * addr,socklen_t addr_len)118*4882a593Smuzhiyun static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	struct timeval now;
121*4882a593Smuzhiyun 	int res;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	res = sendto(sock, sync, sizeof(sync), 0,
124*4882a593Smuzhiyun 		addr, addr_len);
125*4882a593Smuzhiyun 	gettimeofday(&now, 0);
126*4882a593Smuzhiyun 	if (res < 0)
127*4882a593Smuzhiyun 		printf("%s: %s\n", "send", strerror(errno));
128*4882a593Smuzhiyun 	else
129*4882a593Smuzhiyun 		printf("%ld.%06ld: sent %d bytes\n",
130*4882a593Smuzhiyun 		       (long)now.tv_sec, (long)now.tv_usec,
131*4882a593Smuzhiyun 		       res);
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun 
printpacket(struct msghdr * msg,int res,char * data,int sock,int recvmsg_flags,int siocgstamp,int siocgstampns)134*4882a593Smuzhiyun static void printpacket(struct msghdr *msg, int res,
135*4882a593Smuzhiyun 			char *data,
136*4882a593Smuzhiyun 			int sock, int recvmsg_flags,
137*4882a593Smuzhiyun 			int siocgstamp, int siocgstampns)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
140*4882a593Smuzhiyun 	struct cmsghdr *cmsg;
141*4882a593Smuzhiyun 	struct timeval tv;
142*4882a593Smuzhiyun 	struct timespec ts;
143*4882a593Smuzhiyun 	struct timeval now;
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	gettimeofday(&now, 0);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n",
148*4882a593Smuzhiyun 	       (long)now.tv_sec, (long)now.tv_usec,
149*4882a593Smuzhiyun 	       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
150*4882a593Smuzhiyun 	       res,
151*4882a593Smuzhiyun 	       inet_ntoa(from_addr->sin_addr),
152*4882a593Smuzhiyun 	       msg->msg_controllen);
153*4882a593Smuzhiyun 	for (cmsg = CMSG_FIRSTHDR(msg);
154*4882a593Smuzhiyun 	     cmsg;
155*4882a593Smuzhiyun 	     cmsg = CMSG_NXTHDR(msg, cmsg)) {
156*4882a593Smuzhiyun 		printf("   cmsg len %zu: ", cmsg->cmsg_len);
157*4882a593Smuzhiyun 		switch (cmsg->cmsg_level) {
158*4882a593Smuzhiyun 		case SOL_SOCKET:
159*4882a593Smuzhiyun 			printf("SOL_SOCKET ");
160*4882a593Smuzhiyun 			switch (cmsg->cmsg_type) {
161*4882a593Smuzhiyun 			case SO_TIMESTAMP: {
162*4882a593Smuzhiyun 				struct timeval *stamp =
163*4882a593Smuzhiyun 					(struct timeval *)CMSG_DATA(cmsg);
164*4882a593Smuzhiyun 				printf("SO_TIMESTAMP %ld.%06ld",
165*4882a593Smuzhiyun 				       (long)stamp->tv_sec,
166*4882a593Smuzhiyun 				       (long)stamp->tv_usec);
167*4882a593Smuzhiyun 				break;
168*4882a593Smuzhiyun 			}
169*4882a593Smuzhiyun 			case SO_TIMESTAMPNS: {
170*4882a593Smuzhiyun 				struct timespec *stamp =
171*4882a593Smuzhiyun 					(struct timespec *)CMSG_DATA(cmsg);
172*4882a593Smuzhiyun 				printf("SO_TIMESTAMPNS %ld.%09ld",
173*4882a593Smuzhiyun 				       (long)stamp->tv_sec,
174*4882a593Smuzhiyun 				       (long)stamp->tv_nsec);
175*4882a593Smuzhiyun 				break;
176*4882a593Smuzhiyun 			}
177*4882a593Smuzhiyun 			case SO_TIMESTAMPING: {
178*4882a593Smuzhiyun 				struct timespec *stamp =
179*4882a593Smuzhiyun 					(struct timespec *)CMSG_DATA(cmsg);
180*4882a593Smuzhiyun 				printf("SO_TIMESTAMPING ");
181*4882a593Smuzhiyun 				printf("SW %ld.%09ld ",
182*4882a593Smuzhiyun 				       (long)stamp->tv_sec,
183*4882a593Smuzhiyun 				       (long)stamp->tv_nsec);
184*4882a593Smuzhiyun 				stamp++;
185*4882a593Smuzhiyun 				/* skip deprecated HW transformed */
186*4882a593Smuzhiyun 				stamp++;
187*4882a593Smuzhiyun 				printf("HW raw %ld.%09ld",
188*4882a593Smuzhiyun 				       (long)stamp->tv_sec,
189*4882a593Smuzhiyun 				       (long)stamp->tv_nsec);
190*4882a593Smuzhiyun 				break;
191*4882a593Smuzhiyun 			}
192*4882a593Smuzhiyun 			default:
193*4882a593Smuzhiyun 				printf("type %d", cmsg->cmsg_type);
194*4882a593Smuzhiyun 				break;
195*4882a593Smuzhiyun 			}
196*4882a593Smuzhiyun 			break;
197*4882a593Smuzhiyun 		case IPPROTO_IP:
198*4882a593Smuzhiyun 			printf("IPPROTO_IP ");
199*4882a593Smuzhiyun 			switch (cmsg->cmsg_type) {
200*4882a593Smuzhiyun 			case IP_RECVERR: {
201*4882a593Smuzhiyun 				struct sock_extended_err *err =
202*4882a593Smuzhiyun 					(struct sock_extended_err *)CMSG_DATA(cmsg);
203*4882a593Smuzhiyun 				printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
204*4882a593Smuzhiyun 					strerror(err->ee_errno),
205*4882a593Smuzhiyun 					err->ee_origin,
206*4882a593Smuzhiyun #ifdef SO_EE_ORIGIN_TIMESTAMPING
207*4882a593Smuzhiyun 					err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
208*4882a593Smuzhiyun 					"bounced packet" : "unexpected origin"
209*4882a593Smuzhiyun #else
210*4882a593Smuzhiyun 					"probably SO_EE_ORIGIN_TIMESTAMPING"
211*4882a593Smuzhiyun #endif
212*4882a593Smuzhiyun 					);
213*4882a593Smuzhiyun 				if (res < sizeof(sync))
214*4882a593Smuzhiyun 					printf(" => truncated data?!");
215*4882a593Smuzhiyun 				else if (!memcmp(sync, data + res - sizeof(sync),
216*4882a593Smuzhiyun 							sizeof(sync)))
217*4882a593Smuzhiyun 					printf(" => GOT OUR DATA BACK (HURRAY!)");
218*4882a593Smuzhiyun 				break;
219*4882a593Smuzhiyun 			}
220*4882a593Smuzhiyun 			case IP_PKTINFO: {
221*4882a593Smuzhiyun 				struct in_pktinfo *pktinfo =
222*4882a593Smuzhiyun 					(struct in_pktinfo *)CMSG_DATA(cmsg);
223*4882a593Smuzhiyun 				printf("IP_PKTINFO interface index %u",
224*4882a593Smuzhiyun 					pktinfo->ipi_ifindex);
225*4882a593Smuzhiyun 				break;
226*4882a593Smuzhiyun 			}
227*4882a593Smuzhiyun 			default:
228*4882a593Smuzhiyun 				printf("type %d", cmsg->cmsg_type);
229*4882a593Smuzhiyun 				break;
230*4882a593Smuzhiyun 			}
231*4882a593Smuzhiyun 			break;
232*4882a593Smuzhiyun 		default:
233*4882a593Smuzhiyun 			printf("level %d type %d",
234*4882a593Smuzhiyun 				cmsg->cmsg_level,
235*4882a593Smuzhiyun 				cmsg->cmsg_type);
236*4882a593Smuzhiyun 			break;
237*4882a593Smuzhiyun 		}
238*4882a593Smuzhiyun 		printf("\n");
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	if (siocgstamp) {
242*4882a593Smuzhiyun 		if (ioctl(sock, SIOCGSTAMP, &tv))
243*4882a593Smuzhiyun 			printf("   %s: %s\n", "SIOCGSTAMP", strerror(errno));
244*4882a593Smuzhiyun 		else
245*4882a593Smuzhiyun 			printf("SIOCGSTAMP %ld.%06ld\n",
246*4882a593Smuzhiyun 			       (long)tv.tv_sec,
247*4882a593Smuzhiyun 			       (long)tv.tv_usec);
248*4882a593Smuzhiyun 	}
249*4882a593Smuzhiyun 	if (siocgstampns) {
250*4882a593Smuzhiyun 		if (ioctl(sock, SIOCGSTAMPNS, &ts))
251*4882a593Smuzhiyun 			printf("   %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
252*4882a593Smuzhiyun 		else
253*4882a593Smuzhiyun 			printf("SIOCGSTAMPNS %ld.%09ld\n",
254*4882a593Smuzhiyun 			       (long)ts.tv_sec,
255*4882a593Smuzhiyun 			       (long)ts.tv_nsec);
256*4882a593Smuzhiyun 	}
257*4882a593Smuzhiyun }
258*4882a593Smuzhiyun 
recvpacket(int sock,int recvmsg_flags,int siocgstamp,int siocgstampns)259*4882a593Smuzhiyun static void recvpacket(int sock, int recvmsg_flags,
260*4882a593Smuzhiyun 		       int siocgstamp, int siocgstampns)
261*4882a593Smuzhiyun {
262*4882a593Smuzhiyun 	char data[256];
263*4882a593Smuzhiyun 	struct msghdr msg;
264*4882a593Smuzhiyun 	struct iovec entry;
265*4882a593Smuzhiyun 	struct sockaddr_in from_addr;
266*4882a593Smuzhiyun 	struct {
267*4882a593Smuzhiyun 		struct cmsghdr cm;
268*4882a593Smuzhiyun 		char control[512];
269*4882a593Smuzhiyun 	} control;
270*4882a593Smuzhiyun 	int res;
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	memset(&msg, 0, sizeof(msg));
273*4882a593Smuzhiyun 	msg.msg_iov = &entry;
274*4882a593Smuzhiyun 	msg.msg_iovlen = 1;
275*4882a593Smuzhiyun 	entry.iov_base = data;
276*4882a593Smuzhiyun 	entry.iov_len = sizeof(data);
277*4882a593Smuzhiyun 	msg.msg_name = (caddr_t)&from_addr;
278*4882a593Smuzhiyun 	msg.msg_namelen = sizeof(from_addr);
279*4882a593Smuzhiyun 	msg.msg_control = &control;
280*4882a593Smuzhiyun 	msg.msg_controllen = sizeof(control);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
283*4882a593Smuzhiyun 	if (res < 0) {
284*4882a593Smuzhiyun 		printf("%s %s: %s\n",
285*4882a593Smuzhiyun 		       "recvmsg",
286*4882a593Smuzhiyun 		       (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
287*4882a593Smuzhiyun 		       strerror(errno));
288*4882a593Smuzhiyun 	} else {
289*4882a593Smuzhiyun 		printpacket(&msg, res, data,
290*4882a593Smuzhiyun 			    sock, recvmsg_flags,
291*4882a593Smuzhiyun 			    siocgstamp, siocgstampns);
292*4882a593Smuzhiyun 	}
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun 
main(int argc,char ** argv)295*4882a593Smuzhiyun int main(int argc, char **argv)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	int so_timestamping_flags = 0;
298*4882a593Smuzhiyun 	int so_timestamp = 0;
299*4882a593Smuzhiyun 	int so_timestampns = 0;
300*4882a593Smuzhiyun 	int siocgstamp = 0;
301*4882a593Smuzhiyun 	int siocgstampns = 0;
302*4882a593Smuzhiyun 	int ip_multicast_loop = 0;
303*4882a593Smuzhiyun 	char *interface;
304*4882a593Smuzhiyun 	int i;
305*4882a593Smuzhiyun 	int enabled = 1;
306*4882a593Smuzhiyun 	int sock;
307*4882a593Smuzhiyun 	struct ifreq device;
308*4882a593Smuzhiyun 	struct ifreq hwtstamp;
309*4882a593Smuzhiyun 	struct hwtstamp_config hwconfig, hwconfig_requested;
310*4882a593Smuzhiyun 	struct sockaddr_in addr;
311*4882a593Smuzhiyun 	struct ip_mreq imr;
312*4882a593Smuzhiyun 	struct in_addr iaddr;
313*4882a593Smuzhiyun 	int val;
314*4882a593Smuzhiyun 	socklen_t len;
315*4882a593Smuzhiyun 	struct timeval next;
316*4882a593Smuzhiyun 	size_t if_len;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	if (argc < 2)
319*4882a593Smuzhiyun 		usage(0);
320*4882a593Smuzhiyun 	interface = argv[1];
321*4882a593Smuzhiyun 	if_len = strlen(interface);
322*4882a593Smuzhiyun 	if (if_len >= IFNAMSIZ) {
323*4882a593Smuzhiyun 		printf("interface name exceeds IFNAMSIZ\n");
324*4882a593Smuzhiyun 		exit(1);
325*4882a593Smuzhiyun 	}
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	for (i = 2; i < argc; i++) {
328*4882a593Smuzhiyun 		if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
329*4882a593Smuzhiyun 			so_timestamp = 1;
330*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
331*4882a593Smuzhiyun 			so_timestampns = 1;
332*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
333*4882a593Smuzhiyun 			siocgstamp = 1;
334*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
335*4882a593Smuzhiyun 			siocgstampns = 1;
336*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
337*4882a593Smuzhiyun 			ip_multicast_loop = 1;
338*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
339*4882a593Smuzhiyun 			so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
340*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
341*4882a593Smuzhiyun 			so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
342*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
343*4882a593Smuzhiyun 			so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
344*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
345*4882a593Smuzhiyun 			so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
346*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
347*4882a593Smuzhiyun 			so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
348*4882a593Smuzhiyun 		else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
349*4882a593Smuzhiyun 			so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
350*4882a593Smuzhiyun 		else
351*4882a593Smuzhiyun 			usage(argv[i]);
352*4882a593Smuzhiyun 	}
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun 	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
355*4882a593Smuzhiyun 	if (sock < 0)
356*4882a593Smuzhiyun 		bail("socket");
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	memset(&device, 0, sizeof(device));
359*4882a593Smuzhiyun 	memcpy(device.ifr_name, interface, if_len + 1);
360*4882a593Smuzhiyun 	if (ioctl(sock, SIOCGIFADDR, &device) < 0)
361*4882a593Smuzhiyun 		bail("getting interface IP address");
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	memset(&hwtstamp, 0, sizeof(hwtstamp));
364*4882a593Smuzhiyun 	memcpy(hwtstamp.ifr_name, interface, if_len + 1);
365*4882a593Smuzhiyun 	hwtstamp.ifr_data = (void *)&hwconfig;
366*4882a593Smuzhiyun 	memset(&hwconfig, 0, sizeof(hwconfig));
367*4882a593Smuzhiyun 	hwconfig.tx_type =
368*4882a593Smuzhiyun 		(so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
369*4882a593Smuzhiyun 		HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
370*4882a593Smuzhiyun 	hwconfig.rx_filter =
371*4882a593Smuzhiyun 		(so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
372*4882a593Smuzhiyun 		HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
373*4882a593Smuzhiyun 	hwconfig_requested = hwconfig;
374*4882a593Smuzhiyun 	if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
375*4882a593Smuzhiyun 		if ((errno == EINVAL || errno == ENOTSUP) &&
376*4882a593Smuzhiyun 		    hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
377*4882a593Smuzhiyun 		    hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
378*4882a593Smuzhiyun 			printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
379*4882a593Smuzhiyun 		else
380*4882a593Smuzhiyun 			bail("SIOCSHWTSTAMP");
381*4882a593Smuzhiyun 	}
382*4882a593Smuzhiyun 	printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
383*4882a593Smuzhiyun 	       hwconfig_requested.tx_type, hwconfig.tx_type,
384*4882a593Smuzhiyun 	       hwconfig_requested.rx_filter, hwconfig.rx_filter);
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 	/* bind to PTP port */
387*4882a593Smuzhiyun 	addr.sin_family = AF_INET;
388*4882a593Smuzhiyun 	addr.sin_addr.s_addr = htonl(INADDR_ANY);
389*4882a593Smuzhiyun 	addr.sin_port = htons(319 /* PTP event port */);
390*4882a593Smuzhiyun 	if (bind(sock,
391*4882a593Smuzhiyun 		 (struct sockaddr *)&addr,
392*4882a593Smuzhiyun 		 sizeof(struct sockaddr_in)) < 0)
393*4882a593Smuzhiyun 		bail("bind");
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	/* set multicast group for outgoing packets */
396*4882a593Smuzhiyun 	inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
397*4882a593Smuzhiyun 	addr.sin_addr = iaddr;
398*4882a593Smuzhiyun 	imr.imr_multiaddr.s_addr = iaddr.s_addr;
399*4882a593Smuzhiyun 	imr.imr_interface.s_addr =
400*4882a593Smuzhiyun 		((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
401*4882a593Smuzhiyun 	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
402*4882a593Smuzhiyun 		       &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
403*4882a593Smuzhiyun 		bail("set multicast");
404*4882a593Smuzhiyun 
405*4882a593Smuzhiyun 	/* join multicast group, loop our own packet */
406*4882a593Smuzhiyun 	if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
407*4882a593Smuzhiyun 		       &imr, sizeof(struct ip_mreq)) < 0)
408*4882a593Smuzhiyun 		bail("join multicast group");
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
411*4882a593Smuzhiyun 		       &ip_multicast_loop, sizeof(enabled)) < 0) {
412*4882a593Smuzhiyun 		bail("loop multicast");
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	/* set socket options for time stamping */
416*4882a593Smuzhiyun 	if (so_timestamp &&
417*4882a593Smuzhiyun 		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
418*4882a593Smuzhiyun 			   &enabled, sizeof(enabled)) < 0)
419*4882a593Smuzhiyun 		bail("setsockopt SO_TIMESTAMP");
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	if (so_timestampns &&
422*4882a593Smuzhiyun 		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
423*4882a593Smuzhiyun 			   &enabled, sizeof(enabled)) < 0)
424*4882a593Smuzhiyun 		bail("setsockopt SO_TIMESTAMPNS");
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	if (so_timestamping_flags &&
427*4882a593Smuzhiyun 		setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
428*4882a593Smuzhiyun 			   &so_timestamping_flags,
429*4882a593Smuzhiyun 			   sizeof(so_timestamping_flags)) < 0)
430*4882a593Smuzhiyun 		bail("setsockopt SO_TIMESTAMPING");
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	/* request IP_PKTINFO for debugging purposes */
433*4882a593Smuzhiyun 	if (setsockopt(sock, SOL_IP, IP_PKTINFO,
434*4882a593Smuzhiyun 		       &enabled, sizeof(enabled)) < 0)
435*4882a593Smuzhiyun 		printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	/* verify socket options */
438*4882a593Smuzhiyun 	len = sizeof(val);
439*4882a593Smuzhiyun 	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
440*4882a593Smuzhiyun 		printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
441*4882a593Smuzhiyun 	else
442*4882a593Smuzhiyun 		printf("SO_TIMESTAMP %d\n", val);
443*4882a593Smuzhiyun 
444*4882a593Smuzhiyun 	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
445*4882a593Smuzhiyun 		printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
446*4882a593Smuzhiyun 		       strerror(errno));
447*4882a593Smuzhiyun 	else
448*4882a593Smuzhiyun 		printf("SO_TIMESTAMPNS %d\n", val);
449*4882a593Smuzhiyun 
450*4882a593Smuzhiyun 	if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
451*4882a593Smuzhiyun 		printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
452*4882a593Smuzhiyun 		       strerror(errno));
453*4882a593Smuzhiyun 	} else {
454*4882a593Smuzhiyun 		printf("SO_TIMESTAMPING %d\n", val);
455*4882a593Smuzhiyun 		if (val != so_timestamping_flags)
456*4882a593Smuzhiyun 			printf("   not the expected value %d\n",
457*4882a593Smuzhiyun 			       so_timestamping_flags);
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	/* send packets forever every five seconds */
461*4882a593Smuzhiyun 	gettimeofday(&next, 0);
462*4882a593Smuzhiyun 	next.tv_sec = (next.tv_sec + 1) / 5 * 5;
463*4882a593Smuzhiyun 	next.tv_usec = 0;
464*4882a593Smuzhiyun 	while (1) {
465*4882a593Smuzhiyun 		struct timeval now;
466*4882a593Smuzhiyun 		struct timeval delta;
467*4882a593Smuzhiyun 		long delta_us;
468*4882a593Smuzhiyun 		int res;
469*4882a593Smuzhiyun 		fd_set readfs, errorfs;
470*4882a593Smuzhiyun 
471*4882a593Smuzhiyun 		gettimeofday(&now, 0);
472*4882a593Smuzhiyun 		delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
473*4882a593Smuzhiyun 			(long)(next.tv_usec - now.tv_usec);
474*4882a593Smuzhiyun 		if (delta_us > 0) {
475*4882a593Smuzhiyun 			/* continue waiting for timeout or data */
476*4882a593Smuzhiyun 			delta.tv_sec = delta_us / 1000000;
477*4882a593Smuzhiyun 			delta.tv_usec = delta_us % 1000000;
478*4882a593Smuzhiyun 
479*4882a593Smuzhiyun 			FD_ZERO(&readfs);
480*4882a593Smuzhiyun 			FD_ZERO(&errorfs);
481*4882a593Smuzhiyun 			FD_SET(sock, &readfs);
482*4882a593Smuzhiyun 			FD_SET(sock, &errorfs);
483*4882a593Smuzhiyun 			printf("%ld.%06ld: select %ldus\n",
484*4882a593Smuzhiyun 			       (long)now.tv_sec, (long)now.tv_usec,
485*4882a593Smuzhiyun 			       delta_us);
486*4882a593Smuzhiyun 			res = select(sock + 1, &readfs, 0, &errorfs, &delta);
487*4882a593Smuzhiyun 			gettimeofday(&now, 0);
488*4882a593Smuzhiyun 			printf("%ld.%06ld: select returned: %d, %s\n",
489*4882a593Smuzhiyun 			       (long)now.tv_sec, (long)now.tv_usec,
490*4882a593Smuzhiyun 			       res,
491*4882a593Smuzhiyun 			       res < 0 ? strerror(errno) : "success");
492*4882a593Smuzhiyun 			if (res > 0) {
493*4882a593Smuzhiyun 				if (FD_ISSET(sock, &readfs))
494*4882a593Smuzhiyun 					printf("ready for reading\n");
495*4882a593Smuzhiyun 				if (FD_ISSET(sock, &errorfs))
496*4882a593Smuzhiyun 					printf("has error\n");
497*4882a593Smuzhiyun 				recvpacket(sock, 0,
498*4882a593Smuzhiyun 					   siocgstamp,
499*4882a593Smuzhiyun 					   siocgstampns);
500*4882a593Smuzhiyun 				recvpacket(sock, MSG_ERRQUEUE,
501*4882a593Smuzhiyun 					   siocgstamp,
502*4882a593Smuzhiyun 					   siocgstampns);
503*4882a593Smuzhiyun 			}
504*4882a593Smuzhiyun 		} else {
505*4882a593Smuzhiyun 			/* write one packet */
506*4882a593Smuzhiyun 			sendpacket(sock,
507*4882a593Smuzhiyun 				   (struct sockaddr *)&addr,
508*4882a593Smuzhiyun 				   sizeof(addr));
509*4882a593Smuzhiyun 			next.tv_sec += 5;
510*4882a593Smuzhiyun 			continue;
511*4882a593Smuzhiyun 		}
512*4882a593Smuzhiyun 	}
513*4882a593Smuzhiyun 
514*4882a593Smuzhiyun 	return 0;
515*4882a593Smuzhiyun }
516