Linux Audio

Check our new training course

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