Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
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);
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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);
}