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