1b6d2993fb77f771a886c41ace0850773f5498bedbarfab@chromium.org# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
250618caef7d4ca20532f505013cae19e0f78600dSean O'Connor# Use of this source code is governed by a BSD-style license that can be
350618caef7d4ca20532f505013cae19e0f78600dSean O'Connor# found in the LICENSE file.
450618caef7d4ca20532f505013cae19e0f78600dSean O'Connor
5a825d2546070eacb65820075fed615f772eb756bJulius Werner"""Provides utility methods for controlling powerd in ChromiumOS."""
650618caef7d4ca20532f505013cae19e0f78600dSean O'Connor
7db83bfa5062129a22895cb347065ea51a77050dbEric Carusoimport errno
8db83bfa5062129a22895cb347065ea51a77050dbEric Carusoimport logging
9db83bfa5062129a22895cb347065ea51a77050dbEric Carusoimport multiprocessing
10db83bfa5062129a22895cb347065ea51a77050dbEric Carusoimport os
11db83bfa5062129a22895cb347065ea51a77050dbEric Carusoimport rtc
12db83bfa5062129a22895cb347065ea51a77050dbEric Carusoimport time
13db83bfa5062129a22895cb347065ea51a77050dbEric Carusoimport upstart
145395ed272f13e62cf29d621ffb2198b4470b7af7Jason Glasgow
15a825d2546070eacb65820075fed615f772eb756bJulius WernerSYSFS_POWER_STATE = '/sys/power/state'
16b72ffce3b1391891f1d663f72559e13cf3ce3161Michael SpangSYSFS_WAKEUP_COUNT = '/sys/power/wakeup_count'
17b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang
185395ed272f13e62cf29d621ffb2198b4470b7af7Jason Glasgow
19a825d2546070eacb65820075fed615f772eb756bJulius Wernerclass SuspendFailure(Exception):
20a825d2546070eacb65820075fed615f772eb756bJulius Werner    """Base class for a failure during a single suspend/resume cycle."""
21a825d2546070eacb65820075fed615f772eb756bJulius Werner    pass
22c84829623e116342293776b21525097e8d19b59dVivek Dasmohapatra
237a0e9d4a32182a5e2a585ec5e8e8985cc846d30fPaul Stewart
24d7f648775bc21d933c55412e533fe967f6588c40Julius Wernerclass SuspendTimeout(SuspendFailure):
25a825d2546070eacb65820075fed615f772eb756bJulius Werner    """Suspend took too long, got wakeup event (RTC tick) before it was done."""
26a825d2546070eacb65820075fed615f772eb756bJulius Werner    pass
277a0e9d4a32182a5e2a585ec5e8e8985cc846d30fPaul Stewart
287a0e9d4a32182a5e2a585ec5e8e8985cc846d30fPaul Stewart
29a825d2546070eacb65820075fed615f772eb756bJulius Wernerclass KernelError(SuspendFailure):
30a825d2546070eacb65820075fed615f772eb756bJulius Werner    """Kernel problem encountered during suspend/resume."""
3173ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner    WHITELIST = [
3273ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner            # crosbug.com/37594: debug tracing clock desync we don't care about
3373ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner            (r'kernel/trace/ring_buffer.c:\d+ rb_reserve_next_event',
3473ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner             r'Delta way too big!'),
3573ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner        ]
365395ed272f13e62cf29d621ffb2198b4470b7af7Jason Glasgow
377a0e9d4a32182a5e2a585ec5e8e8985cc846d30fPaul Stewart
38a825d2546070eacb65820075fed615f772eb756bJulius Wernerclass FirmwareError(SuspendFailure):
39a825d2546070eacb65820075fed615f772eb756bJulius Werner    """String 'ERROR' found in firmware log after resume."""
4073ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner    WHITELIST = [
4173ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner            # crosbug.com/36762: no one knows, but it has always been there
42ad4dfaef0b19d6dcda5976f5d555a897a4331261Julius Werner            ('^stumpy', r'PNP: 002e\.4 70 irq size: 0x0000000001 not assigned'),
43ad4dfaef0b19d6dcda5976f5d555a897a4331261Julius Werner            # crbug.com/221538: no one knows what ME errors mean anyway
44ad4dfaef0b19d6dcda5976f5d555a897a4331261Julius Werner            ('^parrot', r'ME failed to respond'),
4573ab548a20594b9d4e853995e2590dfcefe1fe58Julius Werner        ]
465395ed272f13e62cf29d621ffb2198b4470b7af7Jason Glasgow
475395ed272f13e62cf29d621ffb2198b4470b7af7Jason Glasgow
48d7f648775bc21d933c55412e533fe967f6588c40Julius Wernerclass SpuriousWakeupError(SuspendFailure):
49d7f648775bc21d933c55412e533fe967f6588c40Julius Werner    """Received spurious wakeup while suspending or woke before schedule."""
50d7f648775bc21d933c55412e533fe967f6588c40Julius Werner    S3_WHITELIST = [  # (<board>, <eventlog wake source>, <syslog wake source>)
51d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            # crbug.com/220014: spurious trackpad IRQs
52d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            ('^link', 'Wake Source | GPIO | 12', ''),
53d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            # crbug.com/345327: unknown, probably same as crbug.com/290923
54d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            ('^x86-alex', '', ''),   # alex can report neither, blanket ignore
55d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            # crbug.com/355106: unknown, possibly related to crbug.com/290923
56cfd1608994281e0ace7e325f1f5d069c87bb6e71Julius Werner            ('^lumpy|^parrot', '', 'PM1_STS: WAK PWRBTN'),
57d7f648775bc21d933c55412e533fe967f6588c40Julius Werner        ]
58d7f648775bc21d933c55412e533fe967f6588c40Julius Werner    S0_WHITELIST = [  # (<board>, <kernel wake source>)
59d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            # crbug.com/290923: spurious keyboard IRQ, believed to be from Servo
60536f1d5b772b031ce513f80072f9308cfa049303Julius Werner            ('^x86-alex|^lumpy|^parrot|^butterfly', 'serio0'),
61d7f648775bc21d933c55412e533fe967f6588c40Julius Werner        ]
62b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang
630537d52c7b85e9454336449f2bc9efae5a4983a4Julius Wernerclass MemoryError(SuspendFailure):
640537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    """memory_suspend_test found memory corruption."""
650537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    pass
660537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
670537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
68dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffreyclass SuspendNotAllowed(SuspendFailure):
69dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """Suspend was not allowed to be performed."""
70dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    pass
71dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
72dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
730537d52c7b85e9454336449f2bc9efae5a4983a4Julius Wernerdef prepare_wakeup(seconds):
74dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """Prepare the device to wake up from an upcoming suspend.
75dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
76dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    @param seconds: The number of seconds to allow the device to suspend.
77dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """
786b146373bfd19e2f5c1d0f1a239d7b2e5b6c4d28Tom Wai-Hong Tam    # May cause DUT not wake from sleep if the suspend time is 1 second.
796b146373bfd19e2f5c1d0f1a239d7b2e5b6c4d28Tom Wai-Hong Tam    # It happens when the current clock (floating point) is close to the
806b146373bfd19e2f5c1d0f1a239d7b2e5b6c4d28Tom Wai-Hong Tam    # next integer, as the RTC sysfs interface only accepts integers.
816b146373bfd19e2f5c1d0f1a239d7b2e5b6c4d28Tom Wai-Hong Tam    # Make sure it is larger than or equal to 2.
826b146373bfd19e2f5c1d0f1a239d7b2e5b6c4d28Tom Wai-Hong Tam    assert seconds >= 2
830537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    wakeup_count = read_wakeup_count()
840537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    alarm = int(rtc.get_seconds() + seconds)
85dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    logging.debug('Suspend for %d seconds, wakealarm = %d', seconds, alarm)
860537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    rtc.set_wake_alarm(alarm)
870537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    return (alarm, wakeup_count)
880537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
890537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
900537d52c7b85e9454336449f2bc9efae5a4983a4Julius Wernerdef check_wakeup(alarm):
91dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """Verify that the device did not wakeup early.
92dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
93dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    @param alarm: The time at which the device was expected to wake up.
94dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """
950537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    now = rtc.get_seconds()
960537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    if now < alarm:
970537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        logging.error('Woke up early at %d', now)
98d7f648775bc21d933c55412e533fe967f6588c40Julius Werner        raise SpuriousWakeupError('Woke from suspend early')
990537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
1000537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
101958a1f590e22ce581ae579d89daa865f847a57fbWade Guthriedef do_suspend(suspend_seconds, delay_seconds=0):
102d9c07476a1625a1f949fd48202d42ffaac06aeacDaniel Erat    """Do a suspend using the power manager.
103dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
104958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    Wait for |delay_seconds|, suspend the system to RAM (S3), waking up again
105958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    after having been suspended for |suspend_seconds|, using the
106958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    powerd_dbus_suspend program. Function will block until suspend/resume
107958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    has completed or failed. Returns the wake alarm time from the RTC as epoch.
108958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie
109958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    @param suspend_seconds: Number of seconds to suspend the DUT.
110958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    @param delay_seconds: Number of seconds wait before suspending the DUT.
111dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
1120537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    """
113958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    alarm, wakeup_count = prepare_wakeup(suspend_seconds)
1141c706b06cf0f5a118cca398c66854ba2bb897d1aJulius Werner    upstart.ensure_running('powerd')
1159297d9005cf6e3ffc7a3158cacf67e9856c142fcSteve Fung    command = ('/usr/bin/powerd_dbus_suspend --delay=%d --timeout=30 '
1169297d9005cf6e3ffc7a3158cacf67e9856c142fcSteve Fung               '--wakeup_count=%d') % (delay_seconds, wakeup_count)
117d9c07476a1625a1f949fd48202d42ffaac06aeacDaniel Erat    logging.info("Running '%s'", command)
118d9c07476a1625a1f949fd48202d42ffaac06aeacDaniel Erat    os.system(command)
1190537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    check_wakeup(alarm)
1200537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    return alarm
1210537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
1220537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
123db83bfa5062129a22895cb347065ea51a77050dbEric Carusodef suspend_bg_for_dark_resume(delay_seconds=0):
124db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    """Do a non-blocking indefinite suspend using power manager. ONLY USE THIS
125db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    IF YOU ARE ABSOLUTELY CERTAIN YOU NEED TO.
126db83bfa5062129a22895cb347065ea51a77050dbEric Caruso
127db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    Wait for |delay_seconds|, then suspend to RAM (S3). This does not set an RTC
128db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    alarm and does not pass an external wakeup count. It is meant to be used for
129db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    dark resume testing, where the server-side API exposes it in such a fashion
130db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    that the DUT will be woken by the server no matter how the test is exited.
131db83bfa5062129a22895cb347065ea51a77050dbEric Caruso
132db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    @param delay_seconds: Number of seconds wait before suspending the DUT.
133db83bfa5062129a22895cb347065ea51a77050dbEric Caruso
134db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    """
1351c706b06cf0f5a118cca398c66854ba2bb897d1aJulius Werner    upstart.ensure_running('powerd')
136db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    command = ('/usr/bin/powerd_dbus_suspend --delay=%d '
137db83bfa5062129a22895cb347065ea51a77050dbEric Caruso               '--timeout=30') % delay_seconds
138db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    logging.info("Running '%s'", command)
139db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    process = multiprocessing.Process(target=os.system, args=(command,))
140db83bfa5062129a22895cb347065ea51a77050dbEric Caruso    process.start()
141db83bfa5062129a22895cb347065ea51a77050dbEric Caruso
142db83bfa5062129a22895cb347065ea51a77050dbEric Caruso
1430537d52c7b85e9454336449f2bc9efae5a4983a4Julius Wernerdef kernel_suspend(seconds):
144dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """Do a kernel suspend.
145dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
1460537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    Suspend the system to RAM (S3), waking up again after |seconds|, by directly
1470537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    writing to /sys/power/state. Function will block until suspend/resume has
1480537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    completed or failed.
149dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
150dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    @param seconds: The number of seconds to suspend the device.
1510537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    """
1520537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    alarm, wakeup_count = prepare_wakeup(seconds)
1530537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    logging.debug('Saving wakeup count: %d', wakeup_count)
1540537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    write_wakeup_count(wakeup_count)
1550537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    try:
1560537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        logging.info('Suspending at %d', rtc.get_seconds())
1570537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        with open(SYSFS_POWER_STATE, 'w') as sysfs_file:
1580537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner            sysfs_file.write('mem')
1590537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    except IOError as e:
160dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey        logging.exception('Writing to %s failed', SYSFS_POWER_STATE)
1610537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        if e.errno == errno.EBUSY and rtc.get_seconds() >= alarm:
1620537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner            # The kernel returns EBUSY if it has to abort because
163d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            # another wakeup fires before we've reached suspend.
164d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            raise SpuriousWakeupError('Received spurious wakeup in kernel.')
165370846981c87c8247d085354564b43d29e6fed99Michael Spang        else:
1660537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner            # Some driver probably failed to suspend properly.
1670537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner            # A hint as to what failed is in errno.
1680537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner            raise KernelError('Suspend failed: %s' % e.strerror)
169a825d2546070eacb65820075fed615f772eb756bJulius Werner    else:
1700537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        logging.info('Woke from suspend at %d', rtc.get_seconds())
1710537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    logging.debug('New wakeup count: %d', read_wakeup_count())
1720537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    check_wakeup(alarm)
1730537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
1740537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner
175a1f6834e38171f6aaf9be479491f888efc716cb3Julius Wernerdef idle_suspend(seconds):
176a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    """
177a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    Wait for the system to suspend to RAM (S3), scheduling the RTC to wake up
178a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    |seconds| after this function was called. Caller must ensure that the system
179a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    will idle-suspend in time for this to happen. Returns the wake alarm time
180a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    from the RTC as epoch.
181958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie
182958a1f590e22ce581ae579d89daa865f847a57fbWade Guthrie    @param seconds: The number of seconds before wakeup.
183a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    """
184a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    alarm, _ = prepare_wakeup(seconds)
185a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    while rtc.get_seconds() < alarm:
186a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner        time.sleep(0.2)
187a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner
188a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    # tell powerd something happened, or it will immediately idle-suspend again
189a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    # TODO: switch to cros.power_utils#call_powerd_dbus_method once this
190a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    # horrible mess with the WiFi tests and this file's imports is solved
191e233b214cbe91e56f9eaa5099866245e918ee40dJulius Werner    logging.debug('Simulating user activity after idle suspend...')
192d9c07476a1625a1f949fd48202d42ffaac06aeacDaniel Erat    os.system('dbus-send --type=method_call --system '
193d9c07476a1625a1f949fd48202d42ffaac06aeacDaniel Erat              '--dest=org.chromium.PowerManager /org/chromium/PowerManager '
194d9c07476a1625a1f949fd48202d42ffaac06aeacDaniel Erat              'org.chromium.PowerManager.HandleUserActivity')
195a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner
196a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner    return alarm
197a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner
198a1f6834e38171f6aaf9be479491f888efc716cb3Julius Werner
199bcf2321823813137321d69e493963c6d945a2555Puthikorn Voravootivatdef memory_suspend(seconds, size=0):
200dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """Do a memory suspend.
201dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
2020537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    Suspend the system to RAM (S3), waking up again after |seconds|, using
2030537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    the memory_suspend_test tool. Function will block until suspend/resume has
2040537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    completed or failed. Returns the wake alarm time from the RTC as epoch.
205dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
206dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    @param seconds: The number of seconds to suspend the device.
207bcf2321823813137321d69e493963c6d945a2555Puthikorn Voravootivat    @param size: Amount of memory to allocate, in bytes.
208bcf2321823813137321d69e493963c6d945a2555Puthikorn Voravootivat                 Set to 0 to let memory_suspend_test determine amount of memory.
2090537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    """
2100537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    # since we cannot have utils.system_output in here, we need a workaround
2110537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    output = '/tmp/memory_suspend_output'
2120537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    alarm, wakeup_count = prepare_wakeup(seconds)
2139297d9005cf6e3ffc7a3158cacf67e9856c142fcSteve Fung    status = os.system('/usr/bin/memory_suspend_test --wakeup_count=%d '
2149297d9005cf6e3ffc7a3158cacf67e9856c142fcSteve Fung                       '--size=%d > %s' % (wakeup_count, size, output))
2150537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    status = os.WEXITSTATUS(status)
2160537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    if status == 2:
2170537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        logging.error('memory_suspend_test found the following errors:')
2180537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        for line in open(output, 'r'):
2190537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner            logging.error(line)
2200537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        raise MemoryError('Memory corruption found after resume')
221cb95b18a46718300c0065559a607100394d75345Julius Werner    elif status == 1:
2220537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner        raise SuspendFailure('Failure in powerd_suspend during memory test')
223cb95b18a46718300c0065559a607100394d75345Julius Werner    elif status:
224cb95b18a46718300c0065559a607100394d75345Julius Werner        raise SuspendFailure('Unknown failure in memory_suspend_test (crash?)')
2250537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    check_wakeup(alarm)
2260537d52c7b85e9454336449f2bc9efae5a4983a4Julius Werner    return alarm
227b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang
228b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang
229b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spangdef read_wakeup_count():
230b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang    """
231b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang    Retrieves the current value of /sys/power/wakeup_count.
232b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang    """
233a825d2546070eacb65820075fed615f772eb756bJulius Werner    with open(SYSFS_WAKEUP_COUNT) as sysfs_file:
234a825d2546070eacb65820075fed615f772eb756bJulius Werner        return int(sysfs_file.read().rstrip('\n'))
235b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang
236b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang
237b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spangdef write_wakeup_count(wakeup_count):
238dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    """Writes a value to /sys/power/wakeup_count.
239dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey
240dee63dd4ca81825e806e9a790201a49f357d6adaDennis Jeffrey    @param wakeup_count: The wakeup count value to write.
241b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang    """
242b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang    try:
243a825d2546070eacb65820075fed615f772eb756bJulius Werner        with open(SYSFS_WAKEUP_COUNT, 'w') as sysfs_file:
244a825d2546070eacb65820075fed615f772eb756bJulius Werner            sysfs_file.write(str(wakeup_count))
245b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang    except IOError as e:
246a825d2546070eacb65820075fed615f772eb756bJulius Werner        if (e.errno == errno.EINVAL and read_wakeup_count() != wakeup_count):
247d7f648775bc21d933c55412e533fe967f6588c40Julius Werner            raise SpuriousWakeupError('wakeup_count changed before suspend')
248b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang        else:
249b72ffce3b1391891f1d663f72559e13cf3ce3161Michael Spang            raise
250