2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* rtc.c - Use /dev/rtc for clock access
|
|
|
|
*/
|
2011-07-24 12:58:17 -05:00
|
|
|
#include <asm/ioctl.h>
|
2006-12-06 17:26:28 -06:00
|
|
|
#include <errno.h>
|
2011-07-24 12:58:17 -05:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
2006-12-06 17:25:39 -06:00
|
|
|
#include <sys/ioctl.h>
|
2011-07-24 12:58:17 -05:00
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2019-01-07 15:34:11 -06:00
|
|
|
#include "monotonic.h"
|
2006-12-06 17:25:39 -06:00
|
|
|
#include "nls.h"
|
|
|
|
|
2012-05-31 04:15:07 -05:00
|
|
|
#include "hwclock.h"
|
|
|
|
|
2006-12-06 17:25:39 -06:00
|
|
|
/*
|
|
|
|
* Get defines for rtc stuff.
|
|
|
|
*
|
2011-07-24 10:35:43 -05:00
|
|
|
* Getting the rtc defines is nontrivial. The obvious way is by including
|
|
|
|
* <linux/mc146818rtc.h> but that again includes <asm/io.h> which again
|
|
|
|
* includes ... and on sparc and alpha this gives compilation errors for
|
|
|
|
* many kernel versions. So, we give the defines ourselves here. Moreover,
|
|
|
|
* some Sparc person decided to be incompatible, and used a struct rtc_time
|
|
|
|
* different from that used in mc146818rtc.h.
|
2006-12-06 17:25:39 -06:00
|
|
|
*/
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* On Sparcs, there is a <asm/rtc.h> that defines different ioctls (that are
|
|
|
|
* required on my machine). However, this include file does not exist on
|
|
|
|
* other architectures.
|
|
|
|
*/
|
2006-12-06 17:25:39 -06:00
|
|
|
/* One might do:
|
|
|
|
#ifdef __sparc__
|
2011-07-24 10:35:43 -05:00
|
|
|
# include <asm/rtc.h>
|
2006-12-06 17:25:39 -06:00
|
|
|
#endif
|
|
|
|
*/
|
2016-07-24 16:33:01 -05:00
|
|
|
#ifdef __sparc__
|
2006-12-06 17:25:39 -06:00
|
|
|
/* The following is roughly equivalent */
|
|
|
|
struct sparc_rtc_time
|
|
|
|
{
|
2011-07-24 10:35:43 -05:00
|
|
|
int sec; /* Seconds 0-59 */
|
|
|
|
int min; /* Minutes 0-59 */
|
|
|
|
int hour; /* Hour 0-23 */
|
|
|
|
int dow; /* Day of the week 1-7 */
|
|
|
|
int dom; /* Day of the month 1-31 */
|
|
|
|
int month; /* Month of year 1-12 */
|
|
|
|
int year; /* Year 0-99 */
|
2006-12-06 17:25:39 -06:00
|
|
|
};
|
|
|
|
#define RTCGET _IOR('p', 20, struct sparc_rtc_time)
|
|
|
|
#define RTCSET _IOW('p', 21, struct sparc_rtc_time)
|
2016-07-24 16:33:01 -05:00
|
|
|
#endif
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* struct rtc_time is present since 1.3.99.
|
|
|
|
* Earlier (since 1.3.89), a struct tm was used.
|
|
|
|
*/
|
2006-12-06 17:25:39 -06:00
|
|
|
struct linux_rtc_time {
|
2011-07-24 10:35:43 -05:00
|
|
|
int tm_sec;
|
|
|
|
int tm_min;
|
|
|
|
int tm_hour;
|
|
|
|
int tm_mday;
|
|
|
|
int tm_mon;
|
|
|
|
int tm_year;
|
|
|
|
int tm_wday;
|
|
|
|
int tm_yday;
|
|
|
|
int tm_isdst;
|
2006-12-06 17:25:39 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
/* RTC_RD_TIME etc have this definition since 1.99.9 (pre2.0-9) */
|
|
|
|
#ifndef RTC_RD_TIME
|
2011-07-24 10:35:43 -05:00
|
|
|
# define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time)
|
|
|
|
# define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time)
|
|
|
|
# define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
|
|
|
|
# define RTC_UIE_OFF _IO('p', 0x04) /* Update int. enable off */
|
2006-12-06 17:25:39 -06:00
|
|
|
#endif
|
2011-07-24 10:35:43 -05:00
|
|
|
|
2006-12-06 17:25:39 -06:00
|
|
|
/* RTC_EPOCH_READ and RTC_EPOCH_SET are present since 2.0.34 and 2.1.89 */
|
|
|
|
#ifndef RTC_EPOCH_READ
|
2011-07-24 10:35:43 -05:00
|
|
|
# define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
|
|
|
|
# define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
|
2006-12-06 17:25:39 -06:00
|
|
|
#endif
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* /dev/rtc is conventionally chardev 10/135
|
2007-03-19 04:14:13 -05:00
|
|
|
* ia64 uses /dev/efirtc, chardev 10/136
|
|
|
|
* devfs (obsolete) used /dev/misc/... for miscdev
|
|
|
|
* new RTC framework + udev uses dynamic major and /dev/rtc0.../dev/rtcN
|
|
|
|
* ... so we need an overridable default
|
|
|
|
*/
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2007-03-19 04:14:13 -05:00
|
|
|
/* default or user defined dev (by hwclock --rtc=<path>) */
|
2016-07-17 11:10:48 -05:00
|
|
|
static const char *rtc_dev_name;
|
2008-08-18 07:08:57 -05:00
|
|
|
static int rtc_dev_fd = -1;
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
static void close_rtc(void)
|
|
|
|
{
|
2008-08-18 07:08:57 -05:00
|
|
|
if (rtc_dev_fd != -1)
|
|
|
|
close(rtc_dev_fd);
|
|
|
|
rtc_dev_fd = -1;
|
|
|
|
}
|
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
static int open_rtc(const struct hwclock_control *ctl)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
2016-07-17 11:10:48 -05:00
|
|
|
static const char *fls[] = {
|
2007-03-19 04:14:13 -05:00
|
|
|
#ifdef __ia64__
|
|
|
|
"/dev/efirtc",
|
|
|
|
"/dev/misc/efirtc",
|
|
|
|
#endif
|
|
|
|
"/dev/rtc0",
|
2017-03-12 19:26:37 -05:00
|
|
|
"/dev/rtc",
|
2016-07-17 11:10:48 -05:00
|
|
|
"/dev/misc/rtc"
|
2007-03-19 04:14:13 -05:00
|
|
|
};
|
2016-07-17 11:10:48 -05:00
|
|
|
size_t i;
|
2007-09-20 04:11:18 -05:00
|
|
|
|
2008-08-18 07:08:57 -05:00
|
|
|
if (rtc_dev_fd != -1)
|
|
|
|
return rtc_dev_fd;
|
|
|
|
|
2007-09-20 04:11:18 -05:00
|
|
|
/* --rtc option has been given */
|
2016-07-16 10:45:07 -05:00
|
|
|
if (ctl->rtc_dev_name) {
|
|
|
|
rtc_dev_name = ctl->rtc_dev_name;
|
2008-08-18 07:08:57 -05:00
|
|
|
rtc_dev_fd = open(rtc_dev_name, O_RDONLY);
|
2016-07-16 10:45:07 -05:00
|
|
|
} else {
|
2016-07-17 11:10:48 -05:00
|
|
|
for (i = 0; i < ARRAY_SIZE(fls); i++) {
|
2017-12-24 14:37:36 -06:00
|
|
|
if (ctl->verbose)
|
2016-07-17 11:10:48 -05:00
|
|
|
printf(_("Trying to open: %s\n"), fls[i]);
|
|
|
|
rtc_dev_fd = open(fls[i], O_RDONLY);
|
2008-08-18 07:08:57 -05:00
|
|
|
|
2019-10-07 05:57:43 -05:00
|
|
|
if (rtc_dev_fd < 0) {
|
|
|
|
if (errno == ENOENT || errno == ENODEV)
|
|
|
|
continue;
|
|
|
|
if (ctl->verbose)
|
|
|
|
warn(_("cannot open %s"), fls[i]);
|
|
|
|
}
|
2016-07-17 11:10:48 -05:00
|
|
|
rtc_dev_name = fls[i];
|
2008-08-18 07:08:57 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (rtc_dev_fd < 0)
|
|
|
|
rtc_dev_name = *fls; /* default for error messages */
|
2007-09-20 04:11:18 -05:00
|
|
|
}
|
2016-07-26 08:34:30 -05:00
|
|
|
if (rtc_dev_fd != -1)
|
2008-08-18 07:08:57 -05:00
|
|
|
atexit(close_rtc);
|
|
|
|
return rtc_dev_fd;
|
2006-12-06 17:25:58 -06:00
|
|
|
}
|
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
static int open_rtc_or_exit(const struct hwclock_control *ctl)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
2016-07-16 10:45:07 -05:00
|
|
|
int rtc_fd = open_rtc(ctl);
|
2006-12-06 17:25:58 -06:00
|
|
|
|
|
|
|
if (rtc_fd < 0) {
|
2016-07-17 11:10:48 -05:00
|
|
|
warn(_("cannot open rtc device"));
|
2017-08-27 19:26:41 -05:00
|
|
|
hwclock_exit(ctl, EXIT_FAILURE);
|
2006-12-06 17:25:48 -06:00
|
|
|
}
|
|
|
|
return rtc_fd;
|
|
|
|
}
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
static int do_rtc_read_ioctl(int rtc_fd, struct tm *tm)
|
|
|
|
{
|
2006-12-06 17:25:48 -06:00
|
|
|
int rc = -1;
|
|
|
|
char *ioctlname;
|
2006-12-06 17:25:39 -06:00
|
|
|
#ifdef __sparc__
|
2006-12-06 17:25:48 -06:00
|
|
|
/* some but not all sparcs use a different ioctl and struct */
|
|
|
|
struct sparc_rtc_time stm;
|
2016-07-26 04:52:15 -05:00
|
|
|
#endif
|
|
|
|
|
|
|
|
ioctlname = "RTC_RD_TIME";
|
|
|
|
rc = ioctl(rtc_fd, RTC_RD_TIME, tm);
|
2006-12-06 17:25:48 -06:00
|
|
|
|
2016-07-26 04:52:15 -05:00
|
|
|
#ifdef __sparc__
|
|
|
|
if (rc == -1) { /* sparc sbus */
|
|
|
|
ioctlname = "RTCGET";
|
|
|
|
rc = ioctl(rtc_fd, RTCGET, &stm);
|
|
|
|
if (rc == 0) {
|
|
|
|
tm->tm_sec = stm.sec;
|
|
|
|
tm->tm_min = stm.min;
|
|
|
|
tm->tm_hour = stm.hour;
|
|
|
|
tm->tm_mday = stm.dom;
|
|
|
|
tm->tm_mon = stm.month - 1;
|
|
|
|
tm->tm_year = stm.year - 1900;
|
|
|
|
tm->tm_wday = stm.dow - 1;
|
|
|
|
tm->tm_yday = -1; /* day in the year */
|
|
|
|
}
|
2006-12-06 17:25:48 -06:00
|
|
|
}
|
2006-12-06 17:25:39 -06:00
|
|
|
#endif
|
2016-07-26 04:52:15 -05:00
|
|
|
|
2006-12-06 17:25:48 -06:00
|
|
|
if (rc == -1) {
|
2011-07-24 13:30:29 -05:00
|
|
|
warn(_("ioctl(%s) to %s to read the time failed"),
|
|
|
|
ioctlname, rtc_dev_name);
|
2008-06-17 06:12:12 -05:00
|
|
|
return -1;
|
2006-12-06 17:25:48 -06:00
|
|
|
}
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
tm->tm_isdst = -1; /* don't know whether it's dst */
|
2006-12-06 17:25:48 -06:00
|
|
|
return 0;
|
|
|
|
}
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
2017-07-24 11:11:28 -05:00
|
|
|
* Wait for the top of a clock tick by reading /dev/rtc in a busy loop
|
|
|
|
* until we see it. This function is used for rtc drivers without ioctl
|
|
|
|
* interrupts. This is typical on an Alpha, where the Hardware Clock
|
|
|
|
* interrupts are used by the kernel for the system clock, so aren't at
|
|
|
|
* the user's disposal.
|
2011-07-24 10:35:43 -05:00
|
|
|
*/
|
2016-07-16 10:45:07 -05:00
|
|
|
static int busywait_for_rtc_clock_tick(const struct hwclock_control *ctl,
|
|
|
|
const int rtc_fd)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
|
|
|
struct tm start_time;
|
|
|
|
/* The time when we were called (and started waiting) */
|
|
|
|
struct tm nowtime;
|
|
|
|
int rc;
|
|
|
|
struct timeval begin, now;
|
|
|
|
|
2017-12-24 14:37:36 -06:00
|
|
|
if (ctl->verbose) {
|
2017-07-24 11:11:28 -05:00
|
|
|
printf("ioctl(%d, RTC_UIE_ON, 0): %s\n",
|
|
|
|
rtc_fd, strerror(errno));
|
2011-07-24 10:35:43 -05:00
|
|
|
printf(_("Waiting in loop for time from %s to change\n"),
|
|
|
|
rtc_dev_name);
|
2017-07-24 11:11:28 -05:00
|
|
|
}
|
2011-07-24 10:35:43 -05:00
|
|
|
|
2017-07-31 10:57:01 -05:00
|
|
|
if (do_rtc_read_ioctl(rtc_fd, &start_time))
|
|
|
|
return 1;
|
2011-07-24 10:35:43 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Wait for change. Should be within a second, but in case
|
|
|
|
* something weird happens, we have a time limit (1.5s) on this loop
|
|
|
|
* to reduce the impact of this failure.
|
|
|
|
*/
|
2019-01-07 15:34:11 -06:00
|
|
|
gettime_monotonic(&begin);
|
2011-07-24 10:35:43 -05:00
|
|
|
do {
|
|
|
|
rc = do_rtc_read_ioctl(rtc_fd, &nowtime);
|
|
|
|
if (rc || start_time.tm_sec != nowtime.tm_sec)
|
|
|
|
break;
|
2019-01-07 15:34:11 -06:00
|
|
|
gettime_monotonic(&now);
|
2011-07-24 10:35:43 -05:00
|
|
|
if (time_diff(now, begin) > 1.5) {
|
2011-07-24 13:30:29 -05:00
|
|
|
warnx(_("Timed out waiting for time change."));
|
2017-07-31 10:57:01 -05:00
|
|
|
return 1;
|
2011-07-24 10:35:43 -05:00
|
|
|
}
|
|
|
|
} while (1);
|
|
|
|
|
|
|
|
if (rc)
|
2017-07-31 10:57:01 -05:00
|
|
|
return 1;
|
|
|
|
return 0;
|
2006-12-06 17:25:39 -06:00
|
|
|
}
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* Same as synchronize_to_clock_tick(), but just for /dev/rtc.
|
|
|
|
*/
|
2016-07-16 10:45:07 -05:00
|
|
|
static int synchronize_to_clock_tick_rtc(const struct hwclock_control *ctl)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
|
|
|
int rtc_fd; /* File descriptor of /dev/rtc */
|
2016-07-26 02:54:00 -05:00
|
|
|
int ret = 1;
|
2011-07-24 10:35:43 -05:00
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
rtc_fd = open_rtc(ctl);
|
2011-07-24 10:35:43 -05:00
|
|
|
if (rtc_fd == -1) {
|
2016-07-17 11:10:48 -05:00
|
|
|
warn(_("cannot open rtc device"));
|
2016-07-26 02:54:00 -05:00
|
|
|
return ret;
|
2011-07-24 10:35:43 -05:00
|
|
|
} else {
|
|
|
|
/* Turn on update interrupts (one per second) */
|
2018-08-03 06:08:21 -05:00
|
|
|
int rc = ioctl(rtc_fd, RTC_UIE_ON, 0);
|
|
|
|
|
2016-07-26 02:54:00 -05:00
|
|
|
if (rc != -1) {
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* Just reading rtc_fd fails on broken hardware: no
|
|
|
|
* update interrupt comes and a bootscript with a
|
|
|
|
* hwclock call hangs
|
|
|
|
*/
|
|
|
|
fd_set rfds;
|
|
|
|
struct timeval tv;
|
|
|
|
|
|
|
|
/*
|
2012-09-21 05:05:12 -05:00
|
|
|
* Wait up to ten seconds for the next update
|
2011-07-24 10:35:43 -05:00
|
|
|
* interrupt
|
|
|
|
*/
|
|
|
|
FD_ZERO(&rfds);
|
|
|
|
FD_SET(rtc_fd, &rfds);
|
2012-09-21 05:05:12 -05:00
|
|
|
tv.tv_sec = 10;
|
2011-07-24 10:35:43 -05:00
|
|
|
tv.tv_usec = 0;
|
|
|
|
rc = select(rtc_fd + 1, &rfds, NULL, NULL, &tv);
|
2016-07-26 02:54:00 -05:00
|
|
|
if (0 < rc)
|
|
|
|
ret = 0;
|
2015-04-21 15:43:41 -05:00
|
|
|
else if (rc == 0) {
|
2017-07-31 08:20:58 -05:00
|
|
|
warnx(_("select() to %s to wait for clock tick timed out"),
|
|
|
|
rtc_dev_name);
|
2015-04-21 15:43:41 -05:00
|
|
|
} else
|
2016-07-26 02:54:00 -05:00
|
|
|
warn(_("select() to %s to wait for clock tick failed"),
|
|
|
|
rtc_dev_name);
|
2011-07-24 10:35:43 -05:00
|
|
|
/* Turn off update interrupts */
|
|
|
|
rc = ioctl(rtc_fd, RTC_UIE_OFF, 0);
|
|
|
|
if (rc == -1)
|
2013-03-21 02:56:15 -05:00
|
|
|
warn(_("ioctl() to %s to turn off update interrupts failed"),
|
2011-07-24 13:30:29 -05:00
|
|
|
rtc_dev_name);
|
2017-11-23 03:30:27 -06:00
|
|
|
} else if (errno == ENOTTY || errno == EINVAL) {
|
2017-07-24 11:11:28 -05:00
|
|
|
/* rtc ioctl interrupts are unimplemented */
|
2016-07-26 02:54:00 -05:00
|
|
|
ret = busywait_for_rtc_clock_tick(ctl, rtc_fd);
|
|
|
|
} else
|
2017-07-24 11:11:28 -05:00
|
|
|
warn(_("ioctl(%d, RTC_UIE_ON, 0) to %s failed"),
|
|
|
|
rtc_fd, rtc_dev_name);
|
2011-07-24 10:35:43 -05:00
|
|
|
}
|
|
|
|
return ret;
|
2006-12-06 17:25:39 -06:00
|
|
|
}
|
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
static int read_hardware_clock_rtc(const struct hwclock_control *ctl,
|
|
|
|
struct tm *tm)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
2008-08-13 04:47:10 -05:00
|
|
|
int rtc_fd, rc;
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
rtc_fd = open_rtc_or_exit(ctl);
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2006-12-06 17:25:48 -06:00
|
|
|
/* Read the RTC time/date, return answer via tm */
|
2008-08-13 04:47:10 -05:00
|
|
|
rc = do_rtc_read_ioctl(rtc_fd, tm);
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2008-08-13 04:47:10 -05:00
|
|
|
return rc;
|
2006-12-06 17:25:39 -06:00
|
|
|
}
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* Set the Hardware Clock to the broken down time <new_broken_time>. Use
|
|
|
|
* ioctls to "rtc" device /dev/rtc.
|
|
|
|
*/
|
2016-07-16 10:45:07 -05:00
|
|
|
static int set_hardware_clock_rtc(const struct hwclock_control *ctl,
|
|
|
|
const struct tm *new_broken_time)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
2006-12-06 17:25:48 -06:00
|
|
|
int rc = -1;
|
|
|
|
int rtc_fd;
|
|
|
|
char *ioctlname;
|
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
rtc_fd = open_rtc_or_exit(ctl);
|
2006-12-06 17:25:58 -06:00
|
|
|
|
2016-07-26 04:52:15 -05:00
|
|
|
ioctlname = "RTC_SET_TIME";
|
|
|
|
rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time);
|
|
|
|
|
2006-12-06 17:25:39 -06:00
|
|
|
#ifdef __sparc__
|
2016-07-26 04:52:15 -05:00
|
|
|
if (rc == -1) { /* sparc sbus */
|
2006-12-06 17:25:48 -06:00
|
|
|
struct sparc_rtc_time stm;
|
|
|
|
|
|
|
|
stm.sec = new_broken_time->tm_sec;
|
|
|
|
stm.min = new_broken_time->tm_min;
|
|
|
|
stm.hour = new_broken_time->tm_hour;
|
|
|
|
stm.dom = new_broken_time->tm_mday;
|
|
|
|
stm.month = new_broken_time->tm_mon + 1;
|
|
|
|
stm.year = new_broken_time->tm_year + 1900;
|
|
|
|
stm.dow = new_broken_time->tm_wday + 1;
|
|
|
|
|
|
|
|
ioctlname = "RTCSET";
|
|
|
|
rc = ioctl(rtc_fd, RTCSET, &stm);
|
|
|
|
}
|
2006-12-06 17:25:39 -06:00
|
|
|
#endif
|
2006-12-06 17:25:48 -06:00
|
|
|
|
|
|
|
if (rc == -1) {
|
2016-12-31 15:29:27 -06:00
|
|
|
warn(_("ioctl(%s) to %s to set the time failed"),
|
2011-07-24 13:30:29 -05:00
|
|
|
ioctlname, rtc_dev_name);
|
2017-08-27 19:26:41 -05:00
|
|
|
hwclock_exit(ctl, EXIT_FAILURE);
|
2006-12-06 17:25:48 -06:00
|
|
|
}
|
|
|
|
|
2017-12-24 14:37:36 -06:00
|
|
|
if (ctl->verbose)
|
2006-12-06 17:25:48 -06:00
|
|
|
printf(_("ioctl(%s) was successful.\n"), ioctlname);
|
|
|
|
|
|
|
|
return 0;
|
2006-12-06 17:25:39 -06:00
|
|
|
}
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
static int get_permissions_rtc(void)
|
|
|
|
{
|
2006-12-06 17:25:39 -06:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-07-18 06:59:15 -05:00
|
|
|
static const char *get_device_path(void)
|
|
|
|
{
|
|
|
|
return rtc_dev_name;
|
|
|
|
}
|
|
|
|
|
2016-07-24 16:33:01 -05:00
|
|
|
static struct clock_ops rtc_interface = {
|
2016-07-16 16:15:54 -05:00
|
|
|
N_("Using the rtc interface to the clock."),
|
2006-12-06 17:25:39 -06:00
|
|
|
get_permissions_rtc,
|
|
|
|
read_hardware_clock_rtc,
|
|
|
|
set_hardware_clock_rtc,
|
|
|
|
synchronize_to_clock_tick_rtc,
|
2018-07-18 06:59:15 -05:00
|
|
|
get_device_path,
|
2006-12-06 17:25:39 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
/* return &rtc if /dev/rtc can be opened, NULL otherwise */
|
2016-07-16 10:45:07 -05:00
|
|
|
struct clock_ops *probe_for_rtc_clock(const struct hwclock_control *ctl)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
2016-07-24 16:33:01 -05:00
|
|
|
const int rtc_fd = open_rtc(ctl);
|
|
|
|
|
|
|
|
if (rtc_fd < 0)
|
|
|
|
return NULL;
|
|
|
|
return &rtc_interface;
|
2006-12-06 17:25:39 -06:00
|
|
|
}
|
|
|
|
|
2016-07-24 16:33:01 -05:00
|
|
|
#ifdef __alpha__
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* Get the Hardware Clock epoch setting from the kernel.
|
|
|
|
*/
|
2017-03-26 10:56:42 -05:00
|
|
|
int get_epoch_rtc(const struct hwclock_control *ctl, unsigned long *epoch_p)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
|
|
|
int rtc_fd;
|
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
rtc_fd = open_rtc(ctl);
|
2011-07-24 10:35:43 -05:00
|
|
|
if (rtc_fd < 0) {
|
2017-07-23 13:25:55 -05:00
|
|
|
warn(_("cannot open %s"), rtc_dev_name);
|
2011-07-24 10:35:43 -05:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) {
|
2017-07-15 14:44:20 -05:00
|
|
|
warn(_("ioctl(%d, RTC_EPOCH_READ, epoch_p) to %s failed"),
|
|
|
|
rtc_fd, rtc_dev_name);
|
2011-07-24 10:35:43 -05:00
|
|
|
return 1;
|
|
|
|
}
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2017-12-24 14:37:36 -06:00
|
|
|
if (ctl->verbose)
|
2017-07-15 14:44:20 -05:00
|
|
|
printf(_("ioctl(%d, RTC_EPOCH_READ, epoch_p) to %s succeeded.\n"),
|
|
|
|
rtc_fd, rtc_dev_name);
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
return 0;
|
2006-12-06 17:25:39 -06:00
|
|
|
}
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
/*
|
|
|
|
* Set the Hardware Clock epoch in the kernel.
|
|
|
|
*/
|
2016-07-16 10:45:07 -05:00
|
|
|
int set_epoch_rtc(const struct hwclock_control *ctl)
|
2011-07-24 10:35:43 -05:00
|
|
|
{
|
|
|
|
int rtc_fd;
|
2017-07-14 14:51:47 -05:00
|
|
|
unsigned long epoch;
|
2011-07-24 10:35:43 -05:00
|
|
|
|
2017-07-14 14:51:47 -05:00
|
|
|
epoch = strtoul(ctl->epoch_option, NULL, 10);
|
|
|
|
|
|
|
|
/* There were no RTC clocks before 1900. */
|
|
|
|
if (epoch < 1900 || epoch == ULONG_MAX) {
|
|
|
|
warnx(_("invalid epoch '%s'."), ctl->epoch_option);
|
2011-07-24 10:35:43 -05:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-07-16 10:45:07 -05:00
|
|
|
rtc_fd = open_rtc(ctl);
|
2011-07-24 10:35:43 -05:00
|
|
|
if (rtc_fd < 0) {
|
2017-07-23 13:25:55 -05:00
|
|
|
warn(_("cannot open %s"), rtc_dev_name);
|
2011-07-24 10:35:43 -05:00
|
|
|
return 1;
|
|
|
|
}
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2017-07-14 14:51:47 -05:00
|
|
|
if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) {
|
2017-07-15 14:44:20 -05:00
|
|
|
warn(_("ioctl(%d, RTC_EPOCH_SET, %lu) to %s failed"),
|
|
|
|
rtc_fd, epoch, rtc_dev_name);
|
2011-07-24 10:35:43 -05:00
|
|
|
return 1;
|
|
|
|
}
|
2006-12-06 17:25:39 -06:00
|
|
|
|
2017-12-24 14:37:36 -06:00
|
|
|
if (ctl->verbose)
|
2017-07-15 14:44:20 -05:00
|
|
|
printf(_("ioctl(%d, RTC_EPOCH_SET, %lu) to %s succeeded.\n"),
|
|
|
|
rtc_fd, epoch, rtc_dev_name);
|
|
|
|
|
2011-07-24 10:35:43 -05:00
|
|
|
return 0;
|
2006-12-06 17:25:39 -06:00
|
|
|
}
|
2016-07-24 16:33:01 -05:00
|
|
|
#endif /* __alpha__ */
|