]>
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> | |
7eda085c | 9 | #include <sys/ioctl.h> |
998f392a SK |
10 | #include <sys/select.h> |
11 | #include <sys/time.h> | |
12 | #include <time.h> | |
13 | #include <unistd.h> | |
7eda085c | 14 | |
f78a9021 | 15 | #include "monotonic.h" |
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 | */ | |
bd078689 | 41 | #ifdef __sparc__ |
7eda085c KZ |
42 | /* The following is roughly equivalent */ |
43 | struct sparc_rtc_time | |
44 | { | |
ef71b8f1 SK |
45 | int sec; /* Seconds 0-59 */ |
46 | int min; /* Minutes 0-59 */ | |
47 | int hour; /* Hour 0-23 */ | |
48 | int dow; /* Day of the week 1-7 */ | |
49 | int dom; /* Day of the month 1-31 */ | |
50 | int month; /* Month of year 1-12 */ | |
51 | int year; /* Year 0-99 */ | |
7eda085c | 52 | }; |
7eda085c KZ |
53 | #define RTCGET _IOR('p', 20, struct sparc_rtc_time) |
54 | #define RTCSET _IOW('p', 21, struct sparc_rtc_time) | |
bd078689 | 55 | #endif |
7eda085c | 56 | |
ef71b8f1 SK |
57 | /* |
58 | * struct rtc_time is present since 1.3.99. | |
59 | * Earlier (since 1.3.89), a struct tm was used. | |
60 | */ | |
7eda085c | 61 | struct linux_rtc_time { |
ef71b8f1 SK |
62 | int tm_sec; |
63 | int tm_min; | |
64 | int tm_hour; | |
65 | int tm_mday; | |
66 | int tm_mon; | |
67 | int tm_year; | |
68 | int tm_wday; | |
69 | int tm_yday; | |
70 | int tm_isdst; | |
7eda085c KZ |
71 | }; |
72 | ||
73 | /* RTC_RD_TIME etc have this definition since 1.99.9 (pre2.0-9) */ | |
74 | #ifndef RTC_RD_TIME | |
ef71b8f1 SK |
75 | # define RTC_RD_TIME _IOR('p', 0x09, struct linux_rtc_time) |
76 | # define RTC_SET_TIME _IOW('p', 0x0a, struct linux_rtc_time) | |
77 | # define RTC_UIE_ON _IO('p', 0x03) /* Update int. enable on */ | |
78 | # define RTC_UIE_OFF _IO('p', 0x04) /* Update int. enable off */ | |
7eda085c | 79 | #endif |
ef71b8f1 | 80 | |
7eda085c KZ |
81 | /* RTC_EPOCH_READ and RTC_EPOCH_SET are present since 2.0.34 and 2.1.89 */ |
82 | #ifndef RTC_EPOCH_READ | |
ef71b8f1 SK |
83 | # define RTC_EPOCH_READ _IOR('p', 0x0d, unsigned long) /* Read epoch */ |
84 | # define RTC_EPOCH_SET _IOW('p', 0x0e, unsigned long) /* Set epoch */ | |
7eda085c KZ |
85 | #endif |
86 | ||
ef71b8f1 SK |
87 | /* |
88 | * /dev/rtc is conventionally chardev 10/135 | |
88681c5f KZ |
89 | * ia64 uses /dev/efirtc, chardev 10/136 |
90 | * devfs (obsolete) used /dev/misc/... for miscdev | |
91 | * new RTC framework + udev uses dynamic major and /dev/rtc0.../dev/rtcN | |
92 | * ... so we need an overridable default | |
93 | */ | |
7eda085c | 94 | |
88681c5f | 95 | /* default or user defined dev (by hwclock --rtc=<path>) */ |
067b6028 | 96 | static const char *rtc_dev_name; |
27f9db17 KZ |
97 | static int rtc_dev_fd = -1; |
98 | ||
ef71b8f1 SK |
99 | static void close_rtc(void) |
100 | { | |
27f9db17 KZ |
101 | if (rtc_dev_fd != -1) |
102 | close(rtc_dev_fd); | |
103 | rtc_dev_fd = -1; | |
104 | } | |
105 | ||
336f7c5f | 106 | static int open_rtc(const struct hwclock_control *ctl) |
ef71b8f1 | 107 | { |
067b6028 | 108 | static const char *fls[] = { |
88681c5f KZ |
109 | #ifdef __ia64__ |
110 | "/dev/efirtc", | |
111 | "/dev/misc/efirtc", | |
112 | #endif | |
88681c5f | 113 | "/dev/rtc0", |
1811900a | 114 | "/dev/rtc", |
067b6028 | 115 | "/dev/misc/rtc" |
88681c5f | 116 | }; |
067b6028 | 117 | size_t i; |
5d1f6bae | 118 | |
27f9db17 KZ |
119 | if (rtc_dev_fd != -1) |
120 | return rtc_dev_fd; | |
121 | ||
5d1f6bae | 122 | /* --rtc option has been given */ |
336f7c5f SK |
123 | if (ctl->rtc_dev_name) { |
124 | rtc_dev_name = ctl->rtc_dev_name; | |
27f9db17 | 125 | rtc_dev_fd = open(rtc_dev_name, O_RDONLY); |
336f7c5f | 126 | } else { |
067b6028 | 127 | for (i = 0; i < ARRAY_SIZE(fls); i++) { |
de4568f7 | 128 | if (ctl->verbose) |
067b6028 SK |
129 | printf(_("Trying to open: %s\n"), fls[i]); |
130 | rtc_dev_fd = open(fls[i], O_RDONLY); | |
27f9db17 | 131 | |
ef71b8f1 SK |
132 | if (rtc_dev_fd < 0 |
133 | && (errno == ENOENT || errno == ENODEV)) | |
27f9db17 | 134 | continue; |
067b6028 | 135 | rtc_dev_name = fls[i]; |
27f9db17 KZ |
136 | break; |
137 | } | |
138 | if (rtc_dev_fd < 0) | |
139 | rtc_dev_name = *fls; /* default for error messages */ | |
5d1f6bae | 140 | } |
926ffe74 | 141 | if (rtc_dev_fd != -1) |
27f9db17 KZ |
142 | atexit(close_rtc); |
143 | return rtc_dev_fd; | |
63cccae4 KZ |
144 | } |
145 | ||
336f7c5f | 146 | static int open_rtc_or_exit(const struct hwclock_control *ctl) |
ef71b8f1 | 147 | { |
336f7c5f | 148 | int rtc_fd = open_rtc(ctl); |
63cccae4 KZ |
149 | |
150 | if (rtc_fd < 0) { | |
067b6028 | 151 | warn(_("cannot open rtc device")); |
c47a8f2a | 152 | hwclock_exit(ctl, EXIT_FAILURE); |
364cda48 KZ |
153 | } |
154 | return rtc_fd; | |
155 | } | |
156 | ||
ef71b8f1 SK |
157 | static int do_rtc_read_ioctl(int rtc_fd, struct tm *tm) |
158 | { | |
364cda48 KZ |
159 | int rc = -1; |
160 | char *ioctlname; | |
7eda085c | 161 | #ifdef __sparc__ |
364cda48 KZ |
162 | /* some but not all sparcs use a different ioctl and struct */ |
163 | struct sparc_rtc_time stm; | |
fc35f2db SK |
164 | #endif |
165 | ||
166 | ioctlname = "RTC_RD_TIME"; | |
167 | rc = ioctl(rtc_fd, RTC_RD_TIME, tm); | |
364cda48 | 168 | |
fc35f2db SK |
169 | #ifdef __sparc__ |
170 | if (rc == -1) { /* sparc sbus */ | |
171 | ioctlname = "RTCGET"; | |
172 | rc = ioctl(rtc_fd, RTCGET, &stm); | |
173 | if (rc == 0) { | |
174 | tm->tm_sec = stm.sec; | |
175 | tm->tm_min = stm.min; | |
176 | tm->tm_hour = stm.hour; | |
177 | tm->tm_mday = stm.dom; | |
178 | tm->tm_mon = stm.month - 1; | |
179 | tm->tm_year = stm.year - 1900; | |
180 | tm->tm_wday = stm.dow - 1; | |
181 | tm->tm_yday = -1; /* day in the year */ | |
182 | } | |
364cda48 | 183 | } |
7eda085c | 184 | #endif |
fc35f2db | 185 | |
364cda48 | 186 | if (rc == -1) { |
111c05d3 SK |
187 | warn(_("ioctl(%s) to %s to read the time failed"), |
188 | ioctlname, rtc_dev_name); | |
cdedde03 | 189 | return -1; |
364cda48 KZ |
190 | } |
191 | ||
ef71b8f1 | 192 | tm->tm_isdst = -1; /* don't know whether it's dst */ |
364cda48 KZ |
193 | return 0; |
194 | } | |
7eda085c | 195 | |
ef71b8f1 | 196 | /* |
0411a57e WP |
197 | * Wait for the top of a clock tick by reading /dev/rtc in a busy loop |
198 | * until we see it. This function is used for rtc drivers without ioctl | |
199 | * interrupts. This is typical on an Alpha, where the Hardware Clock | |
200 | * interrupts are used by the kernel for the system clock, so aren't at | |
201 | * the user's disposal. | |
ef71b8f1 | 202 | */ |
336f7c5f SK |
203 | static int busywait_for_rtc_clock_tick(const struct hwclock_control *ctl, |
204 | const int rtc_fd) | |
ef71b8f1 SK |
205 | { |
206 | struct tm start_time; | |
207 | /* The time when we were called (and started waiting) */ | |
208 | struct tm nowtime; | |
209 | int rc; | |
210 | struct timeval begin, now; | |
211 | ||
de4568f7 | 212 | if (ctl->verbose) { |
0411a57e WP |
213 | printf("ioctl(%d, RTC_UIE_ON, 0): %s\n", |
214 | rtc_fd, strerror(errno)); | |
ef71b8f1 SK |
215 | printf(_("Waiting in loop for time from %s to change\n"), |
216 | rtc_dev_name); | |
0411a57e | 217 | } |
ef71b8f1 | 218 | |
4a6f658c WP |
219 | if (do_rtc_read_ioctl(rtc_fd, &start_time)) |
220 | return 1; | |
ef71b8f1 SK |
221 | |
222 | /* | |
223 | * Wait for change. Should be within a second, but in case | |
224 | * something weird happens, we have a time limit (1.5s) on this loop | |
225 | * to reduce the impact of this failure. | |
226 | */ | |
f78a9021 | 227 | gettime_monotonic(&begin); |
ef71b8f1 SK |
228 | do { |
229 | rc = do_rtc_read_ioctl(rtc_fd, &nowtime); | |
230 | if (rc || start_time.tm_sec != nowtime.tm_sec) | |
231 | break; | |
f78a9021 | 232 | gettime_monotonic(&now); |
ef71b8f1 | 233 | if (time_diff(now, begin) > 1.5) { |
111c05d3 | 234 | warnx(_("Timed out waiting for time change.")); |
4a6f658c | 235 | return 1; |
ef71b8f1 SK |
236 | } |
237 | } while (1); | |
238 | ||
239 | if (rc) | |
4a6f658c WP |
240 | return 1; |
241 | return 0; | |
7eda085c KZ |
242 | } |
243 | ||
ef71b8f1 SK |
244 | /* |
245 | * Same as synchronize_to_clock_tick(), but just for /dev/rtc. | |
246 | */ | |
336f7c5f | 247 | static int synchronize_to_clock_tick_rtc(const struct hwclock_control *ctl) |
ef71b8f1 SK |
248 | { |
249 | int rtc_fd; /* File descriptor of /dev/rtc */ | |
4bfd519e | 250 | int ret = 1; |
ef71b8f1 | 251 | |
336f7c5f | 252 | rtc_fd = open_rtc(ctl); |
ef71b8f1 | 253 | if (rtc_fd == -1) { |
067b6028 | 254 | warn(_("cannot open rtc device")); |
4bfd519e | 255 | return ret; |
ef71b8f1 | 256 | } else { |
ef71b8f1 | 257 | /* Turn on update interrupts (one per second) */ |
0dcb713f KZ |
258 | int rc = ioctl(rtc_fd, RTC_UIE_ON, 0); |
259 | ||
4bfd519e | 260 | if (rc != -1) { |
ef71b8f1 SK |
261 | /* |
262 | * Just reading rtc_fd fails on broken hardware: no | |
263 | * update interrupt comes and a bootscript with a | |
264 | * hwclock call hangs | |
265 | */ | |
266 | fd_set rfds; | |
267 | struct timeval tv; | |
268 | ||
269 | /* | |
72fc8ca0 | 270 | * Wait up to ten seconds for the next update |
ef71b8f1 SK |
271 | * interrupt |
272 | */ | |
273 | FD_ZERO(&rfds); | |
274 | FD_SET(rtc_fd, &rfds); | |
72fc8ca0 | 275 | tv.tv_sec = 10; |
ef71b8f1 SK |
276 | tv.tv_usec = 0; |
277 | rc = select(rtc_fd + 1, &rfds, NULL, NULL, &tv); | |
4bfd519e SK |
278 | if (0 < rc) |
279 | ret = 0; | |
efc4eaf4 | 280 | else if (rc == 0) { |
bbf12e45 WP |
281 | warnx(_("select() to %s to wait for clock tick timed out"), |
282 | rtc_dev_name); | |
efc4eaf4 | 283 | } else |
4bfd519e SK |
284 | warn(_("select() to %s to wait for clock tick failed"), |
285 | rtc_dev_name); | |
ef71b8f1 SK |
286 | /* Turn off update interrupts */ |
287 | rc = ioctl(rtc_fd, RTC_UIE_OFF, 0); | |
288 | if (rc == -1) | |
5724b74d | 289 | warn(_("ioctl() to %s to turn off update interrupts failed"), |
111c05d3 | 290 | rtc_dev_name); |
800e4477 | 291 | } else if (errno == ENOTTY || errno == EINVAL) { |
0411a57e | 292 | /* rtc ioctl interrupts are unimplemented */ |
4bfd519e SK |
293 | ret = busywait_for_rtc_clock_tick(ctl, rtc_fd); |
294 | } else | |
0411a57e WP |
295 | warn(_("ioctl(%d, RTC_UIE_ON, 0) to %s failed"), |
296 | rtc_fd, rtc_dev_name); | |
ef71b8f1 SK |
297 | } |
298 | return ret; | |
7eda085c KZ |
299 | } |
300 | ||
336f7c5f SK |
301 | static int read_hardware_clock_rtc(const struct hwclock_control *ctl, |
302 | struct tm *tm) | |
ef71b8f1 | 303 | { |
55a4a75c | 304 | int rtc_fd, rc; |
7eda085c | 305 | |
336f7c5f | 306 | rtc_fd = open_rtc_or_exit(ctl); |
7eda085c | 307 | |
364cda48 | 308 | /* Read the RTC time/date, return answer via tm */ |
55a4a75c | 309 | rc = do_rtc_read_ioctl(rtc_fd, tm); |
7eda085c | 310 | |
55a4a75c | 311 | return rc; |
7eda085c KZ |
312 | } |
313 | ||
ef71b8f1 SK |
314 | /* |
315 | * Set the Hardware Clock to the broken down time <new_broken_time>. Use | |
316 | * ioctls to "rtc" device /dev/rtc. | |
317 | */ | |
336f7c5f SK |
318 | static int set_hardware_clock_rtc(const struct hwclock_control *ctl, |
319 | const struct tm *new_broken_time) | |
ef71b8f1 | 320 | { |
364cda48 KZ |
321 | int rc = -1; |
322 | int rtc_fd; | |
323 | char *ioctlname; | |
324 | ||
336f7c5f | 325 | rtc_fd = open_rtc_or_exit(ctl); |
63cccae4 | 326 | |
fc35f2db SK |
327 | ioctlname = "RTC_SET_TIME"; |
328 | rc = ioctl(rtc_fd, RTC_SET_TIME, new_broken_time); | |
329 | ||
7eda085c | 330 | #ifdef __sparc__ |
fc35f2db | 331 | if (rc == -1) { /* sparc sbus */ |
364cda48 KZ |
332 | struct sparc_rtc_time stm; |
333 | ||
334 | stm.sec = new_broken_time->tm_sec; | |
335 | stm.min = new_broken_time->tm_min; | |
336 | stm.hour = new_broken_time->tm_hour; | |
337 | stm.dom = new_broken_time->tm_mday; | |
338 | stm.month = new_broken_time->tm_mon + 1; | |
339 | stm.year = new_broken_time->tm_year + 1900; | |
340 | stm.dow = new_broken_time->tm_wday + 1; | |
341 | ||
342 | ioctlname = "RTCSET"; | |
343 | rc = ioctl(rtc_fd, RTCSET, &stm); | |
344 | } | |
7eda085c | 345 | #endif |
364cda48 KZ |
346 | |
347 | if (rc == -1) { | |
c9a86ff6 | 348 | warn(_("ioctl(%s) to %s to set the time failed"), |
111c05d3 | 349 | ioctlname, rtc_dev_name); |
c47a8f2a | 350 | hwclock_exit(ctl, EXIT_FAILURE); |
364cda48 KZ |
351 | } |
352 | ||
de4568f7 | 353 | if (ctl->verbose) |
364cda48 KZ |
354 | printf(_("ioctl(%s) was successful.\n"), ioctlname); |
355 | ||
364cda48 | 356 | return 0; |
7eda085c KZ |
357 | } |
358 | ||
ef71b8f1 SK |
359 | static int get_permissions_rtc(void) |
360 | { | |
7eda085c KZ |
361 | return 0; |
362 | } | |
363 | ||
df4f1a66 KZ |
364 | static const char *get_device_path(void) |
365 | { | |
366 | return rtc_dev_name; | |
367 | } | |
368 | ||
bd078689 | 369 | static struct clock_ops rtc_interface = { |
8f729d60 | 370 | N_("Using the rtc interface to the clock."), |
7eda085c KZ |
371 | get_permissions_rtc, |
372 | read_hardware_clock_rtc, | |
373 | set_hardware_clock_rtc, | |
374 | synchronize_to_clock_tick_rtc, | |
df4f1a66 | 375 | get_device_path, |
7eda085c KZ |
376 | }; |
377 | ||
378 | /* return &rtc if /dev/rtc can be opened, NULL otherwise */ | |
336f7c5f | 379 | struct clock_ops *probe_for_rtc_clock(const struct hwclock_control *ctl) |
ef71b8f1 | 380 | { |
bd078689 SK |
381 | const int rtc_fd = open_rtc(ctl); |
382 | ||
383 | if (rtc_fd < 0) | |
384 | return NULL; | |
385 | return &rtc_interface; | |
7eda085c KZ |
386 | } |
387 | ||
bd078689 | 388 | #ifdef __alpha__ |
ef71b8f1 SK |
389 | /* |
390 | * Get the Hardware Clock epoch setting from the kernel. | |
391 | */ | |
af68bd01 | 392 | int get_epoch_rtc(const struct hwclock_control *ctl, unsigned long *epoch_p) |
ef71b8f1 SK |
393 | { |
394 | int rtc_fd; | |
395 | ||
336f7c5f | 396 | rtc_fd = open_rtc(ctl); |
ef71b8f1 | 397 | if (rtc_fd < 0) { |
cbc36f79 | 398 | warn(_("cannot open %s"), rtc_dev_name); |
ef71b8f1 SK |
399 | return 1; |
400 | } | |
401 | ||
402 | if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) { | |
f613c3c2 WP |
403 | warn(_("ioctl(%d, RTC_EPOCH_READ, epoch_p) to %s failed"), |
404 | rtc_fd, rtc_dev_name); | |
ef71b8f1 SK |
405 | return 1; |
406 | } | |
7eda085c | 407 | |
de4568f7 | 408 | if (ctl->verbose) |
f613c3c2 WP |
409 | printf(_("ioctl(%d, RTC_EPOCH_READ, epoch_p) to %s succeeded.\n"), |
410 | rtc_fd, rtc_dev_name); | |
7eda085c | 411 | |
ef71b8f1 | 412 | return 0; |
7eda085c KZ |
413 | } |
414 | ||
ef71b8f1 SK |
415 | /* |
416 | * Set the Hardware Clock epoch in the kernel. | |
417 | */ | |
336f7c5f | 418 | int set_epoch_rtc(const struct hwclock_control *ctl) |
ef71b8f1 SK |
419 | { |
420 | int rtc_fd; | |
f7599b4f | 421 | unsigned long epoch; |
ef71b8f1 | 422 | |
f7599b4f WP |
423 | epoch = strtoul(ctl->epoch_option, NULL, 10); |
424 | ||
425 | /* There were no RTC clocks before 1900. */ | |
426 | if (epoch < 1900 || epoch == ULONG_MAX) { | |
427 | warnx(_("invalid epoch '%s'."), ctl->epoch_option); | |
ef71b8f1 SK |
428 | return 1; |
429 | } | |
430 | ||
336f7c5f | 431 | rtc_fd = open_rtc(ctl); |
ef71b8f1 | 432 | if (rtc_fd < 0) { |
cbc36f79 | 433 | warn(_("cannot open %s"), rtc_dev_name); |
ef71b8f1 SK |
434 | return 1; |
435 | } | |
7eda085c | 436 | |
f7599b4f | 437 | if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) { |
f613c3c2 WP |
438 | warn(_("ioctl(%d, RTC_EPOCH_SET, %lu) to %s failed"), |
439 | rtc_fd, epoch, rtc_dev_name); | |
ef71b8f1 SK |
440 | return 1; |
441 | } | |
7eda085c | 442 | |
de4568f7 | 443 | if (ctl->verbose) |
f613c3c2 WP |
444 | printf(_("ioctl(%d, RTC_EPOCH_SET, %lu) to %s succeeded.\n"), |
445 | rtc_fd, epoch, rtc_dev_name); | |
446 | ||
ef71b8f1 | 447 | return 0; |
7eda085c | 448 | } |
bd078689 | 449 | #endif /* __alpha__ */ |