• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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