1# Copyright (c) 2012 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 6import re 7 8from autotest_lib.client.common_lib import error 9from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 10 11class firmware_ECBattery(FirmwareTest): 12 """ 13 Servo based EC thermal battery status report test. 14 """ 15 version = 1 16 17 # Battery status path in sysfs 18 BATTERY_STATUS = '/sys/class/power_supply/%s/status' 19 20 # Battery voltage reading path in sysfs 21 BATTERY_VOLTAGE_READING = '/sys/class/power_supply/%s/voltage_now' 22 23 # Battery current reading path in sysfs 24 BATTERY_CURRENT_READING = '/sys/class/power_supply/%s/current_now' 25 26 # Maximum allowed error of voltage reading in mV 27 VOLTAGE_MV_ERROR_MARGIN = 300 28 29 # Maximum allowed error of current reading in mA 30 CURRENT_MA_ERROR_MARGIN = 300 31 32 # Maximum allowed battery temperature in C 33 BATTERY_TEMP_UPPER_BOUND = 70 34 35 # Minimum allowed battery temperature in C 36 BATTERY_TEMP_LOWER_BOUND = 0 37 38 39 def initialize(self, host, cmdline_args): 40 super(firmware_ECBattery, self).initialize(host, cmdline_args) 41 # Don't bother if there is no Chrome EC. 42 if not self.check_ec_capability(): 43 raise error.TestNAError("Nothing needs to be tested on this device") 44 # Only run in normal mode 45 self.switcher.setup_mode('normal') 46 self.ec.send_command("chan 0") 47 self._host = host 48 49 def cleanup(self): 50 try: 51 self.ec.send_command("chan 0xffffffff") 52 except Exception as e: 53 logging.error("Caught exception: %s", str(e)) 54 super(firmware_ECBattery, self).cleanup() 55 56 57 def _get_battery_path(self): 58 """Get battery path in sysfs.""" 59 match = self.faft_client.system.run_shell_command_get_output( 60 'grep -iH --color=no "Battery" /sys/class/power_supply/*/type') 61 name = None 62 for item in match: 63 logging.info("Battery sysfs %s", item) 64 name = re.search("/sys/class/power_supply/([^/]+)/", 65 item).group(1) 66 if (self._host.path_exists(self.BATTERY_STATUS % name) and 67 self._host.path_exists(self.BATTERY_VOLTAGE_READING % name) and 68 self._host.path_exists(self.BATTERY_CURRENT_READING % name)): 69 break 70 else: 71 name = None 72 if name is None: 73 raise error.TestFail("Can't find a battery sysfs node") 74 logging.info("Battery name is %s", name) 75 self._battery_status = self.BATTERY_STATUS % name 76 self._battery_voltage = self.BATTERY_VOLTAGE_READING % name 77 self._battery_current = self.BATTERY_CURRENT_READING % name 78 79 80 def _check_voltage_match(self): 81 """Check if voltage reading from kernel and servo match. 82 83 Raises: 84 error.TestFail: Raised when the two reading mismatch by more than 85 VOLTAGE_MV_ERROR_MARGIN mV. 86 """ 87 servo_reading = int(self.servo.get('ppvar_vbat_mv')) 88 # Kernel gives voltage value in uV. Convert to mV here. 89 kernel_reading = int( 90 self.faft_client.system.run_shell_command_get_output( 91 'cat %s' % self._battery_voltage)[0]) / 1000 92 logging.info("Voltage reading from servo: %dmV", servo_reading) 93 logging.info("Voltage reading from kernel: %dmV", kernel_reading) 94 if abs(servo_reading - kernel_reading) > self.VOLTAGE_MV_ERROR_MARGIN: 95 raise error.TestFail( 96 "Voltage reading from servo (%dmV) and kernel (%dmV) " 97 "mismatch." % (servo_reading, kernel_reading)) 98 99 100 def _check_current_match(self): 101 """Check if current reading from kernel and servo match. 102 103 Raises: 104 error.TestFail: Raised when the two reading mismatch by more than 105 CURRENT_MA_ERROR_MARGIN mA. 106 """ 107 # The signs of the current values from servo and kernel are not 108 # consistent across different devices. So we pick the absolute values. 109 # TODO(victoryang@chromium.org): Investigate the sign issue. 110 servo_reading = abs(int(self.servo.get('ppvar_vbat_ma'))) 111 # Kernel gives current value in uA. Convert to mA here. 112 kernel_reading = abs( 113 int(self.faft_client.system.run_shell_command_get_output( 114 'cat %s' % self._battery_current)[0])) / 1000 115 logging.info("Current reading from servo: %dmA", servo_reading) 116 logging.info("Current reading from kernel: %dmA", kernel_reading) 117 if abs(servo_reading - kernel_reading) > self.CURRENT_MA_ERROR_MARGIN: 118 raise error.TestFail( 119 "Current reading from servo (%dmA) and kernel (%dmA) " 120 "mismatch." % (servo_reading, kernel_reading)) 121 122 123 def _check_temperature(self): 124 """Check if battery temperature is reasonable. 125 126 Raises: 127 error.TestFail: Raised when battery tempearture is higher than 128 BATTERY_TEMP_UPPER_BOUND or lower than BATTERY_TEMP_LOWER_BOUND. 129 """ 130 battery_temp = float(self.ec.send_command_get_output("battery", 131 ["Temp:.+\(([0-9\.]+) C\)"])[0][1]) 132 logging.info("Battery temperature is %f C", battery_temp) 133 if (battery_temp > self.BATTERY_TEMP_UPPER_BOUND or 134 battery_temp < self.BATTERY_TEMP_LOWER_BOUND): 135 raise error.TestFail("Abnormal battery temperature, %.2f C." % 136 battery_temp) 137 138 139 def run_once(self): 140 """Execute the main body of the test. 141 """ 142 if not self.check_ec_capability(['battery']): 143 raise error.TestNAError("Nothing needs to be tested on this device") 144 145 self._get_battery_path() 146 147 logging.info("Checking battery current reading...") 148 self._check_current_match() 149 150 logging.info("Checking battery voltage reading...") 151 self._check_voltage_match() 152 153 logging.info("Checking battery temperature...") 154 self._check_temperature() 155