+
This directive can be used multiple times to specify multiple reference clocks.
+
-There are four drivers included in *chronyd*:
+There are five drivers included in *chronyd*:
+
*PPS*:::
Driver for the kernel PPS (pulse per second) API. The parameter is the path to
refclock PHC /dev/ptp2:extpps:pin=1 width 0.2 poll 2
----
+
+*RTC*:::
+Driver for using the Real Time Clock (RTC) as a reference clock. The parameter
+is the path to the RTC character device which should be used as a time source.
+This driver cannot be used together with the <<rtcfile,*rtcfile*>> or
+<<rtcsync,*rtcsync*>> directive. The driver supports the following option:
++
+*utc*::::
+Assume that RTC keeps Universal Coordinated Time (UTC) instead of local
+time.
+{blank}:::
++
+Examples:
++
+----
+refclock RTC /dev/rtc0:utc
+----
++
{blank}::
The *refclock* directive supports the following options:
+
switch to the Linux *hwclock* program.
+
Note that this setting is overridden by the <<hwclockfile,*hwclockfile*>> file
-and is not relevant for the <<rtcsync,*rtcsync*>> directive.
+and is not relevant for the <<rtcsync,*rtcsync*>> directive or when the RTC
+is used as reference clock.
[[rtcsync]]*rtcsync*::
The *rtcsync* directive enables a mode where the system time is periodically
--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Uwe Kleine-König, Pengutronix 2021
+ * Copyright (C) Ahmad Fatoum, Pengutronix 2024
+ *
+ * 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.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ RTC refclock driver.
+
+ */
+
+#include <linux/rtc.h>
+
+#include "config.h"
+
+#include "conf.h"
+#include "refclock.h"
+#include "local.h"
+
+#include "logging.h"
+#include "memory.h"
+#include "sched.h"
+#include "util.h"
+#include "rtc_linux.h"
+
+#ifdef FEAT_RTC
+
+struct refrtc_instance {
+ int fd;
+ int polling;
+ int utc;
+};
+
+static int refrtc_add_sample(RCL_Instance instance, struct timespec *now,
+ time_t rtc_sec, long rtc_nsec)
+{
+ struct timespec rtc_ts;
+ int status;
+
+ rtc_ts.tv_sec = rtc_sec;
+ rtc_ts.tv_nsec = rtc_nsec;
+
+ RCL_UpdateReachability(instance);
+
+ status = RCL_AddSample(instance, now, &rtc_ts, LEAP_Normal);
+
+ return status;
+}
+
+static void refrtc_read_after_uie(int rtcfd, int event, void *data)
+{
+ RCL_Instance instance = (RCL_Instance)data;
+ struct refrtc_instance *rtc = RCL_GetDriverData(instance);
+ struct timespec now;
+ time_t rtc_sec;
+ int status;
+
+ status = RTC_Linux_CheckInterrupt(rtcfd);
+ if (status < 0) {
+ SCH_RemoveFileHandler(rtcfd);
+ RTC_Linux_SwitchInterrupt(rtcfd, 0); /* Likely to raise error too, but just to be sure... */
+ close(rtcfd);
+ rtc->fd = -1;
+ return;
+ } else if (status == 0) {
+ /* Wait for the next interrupt, this one may be bogus */
+ return;
+ }
+
+ rtc_sec = RTC_Linux_ReadTimeAfterInterrupt(rtcfd, rtc->utc, NULL, &now);
+ if (rtc_sec == (time_t)-1)
+ return;
+
+ refrtc_add_sample(instance, &now, rtc_sec, 0);
+}
+
+static int refrtc_initialise(RCL_Instance instance)
+{
+ const char *options[] = {"utc", NULL};
+ struct refrtc_instance *rtc;
+ int rtcfd, status;
+ const char *path;
+
+ RCL_CheckDriverOptions(instance, options);
+
+ if (CNF_GetRtcSync() || CNF_GetRtcFile())
+ LOG_FATAL("RTC refclock cannot be used together with rtcsync or rtcfile");
+
+ path = RCL_GetDriverParameter(instance);
+
+ rtcfd = open(path, O_RDONLY);
+ if (rtcfd < 0)
+ LOG_FATAL("Could not open RTC device %s : %s", path, strerror(errno));
+
+ /* Close on exec */
+ UTI_FdSetCloexec(rtcfd);
+
+ rtc = MallocNew(struct refrtc_instance);
+ rtc->fd = rtcfd;
+ rtc->utc = RCL_GetDriverOption(instance, "utc") ? 1 : 0;
+
+ RCL_SetDriverData(instance, rtc);
+
+ /* Try to enable update interrupts */
+ status = RTC_Linux_SwitchInterrupt(rtcfd, 1);
+ if (status) {
+ SCH_AddFileHandler(rtcfd, SCH_FILE_INPUT, refrtc_read_after_uie, instance);
+ rtc->polling = 0;
+ } else {
+ LOG(LOGS_INFO, "Falling back to polling for %s", path);
+ rtc->polling = 1;
+ }
+
+ return 1;
+}
+
+static void refrtc_finalise(RCL_Instance instance)
+{
+ struct refrtc_instance *rtc;
+
+ rtc = RCL_GetDriverData(instance);
+
+ if (!rtc->polling) {
+ SCH_RemoveFileHandler(rtc->fd);
+ RTC_Linux_SwitchInterrupt(rtc->fd, 0);
+ }
+
+ close(rtc->fd);
+ Free(rtc);
+}
+
+static int refrtc_poll(RCL_Instance instance)
+{
+ struct refrtc_instance *rtc;
+ struct timespec now;
+ time_t rtc_sec;
+
+ rtc = RCL_GetDriverData(instance);
+
+ if (!rtc->polling)
+ return 0;
+
+ rtc_sec = RTC_Linux_ReadTimeNow(rtc->fd, rtc->utc, NULL, &now);
+ if (rtc_sec == (time_t)-1)
+ return 0;
+
+ /* As the rtc has a resolution of 1s, only add half a second */
+ return refrtc_add_sample(instance, &now, rtc_sec, 500000000);
+}
+
+RefclockDriver RCL_RTC_driver = {
+ refrtc_initialise,
+ refrtc_finalise,
+ refrtc_poll
+};
+
+#else
+
+RefclockDriver RCL_RTC_driver = { NULL, NULL, NULL };
+
+#endif