]>
Commit | Line | Data |
---|---|---|
f9d1edda AM |
1 | diff -urN util-linux-2.11n.org/clock-ppc.c util-linux-2.11n/clock-ppc.c |
2 | --- util-linux-2.11n.org/clock-ppc.c Thu Jan 1 01:00:00 1970 | |
3 | +++ util-linux-2.11n/clock-ppc.c Thu Mar 7 21:15:39 2002 | |
f097cc42 | 4 | @@ -0,0 +1,472 @@ |
f9d1edda AM |
5 | +#include <stdio.h> |
6 | +#include <stdlib.h> | |
7 | +#include <string.h> | |
8 | +#include <errno.h> | |
9 | +#include <unistd.h> | |
10 | + | |
11 | +#include <time.h> | |
12 | +#include <fcntl.h> | |
13 | +#include <getopt.h> | |
14 | +#include <sys/time.h> | |
15 | + | |
9f957f60 | 16 | +#include <linux/version.h> |
17 | + | |
18 | +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) | |
19 | +#include <asm/cuda.h> | |
20 | +#else | |
f9d1edda | 21 | +#include <linux/cuda.h> |
9f957f60 | 22 | +#endif |
f9d1edda AM |
23 | + |
24 | +/* | |
25 | + * Adapted for Power Macintosh by Paul Mackerras. | |
26 | + */ | |
27 | + | |
28 | +/* V1.0 | |
29 | + * CMOS clock manipulation - Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992 | |
30 | + * | |
31 | + * clock [-u] -r - read cmos clock | |
32 | + * clock [-u] -w - write cmos clock from system time | |
33 | + * clock [-u] -s - set system time from cmos clock | |
34 | + * clock [-u] -a - set system time from cmos clock, adjust the time to | |
35 | + * correct for systematic error, and put it back to the cmos. | |
36 | + * -u indicates cmos clock is kept in universal time | |
37 | + * | |
38 | + * The program is designed to run setuid, since we need to be able to | |
39 | + * write to the CUDA. | |
40 | + * | |
41 | + ********************* | |
42 | + * V1.1 | |
43 | + * Modified for clock adjustments - Rob Hooft, hooft@chem.ruu.nl, Nov 1992 | |
44 | + * Also moved error messages to stderr. The program now uses getopt. | |
45 | + * Changed some exit codes. Made 'gcc 2.3 -Wall' happy. | |
46 | + * | |
47 | + * I think a small explanation of the adjustment routine should be given | |
48 | + * here. The problem with my machine is that its CMOS clock is 10 seconds | |
49 | + * per day slow. With this version of clock.c, and my '/etc/rc.local' | |
50 | + * reading '/etc/clock -au' instead of '/etc/clock -u -s', this error | |
51 | + * is automatically corrected at every boot. | |
52 | + * | |
53 | + * To do this job, the program reads and writes the file '/etc/adjtime' | |
54 | + * to determine the correction, and to save its data. In this file are | |
55 | + * three numbers: | |
56 | + * | |
57 | + * 1) the correction in seconds per day (So if your clock runs 5 | |
58 | + * seconds per day fast, the first number should read -5.0) | |
59 | + * 2) the number of seconds since 1/1/1970 the last time the program was | |
60 | + * used. | |
61 | + * 3) the remaining part of a second which was leftover after the last | |
62 | + * adjustment | |
63 | + * | |
64 | + * Installation and use of this program: | |
65 | + * | |
66 | + * a) create a file '/etc/adjtime' containing as the first and only line: | |
67 | + * '0.0 0 0.0' | |
68 | + * b) run 'clock -au' or 'clock -a', depending on whether your cmos is in | |
69 | + * universal or local time. This updates the second number. | |
70 | + * c) set your system time using the 'date' command. | |
71 | + * d) update your cmos time using 'clock -wu' or 'clock -w' | |
72 | + * e) replace the first number in /etc/adjtime by your correction. | |
73 | + * f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local' | |
74 | + * | |
75 | + * If the adjustment doesn't work for you, try contacting me by E-mail. | |
76 | + * | |
77 | + ****** | |
78 | + * V1.2 | |
79 | + * | |
80 | + * Applied patches by Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de) | |
81 | + * Patched and indented by Rob Hooft (hooft@EMBL-Heidelberg.DE) | |
82 | + * | |
83 | + * A free quote from a MAIL-message (with spelling corrections): | |
84 | + * | |
85 | + * "I found the explanation and solution for the CMOS reading 0xff problem | |
86 | + * in the 0.99pl13c (ALPHA) kernel: the RTC goes offline for a small amount | |
87 | + * of time for updating. Solution is included in the kernel source | |
88 | + * (linux/kernel/time.c)." | |
89 | + * | |
90 | + * "I modified clock.c to fix this problem and added an option (now default, | |
91 | + * look for USE_INLINE_ASM_IO) that I/O instructions are used as inline | |
92 | + * code and not via /dev/port (still possible via #undef ...)." | |
93 | + * | |
94 | + * With the new code, which is partially taken from the kernel sources, | |
95 | + * the CMOS clock handling looks much more "official". | |
96 | + * Thanks Harald (and Torsten for the kernel code)! | |
97 | + * | |
98 | + ****** | |
99 | + * V1.3 | |
100 | + * Canges from alan@spri.levels.unisa.edu.au (Alan Modra): | |
101 | + * a) Fix a few typos in comments and remove reference to making | |
102 | + * clock -u a cron job. The kernel adjusts cmos time every 11 | |
103 | + * minutes - see kernel/sched.c and kernel/time.c set_rtc_mmss(). | |
104 | + * This means we should really have a cron job updating | |
105 | + * /etc/adjtime every 11 mins (set last_time to the current time | |
106 | + * and not_adjusted to ???). | |
107 | + * b) Swapped arguments of outb() to agree with asm/io.h macro of the | |
108 | + * same name. Use outb() from asm/io.h as it's slightly better. | |
109 | + * c) Changed CMOS_READ and CMOS_WRITE to inline functions. Inserted | |
110 | + * cli()..sti() pairs in appropriate places to prevent possible | |
111 | + * errors, and changed ioperm() call to iopl() to allow cli. | |
112 | + * d) Moved some variables around to localise them a bit. | |
113 | + * e) Fixed bug with clock -ua or clock -us that cleared environment | |
114 | + * variable TZ. This fix also cured the annoying display of bogus | |
115 | + * day of week on a number of machines. (Use mktime(), ctime() | |
116 | + * rather than asctime() ) | |
117 | + * f) Use settimeofday() rather than stime(). This one is important | |
118 | + * as it sets the kernel's timezone offset, which is returned by | |
119 | + * gettimeofday(), and used for display of MSDOS and OS2 file | |
120 | + * times. | |
121 | + * g) faith@cs.unc.edu added -D flag for debugging | |
122 | + * | |
123 | + * V1.4: alan@SPRI.Levels.UniSA.Edu.Au (Alan Modra) | |
124 | + * Wed Feb 8 12:29:08 1995, fix for years > 2000. | |
125 | + * faith@cs.unc.edu added -v option to print version. | |
126 | + * | |
127 | + * August 1996 Tom Dyas (tdyas@eden.rutgers.edu) | |
128 | + * Converted to be compatible with the SPARC /dev/rtc driver. | |
129 | + * | |
130 | + */ | |
131 | + | |
132 | +#define VERSION "1.4" | |
133 | +#define ADB_PACKET 0 | |
134 | +#define CUDA_PACKET 1 | |
135 | +#define ERROR_PACKET 2 | |
136 | +#define TIMER_PACKET 3 | |
137 | +#define POWER_PACKET 4 | |
138 | +#define MACIIC_PACKET 5 | |
139 | +#define PMU_PACKET 6 | |
140 | + | |
141 | +/* Here the information for time adjustments is kept. */ | |
142 | +#define ADJPATH "/etc/adjtime" | |
143 | + | |
144 | +/* Apparently the RTC on PowerMacs stores seconds since 1 Jan 1904 */ | |
145 | +#define RTC_OFFSET 2082844800 | |
146 | + | |
147 | +/* used for debugging the code. */ | |
148 | +/*#define KEEP_OFF */ | |
149 | + | |
150 | +/* Globals */ | |
151 | +int readit = 0; | |
152 | +int adjustit = 0; | |
153 | +int writeit = 0; | |
154 | +int setit = 0; | |
155 | +int universal = 0; | |
156 | +int debug = 0; | |
157 | + | |
158 | +time_t mkgmtime(struct tm *); | |
159 | + | |
160 | +volatile void | |
161 | +usage ( void ) | |
162 | +{ | |
163 | + (void) fprintf (stderr, | |
164 | + "clock [-u] -r|w|s|a|v\n" | |
165 | + " r: read and print CMOS clock\n" | |
166 | + " w: write CMOS clock from system time\n" | |
167 | + " s, --hctosys: set system time from CMOS clock\n" | |
168 | + " a, --systohc: get system time and adjust CMOS clock\n" | |
169 | + " u, --utc: CMOS clock is in universal time\n" | |
170 | + " v: print version (" VERSION ") and exit\n" | |
171 | + ); | |
172 | + exit(EXIT_FAILURE); | |
173 | +} | |
174 | + | |
175 | +int adb_fd; | |
176 | + | |
177 | +void | |
178 | +adb_init ( void ) | |
179 | +{ | |
180 | + adb_fd = open ("/dev/adb", 2); | |
181 | + if (adb_fd < 0) | |
182 | + { | |
183 | + perror ("unable to open /dev/adb read/write : "); | |
184 | + exit(EXIT_FAILURE); | |
185 | + } | |
186 | +} | |
187 | + | |
188 | +unsigned char get_packet[2] = { (unsigned char) CUDA_PACKET, | |
189 | + (unsigned char) CUDA_GET_TIME }; | |
190 | +unsigned char set_packet[6] = { (unsigned char) CUDA_PACKET, | |
191 | + (unsigned char) CUDA_SET_TIME }; | |
192 | + | |
193 | +int | |
194 | +main (int argc, char **argv ) | |
195 | +{ | |
196 | + struct tm tm, *tmp; | |
197 | + time_t systime; | |
198 | + time_t last_time; | |
199 | + time_t clock_time; | |
200 | + int i, arg; | |
201 | + double factor; | |
202 | + double not_adjusted; | |
203 | + int adjustment = 0; | |
204 | + /* unsigned char save_control, save_freq_select; */ | |
205 | + unsigned char reply[16]; | |
206 | + | |
207 | + /* get clock-ppc to accept systohc, hctosys options like hwclock */ | |
208 | + /* this will avoid having to customize initscripts for Mandrake PPC */ | |
209 | + /* May 15 2001 - S. Benedict <sbenedict@mandrakesoft.com> */ | |
210 | + extern char *optarg; | |
211 | + | |
212 | + while ((arg = getopt (argc, argv, "rwsuaDv-:")) != -1) | |
213 | + { | |
214 | + switch (arg) | |
215 | + { | |
216 | + case 'r': | |
217 | + readit = 1; | |
218 | + break; | |
219 | + case 'w': | |
220 | + writeit = 1; | |
221 | + break; | |
222 | + case 's': | |
223 | + setit = 1; | |
224 | + break; | |
225 | + case 'u': | |
226 | + universal = 1; | |
227 | + break; | |
228 | + case 'a': | |
229 | + adjustit = 1; | |
230 | + break; | |
231 | + case 'D': | |
232 | + debug = 1; | |
233 | + break; | |
234 | + case 'v': | |
235 | + (void) fprintf( stderr, "clock " VERSION "\n" ); | |
236 | + exit(EXIT_SUCCESS); | |
237 | + case '-': | |
238 | + if (!strncmp(optarg, "systohc", 7)) { | |
239 | + adjustit = 1; | |
240 | + break; | |
241 | + } | |
242 | + if (!strncmp(optarg, "hctosys", 7)) { | |
243 | + setit = 1; | |
244 | + break; | |
245 | + } | |
246 | + if (!strncmp(optarg, "localtime", 9)) { | |
247 | + universal = 0; | |
248 | + break; | |
249 | + } | |
250 | + if (!strncmp(optarg, "utc", 3)) { | |
251 | + universal = 1; | |
252 | + break; | |
253 | + } | |
254 | + default: | |
255 | + usage (); | |
256 | + } | |
257 | + } | |
258 | + | |
259 | + /* If we are in MkLinux do not even bother trying to set the clock */ | |
260 | + if(!access("/proc/osfmach3/version", R_OK)) | |
261 | + { // We're running MkLinux | |
262 | + if ( readit | writeit | setit | adjustit ) | |
263 | + printf("You must change the clock setting in MacOS.\n"); | |
264 | + exit(0); | |
265 | + } | |
266 | + | |
267 | + if (readit + writeit + setit + adjustit > 1) | |
268 | + usage (); /* only allow one of these */ | |
269 | + | |
270 | + if (!(readit | writeit | setit | adjustit)) /* default to read */ | |
271 | + readit = 1; | |
272 | + | |
273 | + adb_init (); | |
274 | + | |
275 | + if (adjustit) | |
276 | + { /* Read adjustment parameters first */ | |
277 | + FILE *adj; | |
278 | + if ((adj = fopen (ADJPATH, "r")) == NULL) | |
279 | + { | |
280 | + perror (ADJPATH); | |
281 | + exit(EXIT_FAILURE); | |
282 | + } | |
283 | + if (fscanf (adj, "%lf %d %lf", &factor, (int *) (&last_time), | |
284 | + ¬_adjusted) < 0) | |
285 | + { | |
286 | + perror (ADJPATH); | |
287 | + exit(EXIT_FAILURE); | |
288 | + } | |
289 | + (void) fclose (adj); | |
290 | + if (debug) (void) printf( | |
291 | + "Last adjustment done at %d seconds after 1/1/1970\n", | |
292 | + (int) last_time); | |
293 | + } | |
294 | + | |
295 | + if (readit || setit || adjustit) | |
296 | + { | |
297 | + int ii; | |
298 | + | |
299 | + if (write(adb_fd, get_packet, sizeof(get_packet)) < 0) { | |
300 | + perror("write adb"); | |
301 | + exit(EXIT_FAILURE); | |
302 | + } | |
303 | + ii = (int) read(adb_fd, reply, sizeof(reply)); | |
304 | + if (ii < 0) { | |
305 | + perror("read adb"); | |
306 | + exit(EXIT_FAILURE); | |
307 | + } | |
308 | + if (ii != 7) | |
309 | + (void) fprintf(stderr, | |
310 | + "Warning: bad reply length from CUDA (%d)\n", ii); | |
311 | + clock_time = (time_t) ((reply[3] << 24) + (reply[4] << 16) | |
312 | + + (reply[5] << 8)) + (time_t) reply[6]; | |
313 | + clock_time -= RTC_OFFSET; | |
314 | + | |
315 | + if (universal) { | |
316 | + systime = clock_time; | |
317 | + } else { | |
318 | + tm = *gmtime(&clock_time); | |
319 | + (void) printf("time in rtc is %s", asctime(&tm)); | |
320 | + tm.tm_isdst = -1; /* don't know whether it's DST */ | |
321 | + systime = mktime(&tm); | |
322 | + } | |
323 | + } | |
324 | + | |
325 | + if (readit) | |
326 | + { | |
327 | + (void) printf ("%s", ctime (&systime )); | |
328 | + } | |
329 | + | |
330 | + if (setit || adjustit) | |
331 | + { | |
332 | + struct timeval tv; | |
333 | + struct timezone tz; | |
334 | + | |
335 | +/* program is designed to run setuid, be secure! */ | |
336 | + | |
337 | + if (getuid () != 0) | |
338 | + { | |
339 | + (void) fprintf (stderr, | |
340 | + "Sorry, must be root to set or adjust time\n"); | |
341 | + exit(EXIT_FAILURE); | |
342 | + } | |
343 | + | |
344 | + if (adjustit) | |
345 | + { /* the actual adjustment */ | |
346 | + double exact_adjustment; | |
347 | + | |
348 | + exact_adjustment = ((double) (systime - last_time)) | |
349 | + * factor / (24 * 60 * 60) | |
350 | + + not_adjusted; | |
351 | + if (exact_adjustment > 0.) | |
352 | + adjustment = (int) (exact_adjustment + 0.5); | |
353 | + else | |
354 | + adjustment = (int) (exact_adjustment - 0.5); | |
355 | + not_adjusted = exact_adjustment - (double) adjustment; | |
356 | + systime += adjustment; | |
357 | + if (debug) { | |
358 | + (void) printf ("Time since last adjustment is %d seconds\n", | |
359 | + (int) (systime - last_time)); | |
360 | + (void) printf ("Adjusting time by %d seconds\n", | |
361 | + adjustment); | |
362 | + (void) printf ("remaining adjustment is %.3f seconds\n", | |
363 | + not_adjusted); | |
364 | + } | |
365 | + } | |
366 | +#ifndef KEEP_OFF | |
367 | + tv.tv_sec = systime; | |
368 | + tv.tv_usec = 0; | |
369 | + tz.tz_minuteswest = timezone / 60; | |
370 | + tz.tz_dsttime = daylight; | |
371 | + | |
372 | + if (settimeofday (&tv, &tz) != 0) | |
373 | + { | |
374 | + (void) fprintf (stderr, | |
375 | + "Unable to set time -- probably you are not root\n"); | |
376 | + exit(EXIT_FAILURE); | |
377 | + } | |
378 | + | |
379 | + if (debug) { | |
380 | + (void) printf( "Called settimeofday:\n" ); | |
381 | + (void) printf( "\ttv.tv_sec = %ld, tv.tv_usec = %ld\n", | |
382 | + tv.tv_sec, tv.tv_usec ); | |
383 | + (void) printf( "\ttz.tz_minuteswest = %d, tz.tz_dsttime = %d\n", | |
384 | + tz.tz_minuteswest, tz.tz_dsttime ); | |
385 | + } | |
386 | +#endif | |
387 | + } | |
388 | + | |
389 | + if (writeit || (adjustit && adjustment != 0)) | |
390 | + { | |
391 | + systime = time (NULL); | |
392 | + | |
393 | + if (universal) { | |
394 | + clock_time = systime; | |
395 | + | |
396 | + } else { | |
397 | + tmp = localtime(&systime); | |
398 | + clock_time = mkgmtime(tmp); | |
399 | + } | |
400 | + | |
401 | + clock_time += RTC_OFFSET; | |
402 | + set_packet[2] = clock_time >> 24; | |
403 | + set_packet[3] = clock_time >> 16; | |
404 | + set_packet[4] = clock_time >> 8; | |
405 | + set_packet[5] = (unsigned char) clock_time; | |
406 | + | |
407 | + if (write(adb_fd, set_packet, sizeof(set_packet)) < 0) { | |
408 | + perror("write adb (set)"); | |
409 | + exit(EXIT_FAILURE); | |
410 | + } | |
411 | + i = (int) read(adb_fd, reply, sizeof(reply)); | |
412 | + if (debug) { | |
413 | + int j; | |
414 | + (void) printf("set reply %d bytes:", i); | |
415 | + for (j = 0; j < i; ++j) | |
416 | + (void) printf(" %.2x", (unsigned int) reply[j]); | |
417 | + (void) printf("\n"); | |
418 | + } | |
419 | + if (i != 3 || reply[1] != (unsigned char) 0) | |
420 | + (void) fprintf(stderr, "Warning: error %d setting RTC\n", | |
421 | + (int) reply[1]); | |
422 | + | |
423 | + if (debug) { | |
424 | + clock_time -= RTC_OFFSET; | |
425 | + (void) printf("set RTC to %s", asctime(gmtime(&clock_time))); | |
426 | + } | |
427 | + } | |
428 | + else | |
429 | + if (debug) (void) printf ("CMOS clock unchanged.\n"); | |
430 | + /* Save data for next 'adjustit' call */ | |
431 | + if (adjustit) | |
432 | + { | |
433 | + FILE *adj; | |
434 | + if ((adj = fopen (ADJPATH, "w")) == NULL) | |
435 | + { | |
436 | + perror (ADJPATH); | |
437 | + exit(EXIT_FAILURE); | |
438 | + } | |
439 | + (void) fprintf (adj, "%f %d %f\n", factor, (int) systime, not_adjusted); | |
440 | + (void) fclose (adj); | |
441 | + } | |
442 | + exit(EXIT_SUCCESS); | |
443 | +} | |
444 | + | |
445 | +/* Stolen from linux/arch/i386/kernel/time.c. */ | |
446 | +/* Converts Gregorian date to seconds since 1970-01-01 00:00:00. | |
447 | + * Assumes input in normal date format, i.e. 1980-12-31 23:59:59 | |
448 | + * => year=1980, mon=12, day=31, hour=23, min=59, sec=59. | |
449 | + * | |
450 | + * [For the Julian calendar (which was used in Russia before 1917, | |
451 | + * Britain & colonies before 1752, anywhere else before 1582, | |
452 | + * and is still in use by some communities) leave out the | |
453 | + * -year/100+year/400 terms, and add 10.] | |
454 | + * | |
455 | + * This algorithm was first published by Gauss (I think). | |
456 | + * | |
457 | + * WARNING: this function will overflow on 2106-02-07 06:28:16 on | |
458 | + * machines were long is 32-bit! (However, as time_t is signed, we | |
459 | + * will already get problems at other places on 2038-01-19 03:14:08) | |
460 | + */ | |
461 | +time_t mkgmtime(struct tm *tm) | |
462 | +{ | |
463 | + int mon = tm->tm_mon + 1; | |
464 | + int year = tm->tm_year + 1900; | |
465 | + | |
466 | + if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */ | |
467 | + mon += 12; /* Puts Feb last since it has leap day */ | |
468 | + year -= 1; | |
469 | + } | |
470 | + return ((( | |
471 | + (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12) + | |
472 | + tm->tm_mday + year*365 - 719499 | |
473 | + )*24 + tm->tm_hour /* now have hours */ | |
474 | + )*60 + tm->tm_min /* now have minutes */ | |
475 | + )*60 + tm->tm_sec; /* finally seconds */ | |
476 | +} |