1 /* readclock - read the real time clock Authors: T. Holm & E. Froese
3 * Changed to be user-space driver.
6 /************************************************************************/
10 /* Read the clock value from the 64 byte CMOS RAM */
11 /* area, then set system time. */
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. */
19 /* The clock RAM values are decoded and fed to mktime */
20 /* to make a time_t value, then stime(2) is called. */
24 /* If the machine ID does not match 0xFC or 0xF8 (no */
27 /* If the machine ID is 0xFC or 0xF8 and /dev/mem */
28 /* is missing, or cannot be accessed. */
30 /* If the RTC reports errors in the CMOS RAM. */
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>
48 #include <minix/type.h>
49 #include <minix/const.h>
50 #include <minix/syslib.h>
51 #include <minix/sysutil.h>
52 #include <minix/com.h>
53 #include <machine/cmos.h>
54 #include <sys/svrctl.h>
56 int nflag
= 0; /* Tell what, but don't do it. */
57 int wflag
= 0; /* Set the CMOS clock. */
58 int Wflag
= 0; /* Also set the CMOS clock register bits. */
59 int y2kflag
= 0; /* Interpret 1980 as 2000 for clock with Y2K bug. */
61 #define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */
63 #define PC_AT 0xFC /* Machine ID byte for PC/AT,
64 PC/XT286, and PS/2 Models 50, 60 */
65 #define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */
67 /* Manufacturers usually use the ID value of the IBM model they emulate.
68 * However some manufacturers, notably HP and COMPAQ, have had different
71 * Machine ID byte information source:
72 * _The Programmer's PC Sourcebook_ by Thom Hogan,
73 * published by Microsoft Press
77 void get_time(struct tm
*t
);
78 int read_register(int reg_addr
);
79 void set_time(struct tm
*t
);
80 void write_register(int reg_addr
, int value
);
81 int bcd_to_dec(int n
);
82 int dec_to_bcd(int n
);
85 /* SEF functions and variables. */
86 static void sef_local_startup(void);
88 int main(int argc
, char **argv
)
96 unsigned char mach_id
, cmos_state
;
98 /* SEF local startup. */
99 env_setargs(argc
, argv
);
102 if((s
=sys_readbios(MACH_ID_ADDR
, &mach_id
, sizeof(mach_id
))) != OK
) {
103 printf("readclock: sys_readbios failed: %d.\n", s
);
107 if (mach_id
!= PS_386
&& mach_id
!= PC_AT
) {
108 errmsg("Machine ID unknown." );
109 printf("Machine ID byte = %02x\n", mach_id
);
114 cmos_state
= read_register(CMOS_STATUS
);
116 if (cmos_state
& (CS_LOST_POWER
| CS_BAD_CHKSUM
| CS_BAD_TIME
)) {
117 errmsg( "CMOS RAM error(s) found..." );
118 printf("CMOS state = 0x%02x\n", cmos_state
);
120 if (cmos_state
& CS_LOST_POWER
)
121 errmsg( "RTC lost power. Reset CMOS RAM with SETUP." );
122 if (cmos_state
& CS_BAD_CHKSUM
)
123 errmsg( "CMOS RAM checksum is bad. Run SETUP." );
124 if (cmos_state
& CS_BAD_TIME
)
125 errmsg( "Time invalid in CMOS RAM. Reset clock." );
129 /* Process options. */
133 if (*p
++ != '-') usage();
137 case 'n': nflag
= 1; break;
138 case 'w': wflag
= 1; break;
139 case 'W': Wflag
= 1; break;
140 case '2': y2kflag
= 1; break;
146 if (Wflag
) wflag
= 1; /* -W implies -w */
148 /* Read the CMOS real time clock. */
149 for (i
= 0; i
< 10; i
++) {
153 time1
.tm_isdst
= -1; /* Do timezone calculations. */
156 rtc
= mktime(&time1
); /* Transform to a time_t. */
157 if (rtc
!= -1) break;
160 "readclock: Invalid time read from CMOS RTC: %d-%02d-%02d %02d:%02d:%02d\n",
161 time2
.tm_year
+1900, time2
.tm_mon
+1, time2
.tm_mday
,
162 time2
.tm_hour
, time2
.tm_min
, time2
.tm_sec
);
165 if (i
== 10) exit(1);
168 /* Set system time. */
170 printf("stime(%lu)\n", (unsigned long) rtc
);
172 if (stime(&rtc
) < 0) {
173 errmsg( "Not allowed to set time." );
177 tmnow
= *localtime(&rtc
);
178 if (strftime(date
, sizeof(date
),
179 "%a %b %d %H:%M:%S %Z %Y", &tmnow
) != 0) {
180 if (date
[8] == '0') date
[8]= ' ';
181 printf("%s\n", date
);
184 /* Set the CMOS clock to the system time. */
185 tmnow
= *localtime(&now
);
187 printf("%04d-%02d-%02d %02d:%02d:%02d\n",
188 tmnow
.tm_year
+ 1900,
201 /*===========================================================================*
202 * sef_local_startup *
203 *===========================================================================*/
204 static void sef_local_startup()
206 /* Let SEF perform startup. */
212 static char *prompt
= "readclock: ";
214 printf("%s%s\n", prompt
, s
);
219 /***********************************************************************/
221 /* get_time( time ) */
223 /* Update the structure pointed to by time with the current time */
224 /* as read from CMOS RAM of the RTC. */
225 /* If necessary, the time is converted into a binary format before */
226 /* being stored in the structure. */
228 /***********************************************************************/
230 void get_time(struct tm
*t
)
238 /* Clock update in progress? */
239 if (read_register(RTC_REG_A
) & RTC_A_UIP
) continue;
241 t
->tm_sec
= read_register(RTC_SEC
);
242 if (t
->tm_sec
!= osec
) {
243 /* Seconds changed. First from -1, then because the
244 * clock ticked, which is what we're waiting for to
245 * get a precise reading.
252 /* Read the other registers. */
253 t
->tm_min
= read_register(RTC_MIN
);
254 t
->tm_hour
= read_register(RTC_HOUR
);
255 t
->tm_mday
= read_register(RTC_MDAY
);
256 t
->tm_mon
= read_register(RTC_MONTH
);
257 t
->tm_year
= read_register(RTC_YEAR
);
260 } while (read_register(RTC_SEC
) != t
->tm_sec
261 || read_register(RTC_MIN
) != t
->tm_min
262 || read_register(RTC_HOUR
) != t
->tm_hour
263 || read_register(RTC_MDAY
) != t
->tm_mday
264 || read_register(RTC_MONTH
) != t
->tm_mon
265 || read_register(RTC_YEAR
) != t
->tm_year
);
267 if ((read_register(RTC_REG_B
) & RTC_B_DM_BCD
) == 0) {
268 /* Convert BCD to binary (default RTC mode). */
269 t
->tm_year
= bcd_to_dec(t
->tm_year
);
270 t
->tm_mon
= bcd_to_dec(t
->tm_mon
);
271 t
->tm_mday
= bcd_to_dec(t
->tm_mday
);
272 t
->tm_hour
= bcd_to_dec(t
->tm_hour
);
273 t
->tm_min
= bcd_to_dec(t
->tm_min
);
274 t
->tm_sec
= bcd_to_dec(t
->tm_sec
);
276 t
->tm_mon
--; /* Counts from 0. */
278 /* Correct the year, good until 2080. */
279 if (t
->tm_year
< 80) t
->tm_year
+= 100;
282 /* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */
283 if (t
->tm_year
< 100) t
->tm_year
+= 20;
288 int read_register(int reg_addr
)
292 if(sys_outb(RTC_INDEX
, reg_addr
) != OK
) {
293 printf("cmos: outb failed of %x\n", RTC_INDEX
);
296 if(sys_inb(RTC_IO
, &r
) != OK
) {
297 printf("cmos: inb failed of %x (index %x) failed\n", RTC_IO
, reg_addr
);
305 /***********************************************************************/
307 /* set_time( time ) */
309 /* Set the CMOS RTC to the time found in the structure. */
311 /***********************************************************************/
313 void set_time(struct tm
*t
)
318 /* Set A and B registers to their proper values according to the AT
319 * reference manual. (For if it gets messed up, but the BIOS doesn't
322 write_register(RTC_REG_A
, RTC_A_DV_OK
| RTC_A_RS_DEF
);
323 write_register(RTC_REG_B
, RTC_B_24
);
326 /* Inhibit updates. */
327 regB
= read_register(RTC_REG_B
);
328 write_register(RTC_REG_B
, regB
| RTC_B_SET
);
330 t
->tm_mon
++; /* Counts from 1. */
333 /* Set the clock back 20 years to avoid Y2K bug, good until 2020. */
334 if (t
->tm_year
>= 100) t
->tm_year
-= 20;
337 if ((regB
& 0x04) == 0) {
338 /* Convert binary to BCD (default RTC mode) */
339 t
->tm_year
= dec_to_bcd(t
->tm_year
% 100);
340 t
->tm_mon
= dec_to_bcd(t
->tm_mon
);
341 t
->tm_mday
= dec_to_bcd(t
->tm_mday
);
342 t
->tm_hour
= dec_to_bcd(t
->tm_hour
);
343 t
->tm_min
= dec_to_bcd(t
->tm_min
);
344 t
->tm_sec
= dec_to_bcd(t
->tm_sec
);
346 write_register(RTC_YEAR
, t
->tm_year
);
347 write_register(RTC_MONTH
, t
->tm_mon
);
348 write_register(RTC_MDAY
, t
->tm_mday
);
349 write_register(RTC_HOUR
, t
->tm_hour
);
350 write_register(RTC_MIN
, t
->tm_min
);
351 write_register(RTC_SEC
, t
->tm_sec
);
353 /* Stop the clock. */
354 regA
= read_register(RTC_REG_A
);
355 write_register(RTC_REG_A
, regA
| RTC_A_DV_STOP
);
357 /* Allow updates and restart the clock. */
358 write_register(RTC_REG_B
, regB
);
359 write_register(RTC_REG_A
, regA
);
363 void write_register(int reg_addr
, int value
)
365 if(sys_outb(RTC_INDEX
, reg_addr
) != OK
) {
366 printf("cmos: outb failed of %x\n", RTC_INDEX
);
369 if(sys_outb(RTC_IO
, value
) != OK
) {
370 printf("cmos: outb failed of %x (index %x)\n", RTC_IO
, reg_addr
);
375 int bcd_to_dec(int n
)
377 return ((n
>> 4) & 0x0F) * 10 + (n
& 0x0F);
380 int dec_to_bcd(int n
)
382 return ((n
/ 10) << 4) | (n
% 10);
387 printf("Usage: readclock [-nwW2]\n");