1# Copyright 2016 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import logging, re, time 6 7from autotest_lib.client.common_lib import error 8from autotest_lib.server import test 9from autotest_lib.server.cros.dark_resume_utils import DarkResumeUtils 10from autotest_lib.server.cros.faft.config.config import Config as FAFTConfig 11 12SUSPEND_DURATION = 15 13NUM_DARK_RESUMES = 10 14ERROR_FILE = '/sys/kernel/debug/dri/0/i915_crtc_errors' 15 16 17class power_DarkResumeDisplay(test.test): 18 """ Ensure we don't have display errors after dark resume """ 19 version = 1 20 dark_resume_utils = None 21 22 23 def initialize(self, host): 24 self.dark_resume_utils = DarkResumeUtils(host, 25 duration=SUSPEND_DURATION) 26 27 28 def verify_host_supports_test(self, host): 29 """Check if the test works on the given host 30 31 @param host: reference to the host object 32 """ 33 platform = host.run_output('mosys platform name') 34 logging.info('Checking platform %s for compatibility with display test', 35 platform) 36 37 # TODO(seanpaul) Look at backporting i915_crtc_errors accounting to 38 # other kernels. 39 kernel_ver = host.run('uname -r').stdout.rstrip() 40 logging.info('kernel version is %s', kernel_ver) 41 if not kernel_ver.startswith('3.14') and \ 42 not kernel_ver.startswith('3.18'): 43 raise error.TestNAError('Test support on 3.14 | 3.18 kernels only') 44 45 client_attr = FAFTConfig(platform) 46 if client_attr.dark_resume_capable == False: 47 raise error.TestNAError('platform is not capable of dark resume') 48 49 cmd = host.run('test -r %s' % ERROR_FILE, ignore_status=True) 50 logging.info("node_exists=%s", str(cmd.exit_status)) 51 if cmd.exit_status != 0: 52 raise error.TestError('%s file not found.' % ERROR_FILE) 53 54 55 def get_crtc_error_count(self, host): 56 """Get the current crtc error count for the dut 57 58 @returns: A dict whose key is the crtc id, and whose value is a 59 dict of {pipe, errors} 60 """ 61 output = host.run_output('cat %s' % ERROR_FILE) 62 pattern = 'Crtc ([0-9]+) Pipe ([A-Za-z]+) errors:\t\t([0-9a-fA-F]{8})' 63 regex = re.compile(pattern) 64 counts = {} 65 for line in output.splitlines(): 66 match = regex.match(line) 67 if match == None: 68 raise error.TestError('Unexpected error file string: %s' % line) 69 70 counts[int(match.group(1))] = { 71 'pipe': match.group(2), 72 'errors': int(match.group(3), 16), 73 } 74 return counts 75 76 77 def run_once(self, host=None): 78 """Run the test. 79 80 Setup preferences so that a dark resume will happen shortly after 81 suspending the machine. 82 83 store the current crtc error count 84 suspend the machine 85 wait for dark resume 86 wake the machine 87 retrieve the current crtc error count after suspend 88 ensure the error counts did not increase while suspended 89 90 @param host: The machine to run the tests on 91 """ 92 self.verify_host_supports_test(host) 93 94 pre_err_count = self.get_crtc_error_count(host) 95 96 # The DUT will perform a dark resume every SUSPEND_DURATION seconds 97 # while it is suspended. Suspend the device and wait for the amount 98 # of time to have performed NUM_DARK_RESUMES, plus half the 99 # SUSPEND_DURATION to ensure the last dark resume has a chance to 100 # complete. 101 wait_time = SUSPEND_DURATION * NUM_DARK_RESUMES + SUSPEND_DURATION / 2 102 logging.info('suspending host, and waiting %ds', wait_time) 103 with self.dark_resume_utils.suspend() as _: 104 time.sleep(wait_time) 105 106 dark_resume_count = self.dark_resume_utils.count_dark_resumes() 107 logging.info('dark resume count = %d', dark_resume_count) 108 if dark_resume_count == 0: 109 raise error.TestError('Device did not enter dark resume!') 110 111 logging.info('retrieving post-suspend error counts') 112 post_err_count = self.get_crtc_error_count(host) 113 for k in pre_err_count: 114 pre = pre_err_count[k]['errors'] 115 post = post_err_count[k]['errors'] 116 if pre != post: 117 raise error.TestError('Crtc %d Pipe %s err count changed %d/%d' 118 % (k, pre_err_count[k]['pipe'], pre, 119 post)) 120 logging.info('error counts for Crtc %d Pipe %s constant at %d', k, 121 pre_err_count[k]['pipe'], pre) 122 123 124 def cleanup(self, _): 125 self.dark_resume_utils.teardown() 126 127