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