1 # Copyright (c) 2010 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 5 import glob, os 6 from autotest_lib.client.bin import test 7 from autotest_lib.client.common_lib import error, utils 8 9 class power_ProbeDriver(test.test): 10 """Confirms that AC driver is loaded and functioning 11 unless device is AC only.""" 12 version = 1 13 power_supply_path = '/sys/class/power_supply/*' 14 15 def run_once(self, test_which='Mains'): 16 # This test doesn't apply to systems that run on AC only. 17 cmd = "mosys psu type" 18 if utils.system_output(cmd, ignore_status=True).strip() == "AC_only": 19 return 20 ac_paths = [] 21 bat_paths = [] 22 # Gather power supplies 23 for path in glob.glob(power_ProbeDriver.power_supply_path): 24 type_path = os.path.join(path, 'type') 25 if not os.path.exists(type_path): 26 continue 27 # With the advent of USB Type-C, mains power might show up as 28 # one of several variants of USB. 29 psu_type = utils.read_one_line(type_path) 30 if any( [psu_type == 'Mains', psu_type == 'USB', 31 psu_type == 'USB_DCP', psu_type == 'USB_CDP', 32 psu_type == 'USB_TYPE_C', psu_type == 'USB_PD', 33 psu_type == 'USB_PD_DRP'] ): 34 ac_paths.append(path) 35 elif psu_type == 'Battery': 36 bat_paths.append(path) 37 run_dict = { 'Mains': self.run_ac, 'Battery': self.run_bat } 38 run = run_dict.get(test_which) 39 if run: 40 run(ac_paths, bat_paths) 41 else: 42 raise error.TestNAError('Unknown test type: %s' % test_which) 43 44 def run_ac(self, ac_paths, bat_paths): 45 """Checks AC driver. 46 47 @param ac_paths: sysfs AC entries 48 @param bat_paths: sysfs battery entries 49 """ 50 if not ac_paths: 51 raise error.TestFail('No line power devices found in %s' % 52 power_supply_path) 53 54 if not any([self._online(ac_path) for ac_path in ac_paths]): 55 raise error.TestFail('Line power is not connected') 56 57 # if there are batteries, test fails if one of them is discharging 58 # note: any([]) == False, so we don't have to test len(bat_paths) > 0 59 if any(self._is_discharging(bat_path, ac_paths) 60 for bat_path in bat_paths 61 if self._present(bat_path)): 62 raise error.TestFail('One of batteries is discharging') 63 64 def run_bat(self, ac_paths, bat_paths): 65 """ Checks batteries. 66 67 @param ac_paths: sysfs AC entries 68 @param bat_paths: sysfs battery entries 69 """ 70 if len(bat_paths) == 0: 71 raise error.TestFail('Find no batteries') 72 73 presented = [bat_path for bat_path in bat_paths 74 if self._present(bat_path)] 75 if len(presented) == 0: 76 raise error.TestFail('No batteries are presented') 77 78 if all(not self._is_discharging(bat_path, ac_paths) for bat_path 79 in presented): 80 raise error.TestFail('No batteries are discharging') 81 82 if any(self._online(ac_path) for ac_path in ac_paths): 83 raise error.TestFail('One of ACs is online') 84 85 def _online(self, ac_path): 86 online_path = os.path.join(ac_path, 'online') 87 if not os.path.exists(online_path): 88 raise error.TestFail('online path does not exist: %s' % online_path) 89 online = utils.read_one_line(online_path) 90 return online == '1' 91 92 def _has_property(self, bat_path, field): 93 """ 94 Indicates whether a battery sysfs has the given field. 95 96 Fields: 97 str bat_path: Battery sysfs path 98 str field: Sysfs field to test for. 99 100 Return value: 101 bool True if the field exists, False otherwise. 102 """ 103 return os.path.exists(os.path.join(bat_path, field)) 104 105 def _read_property(self, bat_path, field): 106 """ 107 Reads the contents of a sysfs field for a battery sysfs. 108 109 Fields: 110 str bat_path: Battery sysfs path 111 str field: Sysfs field to read. 112 113 Return value: 114 str The contents of the sysfs field. 115 """ 116 property_path = os.path.join(bat_path, field) 117 if not self._has_property(bat_path, field): 118 raise error.TestNAError('Path not found: %s' % property_path) 119 return utils.read_one_line(property_path) 120 121 def _present(self, bat_path): 122 """ 123 Indicates whether a battery is present, based on sysfs status. 124 125 Fields: 126 str bat_path: Battery sysfs path 127 128 Return value: 129 bool True if the battery is present, False otherwise. 130 """ 131 return self._read_property(bat_path, 'present') == '1' 132 133 def _is_discharging(self, bat_path, ac_paths): 134 """ 135 Indicates whether a battery is discharging, based on sysfs status. 136 137 Sometimes the sysfs will not show status='Discharging' when actually 138 discharging. So this function looks at both battery sysfs and AC sysfs. 139 If the battery is discharging, there will be no line power and the 140 power/current draw will be nonzero. 141 142 Fields: 143 str bat_path: Battery sysfs path 144 str[] ac_paths: List of AC sysfs paths 145 146 Return value: 147 bool True if the battery is discharging, False otherwise. 148 """ 149 if self._read_property(bat_path, 'status') == 'Discharging': 150 return True 151 if all(not self._online(ac_path) for ac_path in ac_paths): 152 if (self._has_property(bat_path, 'power_now') and 153 self._read_property(bat_path, 'power_now') != '0'): 154 return True 155 if (self._has_property(bat_path, 'current_now') and 156 self._read_property(bat_path, 'current_now') != '0'): 157 return True 158 return False 159