Ignore:
Timestamp:
Feb 25, 2011, 9:26:54 PM (13 years ago)
Author:
Bruno Cornec
Message:
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/2.2.9/mindi-busybox/util-linux/hwclock.c

    r1765 r2725  
    55 * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
    66 *
    7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
     7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
    88*/
    99
     10#include "libbb.h"
     11/* After libbb.h, since it needs sys/types.h on some systems */
    1012#include <sys/utsname.h>
    11 #include <getopt.h>
    12 #include "libbb.h"
    13 
    14 /* Copied from linux/rtc.h to eliminate the kernel dependency */
    15 struct linux_rtc_time {
    16     int tm_sec;
    17     int tm_min;
    18     int tm_hour;
    19     int tm_mday;
    20     int tm_mon;
    21     int tm_year;
    22     int tm_wday;
    23     int tm_yday;
    24     int tm_isdst;
    25 };
    26 
    27 #define RTC_SET_TIME   _IOW('p', 0x0a, struct linux_rtc_time) /* Set RTC time    */
    28 #define RTC_RD_TIME    _IOR('p', 0x09, struct linux_rtc_time) /* Read RTC time   */
    29 
    30 #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
    31 # ifndef _GNU_SOURCE
    32 #  define _GNU_SOURCE
    33 # endif
    34 #endif
    35 
    36 static const char *rtcname;
    37 
    38 static int xopen_rtc(int flags)
    39 {
    40     int rtc;
    41 
    42     if (!rtcname) {
    43         rtc = open("/dev/rtc", flags);
    44         if (rtc >= 0)
    45             return rtc;
    46         rtc = open("/dev/rtc0", flags);
    47         if (rtc >= 0)
    48             return rtc;
    49         rtcname = "/dev/misc/rtc";
    50     }
    51     return xopen(rtcname, flags);
    52 }
    53 
    54 static time_t read_rtc(int utc)
    55 {
    56     struct tm tm;
    57     char *oldtz = 0;
    58     time_t t = 0;
    59     int rtc = xopen_rtc(O_RDONLY);
    60 
    61     memset(&tm, 0, sizeof(struct tm));
    62     xioctl(rtc, RTC_RD_TIME, &tm);
    63     tm.tm_isdst = -1; /* not known */
    64 
    65     close(rtc);
    66 
    67     if (utc) {
    68         oldtz = getenv("TZ");
    69         putenv((char*)"TZ=UTC0");
    70         tzset();
    71     }
    72 
    73     t = mktime(&tm);
    74 
    75     if (utc) {
    76         unsetenv("TZ");
    77         if (oldtz)
    78             putenv(oldtz - 3);
    79         tzset();
    80     }
    81     return t;
    82 }
    83 
    84 static void write_rtc(time_t t, int utc)
    85 {
    86     struct tm tm;
    87     int rtc = xopen_rtc(O_WRONLY);
    88 
    89     tm = *(utc ? gmtime(&t) : localtime(&t));
    90     tm.tm_isdst = 0;
    91 
    92     xioctl(rtc, RTC_SET_TIME, &tm);
    93 
    94     close(rtc);
    95 }
    96 
    97 static void show_clock(int utc)
    98 {
    99     //struct tm *ptm;
     13#include "rtc_.h"
     14
     15/* diff code is disabled: it's not sys/hw clock diff, it's some useless
     16 * "time between hwclock was started and we saw CMOS tick" quantity.
     17 * It's useless since hwclock is started at a random moment,
     18 * thus the quantity is also random, useless. Showing 0.000000 does not
     19 * deprive us from any useful info.
     20 *
     21 * SHOW_HWCLOCK_DIFF code in this file shows the difference between system
     22 * and hw clock. It is useful, but not compatible with standard hwclock.
     23 * Thus disabled.
     24 */
     25#define SHOW_HWCLOCK_DIFF 0
     26
     27
     28#if !SHOW_HWCLOCK_DIFF
     29# define read_rtc(pp_rtcname, sys_tv, utc) read_rtc(pp_rtcname, utc)
     30#endif
     31static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
     32{
     33    struct tm tm_time;
     34    int fd;
     35
     36    fd = rtc_xopen(pp_rtcname, O_RDONLY);
     37
     38    rtc_read_tm(&tm_time, fd);
     39
     40#if SHOW_HWCLOCK_DIFF
     41    {
     42        int before = tm_time.tm_sec;
     43        while (1) {
     44            rtc_read_tm(&tm_time, fd);
     45            gettimeofday(sys_tv, NULL);
     46            if (before != tm_time.tm_sec)
     47                break;
     48        }
     49    }
     50#endif
     51
     52    if (ENABLE_FEATURE_CLEAN_UP)
     53        close(fd);
     54
     55    return rtc_tm2time(&tm_time, utc);
     56}
     57
     58static void show_clock(const char **pp_rtcname, int utc)
     59{
     60#if SHOW_HWCLOCK_DIFF
     61    struct timeval sys_tv;
     62#endif
    10063    time_t t;
    10164    char *cp;
    10265
    103     t = read_rtc(utc);
    104     //ptm = localtime(&t);  /* Sets 'tzname[]' */
    105 
     66    t = read_rtc(pp_rtcname, &sys_tv, utc);
    10667    cp = ctime(&t);
    107     if (cp[0])
    108         cp[strlen(cp) - 1] = '\0';
    109 
    110     //printf("%s  %.6f seconds %s\n", cp, 0.0, utc ? "" : (ptm->tm_isdst ? tzname[1] : tzname[0]));
     68    strchrnul(cp, '\n')[0] = '\0';
     69#if !SHOW_HWCLOCK_DIFF
    11170    printf("%s  0.000000 seconds\n", cp);
    112 }
    113 
    114 static void to_sys_clock(int utc)
     71#else
     72    {
     73        long diff = sys_tv.tv_sec - t;
     74        if (diff < 0 /*&& tv.tv_usec != 0*/) {
     75            /* Why? */
     76            /* diff >= 0 is ok:   diff < 0, can't just use tv.tv_usec: */
     77            /*   45.520820          43.520820 */
     78            /* - 44.000000        - 45.000000 */
     79            /* =  1.520820        = -1.479180, not -2.520820! */
     80            diff++;
     81            /* should be 1000000 - tv.tv_usec, but then we must check tv.tv_usec != 0 */
     82            sys_tv.tv_usec = 999999 - sys_tv.tv_usec;
     83        }
     84        printf("%s  %ld.%06lu seconds\n", cp, diff, (unsigned long)sys_tv.tv_usec);
     85    }
     86#endif
     87}
     88
     89static void to_sys_clock(const char **pp_rtcname, int utc)
    11590{
    11691    struct timeval tv;
    117     const struct timezone tz = { timezone/60 - 60*daylight, 0 };
    118 
    119     tv.tv_sec = read_rtc(utc);
     92    struct timezone tz;
     93
     94    tz.tz_minuteswest = timezone/60 - 60*daylight;
     95    tz.tz_dsttime = 0;
     96
     97    tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
    12098    tv.tv_usec = 0;
    12199    if (settimeofday(&tv, &tz))
    122         bb_perror_msg_and_die("settimeofday() failed");
    123 }
    124 
    125 static void from_sys_clock(int utc)
    126 {
     100        bb_perror_msg_and_die("settimeofday");
     101}
     102
     103static void from_sys_clock(const char **pp_rtcname, int utc)
     104{
     105#if 1
    127106    struct timeval tv;
    128 
     107    struct tm tm_time;
     108    int rtc;
     109
     110    rtc = rtc_xopen(pp_rtcname, O_WRONLY);
    129111    gettimeofday(&tv, NULL);
    130     //if (gettimeofday(&tv, NULL))
    131     //  bb_perror_msg_and_die("gettimeofday() failed");
    132     write_rtc(tv.tv_sec, utc);
    133 }
    134 
    135 #if ENABLE_FEATURE_HWCLOCK_ADJTIME_FHS
    136 # define ADJTIME_PATH "/var/lib/hwclock/adjtime"
     112    /* Prepare tm_time */
     113    if (sizeof(time_t) == sizeof(tv.tv_sec)) {
     114        if (utc)
     115            gmtime_r((time_t*)&tv.tv_sec, &tm_time);
     116        else
     117            localtime_r((time_t*)&tv.tv_sec, &tm_time);
     118    } else {
     119        time_t t = tv.tv_sec;
     120        if (utc)
     121            gmtime_r(&t, &tm_time);
     122        else
     123            localtime_r(&t, &tm_time);
     124    }
    137125#else
    138 # define ADJTIME_PATH "/etc/adjtime"
    139 #endif
    140 static int check_utc(void)
    141 {
    142     int utc = 0;
    143     FILE *f = fopen(ADJTIME_PATH, "r");
    144 
    145     if (f) {
    146         RESERVE_CONFIG_BUFFER(buffer, 128);
    147 
    148         while (fgets(buffer, sizeof(buffer), f)) {
    149             int len = strlen(buffer);
    150 
    151             while (len && isspace(buffer[len - 1]))
    152                 len--;
    153 
    154             buffer[len] = 0;
    155 
    156             if (strncmp(buffer, "UTC", 3) == 0) {
    157                 utc = 1;
    158                 break;
    159             }
    160         }
    161         fclose(f);
    162         RELEASE_CONFIG_BUFFER(buffer);
    163     }
    164     return utc;
     126/* Bloated code which tries to set hw clock with better precision.
     127 * On x86, even though code does set hw clock within <1ms of exact
     128 * whole seconds, apparently hw clock (at least on some machines)
     129 * doesn't reset internal fractional seconds to 0,
     130 * making all this a pointless excercise.
     131 */
     132    /* If we see that we are N usec away from whole second,
     133     * we'll sleep for N-ADJ usecs. ADJ corrects for the fact
     134     * that CPU is not infinitely fast.
     135     * On infinitely fast CPU, next wakeup would be
     136     * on (exactly_next_whole_second - ADJ). On real CPUs,
     137     * this difference between current time and whole second
     138     * is less than ADJ (assuming system isn't heavily loaded).
     139     */
     140    /* Small value of 256us gives very precise sync for 2+ GHz CPUs.
     141     * Slower CPUs will fail to sync and will go to bigger
     142     * ADJ values. qemu-emulated armv4tl with ~100 MHz
     143     * performance ends up using ADJ ~= 4*1024 and it takes
     144     * 2+ secs (2 tries with successively larger ADJ)
     145     * to sync. Even straced one on the same qemu (very slow)
     146     * takes only 4 tries.
     147     */
     148#define TWEAK_USEC 256
     149    unsigned adj = TWEAK_USEC;
     150    struct tm tm_time;
     151    struct timeval tv;
     152    int rtc = rtc_xopen(pp_rtcname, O_WRONLY);
     153
     154    /* Try to catch the moment when whole second is close */
     155    while (1) {
     156        unsigned rem_usec;
     157        time_t t;
     158
     159        gettimeofday(&tv, NULL);
     160
     161        t = tv.tv_sec;
     162        rem_usec = 1000000 - tv.tv_usec;
     163        if (rem_usec < adj) {
     164            /* Close enough */
     165 small_rem:
     166            t++;
     167        }
     168
     169        /* Prepare tm_time from t */
     170        if (utc)
     171            gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */
     172        else
     173            localtime_r(&t, &tm_time); /* same */
     174
     175        if (adj >= 32*1024) {
     176            break; /* 32 ms diff and still no luck?? give up trying to sync */
     177        }
     178
     179        /* gmtime/localtime took some time, re-get cur time */
     180        gettimeofday(&tv, NULL);
     181
     182        if (tv.tv_sec < t /* we are still in old second */
     183         || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */
     184        ) {
     185            break; /* good, we are in sync! */
     186        }
     187
     188        rem_usec = 1000000 - tv.tv_usec;
     189        if (rem_usec < adj) {
     190            t = tv.tv_sec;
     191            goto small_rem; /* already close to next sec, don't sleep */
     192        }
     193
     194        /* Try to sync up by sleeping */
     195        usleep(rem_usec - adj);
     196
     197        /* Jump to 1ms diff, then increase fast (x2): EVERY loop
     198         * takes ~1 sec, people won't like slowly converging code here!
     199         */
     200    //bb_error_msg("adj:%d tv.tv_usec:%d", adj, (int)tv.tv_usec);
     201        if (adj < 512)
     202            adj = 512;
     203        /* ... and if last "overshoot" does not look insanely big,
     204         * just use it as adj increment. This makes convergence faster.
     205         */
     206        if (tv.tv_usec < adj * 8) {
     207            adj += tv.tv_usec;
     208            continue;
     209        }
     210        adj *= 2;
     211    }
     212    /* Debug aid to find "optimal" TWEAK_USEC with nearly exact sync.
     213     * Look for a value which makes tv_usec close to 999999 or 0.
     214     * For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200
     215     */
     216    //bb_error_msg("tv.tv_usec:%d", (int)tv.tv_usec);
     217#endif
     218
     219    tm_time.tm_isdst = 0;
     220    xioctl(rtc, RTC_SET_TIME, &tm_time);
     221
     222    if (ENABLE_FEATURE_CLEAN_UP)
     223        close(rtc);
    165224}
    166225
     
    172231#define HWCLOCK_OPT_RTCFILE     0x20
    173232
    174 int hwclock_main(int argc, char **argv);
    175 int hwclock_main(int argc, char **argv)
    176 {
     233int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
     234int hwclock_main(int argc UNUSED_PARAM, char **argv)
     235{
     236    const char *rtcname = NULL;
    177237    unsigned opt;
    178238    int utc;
     
    194254    /* If -u or -l wasn't given check if we are using utc */
    195255    if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
    196         utc = opt & HWCLOCK_OPT_UTC;
     256        utc = (opt & HWCLOCK_OPT_UTC);
    197257    else
    198         utc = check_utc();
    199 
    200     if (opt & HWCLOCK_OPT_HCTOSYS) {
    201         to_sys_clock(utc);
    202         return 0;
    203     }
    204     if (opt & HWCLOCK_OPT_SYSTOHC) {
    205         from_sys_clock(utc);
    206         return 0;
    207     }
    208     /* default HWCLOCK_OPT_SHOW */
    209     show_clock(utc);
     258        utc = rtc_adjtime_is_utc();
     259
     260    if (opt & HWCLOCK_OPT_HCTOSYS)
     261        to_sys_clock(&rtcname, utc);
     262    else if (opt & HWCLOCK_OPT_SYSTOHC)
     263        from_sys_clock(&rtcname, utc);
     264    else
     265        /* default HWCLOCK_OPT_SHOW */
     266        show_clock(&rtcname, utc);
     267
    210268    return 0;
    211269}
Note: See TracChangeset for help on using the changeset viewer.