2023-06-26 11:44:57 -07:00
|
|
|
// SPDX-License-Identifier: BSD-2-Clause-Views
|
2019-07-17 18:48:50 -07:00
|
|
|
/*
|
2023-06-26 11:44:57 -07:00
|
|
|
* Copyright (c) 2019-2023 The Regents of the University of California
|
|
|
|
*/
|
2019-07-17 18:48:50 -07:00
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <linux/ptp_clock.h>
|
|
|
|
|
2019-07-22 18:06:17 -07:00
|
|
|
#include "timespec.h"
|
|
|
|
|
2019-07-17 18:48:50 -07:00
|
|
|
#ifndef CLOCK_INVALID
|
|
|
|
#define CLOCK_INVALID -1
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static clockid_t get_clockid(int fd)
|
|
|
|
{
|
|
|
|
#define CLOCKFD 3
|
|
|
|
#define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD)
|
|
|
|
|
|
|
|
return FD_TO_CLOCKID(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NSEC_PER_SEC 1000000000
|
|
|
|
|
|
|
|
static void usage(char *name)
|
|
|
|
{
|
|
|
|
fprintf(stderr,
|
|
|
|
"usage: %s [options]\n"
|
|
|
|
" -d name device to open\n"
|
|
|
|
" -s number start time (ns)\n"
|
|
|
|
" -p number period (ns)\n",
|
|
|
|
name);
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
int opt;
|
|
|
|
|
|
|
|
char *device = NULL;
|
|
|
|
int ptp_fd;
|
|
|
|
clockid_t clkid;
|
|
|
|
|
|
|
|
struct ptp_perout_request perout_request;
|
2019-07-22 18:06:17 -07:00
|
|
|
struct timespec ts_now;
|
|
|
|
struct timespec ts_start;
|
|
|
|
struct timespec ts_period;
|
2019-07-17 18:48:50 -07:00
|
|
|
|
|
|
|
int64_t start_nsec = 0;
|
|
|
|
int64_t period_nsec = 0;
|
|
|
|
|
|
|
|
name = strrchr(argv[0], '/');
|
|
|
|
name = name ? 1+name : argv[0];
|
|
|
|
|
|
|
|
while ((opt = getopt(argc, argv, "d:s:p:h?")) != EOF)
|
|
|
|
{
|
|
|
|
switch (opt)
|
|
|
|
{
|
|
|
|
case 'd':
|
|
|
|
device = optarg;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
start_nsec = atoll(optarg);
|
|
|
|
break;
|
|
|
|
case 'p':
|
|
|
|
period_nsec = atoll(optarg);
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
case '?':
|
|
|
|
usage(name);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
usage(name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!device)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "PTP device not specified\n");
|
|
|
|
usage(name);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ptp_fd = open(device, O_RDWR);
|
|
|
|
if (ptp_fd < 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to open %s: %s\n", device, strerror(errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
clkid = get_clockid(ptp_fd);
|
|
|
|
if (clkid == CLOCK_INVALID)
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Failed to read clock id\n");
|
|
|
|
close(ptp_fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (period_nsec > 0)
|
|
|
|
{
|
2019-07-22 18:06:17 -07:00
|
|
|
if (clock_gettime(clkid, &ts_now))
|
2019-07-17 18:48:50 -07:00
|
|
|
{
|
|
|
|
perror("Failed to read current time");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// normalize start
|
2019-07-22 18:06:17 -07:00
|
|
|
ts_start.tv_sec = start_nsec / NSEC_PER_SEC;
|
|
|
|
ts_start.tv_nsec = start_nsec - ts_start.tv_sec * NSEC_PER_SEC;
|
2019-07-17 18:48:50 -07:00
|
|
|
|
|
|
|
// normalize period
|
2019-07-22 18:06:17 -07:00
|
|
|
ts_period.tv_sec = period_nsec / NSEC_PER_SEC;
|
|
|
|
ts_period.tv_nsec = period_nsec - ts_period.tv_sec * NSEC_PER_SEC;
|
2019-07-17 18:48:50 -07:00
|
|
|
|
2019-07-22 18:06:17 -07:00
|
|
|
printf("time %ld.%09ld\n", ts_now.tv_sec, ts_now.tv_nsec);
|
|
|
|
printf("start %ld.%09ld\n", ts_start.tv_sec, ts_start.tv_nsec);
|
|
|
|
printf("period %ld.%09ld\n", ts_period.tv_sec, ts_period.tv_nsec);
|
2019-07-17 18:48:50 -07:00
|
|
|
|
2019-07-22 18:06:17 -07:00
|
|
|
if (timespec_lt(ts_start, ts_now))
|
2019-07-17 18:48:50 -07:00
|
|
|
{
|
|
|
|
// start time is in the past
|
|
|
|
|
|
|
|
// modulo start with period
|
2019-07-22 18:06:17 -07:00
|
|
|
ts_start = timespec_mod(ts_start, ts_period);
|
2019-07-17 18:48:50 -07:00
|
|
|
|
|
|
|
// align time with period
|
2019-07-22 18:06:17 -07:00
|
|
|
struct timespec ts_aligned = timespec_sub(ts_now, timespec_mod(ts_now, ts_period));
|
|
|
|
|
|
|
|
// add aligned time
|
|
|
|
ts_start = timespec_add(ts_start, ts_aligned);
|
2019-07-17 18:48:50 -07:00
|
|
|
}
|
|
|
|
|
2019-07-22 18:06:17 -07:00
|
|
|
printf("time %ld.%09ld\n", ts_now.tv_sec, ts_now.tv_nsec);
|
|
|
|
printf("start %ld.%09ld\n", ts_start.tv_sec, ts_start.tv_nsec);
|
|
|
|
printf("period %ld.%09ld\n", ts_period.tv_sec, ts_period.tv_nsec);
|
2019-07-17 18:48:50 -07:00
|
|
|
|
|
|
|
memset(&perout_request, 0, sizeof(perout_request));
|
|
|
|
perout_request.index = 0;
|
2019-07-22 18:06:17 -07:00
|
|
|
perout_request.start.sec = ts_start.tv_sec;
|
|
|
|
perout_request.start.nsec = ts_start.tv_nsec;
|
|
|
|
perout_request.period.sec = ts_period.tv_sec;
|
|
|
|
perout_request.period.nsec = ts_period.tv_nsec;
|
2019-07-17 18:48:50 -07:00
|
|
|
|
|
|
|
if (ioctl(ptp_fd, PTP_PEROUT_REQUEST, &perout_request))
|
|
|
|
{
|
|
|
|
perror("PTP_PEROUT_REQUEST ioctl failed");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printf("PTP_PEROUT_REQUEST ioctl OK\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
close(ptp_fd);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|