aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation/networking
diff options
context:
space:
mode:
authorPatrick Ohly <patrick.ohly@intel.com>2009-02-12 00:03:36 -0500
committerDavid S. Miller <davem@davemloft.net>2009-02-16 01:43:33 -0500
commitcb9eff097831007afb30d64373f29d99825d0068 (patch)
tree823a5668c6f4b3f577a1d4cc73b3df2d7e33c5c4 /Documentation/networking
parenta75244c3d519fcb490ca2bf3f123c98017f1e8d0 (diff)
net: new user space API for time stamping of incoming and outgoing packets
User space can request hardware and/or software time stamping. Reporting of the result(s) via a new control message is enabled separately for each field in the message because some of the fields may require additional computation and thus cause overhead. User space can tell the different kinds of time stamps apart and choose what suits its needs. When a TX timestamp operation is requested, the TX skb will be cloned and the clone will be time stamped (in hardware or software) and added to the socket error queue of the skb, if the skb has a socket associated with it. The actual TX timestamp will reach userspace as a RX timestamp on the cloned packet. If timestamping is requested and no timestamping is done in the device driver (potentially this may use hardware timestamping), it will be done in software after the device's start_hard_xmit routine. Signed-off-by: Patrick Ohly <patrick.ohly@intel.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'Documentation/networking')
-rw-r--r--Documentation/networking/timestamping.txt178
-rw-r--r--Documentation/networking/timestamping/.gitignore1
-rw-r--r--Documentation/networking/timestamping/Makefile6
-rw-r--r--Documentation/networking/timestamping/timestamping.c533
4 files changed, 718 insertions, 0 deletions
diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt
new file mode 100644
index 000000000000..a681a65b5bc7
--- /dev/null
+++ b/Documentation/networking/timestamping.txt
@@ -0,0 +1,178 @@
1The existing interfaces for getting network packages time stamped are:
2
3* SO_TIMESTAMP
4 Generate time stamp for each incoming packet using the (not necessarily
5 monotonous!) system time. Result is returned via recv_msg() in a
6 control message as timeval (usec resolution).
7
8* SO_TIMESTAMPNS
9 Same time stamping mechanism as SO_TIMESTAMP, but returns result as
10 timespec (nsec resolution).
11
12* IP_MULTICAST_LOOP + SO_TIMESTAMP[NS]
13 Only for multicasts: approximate send time stamp by receiving the looped
14 packet and using its receive time stamp.
15
16The following interface complements the existing ones: receive time
17stamps can be generated and returned for arbitrary packets and much
18closer to the point where the packet is really sent. Time stamps can
19be generated in software (as before) or in hardware (if the hardware
20has such a feature).
21
22SO_TIMESTAMPING:
23
24Instructs the socket layer which kind of information is wanted. The
25parameter is an integer with some of the following bits set. Setting
26other bits is an error and doesn't change the current state.
27
28SOF_TIMESTAMPING_TX_HARDWARE: try to obtain send time stamp in hardware
29SOF_TIMESTAMPING_TX_SOFTWARE: if SOF_TIMESTAMPING_TX_HARDWARE is off or
30 fails, then do it in software
31SOF_TIMESTAMPING_RX_HARDWARE: return the original, unmodified time stamp
32 as generated by the hardware
33SOF_TIMESTAMPING_RX_SOFTWARE: if SOF_TIMESTAMPING_RX_HARDWARE is off or
34 fails, then do it in software
35SOF_TIMESTAMPING_RAW_HARDWARE: return original raw hardware time stamp
36SOF_TIMESTAMPING_SYS_HARDWARE: return hardware time stamp transformed to
37 the system time base
38SOF_TIMESTAMPING_SOFTWARE: return system time stamp generated in
39 software
40
41SOF_TIMESTAMPING_TX/RX determine how time stamps are generated.
42SOF_TIMESTAMPING_RAW/SYS determine how they are reported in the
43following control message:
44 struct scm_timestamping {
45 struct timespec systime;
46 struct timespec hwtimetrans;
47 struct timespec hwtimeraw;
48 };
49
50recvmsg() can be used to get this control message for regular incoming
51packets. For send time stamps the outgoing packet is looped back to
52the socket's error queue with the send time stamp(s) attached. It can
53be received with recvmsg(flags=MSG_ERRQUEUE). The call returns the
54original outgoing packet data including all headers preprended down to
55and including the link layer, the scm_timestamping control message and
56a sock_extended_err control message with ee_errno==ENOMSG and
57ee_origin==SO_EE_ORIGIN_TIMESTAMPING. A socket with such a pending
58bounced packet is ready for reading as far as select() is concerned.
59
60All three values correspond to the same event in time, but were
61generated in different ways. Each of these values may be empty (= all
62zero), in which case no such value was available. If the application
63is not interested in some of these values, they can be left blank to
64avoid the potential overhead of calculating them.
65
66systime is the value of the system time at that moment. This
67corresponds to the value also returned via SO_TIMESTAMP[NS]. If the
68time stamp was generated by hardware, then this field is
69empty. Otherwise it is filled in if SOF_TIMESTAMPING_SOFTWARE is
70set.
71
72hwtimeraw is the original hardware time stamp. Filled in if
73SOF_TIMESTAMPING_RAW_HARDWARE is set. No assumptions about its
74relation to system time should be made.
75
76hwtimetrans is the hardware time stamp transformed so that it
77corresponds as good as possible to system time. This correlation is
78not perfect; as a consequence, sorting packets received via different
79NICs by their hwtimetrans may differ from the order in which they were
80received. hwtimetrans may be non-monotonic even for the same NIC.
81Filled in if SOF_TIMESTAMPING_SYS_HARDWARE is set. Requires support
82by the network device and will be empty without that support.
83
84
85SIOCSHWTSTAMP:
86
87Hardware time stamping must also be initialized for each device driver
88that is expected to do hardware time stamping. The parameter is:
89
90struct hwtstamp_config {
91 int flags; /* no flags defined right now, must be zero */
92 int tx_type; /* HWTSTAMP_TX_* */
93 int rx_filter; /* HWTSTAMP_FILTER_* */
94};
95
96Desired behavior is passed into the kernel and to a specific device by
97calling ioctl(SIOCSHWTSTAMP) with a pointer to a struct ifreq whose
98ifr_data points to a struct hwtstamp_config. The tx_type and
99rx_filter are hints to the driver what it is expected to do. If
100the requested fine-grained filtering for incoming packets is not
101supported, the driver may time stamp more than just the requested types
102of packets.
103
104A driver which supports hardware time stamping shall update the struct
105with the actual, possibly more permissive configuration. If the
106requested packets cannot be time stamped, then nothing should be
107changed and ERANGE shall be returned (in contrast to EINVAL, which
108indicates that SIOCSHWTSTAMP is not supported at all).
109
110Only a processes with admin rights may change the configuration. User
111space is responsible to ensure that multiple processes don't interfere
112with each other and that the settings are reset.
113
114/* possible values for hwtstamp_config->tx_type */
115enum {
116 /*
117 * no outgoing packet will need hardware time stamping;
118 * should a packet arrive which asks for it, no hardware
119 * time stamping will be done
120 */
121 HWTSTAMP_TX_OFF,
122
123 /*
124 * enables hardware time stamping for outgoing packets;
125 * the sender of the packet decides which are to be
126 * time stamped by setting SOF_TIMESTAMPING_TX_SOFTWARE
127 * before sending the packet
128 */
129 HWTSTAMP_TX_ON,
130};
131
132/* possible values for hwtstamp_config->rx_filter */
133enum {
134 /* time stamp no incoming packet at all */
135 HWTSTAMP_FILTER_NONE,
136
137 /* time stamp any incoming packet */
138 HWTSTAMP_FILTER_ALL,
139
140 /* return value: time stamp all packets requested plus some others */
141 HWTSTAMP_FILTER_SOME,
142
143 /* PTP v1, UDP, any kind of event packet */
144 HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
145
146 ...
147};
148
149
150DEVICE IMPLEMENTATION
151
152A driver which supports hardware time stamping must support the
153SIOCSHWTSTAMP ioctl. Time stamps for received packets must be stored
154in the skb with skb_hwtstamp_set().
155
156Time stamps for outgoing packets are to be generated as follows:
157- In hard_start_xmit(), check if skb_hwtstamp_check_tx_hardware()
158 returns non-zero. If yes, then the driver is expected
159 to do hardware time stamping.
160- If this is possible for the skb and requested, then declare
161 that the driver is doing the time stamping by calling
162 skb_hwtstamp_tx_in_progress(). A driver not supporting
163 hardware time stamping doesn't do that. A driver must never
164 touch sk_buff::tstamp! It is used to store how time stamping
165 for an outgoing packets is to be done.
166- As soon as the driver has sent the packet and/or obtained a
167 hardware time stamp for it, it passes the time stamp back by
168 calling skb_hwtstamp_tx() with the original skb, the raw
169 hardware time stamp and a handle to the device (necessary
170 to convert the hardware time stamp to system time). If obtaining
171 the hardware time stamp somehow fails, then the driver should
172 not fall back to software time stamping. The rationale is that
173 this would occur at a later time in the processing pipeline
174 than other software time stamping and therefore could lead
175 to unexpected deltas between time stamps.
176- If the driver did not call skb_hwtstamp_tx_in_progress(), then
177 dev_hard_start_xmit() checks whether software time stamping
178 is wanted as fallback and potentially generates the time stamp.
diff --git a/Documentation/networking/timestamping/.gitignore b/Documentation/networking/timestamping/.gitignore
new file mode 100644
index 000000000000..71e81eb2e22f
--- /dev/null
+++ b/Documentation/networking/timestamping/.gitignore
@@ -0,0 +1 @@
timestamping
diff --git a/Documentation/networking/timestamping/Makefile b/Documentation/networking/timestamping/Makefile
new file mode 100644
index 000000000000..2a1489fdc036
--- /dev/null
+++ b/Documentation/networking/timestamping/Makefile
@@ -0,0 +1,6 @@
1CPPFLAGS = -I../../../include
2
3timestamping: timestamping.c
4
5clean:
6 rm -f timestamping
diff --git a/Documentation/networking/timestamping/timestamping.c b/Documentation/networking/timestamping/timestamping.c
new file mode 100644
index 000000000000..43d143104210
--- /dev/null
+++ b/Documentation/networking/timestamping/timestamping.c
@@ -0,0 +1,533 @@
1/*
2 * This program demonstrates how the various time stamping features in
3 * the Linux kernel work. It emulates the behavior of a PTP
4 * implementation in stand-alone master mode by sending PTPv1 Sync
5 * multicasts once every second. It looks for similar packets, but
6 * beyond that doesn't actually implement PTP.
7 *
8 * Outgoing packets are time stamped with SO_TIMESTAMPING with or
9 * without hardware support.
10 *
11 * Incoming packets are time stamped with SO_TIMESTAMPING with or
12 * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and
13 * SO_TIMESTAMP[NS].
14 *
15 * Copyright (C) 2009 Intel Corporation.
16 * Author: Patrick Ohly <patrick.ohly@intel.com>
17 *
18 * This program is free software; you can redistribute it and/or modify it
19 * under the terms and conditions of the GNU General Public License,
20 * version 2, as published by the Free Software Foundation.
21 *
22 * This program is distributed in the hope it will be useful, but WITHOUT
23 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
24 * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for
25 * more details.
26 *
27 * You should have received a copy of the GNU General Public License along with
28 * this program; if not, write to the Free Software Foundation, Inc.,
29 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
30 */
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <errno.h>
35#include <string.h>
36
37#include <sys/time.h>
38#include <sys/socket.h>
39#include <sys/select.h>
40#include <sys/ioctl.h>
41#include <arpa/inet.h>
42#include <net/if.h>
43
44#include "asm/types.h"
45#include "linux/net_tstamp.h"
46#include "linux/errqueue.h"
47
48#ifndef SO_TIMESTAMPING
49# define SO_TIMESTAMPING 37
50# define SCM_TIMESTAMPING SO_TIMESTAMPING
51#endif
52
53#ifndef SO_TIMESTAMPNS
54# define SO_TIMESTAMPNS 35
55#endif
56
57#ifndef SIOCGSTAMPNS
58# define SIOCGSTAMPNS 0x8907
59#endif
60
61#ifndef SIOCSHWTSTAMP
62# define SIOCSHWTSTAMP 0x89b0
63#endif
64
65static void usage(const char *error)
66{
67 if (error)
68 printf("invalid option: %s\n", error);
69 printf("timestamping interface option*\n\n"
70 "Options:\n"
71 " IP_MULTICAST_LOOP - looping outgoing multicasts\n"
72 " SO_TIMESTAMP - normal software time stamping, ms resolution\n"
73 " SO_TIMESTAMPNS - more accurate software time stamping\n"
74 " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n"
75 " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n"
76 " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n"
77 " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n"
78 " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n"
79 " SOF_TIMESTAMPING_SYS_HARDWARE - request reporting of transformed HW time stamps\n"
80 " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n"
81 " SIOCGSTAMP - check last socket time stamp\n"
82 " SIOCGSTAMPNS - more accurate socket time stamp\n");
83 exit(1);
84}
85
86static void bail(const char *error)
87{
88 printf("%s: %s\n", error, strerror(errno));
89 exit(1);
90}
91
92static const unsigned char sync[] = {
93 0x00, 0x01, 0x00, 0x01,
94 0x5f, 0x44, 0x46, 0x4c,
95 0x54, 0x00, 0x00, 0x00,
96 0x00, 0x00, 0x00, 0x00,
97 0x00, 0x00, 0x00, 0x00,
98 0x01, 0x01,
99
100 /* fake uuid */
101 0x00, 0x01,
102 0x02, 0x03, 0x04, 0x05,
103
104 0x00, 0x01, 0x00, 0x37,
105 0x00, 0x00, 0x00, 0x08,
106 0x00, 0x00, 0x00, 0x00,
107 0x49, 0x05, 0xcd, 0x01,
108 0x29, 0xb1, 0x8d, 0xb0,
109 0x00, 0x00, 0x00, 0x00,
110 0x00, 0x01,
111
112 /* fake uuid */
113 0x00, 0x01,
114 0x02, 0x03, 0x04, 0x05,
115
116 0x00, 0x00, 0x00, 0x37,
117 0x00, 0x00, 0x00, 0x04,
118 0x44, 0x46, 0x4c, 0x54,
119 0x00, 0x00, 0xf0, 0x60,
120 0x00, 0x01, 0x00, 0x00,
121 0x00, 0x00, 0x00, 0x01,
122 0x00, 0x00, 0xf0, 0x60,
123 0x00, 0x00, 0x00, 0x00,
124 0x00, 0x00, 0x00, 0x04,
125 0x44, 0x46, 0x4c, 0x54,
126 0x00, 0x01,
127
128 /* fake uuid */
129 0x00, 0x01,
130 0x02, 0x03, 0x04, 0x05,
131
132 0x00, 0x00, 0x00, 0x00,
133 0x00, 0x00, 0x00, 0x00,
134 0x00, 0x00, 0x00, 0x00,
135 0x00, 0x00, 0x00, 0x00
136};
137
138static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len)
139{
140 struct timeval now;
141 int res;
142
143 res = sendto(sock, sync, sizeof(sync), 0,
144 addr, addr_len);
145 gettimeofday(&now, 0);
146 if (res < 0)
147 printf("%s: %s\n", "send", strerror(errno));
148 else
149 printf("%ld.%06ld: sent %d bytes\n",
150 (long)now.tv_sec, (long)now.tv_usec,
151 res);
152}
153
154static void printpacket(struct msghdr *msg, int res,
155 char *data,
156 int sock, int recvmsg_flags,
157 int siocgstamp, int siocgstampns)
158{
159 struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name;
160 struct cmsghdr *cmsg;
161 struct timeval tv;
162 struct timespec ts;
163 struct timeval now;
164
165 gettimeofday(&now, 0);
166
167 printf("%ld.%06ld: received %s data, %d bytes from %s, %d bytes control messages\n",
168 (long)now.tv_sec, (long)now.tv_usec,
169 (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
170 res,
171 inet_ntoa(from_addr->sin_addr),
172 msg->msg_controllen);
173 for (cmsg = CMSG_FIRSTHDR(msg);
174 cmsg;
175 cmsg = CMSG_NXTHDR(msg, cmsg)) {
176 printf(" cmsg len %d: ", cmsg->cmsg_len);
177 switch (cmsg->cmsg_level) {
178 case SOL_SOCKET:
179 printf("SOL_SOCKET ");
180 switch (cmsg->cmsg_type) {
181 case SO_TIMESTAMP: {
182 struct timeval *stamp =
183 (struct timeval *)CMSG_DATA(cmsg);
184 printf("SO_TIMESTAMP %ld.%06ld",
185 (long)stamp->tv_sec,
186 (long)stamp->tv_usec);
187 break;
188 }
189 case SO_TIMESTAMPNS: {
190 struct timespec *stamp =
191 (struct timespec *)CMSG_DATA(cmsg);
192 printf("SO_TIMESTAMPNS %ld.%09ld",
193 (long)stamp->tv_sec,
194 (long)stamp->tv_nsec);
195 break;
196 }
197 case SO_TIMESTAMPING: {
198 struct timespec *stamp =
199 (struct timespec *)CMSG_DATA(cmsg);
200 printf("SO_TIMESTAMPING ");
201 printf("SW %ld.%09ld ",
202 (long)stamp->tv_sec,
203 (long)stamp->tv_nsec);
204 stamp++;
205 printf("HW transformed %ld.%09ld ",
206 (long)stamp->tv_sec,
207 (long)stamp->tv_nsec);
208 stamp++;
209 printf("HW raw %ld.%09ld",
210 (long)stamp->tv_sec,
211 (long)stamp->tv_nsec);
212 break;
213 }
214 default:
215 printf("type %d", cmsg->cmsg_type);
216 break;
217 }
218 break;
219 case IPPROTO_IP:
220 printf("IPPROTO_IP ");
221 switch (cmsg->cmsg_type) {
222 case IP_RECVERR: {
223 struct sock_extended_err *err =
224 (struct sock_extended_err *)CMSG_DATA(cmsg);
225 printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s",
226 strerror(err->ee_errno),
227 err->ee_origin,
228#ifdef SO_EE_ORIGIN_TIMESTAMPING
229 err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ?
230 "bounced packet" : "unexpected origin"
231#else
232 "probably SO_EE_ORIGIN_TIMESTAMPING"
233#endif
234 );
235 if (res < sizeof(sync))
236 printf(" => truncated data?!");
237 else if (!memcmp(sync, data + res - sizeof(sync),
238 sizeof(sync)))
239 printf(" => GOT OUR DATA BACK (HURRAY!)");
240 break;
241 }
242 case IP_PKTINFO: {
243 struct in_pktinfo *pktinfo =
244 (struct in_pktinfo *)CMSG_DATA(cmsg);
245 printf("IP_PKTINFO interface index %u",
246 pktinfo->ipi_ifindex);
247 break;
248 }
249 default:
250 printf("type %d", cmsg->cmsg_type);
251 break;
252 }
253 break;
254 default:
255 printf("level %d type %d",
256 cmsg->cmsg_level,
257 cmsg->cmsg_type);
258 break;
259 }
260 printf("\n");
261 }
262
263 if (siocgstamp) {
264 if (ioctl(sock, SIOCGSTAMP, &tv))
265 printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno));
266 else
267 printf("SIOCGSTAMP %ld.%06ld\n",
268 (long)tv.tv_sec,
269 (long)tv.tv_usec);
270 }
271 if (siocgstampns) {
272 if (ioctl(sock, SIOCGSTAMPNS, &ts))
273 printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno));
274 else
275 printf("SIOCGSTAMPNS %ld.%09ld\n",
276 (long)ts.tv_sec,
277 (long)ts.tv_nsec);
278 }
279}
280
281static void recvpacket(int sock, int recvmsg_flags,
282 int siocgstamp, int siocgstampns)
283{
284 char data[256];
285 struct msghdr msg;
286 struct iovec entry;
287 struct sockaddr_in from_addr;
288 struct {
289 struct cmsghdr cm;
290 char control[512];
291 } control;
292 int res;
293
294 memset(&msg, 0, sizeof(msg));
295 msg.msg_iov = &entry;
296 msg.msg_iovlen = 1;
297 entry.iov_base = data;
298 entry.iov_len = sizeof(data);
299 msg.msg_name = (caddr_t)&from_addr;
300 msg.msg_namelen = sizeof(from_addr);
301 msg.msg_control = &control;
302 msg.msg_controllen = sizeof(control);
303
304 res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT);
305 if (res < 0) {
306 printf("%s %s: %s\n",
307 "recvmsg",
308 (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
309 strerror(errno));
310 } else {
311 printpacket(&msg, res, data,
312 sock, recvmsg_flags,
313 siocgstamp, siocgstampns);
314 }
315}
316
317int main(int argc, char **argv)
318{
319 int so_timestamping_flags = 0;
320 int so_timestamp = 0;
321 int so_timestampns = 0;
322 int siocgstamp = 0;
323 int siocgstampns = 0;
324 int ip_multicast_loop = 0;
325 char *interface;
326 int i;
327 int enabled = 1;
328 int sock;
329 struct ifreq device;
330 struct ifreq hwtstamp;
331 struct hwtstamp_config hwconfig, hwconfig_requested;
332 struct sockaddr_in addr;
333 struct ip_mreq imr;
334 struct in_addr iaddr;
335 int val;
336 socklen_t len;
337 struct timeval next;
338
339 if (argc < 2)
340 usage(0);
341 interface = argv[1];
342
343 for (i = 2; i < argc; i++) {
344 if (!strcasecmp(argv[i], "SO_TIMESTAMP"))
345 so_timestamp = 1;
346 else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS"))
347 so_timestampns = 1;
348 else if (!strcasecmp(argv[i], "SIOCGSTAMP"))
349 siocgstamp = 1;
350 else if (!strcasecmp(argv[i], "SIOCGSTAMPNS"))
351 siocgstampns = 1;
352 else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP"))
353 ip_multicast_loop = 1;
354 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE"))
355 so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
356 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE"))
357 so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE;
358 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE"))
359 so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE;
360 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE"))
361 so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE;
362 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE"))
363 so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
364 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SYS_HARDWARE"))
365 so_timestamping_flags |= SOF_TIMESTAMPING_SYS_HARDWARE;
366 else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE"))
367 so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
368 else
369 usage(argv[i]);
370 }
371
372 sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
373 if (socket < 0)
374 bail("socket");
375
376 memset(&device, 0, sizeof(device));
377 strncpy(device.ifr_name, interface, sizeof(device.ifr_name));
378 if (ioctl(sock, SIOCGIFADDR, &device) < 0)
379 bail("getting interface IP address");
380
381 memset(&hwtstamp, 0, sizeof(hwtstamp));
382 strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name));
383 hwtstamp.ifr_data = (void *)&hwconfig;
384 memset(&hwconfig, 0, sizeof(&hwconfig));
385 hwconfig.tx_type =
386 (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ?
387 HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF;
388 hwconfig.rx_filter =
389 (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ?
390 HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE;
391 hwconfig_requested = hwconfig;
392 if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) {
393 if ((errno == EINVAL || errno == ENOTSUP) &&
394 hwconfig_requested.tx_type == HWTSTAMP_TX_OFF &&
395 hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE)
396 printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n");
397 else
398 bail("SIOCSHWTSTAMP");
399 }
400 printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n",
401 hwconfig_requested.tx_type, hwconfig.tx_type,
402 hwconfig_requested.rx_filter, hwconfig.rx_filter);
403
404 /* bind to PTP port */
405 addr.sin_family = AF_INET;
406 addr.sin_addr.s_addr = htonl(INADDR_ANY);
407 addr.sin_port = htons(319 /* PTP event port */);
408 if (bind(sock,
409 (struct sockaddr *)&addr,
410 sizeof(struct sockaddr_in)) < 0)
411 bail("bind");
412
413 /* set multicast group for outgoing packets */
414 inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */
415 addr.sin_addr = iaddr;
416 imr.imr_multiaddr.s_addr = iaddr.s_addr;
417 imr.imr_interface.s_addr =
418 ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr;
419 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF,
420 &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0)
421 bail("set multicast");
422
423 /* join multicast group, loop our own packet */
424 if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
425 &imr, sizeof(struct ip_mreq)) < 0)
426 bail("join multicast group");
427
428 if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP,
429 &ip_multicast_loop, sizeof(enabled)) < 0) {
430 bail("loop multicast");
431 }
432
433 /* set socket options for time stamping */
434 if (so_timestamp &&
435 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP,
436 &enabled, sizeof(enabled)) < 0)
437 bail("setsockopt SO_TIMESTAMP");
438
439 if (so_timestampns &&
440 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
441 &enabled, sizeof(enabled)) < 0)
442 bail("setsockopt SO_TIMESTAMPNS");
443
444 if (so_timestamping_flags &&
445 setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
446 &so_timestamping_flags,
447 sizeof(so_timestamping_flags)) < 0)
448 bail("setsockopt SO_TIMESTAMPING");
449
450 /* request IP_PKTINFO for debugging purposes */
451 if (setsockopt(sock, SOL_IP, IP_PKTINFO,
452 &enabled, sizeof(enabled)) < 0)
453 printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno));
454
455 /* verify socket options */
456 len = sizeof(val);
457 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0)
458 printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno));
459 else
460 printf("SO_TIMESTAMP %d\n", val);
461
462 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0)
463 printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS",
464 strerror(errno));
465 else
466 printf("SO_TIMESTAMPNS %d\n", val);
467
468 if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) {
469 printf("%s: %s\n", "getsockopt SO_TIMESTAMPING",
470 strerror(errno));
471 } else {
472 printf("SO_TIMESTAMPING %d\n", val);
473 if (val != so_timestamping_flags)
474 printf(" not the expected value %d\n",
475 so_timestamping_flags);
476 }
477
478 /* send packets forever every five seconds */
479 gettimeofday(&next, 0);
480 next.tv_sec = (next.tv_sec + 1) / 5 * 5;
481 next.tv_usec = 0;
482 while (1) {
483 struct timeval now;
484 struct timeval delta;
485 long delta_us;
486 int res;
487 fd_set readfs, errorfs;
488
489 gettimeofday(&now, 0);
490 delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 +
491 (long)(next.tv_usec - now.tv_usec);
492 if (delta_us > 0) {
493 /* continue waiting for timeout or data */
494 delta.tv_sec = delta_us / 1000000;
495 delta.tv_usec = delta_us % 1000000;
496
497 FD_ZERO(&readfs);
498 FD_ZERO(&errorfs);
499 FD_SET(sock, &readfs);
500 FD_SET(sock, &errorfs);
501 printf("%ld.%06ld: select %ldus\n",
502 (long)now.tv_sec, (long)now.tv_usec,
503 delta_us);
504 res = select(sock + 1, &readfs, 0, &errorfs, &delta);
505 gettimeofday(&now, 0);
506 printf("%ld.%06ld: select returned: %d, %s\n",
507 (long)now.tv_sec, (long)now.tv_usec,
508 res,
509 res < 0 ? strerror(errno) : "success");
510 if (res > 0) {
511 if (FD_ISSET(sock, &readfs))
512 printf("ready for reading\n");
513 if (FD_ISSET(sock, &errorfs))
514 printf("has error\n");
515 recvpacket(sock, 0,
516 siocgstamp,
517 siocgstampns);
518 recvpacket(sock, MSG_ERRQUEUE,
519 siocgstamp,
520 siocgstampns);
521 }
522 } else {
523 /* write one packet */
524 sendpacket(sock,
525 (struct sockaddr *)&addr,
526 sizeof(addr));
527 next.tv_sec += 5;
528 continue;
529 }
530 }
531
532 return 0;
533}