static time_t read_rtc(const char **pp_rtcname, struct timeval *sys_tv, int utc) { struct tm tm_time; int fd; fd = rtc_xopen(pp_rtcname, O_RDONLY); rtc_read_tm(&tm_time, fd); #if SHOW_HWCLOCK_DIFF { int before = tm_time.tm_sec; while (1) { rtc_read_tm(&tm_time, fd); gettimeofday(sys_tv, NULL); if (before != tm_time.tm_sec) break; } } #endif if (ENABLE_FEATURE_CLEAN_UP) close(fd); return rtc_tm2time(&tm_time, utc); }
static void from_sys_clock(const char **pp_rtcname, int utc) { #if 1 struct timeval tv; struct tm tm_time; int rtc; rtc = rtc_xopen(pp_rtcname, O_WRONLY); gettimeofday(&tv, NULL); /* Prepare tm_time */ if (sizeof(time_t) == sizeof(tv.tv_sec)) { if (utc) gmtime_r((time_t*)&tv.tv_sec, &tm_time); else localtime_r((time_t*)&tv.tv_sec, &tm_time); } else { time_t t = tv.tv_sec; if (utc) gmtime_r(&t, &tm_time); else localtime_r(&t, &tm_time); } #else /* Bloated code which tries to set hw clock with better precision. * On x86, even though code does set hw clock within <1ms of exact * whole seconds, apparently hw clock (at least on some machines) * doesn't reset internal fractional seconds to 0, * making all this a pointless excercise. */ /* If we see that we are N usec away from whole second, * we'll sleep for N-ADJ usecs. ADJ corrects for the fact * that CPU is not infinitely fast. * On infinitely fast CPU, next wakeup would be * on (exactly_next_whole_second - ADJ). On real CPUs, * this difference between current time and whole second * is less than ADJ (assuming system isn't heavily loaded). */ /* Small value of 256us gives very precise sync for 2+ GHz CPUs. * Slower CPUs will fail to sync and will go to bigger * ADJ values. qemu-emulated armv4tl with ~100 MHz * performance ends up using ADJ ~= 4*1024 and it takes * 2+ secs (2 tries with successively larger ADJ) * to sync. Even straced one on the same qemu (very slow) * takes only 4 tries. */ #define TWEAK_USEC 256 unsigned adj = TWEAK_USEC; struct tm tm_time; struct timeval tv; int rtc = rtc_xopen(pp_rtcname, O_WRONLY); /* Try to catch the moment when whole second is close */ while (1) { unsigned rem_usec; time_t t; gettimeofday(&tv, NULL); t = tv.tv_sec; rem_usec = 1000000 - tv.tv_usec; if (rem_usec < adj) { /* Close enough */ small_rem: t++; } /* Prepare tm_time from t */ if (utc) gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */ else localtime_r(&t, &tm_time); /* same */ if (adj >= 32*1024) { break; /* 32 ms diff and still no luck?? give up trying to sync */ } /* gmtime/localtime took some time, re-get cur time */ gettimeofday(&tv, NULL); if (tv.tv_sec < t /* we are still in old second */ || (tv.tv_sec == t && tv.tv_usec < adj) /* not too far into next second */ ) { break; /* good, we are in sync! */ } rem_usec = 1000000 - tv.tv_usec; if (rem_usec < adj) { t = tv.tv_sec; goto small_rem; /* already close to next sec, don't sleep */ } /* Try to sync up by sleeping */ usleep(rem_usec - adj); /* Jump to 1ms diff, then increase fast (x2): EVERY loop * takes ~1 sec, people won't like slowly converging code here! */ //bb_error_msg("adj:%d tv.tv_usec:%d", adj, (int)tv.tv_usec); if (adj < 512) adj = 512; /* ... and if last "overshoot" does not look insanely big, * just use it as adj increment. This makes convergence faster. */ if (tv.tv_usec < adj * 8) { adj += tv.tv_usec; continue; } adj *= 2; } /* Debug aid to find "optimal" TWEAK_USEC with nearly exact sync. * Look for a value which makes tv_usec close to 999999 or 0. * For 2.20GHz Intel Core 2: optimal TWEAK_USEC ~= 200 */ //bb_error_msg("tv.tv_usec:%d", (int)tv.tv_usec); #endif tm_time.tm_isdst = 0; xioctl(rtc, RTC_SET_TIME, &tm_time); if (ENABLE_FEATURE_CLEAN_UP) close(rtc); }
int rtcwake_main(int argc UNUSED_PARAM, char **argv) { unsigned opt; const char *rtcname = NULL; const char *suspend = "standby"; const char *opt_seconds; const char *opt_time; time_t rtc_time; time_t sys_time; time_t alarm_time = alarm_time; unsigned seconds = seconds; /* for compiler */ int utc = -1; int fd; #if ENABLE_LONG_OPTS static const char rtcwake_longopts[] ALIGN1 = "auto\0" No_argument "a" "local\0" No_argument "l" "utc\0" No_argument "u" "device\0" Required_argument "d" "mode\0" Required_argument "m" "seconds\0" Required_argument "s" "time\0" Required_argument "t" ; #endif opt = getopt32long(argv, /* Must have -s or -t, exclusive */ "^alud:m:s:t:" "\0" "s:t:s--t:t--s", rtcwake_longopts, &rtcname, &suspend, &opt_seconds, &opt_time); /* this is the default if (opt & RTCWAKE_OPT_AUTO) utc = -1; */ if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL)) utc = opt & RTCWAKE_OPT_UTC; if (opt & RTCWAKE_OPT_SECONDS) { /* alarm time, seconds-to-sleep (relative) */ seconds = xatou(opt_seconds); } else { /* RTCWAKE_OPT_TIME */ /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */ if (sizeof(alarm_time) <= sizeof(long)) alarm_time = xatol(opt_time); else alarm_time = xatoll(opt_time); } if (utc == -1) utc = rtc_adjtime_is_utc(); /* the rtcname is relative to /dev */ xchdir("/dev"); /* this RTC must exist and (if we'll sleep) be wakeup-enabled */ fd = rtc_xopen(&rtcname, O_RDONLY); if (strcmp(suspend, "on") != 0) if (!may_wakeup(rtcname)) bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); /* relative or absolute alarm time, normalized to time_t */ sys_time = time(NULL); { struct tm tm_time; rtc_read_tm(&tm_time, fd); rtc_time = rtc_tm2time(&tm_time, utc); } if (opt & RTCWAKE_OPT_TIME) { /* Correct for RTC<->system clock difference */ alarm_time += rtc_time - sys_time; if (alarm_time < rtc_time) /* * Compat message text. * I'd say "RTC time is already ahead of ..." instead. */ bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time)); } else alarm_time = rtc_time + seconds + 1; setup_alarm(fd, &alarm_time, rtc_time); sync(); #if 0 /*debug*/ printf("sys_time: %s", ctime(&sys_time)); printf("rtc_time: %s", ctime(&rtc_time)); #endif printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time)); fflush_all(); usleep(10 * 1000); if (strcmp(suspend, "on") != 0) xopen_xwrite_close(SYS_POWER_PATH, suspend); else { /* "fake" suspend ... we'll do the delay ourselves */ unsigned long data; do { ssize_t ret = safe_read(fd, &data, sizeof(data)); if (ret < 0) { bb_perror_msg("rtc read"); break; } } while (!(data & RTC_AF)); } xioctl(fd, RTC_AIE_OFF, 0); if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; }
int rtcwake_main(int argc UNUSED_PARAM, char **argv) { time_t rtc_time; unsigned opt; const char *rtcname = NULL; const char *suspend; const char *opt_seconds; const char *opt_time; time_t sys_time; time_t alarm_time = 0; unsigned seconds = 0; int utc = -1; int fd; #if ENABLE_LONG_OPTS static const char rtcwake_longopts[] ALIGN1 = "auto\0" No_argument "a" "local\0" No_argument "l" "utc\0" No_argument "u" "device\0" Required_argument "d" "mode\0" Required_argument "m" "seconds\0" Required_argument "s" "time\0" Required_argument "t" ; applet_long_options = rtcwake_longopts; #endif opt = getopt32(argv, "alud:m:s:t:", &rtcname, &suspend, &opt_seconds, &opt_time); /* this is the default if (opt & RTCWAKE_OPT_AUTO) utc = -1; */ if (opt & (RTCWAKE_OPT_UTC | RTCWAKE_OPT_LOCAL)) utc = opt & RTCWAKE_OPT_UTC; if (!(opt & RTCWAKE_OPT_SUSPEND_MODE)) suspend = DEFAULT_MODE; if (opt & RTCWAKE_OPT_SECONDS) /* alarm time, seconds-to-sleep (relative) */ seconds = xatoi(opt_seconds); if (opt & RTCWAKE_OPT_TIME) /* alarm time, time_t (absolute, seconds since 1/1 1970 UTC) */ alarm_time = xatol(opt_time); if (!alarm_time && !seconds) bb_error_msg_and_die("must provide wake time"); if (utc == -1) utc = rtc_adjtime_is_utc(); /* the rtcname is relative to /dev */ xchdir("/dev"); /* this RTC must exist and (if we'll sleep) be wakeup-enabled */ fd = rtc_xopen(&rtcname, O_RDONLY); if (strcmp(suspend, "on") && !may_wakeup(rtcname)) bb_error_msg_and_die("%s not enabled for wakeup events", rtcname); /* relative or absolute alarm time, normalized to time_t */ sys_time = time(NULL); { struct tm tm_time; rtc_read_tm(&tm_time, fd); rtc_time = rtc_tm2time(&tm_time, utc); } if (alarm_time) { if (alarm_time < sys_time) bb_error_msg_and_die("time doesn't go backward to %s", ctime(&alarm_time)); alarm_time += sys_time - rtc_time; } else alarm_time = rtc_time + seconds + 1; setup_alarm(fd, &alarm_time, rtc_time); sync(); printf("wakeup from \"%s\" at %s", suspend, ctime(&alarm_time)); fflush_all(); usleep(10 * 1000); if (strcmp(suspend, "on")) xopen_xwrite_close(SYS_POWER_PATH, suspend); else { /* "fake" suspend ... we'll do the delay ourselves */ unsigned long data; do { ssize_t ret = safe_read(fd, &data, sizeof(data)); if (ret < 0) { bb_perror_msg("rtc read"); break; } } while (!(data & RTC_AF)); } xioctl(fd, RTC_AIE_OFF, 0); if (ENABLE_FEATURE_CLEAN_UP) close(fd); return EXIT_SUCCESS; }
static void from_sys_clock(const char **pp_rtcname, int utc) { #define TWEAK_USEC 200 struct tm tm_time; struct timeval tv; unsigned adj = TWEAK_USEC; int rtc = rtc_xopen(pp_rtcname, O_WRONLY); /* Try to catch the moment when whole second is close */ while (1) { unsigned rem_usec; time_t t; gettimeofday(&tv, NULL); t = tv.tv_sec; rem_usec = 1000000 - tv.tv_usec; if (rem_usec < 1024) { /* Less than 1ms to next second. Good enough */ small_rem: t++; } /* Prepare tm */ if (utc) gmtime_r(&t, &tm_time); /* may read /etc/xxx (it takes time) */ else localtime_r(&t, &tm_time); /* same */ tm_time.tm_isdst = 0; /* gmtime/localtime took some time, re-get cur time */ gettimeofday(&tv, NULL); if (tv.tv_sec < t /* may happen if rem_usec was < 1024 */ || (tv.tv_sec == t && tv.tv_usec < 1024) ) { /* We are not too far into next second. Good. */ break; } adj += 32; /* 2^(10-5) = 2^5 = 32 iterations max */ if (adj >= 1024) { /* Give up trying to sync */ break; } /* Try to sync up by sleeping */ rem_usec = 1000000 - tv.tv_usec; if (rem_usec < 1024) { goto small_rem; /* already close, don't sleep */ } /* Need to sleep. * Note that small adj on slow processors can make us * to always overshoot tv.tv_usec < 1024 check on next * iteration. That's why adj is increased on each iteration. * This also allows it to be reused as a loop limiter. */ usleep(rem_usec - adj); } xioctl(rtc, RTC_SET_TIME, &tm_time); /* Debug aid to find "good" TWEAK_USEC. * Look for a value which makes tv_usec close to 999999 or 0. * for 2.20GHz Intel Core 2: TWEAK_USEC ~= 200 */ //bb_error_msg("tv.tv_usec:%d adj:%d", (int)tv.tv_usec, adj); if (ENABLE_FEATURE_CLEAN_UP) close(rtc); }