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