1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
/**
* @file phc.c
* @note Copyright (C) 2011 Richard Cochran <richardcochran@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
#include <fcntl.h>
#include <linux/ptp_clock.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "phc.h"
/*
* On 32 bit platforms, the PHC driver's maximum adjustment (type
* 'int' in units of ppb) can overflow the timex.freq field (type
* 'long'). So in this case we clamp the maximum to the largest
* possible adjustment that fits into a 32 bit long.
*/
#define BITS_PER_LONG (sizeof(long)*8)
#define MAX_PPB_32 32767999 /* 2^31 - 1 / 65.536 */
static int phc_get_caps(clockid_t clkid, struct ptp_clock_caps *caps);
clockid_t phc_open(const char *phc)
{
clockid_t clkid;
struct timespec ts;
struct timex tx;
int fd;
memset(&tx, 0, sizeof(tx));
fd = open(phc, O_RDWR);
if (fd < 0)
return CLOCK_INVALID;
clkid = FD_TO_CLOCKID(fd);
/* check if clkid is valid */
if (clock_gettime(clkid, &ts)) {
close(fd);
return CLOCK_INVALID;
}
if (clock_adjtime(clkid, &tx)) {
close(fd);
return CLOCK_INVALID;
}
return clkid;
}
void phc_close(clockid_t clkid)
{
if (clkid == CLOCK_INVALID)
return;
close(CLOCKID_TO_FD(clkid));
}
static int phc_get_caps(clockid_t clkid, struct ptp_clock_caps *caps)
{
int fd = CLOCKID_TO_FD(clkid), err;
err = ioctl(fd, PTP_CLOCK_GETCAPS, caps);
if (err)
perror("PTP_CLOCK_GETCAPS");
return err;
}
int phc_max_adj(clockid_t clkid)
{
int max;
struct ptp_clock_caps caps;
if (phc_get_caps(clkid, &caps))
return 0;
max = caps.max_adj;
if (BITS_PER_LONG == 32 && max > MAX_PPB_32)
max = MAX_PPB_32;
return max;
}
int phc_number_pins(clockid_t clkid)
{
struct ptp_clock_caps caps;
if (phc_get_caps(clkid, &caps)) {
return 0;
}
return caps.n_pins;
}
int phc_pin_setfunc(clockid_t clkid, struct ptp_pin_desc *desc)
{
int err = ioctl(CLOCKID_TO_FD(clkid), PTP_PIN_SETFUNC2, desc);
if (err) {
fprintf(stderr, PTP_PIN_SETFUNC_FAILED "\n");
}
return err;
}
int phc_has_pps(clockid_t clkid)
{
struct ptp_clock_caps caps;
if (phc_get_caps(clkid, &caps))
return 0;
return caps.pps;
}
int phc_has_writephase(clockid_t clkid)
{
struct ptp_clock_caps caps;
if (phc_get_caps(clkid, &caps)) {
return 0;
}
return caps.adjust_phase;
}
|