Changeset 2725 in MondoRescue for branches/2.2.9/mindi-busybox/util-linux/hwclock.c
- Timestamp:
- Feb 25, 2011, 9:26:54 PM (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/2.2.9/mindi-busybox/util-linux/hwclock.c
r1765 r2725 5 5 * Copyright (C) 2002 Robert Griebl <griebl@gmx.de> 6 6 * 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. 8 8 */ 9 9 10 #include "libbb.h" 11 /* After libbb.h, since it needs sys/types.h on some systems */ 10 12 #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 31 static 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 58 static void show_clock(const char **pp_rtcname, int utc) 59 { 60 #if SHOW_HWCLOCK_DIFF 61 struct timeval sys_tv; 62 #endif 100 63 time_t t; 101 64 char *cp; 102 65 103 t = read_rtc(utc); 104 //ptm = localtime(&t); /* Sets 'tzname[]' */ 105 66 t = read_rtc(pp_rtcname, &sys_tv, utc); 106 67 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 111 70 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 89 static void to_sys_clock(const char **pp_rtcname, int utc) 115 90 { 116 91 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); 120 98 tv.tv_usec = 0; 121 99 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 103 static void from_sys_clock(const char **pp_rtcname, int utc) 104 { 105 #if 1 127 106 struct timeval tv; 128 107 struct tm tm_time; 108 int rtc; 109 110 rtc = rtc_xopen(pp_rtcname, O_WRONLY); 129 111 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 } 137 125 #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); 165 224 } 166 225 … … 172 231 #define HWCLOCK_OPT_RTCFILE 0x20 173 232 174 int hwclock_main(int argc, char **argv); 175 int hwclock_main(int argc, char **argv) 176 { 233 int hwclock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 234 int hwclock_main(int argc UNUSED_PARAM, char **argv) 235 { 236 const char *rtcname = NULL; 177 237 unsigned opt; 178 238 int utc; … … 194 254 /* If -u or -l wasn't given check if we are using utc */ 195 255 if (opt & (HWCLOCK_OPT_UTC | HWCLOCK_OPT_LOCALTIME)) 196 utc = opt & HWCLOCK_OPT_UTC;256 utc = (opt & HWCLOCK_OPT_UTC); 197 257 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 210 268 return 0; 211 269 }
Note:
See TracChangeset
for help on using the changeset viewer.