]> git.ipfire.org Git - thirdparty/util-linux.git/blame - hwclock/rtc.c
Imported from util-linux-2.10s tarball.
[thirdparty/util-linux.git] / hwclock / rtc.c
CommitLineData
7eda085c
KZ
1/* rtc.c - Use /dev/rtc for clock access */
2#include <unistd.h> /* for close() */
3#include <fcntl.h> /* for O_RDONLY */
4#include <sys/ioctl.h>
5
6#include "clock.h"
7#include "nls.h"
8
9/*
10 * Get defines for rtc stuff.
11 *
12 * Getting the rtc defines is nontrivial.
13 * The obvious way is by including <linux/mc146818rtc.h>
14 * but that again includes <asm/io.h> which again includes ...
15 * and on sparc and alpha this gives compilation errors for
16 * many kernel versions. So, we give the defines ourselves here.
17 * Moreover, some Sparc person decided to be incompatible, and
18 * used a struct rtc_time different from that used in mc146818rtc.h.
19 */
20
66ee8158
KZ
21/* ia64 uses /dev/efirtc (char 10,136) */
22#if __ia64__
23#define RTC_DEV "/dev/efirtc"
24#else
25#define RTC_DEV "/dev/rtc"
26#endif
27
7eda085c
KZ
28/* On Sparcs, there is a <asm/rtc.h> that defines different ioctls
29 (that are required on my machine). However, this include file
30 does not exist on other architectures. */
31/* One might do:
32#ifdef __sparc__
33#include <asm/rtc.h>
34#endif
35 */
36/* The following is roughly equivalent */
37struct sparc_rtc_time
38{
39 int sec; /* Seconds (0-59) */
40 int min; /* Minutes (0-59) */
41 int hour; /* Hour (0-23) */
42 int dow; /* Day of the week (1-7) */
43 int dom; /* Day of the month (1-31) */
44 int month; /* Month of year (1-12) */
45 int year; /* Year (0-99) */
46};
47
48#define RTCGET _IOR('p', 20, struct sparc_rtc_time)
49#define RTCSET _IOW('p', 21, struct sparc_rtc_time)
50
51
52/* non-sparc stuff */
53#if 0
54#include <linux/version.h>
55/* Check if the /dev/rtc interface is available in this version of
56 the system headers. 131072 is linux 2.0.0. */
57#if LINUX_VERSION_CODE >= 131072
58#include <linux/mc146818rtc.h>
59#endif
60#endif
61
62/* struct rtc_time is present since 1.3.99 */
63/* Earlier (since 1.3.89), a struct tm was used. */
64struct linux_rtc_time {
65 int tm_sec;
66 int tm_min;
67 int tm_hour;
68 int tm_mday;
69 int tm_mon;
70 int tm_year;
71 int tm_wday;
72 int tm_yday;
73 int tm_isdst;
74};
75
76/* RTC_RD_TIME etc have this definition since 1.99.9 (pre2.0-9) */
77#ifndef RTC_RD_TIME
78#define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time)
79#define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time)
80#define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */
81#define RTC_UIE_OFF _IO('p', 0x04) /* Update int. enable off */
82#endif
83/* RTC_EPOCH_READ and RTC_EPOCH_SET are present since 2.0.34 and 2.1.89 */
84#ifndef RTC_EPOCH_READ
85#define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */
86#define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */
87#endif
88
89
90static int
91do_rtc_read_ioctl(int rtc_fd, struct tm *tm) {
92 int rc;
93 char *ioctlname;
94#ifdef __sparc__
95 struct sparc_rtc_time stm;
96
97 ioctlname = "RTCGET";
98 rc = ioctl(rtc_fd, RTCGET, &stm);
99#else
100 ioctlname = "RTC_RD_TIME";
101 rc = ioctl(rtc_fd, RTC_RD_TIME, tm);
102#endif
103 if (rc == -1) {
104 perror(ioctlname);
66ee8158 105 fprintf(stderr, _("ioctl() to %s to read the time failed.\n"),RTC_DEV);
7eda085c
KZ
106 exit(5);
107 }
108#ifdef __sparc__
109 tm->tm_sec = stm.sec;
110 tm->tm_min = stm.min;
111 tm->tm_hour = stm.hour;
112 tm->tm_mday = stm.dom;
113 tm->tm_mon = stm.month - 1;
114 tm->tm_year = stm.year - 1900;
115 tm->tm_wday = stm.dow - 1;
116 tm->tm_yday = -1; /* day in the year */
117#endif
118 tm->tm_isdst = -1; /* don't know whether it's daylight */
119 return 0;
120}
121
122
123static int
124busywait_for_rtc_clock_tick(const int rtc_fd) {
125/*----------------------------------------------------------------------------
126 Wait for the top of a clock tick by reading /dev/rtc in a busy loop until
127 we see it.
128-----------------------------------------------------------------------------*/
129 struct tm start_time;
130 /* The time when we were called (and started waiting) */
131 struct tm nowtime;
132 int i; /* local loop index */
133 int rc;
134
135 if (debug)
66ee8158 136 printf(_("Waiting in loop for time from %s to change\n"),RTC_DEV);
7eda085c
KZ
137
138 rc = do_rtc_read_ioctl(rtc_fd, &start_time);
139 if (rc)
140 return 1;
141
142 /* Wait for change. Should be within a second, but in case something
143 weird happens, we have a limit on this loop to reduce the impact
144 of this failure.
145 */
146 for (i = 0;
147 (rc = do_rtc_read_ioctl(rtc_fd, &nowtime)) == 0
148 && start_time.tm_sec == nowtime.tm_sec;
149 i++)
150 if (i >= 1000000) {
151 fprintf(stderr, _("Timed out waiting for time change.\n"));
152 return 2;
153 }
154
155 if (rc)
156 return 3;
157 return 0;
158}
159
160
161
162static int
163synchronize_to_clock_tick_rtc(void) {
164/*----------------------------------------------------------------------------
165 Same as synchronize_to_clock_tick(), but just for /dev/rtc.
166-----------------------------------------------------------------------------*/
167int rtc_fd; /* File descriptor of /dev/rtc */
168int ret;
169
66ee8158 170 rtc_fd = open(RTC_DEV,O_RDONLY);
7eda085c 171 if (rtc_fd == -1) {
66ee8158 172 outsyserr(_("open() of %s failed"),RTC_DEV);
7eda085c
KZ
173 ret = 1;
174 } else {
175 int rc; /* Return code from ioctl */
176 /* Turn on update interrupts (one per second) */
177#if defined(__alpha__) || defined(__sparc__)
178 /* Not all alpha kernels reject RTC_UIE_ON, but probably they should. */
179 rc = -1;
180 errno = EINVAL;
181#else
182 rc = ioctl(rtc_fd, RTC_UIE_ON, 0);
183#endif
184 if (rc == -1 && errno == EINVAL) {
185 /* This rtc device doesn't have interrupt functions. This is typical
186 on an Alpha, where the Hardware Clock interrupts are used by the
187 kernel for the system clock, so aren't at the user's disposal.
188 */
66ee8158
KZ
189 if (debug)
190 printf(_("%s does not have interrupt functions. "),RTC_DEV);
7eda085c 191 ret = busywait_for_rtc_clock_tick(rtc_fd);
66ee8158 192 } else if (rc == 0) {
7eda085c
KZ
193 unsigned long dummy;
194
195 /* this blocks until the next update interrupt */
196 rc = read(rtc_fd, &dummy, sizeof(dummy));
197 if (rc == -1) {
66ee8158 198 outsyserr(_("read() to %s to wait for clock tick failed"),RTC_DEV);
7eda085c
KZ
199 ret = 1;
200 } else {
201 ret = 0;
202 }
203 /* Turn off update interrupts */
204 rc = ioctl(rtc_fd, RTC_UIE_OFF, 0);
205 if (rc == -1)
66ee8158
KZ
206 outsyserr(_("ioctl() to %s to turn off update interrupts "
207 "failed"),RTC_DEV);
7eda085c 208 } else {
66ee8158
KZ
209 outsyserr(_("ioctl() to %s to turn on update interrupts "
210 "failed unexpectedly"),RTC_DEV);
7eda085c
KZ
211 ret = 1;
212 }
213 close(rtc_fd);
214 }
215 return ret;
216}
217
218
219static int
220read_hardware_clock_rtc(struct tm *tm) {
221/*----------------------------------------------------------------------------
222 Read the hardware clock and return the current time via <tm>
223 argument. Use ioctls to "rtc" device /dev/rtc.
224-----------------------------------------------------------------------------*/
225 int rtc_fd; /* File descriptor of /dev/rtc */
226
66ee8158 227 rtc_fd = open(RTC_DEV,O_RDONLY);
7eda085c 228 if (rtc_fd == -1) {
66ee8158 229 outsyserr(_("open() of %s failed"),RTC_DEV);
7eda085c
KZ
230 exit(5);
231 }
232
233 /* Read the RTC time/date */
234 do_rtc_read_ioctl(rtc_fd, tm);
235
236 close(rtc_fd);
237 return 0;
238}
239
240
241static int
242set_hardware_clock_rtc(const struct tm *new_broken_time) {
243/*----------------------------------------------------------------------------
244 Set the Hardware Clock to the broken down time <new_broken_time>.
245 Use ioctls to "rtc" device /dev/rtc.
246----------------------------------------------------------------------------*/
247 int rc;
248 int rtc_fd;
249
66ee8158 250 rtc_fd = open(RTC_DEV, O_RDONLY);
7eda085c 251 if (rtc_fd < 0) {
66ee8158 252 outsyserr(_("Unable to open %s"),RTC_DEV);
7eda085c
KZ
253 exit(5);
254 } else {
255 char *ioctlname;
256#ifdef __sparc__
257 struct sparc_rtc_time stm;
258
259 stm.sec = new_broken_time->tm_sec;
260 stm.min = new_broken_time->tm_min;
261 stm.hour = new_broken_time->tm_hour;
262 stm.dom = new_broken_time->tm_mday;
263 stm.month = new_broken_time->tm_mon + 1;
264 stm.year = new_broken_time->tm_year + 1900;
265 stm.dow = new_broken_time->tm_wday + 1;
266
267 ioctlname = "RTCSET";
268 rc = ioctl(rtc_fd, RTCSET, &stm);
269#else
270 ioctlname = "RTC_SET_TIME";
271 rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time);
272#endif
273 if (rc == -1) {
274 perror(ioctlname);
66ee8158 275 fprintf(stderr, _("ioctl() to %s to set the time failed.\n"),RTC_DEV);
7eda085c
KZ
276 exit(5);
277 } else {
278 if (debug)
279 printf(_("ioctl(%s) was successful.\n"), ioctlname);
280 }
281 close(rtc_fd);
282 }
283 return 0;
284}
285
286
287static int
288get_permissions_rtc(void) {
289 return 0;
290}
291
292static struct clock_ops rtc = {
293 "/dev/rtc interface to clock",
294 get_permissions_rtc,
295 read_hardware_clock_rtc,
296 set_hardware_clock_rtc,
297 synchronize_to_clock_tick_rtc,
298};
299
300/* return &rtc if /dev/rtc can be opened, NULL otherwise */
301struct clock_ops *
302probe_for_rtc_clock(){
66ee8158 303 int rtc_fd = open(RTC_DEV, O_RDONLY);
7eda085c
KZ
304 if (rtc_fd > 0) {
305 close(rtc_fd);
306 return &rtc;
307 }
308 if (debug)
66ee8158 309 outsyserr(_("Open of %s failed"),RTC_DEV);
7eda085c
KZ
310 return NULL;
311}
312
313
314
315int
316get_epoch_rtc(unsigned long *epoch_p, int silent) {
317/*----------------------------------------------------------------------------
318 Get the Hardware Clock epoch setting from the kernel.
319----------------------------------------------------------------------------*/
320 int rtc_fd;
321
66ee8158 322 rtc_fd = open(RTC_DEV, O_RDONLY);
7eda085c
KZ
323 if (rtc_fd < 0) {
324 if (!silent) {
325 if (errno == ENOENT)
326 fprintf(stderr, _(
327 "To manipulate the epoch value in the kernel, we must "
328 "access the Linux 'rtc' device driver via the device special "
66ee8158 329 "file %s. This file does not exist on this system.\n"),RTC_DEV);
7eda085c 330 else
66ee8158 331 outsyserr(_("Unable to open %s"),RTC_DEV);
7eda085c
KZ
332 }
333 return 1;
334 }
335
336 if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) {
337 if (!silent)
66ee8158 338 outsyserr(_("ioctl(RTC_EPOCH_READ) to %s failed"),RTC_DEV);
7eda085c
KZ
339 close(rtc_fd);
340 return 1;
341 }
342
343 if (debug)
66ee8158
KZ
344 printf(_("we have read epoch %ld from %s "
345 "with RTC_EPOCH_READ ioctl.\n"), *epoch_p,RTC_DEV);
7eda085c
KZ
346
347 close(rtc_fd);
348 return 0;
349}
350
351
352
353int
354set_epoch_rtc(unsigned long epoch) {
355/*----------------------------------------------------------------------------
356 Set the Hardware Clock epoch in the kernel.
357----------------------------------------------------------------------------*/
358 int rtc_fd;
359
360 if (epoch < 1900) {
361 /* kernel would not accept this epoch value */
362 /* Hmm - bad habit, deciding not to do what the user asks
363 just because one believes that the kernel might not like it. */
364 fprintf(stderr, _("The epoch value may not be less than 1900. "
365 "You requested %ld\n"), epoch);
366 return 1;
367 }
368
66ee8158 369 rtc_fd = open(RTC_DEV, O_RDONLY);
7eda085c
KZ
370 if (rtc_fd < 0) {
371 if (errno == ENOENT)
372 fprintf(stderr, _("To manipulate the epoch value in the kernel, we must "
373 "access the Linux 'rtc' device driver via the device special "
66ee8158 374 "file %s. This file does not exist on this system.\n"),RTC_DEV);
7eda085c 375 else
66ee8158 376 outsyserr(_("Unable to open %s"),RTC_DEV);
7eda085c
KZ
377 return 1;
378 }
379
380 if (debug)
381 printf(_("setting epoch to %ld "
66ee8158 382 "with RTC_EPOCH_SET ioctl to %s.\n"), epoch, RTC_DEV);
7eda085c
KZ
383
384 if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) {
385 if (errno == EINVAL)
66ee8158
KZ
386 fprintf(stderr, _("The kernel device driver for %s "
387 "does not have the RTC_EPOCH_SET ioctl.\n"),RTC_DEV);
7eda085c 388 else
66ee8158 389 outsyserr(_("ioctl(RTC_EPOCH_SET) to %s failed"),RTC_DEV);
7eda085c
KZ
390 close(rtc_fd);
391 return 1;
392 }
393
394 close(rtc_fd);
395 return 0;
396}