source: trunk/minix/commands/ibm/backup.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

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