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         /* if the quartz is off by more than 10% something is VERY wrong ! */
 325         if (txc.mode & ADJ_TICK)
 326           if (txc.tick < 900000/HZ || txc.tick > 1100000/HZ)
 327             return -EINVAL;
 328 
 329         cli();
 330 
 331         /* Save for later - semantics of adjtime is to return old value */
 332         save_adjust = time_adjust;
 333 
 334         /* If there are input parameters, then process them */
 335         if (txc.mode)
 336         {
 337             if (time_status == TIME_BAD)
 338                 time_status = TIME_OK;
 339 
 340             if (txc.mode & ADJ_STATUS)
 341                 time_status = txc.status;
 342 
 343             if (txc.mode & ADJ_FREQUENCY)
 344                 time_freq = txc.frequency << (SHIFT_KF - 16);
 345 
 346             if (txc.mode & ADJ_MAXERROR)
 347                 time_maxerror = txc.maxerror;
 348 
 349             if (txc.mode & ADJ_ESTERROR)
 350                 time_esterror = txc.esterror;
 351 
 352             if (txc.mode & ADJ_TIMECONST)
 353                 time_constant = txc.time_constant;
 354 
 355             if (txc.mode & ADJ_OFFSET)
 356               if (txc.mode == ADJ_OFFSET_SINGLESHOT)
 357                 {
 358                   time_adjust = txc.offset;
 359                 }
 360               else /* XXX should give an error if other bits set */
 361                 {
 362                   time_offset = txc.offset << SHIFT_UPDATE;
 363                   mtemp = xtime.tv_sec - time_reftime;
 364                   time_reftime = xtime.tv_sec;
 365                   if (mtemp > (MAXSEC+2) || mtemp < 0)
 366                     mtemp = 0;
 367 
 368                   if (txc.offset < 0)
 369                     time_freq -= (-txc.offset * mtemp) >>
 370                       (time_constant + time_constant);
 371                   else
 372                     time_freq += (txc.offset * mtemp) >>
 373                       (time_constant + time_constant);
 374 
 375                   ltemp = time_tolerance << SHIFT_KF;
 376 
 377                   if (time_freq > ltemp)
 378                     time_freq = ltemp;
 379                   else if (time_freq < -ltemp)
 380                     time_freq = -ltemp;
 381                 }
 382             if (txc.mode & ADJ_TICK)
 383               tick = txc.tick;
 384 
 385         }
 386         txc.offset         = save_adjust;
 387         txc.frequency      = ((time_freq+1) >> (SHIFT_KF - 16));
 388         txc.maxerror       = time_maxerror;
 389         txc.esterror       = time_esterror;
 390         txc.status         = time_status;
 391         txc.time_constant  = time_constant;
 392         txc.precision      = time_precision;
 393         txc.tolerance      = time_tolerance;
 394         txc.time           = xtime;
 395         txc.tick           = tick;
 396 
 397         sti();
 398 
 399         memcpy_tofs(txc_p, &txc, sizeof(struct timex));
 400         return time_status;
 401 }
 402 
 403 int set_rtc_mmss(unsigned long nowtime)
     /* [previous][next][first][last][top][bottom][index][help] */
 404 {
 405   int retval = 0;
 406   short real_seconds = nowtime % 60, real_minutes = (nowtime / 60) % 60;
 407   unsigned char save_control, save_freq_select, cmos_minutes;
 408 
 409   save_control = CMOS_READ(RTC_CONTROL); /* tell the clock it's being set */
 410   CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
 411 
 412   save_freq_select = CMOS_READ(RTC_FREQ_SELECT); /* stop and reset prescaler */
 413   CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
 414 
 415   cmos_minutes = CMOS_READ(RTC_MINUTES);
 416   if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 417     BCD_TO_BIN(cmos_minutes);
 418 
 419   /* since we're only adjusting minutes and seconds,
 420    * don't interfere with hour overflow. This avoids
 421    * messing with unknown time zones but requires your
 422    * RTC not to be off by more than 30 minutes
 423    */
 424   if (((cmos_minutes < real_minutes) ?
 425        (real_minutes - cmos_minutes) :
 426        (cmos_minutes - real_minutes)) < 30)
 427     {
 428       if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
 429         {
 430           BIN_TO_BCD(real_seconds);
 431           BIN_TO_BCD(real_minutes);
 432         }
 433       CMOS_WRITE(real_seconds,RTC_SECONDS);
 434       CMOS_WRITE(real_minutes,RTC_MINUTES);
 435     }
 436   else
 437     retval = -1;
 438 
 439   CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
 440   CMOS_WRITE(save_control, RTC_CONTROL);
 441   return retval;
 442 }

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