def kernel_suspend(seconds): """Do a kernel suspend. Suspend the system to RAM (S3), waking up again after |seconds|, by directly writing to /sys/power/state. Function will block until suspend/resume has completed or failed. @param seconds: The number of seconds to suspend the device. """ alarm, wakeup_count = prepare_wakeup(seconds) logging.debug('Saving wakeup count: %d', wakeup_count) write_wakeup_count(wakeup_count) try: logging.info('Suspending at %d', rtc.get_seconds()) with open(SYSFS_POWER_STATE, 'w') as sysfs_file: sysfs_file.write('mem') except IOError as e: logging.exception('Writing to %s failed', SYSFS_POWER_STATE) if e.errno == errno.EBUSY and rtc.get_seconds() >= alarm: # The kernel returns EBUSY if it has to abort because # another wakeup fires before we've reached suspend. raise SpuriousWakeupError('Received spurious wakeup in kernel.') else: # Some driver probably failed to suspend properly. # A hint as to what failed is in errno. raise KernelError('Suspend failed: %s' % e.strerror) else: logging.info('Woke from suspend at %d', rtc.get_seconds()) logging.debug('New wakeup count: %d', read_wakeup_count()) check_wakeup(alarm)
def check_wakeup(alarm): """Verify that the device did not wakeup early. @param alarm: The time at which the device was expected to wake up. """ now = rtc.get_seconds() if now < alarm: logging.error('Woke up early at %d', now) raise SpuriousWakeupError('Woke from suspend early')
def prepare_wakeup(seconds): """Prepare the device to wake up from an upcoming suspend. @param seconds: The number of seconds to allow the device to suspend. """ # May cause DUT not wake from sleep if the suspend time is 1 second. # It happens when the current clock (floating point) is close to the # next integer, as the RTC sysfs interface only accepts integers. # Make sure it is larger than or equal to 2. assert seconds >= 2 wakeup_count = read_wakeup_count() alarm = int(rtc.get_seconds() + seconds) logging.debug('Suspend for %d seconds, wakealarm = %d', seconds, alarm) rtc.set_wake_alarm(alarm) return (alarm, wakeup_count)
def idle_suspend(seconds): """ Wait for the system to suspend to RAM (S3), scheduling the RTC to wake up |seconds| after this function was called. Caller must ensure that the system will idle-suspend in time for this to happen. Returns the wake alarm time from the RTC as epoch. @param seconds: The number of seconds before wakeup. """ alarm, _ = prepare_wakeup(seconds) while rtc.get_seconds() < alarm: time.sleep(0.2) # tell powerd something happened, or it will immediately idle-suspend again # TODO: switch to cros.power_utils#call_powerd_dbus_method once this # horrible mess with the WiFi tests and this file's imports is solved logging.debug('Simulating user activity after idle suspend...') os.system('dbus-send --type=method_call --system ' '--dest=org.chromium.PowerManager /org/chromium/PowerManager ' 'org.chromium.PowerManager.HandleUserActivity') return alarm