| /* |
| chronyd/chronyc - Programs for keeping computer clocks accurate. |
| |
| ********************************************************************** |
| * Copyright (C) Miroslav Lichvar 2009 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * 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. |
| * |
| ********************************************************************** |
| |
| ======================================================================= |
| |
| PPSAPI refclock driver. |
| |
| */ |
| |
| #include "config.h" |
| |
| #include "refclock.h" |
| |
| #if FEAT_PPS |
| |
| #if defined(HAVE_SYS_TIMEPPS_H) |
| #include <sys/timepps.h> |
| #elif defined(HAVE_TIMEPPS_H) |
| #include <timepps.h> |
| #endif |
| |
| #include "logging.h" |
| #include "memory.h" |
| #include "util.h" |
| |
| struct pps_instance { |
| pps_handle_t handle; |
| pps_seq_t last_seq; |
| int edge_clear; |
| }; |
| |
| static int pps_initialise(RCL_Instance instance) { |
| const char *options[] = {"clear", NULL}; |
| pps_handle_t handle; |
| pps_params_t params; |
| struct pps_instance *pps; |
| int fd, edge_clear, mode; |
| char *path; |
| |
| RCL_CheckDriverOptions(instance, options); |
| |
| path = RCL_GetDriverParameter(instance); |
| edge_clear = RCL_GetDriverOption(instance, "clear") ? 1 : 0; |
| |
| fd = open(path, O_RDWR); |
| if (fd < 0) |
| LOG_FATAL("Could not open %s : %s", path, strerror(errno)); |
| |
| UTI_FdSetCloexec(fd); |
| |
| if (time_pps_create(fd, &handle) < 0) |
| LOG_FATAL("time_pps_create() failed on %s : %s", path, strerror(errno)); |
| |
| if (time_pps_getcap(handle, &mode) < 0) |
| LOG_FATAL("time_pps_getcap() failed on %s : %s", path, strerror(errno)); |
| |
| if (time_pps_getparams(handle, ¶ms) < 0) |
| LOG_FATAL("time_pps_getparams() failed on %s : %s", path, strerror(errno)); |
| |
| if (!edge_clear) { |
| if (!(mode & PPS_CAPTUREASSERT)) |
| LOG_FATAL("CAPTUREASSERT not supported on %s", path); |
| |
| params.mode |= PPS_CAPTUREASSERT; |
| params.mode &= ~PPS_CAPTURECLEAR; |
| } else { |
| if (!(mode & PPS_CAPTURECLEAR)) |
| LOG_FATAL("CAPTURECLEAR not supported on %s", path); |
| |
| params.mode |= PPS_CAPTURECLEAR; |
| params.mode &= ~PPS_CAPTUREASSERT; |
| } |
| |
| if (time_pps_setparams(handle, ¶ms) < 0) |
| LOG_FATAL("time_pps_setparams() failed on %s : %s", path, strerror(errno)); |
| |
| pps = MallocNew(struct pps_instance); |
| pps->handle = handle; |
| pps->last_seq = 0; |
| pps->edge_clear = edge_clear; |
| |
| RCL_SetDriverData(instance, pps); |
| return 1; |
| } |
| |
| static void pps_finalise(RCL_Instance instance) |
| { |
| struct pps_instance *pps; |
| |
| pps = (struct pps_instance *)RCL_GetDriverData(instance); |
| time_pps_destroy(pps->handle); |
| Free(pps); |
| } |
| |
| static int pps_poll(RCL_Instance instance) |
| { |
| struct pps_instance *pps; |
| struct timespec ts; |
| pps_info_t pps_info; |
| pps_seq_t seq; |
| |
| pps = (struct pps_instance *)RCL_GetDriverData(instance); |
| |
| ts.tv_sec = 0; |
| ts.tv_nsec = 0; |
| |
| if (time_pps_fetch(pps->handle, PPS_TSFMT_TSPEC, &pps_info, &ts) < 0) { |
| LOG(LOGS_ERR, "time_pps_fetch() failed : %s", strerror(errno)); |
| return 0; |
| } |
| |
| if (!pps->edge_clear) { |
| seq = pps_info.assert_sequence; |
| ts = pps_info.assert_timestamp; |
| } else { |
| seq = pps_info.clear_sequence; |
| ts = pps_info.clear_timestamp; |
| } |
| |
| if (seq == pps->last_seq || UTI_IsZeroTimespec(&ts)) { |
| DEBUG_LOG("PPS sample ignored seq=%lu ts=%s", |
| (unsigned long)seq, UTI_TimespecToString(&ts)); |
| return 0; |
| } |
| |
| pps->last_seq = seq; |
| |
| return RCL_AddPulse(instance, &ts, 1.0e-9 * ts.tv_nsec); |
| } |
| |
| RefclockDriver RCL_PPS_driver = { |
| pps_initialise, |
| pps_finalise, |
| pps_poll |
| }; |
| |
| #else |
| |
| RefclockDriver RCL_PPS_driver = { NULL, NULL, NULL }; |
| |
| #endif |