supply instance to 2nd at_wini instance.
[minix3.git] / drivers / readclock / readclock.c
blobb2cb6b2dace8a5ae70bfc1e22756680a63538bbf
1 /* readclock - read the real time clock Authors: T. Holm & E. Froese
3 * Changed to be user-space driver.
4 */
6 /************************************************************************/
7 /* */
8 /* readclock.c */
9 /* */
10 /* Read the clock value from the 64 byte CMOS RAM */
11 /* area, then set system time. */
12 /* */
13 /* If the machine ID byte is 0xFC or 0xF8, the device */
14 /* /dev/mem exists and can be opened for reading, */
15 /* and no errors in the CMOS RAM are reported by the */
16 /* RTC, then the time is read from the clock RAM */
17 /* area maintained by the RTC. */
18 /* */
19 /* The clock RAM values are decoded and fed to mktime */
20 /* to make a time_t value, then stime(2) is called. */
21 /* */
22 /* This fails if: */
23 /* */
24 /* If the machine ID does not match 0xFC or 0xF8 (no */
25 /* error message.) */
26 /* */
27 /* If the machine ID is 0xFC or 0xF8 and /dev/mem */
28 /* is missing, or cannot be accessed. */
29 /* */
30 /* If the RTC reports errors in the CMOS RAM. */
31 /* */
32 /************************************************************************/
33 /* origination 1987-Dec-29 efth */
34 /* robustness 1990-Oct-06 C. Sylvain */
35 /* incorp. B. Evans ideas 1991-Jul-06 C. Sylvain */
36 /* set time & calibrate 1992-Dec-17 Kees J. Bot */
37 /* clock timezone 1993-Oct-10 Kees J. Bot */
38 /* set CMOS clock 1994-Jun-12 Kees J. Bot */
39 /************************************************************************/
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <time.h>
50 #include <errno.h>
51 #include <signal.h>
52 #include <minix/type.h>
53 #include <minix/syslib.h>
54 #include <minix/com.h>
55 #include <minix/portio.h>
56 #include <ibm/cmos.h>
57 #include <sys/svrctl.h>
59 int nflag = 0; /* Tell what, but don't do it. */
60 int wflag = 0; /* Set the CMOS clock. */
61 int Wflag = 0; /* Also set the CMOS clock register bits. */
62 int y2kflag = 0; /* Interpret 1980 as 2000 for clock with Y2K bug. */
64 char clocktz[128]; /* Timezone of the clock. */
66 #define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */
68 #define PC_AT 0xFC /* Machine ID byte for PC/AT,
69 PC/XT286, and PS/2 Models 50, 60 */
70 #define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */
72 /* Manufacturers usually use the ID value of the IBM model they emulate.
73 * However some manufacturers, notably HP and COMPAQ, have had different
74 * ideas in the past.
76 * Machine ID byte information source:
77 * _The Programmer's PC Sourcebook_ by Thom Hogan,
78 * published by Microsoft Press
81 void errmsg(char *s);
82 void get_time(struct tm *t);
83 int read_register(int reg_addr);
84 void set_time(struct tm *t);
85 void write_register(int reg_addr, int value);
86 int bcd_to_dec(int n);
87 int dec_to_bcd(int n);
88 void usage(void);
90 int main(int argc, char **argv)
92 struct tm time1;
93 struct tm time2;
94 struct tm tmnow;
95 char date[64];
96 time_t now, rtc;
97 int i, s;
98 unsigned char mach_id, cmos_state;
99 struct sysgetenv sysgetenv;
101 if((s=sys_readbios(MACH_ID_ADDR, &mach_id, sizeof(mach_id))) != OK) {
102 printf("readclock: sys_readbios failed: %d.\n", s);
103 exit(1);
106 if (mach_id != PS_386 && mach_id != PC_AT) {
107 errmsg("Machine ID unknown." );
108 printf("Machine ID byte = %02x\n", mach_id );
110 exit(1);
113 cmos_state = read_register(CMOS_STATUS);
115 if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) {
116 errmsg( "CMOS RAM error(s) found..." );
117 printf("CMOS state = 0x%02x\n", cmos_state );
119 if (cmos_state & CS_LOST_POWER)
120 errmsg( "RTC lost power. Reset CMOS RAM with SETUP." );
121 if (cmos_state & CS_BAD_CHKSUM)
122 errmsg( "CMOS RAM checksum is bad. Run SETUP." );
123 if (cmos_state & CS_BAD_TIME)
124 errmsg( "Time invalid in CMOS RAM. Reset clock." );
125 exit(1);
128 /* Process options. */
129 while (argc > 1) {
130 char *p = *++argv;
132 if (*p++ != '-') usage();
134 while (*p != 0) {
135 switch (*p++) {
136 case 'n': nflag = 1; break;
137 case 'w': wflag = 1; break;
138 case 'W': Wflag = 1; break;
139 case '2': y2kflag = 1; break;
140 default: usage();
143 argc--;
145 if (Wflag) wflag = 1; /* -W implies -w */
147 #if 0
148 /* The hardware clock may run in a different time zone, likely GMT or
149 * winter time. Select that time zone.
151 strcpy(clocktz, "TZ=");
152 sysgetenv.key = "TZ";
153 sysgetenv.keylen = 2+1;
154 sysgetenv.val = clocktz+3;
155 sysgetenv.vallen = sizeof(clocktz)-3;
156 if (svrctl(SYSGETENV, &sysgetenv) == 0) {
157 putenv(clocktz);
158 tzset();
160 #endif
162 /* Read the CMOS real time clock. */
163 for (i = 0; i < 10; i++) {
164 get_time(&time1);
165 now = time(NULL);
167 time1.tm_isdst = -1; /* Do timezone calculations. */
168 time2 = time1;
170 rtc= mktime(&time1); /* Transform to a time_t. */
171 if (rtc != -1) break;
173 printf(
174 "readclock: Invalid time read from CMOS RTC: %d-%02d-%02d %02d:%02d:%02d\n",
175 time2.tm_year+1900, time2.tm_mon+1, time2.tm_mday,
176 time2.tm_hour, time2.tm_min, time2.tm_sec);
177 sleep(5);
179 if (i == 10) exit(1);
181 if (!wflag) {
182 /* Set system time. */
183 if (nflag) {
184 printf("stime(%lu)\n", (unsigned long) rtc);
185 } else {
186 if (stime(&rtc) < 0) {
187 errmsg( "Not allowed to set time." );
188 exit(1);
191 tmnow = *localtime(&rtc);
192 if (strftime(date, sizeof(date),
193 "%a %b %d %H:%M:%S %Z %Y", &tmnow) != 0) {
194 if (date[8] == '0') date[8]= ' ';
195 printf("%s\n", date);
197 } else {
198 /* Set the CMOS clock to the system time. */
199 tmnow = *localtime(&now);
200 if (nflag) {
201 printf("%04d-%02d-%02d %02d:%02d:%02d\n",
202 tmnow.tm_year + 1900,
203 tmnow.tm_mon + 1,
204 tmnow.tm_mday,
205 tmnow.tm_hour,
206 tmnow.tm_min,
207 tmnow.tm_sec);
208 } else {
209 set_time(&tmnow);
212 exit(0);
215 void errmsg(char *s)
217 static char *prompt = "readclock: ";
219 printf("%s%s\n", prompt, s);
220 prompt = "";
224 /***********************************************************************/
225 /* */
226 /* get_time( time ) */
227 /* */
228 /* Update the structure pointed to by time with the current time */
229 /* as read from CMOS RAM of the RTC. */
230 /* If necessary, the time is converted into a binary format before */
231 /* being stored in the structure. */
232 /* */
233 /***********************************************************************/
235 int dead;
236 void timeout(int sig) { dead= 1; }
238 void get_time(struct tm *t)
240 int osec, n;
241 unsigned long i;
242 struct sigaction sa;
244 /* Start a timer to keep us from getting stuck on a dead clock. */
245 sigemptyset(&sa.sa_mask);
246 sa.sa_flags = 0;
247 sa.sa_handler = timeout;
248 sigaction(SIGALRM, &sa, NULL);
249 dead = 0;
250 alarm(5);
252 do {
253 osec = -1;
254 n = 0;
255 do {
256 if (dead) {
257 printf("readclock: CMOS clock appears dead\n");
258 exit(1);
261 /* Clock update in progress? */
262 if (read_register(RTC_REG_A) & RTC_A_UIP) continue;
264 t->tm_sec = read_register(RTC_SEC);
265 if (t->tm_sec != osec) {
266 /* Seconds changed. First from -1, then because the
267 * clock ticked, which is what we're waiting for to
268 * get a precise reading.
270 osec = t->tm_sec;
271 n++;
273 } while (n < 2);
275 /* Read the other registers. */
276 t->tm_min = read_register(RTC_MIN);
277 t->tm_hour = read_register(RTC_HOUR);
278 t->tm_mday = read_register(RTC_MDAY);
279 t->tm_mon = read_register(RTC_MONTH);
280 t->tm_year = read_register(RTC_YEAR);
282 /* Time stable? */
283 } while (read_register(RTC_SEC) != t->tm_sec
284 || read_register(RTC_MIN) != t->tm_min
285 || read_register(RTC_HOUR) != t->tm_hour
286 || read_register(RTC_MDAY) != t->tm_mday
287 || read_register(RTC_MONTH) != t->tm_mon
288 || read_register(RTC_YEAR) != t->tm_year);
290 if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) {
291 /* Convert BCD to binary (default RTC mode). */
292 t->tm_year = bcd_to_dec(t->tm_year);
293 t->tm_mon = bcd_to_dec(t->tm_mon);
294 t->tm_mday = bcd_to_dec(t->tm_mday);
295 t->tm_hour = bcd_to_dec(t->tm_hour);
296 t->tm_min = bcd_to_dec(t->tm_min);
297 t->tm_sec = bcd_to_dec(t->tm_sec);
299 t->tm_mon--; /* Counts from 0. */
301 /* Correct the year, good until 2080. */
302 if (t->tm_year < 80) t->tm_year += 100;
304 if (y2kflag) {
305 /* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */
306 if (t->tm_year < 100) t->tm_year += 20;
311 int read_register(int reg_addr)
313 u32_t r;
315 if(sys_outb(RTC_INDEX, reg_addr) != OK) {
316 printf("cmos: outb failed of %x\n", RTC_INDEX);
317 exit(1);
319 if(sys_inb(RTC_IO, &r) != OK) {
320 printf("cmos: inb failed of %x (index %x) failed\n", RTC_IO, reg_addr);
321 exit(1);
323 return r;
328 /***********************************************************************/
329 /* */
330 /* set_time( time ) */
331 /* */
332 /* Set the CMOS RTC to the time found in the structure. */
333 /* */
334 /***********************************************************************/
336 void set_time(struct tm *t)
338 int regA, regB;
340 if (Wflag) {
341 /* Set A and B registers to their proper values according to the AT
342 * reference manual. (For if it gets messed up, but the BIOS doesn't
343 * repair it.)
345 write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF);
346 write_register(RTC_REG_B, RTC_B_24);
349 /* Inhibit updates. */
350 regB= read_register(RTC_REG_B);
351 write_register(RTC_REG_B, regB | RTC_B_SET);
353 t->tm_mon++; /* Counts from 1. */
355 if (y2kflag) {
356 /* Set the clock back 20 years to avoid Y2K bug, good until 2020. */
357 if (t->tm_year >= 100) t->tm_year -= 20;
360 if ((regB & 0x04) == 0) {
361 /* Convert binary to BCD (default RTC mode) */
362 t->tm_year = dec_to_bcd(t->tm_year % 100);
363 t->tm_mon = dec_to_bcd(t->tm_mon);
364 t->tm_mday = dec_to_bcd(t->tm_mday);
365 t->tm_hour = dec_to_bcd(t->tm_hour);
366 t->tm_min = dec_to_bcd(t->tm_min);
367 t->tm_sec = dec_to_bcd(t->tm_sec);
369 write_register(RTC_YEAR, t->tm_year);
370 write_register(RTC_MONTH, t->tm_mon);
371 write_register(RTC_MDAY, t->tm_mday);
372 write_register(RTC_HOUR, t->tm_hour);
373 write_register(RTC_MIN, t->tm_min);
374 write_register(RTC_SEC, t->tm_sec);
376 /* Stop the clock. */
377 regA= read_register(RTC_REG_A);
378 write_register(RTC_REG_A, regA | RTC_A_DV_STOP);
380 /* Allow updates and restart the clock. */
381 write_register(RTC_REG_B, regB);
382 write_register(RTC_REG_A, regA);
386 void write_register(int reg_addr, int value)
388 if(sys_outb(RTC_INDEX, reg_addr) != OK) {
389 printf("cmos: outb failed of %x\n", RTC_INDEX);
390 exit(1);
392 if(sys_outb(RTC_IO, value) != OK) {
393 printf("cmos: outb failed of %x (index %x)\n", RTC_IO, reg_addr);
394 exit(1);
398 int bcd_to_dec(int n)
400 return ((n >> 4) & 0x0F) * 10 + (n & 0x0F);
403 int dec_to_bcd(int n)
405 return ((n / 10) << 4) | (n % 10);
408 void usage(void)
410 printf("Usage: readclock [-nwW2]\n");
411 exit(1);