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