Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 990 - (hide annotations) (download)
Sun May 30 11:42:11 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 7312 byte(s)
-added upstream hwclock patch
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     #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
16     # ifndef _GNU_SOURCE
17     # define _GNU_SOURCE
18     # endif
19     #endif
20    
21    
22 niro 984 /* diff code is disabled: it's not sys/hw clock diff, it's some useless
23     * "time between hwclock was started and we saw CMOS tick" quantity.
24     * It's useless since hwclock is started at a random moment,
25     * thus the quantity is also random, useless. Showing 0.000000 does not
26     * deprive us from any useful info.
27     *
28     * SHOW_HWCLOCK_DIFF code in this file shows the difference between system
29     * and hw clock. It is useful, but not compatible with standard hwclock.
30     * Thus disabled.
31     */
32     #define SHOW_HWCLOCK_DIFF 0
33    
34    
35     #if !SHOW_HWCLOCK_DIFF
36     # define read_rtc(pp_rtcname, sys_tv, utc) read_rtc(pp_rtcname, utc)
37     #endif
38     static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc)
39 niro 532 {
40 niro 984 struct tm tm_time;
41 niro 816 int fd;
42 niro 532
43 niro 984 fd = rtc_xopen(pp_rtcname, O_RDONLY);
44 niro 532
45 niro 984 rtc_read_tm(&tm_time, fd);
46 niro 532
47 niro 984 #if SHOW_HWCLOCK_DIFF
48     {
49     int before = tm_time.tm_sec;
50     while (1) {
51     rtc_read_tm(&tm_time, fd);
52     gettimeofday(sys_tv, NULL);
53     if (before != tm_time.tm_sec)
54     break;
55     }
56     }
57     #endif
58 niro 532
59 niro 984 if (ENABLE_FEATURE_CLEAN_UP)
60     close(fd);
61 niro 532
62 niro 984 return rtc_tm2time(&tm_time, utc);
63 niro 532 }
64    
65 niro 984 static void show_clock(const char **pp_rtcname, int utc)
66 niro 532 {
67 niro 984 #if SHOW_HWCLOCK_DIFF
68     struct timeval sys_tv;
69     #endif
70 niro 532 time_t t;
71 niro 816 char *cp;
72 niro 532
73 niro 984 t = read_rtc(pp_rtcname, &sys_tv, utc);
74 niro 816 cp = ctime(&t);
75 niro 984 strchrnul(cp, '\n')[0] = '\0';
76     #if !SHOW_HWCLOCK_DIFF
77 niro 816 printf("%s 0.000000 seconds\n", cp);
78 niro 984 #else
79     {
80     long diff = sys_tv.tv_sec - t;
81     if (diff < 0 /*&& tv.tv_usec != 0*/) {
82     /* Why? */
83     /* diff >= 0 is ok: diff < 0, can't just use tv.tv_usec: */
84     /* 45.520820 43.520820 */
85     /* - 44.000000 - 45.000000 */
86     /* = 1.520820 = -1.479180, not -2.520820! */
87     diff++;
88     /* should be 1000000 - tv.tv_usec, but then we must check tv.tv_usec != 0 */
89     sys_tv.tv_usec = 999999 - sys_tv.tv_usec;
90     }
91     printf("%s %ld.%06lu seconds\n", cp, diff, (unsigned long)sys_tv.tv_usec);
92     }
93     #endif
94 niro 532 }
95    
96 niro 984 static void to_sys_clock(const char **pp_rtcname, int utc)
97 niro 532 {
98 niro 816 struct timeval tv;
99 niro 984 struct timezone tz;
100 niro 532
101 niro 984 tz.tz_minuteswest = timezone/60 - 60*daylight;
102     tz.tz_dsttime = 0;
103    
104     tv.tv_sec = read_rtc(pp_rtcname, NULL, utc);
105 niro 816 tv.tv_usec = 0;
106 niro 532 if (settimeofday(&tv, &tz))
107 niro 984 bb_perror_msg_and_die("settimeofday");
108 niro 532 }
109    
110 niro 984 static void from_sys_clock(const char **pp_rtcname, int utc)
111 niro 532 {
112 niro 990 #if 1
113     struct timeval tv;
114 niro 984 struct tm tm_time;
115 niro 990 int rtc;
116    
117     rtc = rtc_xopen(pp_rtcname, O_WRONLY);
118     gettimeofday(&tv, NULL);
119     /* Prepare tm_time */
120     if (sizeof(time_t) == sizeof(tv.tv_sec)) {
121     if (utc)
122     gmtime_r((time_t*)&tv.tv_sec, &tm_time);
123     else
124     localtime_r((time_t*)&tv.tv_sec, &tm_time);
125     } else {
126     time_t t = tv.tv_sec;
127     if (utc)
128     gmtime_r(&t, &tm_time);
129     else
130     localtime_r(&t, &tm_time);
131     }
132     #else
133     /* Bloated code which tries to set hw clock with better precision.
134     * On x86, even though code does set hw clock within <1ms of exact
135     * whole seconds, apparently hw clock (at least on some machines)
136     * doesn't reset internal fractional seconds to 0,
137     * making all this a pointless excercise.
138     */
139     /* If we see that we are N usec away from whole second,
140     * we'll sleep for N-ADJ usecs. ADJ corrects for the fact
141     * that CPU is not infinitely fast.
142     * On infinitely fast CPU, next wakeup would be
143     * on (exactly_next_whole_second - ADJ). On real CPUs,
144     * this difference between current time and whole second
145     * is less than ADJ (assuming system isn't heavily loaded).
146     */
147     /* Small value of 256us gives very precise sync for 2+ GHz CPUs.
148     * Slower CPUs will fail to sync and will go to bigger
149     * ADJ values. qemu-emulated armv4tl with ~100 MHz
150     * performance ends up using ADJ ~= 4*1024 and it takes
151     * 2+ secs (2 tries with successively larger ADJ)
152     * to sync. Even straced one on the same qemu (very slow)
153     * takes only 4 tries.
154     */
155     #define TWEAK_USEC 256
156     unsigned adj = TWEAK_USEC;
157     struct tm tm_time;
158 niro 816 struct timeval tv;
159 niro 984 int rtc = rtc_xopen(pp_rtcname, O_WRONLY);
160 niro 532
161 niro 984 /* Try to catch the moment when whole second is close */
162     while (1) {
163     unsigned rem_usec;
164     time_t t;
165    
166     gettimeofday(&tv, NULL);
167    
168     t = tv.tv_sec;
169     rem_usec = 1000000 - tv.tv_usec;
170 niro 990 if (rem_usec < adj) {
171     /* Close enough */
172 niro 984 small_rem:
173     t++;
174     }
175    
176 niro 990 /* Prepare tm_time from t */
177 niro 984 if (utc)
178     gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */
179     else
180     localtime_r(&t, &tm_time); /* same */
181    
182 niro 990 if (adj >= 32*1024) {
183     break; /* 32 ms diff and still no luck?? give up trying to sync */
184     }
185    
186 niro 984 /* gmtime/localtime took some time, re-get cur time */
187     gettimeofday(&tv, NULL);
188    
189 niro 990 if (tv.tv_sec < t /* we are still in old second */
190     || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */
191 niro 984 ) {
192 niro 990 break; /* good, we are in sync! */
193 niro 984 }
194 niro 990
195     rem_usec = 1000000 - tv.tv_usec;
196     if (rem_usec < adj) {
197     t = tv.tv_sec;
198     goto small_rem; /* already close to next sec, don't sleep */
199 niro 984 }
200    
201     /* Try to sync up by sleeping */
202 niro 990 usleep(rem_usec - adj);
203    
204     /* Jump to 1ms diff, then increase fast (x2): EVERY loop
205     * takes ~1 sec, people won't like slowly converging code here!
206     */
207     //bb_error_msg("adj:%d tv.tv_usec:%d", adj, (int)tv.tv_usec);
208     if (adj < 512)
209     adj = 512;
210     /* ... and if last "overshoot" does not look insanely big,
211     * just use it as adj increment. This makes convergence faster.
212     */
213     if (tv.tv_usec < adj * 8) {
214     adj += tv.tv_usec;
215     continue;
216 niro 984 }
217 niro 990 adj *= 2;
218 niro 984 }
219 niro 990 /* Debug aid to find "optimal" TWEAK_USEC with nearly exact sync.
220     * Look for a value which makes tv_usec close to 999999 or 0.
221     * For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200
222     */
223     //bb_error_msg("tv.tv_usec:%d", (int)tv.tv_usec);
224     #endif
225 niro 984
226 niro 990 tm_time.tm_isdst = 0;
227 niro 984 xioctl(rtc, RTC_SET_TIME, &tm_time);
228    
229     if (ENABLE_FEATURE_CLEAN_UP)
230     close(rtc);
231 niro 532 }
232    
233     #define HWCLOCK_OPT_LOCALTIME 0x01
234     #define HWCLOCK_OPT_UTC 0x02
235     #define HWCLOCK_OPT_SHOW 0x04
236     #define HWCLOCK_OPT_HCTOSYS 0x08
237     #define HWCLOCK_OPT_SYSTOHC 0x10
238 niro 816 #define HWCLOCK_OPT_RTCFILE 0x20
239 niro 532
240 niro 816 int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
241     int hwclock_main(int argc UNUSED_PARAM, char **argv)
242 niro 532 {
243 niro 984 const char *rtcname = NULL;
244 niro 532 unsigned opt;
245     int utc;
246    
247     #if ENABLE_FEATURE_HWCLOCK_LONG_OPTIONS
248 niro 816 static const char hwclock_longopts[] ALIGN1 =
249     "localtime\0" No_argument "l"
250     "utc\0" No_argument "u"
251     "show\0" No_argument "r"
252     "hctosys\0" No_argument "s"
253     "systohc\0" No_argument "w"
254     "file\0" Required_argument "f"
255     ;
256     applet_long_options = hwclock_longopts;
257 niro 532 #endif
258 niro 816 opt_complementary = "r--ws:w--rs:s--wr:l--u:u--l";
259     opt = getopt32(argv, "lurswf:", &rtcname);
260 niro 532
261     /* If -u or -l wasn't given check if we are using utc */
262     if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME))
263 niro 816 utc = (opt & HWCLOCK_OPT_UTC);
264 niro 532 else
265 niro 816 utc = rtc_adjtime_is_utc();
266 niro 532
267 niro 816 if (opt & HWCLOCK_OPT_HCTOSYS)
268 niro 984 to_sys_clock(&rtcname, utc);
269 niro 816 else if (opt & HWCLOCK_OPT_SYSTOHC)
270 niro 984 from_sys_clock(&rtcname, utc);
271 niro 816 else
272 niro 532 /* default HWCLOCK_OPT_SHOW */
273 niro 984 show_clock(&rtcname, utc);
274 niro 816
275     return 0;
276 niro 532 }