From 7eda085c41faa3445b4b168ce78ab18dab87d98a Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 7 Dec 2006 00:25:39 +0100 Subject: Imported from util-linux-2.9v tarball. --- clock/rtc.c | 389 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 389 insertions(+) create mode 100644 clock/rtc.c (limited to 'clock/rtc.c') diff --git a/clock/rtc.c b/clock/rtc.c new file mode 100644 index 000000000..652388430 --- /dev/null +++ b/clock/rtc.c @@ -0,0 +1,389 @@ +/* rtc.c - Use /dev/rtc for clock access */ +#include /* for close() */ +#include /* for O_RDONLY */ +#include + +#include "clock.h" +#include "nls.h" + +/* + * Get defines for rtc stuff. + * + * Getting the rtc defines is nontrivial. + * The obvious way is by including + * but that again includes 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. + */ + +/* On Sparcs, there is a that defines different ioctls + (that are required on my machine). However, this include file + does not exist on other architectures. */ +/* One might do: +#ifdef __sparc__ +#include +#endif + */ +/* The following is roughly equivalent */ +struct sparc_rtc_time +{ + 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) */ +}; + +#define RTCGET _IOR('p', 20, struct sparc_rtc_time) +#define RTCSET _IOW('p', 21, struct sparc_rtc_time) + + +/* non-sparc stuff */ +#if 0 +#include +/* Check if the /dev/rtc interface is available in this version of + the system headers. 131072 is linux 2.0.0. */ +#if LINUX_VERSION_CODE >= 131072 +#include +#endif +#endif + +/* struct rtc_time is present since 1.3.99 */ +/* Earlier (since 1.3.89), a struct tm was used. */ +struct linux_rtc_time { + 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; +}; + +/* RTC_RD_TIME etc have this definition since 1.99.9 (pre2.0-9) */ +#ifndef RTC_RD_TIME +#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 */ +#endif +/* RTC_EPOCH_READ and RTC_EPOCH_SET are present since 2.0.34 and 2.1.89 */ +#ifndef RTC_EPOCH_READ +#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ +#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ +#endif + + +static int +do_rtc_read_ioctl(int rtc_fd, struct tm *tm) { + int rc; + char *ioctlname; +#ifdef __sparc__ + struct sparc_rtc_time stm; + + ioctlname = "RTCGET"; + rc = ioctl(rtc_fd, RTCGET, &stm); +#else + ioctlname = "RTC_RD_TIME"; + rc = ioctl(rtc_fd, RTC_RD_TIME, tm); +#endif + if (rc == -1) { + perror(ioctlname); + fprintf(stderr, _("ioctl() to /dev/rtc to read the time failed.\n")); + exit(5); + } +#ifdef __sparc__ + 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 */ +#endif + tm->tm_isdst = -1; /* don't know whether it's daylight */ + return 0; +} + + +static int +busywait_for_rtc_clock_tick(const int rtc_fd) { +/*---------------------------------------------------------------------------- + Wait for the top of a clock tick by reading /dev/rtc in a busy loop until + we see it. +-----------------------------------------------------------------------------*/ + struct tm start_time; + /* The time when we were called (and started waiting) */ + struct tm nowtime; + int i; /* local loop index */ + int rc; + + if (debug) + printf(_("Waiting in loop for time from /dev/rtc to change\n")); + + rc = do_rtc_read_ioctl(rtc_fd, &start_time); + if (rc) + return 1; + + /* Wait for change. Should be within a second, but in case something + weird happens, we have a limit on this loop to reduce the impact + of this failure. + */ + for (i = 0; + (rc = do_rtc_read_ioctl(rtc_fd, &nowtime)) == 0 + && start_time.tm_sec == nowtime.tm_sec; + i++) + if (i >= 1000000) { + fprintf(stderr, _("Timed out waiting for time change.\n")); + return 2; + } + + if (rc) + return 3; + return 0; +} + + + +static int +synchronize_to_clock_tick_rtc(void) { +/*---------------------------------------------------------------------------- + Same as synchronize_to_clock_tick(), but just for /dev/rtc. +-----------------------------------------------------------------------------*/ +int rtc_fd; /* File descriptor of /dev/rtc */ +int ret; + + rtc_fd = open("/dev/rtc",O_RDONLY); + if (rtc_fd == -1) { + outsyserr(_("open() of /dev/rtc failed")); + ret = 1; + } else { + int rc; /* Return code from ioctl */ + /* Turn on update interrupts (one per second) */ +#if defined(__alpha__) || defined(__sparc__) + /* Not all alpha kernels reject RTC_UIE_ON, but probably they should. */ + rc = -1; + errno = EINVAL; +#else + rc = ioctl(rtc_fd, RTC_UIE_ON, 0); +#endif + if (rc == -1 && errno == EINVAL) { + /* This rtc device doesn't have interrupt functions. 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. + */ + if (debug) printf(_("/dev/rtc does not have interrupt functions. ")); + ret = busywait_for_rtc_clock_tick(rtc_fd); + } else if (rc != -1) { + int rc; /* return code from ioctl */ + unsigned long dummy; + + /* this blocks until the next update interrupt */ + rc = read(rtc_fd, &dummy, sizeof(dummy)); + if (rc == -1) { + outsyserr(_("read() to /dev/rtc to wait for clock tick failed")); + ret = 1; + } else { + ret = 0; + } + /* Turn off update interrupts */ + rc = ioctl(rtc_fd, RTC_UIE_OFF, 0); + if (rc == -1) + outsyserr(_("ioctl() to /dev/rtc to turn off update interrupts " + "failed")); + } else { + outsyserr(_("ioctl() to /dev/rtc to turn on update interrupts " + "failed unexpectedly")); + ret = 1; + } + close(rtc_fd); + } + return ret; +} + + +static int +read_hardware_clock_rtc(struct tm *tm) { +/*---------------------------------------------------------------------------- + Read the hardware clock and return the current time via + argument. Use ioctls to "rtc" device /dev/rtc. +-----------------------------------------------------------------------------*/ + int rtc_fd; /* File descriptor of /dev/rtc */ + + rtc_fd = open("/dev/rtc",O_RDONLY); + if (rtc_fd == -1) { + outsyserr(_("open() of /dev/rtc failed")); + exit(5); + } + + /* Read the RTC time/date */ + do_rtc_read_ioctl(rtc_fd, tm); + + close(rtc_fd); + return 0; +} + + +static int +set_hardware_clock_rtc(const struct tm *new_broken_time) { +/*---------------------------------------------------------------------------- + Set the Hardware Clock to the broken down time . + Use ioctls to "rtc" device /dev/rtc. +----------------------------------------------------------------------------*/ + int rc; + int rtc_fd; + + rtc_fd = open("/dev/rtc", O_RDONLY); + if (rtc_fd < 0) { + outsyserr(_("Unable to open /dev/rtc")); + exit(5); + } else { + char *ioctlname; +#ifdef __sparc__ + 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); +#else + ioctlname = "RTC_SET_TIME"; + rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time); +#endif + if (rc == -1) { + perror(ioctlname); + fprintf(stderr, _("ioctl() to /dev/rtc to set the time failed.\n")); + exit(5); + } else { + if (debug) + printf(_("ioctl(%s) was successful.\n"), ioctlname); + } + close(rtc_fd); + } + return 0; +} + + +static int +get_permissions_rtc(void) { + return 0; +} + +static struct clock_ops rtc = { + "/dev/rtc interface to clock", + get_permissions_rtc, + read_hardware_clock_rtc, + set_hardware_clock_rtc, + synchronize_to_clock_tick_rtc, +}; + +/* return &rtc if /dev/rtc can be opened, NULL otherwise */ +struct clock_ops * +probe_for_rtc_clock(){ + int rtc_fd = open("/dev/rtc", O_RDONLY); + if (rtc_fd > 0) { + close(rtc_fd); + return &rtc; + } + if (debug) + outsyserr(_("Open of /dev/rtc failed")); + return NULL; +} + + + +int +get_epoch_rtc(unsigned long *epoch_p, int silent) { +/*---------------------------------------------------------------------------- + Get the Hardware Clock epoch setting from the kernel. +----------------------------------------------------------------------------*/ + int rtc_fd; + + rtc_fd = open("/dev/rtc", O_RDONLY); + if (rtc_fd < 0) { + if (!silent) { + if (errno == ENOENT) + fprintf(stderr, _( + "To manipulate the epoch value in the kernel, we must " + "access the Linux 'rtc' device driver via the device special " + "file /dev/rtc. This file does not exist on this system.\n")); + else + outsyserr(_("Unable to open /dev/rtc")); + } + return 1; + } + + if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) { + if (!silent) + outsyserr(_("ioctl(RTC_EPOCH_READ) to /dev/rtc failed")); + close(rtc_fd); + return 1; + } + + if (debug) + printf(_("we have read epoch %ld from /dev/rtc " + "with RTC_EPOCH_READ ioctl.\n"), *epoch_p); + + close(rtc_fd); + return 0; +} + + + +int +set_epoch_rtc(unsigned long epoch) { +/*---------------------------------------------------------------------------- + Set the Hardware Clock epoch in the kernel. +----------------------------------------------------------------------------*/ + int rtc_fd; + + if (epoch < 1900) { + /* kernel would not accept this epoch value */ + /* Hmm - bad habit, deciding not to do what the user asks + just because one believes that the kernel might not like it. */ + fprintf(stderr, _("The epoch value may not be less than 1900. " + "You requested %ld\n"), epoch); + return 1; + } + + rtc_fd = open("/dev/rtc", O_RDONLY); + if (rtc_fd < 0) { + if (errno == ENOENT) + fprintf(stderr, _("To manipulate the epoch value in the kernel, we must " + "access the Linux 'rtc' device driver via the device special " + "file /dev/rtc. This file does not exist on this system.\n")); + else + outsyserr(_("Unable to open /dev/rtc")); + return 1; + } + + if (debug) + printf(_("setting epoch to %ld " + "with RTC_EPOCH_SET ioctl to /dev/rtc.\n"), epoch); + + if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) { + if (errno == EINVAL) + fprintf(stderr, _("The kernel device driver for /dev/rtc " + "does not have the RTC_EPOCH_SET ioctl.\n")); + else + outsyserr(_("ioctl(RTC_EPOCH_SET) to /dev/rtc failed")); + close(rtc_fd); + return 1; + } + + close(rtc_fd); + return 0; +} -- cgit v1.2.3-55-g7522