]>
Commit | Line | Data |
---|---|---|
ef71b8f1 SK |
1 | /* |
2 | * rtc.c - Use /dev/rtc for clock access | |
3 | */ | |
998f392a | 4 | #include <asm/ioctl.h> |
5213517f | 5 | #include <errno.h> |
998f392a SK |
6 | #include <fcntl.h> |
7 | #include <stdio.h> | |
8 | #include <stdlib.h> | |
63cccae4 | 9 | #include <sysexits.h> |
7eda085c | 10 | #include <sys/ioctl.h> |
998f392a SK |
11 | #include <sys/select.h> |
12 | #include <sys/time.h> | |
13 | #include <time.h> | |
14 | #include <unistd.h> | |
7eda085c | 15 | |
7eda085c KZ |
16 | #include "nls.h" |
17 | ||
c7f75390 KZ |
18 | #include "hwclock.h" |
19 | ||
7eda085c KZ |
20 | /* |
21 | * Get defines for rtc stuff. | |
22 | * | |
ef71b8f1 SK |
23 | * Getting the rtc defines is nontrivial. The obvious way is by including |
24 | * <linux/mc146818rtc.h> but that again includes <asm/io.h> which again | |
25 | * includes ... and on sparc and alpha this gives compilation errors for | |
26 | * many kernel versions. So, we give the defines ourselves here. Moreover, | |
27 | * some Sparc person decided to be incompatible, and used a struct rtc_time | |
28 | * different from that used in mc146818rtc.h. | |
7eda085c KZ |
29 | */ |
30 | ||
ef71b8f1 SK |
31 | /* |
32 | * On Sparcs, there is a <asm/rtc.h> that defines different ioctls (that are | |
33 | * required on my machine). However, this include file does not exist on | |
34 | * other architectures. | |
35 | */ | |
7eda085c KZ |
36 | /* One might do: |
37 | #ifdef __sparc__ | |
ef71b8f1 | 38 | # include <asm/rtc.h> |
7eda085c KZ |
39 | #endif |
40 | */ | |
41 | /* The following is roughly equivalent */ | |
42 | struct sparc_rtc_time | |
43 | { | |
ef71b8f1 SK |
44 | int sec; /* Seconds 0-59 */ |
45 | int min; /* Minutes 0-59 */ | |
46 | int hour; /* Hour 0-23 */ | |
47 | int dow; /* Day of the week 1-7 */ | |
48 | int dom; /* Day of the month 1-31 */ | |
49 | int month; /* Month of year 1-12 */ | |
50 | int year; /* Year 0-99 */ | |
7eda085c KZ |
51 | }; |
52 | ||
53 | #define RTCGET _IOR('p', 20, struct sparc_rtc_time) | |
54 | #define RTCSET _IOW('p', 21, struct sparc_rtc_time) | |
55 | ||
7eda085c KZ |
56 | /* non-sparc stuff */ |
57 | #if 0 | |
ef71b8f1 SK |
58 | # include <linux/version.h> |
59 | /* | |
60 | * Check if the /dev/rtc interface is available in this version of the | |
61 | * system headers. 131072 is linux 2.0.0. | |
62 | */ | |
63 | # if LINUX_VERSION_CODE >= 131072 | |
64 | # include <linux/mc146818rtc.h> | |
65 | # endif | |
7eda085c KZ |
66 | #endif |
67 | ||
ef71b8f1 SK |
68 | /* |
69 | * struct rtc_time is present since 1.3.99. | |
70 | * Earlier (since 1.3.89), a struct tm was used. | |
71 | */ | |
7eda085c | 72 | struct linux_rtc_time { |
ef71b8f1 SK |
73 | int tm_sec; |
74 | int tm_min; | |
75 | int tm_hour; | |
76 | int tm_mday; | |
77 | int tm_mon; | |
78 | int tm_year; | |
79 | int tm_wday; | |
80 | int tm_yday; | |
81 | int tm_isdst; | |
7eda085c KZ |
82 | }; |
83 | ||
84 | /* RTC_RD_TIME etc have this definition since 1.99.9 (pre2.0-9) */ | |
85 | #ifndef RTC_RD_TIME | |
ef71b8f1 SK |
86 | # define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) |
87 | # define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) | |
88 | # define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */ | |
89 | # define RTC_UIE_OFF _IO('p', 0x04) /* Update int. enable off */ | |
7eda085c | 90 | #endif |
ef71b8f1 | 91 | |
7eda085c KZ |
92 | /* RTC_EPOCH_READ and RTC_EPOCH_SET are present since 2.0.34 and 2.1.89 */ |
93 | #ifndef RTC_EPOCH_READ | |
ef71b8f1 SK |
94 | # define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ |
95 | # define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ | |
7eda085c KZ |
96 | #endif |
97 | ||
ef71b8f1 SK |
98 | /* |
99 | * /dev/rtc is conventionally chardev 10/135 | |
88681c5f KZ |
100 | * ia64 uses /dev/efirtc, chardev 10/136 |
101 | * devfs (obsolete) used /dev/misc/... for miscdev | |
102 | * new RTC framework + udev uses dynamic major and /dev/rtc0.../dev/rtcN | |
103 | * ... so we need an overridable default | |
104 | */ | |
7eda085c | 105 | |
88681c5f KZ |
106 | /* default or user defined dev (by hwclock --rtc=<path>) */ |
107 | char *rtc_dev_name; | |
364cda48 | 108 | |
27f9db17 KZ |
109 | static int rtc_dev_fd = -1; |
110 | ||
ef71b8f1 SK |
111 | static void close_rtc(void) |
112 | { | |
27f9db17 KZ |
113 | if (rtc_dev_fd != -1) |
114 | close(rtc_dev_fd); | |
115 | rtc_dev_fd = -1; | |
116 | } | |
117 | ||
ef71b8f1 SK |
118 | static int open_rtc(void) |
119 | { | |
88681c5f KZ |
120 | char *fls[] = { |
121 | #ifdef __ia64__ | |
122 | "/dev/efirtc", | |
123 | "/dev/misc/efirtc", | |
124 | #endif | |
125 | "/dev/rtc", | |
126 | "/dev/rtc0", | |
127 | "/dev/misc/rtc", | |
128 | NULL | |
129 | }; | |
5d1f6bae MK |
130 | char **p; |
131 | ||
27f9db17 KZ |
132 | if (rtc_dev_fd != -1) |
133 | return rtc_dev_fd; | |
134 | ||
5d1f6bae MK |
135 | /* --rtc option has been given */ |
136 | if (rtc_dev_name) | |
27f9db17 KZ |
137 | rtc_dev_fd = open(rtc_dev_name, O_RDONLY); |
138 | else { | |
ef71b8f1 | 139 | for (p = fls; *p; ++p) { |
27f9db17 KZ |
140 | rtc_dev_fd = open(*p, O_RDONLY); |
141 | ||
ef71b8f1 SK |
142 | if (rtc_dev_fd < 0 |
143 | && (errno == ENOENT || errno == ENODEV)) | |
27f9db17 KZ |
144 | continue; |
145 | rtc_dev_name = *p; | |
146 | break; | |
147 | } | |
148 | if (rtc_dev_fd < 0) | |
149 | rtc_dev_name = *fls; /* default for error messages */ | |
5d1f6bae | 150 | } |
27f9db17 KZ |
151 | |
152 | if (rtc_dev_fd != 1) | |
153 | atexit(close_rtc); | |
154 | return rtc_dev_fd; | |
63cccae4 KZ |
155 | } |
156 | ||
ef71b8f1 SK |
157 | static int open_rtc_or_exit(void) |
158 | { | |
63cccae4 KZ |
159 | int rtc_fd = open_rtc(); |
160 | ||
161 | if (rtc_fd < 0) { | |
111c05d3 | 162 | warn(_("open() of %s failed"), rtc_dev_name); |
88058a71 | 163 | hwclock_exit(EX_OSFILE); |
364cda48 KZ |
164 | } |
165 | return rtc_fd; | |
166 | } | |
167 | ||
ef71b8f1 SK |
168 | static int do_rtc_read_ioctl(int rtc_fd, struct tm *tm) |
169 | { | |
364cda48 KZ |
170 | int rc = -1; |
171 | char *ioctlname; | |
7eda085c | 172 | |
7eda085c | 173 | #ifdef __sparc__ |
364cda48 KZ |
174 | /* some but not all sparcs use a different ioctl and struct */ |
175 | struct sparc_rtc_time stm; | |
176 | ||
177 | ioctlname = "RTCGET"; | |
178 | rc = ioctl(rtc_fd, RTCGET, &stm); | |
179 | if (rc == 0) { | |
180 | tm->tm_sec = stm.sec; | |
181 | tm->tm_min = stm.min; | |
182 | tm->tm_hour = stm.hour; | |
183 | tm->tm_mday = stm.dom; | |
184 | tm->tm_mon = stm.month - 1; | |
185 | tm->tm_year = stm.year - 1900; | |
186 | tm->tm_wday = stm.dow - 1; | |
ef71b8f1 | 187 | tm->tm_yday = -1; /* day in the year */ |
364cda48 | 188 | } |
7eda085c | 189 | #endif |
364cda48 KZ |
190 | if (rc == -1) { /* no sparc, or RTCGET failed */ |
191 | ioctlname = "RTC_RD_TIME"; | |
192 | rc = ioctl(rtc_fd, RTC_RD_TIME, tm); | |
193 | } | |
194 | if (rc == -1) { | |
111c05d3 SK |
195 | warn(_("ioctl(%s) to %s to read the time failed"), |
196 | ioctlname, rtc_dev_name); | |
cdedde03 | 197 | return -1; |
364cda48 KZ |
198 | } |
199 | ||
ef71b8f1 | 200 | tm->tm_isdst = -1; /* don't know whether it's dst */ |
364cda48 KZ |
201 | return 0; |
202 | } | |
7eda085c | 203 | |
ef71b8f1 SK |
204 | /* |
205 | * Wait for the top of a clock tick by reading /dev/rtc in a busy loop until | |
206 | * we see it. | |
207 | */ | |
208 | static int busywait_for_rtc_clock_tick(const int rtc_fd) | |
209 | { | |
210 | struct tm start_time; | |
211 | /* The time when we were called (and started waiting) */ | |
212 | struct tm nowtime; | |
213 | int rc; | |
214 | struct timeval begin, now; | |
215 | ||
216 | if (debug) | |
217 | printf(_("Waiting in loop for time from %s to change\n"), | |
218 | rtc_dev_name); | |
219 | ||
220 | rc = do_rtc_read_ioctl(rtc_fd, &start_time); | |
221 | if (rc) | |
222 | return 1; | |
223 | ||
224 | /* | |
225 | * Wait for change. Should be within a second, but in case | |
226 | * something weird happens, we have a time limit (1.5s) on this loop | |
227 | * to reduce the impact of this failure. | |
228 | */ | |
229 | gettimeofday(&begin, NULL); | |
230 | do { | |
231 | rc = do_rtc_read_ioctl(rtc_fd, &nowtime); | |
232 | if (rc || start_time.tm_sec != nowtime.tm_sec) | |
233 | break; | |
234 | gettimeofday(&now, NULL); | |
235 | if (time_diff(now, begin) > 1.5) { | |
111c05d3 | 236 | warnx(_("Timed out waiting for time change.")); |
ef71b8f1 SK |
237 | return 2; |
238 | } | |
239 | } while (1); | |
240 | ||
241 | if (rc) | |
242 | return 3; | |
243 | return 0; | |
7eda085c KZ |
244 | } |
245 | ||
ef71b8f1 SK |
246 | /* |
247 | * Same as synchronize_to_clock_tick(), but just for /dev/rtc. | |
248 | */ | |
249 | static int synchronize_to_clock_tick_rtc(void) | |
250 | { | |
251 | int rtc_fd; /* File descriptor of /dev/rtc */ | |
252 | int ret; | |
253 | ||
254 | rtc_fd = open_rtc(); | |
255 | if (rtc_fd == -1) { | |
111c05d3 | 256 | warn(_("open() of %s failed"), rtc_dev_name); |
ef71b8f1 SK |
257 | ret = 1; |
258 | } else { | |
259 | int rc; /* Return code from ioctl */ | |
260 | /* Turn on update interrupts (one per second) */ | |
442d61c6 | 261 | #if defined(__alpha__) || defined(__sparc__) |
ef71b8f1 SK |
262 | /* |
263 | * Not all alpha kernels reject RTC_UIE_ON, but probably | |
264 | * they should. | |
265 | */ | |
266 | rc = -1; | |
267 | errno = EINVAL; | |
7eda085c | 268 | #else |
ef71b8f1 | 269 | rc = ioctl(rtc_fd, RTC_UIE_ON, 0); |
7eda085c | 270 | #endif |
ef71b8f1 SK |
271 | if (rc == -1 && (errno == ENOTTY || errno == EINVAL)) { |
272 | /* | |
273 | * This rtc device doesn't have interrupt functions. | |
274 | * This is typical on an Alpha, where the Hardware | |
275 | * Clock interrupts are used by the kernel for the | |
276 | * system clock, so aren't at the user's disposal. | |
277 | */ | |
278 | if (debug) | |
279 | printf(_ | |
280 | ("%s does not have interrupt functions. "), | |
281 | rtc_dev_name); | |
282 | ret = busywait_for_rtc_clock_tick(rtc_fd); | |
283 | } else if (rc == 0) { | |
d03dd608 | 284 | #ifdef Wait_until_update_interrupt |
ef71b8f1 SK |
285 | unsigned long dummy; |
286 | ||
287 | /* this blocks until the next update interrupt */ | |
288 | rc = read(rtc_fd, &dummy, sizeof(dummy)); | |
289 | ret = 1; | |
290 | if (rc == -1) | |
111c05d3 SK |
291 | warn(_ |
292 | ("read() to %s to wait for clock tick failed"), | |
293 | rtc_dev_name); | |
ef71b8f1 SK |
294 | else |
295 | ret = 0; | |
d03dd608 | 296 | #else |
ef71b8f1 SK |
297 | /* |
298 | * Just reading rtc_fd fails on broken hardware: no | |
299 | * update interrupt comes and a bootscript with a | |
300 | * hwclock call hangs | |
301 | */ | |
302 | fd_set rfds; | |
303 | struct timeval tv; | |
304 | ||
305 | /* | |
306 | * Wait up to five seconds for the next update | |
307 | * interrupt | |
308 | */ | |
309 | FD_ZERO(&rfds); | |
310 | FD_SET(rtc_fd, &rfds); | |
311 | tv.tv_sec = 5; | |
312 | tv.tv_usec = 0; | |
313 | rc = select(rtc_fd + 1, &rfds, NULL, NULL, &tv); | |
314 | ret = 1; | |
315 | if (rc == -1) | |
111c05d3 SK |
316 | warn(_ |
317 | ("select() to %s to wait for clock tick failed"), | |
318 | rtc_dev_name); | |
ef71b8f1 | 319 | else if (rc == 0) |
111c05d3 SK |
320 | warn(_ |
321 | ("select() to %s to wait for clock tick timed out"), | |
322 | rtc_dev_name); | |
ef71b8f1 SK |
323 | else |
324 | ret = 0; | |
d03dd608 KZ |
325 | #endif |
326 | ||
ef71b8f1 SK |
327 | /* Turn off update interrupts */ |
328 | rc = ioctl(rtc_fd, RTC_UIE_OFF, 0); | |
329 | if (rc == -1) | |
111c05d3 SK |
330 | warn(_ |
331 | ("ioctl() to %s to turn off update interrupts failed"), | |
332 | rtc_dev_name); | |
ef71b8f1 | 333 | } else { |
111c05d3 SK |
334 | warn(_ |
335 | ("ioctl() to %s to turn on update interrupts " | |
336 | "failed unexpectedly"), rtc_dev_name); | |
ef71b8f1 SK |
337 | ret = 1; |
338 | } | |
339 | } | |
340 | return ret; | |
7eda085c KZ |
341 | } |
342 | ||
ef71b8f1 SK |
343 | static int read_hardware_clock_rtc(struct tm *tm) |
344 | { | |
55a4a75c | 345 | int rtc_fd, rc; |
7eda085c | 346 | |
63cccae4 | 347 | rtc_fd = open_rtc_or_exit(); |
7eda085c | 348 | |
364cda48 | 349 | /* Read the RTC time/date, return answer via tm */ |
55a4a75c | 350 | rc = do_rtc_read_ioctl(rtc_fd, tm); |
7eda085c | 351 | |
55a4a75c | 352 | return rc; |
7eda085c KZ |
353 | } |
354 | ||
ef71b8f1 SK |
355 | /* |
356 | * Set the Hardware Clock to the broken down time <new_broken_time>. Use | |
357 | * ioctls to "rtc" device /dev/rtc. | |
358 | */ | |
359 | static int set_hardware_clock_rtc(const struct tm *new_broken_time) | |
360 | { | |
364cda48 KZ |
361 | int rc = -1; |
362 | int rtc_fd; | |
363 | char *ioctlname; | |
364 | ||
63cccae4 KZ |
365 | rtc_fd = open_rtc_or_exit(); |
366 | ||
7eda085c | 367 | #ifdef __sparc__ |
364cda48 KZ |
368 | { |
369 | struct sparc_rtc_time stm; | |
370 | ||
371 | stm.sec = new_broken_time->tm_sec; | |
372 | stm.min = new_broken_time->tm_min; | |
373 | stm.hour = new_broken_time->tm_hour; | |
374 | stm.dom = new_broken_time->tm_mday; | |
375 | stm.month = new_broken_time->tm_mon + 1; | |
376 | stm.year = new_broken_time->tm_year + 1900; | |
377 | stm.dow = new_broken_time->tm_wday + 1; | |
378 | ||
379 | ioctlname = "RTCSET"; | |
380 | rc = ioctl(rtc_fd, RTCSET, &stm); | |
381 | } | |
7eda085c | 382 | #endif |
364cda48 KZ |
383 | if (rc == -1) { /* no sparc, or RTCSET failed */ |
384 | ioctlname = "RTC_SET_TIME"; | |
385 | rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time); | |
386 | } | |
387 | ||
388 | if (rc == -1) { | |
111c05d3 SK |
389 | warn(_("ioctl(%s) to %s to set the time failed."), |
390 | ioctlname, rtc_dev_name); | |
88058a71 | 391 | hwclock_exit(EX_IOERR); |
364cda48 KZ |
392 | } |
393 | ||
394 | if (debug) | |
395 | printf(_("ioctl(%s) was successful.\n"), ioctlname); | |
396 | ||
364cda48 | 397 | return 0; |
7eda085c KZ |
398 | } |
399 | ||
ef71b8f1 SK |
400 | static int get_permissions_rtc(void) |
401 | { | |
7eda085c KZ |
402 | return 0; |
403 | } | |
404 | ||
405 | static struct clock_ops rtc = { | |
88681c5f | 406 | "/dev interface to clock", |
7eda085c KZ |
407 | get_permissions_rtc, |
408 | read_hardware_clock_rtc, | |
409 | set_hardware_clock_rtc, | |
410 | synchronize_to_clock_tick_rtc, | |
411 | }; | |
412 | ||
413 | /* return &rtc if /dev/rtc can be opened, NULL otherwise */ | |
c9c1304d | 414 | struct clock_ops *probe_for_rtc_clock(void) |
ef71b8f1 | 415 | { |
364cda48 | 416 | int rtc_fd = open_rtc(); |
27f9db17 | 417 | if (rtc_fd >= 0) |
364cda48 | 418 | return &rtc; |
364cda48 | 419 | if (debug) |
111c05d3 | 420 | warn(_("Open of %s failed"), rtc_dev_name); |
364cda48 | 421 | return NULL; |
7eda085c KZ |
422 | } |
423 | ||
ef71b8f1 SK |
424 | /* |
425 | * Get the Hardware Clock epoch setting from the kernel. | |
426 | */ | |
427 | int get_epoch_rtc(unsigned long *epoch_p, int silent) | |
428 | { | |
429 | int rtc_fd; | |
430 | ||
431 | rtc_fd = open_rtc(); | |
432 | if (rtc_fd < 0) { | |
433 | if (!silent) { | |
434 | if (errno == ENOENT) | |
111c05d3 SK |
435 | warnx(_ |
436 | ("To manipulate the epoch value in the kernel, we must " | |
437 | "access the Linux 'rtc' device driver via the device special " | |
438 | "file %s. This file does not exist on this system."), | |
439 | rtc_dev_name); | |
ef71b8f1 | 440 | else |
111c05d3 | 441 | warn(_("Unable to open %s"), rtc_dev_name); |
ef71b8f1 SK |
442 | } |
443 | return 1; | |
444 | } | |
445 | ||
446 | if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) { | |
447 | if (!silent) | |
111c05d3 | 448 | warn(_("ioctl(RTC_EPOCH_READ) to %s failed"), |
ef71b8f1 SK |
449 | rtc_dev_name); |
450 | return 1; | |
451 | } | |
7eda085c | 452 | |
ef71b8f1 SK |
453 | if (debug) |
454 | printf(_("we have read epoch %ld from %s " | |
455 | "with RTC_EPOCH_READ ioctl.\n"), *epoch_p, | |
456 | rtc_dev_name); | |
7eda085c | 457 | |
ef71b8f1 | 458 | return 0; |
7eda085c KZ |
459 | } |
460 | ||
ef71b8f1 SK |
461 | /* |
462 | * Set the Hardware Clock epoch in the kernel. | |
463 | */ | |
464 | int set_epoch_rtc(unsigned long epoch) | |
465 | { | |
466 | int rtc_fd; | |
467 | ||
468 | if (epoch < 1900) { | |
469 | /* kernel would not accept this epoch value | |
470 | * | |
471 | * Bad habit, deciding not to do what the user asks just | |
472 | * because one believes that the kernel might not like it. | |
473 | */ | |
111c05d3 SK |
474 | warnx(_("The epoch value may not be less than 1900. " |
475 | "You requested %ld"), epoch); | |
ef71b8f1 SK |
476 | return 1; |
477 | } | |
478 | ||
479 | rtc_fd = open_rtc(); | |
480 | if (rtc_fd < 0) { | |
481 | if (errno == ENOENT) | |
111c05d3 SK |
482 | warnx(_ |
483 | ("To manipulate the epoch value in the kernel, we must " | |
484 | "access the Linux 'rtc' device driver via the device special " | |
485 | "file %s. This file does not exist on this system."), | |
486 | rtc_dev_name); | |
ef71b8f1 | 487 | else |
111c05d3 | 488 | warn(_("Unable to open %s"), rtc_dev_name); |
ef71b8f1 SK |
489 | return 1; |
490 | } | |
7eda085c | 491 | |
ef71b8f1 SK |
492 | if (debug) |
493 | printf(_("setting epoch to %ld " | |
494 | "with RTC_EPOCH_SET ioctl to %s.\n"), epoch, | |
495 | rtc_dev_name); | |
496 | ||
497 | if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) { | |
498 | if (errno == EINVAL) | |
111c05d3 SK |
499 | warnx(_("The kernel device driver for %s " |
500 | "does not have the RTC_EPOCH_SET ioctl."), | |
501 | rtc_dev_name); | |
ef71b8f1 | 502 | else |
111c05d3 | 503 | warn(_("ioctl(RTC_EPOCH_SET) to %s failed"), |
ef71b8f1 SK |
504 | rtc_dev_name); |
505 | return 1; | |
506 | } | |
7eda085c | 507 | |
ef71b8f1 | 508 | return 0; |
7eda085c | 509 | } |