Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/util-linux/hwclock.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1123 - (hide annotations) (download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 7212 byte(s)
-updated to busybox-1.17.1
1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * Mini hwclock implementation for busybox
4     *
5     * Copyright (C) 2002 Robert Griebl <griebl@gmx.de>
6     *
7     * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8     */
9    
10 niro 984 #include "libbb.h"
11     /* After libbb.h, since it needs sys/types.h on some systems */
12 niro 532 #include <sys/utsname.h>
13 niro 816 #include "rtc_.h"
14 niro 532
15 niro 984 /* 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
31     static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
32 niro 532 {
33 niro 984 struct tm tm_time;
34 niro 816 int fd;
35 niro 532
36 niro 984 fd = rtc_xopen(pp_rtcname, O_RDONLY);
37 niro 532
38 niro 984 rtc_read_tm(&tm_time, fd);
39 niro 532
40 niro 984 #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 niro 532
52 niro 984 if (ENABLE_FEATURE_CLEAN_UP)
53     close(fd);
54 niro 532
55 niro 984 return rtc_tm2time(&tm_time, utc);
56 niro 532 }
57    
58 niro 984 static void show_clock(const char **pp_rtcname, int utc)
59 niro 532 {
60 niro 984 #if SHOW_HWCLOCK_DIFF
61     struct timeval sys_tv;
62     #endif
63 niro 532 time_t t;
64 niro 816 char *cp;
65 niro 532
66 niro 984 t = read_rtc(pp_rtcname, &sys_tv, utc);
67 niro 816 cp = ctime(&t);
68 niro 984 strchrnul(cp, '\n')[0] = '\0';
69     #if !SHOW_HWCLOCK_DIFF
70 niro 816 printf("%s 0.000000 seconds\n", cp);
71 niro 984 #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 niro 532 }
88    
89 niro 984 static void to_sys_clock(const char **pp_rtcname, int utc)
90 niro 532 {
91 niro 816 struct timeval tv;
92 niro 984 struct timezone tz;
93 niro 532
94 niro 984 tz.tz_minuteswest = timezone/60 - 60*daylight;
95     tz.tz_dsttime = 0;
96    
97     tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
98 niro 816 tv.tv_usec = 0;
99 niro 532 if (settimeofday(&tv, &tz))
100 niro 984 bb_perror_msg_and_die("settimeofday");
101 niro 532 }
102    
103 niro 984 static void from_sys_clock(const char **pp_rtcname, int utc)
104 niro 532 {
105 niro 990 #if 1
106     struct timeval tv;
107 niro 984 struct tm tm_time;
108 niro 990 int rtc;
109    
110     rtc = rtc_xopen(pp_rtcname, O_WRONLY);
111     gettimeofday(&tv, NULL);
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     }
125     #else
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 niro 816 struct timeval tv;
152 niro 984 int rtc = rtc_xopen(pp_rtcname, O_WRONLY);
153 niro 532
154 niro 984 /* 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 niro 990 if (rem_usec < adj) {
164     /* Close enough */
165 niro 984 small_rem:
166     t++;
167     }
168    
169 niro 990 /* Prepare tm_time from t */
170 niro 984 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 niro 990 if (adj >= 32*1024) {
176     break; /* 32 ms diff and still no luck?? give up trying to sync */
177     }
178    
179 niro 984 /* gmtime/localtime took some time, re-get cur time */
180     gettimeofday(&tv, NULL);
181    
182 niro 990 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 niro 984 ) {
185 niro 990 break; /* good, we are in sync! */
186 niro 984 }
187 niro 990
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 niro 984 }
193    
194     /* Try to sync up by sleeping */
195 niro 990 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 niro 984 }
210 niro 990 adj *= 2;
211 niro 984 }
212 niro 990 /* 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 niro 984
219 niro 990 tm_time.tm_isdst = 0;
220 niro 984 xioctl(rtc, RTC_SET_TIME, &tm_time);
221    
222     if (ENABLE_FEATURE_CLEAN_UP)
223     close(rtc);
224 niro 532 }
225    
226     #define HWCLOCK_OPT_LOCALTIME 0x01
227     #define HWCLOCK_OPT_UTC 0x02
228     #define HWCLOCK_OPT_SHOW 0x04
229     #define HWCLOCK_OPT_HCTOSYS 0x08
230     #define HWCLOCK_OPT_SYSTOHC 0x10
231 niro 816 #define HWCLOCK_OPT_RTCFILE 0x20
232 niro 532
233 niro 816 int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
234     int hwclock_main(int argc UNUSED_PARAM, char **argv)
235 niro 532 {
236 niro 984 const char *rtcname = NULL;
237 niro 532 unsigned opt;
238     int utc;
239    
240     #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
241 niro 816 static const char hwclock_longopts[] ALIGN1 =
242     "localtime\0" No_argument "l"
243     "utc\0" No_argument "u"
244     "show\0" No_argument "r"
245     "hctosys\0" No_argument "s"
246     "systohc\0" No_argument "w"
247     "file\0" Required_argument "f"
248     ;
249     applet_long_options = hwclock_longopts;
250 niro 532 #endif
251 niro 816 opt_complementary = "r--ws:w--rs:s--wr:l--u:u--l";
252     opt = getopt32(argv, "lurswf:", &rtcname);
253 niro 532
254     /* If -u or -l wasn't given check if we are using utc */
255     if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
256 niro 816 utc = (opt & HWCLOCK_OPT_UTC);
257 niro 532 else
258 niro 816 utc = rtc_adjtime_is_utc();
259 niro 532
260 niro 816 if (opt & HWCLOCK_OPT_HCTOSYS)
261 niro 984 to_sys_clock(&rtcname, utc);
262 niro 816 else if (opt & HWCLOCK_OPT_SYSTOHC)
263 niro 984 from_sys_clock(&rtcname, utc);
264 niro 816 else
265 niro 532 /* default HWCLOCK_OPT_SHOW */
266 niro 984 show_clock(&rtcname, utc);
267 niro 816
268     return 0;
269 niro 532 }