~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to hwclock/clock-ppc.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
}