]> git.ipfire.org Git - thirdparty/util-linux.git/blob - hwclock/rtc.c
Imported from util-linux-2.10s tarball.
[thirdparty/util-linux.git] / hwclock / rtc.c
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
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
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 */
37 struct 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. */
64 struct 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
90 static int
91 do_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);
105 fprintf(stderr, _("ioctl() to %s to read the time failed.\n"),RTC_DEV);
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
123 static int
124 busywait_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)
136 printf(_("Waiting in loop for time from %s to change\n"),RTC_DEV);
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
162 static int
163 synchronize_to_clock_tick_rtc(void) {
164 /*----------------------------------------------------------------------------
165 Same as synchronize_to_clock_tick(), but just for /dev/rtc.
166 -----------------------------------------------------------------------------*/
167 int rtc_fd; /* File descriptor of /dev/rtc */
168 int ret;
169
170 rtc_fd = open(RTC_DEV,O_RDONLY);
171 if (rtc_fd == -1) {
172 outsyserr(_("open() of %s failed"),RTC_DEV);
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 */
189 if (debug)
190 printf(_("%s does not have interrupt functions. "),RTC_DEV);
191 ret = busywait_for_rtc_clock_tick(rtc_fd);
192 } else if (rc == 0) {
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) {
198 outsyserr(_("read() to %s to wait for clock tick failed"),RTC_DEV);
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)
206 outsyserr(_("ioctl() to %s to turn off update interrupts "
207 "failed"),RTC_DEV);
208 } else {
209 outsyserr(_("ioctl() to %s to turn on update interrupts "
210 "failed unexpectedly"),RTC_DEV);
211 ret = 1;
212 }
213 close(rtc_fd);
214 }
215 return ret;
216 }
217
218
219 static int
220 read_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
227 rtc_fd = open(RTC_DEV,O_RDONLY);
228 if (rtc_fd == -1) {
229 outsyserr(_("open() of %s failed"),RTC_DEV);
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
241 static int
242 set_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
250 rtc_fd = open(RTC_DEV, O_RDONLY);
251 if (rtc_fd < 0) {
252 outsyserr(_("Unable to open %s"),RTC_DEV);
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);
275 fprintf(stderr, _("ioctl() to %s to set the time failed.\n"),RTC_DEV);
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
287 static int
288 get_permissions_rtc(void) {
289 return 0;
290 }
291
292 static 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 */
301 struct clock_ops *
302 probe_for_rtc_clock(){
303 int rtc_fd = open(RTC_DEV, O_RDONLY);
304 if (rtc_fd > 0) {
305 close(rtc_fd);
306 return &rtc;
307 }
308 if (debug)
309 outsyserr(_("Open of %s failed"),RTC_DEV);
310 return NULL;
311 }
312
313
314
315 int
316 get_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
322 rtc_fd = open(RTC_DEV, O_RDONLY);
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 "
329 "file %s. This file does not exist on this system.\n"),RTC_DEV);
330 else
331 outsyserr(_("Unable to open %s"),RTC_DEV);
332 }
333 return 1;
334 }
335
336 if (ioctl(rtc_fd, RTC_EPOCH_READ, epoch_p) == -1) {
337 if (!silent)
338 outsyserr(_("ioctl(RTC_EPOCH_READ) to %s failed"),RTC_DEV);
339 close(rtc_fd);
340 return 1;
341 }
342
343 if (debug)
344 printf(_("we have read epoch %ld from %s "
345 "with RTC_EPOCH_READ ioctl.\n"), *epoch_p,RTC_DEV);
346
347 close(rtc_fd);
348 return 0;
349 }
350
351
352
353 int
354 set_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
369 rtc_fd = open(RTC_DEV, O_RDONLY);
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 "
374 "file %s. This file does not exist on this system.\n"),RTC_DEV);
375 else
376 outsyserr(_("Unable to open %s"),RTC_DEV);
377 return 1;
378 }
379
380 if (debug)
381 printf(_("setting epoch to %ld "
382 "with RTC_EPOCH_SET ioctl to %s.\n"), epoch, RTC_DEV);
383
384 if (ioctl(rtc_fd, RTC_EPOCH_SET, epoch) == -1) {
385 if (errno == EINVAL)
386 fprintf(stderr, _("The kernel device driver for %s "
387 "does not have the RTC_EPOCH_SET ioctl.\n"),RTC_DEV);
388 else
389 outsyserr(_("ioctl(RTC_EPOCH_SET) to %s failed"),RTC_DEV);
390 close(rtc_fd);
391 return 1;
392 }
393
394 close(rtc_fd);
395 return 0;
396 }