155 lines
6.0 KiB
Python
155 lines
6.0 KiB
Python
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import logging
|
|
import re
|
|
|
|
from autotest_lib.client.common_lib import error
|
|
from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
|
|
|
|
class firmware_ECBattery(FirmwareTest):
|
|
"""
|
|
Servo based EC thermal battery status report test.
|
|
"""
|
|
version = 1
|
|
|
|
# Battery status path in sysfs
|
|
BATTERY_STATUS = '/sys/class/power_supply/%s/status'
|
|
|
|
# Battery voltage reading path in sysfs
|
|
BATTERY_VOLTAGE_READING = '/sys/class/power_supply/%s/voltage_now'
|
|
|
|
# Battery current reading path in sysfs
|
|
BATTERY_CURRENT_READING = '/sys/class/power_supply/%s/current_now'
|
|
|
|
# Maximum allowed error of voltage reading in mV
|
|
VOLTAGE_MV_ERROR_MARGIN = 300
|
|
|
|
# Maximum allowed error of current reading in mA
|
|
CURRENT_MA_ERROR_MARGIN = 300
|
|
|
|
# Maximum allowed battery temperature in C
|
|
BATTERY_TEMP_UPPER_BOUND = 70
|
|
|
|
# Minimum allowed battery temperature in C
|
|
BATTERY_TEMP_LOWER_BOUND = 0
|
|
|
|
|
|
def initialize(self, host, cmdline_args):
|
|
super(firmware_ECBattery, self).initialize(host, cmdline_args)
|
|
# Don't bother if there is no Chrome EC.
|
|
if not self.check_ec_capability():
|
|
raise error.TestNAError("Nothing needs to be tested on this device")
|
|
# Only run in normal mode
|
|
self.switcher.setup_mode('normal')
|
|
self.ec.send_command("chan 0")
|
|
self._host = host
|
|
|
|
def cleanup(self):
|
|
try:
|
|
self.ec.send_command("chan 0xffffffff")
|
|
except Exception as e:
|
|
logging.error("Caught exception: %s", str(e))
|
|
super(firmware_ECBattery, self).cleanup()
|
|
|
|
|
|
def _get_battery_path(self):
|
|
"""Get battery path in sysfs."""
|
|
match = self.faft_client.system.run_shell_command_get_output(
|
|
'grep -iH --color=no "Battery" /sys/class/power_supply/*/type')
|
|
name = None
|
|
for item in match:
|
|
logging.info("Battery sysfs %s", item)
|
|
name = re.search("/sys/class/power_supply/([^/]+)/",
|
|
item).group(1)
|
|
if (self._host.path_exists(self.BATTERY_STATUS % name) and
|
|
self._host.path_exists(self.BATTERY_VOLTAGE_READING % name) and
|
|
self._host.path_exists(self.BATTERY_CURRENT_READING % name)):
|
|
break
|
|
else:
|
|
name = None
|
|
if name is None:
|
|
raise error.TestFail("Can't find a battery sysfs node")
|
|
logging.info("Battery name is %s", name)
|
|
self._battery_status = self.BATTERY_STATUS % name
|
|
self._battery_voltage = self.BATTERY_VOLTAGE_READING % name
|
|
self._battery_current = self.BATTERY_CURRENT_READING % name
|
|
|
|
|
|
def _check_voltage_match(self):
|
|
"""Check if voltage reading from kernel and servo match.
|
|
|
|
Raises:
|
|
error.TestFail: Raised when the two reading mismatch by more than
|
|
VOLTAGE_MV_ERROR_MARGIN mV.
|
|
"""
|
|
servo_reading = int(self.servo.get('ppvar_vbat_mv'))
|
|
# Kernel gives voltage value in uV. Convert to mV here.
|
|
kernel_reading = int(
|
|
self.faft_client.system.run_shell_command_get_output(
|
|
'cat %s' % self._battery_voltage)[0]) / 1000
|
|
logging.info("Voltage reading from servo: %dmV", servo_reading)
|
|
logging.info("Voltage reading from kernel: %dmV", kernel_reading)
|
|
if abs(servo_reading - kernel_reading) > self.VOLTAGE_MV_ERROR_MARGIN:
|
|
raise error.TestFail(
|
|
"Voltage reading from servo (%dmV) and kernel (%dmV) "
|
|
"mismatch." % (servo_reading, kernel_reading))
|
|
|
|
|
|
def _check_current_match(self):
|
|
"""Check if current reading from kernel and servo match.
|
|
|
|
Raises:
|
|
error.TestFail: Raised when the two reading mismatch by more than
|
|
CURRENT_MA_ERROR_MARGIN mA.
|
|
"""
|
|
# The signs of the current values from servo and kernel are not
|
|
# consistent across different devices. So we pick the absolute values.
|
|
# TODO(victoryang@chromium.org): Investigate the sign issue.
|
|
servo_reading = abs(int(self.servo.get('ppvar_vbat_ma')))
|
|
# Kernel gives current value in uA. Convert to mA here.
|
|
kernel_reading = abs(
|
|
int(self.faft_client.system.run_shell_command_get_output(
|
|
'cat %s' % self._battery_current)[0])) / 1000
|
|
logging.info("Current reading from servo: %dmA", servo_reading)
|
|
logging.info("Current reading from kernel: %dmA", kernel_reading)
|
|
if abs(servo_reading - kernel_reading) > self.CURRENT_MA_ERROR_MARGIN:
|
|
raise error.TestFail(
|
|
"Current reading from servo (%dmA) and kernel (%dmA) "
|
|
"mismatch." % (servo_reading, kernel_reading))
|
|
|
|
|
|
def _check_temperature(self):
|
|
"""Check if battery temperature is reasonable.
|
|
|
|
Raises:
|
|
error.TestFail: Raised when battery tempearture is higher than
|
|
BATTERY_TEMP_UPPER_BOUND or lower than BATTERY_TEMP_LOWER_BOUND.
|
|
"""
|
|
battery_temp = float(self.ec.send_command_get_output("battery",
|
|
["Temp:.+\(([0-9\.]+) C\)"])[0][1])
|
|
logging.info("Battery temperature is %f C", battery_temp)
|
|
if (battery_temp > self.BATTERY_TEMP_UPPER_BOUND or
|
|
battery_temp < self.BATTERY_TEMP_LOWER_BOUND):
|
|
raise error.TestFail("Abnormal battery temperature, %.2f C." %
|
|
battery_temp)
|
|
|
|
|
|
def run_once(self):
|
|
"""Execute the main body of the test.
|
|
"""
|
|
if not self.check_ec_capability(['battery']):
|
|
raise error.TestNAError("Nothing needs to be tested on this device")
|
|
|
|
self._get_battery_path()
|
|
|
|
logging.info("Checking battery current reading...")
|
|
self._check_current_match()
|
|
|
|
logging.info("Checking battery voltage reading...")
|
|
self._check_voltage_match()
|
|
|
|
logging.info("Checking battery temperature...")
|
|
self._check_temperature()
|