+ clock_sec_t wake_sys_sec;
+ clock_usec_t wake_sys_usec;
+ clock_sec_t wake_sec;
+ clock_usec_t wake_usec;
+ clock_sec_t wall_time_sec;
+ clock_usec_t wall_time_usec;
+ clock_sec_t diff_sec;
+ clock_usec_t diff_usec;
+ clock_sec_t var_s;
+ clock_usec_t var_us;
+ spl_t s;
+ struct bintime bt, last_sleep_bt;
+ struct latched_time monotonic_time;
+ uint64_t monotonic_usec_total;
+ uint64_t wake_abs;
+ size_t size;
+
+ /*
+ * If the platform has the monotonic clock use that to
+ * compute the sleep time. The monotonic clock does not have an offset
+ * that can be modified, so nor kernel or userspace can change the time
+ * of this clock, it can only monotonically increase over time.
+ * During sleep mach_absolute_time (sys time) does not tick,
+ * so the sleep time is the difference between the current monotonic time
+ * less the absolute time and the previous difference stored at wake time.
+ *
+ * basesleep = (monotonic - sys) ---> computed at last wake
+ * sleep_time = (monotonic - sys) - basesleep
+ *
+ * If the platform does not support monotonic clock we set the wall time to what the
+ * UTC clock returns us.
+ * Setting the wall time to UTC time implies that we loose all the adjustments
+ * done during wake time through adjtime/ntp_adjustime.
+ * The UTC time is the monotonic clock + an offset that can be set
+ * by kernel.
+ * The time slept in this case is the difference between wall time and UTC
+ * at wake.
+ *
+ * IMPORTANT:
+ * We assume that only the kernel is setting the offset of the PMU/RTC and that
+ * it is doing it only througth the settimeofday interface.
+ */
+ if (has_monotonic_clock) {
+
+#if DEVELOPMENT || DEBUG
+ /*
+ * Just for debugging, get the wake UTC time.
+ */
+ PEGetUTCTimeOfDay(&var_s, &var_us);
+#endif
+ /*
+ * Get monotonic time with corresponding sys time
+ */
+ size = sizeof(monotonic_time);
+ if (kernel_sysctlbyname("kern.monotonicclock_usecs", &monotonic_time, &size, NULL, 0) != 0) {
+ panic("%s: could not call kern.monotonicclock_usecs", __func__);
+ }
+ wake_abs = monotonic_time.mach_time;
+ absolutetime_to_microtime(wake_abs, &wake_sys_sec, &wake_sys_usec);
+
+ monotonic_usec_total = monotonic_time.monotonic_time_usec;
+ wake_sec = monotonic_usec_total / (clock_sec_t)USEC_PER_SEC;
+ wake_usec = monotonic_usec_total % (clock_usec_t)USEC_PER_SEC;
+ } else {
+ /*
+ * Get UTC time and corresponding sys time
+ */
+ PEGetUTCTimeOfDay(&wake_sec, &wake_usec);
+ wake_abs = mach_absolute_time();
+ absolutetime_to_microtime(wake_abs, &wake_sys_sec, &wake_sys_usec);
+ }
+
+#if DEVELOPMENT || DEBUG
+ os_log(OS_LOG_DEFAULT, "time at wake %lu s %d u from %s clock, abs %llu\n", (unsigned long)wake_sec, wake_usec, (has_monotonic_clock)?"monotonic":"UTC", wake_abs);
+ if (has_monotonic_clock) {
+ os_log(OS_LOG_DEFAULT, "UTC time %lu s %d u\n", (unsigned long)var_s, var_us);
+ }
+#endif /* DEVELOPMENT || DEBUG */