root/kernel/time.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. time_init
  2. sys_time
  3. sys_stime
  4. do_gettimeoffset
  5. do_gettimeofday
  6. sys_gettimeofday
  7. warp_clock
  8. sys_settimeofday
  9. sys_adjtimex
  10. set_rtc_mmss

   1 /*
   2  *  linux/kernel/time.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  *
   6  *  This file contains the interface functions for the various
   7  *  time related system calls: time, stime, gettimeofday, settimeofday,
   8  *                             adjtime
   9  */
  10 /*
  11  * Modification history kernel/time.c
  12  * 
  13  * 02 Sep 93    Philip Gladstone
  14  *      Created file with time related functions from sched.c and adjtimex() 
  15  * 08 Oct 93    Torsten Duwe
  16  *      adjtime interface update and CMOS clock write code
  17  */
  18 
  19 #include <linux/config.h>
  20 #include <linux/errno.h>
  21 #include <linux/sched.h>
  22 #include <linux/kernel.h>
  23 #include <linux/param.h>
  24 #include <linux/string.h>
  25 
  26 #include <asm/segment.h>
  27 #include <asm/io.h>
  28 
  29 #include <linux/mc146818rtc.h>
  30 #define RTC_ALWAYS_BCD 1
  31 
  32 #include <linux/timex.h>
  33 extern struct timeval xtime;
  34 
  35 #include <linux/mktime.h>
  36 extern long kernel_mktime(struct mktime * time);
  37 
  38 void time_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  39 {
  40         struct mktime time;
  41         int i;
  42 
  43         /* checking for Update-In-Progress could be done more elegantly
  44          * (using the "update finished"-interrupt for example), but that
  45          * would require excessive testing. promise I'll do that when I find
  46          * the time.                    - Torsten
  47          */
  48         /* read RTC exactly on falling edge of update flag */
  49         for (i = 0 ; i < 1000000 ; i++) /* may take up to 1 second... */
  50                 if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)
  51                         break;
  52         for (i = 0 ; i < 1000000 ; i++) /* must try at least 2.228 ms*/
  53                 if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP))
  54                         break;
  55         do { /* Isn't this overkill ? UIP above should guarantee consistency */
  56                 time.sec = CMOS_READ(RTC_SECONDS);
  57                 time.min = CMOS_READ(RTC_MINUTES);
  58                 time.hour = CMOS_READ(RTC_HOURS);
  59                 time.day = CMOS_READ(RTC_DAY_OF_MONTH);
  60                 time.mon = CMOS_READ(RTC_MONTH);
  61                 time.year = CMOS_READ(RTC_YEAR);
  62         } while (time.sec != CMOS_READ(RTC_SECONDS));
  63         if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
  64           {
  65             BCD_TO_BIN(time.sec);
  66             BCD_TO_BIN(time.min);
  67             BCD_TO_BIN(time.hour);
  68             BCD_TO_BIN(time.day);
  69             BCD_TO_BIN(time.mon);
  70             BCD_TO_BIN(time.year);
  71           }
  72         time.mon--;
  73         xtime.tv_sec = kernel_mktime(&time);
  74       }
  75 /* 
  76  * The timezone where the local system is located.  Used as a default by some
  77  * programs who obtain this value by using gettimeofday.
  78  */
  79 struct timezone sys_tz = { 0, 0};
  80 
  81 asmlinkage int sys_time(long * tloc)
     /* [previous][next][first][last][top][bottom][index][help] */
  82 {
  83         int i, error;
  84 
  85         i = CURRENT_TIME;
  86         if (tloc) {
  87                 error = verify_area(VERIFY_WRITE, tloc, 4);
  88                 if (error)
  89                         return error;
  90                 put_fs_long(i,(unsigned long *)tloc);
  91         }
  92         return i;
  93 }
  94 
  95 asmlinkage int sys_stime(long * tptr)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97         if (!suser())
  98                 return -EPERM;
  99         cli();
 100         xtime.tv_sec = get_fs_long((unsigned long *) tptr);
 101         xtime.tv_usec = 0;
 102         time_status = TIME_BAD;
 103         time_maxerror = 0x70000000;
 104         time_esterror = 0x70000000;
 105         sti();
 106         return 0;
 107 }
 108 
 109 /* This function must be called with interrupts disabled 
 110  * It was inspired by Steve McCanne's microtime-i386 for BSD.  -- jrs
 111  * 
 112  * However, the pc-audio speaker driver changes the divisor so that
 113  * it gets interrupted rather more often - it loads 64 into the
 114  * counter rather than 11932! This has an adverse impact on
 115  * do_gettimeoffset() -- it stops working! What is also not
 116  * good is that the interval that our timer function gets called
 117  * is no longer 10.0002 msecs, but 9.9767 msec. To get around this
 118  * would require using a different timing source. Maybe someone
 119  * could use the RTC - I know that this can interrupt at frequencies
 120  * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix
 121  * it so that at startup, the timer code in sched.c would select
 122  * using either the RTC or the 8253 timer. The decision would be
 123  * based on whether there was any other device around that needed
 124  * to trample on the 8253. I'd set up the RTC to interrupt at 1024Hz,
 125  * and then do some jiggery to have a version of do_timer that 
 126  * advanced the clock by 1/1024 sec. Every time that reached over 1/100
 127  * of a second, then do all the old code. If the time was kept correct
 128  * then do_gettimeoffset could just return 0 - there is no low order
 129  * divider that can be accessed.
 130  *
 131  * Ideally, you would be able to use the RTC for the speaker driver,
 132  * but it appears that the speaker driver really needs interrupt more
 133  * often than every 120us or so.
 134  *
 135  * Anyway, this needs more thought....          pjsg (28 Aug 93)
 136  * 
 137  * If you are really that interested, you should be reading
 138  * comp.protocols.time.ntp!
 139  */
 140 
 141 #define TICK_SIZE tick
 142 
 143 static inline unsigned long do_gettimeoffset(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 144 {
 145         int count;
 146         unsigned long offset = 0;
 147 
 148         /* timer count may underflow right here */
 149         outb_p(0x00, 0x43);     /* latch the count ASAP */
 150         count = inb_p(0x40);    /* read the latched count */
 151         count |= inb(0x40) << 8;
 152         /* we know probability of underflow is always MUCH less than 1% */
 153         if (count > (LATCH - LATCH/100)) {
 154                 /* check for pending timer interrupt */
 155                 outb_p(0x0a, 0x20);
 156                 if (inb(0x20) & 1)
 157                         offset = TICK_SIZE;
 158         }
 159         count = ((LATCH-1) - count) * TICK_SIZE;
 160         count = (count + LATCH/2) / LATCH;
 161         return offset + count;
 162 }
 163 
 164 /*
 165  * This version of gettimeofday has near microsecond resolution.
 166  */
 167 static inline void do_gettimeofday(struct timeval *tv)
     /* [previous][next][first][last][top][bottom][index][help] */
 168 {
 169 #ifdef __i386__
 170         cli();
 171         *tv = xtime;
 172         tv->tv_usec += do_gettimeoffset();
 173         if (tv->tv_usec >= 1000000) {
 174                 tv->tv_usec -= 1000000;
 175                 tv->tv_sec++;
 176         }
 177         sti();
 178 #else /* not __i386__ */
 179         cli();
 180         *tv = xtime;
 181         sti();
 182 #endif /* not __i386__ */
 183 }
 184 
 185 asmlinkage int sys_gettimeofday(struct timeval *tv, struct timezone *tz)
     /* [previous][next][first][last][top][bottom][index][help] */
 186 {
 187         int error;
 188 
 189         if (tv) {
 190                 struct timeval ktv;
 191                 error = verify_area(VERIFY_WRITE, tv, sizeof *tv);
 192                 if (error)
 193                         return error;
 194                 do_gettimeofday(&ktv);
 195                 put_fs_long(ktv.tv_sec, (unsigned long *) &tv->tv_sec);
 196                 put_fs_long(ktv.tv_usec, (unsigned long *) &tv->tv_usec);
 197         }
 198         if (tz) {
 199                 error = verify_area(VERIFY_WRITE, tz, sizeof *tz);
 200                 if (error)
 201                         return error;
 202                 put_fs_long(sys_tz.tz_minuteswest, (unsigned long *) tz);
 203                 put_fs_long(sys_tz.tz_dsttime, ((unsigned long *) tz)+1);
 204         }
 205         return 0;
 206 }
 207 
 208 /*
 209  * Adjust the time obtained from the CMOS to be GMT time instead of
 210  * local time.
 211  * 
 212  * This is ugly, but preferable to the alternatives.  Otherwise we
 213  * would either need to write a program to do it in /etc/rc (and risk
 214  * confusion if the program gets run more than once; it would also be 
 215  * hard to make the program warp the clock precisely n hours)  or
 216  * compile in the timezone information into the kernel.  Bad, bad....
 217  *
 218  * XXX Currently does not adjust for daylight savings time.  May not
 219  * need to do anything, depending on how smart (dumb?) the BIOS
 220  * is.  Blast it all.... the best thing to do not depend on the CMOS
 221  * clock at all, but get the time via NTP or timed if you're on a 
 222  * network....                          - TYT, 1/1/92
 223  */
 224 inline static void warp_clock(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226         cli();
 227         xtime.tv_sec += sys_tz.tz_minuteswest * 60;
 228         sti();
 229 }
 230 
 231 /*
 232  * The first time we set the timezone, we will warp the clock so that
 233  * it is ticking GMT time instead of local time.  Presumably, 
 234  * if someone is setting the timezone then we are running in an
 235  * environment where the programs understand about timezones.
 236  * This should be done at boot time in the /etc/rc script, as
 237  * soon as possible, so that the clock can be set right.  Otherwise,
 238  * various programs will get confused when the clock gets warped.
 239  */
 240 asmlinkage int sys_settimeofday(struct timeval *tv, struct timezone *tz)
     /* [previous][next][first][last][top][bottom][index][help] */
 241 {
 242         static int      firsttime = 1;
 243 
 244         if (!suser())
 245                 return -EPERM;
 246         if (tz) {
 247                 sys_tz.tz_minuteswest = get_fs_long((unsigned long *) tz);
 248                 sys_tz.tz_dsttime = get_fs_long(((unsigned long *) tz)+1);
 249                 if (firsttime) {
 250                         firsttime = 0;
 251                         if (!tv)
 252                                 warp_clock();
 253                 }
 254         }
 255         if (tv) {
 256                 int sec, usec;
 257 
 258                 sec = get_fs_long((unsigned long *)tv);
 259                 usec = get_fs_long(((unsigned long *)tv)+1);
 260         
 261                 cli();
 262                 /* This is revolting. We need to set the xtime.tv_usec
 263                  * correctly. However, the value in this location is
 264                  * is value at the last tick.
 265                  * Discover what correction gettimeofday
 266                  * would have done, and then undo it!
 267                  */
 268                 usec -= do_gettimeoffset();
 269 
 270                 if (usec < 0)
 271                 {
 272                         usec += 1000000;
 273                         sec--;
 274                 }
 275                 xtime.tv_sec = sec;
 276                 xtime.tv_usec = usec;
 277                 time_status = TIME_BAD;
 278                 time_maxerror = 0x70000000;
 279                 time_esterror = 0x70000000;
 280                 sti();
 281         }
 282         return 0;
 283 }
 284 
 285 /* adjtimex mainly allows reading (and writing, if superuser) of
 286  * kernel time-keeping variables. used by xntpd.
 287  */
 288 asmlinkage int sys_adjtimex(struct timex *txc_p)
     /* [previous][next][first][last][top][bottom][index][help] */
 289 {
 290         long ltemp, mtemp, save_adjust;
 291         int error;
 292 
 293         /* Local copy of parameter */
 294         struct timex txc;
 295 
 296         error = verify_area(VERIFY_WRITE, txc_p, sizeof(struct timex));
 297         if (error)
 298           return error;
 299 
 300         /* Copy the user data space into the kernel copy
 301          * structure. But bear in mind that the structures
 302          * may change
 303          */
 304         memcpy_fromfs(&txc, txc_p, sizeof(struct timex));
 305 
 306         /* In order to modify anything, you gotta be super-user! */
 307         if (txc.mode && !suser())
 308                 return -EPERM;
 309 
 310         /* Now we validate the data before disabling interrupts
 311          */
 312 
 313         if (txc.mode & ADJ_OFFSET)
 314           /* Microsec field limited to -131000 .. 131000 usecs */
 315           if (txc.offset <= -(1 << (31 - SHIFT_UPDATE))
 316               || txc.offset >= (1 << (31 - SHIFT_UPDATE)))
 317             return -EINVAL;
 318 
 319         /* time_status must be in a fairly small range */
 320         if (txc.mode & ADJ_STATUS)
 321           if (txc.status < TIME_OK || txc.status > TIME_BAD)
 322             return -EINVAL;
 323 
 324         cli();
 325 
 326         /* Save for later - semantics of adjtime is to return old value */
 327         save_adjust = time_adjust;
 328 
 329         /* If there are input parameters, then process them */
 330         if (txc.mode)
 331         {
 332             if (time_status == TIME_BAD)
 333                 time_status = TIME_OK;
 334 
 335             if (txc.mode & ADJ_STATUS)
 336                 time_status = txc.status;
 337 
 338             if (txc.mode & ADJ_FREQUENCY)
 339                 time_freq = txc.frequency;
 340 
 341             if (txc.mode & ADJ_MAXERROR)
 342                 time_maxerror = txc.maxerror;
 343 
 344             if (txc.mode & ADJ_ESTERROR)
 345                 time_esterror = txc.esterror;
 346 
 347             if (txc.mode & ADJ_TIMECONST)
 348                 time_constant = txc.time_constant;
 349 
 350             if (txc.mode & ADJ_OFFSET)
 351               if (txc.mode == ADJ_OFFSET_SINGLESHOT)
 352                 {
 353                   time_adjust = txc.offset;
 354                 }
 355               else /* XXX should give an error if other bits set */
 356                 {
 357                   time_offset = txc.offset << SHIFT_UPDATE;
 358                   mtemp = xtime.tv_sec - time_reftime;
 359                   time_reftime = xtime.tv_sec;
 360                   if (mtemp > (MAXSEC+2) || mtemp < 0)
 361                     mtemp = 0;
 362 
 363                   if (txc.offset < 0)
 364                     time_freq -= (-txc.offset * mtemp) >>
 365                       (time_constant + time_constant);
 366                   else
 367                     time_freq += (txc.offset * mtemp) >>
 368                       (time_constant + time_constant);
 369 
 370                   ltemp = time_tolerance << SHIFT_KF;
 371 
 372                   if (time_freq > ltemp)
 373                     time_freq = ltemp;
 374                   else if (time_freq < -ltemp)
 375                     time_freq = -ltemp;
 376                 }
 377         }
 378         txc.offset         = save_adjust;
 379         txc.frequency      = time_freq;
 380         txc.maxerror       = time_maxerror;
 381         txc.esterror       = time_esterror;
 382         txc.status         = time_status;
 383         txc.time_constant  = time_constant;
 384         txc.precision      = time_precision;
 385         txc.tolerance      = time_tolerance;
 386         txc.time           = xtime;
 387 
 388         sti();
 389 
 390         memcpy_tofs(txc_p, &txc, sizeof(struct timex));
 391         return time_status;
 392 }
 393 
 394 int set_rtc_mmss(unsigned long nowtime)
     /* [previous][next][first][last][top][bottom][index][help] */
 395 {
 396   int retval = 0;
 397   short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
 398   unsigned char save_control, save_freq_select, cmos_minutes;
 399 
 400   save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
 401   CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 402 
 403   save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
 404   CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 405 
 406   cmos_minutes = CMOS_READ(RTC_MINUTES);
 407   if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 408     BCD_TO_BIN(cmos_minutes);
 409 
 410   /* since we're only adjusting minutes and seconds,
 411    * don't interfere with hour overflow. This avoids
 412    * messing with unknown time zones but requires your
 413    * RTC not to be off by more than 30 minutes
 414    */
 415   if (((cmos_minutes < real_minutes) ?
 416        (real_minutes - cmos_minutes) :
 417        (cmos_minutes - real_minutes)) < 30)
 418     {
 419       if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 420         {
 421           BIN_TO_BCD(real_seconds);
 422           BIN_TO_BCD(real_minutes);
 423         }
 424       CMOS_WRITE(real_seconds,RTC_SECONDS);
 425       CMOS_WRITE(real_minutes,RTC_MINUTES);
 426     }
 427   else
 428     retval = -1;
 429 
 430   CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 431   CMOS_WRITE(save_control, RTC_CONTROL);
 432   return retval;
 433 }

/* [previous][next][first][last][top][bottom][index][help] */