home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.0 / LINUX-1.0 / LINUX-1 / linux / kernel / time.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-31  |  12.2 KB  |  443 lines

  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)
  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)
  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)
  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)
  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)
  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)
  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)
  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)
  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)
  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)
  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. }
  443.